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
//! The shared deadline-arbitration core: "how long until the deadline, sleep
//! that long, then try to claim the arbiter" — reused by the three deadline
//! watchdogs ([`stream::arm_stream_deadline`](super::stream), scripted's
//! `arm_scripted_deadline`, and `drive_to_exit_inner`'s deadline arm) so the
//! CAS protocol lives in exactly one place. Only the teardown that follows a
//! win (graceful/group/pid/scripted-kill, or `select!` integration) stays at
//! each call site — that part is genuinely different per site.
//!
//! Both sides of the single-word arbiter are funnelled through the two claim
//! helpers here — [`claim_timed_out`] (a fired deadline) and [`claim_exited`] (a
//! natural reap) — so the CAS from `TS_PENDING` and its memory ordering live in
//! one place, and the [`loom_model`] suite can exhaustively check that the two
//! can never both win.
// The arbiter atomic comes from the crate's `cfg(loom)`-swappable sync layer
// (`std::sync::atomic` in ordinary builds, loom's model under the standalone loom
// harness) so `loom_model` below can permute the deadline-vs-reap CAS race. See
// `crate::sync`.
use crate;
// The wait side is the arbiter's only impure part (tokio's clock + sleep); the
// loom models exercise only the pure `claim_*` CAS below, so this and
// `wait_deadline_and_claim` are `cfg(not(loom))` — that lets the standalone loom
// harness (`loom/`) `#[path]`-include this file without tokio.
use Duration;
// `tokio::time::Instant` (not `std::time::Instant`): the remaining budget is
// computed as `limit - started.elapsed()` and slept out via `tokio::time::sleep`
// below, so the anchor must share tokio's clock. Otherwise, under a paused
// runtime, virtual time already burned (e.g. by a readiness probe that advanced
// the clock without arming this watchdog) would not count against the limit, and
// a late arm would silently re-grant the full budget — diverging from the live
// clock the hermetic tests are meant to mirror. `sys::graceful` anchors its own
// deadline on the same clock for the same reason.
use Instant;
use ;
/// Wait out the remaining time until `started + limit`, then try to claim the
/// timeout arbiter by CASing `flag` from `TS_PENDING` to `TS_TIMED_OUT`.
///
/// Anchored to `started` (not "now") so a late arm can't re-grant the full
/// limit. `started` is a [`tokio::time::Instant`] so that remaining-budget
/// arithmetic shares the clock the `sleep` below runs on (see the import note).
/// Returns `true` when this call won the race and now owns the
/// "timed out vs exited" decision (the caller may proceed to kill/teardown
/// and publish `TimedOut`); `false` when a natural reap already claimed the
/// arbiter first — the caller must not kill the child or publish its own
/// outcome.
pub async
/// Claim the timeout arbiter for a **fired deadline**: `compare_exchange` `flag`
/// from `TS_PENDING` to `TS_TIMED_OUT`. Returns `true` when this call won the
/// arbiter (the caller owns the "timed out vs exited" decision and may kill and
/// publish `TimedOut`); `false` when a natural reap already claimed it — the
/// caller must not kill or publish. The winning side is unique: at most one of
/// `claim_timed_out` / [`claim_exited`] can succeed from `TS_PENDING`.
///
/// `AcqRel` on success / `Relaxed` on failure: the release publishes the decision
/// to the finisher that later reads the arbiter (`classify_timed_out`), and the
/// acquire pairs with the losing side's release.
pub
/// Claim the timeout arbiter for a **natural reap**: `compare_exchange` `flag`
/// from `TS_PENDING` to `TS_EXITED`. Returns `true` when this call won (a clean
/// exit is recorded); `false` when a fired deadline already claimed
/// `TS_TIMED_OUT` first — the run stays `TimedOut`. The counterpart to
/// [`claim_timed_out`]; the two race on the same word and exactly one can win.
///
/// Same `AcqRel`/`Relaxed` ordering and rationale as [`claim_timed_out`].
pub
/// Loom model-checking suite for the deadline arbiter (run under `--cfg loom`;
/// see [`crate::sync`]).
///
/// The arbiter is a single `AtomicU8` two watchdogs and the natural reap race on:
/// a fired deadline claims `TS_TIMED_OUT`, a clean exit claims `TS_EXITED`, both
/// from `TS_PENDING`. Loom exhaustively permutes that race and asserts the two
/// core invariants: **no double claim** (at most one side wins, so exactly one of
/// "kill + publish TimedOut" and "record a clean exit" ever fires — never both),
/// and **no missed timeout** (with no reap, the deadline claim always wins, so a
/// real timeout is never silently swallowed). A finisher's later
/// `classify`-style acquire load always observes the unique winner's value.