diff --git a/go.mod b/go.mod index 76c19e8ae..6fccabc27 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.0 github.com/jackc/pgconn v1.14.3 - github.com/jonboulle/clockwork v0.4.0 + github.com/jonboulle/clockwork v0.5.0 github.com/mattn/go-sqlite3 v1.14.24 github.com/prometheus/client_golang v1.20.5 github.com/robfig/cron/v3 v3.0.1 diff --git a/go.sum b/go.sum index aaec47abc..af9bc18a5 100644 --- a/go.sum +++ b/go.sum @@ -535,8 +535,8 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs= github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI= -github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= -github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= +github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= +github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= diff --git a/vendor/github.com/jonboulle/clockwork/README.md b/vendor/github.com/jonboulle/clockwork/README.md index 42970da80..5e9d472ea 100644 --- a/vendor/github.com/jonboulle/clockwork/README.md +++ b/vendor/github.com/jonboulle/clockwork/README.md @@ -36,6 +36,7 @@ Now you can easily test `myFunc` with a `FakeClock`: ```go func TestMyFunc(t *testing.T) { + ctx := context.Background() c := clockwork.NewFakeClock() // Start our sleepy function @@ -46,8 +47,12 @@ func TestMyFunc(t *testing.T) { wg.Done() }() - // Ensure we wait until myFunc is sleeping - c.BlockUntil(1) + // Ensure we wait until myFunc is waiting on the clock. + // Use a context to avoid blocking forever if something + // goes wrong. + ctx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + c.BlockUntilContext(ctx, 1) assertState() diff --git a/vendor/github.com/jonboulle/clockwork/SECURITY.md b/vendor/github.com/jonboulle/clockwork/SECURITY.md new file mode 100644 index 000000000..0efcad9f6 --- /dev/null +++ b/vendor/github.com/jonboulle/clockwork/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +If you have discovered a security vulnerability in this project, please report it +privately. **Do not disclose it as a public issue.** This gives me time to work with you +to fix the issue before public exposure, reducing the chance that the exploit will be +used before a patch is released. + +You may submit the report in the following ways: + +- send an email to ???@???; and/or +- send a [private vulnerability report](https://github.com/jonboulle/clockwork/security/advisories/new) + +Please provide the following information in your report: + +- A description of the vulnerability and its impact +- How to reproduce the issue + +This project is maintained by a single maintainer on a reasonable-effort basis. As such, +please give me 90 days to work on a fix before public exposure. diff --git a/vendor/github.com/jonboulle/clockwork/clockwork.go b/vendor/github.com/jonboulle/clockwork/clockwork.go index 3206b36e4..85a993467 100644 --- a/vendor/github.com/jonboulle/clockwork/clockwork.go +++ b/vendor/github.com/jonboulle/clockwork/clockwork.go @@ -1,8 +1,10 @@ +// Package clockwork contains a simple fake clock for Go. package clockwork import ( "context" - "sort" + "errors" + "slices" "sync" "time" ) @@ -14,49 +16,18 @@ type Clock interface { Sleep(d time.Duration) Now() time.Time Since(t time.Time) time.Duration + Until(t time.Time) time.Duration NewTicker(d time.Duration) Ticker NewTimer(d time.Duration) Timer AfterFunc(d time.Duration, f func()) Timer } -// FakeClock provides an interface for a clock which can be manually advanced -// through time. -// -// FakeClock maintains a list of "waiters," which consists of all callers -// waiting on the underlying clock (i.e. Tickers and Timers including callers of -// Sleep or After). Users can call BlockUntil to block until the clock has an -// expected number of waiters. -type FakeClock interface { - Clock - // Advance advances the FakeClock to a new point in time, ensuring any existing - // waiters are notified appropriately before returning. - Advance(d time.Duration) - // BlockUntil blocks until the FakeClock has the given number of waiters. - BlockUntil(waiters int) -} - // NewRealClock returns a Clock which simply delegates calls to the actual time // package; it should be used by packages in production. func NewRealClock() Clock { return &realClock{} } -// NewFakeClock returns a FakeClock implementation which can be -// manually advanced through time for testing. The initial time of the -// FakeClock will be the current system time. -// -// Tests that require a deterministic time must use NewFakeClockAt. -func NewFakeClock() FakeClock { - return NewFakeClockAt(time.Now()) -} - -// NewFakeClockAt returns a FakeClock initialised at the given time.Time. -func NewFakeClockAt(t time.Time) FakeClock { - return &fakeClock{ - time: t, - } -} - type realClock struct{} func (rc *realClock) After(d time.Duration) <-chan time.Time { @@ -75,6 +46,10 @@ func (rc *realClock) Since(t time.Time) time.Duration { return rc.Now().Sub(t) } +func (rc *realClock) Until(t time.Time) time.Duration { + return t.Sub(rc.Now()) +} + func (rc *realClock) NewTicker(d time.Duration) Ticker { return realTicker{time.NewTicker(d)} } @@ -87,7 +62,14 @@ func (rc *realClock) AfterFunc(d time.Duration, f func()) Timer { return realTimer{time.AfterFunc(d, f)} } -type fakeClock struct { +// FakeClock provides an interface for a clock which can be manually advanced +// through time. +// +// FakeClock maintains a list of "waiters," which consists of all callers +// waiting on the underlying clock (i.e. Tickers and Timers including callers of +// Sleep or After). Users can call BlockUntil to block until the clock has an +// expected number of waiters. +type FakeClock struct { // l protects all attributes of the clock, including all attributes of all // waiters and blockers. l sync.RWMutex @@ -96,11 +78,27 @@ type fakeClock struct { time time.Time } +// NewFakeClock returns a FakeClock implementation which can be +// manually advanced through time for testing. The initial time of the +// FakeClock will be the current system time. +// +// Tests that require a deterministic time must use NewFakeClockAt. +func NewFakeClock() *FakeClock { + return NewFakeClockAt(time.Now()) +} + +// NewFakeClockAt returns a FakeClock initialised at the given time.Time. +func NewFakeClockAt(t time.Time) *FakeClock { + return &FakeClock{ + time: t, + } +} + // blocker is a caller of BlockUntil. type blocker struct { count int - // ch is closed when the underlying clock has the specificed number of blockers. + // ch is closed when the underlying clock has the specified number of blockers. ch chan struct{} } @@ -111,23 +109,23 @@ type expirer interface { expire(now time.Time) (next *time.Duration) // Get and set the expiration time. - expiry() time.Time - setExpiry(time.Time) + expiration() time.Time + setExpiration(time.Time) } // After mimics [time.After]; it waits for the given duration to elapse on the // fakeClock, then sends the current time on the returned channel. -func (fc *fakeClock) After(d time.Duration) <-chan time.Time { +func (fc *FakeClock) After(d time.Duration) <-chan time.Time { return fc.NewTimer(d).Chan() } // Sleep blocks until the given duration has passed on the fakeClock. -func (fc *fakeClock) Sleep(d time.Duration) { +func (fc *FakeClock) Sleep(d time.Duration) { <-fc.After(d) } // Now returns the current time of the fakeClock -func (fc *fakeClock) Now() time.Time { +func (fc *FakeClock) Now() time.Time { fc.l.RLock() defer fc.l.RUnlock() return fc.time @@ -135,61 +133,73 @@ func (fc *fakeClock) Now() time.Time { // Since returns the duration that has passed since the given time on the // fakeClock. -func (fc *fakeClock) Since(t time.Time) time.Duration { +func (fc *FakeClock) Since(t time.Time) time.Duration { return fc.Now().Sub(t) } +// Until returns the duration that has to pass from the given time on the fakeClock +// to reach the given time. +func (fc *FakeClock) Until(t time.Time) time.Duration { + return t.Sub(fc.Now()) +} + // NewTicker returns a Ticker that will expire only after calls to -// fakeClock.Advance() have moved the clock past the given duration. -func (fc *fakeClock) NewTicker(d time.Duration) Ticker { - var ft *fakeTicker - ft = &fakeTicker{ - firer: newFirer(), - d: d, - reset: func(d time.Duration) { fc.set(ft, d) }, - stop: func() { fc.stop(ft) }, +// FakeClock.Advance() have moved the clock past the given duration. +// +// The duration d must be greater than zero; if not, NewTicker will panic. +func (fc *FakeClock) NewTicker(d time.Duration) Ticker { + // Maintain parity with + // https://cs.opensource.google/go/go/+/refs/tags/go1.20.3:src/time/tick.go;l=23-25 + if d <= 0 { + panic(errors.New("non-positive interval for NewTicker")) } - fc.set(ft, d) + ft := newFakeTicker(fc, d) + fc.l.Lock() + defer fc.l.Unlock() + fc.setExpirer(ft, d) return ft } // NewTimer returns a Timer that will fire only after calls to // fakeClock.Advance() have moved the clock past the given duration. -func (fc *fakeClock) NewTimer(d time.Duration) Timer { - return fc.newTimer(d, nil) +func (fc *FakeClock) NewTimer(d time.Duration) Timer { + t, _ := fc.newTimer(d, nil) + return t } // AfterFunc mimics [time.AfterFunc]; it returns a Timer that will invoke the // given function only after calls to fakeClock.Advance() have moved the clock // past the given duration. -func (fc *fakeClock) AfterFunc(d time.Duration, f func()) Timer { - return fc.newTimer(d, f) -} - -// newTimer returns a new timer, using an optional afterFunc. -func (fc *fakeClock) newTimer(d time.Duration, afterfunc func()) *fakeTimer { - var ft *fakeTimer - ft = &fakeTimer{ - firer: newFirer(), - reset: func(d time.Duration) bool { - fc.l.Lock() - defer fc.l.Unlock() - // fc.l must be held across the calls to stopExpirer & setExpirer. - stopped := fc.stopExpirer(ft) - fc.setExpirer(ft, d) - return stopped - }, - stop: func() bool { return fc.stop(ft) }, - - afterFunc: afterfunc, - } - fc.set(ft, d) +func (fc *FakeClock) AfterFunc(d time.Duration, f func()) Timer { + t, _ := fc.newTimer(d, f) + return t +} + +// newTimer returns a new timer using an optional afterFunc and the time that +// timer expires. +func (fc *FakeClock) newTimer(d time.Duration, afterfunc func()) (*fakeTimer, time.Time) { + ft := newFakeTimer(fc, afterfunc) + fc.l.Lock() + defer fc.l.Unlock() + fc.setExpirer(ft, d) + return ft, ft.expiration() +} + +// newTimerAtTime is like newTimer, but uses a time instead of a duration. +// +// It is used to ensure FakeClock's lock is held constant through calling +// fc.After(t.Sub(fc.Now())). It should not be exposed externally. +func (fc *FakeClock) newTimerAtTime(t time.Time, afterfunc func()) *fakeTimer { + ft := newFakeTimer(fc, afterfunc) + fc.l.Lock() + defer fc.l.Unlock() + fc.setExpirer(ft, t.Sub(fc.time)) return ft } // Advance advances fakeClock to a new point in time, ensuring waiters and // blockers are notified appropriately before returning. -func (fc *fakeClock) Advance(d time.Duration) { +func (fc *FakeClock) Advance(d time.Duration) { fc.l.Lock() defer fc.l.Unlock() end := fc.time.Add(d) @@ -198,38 +208,34 @@ func (fc *fakeClock) Advance(d time.Duration) { // // We don't iterate because the callback of the waiter might register a new // waiter, so the list of waiters might change as we execute this. - for len(fc.waiters) > 0 && !end.Before(fc.waiters[0].expiry()) { + for len(fc.waiters) > 0 && !end.Before(fc.waiters[0].expiration()) { w := fc.waiters[0] fc.waiters = fc.waiters[1:] - // Use the waiter's expriation as the current time for this expiration. - now := w.expiry() + // Use the waiter's expiration as the current time for this expiration. + now := w.expiration() fc.time = now if d := w.expire(now); d != nil { - // Set the new exipration if needed. + // Set the new expiration if needed. fc.setExpirer(w, *d) } } fc.time = end } -// BlockUntil blocks until the fakeClock has the given number of waiters. +// BlockUntil blocks until the FakeClock has the given number of waiters. // -// Prefer BlockUntilContext, which offers context cancellation to prevent -// deadlock. +// Prefer BlockUntilContext in new code, which offers context cancellation to +// prevent deadlock. // -// Deprecation warning: This function might be deprecated in later versions. -func (fc *fakeClock) BlockUntil(n int) { - b := fc.newBlocker(n) - if b == nil { - return - } - <-b.ch +// Deprecated: New code should prefer BlockUntilContext. +func (fc *FakeClock) BlockUntil(n int) { + fc.BlockUntilContext(context.TODO(), n) } // BlockUntilContext blocks until the fakeClock has the given number of waiters // or the context is cancelled. -func (fc *fakeClock) BlockUntilContext(ctx context.Context, n int) error { +func (fc *FakeClock) BlockUntilContext(ctx context.Context, n int) error { b := fc.newBlocker(n) if b == nil { return nil @@ -243,7 +249,7 @@ func (fc *fakeClock) BlockUntilContext(ctx context.Context, n int) error { } } -func (fc *fakeClock) newBlocker(n int) *blocker { +func (fc *FakeClock) newBlocker(n int) *blocker { fc.l.Lock() defer fc.l.Unlock() // Fast path: we already have >= n waiters. @@ -260,7 +266,7 @@ func (fc *fakeClock) newBlocker(n int) *blocker { } // stop stops an expirer, returning true if the expirer was stopped. -func (fc *fakeClock) stop(e expirer) bool { +func (fc *FakeClock) stop(e expirer) bool { fc.l.Lock() defer fc.l.Unlock() return fc.stopExpirer(e) @@ -269,81 +275,45 @@ func (fc *fakeClock) stop(e expirer) bool { // stopExpirer stops an expirer, returning true if the expirer was stopped. // // The caller must hold fc.l. -func (fc *fakeClock) stopExpirer(e expirer) bool { - for i, t := range fc.waiters { - if t == e { - // Remove element, maintaining order. - copy(fc.waiters[i:], fc.waiters[i+1:]) - fc.waiters[len(fc.waiters)-1] = nil - fc.waiters = fc.waiters[:len(fc.waiters)-1] - return true - } +func (fc *FakeClock) stopExpirer(e expirer) bool { + idx := slices.Index(fc.waiters, e) + if idx == -1 { + return false } - return false -} - -// set sets an expirer to expire at a future point in time. -func (fc *fakeClock) set(e expirer, d time.Duration) { - fc.l.Lock() - defer fc.l.Unlock() - fc.setExpirer(e, d) + // Remove element, maintaining order, setting inaccessible elements to nil so + // they can be garbage collected. + copy(fc.waiters[idx:], fc.waiters[idx+1:]) + fc.waiters[len(fc.waiters)-1] = nil + fc.waiters = fc.waiters[:len(fc.waiters)-1] + return true } // setExpirer sets an expirer to expire at a future point in time. // // The caller must hold fc.l. -func (fc *fakeClock) setExpirer(e expirer, d time.Duration) { +func (fc *FakeClock) setExpirer(e expirer, d time.Duration) { if d.Nanoseconds() <= 0 { - // special case - trigger immediately, never reset. + // Special case for timers with duration <= 0: trigger immediately, never + // reset. // - // TODO: Explain what cases this covers. + // Tickers never get here, they panic if d is < 0. e.expire(fc.time) return } // Add the expirer to the set of waiters and notify any blockers. - e.setExpiry(fc.time.Add(d)) + e.setExpiration(fc.time.Add(d)) fc.waiters = append(fc.waiters, e) - sort.Slice(fc.waiters, func(i int, j int) bool { - return fc.waiters[i].expiry().Before(fc.waiters[j].expiry()) + slices.SortFunc(fc.waiters, func(a, b expirer) int { + return a.expiration().Compare(b.expiration()) }) - // Notify blockers of our new waiter. - var blocked []*blocker + // Notify blockers of our new waiter. count := len(fc.waiters) - for _, b := range fc.blockers { + fc.blockers = slices.DeleteFunc(fc.blockers, func(b *blocker) bool { if b.count <= count { close(b.ch) - continue + return true } - blocked = append(blocked, b) - } - fc.blockers = blocked -} - -// firer is used by fakeTimer and fakeTicker used to help implement expirer. -type firer struct { - // The channel associated with the firer, used to send expriation times. - c chan time.Time - - // The time when the firer expires. Only meaningful if the firer is currently - // one of a fakeClock's waiters. - exp time.Time -} - -func newFirer() firer { - return firer{c: make(chan time.Time, 1)} -} - -func (f *firer) Chan() <-chan time.Time { - return f.c -} - -// expiry implements expirer. -func (f *firer) expiry() time.Time { - return f.exp -} - -// setExpiry implements expirer. -func (f *firer) setExpiry(t time.Time) { - f.exp = t + return false + }) } diff --git a/vendor/github.com/jonboulle/clockwork/context.go b/vendor/github.com/jonboulle/clockwork/context.go index edbb368f0..59242619e 100644 --- a/vendor/github.com/jonboulle/clockwork/context.go +++ b/vendor/github.com/jonboulle/clockwork/context.go @@ -2,24 +2,168 @@ package clockwork import ( "context" + "fmt" + "sync" + "time" ) // contextKey is private to this package so we can ensure uniqueness here. This // type identifies context values provided by this package. type contextKey string -// keyClock provides a clock for injecting during tests. If absent, a real clock should be used. +// keyClock provides a clock for injecting during tests. If absent, a real clock +// should be used. var keyClock = contextKey("clock") // clockwork.Clock // AddToContext creates a derived context that references the specified clock. +// +// Be aware this doesn't change the behavior of standard library functions, such +// as [context.WithTimeout] or [context.WithDeadline]. For this reason, users +// should prefer passing explicit [clockwork.Clock] variables rather can passing +// the clock via the context. func AddToContext(ctx context.Context, clock Clock) context.Context { return context.WithValue(ctx, keyClock, clock) } -// FromContext extracts a clock from the context. If not present, a real clock is returned. +// FromContext extracts a clock from the context. If not present, a real clock +// is returned. func FromContext(ctx context.Context) Clock { if clock, ok := ctx.Value(keyClock).(Clock); ok { return clock } return NewRealClock() } + +// ErrFakeClockDeadlineExceeded is the error returned by [context.Context] when +// the deadline passes on a context which uses a [FakeClock]. +// +// It wraps a [context.DeadlineExceeded] error, i.e.: +// +// // The following is true for any Context whose deadline has been exceeded, +// // including contexts made with clockwork.WithDeadline or clockwork.WithTimeout. +// +// errors.Is(ctx.Err(), context.DeadlineExceeded) +// +// // The following can only be true for contexts made +// // with clockwork.WithDeadline or clockwork.WithTimeout. +// +// errors.Is(ctx.Err(), clockwork.ErrFakeClockDeadlineExceeded) +var ErrFakeClockDeadlineExceeded error = fmt.Errorf("clockwork.FakeClock: %w", context.DeadlineExceeded) + +// WithDeadline returns a context with a deadline based on a [FakeClock]. +// +// The returned context ignores parent cancelation if the parent was cancelled +// with a [context.DeadlineExceeded] error. Any other error returned by the +// parent is treated normally, cancelling the returned context. +// +// If the parent is cancelled with a [context.DeadlineExceeded] error, the only +// way to then cancel the returned context is by calling the returned +// context.CancelFunc. +func WithDeadline(parent context.Context, clock Clock, t time.Time) (context.Context, context.CancelFunc) { + if fc, ok := clock.(*FakeClock); ok { + return newFakeClockContext(parent, t, fc.newTimerAtTime(t, nil).Chan()) + } + return context.WithDeadline(parent, t) +} + +// WithTimeout returns a context with a timeout based on a [FakeClock]. +// +// The returned context follows the same behaviors as [WithDeadline]. +func WithTimeout(parent context.Context, clock Clock, d time.Duration) (context.Context, context.CancelFunc) { + if fc, ok := clock.(*FakeClock); ok { + t, deadline := fc.newTimer(d, nil) + return newFakeClockContext(parent, deadline, t.Chan()) + } + return context.WithTimeout(parent, d) +} + +// fakeClockContext implements context.Context, using a fake clock for its +// deadline. +// +// It ignores parent cancellation if the parent is cancelled with +// context.DeadlineExceeded. +type fakeClockContext struct { + parent context.Context + deadline time.Time // The user-facing deadline based on the fake clock's time. + + // Tracks timeout/deadline cancellation. + timerDone <-chan time.Time + + // Tracks manual calls to the cancel function. + cancel func() // Closes cancelCalled wrapped in a sync.Once. + cancelCalled chan struct{} + + // The user-facing data from the context.Context interface. + ctxDone chan struct{} // Returned by Done(). + err error // nil until ctxDone is ready to be closed. +} + +func newFakeClockContext(parent context.Context, deadline time.Time, timer <-chan time.Time) (context.Context, context.CancelFunc) { + cancelCalled := make(chan struct{}) + ctx := &fakeClockContext{ + parent: parent, + deadline: deadline, + timerDone: timer, + cancelCalled: cancelCalled, + ctxDone: make(chan struct{}), + cancel: sync.OnceFunc(func() { + close(cancelCalled) + }), + } + ready := make(chan struct{}, 1) + go ctx.runCancel(ready) + <-ready // Wait until the cancellation goroutine is running. + return ctx, ctx.cancel +} + +func (c *fakeClockContext) Deadline() (time.Time, bool) { + return c.deadline, true +} + +func (c *fakeClockContext) Done() <-chan struct{} { + return c.ctxDone +} + +func (c *fakeClockContext) Err() error { + <-c.Done() // Don't return the error before it is ready. + return c.err +} + +func (c *fakeClockContext) Value(key any) any { + return c.parent.Value(key) +} + +// runCancel runs the fakeClockContext's cancel goroutine and returns the +// fakeClockContext's cancel function. +// +// fakeClockContext is then cancelled when any of the following occur: +// +// - The fakeClockContext.done channel is closed by its timer. +// - The returned CancelFunc is executed. +// - The fakeClockContext's parent context is cancelled with an error other +// than context.DeadlineExceeded. +func (c *fakeClockContext) runCancel(ready chan struct{}) { + parentDone := c.parent.Done() + + // Close ready when done, just in case the ready signal races with other + // branches of our select statement below. + defer close(ready) + + for c.err == nil { + select { + case <-c.timerDone: + c.err = ErrFakeClockDeadlineExceeded + case <-c.cancelCalled: + c.err = context.Canceled + case <-parentDone: + c.err = c.parent.Err() + + case ready <- struct{}{}: + // Signals the cancellation goroutine has begun, in an attempt to minimize + // race conditions related to goroutine startup time. + ready = nil // This case statement can only fire once. + } + } + close(c.ctxDone) + return +} diff --git a/vendor/github.com/jonboulle/clockwork/ticker.go b/vendor/github.com/jonboulle/clockwork/ticker.go index b68e4d777..aa56952e7 100644 --- a/vendor/github.com/jonboulle/clockwork/ticker.go +++ b/vendor/github.com/jonboulle/clockwork/ticker.go @@ -19,7 +19,12 @@ func (r realTicker) Chan() <-chan time.Time { } type fakeTicker struct { - firer + // 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. @@ -30,13 +35,27 @@ type fakeTicker struct { d time.Duration } -func (f *fakeTicker) Reset(d time.Duration) { - f.reset(d) +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 (f *fakeTicker) Stop() { - f.stop() -} +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. @@ -46,3 +65,7 @@ func (f *fakeTicker) expire(now time.Time) *time.Duration { } return &f.d } + +func (f *fakeTicker) expiration() time.Time { return f.exp } + +func (f *fakeTicker) setExpiration(t time.Time) { f.exp = t } diff --git a/vendor/github.com/jonboulle/clockwork/timer.go b/vendor/github.com/jonboulle/clockwork/timer.go index 6f928b3dd..e7e1d4001 100644 --- a/vendor/github.com/jonboulle/clockwork/timer.go +++ b/vendor/github.com/jonboulle/clockwork/timer.go @@ -18,9 +18,14 @@ func (r realTimer) Chan() <-chan time.Time { } type fakeTimer struct { - firer + // The channel associated with the firer, used to send expiration times. + c chan time.Time - // reset and stop provide the implmenetation of the respective exported + // The time when the firer expires. Only meaningful if the firer 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) bool stop func() bool @@ -30,14 +35,31 @@ type fakeTimer struct { afterFunc func() } -func (f *fakeTimer) Reset(d time.Duration) bool { - return f.reset(d) -} +func newFakeTimer(fc *FakeClock, afterfunc func()) *fakeTimer { + var ft *fakeTimer + ft = &fakeTimer{ + c: make(chan time.Time, 1), + reset: func(d time.Duration) bool { + fc.l.Lock() + defer fc.l.Unlock() + // fc.l must be held across the calls to stopExpirer & setExpirer. + stopped := fc.stopExpirer(ft) + fc.setExpirer(ft, d) + return stopped + }, + stop: func() bool { return fc.stop(ft) }, -func (f *fakeTimer) Stop() bool { - return f.stop() + afterFunc: afterfunc, + } + return ft } +func (f *fakeTimer) Chan() <-chan time.Time { return f.c } + +func (f *fakeTimer) Reset(d time.Duration) bool { return f.reset(d) } + +func (f *fakeTimer) Stop() bool { return f.stop() } + func (f *fakeTimer) expire(now time.Time) *time.Duration { if f.afterFunc != nil { go f.afterFunc() @@ -51,3 +73,7 @@ func (f *fakeTimer) expire(now time.Time) *time.Duration { } return nil } + +func (f *fakeTimer) expiration() time.Time { return f.exp } + +func (f *fakeTimer) setExpiration(t time.Time) { f.exp = t } diff --git a/vendor/modules.txt b/vendor/modules.txt index f929e4ec9..51eb19c25 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -751,8 +751,8 @@ github.com/jinzhu/now # github.com/jmespath/go-jmespath v0.4.0 ## explicit; go 1.14 github.com/jmespath/go-jmespath -# github.com/jonboulle/clockwork v0.4.0 -## explicit; go 1.15 +# github.com/jonboulle/clockwork v0.5.0 +## explicit; go 1.21 github.com/jonboulle/clockwork # github.com/josharian/intern v1.0.0 ## explicit; go 1.5