forked from eikmadsen/shutdown
-
Notifications
You must be signed in to change notification settings - Fork 0
/
shutdown.go
162 lines (150 loc) · 4.35 KB
/
shutdown.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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright (c) 2015 Klaus Post, 2023 Eik Madsen, released under MIT License. See LICENSE file.
// Package shutdown provides management of your shutdown process.
//
// The package will enable you to get notifications for your application and handle the shutdown process.
//
// # See more information about the how to use it in the README.md file
//
// Package home: https://github.com/Vivino/go-shutdown
package shutdown
// Stage contains stage information.
// Valid values for this are exported as variables StageN.
type Stage struct {
n int
}
// LogPrinter is an interface for writing logging information.
// The writer must handle concurrent writes.
type LogPrinter interface {
Printf(format string, v ...interface{})
}
// Internal notifier
type iNotifier struct {
n Notifier
calledFrom string
}
type fnNotify struct {
client Notifier
internal iNotifier
cancel chan struct{}
}
type logWrapper struct {
w func(format string, v ...interface{})
}
func (l logWrapper) Printf(format string, v ...interface{}) {
l.w(format, v...)
}
// Notifier is a channel, that will be sent a channel
// once the application shuts down.
// When you have performed your shutdown actions close the channel you are given.
type Notifier struct {
c chan chan struct{}
m *Manager
}
// Valid returns true if it can be used as a notifier. If false shutdown has already started
func (n Notifier) Valid() bool {
return n.c != nil && n.m != nil
}
// Notify returns a channel to listen to for shutdown events.
func (n Notifier) Notify() <-chan chan struct{} {
return n.c
}
// Cancel a Notifier.
// This will remove a notifier from the shutdown queue,
// and it will not be signalled when shutdown starts.
// If the shutdown has already started this will not have any effect,
// but a goroutine will wait for the notifier to be triggered.
func (s Notifier) Cancel() {
if !s.Valid() {
return
}
s.m.srM.RLock()
if s.m.shutdownRequested.Load() {
s.m.srM.RUnlock()
// Wait until we get the notification and close it:
go func() {
v := <-s.c
close(v)
}()
return
}
s.m.srM.RUnlock()
s.m.sqM.Lock()
var a chan chan struct{}
var b chan chan struct{}
a = s.c
for n, sdq := range s.m.shutdownQueue {
for i, qi := range sdq {
b = qi.n.c
if a == b {
s.m.shutdownQueue[n] = append(s.m.shutdownQueue[n][:i], s.m.shutdownQueue[n][i+1:]...)
}
}
for i, fn := range s.m.shutdownFnQueue[n] {
b = fn.client.c
if a == b {
// Find the matching internal and remove that.
for i := range s.m.shutdownQueue[n] {
b = s.m.shutdownQueue[n][i].n.c
if fn.internal.n.c == b {
s.m.shutdownQueue[n] = append(s.m.shutdownQueue[n][:i], s.m.shutdownQueue[n][i+1:]...)
break
}
}
// Cancel, so the goroutine exits.
close(fn.cancel)
// Remove this
s.m.shutdownFnQueue[n] = append(s.m.shutdownFnQueue[n][:i], s.m.shutdownFnQueue[n][i+1:]...)
}
}
}
s.m.sqM.Unlock()
}
// CancelWait will cancel a Notifier, or wait for it to become active if shutdown has been started.
// This will remove a notifier from the shutdown queue, and it will not be signalled when shutdown starts.
// If the notifier is invalid (requested after its stage has started), it will return at once.
// If the shutdown has already started, this will wait for the notifier to be called and close it.
func (s Notifier) CancelWait() {
if !s.Valid() {
return
}
s.m.sqM.Lock()
var a chan chan struct{}
var b chan chan struct{}
a = s.c
for n, sdq := range s.m.shutdownQueue {
for i, qi := range sdq {
b = qi.n.c
if a == b {
s.m.shutdownQueue[n] = append(s.m.shutdownQueue[n][:i], s.m.shutdownQueue[n][i+1:]...)
}
}
for i, fn := range s.m.shutdownFnQueue[n] {
b = fn.client.c
if a == b {
// Find the matching internal and remove that.
for i := range s.m.shutdownQueue[n] {
b = s.m.shutdownQueue[n][i].n.c
if fn.internal.n.c == b {
s.m.shutdownQueue[n] = append(s.m.shutdownQueue[n][:i], s.m.shutdownQueue[n][i+1:]...)
break
}
}
// Cancel, so the goroutine exits.
close(fn.cancel)
// Remove this
s.m.shutdownFnQueue[n] = append(s.m.shutdownFnQueue[n][:i], s.m.shutdownFnQueue[n][i+1:]...)
}
}
}
s.m.srM.Lock()
if s.m.shutdownRequested.Load() {
s.m.sqM.Unlock()
s.m.srM.Unlock()
// Wait until we get the notification and close it:
v := <-s.c
close(v)
return
}
s.m.srM.Unlock()
s.m.sqM.Unlock()
}