Source File
fastclock.go
Belonging Package
github.com/dlclark/regexp2
package regexp2import ()// fasttime holds a time value (ticks since clock initialization)type fasttime int64// fastclock provides a fast clock implementation.//// A background goroutine periodically stores the current time// into an atomic variable.//// A deadline can be quickly checked for expiration by comparing// its value to the clock stored in the atomic variable.//// The goroutine automatically stops once clockEnd is reached.// (clockEnd covers the largest deadline seen so far + some// extra time). This ensures that if regexp2 with timeouts// stops being used we will stop background work.type fastclock struct {// instances of atomicTime must be at the start of the struct (or at least 64-bit aligned)// otherwise 32-bit architectures will paniccurrent atomicTime // Current time (approximate)clockEnd atomicTime // When clock updater is supposed to stop (>= any existing deadline)// current and clockEnd can be read via atomic loads.// Reads and writes of other fields require mu to be held.mu sync.Mutexstart time.Time // Time corresponding to fasttime(0)running bool // Is a clock updater running?}var fast fastclock// reached returns true if current time is at or past t.func ( fasttime) () bool {return fast.current.read() >=}// makeDeadline returns a time that is approximately time.Now().Add(d)func makeDeadline( time.Duration) fasttime {// Increase the deadline since the clock we are reading may be// just about to tick forwards.:= fast.current.read() + durationToTicks(+clockPeriod)// Start or extend clock if necessary.if > fast.clockEnd.read() {extendClock()}return}// extendClock ensures that clock is live and will run until at least end.func extendClock( fasttime) {fast.mu.Lock()defer fast.mu.Unlock()if fast.start.IsZero() {fast.start = time.Now()}// Extend the running time to cover end as well as a bit of slop.if := + durationToTicks(time.Second); > fast.clockEnd.read() {fast.clockEnd.write()}// Start clock if necessaryif !fast.running {fast.running = truego runClock()}}// stop the timeout clock in the background// should only used for unit tests to abandon the background goroutinefunc stopClock() {fast.mu.Lock()if fast.running {fast.clockEnd.write(fasttime(0))}fast.mu.Unlock()// pause until not running// get and release the lock:= truefor {time.Sleep(clockPeriod / 2)fast.mu.Lock()= fast.runningfast.mu.Unlock()}}func durationToTicks( time.Duration) fasttime {// Downscale nanoseconds to approximately a millisecond so that we can avoid// overflow even if the caller passes in math.MaxInt64.return fasttime() >> 20}const DefaultClockPeriod = 100 * time.Millisecond// clockPeriod is the approximate interval between updates of approximateClock.var clockPeriod = DefaultClockPeriodfunc runClock() {fast.mu.Lock()defer fast.mu.Unlock()for fast.current.read() <= fast.clockEnd.read() {// Unlock while sleeping.fast.mu.Unlock()time.Sleep(clockPeriod)fast.mu.Lock():= durationToTicks(time.Since(fast.start))fast.current.write()}fast.running = false}type atomicTime struct{ v int64 } // Should change to atomic.Int64 when we can use go 1.19func ( *atomicTime) () fasttime { return fasttime(atomic.LoadInt64(&.v)) }func ( *atomicTime) ( fasttime) { atomic.StoreInt64(&.v, int64()) }
![]() |
The pages are generated with Golds v0.8.2. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |