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
//! SHUTDOWN: prompt, bounded exit on SIGTERM/SIGINT regardless of link count.
//!
//! The daemon acceptor's signal handling used to route a signal through the
//! event-loop channel (`Ev::Interrupted`): the loop had to be FREE to see it,
//! then ran `flush + sio.disconnect().await + process::exit`. With several live
//! peer links one wedged transport write inside the per-tick work (a WebRTC data
//! channel `write_data_channel().await` against a frozen / half-open peer, or a
//! `send_frame` parked on backpressure that never drains) starves the loop. The
//! `Interrupted` event then never gets processed, the process ignores SIGTERM for
//! the full systemd `TimeoutStopSec` (90s) and is SIGKILLed. The class of
//! webrtc-rs `close()`/write deadlock-on-drop is well known; the resilience layer
//! already treats a dropped link as an ordinary disconnect on both ends.
//!
//! The fix here is a SIGNAL-OWNED watchdog: the moment a termination signal lands
//! we arm a hard, bounded deadline that force-exits the process even if the
//! graceful path (event loop) is wedged. A dropped link is recovered by the
//! resilience layer, so a bounded best-effort teardown loses nothing a normal
//! disconnect wouldn't.
use Duration;
/// Default grace before the watchdog force-exits, in milliseconds. Comfortably
/// under systemd's default `TimeoutStopSec` (90s) so the process exits on its
/// own terms well before SIGKILL. Overridable for tests / tuning via
/// `FILAMENT_SHUTDOWN_GRACE_MS`.
pub const DEFAULT_GRACE_MS: u64 = 3_000;
/// The bounded shutdown grace, env-tunable. Pure given the environment; the
/// parsing/clamping logic is unit-tested via `grace_from`.
/// Pure: turn an optional `FILAMENT_SHUTDOWN_GRACE_MS` value into the grace
/// duration. A missing / unpar0able value falls back to the default. `0` is
/// honored (force-exit on the next scheduler tick) so an operator can opt into
/// an immediate exit; everything else is taken verbatim.
/// Arm a detached watchdog that force-exits the process with `code` after
/// `grace`, independent of the event loop. Call this the instant a termination
/// signal is observed: even if the graceful shutdown (flush + signaling
/// disconnect + the loop's own `process::exit`) wedges on a stuck transport, the
/// watchdog guarantees the process is gone within `grace`. Idempotent in effect
/// (extra arms just schedule more force-exits at the same deadline; the first to
/// fire wins).