-
Notifications
You must be signed in to change notification settings - Fork 0
/
signal.go
122 lines (97 loc) · 2.06 KB
/
signal.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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package xcobra
import (
"context"
"os"
"os/signal"
"sync/atomic"
)
var gsigh = newSighandler(context.Background())
func WithSignal(ctx context.Context, signals ...os.Signal) (context.Context, context.CancelFunc) {
return gsigh.withSignal(ctx, signals...)
}
type sighandler struct {
args chan sig
queues map[os.Signal][]*cancelFunc
ch chan os.Signal
}
type sig struct {
fn context.CancelFunc
sigs []os.Signal
stop chan<- context.CancelFunc
}
func newSighandler(ctx context.Context) *sighandler {
sigh := &sighandler{
args: make(chan sig),
queues: make(map[os.Signal][]*cancelFunc),
ch: make(chan os.Signal),
}
go sigh.join(ctx)
return sigh
}
func (sigh *sighandler) withSignal(ctx context.Context, signals ...os.Signal) (context.Context, context.CancelFunc) {
ctx, cancel := context.WithCancel(ctx)
stop := make(chan context.CancelFunc, 1)
sigh.args <- sig{
fn: cancel,
sigs: signals,
stop: stop,
}
return ctx, <-stop
}
func (sigh *sighandler) stop() {
signal.Stop(sigh.ch)
for _, queue := range sigh.queues {
for _, cancel := range queue {
cancel.do()
}
}
sigh.queues = nil
}
func (sigh *sighandler) join(ctx context.Context) {
defer sigh.stop()
for {
select {
case <-ctx.Done():
return
case s := <-sigh.ch:
fn, queue := (*cancelFunc)(nil), sigh.queues[s]
for len(queue) != 0 {
if fn, queue = queue[0], queue[1:]; fn.do() {
break
}
}
sigh.queues[s] = queue
case arg, ok := <-sigh.args:
if !ok {
return
}
fn := &cancelFunc{
fn: arg.fn,
}
n := len(sigh.queues)
for _, s := range arg.sigs {
sigh.queues[s] = append(sigh.queues[s], fn)
}
if len(sigh.queues) > n {
signal.Stop(sigh.ch)
var sigs []os.Signal
for s := range sigh.queues {
sigs = append(sigs, s)
}
signal.Notify(sigh.ch, sigs...)
}
arg.stop <- func() { fn.do() }
}
}
}
type cancelFunc struct {
done int32
fn context.CancelFunc
}
func (cfn *cancelFunc) do() bool {
if !atomic.CompareAndSwapInt32(&cfn.done, 0, 1) {
return false
}
cfn.fn()
return true
}