/
signaler.go
80 lines (72 loc) · 1.5 KB
/
signaler.go
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
package registry
import (
"sync"
)
// signaler is a single-shot notification system through channel and callbacks
type signaler struct {
callbacks map[int]func()
signals []chan struct{}
mutex sync.Mutex
triggered bool
nextIndex int
}
// newSignaler returns a new signaler
func newSignaler() *signaler {
return &signaler{
callbacks: make(map[int]func()),
}
}
// Register registers a callback function on trigger and returns an uid
func (s *signaler) Register(f func()) int {
s.mutex.Lock()
if s.triggered {
s.mutex.Unlock()
f()
return -1
}
idx := s.nextIndex
s.nextIndex++
s.callbacks[idx] = f
s.mutex.Unlock()
return idx
}
// Unregister unregisters a callback function by uid
func (s *signaler) Unregister(id int) {
s.mutex.Lock()
defer s.mutex.Unlock()
delete(s.callbacks, id)
}
// Channel registers a channel reader.
func (s *signaler) Channel() chan struct{} {
res := make(chan struct{}, 1)
s.mutex.Lock()
if s.triggered {
res <- struct{}{}
} else {
s.signals = append(s.signals, res)
}
s.mutex.Unlock()
return res
}
// Signal triggers the signaler
func (s *signaler) Signal() {
s.mutex.Lock()
if s.triggered {
s.mutex.Unlock()
return
}
cb := s.callbacks
s.triggered = true
for _, s := range s.signals {
s <- struct{}{}
}
s.mutex.Unlock()
// callbacks must be invoked without holding the lock since they might Unregister()
for _, f := range cb {
f()
}
}
// Triggered check if the signaler was triggered
func (s *signaler) Triggered() bool {
return s.triggered
}