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
//! 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.
//!
//! 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`,
//! `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.
///
/// 🔴 **A liveness probe must not restart the window.** A bare connect-and-close
/// — what [`crate::uds::socket_is_serving`] does, and what `trusty-console`'s
/// service detector does on a poll loop — arrives here with `answered` false.
/// Counting it as activity would let a status page that polls every few seconds
/// keep an on-demand service resident forever, which is precisely the outcome
/// this module exists to prevent.
///
/// Test: `serve_until_idle_ignores_liveness_probes`,
/// `idle_tracker_counts_open_connections_and_restores_on_drop`.