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
//! Idle-exit accounting for an on-demand UDS service (#6350, ADR-0032).
//!
//! Why: a service that clients start on demand has to end on its own, or the
//! first request of the day leaves a process resident until the machine
//! reboots — which is the resident daemon it was supposed to replace, minus the
//! launchd unit that would at least have restarted it. The exit has to be
//! driven by the serve loop rather than by a timer the service arms itself,
//! because only the loop knows whether a connection is open, and killing a
//! process mid-`analyze.diagnostics` is a worse failure than never reclaiming
//! it.
//!
//! What: [`IdleTracker`] counts open connections and stamps the moment the last
//! one closed; [`IdleTracker::expired`] is the future
//! [`super::serve_until_idle`] races against `accept`. A connection that
//! ANSWERED a request refreshes the stamp; one that connected and closed
//! without writing a frame does not — see [`IdleGuard::answered`] for why that
//! distinction is the whole point.
//!
//! #6621 widened "does not": an answer to a method the router marked with
//! [`super::RpcRouter::mark_liveness`] leaves the stamp alone too. A monitor
//! that dials a health METHOD rather than connecting and closing was otherwise
//! indistinguishable from a working client.
//!
//! Test: `tests.rs` — `serve_until_idle_exits_when_the_window_elapses`,
//! `serve_until_idle_is_reset_by_an_answered_request`,
//! `serve_until_idle_ignores_liveness_probes`,
//! `serve_until_idle_ignores_a_registered_liveness_method`,
//! `serve_until_idle_is_held_open_by_a_non_liveness_call`,
//! `idle_tracker_counts_open_connections_and_restores_on_drop`.
use Arc;
use ;
use Duration;
use Mutex;
use Instant;
/// Open-connection count and last-activity stamp for one serve loop.
///
/// Why: the two facts an idle policy needs are "is anyone connected right now"
/// and "how long since the last one finished", and they have to be read
/// together — a service with a connection open is not idle no matter how old
/// the stamp is, and a service whose stamp is fresh is not idle even with
/// nothing connected.
///
/// What: an [`AtomicUsize`] of live connections plus the [`Instant`] at which
/// the count last returned to zero having answered something. The stamp is a
/// `tokio::time::Instant` rather than `std::time::Instant` so a test can drive
/// the whole policy under `tokio::time::pause()` if it wants to; the tests here
/// use real short windows instead, because the accept loop's `sleep` is what is
/// actually being verified.
///
/// Test: see the module docs.
/// Live-connection guard held for the lifetime of one accepted connection.
///
/// Why: see [`IdleTracker::connection_opened`]. The `answered` flag is set only
/// by the serve loop, after the connection has produced a response.
///
/// What: decrements the open count on drop and, when the connection ANSWERED
/// something and was the last one open, restarts the idle window.
///
/// 🔴 **Monitoring must not restart the window**, in either of its two shapes.
/// A bare connect-and-close — what [`crate::uds::socket_is_serving`] does —
/// arrives here with `answered` false. An answer to a method the router marked
/// with [`super::RpcRouter::mark_liveness`] reaches the serve loop as
/// [`super::Served::Answered`] with `liveness` true, and the loop does not call
/// [`Self::answered`] for it (#6621). Counting either as activity lets a status
/// page polling every few seconds keep an on-demand service resident forever —
/// observed, not hypothetical: a 15s console poll against a 600s window held one
/// `trusty-analyze` process up for 46 hours.
///
/// Test: `serve_until_idle_ignores_liveness_probes`,
/// `serve_until_idle_ignores_a_registered_liveness_method`,
/// `idle_tracker_counts_open_connections_and_restores_on_drop`.