-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Expand file tree
/
Copy pathtimer.go
More file actions
90 lines (82 loc) · 2.69 KB
/
timer.go
File metadata and controls
90 lines (82 loc) · 2.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// Copyright 2016 The Cockroach Authors.
//
// Use of this software is governed by the CockroachDB Software License
// included in the /LICENSE file.
package timeutil
import (
"sync"
"sync/atomic"
"time"
)
var timeTimerPool sync.Pool
// activeSynctestBubbles counts the number of active synctest bubbles.
// When non-zero, the timer pool is bypassed: Get returns nil (forcing a
// fresh allocation) and Put is a no-op. This prevents non-bubble timers
// from being reused inside the bubble (which would prevent the bubble's
// fake clock from advancing) and prevents bubble timers from leaking
// out.
//
// We intentionally do not gate on buildutil.CrdbTestBuild here. That
// constant is tied to the crdb_test build tag, which is not set in
// local IDE builds. Skipping the check there would silently break
// synctest bubbles. The atomic load is cheap enough to keep
// unconditionally.
var activeSynctestBubbles atomic.Int32
// The Timer type represents a single event. When the Timer expires,
// the current time will be sent on Timer.C.
//
// This timer implementation is an abstraction around the standard library's
// time.Timer that uses a pool of stopped timers to reduce allocations.
//
// Note that unlike the standard library's Timer type, this Timer will
// not begin counting down until Reset is called for the first time, as
// there is no constructor function. The zero value for Timer is ready
// to use.
type Timer struct {
timer *time.Timer
// C is a local "copy" of timer.C that can be used in a select case before
// the timer has been initialized (via Reset).
C <-chan time.Time
Read bool
}
// AsTimerI returns the Timer as a TimerI. This is helpful
// to write code that accepts a Timer in production and a manual
// timer in tests.
func (t *Timer) AsTimerI() TimerI {
return (*timer)(t)
}
// Reset changes the timer to expire after duration d and returns
// the new value of the timer.
func (t *Timer) Reset(d time.Duration) {
if t.timer == nil {
var pooled any
if activeSynctestBubbles.Load() == 0 {
pooled = timeTimerPool.Get()
}
switch pooled {
case nil:
t.timer = time.NewTimer(d)
default:
t.timer = pooled.(*time.Timer)
t.timer.Reset(d)
}
t.C = t.timer.C
return
}
t.timer.Reset(d)
}
// Stop prevents the Timer from firing. It returns true if the call stops
// the timer, false if the timer has already expired, been stopped previously,
// or had never been initialized with a call to Timer.Reset. Stop does not
// close the channel, to prevent a read from succeeding incorrectly.
func (t *Timer) Stop() bool {
var res bool
if t.timer != nil {
res = t.timer.Stop()
if activeSynctestBubbles.Load() == 0 {
timeTimerPool.Put(t.timer)
}
}
*t = Timer{}
return res
}