Skip to main content

khive_runtime/
process_retry.rs

1//! Bounded retry for a process image temporarily busy during replacement.
2
3use std::io::{ErrorKind, Result};
4use std::time::Duration;
5
6/// Three short delays permit four total spawn attempts.
7pub const EXECUTABLE_BUSY_BACKOFF_MS: [u64; 3] = [5, 20, 50];
8
9fn is_executable_busy(error: &std::io::Error) -> bool {
10    error.kind() == ErrorKind::ExecutableFileBusy
11}
12
13/// Retry only before a child exists; a successful command is never repeated.
14/// Every other error, including the final busy error, is returned unchanged.
15pub async fn spawn_retrying_executable_busy_async<T>(
16    delays_ms: &[u64],
17    mut spawn: impl FnMut() -> Result<T>,
18) -> Result<T> {
19    for delay_ms in delays_ms {
20        match spawn() {
21            Err(error) if is_executable_busy(&error) => {
22                tokio::time::sleep(Duration::from_millis(*delay_ms)).await;
23            }
24            outcome => return outcome,
25        }
26    }
27    spawn()
28}
29
30/// Blocking counterpart for synchronous subprocess call sites.
31pub fn spawn_retrying_executable_busy<T>(
32    delays_ms: &[u64],
33    mut spawn: impl FnMut() -> Result<T>,
34) -> Result<T> {
35    for delay_ms in delays_ms {
36        match spawn() {
37            Err(error) if is_executable_busy(&error) => {
38                std::thread::sleep(Duration::from_millis(*delay_ms));
39            }
40            outcome => return outcome,
41        }
42    }
43    spawn()
44}