1
1
package notify
2
2
3
3
import (
4
+ "context"
4
5
"sort"
5
6
"sync"
6
7
"time"
7
8
)
8
9
9
10
// Notifier calls a Condition at most once for each count in countdown.
10
11
type Notifier struct {
12
+ ctx context.Context
13
+ cancel context.CancelFunc
14
+ pollDone chan struct {}
15
+
11
16
lock sync.Mutex
12
17
condition Condition
13
18
notifiedAt map [time.Duration ]bool
@@ -28,11 +33,14 @@ type Condition func(now time.Time) (deadline time.Time, callback func())
28
33
// Notify is a convenience function that initializes a new Notifier
29
34
// with the given condition, interval, and countdown.
30
35
// It is the responsibility of the caller to call close to stop polling.
31
- func Notify (cond Condition , interval time.Duration , countdown ... time.Duration ) (close func ()) {
36
+ func Notify (cond Condition , interval time.Duration , countdown ... time.Duration ) (closeFunc func ()) {
32
37
notifier := New (cond , countdown ... )
33
38
ticker := time .NewTicker (interval )
34
39
go notifier .Poll (ticker .C )
35
- return ticker .Stop
40
+ return func () {
41
+ ticker .Stop ()
42
+ _ = notifier .Close ()
43
+ }
36
44
}
37
45
38
46
// New returns a Notifier that calls cond once every time it polls.
@@ -45,7 +53,11 @@ func New(cond Condition, countdown ...time.Duration) *Notifier {
45
53
return ct [i ] < ct [j ]
46
54
})
47
55
56
+ ctx , cancel := context .WithCancel (context .Background ())
48
57
n := & Notifier {
58
+ ctx : ctx ,
59
+ cancel : cancel ,
60
+ pollDone : make (chan struct {}),
49
61
countdown : ct ,
50
62
condition : cond ,
51
63
notifiedAt : make (map [time.Duration ]bool ),
@@ -57,13 +69,29 @@ func New(cond Condition, countdown ...time.Duration) *Notifier {
57
69
// Poll polls once immediately, and then once for every value from ticker.
58
70
// Poll exits when ticker is closed.
59
71
func (n * Notifier ) Poll (ticker <- chan time.Time ) {
72
+ defer close (n .pollDone )
73
+
60
74
// poll once immediately
61
75
n .pollOnce (time .Now ())
62
- for t := range ticker {
63
- n .pollOnce (t )
76
+ for {
77
+ select {
78
+ case <- n .ctx .Done ():
79
+ return
80
+ case t , ok := <- ticker :
81
+ if ! ok {
82
+ return
83
+ }
84
+ n .pollOnce (t )
85
+ }
64
86
}
65
87
}
66
88
89
+ func (n * Notifier ) Close () error {
90
+ n .cancel ()
91
+ <- n .pollDone
92
+ return nil
93
+ }
94
+
67
95
func (n * Notifier ) pollOnce (tick time.Time ) {
68
96
n .lock .Lock ()
69
97
defer n .lock .Unlock ()
0 commit comments