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
//! The flag a shutdown signal sets, and the handler that sets it.
//!
//! `yodb serve` turns its loop against an [`AtomicBool`] and always has. Nothing
//! set it, so the only way to stop the server was to kill it, which on Unix
//! leaves the socket file behind for the next start to clear up and on every
//! platform means the loop never gets to run its own way out. That was fine
//! while there was nothing to run out of, and it stops being fine the moment
//! there is a file to close: the same flag that ends the loop here is what a
//! flush on the way out will hang off in M5.
//!
//! # What a handler is allowed to do
//!
//! One store to a static, and nothing else. A signal arrives on whatever thread
//! the kernel picks and interrupts it wherever it happens to be, so a handler
//! that allocates can deadlock against an allocator the interrupted thread was
//! already inside. A relaxed store to an `AtomicBool` is one instruction and is
//! on the short list POSIX says is safe from a handler.
//!
//! The exception is the second signal. Somebody pressing Ctrl-C twice is saying
//! the polite way is taking too long, and answering that means leaving without
//! unwinding, so it is `_exit` and not `exit`: the first is on the safe list and
//! the second runs destructors that the interrupted thread may be halfway
//! through already.
//!
//! # Which signals
//!
//! `SIGINT` and `SIGTERM` on Unix, which are Ctrl-C and what a service manager
//! sends. Not `SIGHUP`, because Redis ignores it and a server that dies when the
//! terminal it was started from closes is a surprise nobody asked for.
//!
//! On Windows the console handler covers Ctrl-C, Ctrl-Break, the window being
//! closed and the machine shutting down. The last two come with a deadline of a
//! few seconds before the process is killed anyway, which is the same promise as
//! `SIGTERM` with a service manager's timer behind it.
use ;
/// Set once a shutdown signal has arrived.
///
/// A static rather than something handed around, because a signal handler takes
/// no argument and cannot be a closure over anything.
static STOP: AtomicBool = new;
/// The flag the serve loop turns against.
/// Whether a signal is what ended the loop, as opposed to an error.
/// Ask for [`stop`] to be set when the operating system says to stop.
///
/// Called once, before the loop starts. A second call installs the same handler
/// again, which is harmless and is not something any caller has a reason to do.
/// Nowhere to hang a handler, so the flag is never set and the loop runs until
/// something kills it, which is where every platform was before this.
// Everything in here raises a signal and looks at what the handler did, so it
// is unix only, and the cfg goes on the module rather than on the test. On the
// test it leaves `use super::*` behind with nothing using it, which is a warning
// on Windows and a warning is an error in CI.