zc2 0.0.25

P2P compute broker with credit-based billing, WAL, and broker mesh support
//! **Asynchronous execution policy for zc**
//!
//! zc keeps interfaces ultra-responsive by never blocking the main or UI thread on
//! long or I/O-bound work. All such work runs on **separated threads** (or, where
//! applicable, on a shared thread pool). Rust is well-suited for this: we use
//! `thread::spawn` and optional thread pools so that:
//!
//! - **Broker**: Each HTTP request is handled in its own thread (or pool worker),
//!   so one slow `/execute` never blocks `/health`, `/stats`, or other requests.
//! - **CLI / TUI**: Network fetches (e.g. `zc attach` stats) and long-running
//!   tasks run in background threads so the terminal UI stays responsive.
//! - **Bench**: Worker threads and progress reporting run in parallel; the main
//!   flow only coordinates.
//!
//! Use `spawn_detached` for fire-and-forget work (no result needed). Use
//! `spawn_blocking` when you need a `JoinHandle` to wait or cancel later.

use std::thread::{self, JoinHandle};

/// Runs `f` on a new thread and returns a handle. Use when you need to join or
/// coordinate with the result. For request handling and background loops, prefer
/// `spawn_detached` so threads are not leaked by forgotten handles.
#[inline]
pub fn spawn_blocking<F, R>(f: F) -> JoinHandle<R>
where
    F: FnOnce() -> R + Send + 'static,
    R: Send + 'static,
{
    thread::spawn(f)
}

/// Runs `f` on a new thread and forgets the handle. Use for long-running or
/// I/O-bound work that must not block the caller (e.g. HTTP fetch loop for TUI,
/// or per-request handler in the broker). The thread runs until `f` returns.
#[inline]
pub fn spawn_detached<F>(f: F)
where
    F: FnOnce() + Send + 'static,
{
    let _ = thread::Builder::new().name("zc-async".into()).spawn(f);
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::sync::atomic::{AtomicU64, Ordering};

    #[test]
    fn spawn_detached_runs() {
        let done = std::sync::Arc::new(AtomicU64::new(0));
        let d = done.clone();
        spawn_detached(move || {
            d.store(1, Ordering::Relaxed);
        });
        // Give the thread a moment to run
        thread::sleep(std::time::Duration::from_millis(50));
        assert_eq!(done.load(Ordering::Relaxed), 1);
    }

    #[test]
    fn spawn_blocking_returns_handle() {
        let handle = spawn_blocking(|| 42);
        assert_eq!(handle.join().unwrap(), 42);
    }
}