OXYGENHUB

A stop no longer waits on blocking work past the drain 529c703db24d

After the 30 s drain, serve returned and logged stopped, but dropping the
runtime waited for whatever still ran on its blocking threads: on
2026-10-17 04:07 a restart stood a minute past stopped until systemd
killed it, so the hub was down 90 s. The runtime now gets one second
(shutdown::finish). Test: a blocking task of 30 s does not hold it.
Also pins oxygen with the faster log and clean diff (oxygen#94).

Refs #233
Author
Andy McCall @andymccall <[email protected]>
via
agent Claude Code <[email protected]> · delegate for changes, until 2026-11-13 19:00Z · key ea5f78160088
When
· 2026-10-17 03:20 UTC
Signature
✔ signed by Claude Code
Hash
529c703db24dc1ee425177acbd16d99050046e4ae6cd949493d6dc83515c0ee5
Depends on
2bae995eb2f1 380996db84e5 b210727578e8 c92a99490cba d23464c0ac60
Depended on by
6aedcd6c0ad2
In bookmarks
api-unknown-fields cancel-landed-runs diffusion-pin-bookmark draft-ready main oh-comment-body-file proposal-list-fast
In tags
v0.6.0 v0.6.1 v0.7.0 v0.8.0 v0.9.0 v0.9.1 v0.9.11 v0.9.15 v0.9.16 v0.9.19 v0.9.2 v0.9.20 v0.9.21 v0.9.22 v0.9.23 v0.9.24 v0.9.3 v0.9.4 v0.9.5 v0.9.6 v0.9.7 v0.9.8 v0.9.9
Edits
5 atoms, 0 tree operations, 3 files

Browse the tree at this change

modified crates/hub/src/main.rs +3 −1
…
125 unchanged lines
126 126
    tune_allocator();
127 127
    let rt = tokio::runtime::Builder::new_multi_thread().enable_all().build()?;
128 128
    rt.spawn(return_freed_memory());
129 -
    rt.block_on(run())
129 +
    let out = rt.block_on(run());
130 +
    oxygenhub::shutdown::finish(rt);
131 +
    out
130 132
}
131 133
132 134
/// glibc's allocator gives each thread that allocates an arena of its own
…
249 unchanged lines
modified crates/hub/src/shutdown.rs +26 −0
…
12 unchanged lines
13 13
/// How long the requests in flight get to finish.
14 14
pub const DRAIN: std::time::Duration = std::time::Duration::from_secs(30);
15 15
16 +
/// How long, after the drain, work still running on the runtime's
17 +
/// blocking threads (a slow request's repository work, a bucket call) may
18 +
/// hold the process. Dropping the runtime would wait for it however long
19 +
/// it takes: on 2026-10-17 a restart stood a minute past "stopped", until
20 +
/// systemd killed it.
21 +
pub const EXIT_GRACE: std::time::Duration = std::time::Duration::from_secs(1);
22 +
23 +
/// End the runtime once `serve` has returned, cutting off what still runs.
24 +
pub fn finish(rt: tokio::runtime::Runtime) {
25 +
    rt.shutdown_timeout(EXIT_GRACE);
26 +
}
27 +
16 28
fn sender() -> &'static watch::Sender<bool> {
17 29
    static STOP: OnceLock<watch::Sender<bool>> = OnceLock::new();
18 30
    STOP.get_or_init(|| watch::channel(false).0)
…
54 unchanged lines
73 85
    }
74 86
    #[cfg(not(unix))]
75 87
    None
88 +
}
89 +
90 +
#[cfg(test)]
91 +
mod tests {
92 +
    /// Blocking work still running when the hub stops does not hold the
93 +
    /// process: the runtime ends within its grace.
94 +
    #[test]
95 +
    fn finishing_does_not_wait_for_blocking_work() {
96 +
        let rt = tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap();
97 +
        rt.spawn_blocking(|| std::thread::sleep(std::time::Duration::from_secs(30)));
98 +
        let started = std::time::Instant::now();
99 +
        super::finish(rt);
100 +
        assert!(started.elapsed() < super::EXIT_GRACE * 3, "{:?}", started.elapsed());
101 +
    }
76 102
}
replaced deps/oxygenflow +0 −0

No line changes.