switch back to upstream

Signed-off-by: Christian Richter <c.richter@opencloud.eu>
This commit is contained in:
Christian Richter
2025-04-28 13:43:21 +02:00
parent 1a79e1aa7a
commit 6c6033a830
16 changed files with 904 additions and 291 deletions
+52 -53
View File
@@ -1,72 +1,71 @@
package clockwork
import (
"time"
)
import "time"
// Ticker provides an interface which can be used instead of directly
// using the ticker within the time module. The real-time ticker t
// provides ticks through t.C which becomes now t.Chan() to make
// this channel requirement definable in this interface.
// Ticker provides an interface which can be used instead of directly using
// [time.Ticker]. The real-time ticker t provides ticks through t.C which
// becomes t.Chan() to make this channel requirement definable in this
// interface.
type Ticker interface {
Chan() <-chan time.Time
Reset(d time.Duration)
Stop()
}
type realTicker struct{ *time.Ticker }
func (rt *realTicker) Chan() <-chan time.Time {
return rt.C
func (r realTicker) Chan() <-chan time.Time {
return r.C
}
type fakeTicker struct {
c chan time.Time
stop chan bool
clock FakeClock
period time.Duration
// The channel associated with the firer, used to send expiration times.
c chan time.Time
// The time when the ticker expires. Only meaningful if the ticker is currently
// one of a FakeClock's waiters.
exp time.Time
// reset and stop provide the implementation of the respective exported
// functions.
reset func(d time.Duration)
stop func()
// The duration of the ticker.
d time.Duration
}
func (ft *fakeTicker) Chan() <-chan time.Time {
return ft.c
func newFakeTicker(fc *FakeClock, d time.Duration) *fakeTicker {
var ft *fakeTicker
ft = &fakeTicker{
c: make(chan time.Time, 1),
d: d,
reset: func(d time.Duration) {
fc.l.Lock()
defer fc.l.Unlock()
ft.d = d
fc.setExpirer(ft, d)
},
stop: func() { fc.stop(ft) },
}
return ft
}
func (ft *fakeTicker) Stop() {
ft.stop <- true
func (f *fakeTicker) Chan() <-chan time.Time { return f.c }
func (f *fakeTicker) Reset(d time.Duration) { f.reset(d) }
func (f *fakeTicker) Stop() { f.stop() }
func (f *fakeTicker) expire(now time.Time) *time.Duration {
// Never block on expiration.
select {
case f.c <- now:
default:
}
return &f.d
}
// runTickThread initializes a background goroutine to send the tick time to the ticker channel
// after every period. Tick events are discarded if the underlying ticker channel does not have
// enough capacity.
func (ft *fakeTicker) runTickThread() {
nextTick := ft.clock.Now().Add(ft.period)
next := ft.clock.After(ft.period)
go func() {
for {
select {
case <-ft.stop:
return
case <-next:
// We send the time that the tick was supposed to occur at.
tick := nextTick
// Before sending the tick, we'll compute the next tick time and star the clock.After call.
now := ft.clock.Now()
// First, figure out how many periods there have been between "now" and the time we were
// supposed to have trigged, then advance over all of those.
skipTicks := (now.Sub(tick) + ft.period - 1) / ft.period
nextTick = nextTick.Add(skipTicks * ft.period)
// Now, keep advancing until we are past now. This should happen at most once.
for !nextTick.After(now) {
nextTick = nextTick.Add(ft.period)
}
// Figure out how long between now and the next scheduled tick, then wait that long.
remaining := nextTick.Sub(now)
next = ft.clock.After(remaining)
// Finally, we can actually send the tick.
select {
case ft.c <- tick:
default:
}
}
}
}()
}
func (f *fakeTicker) expiration() time.Time { return f.exp }
func (f *fakeTicker) setExpiration(t time.Time) { f.exp = t }