WIP update deps, sql builder instead of jet
This commit is contained in:
9
vendor/github.com/jonboulle/clockwork/README.md
generated
vendored
9
vendor/github.com/jonboulle/clockwork/README.md
generated
vendored
@@ -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()
|
||||
|
||||
|
||||
19
vendor/github.com/jonboulle/clockwork/SECURITY.md
generated
vendored
Normal file
19
vendor/github.com/jonboulle/clockwork/SECURITY.md
generated
vendored
Normal file
@@ -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.
|
||||
264
vendor/github.com/jonboulle/clockwork/clockwork.go
generated
vendored
264
vendor/github.com/jonboulle/clockwork/clockwork.go
generated
vendored
@@ -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)
|
||||
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.
|
||||
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) },
|
||||
// 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()
|
||||
}
|
||||
|
||||
afterFunc: afterfunc,
|
||||
}
|
||||
fc.set(ft, d)
|
||||
// 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
|
||||
})
|
||||
}
|
||||
|
||||
148
vendor/github.com/jonboulle/clockwork/context.go
generated
vendored
148
vendor/github.com/jonboulle/clockwork/context.go
generated
vendored
@@ -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
|
||||
}
|
||||
|
||||
35
vendor/github.com/jonboulle/clockwork/ticker.go
generated
vendored
35
vendor/github.com/jonboulle/clockwork/ticker.go
generated
vendored
@@ -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 }
|
||||
|
||||
40
vendor/github.com/jonboulle/clockwork/timer.go
generated
vendored
40
vendor/github.com/jonboulle/clockwork/timer.go
generated
vendored
@@ -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,13 +35,30 @@ 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) },
|
||||
|
||||
afterFunc: afterfunc,
|
||||
}
|
||||
return ft
|
||||
}
|
||||
|
||||
func (f *fakeTimer) Stop() bool {
|
||||
return f.stop()
|
||||
}
|
||||
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 {
|
||||
@@ -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 }
|
||||
|
||||
Reference in New Issue
Block a user