@@ -2,39 +2,58 @@ package time
2
2
3
3
import (
4
4
"time"
5
-
6
- "github.com/searKing/golang/go/sync/atomic"
7
5
)
8
6
9
7
// https://github.com/golang/go/issues/27169
10
8
// Timer to fix time: Timer.Stop documentation example easily leads to deadlocks
11
9
type Timer struct {
12
10
* time.Timer
13
- chanConsumed atomic.Bool
14
11
}
15
12
16
- // saw channel read, must be called after receiving value from timer chan
17
- // an example case is AfterFunc bellow.
18
- func (t * Timer ) ChanConsumed () {
19
- t .chanConsumed .Store (true )
13
+ // Stop prevents the Timer from firing, with the channel drained.
14
+ // Stop ensures the channel is empty after a call to Stop.
15
+ // Stop == std [Stop + drain]
16
+ // It returns true if the call stops the timer, false if the timer has already
17
+ // expired or been stopped.
18
+ // Stop does not close the channel, to prevent a read from the channel succeeding
19
+ // incorrectly.
20
+ func (t * Timer ) Stop () bool {
21
+ if t .Timer == nil {
22
+ panic ("time: Stop called on uninitialized Timer" )
23
+ }
24
+
25
+ active := t .Timer .Stop ()
26
+ if ! active {
27
+ // drain the channel, prevents the Timer from blocking on Send to t.C by sendTime, t.C is reused.
28
+ // The underlying Timer is not recovered by the garbage collector until the timer fires.
29
+ // consume the channel only once for the channel can be triggered only one time at most before Stop is called.
30
+ L:
31
+ for {
32
+ select {
33
+ case _ , ok := <- t .Timer .C :
34
+ if ! ok {
35
+ break L
36
+ }
37
+ default :
38
+ break L
39
+ }
40
+ }
41
+ }
42
+ return active
20
43
}
21
44
22
45
// Reset changes the timer to expire after duration d.
46
+ // Reset can be invoked anytime, which enhances std time.Reset
47
+ // Reset == std [Stop + drain + Reset]
23
48
// It returns true if the timer had been active, false if the timer had
24
49
// expired or been stopped.
25
- // Reset can be invoked anytime, which enhances std time.Reset
26
- // that should be invoked only on stopped or expired timers with drained channels,
27
50
func (t * Timer ) Reset (d time.Duration ) bool {
28
- ret := t .Stop ()
29
- if ! ret && ! t .chanConsumed .Load () {
30
- // drain the channel, prevents the Timer from blocking on Send to t.C by sendTime, t.C is reused.
31
- // The underlying Timer is not recovered by the garbage collector until the timer fires.
32
- // consume the channel only once for the channel can be triggered only one time at most before Stop is called.
33
- <- t .C
51
+ if t .Timer == nil {
52
+ panic ("time: Reset called on uninitialized Timer" )
34
53
}
54
+ active := t .Stop ()
35
55
t .Timer .Reset (d )
36
- t .chanConsumed .Store (false )
37
- return ret
56
+ return active
38
57
}
39
58
40
59
func NewTimer (d time.Duration ) * Timer {
@@ -56,7 +75,6 @@ func After(d time.Duration) <-chan time.Time {
56
75
func AfterFunc (d time.Duration , f func ()) * Timer {
57
76
t := & Timer {}
58
77
t .Timer = time .AfterFunc (d , func () {
59
- t .ChanConsumed ()
60
78
f ()
61
79
})
62
80
return t
0 commit comments