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
//! What:
//! The service's side of one waiting client: the queue of messages the landing thread answers with, and the loop that writes
//! them to the client's connection together with a heartbeat while nothing else is to say.
//! Why:
//! A client waits for its answer with no clock of its own (`service_client.rs`), so the only thing that ends its wait is
//! silence. The thread that read the request has nothing else to do while the request is queued or landing, so it writes
//! the heartbeats, and a long batch, a long queue or a slow disk in front of the landing never reads as a dead service. The
//! landing thread hands its messages to that same thread through a channel instead of writing the connection itself, so the
//! connection has one writer, a line is written whole, and no lock guards it.
//!
//! In TS you'd write (pseudocode):
//! ```ts
//! const [reply, messages] = makeReply(); queue.push({ request, reply }); await writeUntilAnswered(socket, messages, HEARTBEAT_EVERY);
//! ```
use crate;
use Write;
use UnixStream;
use ;
use Duration;
/// What:
/// The landing thread's end of one client's messages.
/// Why:
/// Dropping it is what answering the request ends with: the writer sees the channel closed, writes what is left, and closes
/// the connection.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// class Reply { constructor(private queue: Channel<ServiceMessage>) {} send(message) { this.queue.push(message) } }
/// ```
/// Writes `message` and a newline to `stream` as one write; false when the client is gone.
/// What:
/// Writes each queued message to the client, and a heartbeat whenever `every` passes with nothing queued, until the reply is
/// dropped or the client is gone.
/// Why:
/// The heartbeat is the proof of life the client's wait rests on; the queued messages come first, in order, so a beat never
/// stands between a notice and the answer that follows it.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// for (;;) { const m = await messages.next({ timeout: every }); if (m === END) return; if (!write(m ?? heartbeat)) return; }
/// ```
/// What:
/// `write_until_answered`, where `note` is asked for a message whenever the beat is due, and what it returns is written before the
/// beat.
/// Why:
/// A client queued behind a write that never ends is told what the service is stuck in (`service_busy.rs`); the beat is the
/// one moment this thread has nothing queued to write, so the notice rides on it and is asked for at most once a beat.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// for (;;) { const m = await messages.next({ timeout: every }); if (m === END) return; if (m === undefined) { const n = note(); if (n && !write(n)) return; } if (!write(m ?? heartbeat)) return; }
/// ```
/// The reply and its beat are controlled in the test module.