mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 04:08:16 +00:00 
			
		
		
		
	Merge pull request #61976 from atlassian/ticker-with-stop
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Stop() for Ticker to enable leak-free code **What this PR does / why we need it**: I wanted to use the clock package but the `Ticker` without a `Stop()` method is a deal breaker for me. **Release note**: ```release-note NONE ``` /kind enhancement /sig api-machinery
This commit is contained in:
		@@ -414,13 +414,15 @@ func (m *managerImpl) synchronize(diskInfoProvider DiskInfoProvider, podFunc Act
 | 
			
		||||
 | 
			
		||||
func (m *managerImpl) waitForPodsCleanup(podCleanedUpFunc PodCleanedUpFunc, pods []*v1.Pod) {
 | 
			
		||||
	timeout := m.clock.NewTimer(podCleanupTimeout)
 | 
			
		||||
	tick := m.clock.Tick(podCleanupPollFreq)
 | 
			
		||||
	defer timeout.Stop()
 | 
			
		||||
	ticker := m.clock.NewTicker(podCleanupPollFreq)
 | 
			
		||||
	defer ticker.Stop()
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case <-timeout.C():
 | 
			
		||||
			glog.Warningf("eviction manager: timed out waiting for pods %s to be cleaned up", format.Pods(pods))
 | 
			
		||||
			return
 | 
			
		||||
		case <-tick:
 | 
			
		||||
		case <-ticker.C():
 | 
			
		||||
			for i, pod := range pods {
 | 
			
		||||
				if !podCleanedUpFunc(pod) {
 | 
			
		||||
					break
 | 
			
		||||
 
 | 
			
		||||
@@ -26,18 +26,12 @@ import (
 | 
			
		||||
type Clock interface {
 | 
			
		||||
	Now() time.Time
 | 
			
		||||
	Since(time.Time) time.Duration
 | 
			
		||||
	After(d time.Duration) <-chan time.Time
 | 
			
		||||
	NewTimer(d time.Duration) Timer
 | 
			
		||||
	Sleep(d time.Duration)
 | 
			
		||||
	Tick(d time.Duration) <-chan time.Time
 | 
			
		||||
	After(time.Duration) <-chan time.Time
 | 
			
		||||
	NewTimer(time.Duration) Timer
 | 
			
		||||
	Sleep(time.Duration)
 | 
			
		||||
	NewTicker(time.Duration) Ticker
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	_ = Clock(RealClock{})
 | 
			
		||||
	_ = Clock(&FakeClock{})
 | 
			
		||||
	_ = Clock(&IntervalClock{})
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RealClock really calls time.Now()
 | 
			
		||||
type RealClock struct{}
 | 
			
		||||
 | 
			
		||||
@@ -62,8 +56,10 @@ func (RealClock) NewTimer(d time.Duration) Timer {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (RealClock) Tick(d time.Duration) <-chan time.Time {
 | 
			
		||||
	return time.Tick(d)
 | 
			
		||||
func (RealClock) NewTicker(d time.Duration) Ticker {
 | 
			
		||||
	return &realTicker{
 | 
			
		||||
		ticker: time.NewTicker(d),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (RealClock) Sleep(d time.Duration) {
 | 
			
		||||
@@ -137,7 +133,7 @@ func (f *FakeClock) NewTimer(d time.Duration) Timer {
 | 
			
		||||
	return timer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *FakeClock) Tick(d time.Duration) <-chan time.Time {
 | 
			
		||||
func (f *FakeClock) NewTicker(d time.Duration) Ticker {
 | 
			
		||||
	f.lock.Lock()
 | 
			
		||||
	defer f.lock.Unlock()
 | 
			
		||||
	tickTime := f.time.Add(d)
 | 
			
		||||
@@ -149,7 +145,9 @@ func (f *FakeClock) Tick(d time.Duration) <-chan time.Time {
 | 
			
		||||
		destChan:      ch,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return ch
 | 
			
		||||
	return &fakeTicker{
 | 
			
		||||
		c: ch,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Move clock by Duration, notify anyone that's called After, Tick, or NewTimer
 | 
			
		||||
@@ -242,8 +240,8 @@ func (*IntervalClock) NewTimer(d time.Duration) Timer {
 | 
			
		||||
 | 
			
		||||
// Unimplemented, will panic.
 | 
			
		||||
// TODO: make interval clock use FakeClock so this can be implemented.
 | 
			
		||||
func (*IntervalClock) Tick(d time.Duration) <-chan time.Time {
 | 
			
		||||
	panic("IntervalClock doesn't implement Tick")
 | 
			
		||||
func (*IntervalClock) NewTicker(d time.Duration) Ticker {
 | 
			
		||||
	panic("IntervalClock doesn't implement NewTicker")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (*IntervalClock) Sleep(d time.Duration) {
 | 
			
		||||
@@ -258,11 +256,6 @@ type Timer interface {
 | 
			
		||||
	Reset(d time.Duration) bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	_ = Timer(&realTimer{})
 | 
			
		||||
	_ = Timer(&fakeTimer{})
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// realTimer is backed by an actual time.Timer.
 | 
			
		||||
type realTimer struct {
 | 
			
		||||
	timer *time.Timer
 | 
			
		||||
@@ -325,3 +318,31 @@ func (f *fakeTimer) Reset(d time.Duration) bool {
 | 
			
		||||
 | 
			
		||||
	return active
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Ticker interface {
 | 
			
		||||
	C() <-chan time.Time
 | 
			
		||||
	Stop()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type realTicker struct {
 | 
			
		||||
	ticker *time.Ticker
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *realTicker) C() <-chan time.Time {
 | 
			
		||||
	return t.ticker.C
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *realTicker) Stop() {
 | 
			
		||||
	t.ticker.Stop()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type fakeTicker struct {
 | 
			
		||||
	c <-chan time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *fakeTicker) C() <-chan time.Time {
 | 
			
		||||
	return t.c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *fakeTicker) Stop() {
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,18 @@ import (
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	_ = Clock(RealClock{})
 | 
			
		||||
	_ = Clock(&FakeClock{})
 | 
			
		||||
	_ = Clock(&IntervalClock{})
 | 
			
		||||
 | 
			
		||||
	_ = Timer(&realTimer{})
 | 
			
		||||
	_ = Timer(&fakeTimer{})
 | 
			
		||||
 | 
			
		||||
	_ = Ticker(&realTicker{})
 | 
			
		||||
	_ = Ticker(&fakeTicker{})
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestFakeClock(t *testing.T) {
 | 
			
		||||
	startTime := time.Now()
 | 
			
		||||
	tc := NewFakeClock(startTime)
 | 
			
		||||
@@ -110,13 +122,13 @@ func TestFakeTick(t *testing.T) {
 | 
			
		||||
	if tc.HasWaiters() {
 | 
			
		||||
		t.Errorf("unexpected waiter?")
 | 
			
		||||
	}
 | 
			
		||||
	oneSec := tc.Tick(time.Second)
 | 
			
		||||
	oneSec := tc.NewTicker(time.Second).C()
 | 
			
		||||
	if !tc.HasWaiters() {
 | 
			
		||||
		t.Errorf("unexpected lack of waiter?")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	oneOhOneSec := tc.Tick(time.Second + time.Millisecond)
 | 
			
		||||
	twoSec := tc.Tick(2 * time.Second)
 | 
			
		||||
	oneOhOneSec := tc.NewTicker(time.Second + time.Millisecond).C()
 | 
			
		||||
	twoSec := tc.NewTicker(2 * time.Second).C()
 | 
			
		||||
	select {
 | 
			
		||||
	case <-oneSec:
 | 
			
		||||
		t.Errorf("unexpected channel read")
 | 
			
		||||
 
 | 
			
		||||
@@ -45,7 +45,7 @@ func newDelayingQueue(clock clock.Clock, name string) DelayingInterface {
 | 
			
		||||
	ret := &delayingType{
 | 
			
		||||
		Interface:       NewNamed(name),
 | 
			
		||||
		clock:           clock,
 | 
			
		||||
		heartbeat:       clock.Tick(maxWait),
 | 
			
		||||
		heartbeat:       clock.NewTicker(maxWait),
 | 
			
		||||
		stopCh:          make(chan struct{}),
 | 
			
		||||
		waitingForAddCh: make(chan *waitFor, 1000),
 | 
			
		||||
		metrics:         newRetryMetrics(name),
 | 
			
		||||
@@ -67,10 +67,7 @@ type delayingType struct {
 | 
			
		||||
	stopCh chan struct{}
 | 
			
		||||
 | 
			
		||||
	// heartbeat ensures we wait no more than maxWait before firing
 | 
			
		||||
	//
 | 
			
		||||
	// TODO: replace with Ticker (and add to clock) so this can be cleaned up.
 | 
			
		||||
	// clock.Tick will leak.
 | 
			
		||||
	heartbeat <-chan time.Time
 | 
			
		||||
	heartbeat clock.Ticker
 | 
			
		||||
 | 
			
		||||
	// waitingForAddCh is a buffered channel that feeds waitingForAdd
 | 
			
		||||
	waitingForAddCh chan *waitFor
 | 
			
		||||
@@ -138,6 +135,7 @@ func (pq waitForPriorityQueue) Peek() interface{} {
 | 
			
		||||
func (q *delayingType) ShutDown() {
 | 
			
		||||
	q.Interface.ShutDown()
 | 
			
		||||
	close(q.stopCh)
 | 
			
		||||
	q.heartbeat.Stop()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddAfter adds the given item to the work queue after the given delay
 | 
			
		||||
@@ -209,7 +207,7 @@ func (q *delayingType) waitingLoop() {
 | 
			
		||||
		case <-q.stopCh:
 | 
			
		||||
			return
 | 
			
		||||
 | 
			
		||||
		case <-q.heartbeat:
 | 
			
		||||
		case <-q.heartbeat.C():
 | 
			
		||||
			// continue the loop, which will add ready items
 | 
			
		||||
 | 
			
		||||
		case <-nextReadyAt:
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,7 @@ func TestRateLimitingQueue(t *testing.T) {
 | 
			
		||||
	delayingQueue := &delayingType{
 | 
			
		||||
		Interface:       New(),
 | 
			
		||||
		clock:           fakeClock,
 | 
			
		||||
		heartbeat:       fakeClock.Tick(maxWait),
 | 
			
		||||
		heartbeat:       fakeClock.NewTicker(maxWait),
 | 
			
		||||
		stopCh:          make(chan struct{}),
 | 
			
		||||
		waitingForAddCh: make(chan *waitFor, 1000),
 | 
			
		||||
		metrics:         newRetryMetrics(""),
 | 
			
		||||
 
 | 
			
		||||
@@ -62,10 +62,11 @@ func generateLogs(linesTotal int, duration time.Duration) {
 | 
			
		||||
	delay := duration / time.Duration(linesTotal)
 | 
			
		||||
	rand.Seed(time.Now().UnixNano())
 | 
			
		||||
 | 
			
		||||
	tick := time.Tick(delay)
 | 
			
		||||
	ticker := time.NewTicker(delay)
 | 
			
		||||
	defer ticker.Stop()
 | 
			
		||||
	for id := 0; id < linesTotal; id++ {
 | 
			
		||||
		glog.Info(generateLogLine(id))
 | 
			
		||||
		<-tick
 | 
			
		||||
		<-ticker.C
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user