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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
use crate;
use ControlFlow;
use Arc;
use AtomicBool;
use ;
/// What a watcher applies when SIGINT/SIGTERM arrives.
///
/// A runtime-owned watcher applies the whole shutdown request, so an OS signal
/// disposes of admission exactly as `runtime::request_shutdown` does — the
/// "shutdown implies scope closing" definition stays in `RuntimeInner`, and no
/// spawn is admitted after a SIGTERM that a `request_shutdown` would have
/// refused. The public wrapper owns no runtime, so it fires the caller's latch
/// alone.
pub
/// The OS signal sources a watcher observes.
///
/// Registered synchronously, before the watcher is spawned or admitted, so a
/// signal raised immediately afterwards is not missed by a handler that had
/// not been installed yet.
pub
/// Spawn an async task that watches for OS signals.
///
/// The returned handle is caller-owned: this task is not admitted to the
/// runtime's root scope, because the scope cannot single-own a handle it also
/// hands back. The runtime's own watcher is scope-owned instead, and it
/// applies the whole shutdown request rather than the caller's latch alone.
///
/// The task has TWO completion paths, and they are not interchangeable:
///
/// - an OS SIGINT/SIGTERM arrives — `shutdown` is set to true and waiters are
/// notified;
/// - inside a Camber runtime, the root scope closes or shutdown latches — the
/// watch simply ends and `shutdown` is left untouched, so the watcher does
/// not outlive the runtime that hosts it.
///
/// A caller must therefore READ `shutdown` rather than infer it from the
/// handle resolving: completion alone does not mean a signal arrived. Spawned
/// outside a Camber runtime the captured signals are inert, so only an OS
/// signal ever ends the watch.
///
/// The `notify` half is a wake, not a record. Firing wakes the waiters
/// registered at that moment and stores no permit, so a `notified()` registered
/// after the signal lands never resolves — the flag is the durable half and the
/// notification is lossy for late waiters. Register the wait BEFORE checking
/// `shutdown`, then read the flag: a fire cannot slip between the two, whereas
/// checking first and then waiting can miss it forever.
///
/// # Panics
///
/// Panics if called outside a Tokio runtime context. Both halves of this
/// function need one and neither degrades: the OS handlers are installed
/// synchronously here, and `tokio::signal::unix::signal` panics with no reactor
/// on the current thread; `tokio::spawn` then panics with no runtime to admit
/// the watcher to. Call it from inside `runtime::run`, or from any other Tokio
/// runtime context.
/// Watch for SIGINT/SIGTERM until one arrives or a lifecycle signal ends the
/// watch.
///
/// The OS signal is an external edge that may never fire, so the watch needs
/// its own exit arm: without one a scope-owned watcher would never let the
/// drain finish. `guard` is that arm, and the one definition of racing work
/// against the lifecycle signals — a lifecycle exit leaves the shutdown
/// request unapplied, because no OS signal arrived to apply.
///
/// The signal is recorded here because this is the only place that knows it.
/// `RuntimeInner::request_shutdown` applies a request without caring where it
/// came from, so without this line a SIGTERM'd process logs a drain that starts
/// for no stated reason.
///
/// The record covers the repeat signal too. This watcher is one-shot, and
/// tokio's handler stays installed for the process lifetime once registered, so
/// a second Ctrl-C is swallowed rather than ending the process — the operator
/// waiting on a slow drain needs to be told that up front.
pub async