Skip to main content

graphrefly/
async_driver.rs

1//! Injectable local async/time driver boundary (D111).
2//!
3//! The wave core stays synchronous: driver callbacks re-enter through
4//! `DeferredCtx`, and `Dispatcher::invoke` remains sync void. Tokio, when enabled,
5//! is only an adapter behind this trait.
6
7use std::future::Future;
8use std::pin::Pin;
9use std::rc::Rc;
10use std::time::Duration;
11
12/// Cancels driver-owned work. Dropping it intentionally does nothing; sources
13/// register it with `ctx.on_deactivation` so teardown is explicit.
14pub type DriverCancel = Box<dyn FnOnce()>;
15
16/// Local, single-thread async/time driver.
17///
18/// No `Send`/`Sync` bounds: a graph is one single-thread concurrency domain (D22).
19pub trait LocalAsyncDriver {
20    /// Updates or reads `sleep`.
21    fn sleep(&self, duration: Duration, callback: Box<dyn FnOnce()>) -> DriverCancel;
22    /// Updates or reads `interval`.
23    fn interval(&self, period: Duration, callback: Rc<dyn Fn()>) -> DriverCancel;
24    /// Updates or reads `spawn_local`.
25    fn spawn_local(&self, fut: Pin<Box<dyn Future<Output = ()> + 'static>>) -> DriverCancel;
26}
27
28#[cfg(feature = "tokio")]
29#[derive(Debug, Clone, Copy, Default)]
30/// `TokioLocalDriver` data container.
31pub struct TokioLocalDriver;
32
33#[cfg(feature = "tokio")]
34impl LocalAsyncDriver for TokioLocalDriver {
35    fn sleep(&self, duration: Duration, callback: Box<dyn FnOnce()>) -> DriverCancel {
36        let handle = tokio::task::spawn_local(async move {
37            tokio::time::sleep(duration).await;
38            callback();
39        });
40        Box::new(move || handle.abort())
41    }
42
43    fn interval(&self, period: Duration, callback: Rc<dyn Fn()>) -> DriverCancel {
44        let handle = tokio::task::spawn_local(async move {
45            loop {
46                tokio::time::sleep(period).await;
47                callback();
48            }
49        });
50        Box::new(move || handle.abort())
51    }
52
53    fn spawn_local(&self, fut: Pin<Box<dyn Future<Output = ()> + 'static>>) -> DriverCancel {
54        let handle = tokio::task::spawn_local(fut);
55        Box::new(move || handle.abort())
56    }
57}