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
//! **One channel's lifetime**: taken up, served, and taken up again (yog's
//! `docs/REMOTE.md` §5.3, DESIGN §3.8, bl-916d).
//!
//! **The no-reconnect ruling is reversed at the channel and kept at the
//! process.** Its premise was sound and its conclusion did not follow:
//! supervision restarts a *process*, and the failure this exists for does not
//! kill one. REMOTE §1's canonical box sleeps, changes network and crosses a
//! relay switch; TCP drops; the channel's conversation ends with its sentence;
//! and the foot stays healthy, serving whatever other engines it holds. A
//! multi-entry box loses one channel of several and there is no exit code for a
//! supervisor to see, while the engine believes this box is gone (presence is
//! connection RAM, correctly) and the box believes it is serving. **So a foot
//! redials its own channels, and still exits when it cannot be a foot at all** —
//! which is the part supervision was always the right owner of.
//!
//! **It is not a session resume and there is nothing to resume.** A redial
//! makes a *new* channel: presence re-forms as it does for any fresh
//! connection, the advertisement rides the connection already, registration is
//! durable engine-side, and an invocation in flight when the wire died is the
//! engine's mailbox lease rather than this loop's. DESIGN §3.8 states what
//! that costs and why it is the right price.
//!
//! **One thing crosses, and it is an act rather than a memory** (REMOTE §5.6
//! ruling 1, `run::held`): a capture this box computed and the wire swallowed
//! on the way back. The next dial posts it first and is done with it — landed,
//! refused or dropped — before it reads. That is not a resumed session and not
//! a ledger: one capture, on this loop's stack, spent on the next channel's
//! first act.
//!
//! **It must not hammer, so the wait is the whole of this file's decision.**
//! Three numbers and one line of arithmetic ([`next`]), kept apart from the
//! conversation that produces the endings so that both are readable and the
//! arithmetic is testable as values.
//!
//! **And it imposes no deadline of its own.** The engine has no socket timeouts
//! (yog bl-1421, filed and unbuilt), so the first dial after a flap may be
//! answered slowly by an engine still working out that the previous connection
//! is gone. The only deadline in the path is the transport one the channel
//! already had — a bound on the *socket*, sitting well above the engine's hold
//! — and if it fires it is the wire, which is retryable. So a slow engine costs
//! one more dial rather than the channel, and nothing here waits on bl-1421.
use Duration;
use ;
use ;
use crateEntry;
use crateLocal;
/// **The first wait after a channel drops**, and the floor the series returns
/// to. It is short because the ordinary case is a blip that is already over by
/// the time it is noticed — a laptop's lid, a relay switch — and a foot that
/// slept a minute over that would be absent for the failure rather than for its
/// cause.
pub const FIRST: Duration = from_secs;
/// **The longest a foot waits between dials.** The series doubles from
/// [`FIRST`] and stops here, so a box in airplane mode reaches a dial a minute
/// on its seventh attempt and stays there: a slow cadence rather than a burnt
/// core, which is what "it must not spin" has to mean for a box whose network
/// is not coming back for hours.
pub const CAP: Duration = from_secs;
/// **How long a vanished predecessor's claim can still stand.** REMOTE §5.1
/// states it as a contract rather than an accident — *"Its life is the hold and
/// not the connection's ... `Mailbox::take` drops the claim on the way out,
/// before the caller writes the answer, so a peer that vanished without a FIN
/// frees the slot within one hold's width — thirty seconds"* — so a redial that
/// meets the one-reader refusal knows exactly how long the thing refusing it
/// has left. Two seconds over the stated width, because the predecessor's
/// window began before this end noticed the drop and the series doubles past
/// this anyway if the guess is short.
pub const PREDECESSOR: Duration = from_secs;
/// **Serve one channel for as long as this process lives.**
///
/// The entry is opened once and not per dial: opening reads no file and dials
/// nothing — it is a fact about this box's own material — so a failure there is
/// this box's configuration and never an engine, and asking it again would ask
/// the same question. Everything after it is the engine's, and that is the part
/// that is asked again.
pub
/// **The wait before the next dial, and the series it leaves behind.**
///
/// Three rules, and each answers one of the ways this loop could be wrong:
///
/// - **It doubles and it caps**, so a box with no network settles instead of
/// spinning.
/// - **A one-reader refusal waits at least one hold's width**, because the
/// thing refusing is this box's own predecessor and REMOTE §5.1 says exactly
/// how long it has left. Asking sooner earns the same sentence and spends a
/// handshake to hear it. The series still advances underneath, so a refusal
/// that is a genuine rival rather than a predecessor backs off to the cap
/// like anything else.
/// - **A channel that served returns the series to its floor.** One answered
/// read is the engine having parked this foot for its own hold, which is the
/// evidence that this channel was real; a loop hammering a broken engine
/// cannot produce it without that engine handing out work, and a foot already
/// dials as fast as an engine answers reads. So the reset adds no cadence the
/// conversation did not already have — and without it a laptop that sleeps
/// nightly would creep to a minute's wait and stay there for the life of the
/// process, which is the failure this whole loop exists to shorten.
pub
/// **What a redial says while it is still serving.**
///
/// Under a loop the sentence that ended a channel is no longer a sentence
/// anything returns, so it has to reach the operator as it happens or not at
/// all — and what an operator needs from it is the same two facts the ending
/// sentence always carried: what happened, and what this box will do next.
///
/// The predecessor case names itself, because "refused, and dialling again"
/// reads as a foot ignoring a refusal unless it says whose refusal it expects.