Solo para leer y verificar. No puedes copiar, usar, modificar ni redistribuir este código. Aviso completo en la página principal.
tunnel/clock.go
| 1 | package main |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "math/rand/v2" |
| 6 | "time" |
| 7 | ) |
| 8 | |
| 9 | // clock is the node's source of time. Production uses the real one; the tests that cover hours (an outage of two hours, a grace of fifteen minutes, |
| 10 | // a JWKS that goes stale) drive their own by hand, so nothing in the node reads the wall clock for a decision except through it. Socket deadlines are |
| 11 | // not decisions of that kind: they stay on the real clock. |
| 12 | type clock struct { |
| 13 | now func() time.Time |
| 14 | afterFunc func(d time.Duration, f func()) (stop func() bool) |
| 15 | } |
| 16 | |
| 17 | func newClock(now func() time.Time, afterFunc func(d time.Duration, f func()) func() bool) clock { |
| 18 | if now == nil { |
| 19 | now = time.Now |
| 20 | } |
| 21 | if afterFunc == nil { |
| 22 | afterFunc = func(d time.Duration, f func()) func() bool { return time.AfterFunc(d, f).Stop } |
| 23 | } |
| 24 | return clock{now: now, afterFunc: afterFunc} |
| 25 | } |
| 26 | |
| 27 | func (c clock) Now() time.Time { return c.now() } |
| 28 | |
| 29 | // AfterFunc runs f once, d from now on this clock, in its own goroutine. The returned function stops it and reports whether it did so in time. |
| 30 | func (c clock) AfterFunc(d time.Duration, f func()) (stop func() bool) { |
| 31 | return c.afterFunc(max(d, 0), f) |
| 32 | } |
| 33 | |
| 34 | // sleep waits d on this clock; it reports false when ctx ended first. |
| 35 | func (c clock) sleep(ctx context.Context, d time.Duration) bool { |
| 36 | if ctx.Err() != nil { |
| 37 | return false |
| 38 | } |
| 39 | done := make(chan struct{}) |
| 40 | stop := c.AfterFunc(d, func() { close(done) }) |
| 41 | defer stop() |
| 42 | select { |
| 43 | case <-done: |
| 44 | return true |
| 45 | case <-ctx.Done(): |
| 46 | return false |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | // jitter spreads d by up to +/- pct percent, so that a fleet of nodes or a loop restarted together does not stay in step. |
| 51 | func jitter(d time.Duration, pct int) time.Duration { |
| 52 | if pct <= 0 || d <= 0 { |
| 53 | return d |
| 54 | } |
| 55 | span := int64(d) * int64(pct) / 100 |
| 56 | return d + time.Duration(rand.Int64N(2*span+1)-span) |
| 57 | } |
| 58 | |
| 59 | // backoff is the delay after the n-th failure in a row (n from 1): the policy's minimum, doubled each time, up to its maximum. |
| 60 | func backoff(n int) time.Duration { |
| 61 | d := secs(policyData.Timing.BackoffMinS) |
| 62 | limit := secs(policyData.Timing.BackoffMaxS) |
| 63 | for i := 1; i < n && d < limit; i++ { |
| 64 | d *= 2 |
| 65 | } |
| 66 | return min(d, limit) |
| 67 | } |