use std::rc::Rc; use crate::{ExtPort, ExtensionBuilder}; /// Run an extension inside a Tokio localset. Since the extension API does not /// provide a forking mechanism, it can safely abort once the localset is /// exhausted. If an extension absolutely needs a parallel thread, it can import /// and call [tokio::task::spawn_local] which will keep alive the localset and /// postpone the aggressive shutdown, and listen for the [Drop::drop] of the /// value returned by [crate::system_ctor::SystemCtor::inst] to initiate /// shutdown. #[cfg(feature = "tokio")] pub async fn __tokio_entrypoint(builder: ExtensionBuilder) { use std::cell::RefCell; use async_event::Event; use tokio::io::{stderr, stdin, stdout}; use tokio::task::{LocalSet, spawn_local}; use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt}; let cc = Rc::new(Event::new()); let c = Rc::new(RefCell::new(1)); LocalSet::new() .run_until(async { let counter = (c.clone(), cc.clone()); builder .run(ExtPort { input: Box::pin(stdin().compat()), output: Box::pin(stdout().compat_write()), log: Box::pin(stderr().compat_write()), spawn: Rc::new(move |delay, fut| { let (c, cc) = counter.clone(); if delay.is_zero() { *c.borrow_mut() += 1; cc.notify_all(); spawn_local(async move { fut.await; *c.borrow_mut() -= 1; cc.notify_all(); }); } else { let at = tokio::time::Instant::now() + delay; spawn_local(async move { tokio::time::sleep_until(at).await; *c.borrow_mut() += 1; cc.notify_all(); fut.await; *c.borrow_mut() -= 1; cc.notify_all(); }); } }), }) .await; cc.wait_until(|| (*c.borrow() == 0).then_some(())).await; }) .await; } #[macro_export] macro_rules! tokio_main { ($builder:expr) => { #[tokio::main] pub async fn main() { $crate::__tokio_entrypoint($builder).await } }; }