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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
// Copyright (c) Mike Grier
//! Whether a thread-pool object still owes the drain that `Drop` would do for it.
//!
//! Every teardown in this crate blocks to drain, per [the teardown-drains
//! decision](../../../DESIGN-NOTES.md#teardown-drains). Each type also offers a
//! synchronous close performing that same drain at a point the caller chooses,
//! because where `Drop` lands in the caller's control flow is often accidental.
//! This records which of the two is going to happen, so `Drop` can report having
//! been left the work.
use ;
/// Whether an undischarged drain obligation is outstanding.
///
/// The polarity is "owed", not "drained", and that is the whole reason this is a
/// type rather than a bare `bool` on each struct. An object that was created and
/// never armed owes nothing -- there is no callback for `Drop` to wait on -- so
/// the quiet state has to be the initial one. Recording "the caller called the
/// close" instead would make every never-armed object report.
///
/// # Ordering
///
/// Every access is `Relaxed`, which is sufficient here and would not be if this
/// guarded data. It guards none: no load of this flag decides whether to touch
/// memory, only whether to emit a trace record. And the read happens in `Drop`,
/// which takes `&mut self`, so every earlier `&self` call that set or cleared it
/// is already ordered before the read by exclusive access -- there is no race to
/// order, only a value to observe.
pub ;
/// Fail fast, if the `fail-fast` feature is on and a drain was owed.
///
/// Called at the **very end** of a `Drop`, after the drain, the close, and the
/// context free. That placement is the whole constraint: a panic unwinds, so
/// anything left after it would be skipped, and skipping the close would leave
/// the pool able to dispatch into a context nothing frees. The panic reports
/// the violation; it must not cause one. See [The teardown fail-fast is a
/// default-off Cargo feature](../../../DESIGN-NOTES.md#fail-fast-is-a-default-off-feature).
///
/// `owed` is read *before* the drain by every caller, because a callback
/// dispatched during the drain settles the obligation -- asking afterwards
/// would find nothing owed on exactly the objects that owed something.
///
/// One site for the message rather than five, so the wording a consumer sees
/// cannot differ by which type they dropped.
///
/// # What this does not catch, stated rather than left to be discovered
///
/// The flag records an **outstanding arming**, not "this `Drop` blocked". A
/// dispatch settles it for a wait and a one-shot timer -- the pool is no longer
/// watching once the callback has entered -- but `Drop` still waits for that
/// callback to finish. So dropping while a callback is executing performs a
/// blocking drain and reports nothing, and a callback that blocks for a long
/// time is exactly the case a consumer would most want told about.
///
/// This is a consequence of what the flag answers, not an oversight in it:
/// asking instead "is a callback running right now" is a question only the pool
/// can answer, and only by being asked at the moment `Drop` has already
/// committed to waiting. The feature therefore catches the obligation a caller
/// *left* -- an object still armed at `Drop` -- and not every teardown that
/// happens to block. `CleanupGroup` is outside it altogether: its teardown
/// drains, deliberately, but it carries no obligation flag and so never
/// reports.
pub
/// Fail fast, if the `fail-fast` feature is on and a cancellation went
/// untracked.
///
/// `tracked` is what the registration reported -- `owe_repair` with `self-heal`
/// on, `owe_repair_claimed` with it off, which is why this names them rather
/// than linking: the feature-off `Registration` deliberately has no
/// `owe_repair`, so a link to one would be unresolvable in exactly the
/// configuration the `no self-heal` CI job documents. A
/// `false` means three allocations failed for one pool -- at the object's
/// creation, at any later registration by something else on it, and at the
/// retry the cancellation itself makes -- so the pool was cancelled with
/// nothing that will ever repair it. That is the single case
/// `try_cancel_pending`'s safety claim does not cover, and this is where a
/// build that asked to be told about it finds out.
///
/// **Called at a point where unwinding skips nothing that must happen**, which
/// is the same constraint [`fail_fast_if_owed`] carries and for the same
/// reason. `CleanupGroup::close_members_cancelling` marks its members between
/// the native release and the loop that frees their contexts; a panic raised
/// inside that loop would unwind past the frees and leak every context. So
/// callers collect the answer during the cancellation and call this afterwards,
/// never in the middle of one.
///
/// One site for the message rather than one per caller, so the wording cannot
/// drift between the per-object path and the group path.
pub
/// Fail fast, if the `fail-fast` feature is on, when a pool will not take a
/// repair.
///
/// Reached only from the self-heal timer, and only when a repair has sat with a
/// pool unstarted past the overdue threshold more than once with a reattempt in
/// between. At that point the crate has submitted the one action measured to
/// recover the stall, twice, and the pool has dispatched neither -- so it has no
/// remaining idea whether that pool can be recovered, which is the condition
/// this feature exists to stop on rather than continue past.
///
/// **This aborts rather than unwinds, which the other two fail-fast sites do
/// not.** It runs on the healer's timer callback, and a panic escaping an
/// `extern "system"` trampoline is turned into an abort by Rust -- see
/// `tests/callback_panic_aborts.rs`. The message still reaches stderr, because
/// the panic hook runs before unwinding begins. That is the honest outcome
/// here: the condition being reported is that a thread pool in this process has
/// stopped dispatching and would not take the repair, so there is no thread to
/// unwind to that is known to still work.
///
/// With the feature off the caller records the episode and re-submits, which it
/// will go on doing for as long as the pool stays stuck.
///
/// # Availability
///
/// Gated on `self-heal`, because the self-heal timer is its only caller: with
/// that feature off there is no repair to go unstarted and nothing to report.
pub
/// The event every type emits when it finds an obligation owed at `Drop`.
///
/// One constant rather than the string at five call sites, so the tag a consumer
/// filters on cannot drift between them.
pub const DROP_OBLIGATION_OWED: &str = "drop-obligation-owed";