smol_macros/
lib.rs

1//! Macros for using `smol-rs`.
2//!
3//! One of the advantages of [`smol`] is that it lets you set up your own executor, optimized for
4//! your own use cases. However, quick scaffolding is important for many organizational use cases.
5//! Especially when sane defaults are appreciated, setting up your own executor is a waste of
6//! time.
7//!
8//! This crate provides macros for setting up an efficient [`smol`] runtime quickly and
9//! effectively. It provides sane defaults that are useful for most applications.
10//!
11//! ## Simple Executor
12//!
13//! Just have an `async` main function, using the [`main`] macro.
14//!
15//!
16//! ```
17//! use smol_macros::main;
18//!
19//! main! {
20//!     async fn main() {
21//!         println!("Hello, world!");
22//!     }
23//! }
24//! ```
25//!
26//! This crate uses declarative macros rather than procedural macros, in order to avoid needing
27//! to use heavy macro dependencies. If you want to use the proc macro syntax, you can use the
28//! [`macro_rules_attribute::apply`] function to emulate it.
29//!
30//! The following is equivalent to the previous example.
31//!
32//! ```
33//! use macro_rules_attribute::apply;
34//! use smol_macros::main;
35//!
36//! #[apply(main!)]
37//! async fn main() {
38//!     println!("Hello, world!");
39//! }
40//! ```
41//!
42//! ## Task-Based Executor
43//!
44//! This crate re-exports [`smol::Executor`]. If that is used as the first parameter in a
45//! function in [`main`], it will automatically create the executor.
46//!
47//! ```
48//! use macro_rules_attribute::apply;
49//! use smol_macros::{main, Executor};
50//!
51//! #[apply(main!)]
52//! async fn main(ex: &Executor<'_>) {
53//!     ex.spawn(async { println!("Hello world!"); }).await;
54//! }
55//! ```
56//!
57//! If the thread-safe [`smol::Executor`] is used here, a thread pool will be spawned to run
58//! the executor on multiple threads. For the thread-unsafe [`smol::LocalExecutor`], no threads
59//! will be spawned.
60//!
61//! See documentation for the [`main`] function for more details.
62//!
63//! ## Tests
64//!
65//! Use the [`test`] macro to set up test cases that run self-contained executors.
66//!
67//! ```
68//! use macro_rules_attribute::apply;
69//! use smol_macros::{test, Executor};
70//!
71//! #[apply(test!)]
72//! async fn do_test(ex: &Executor<'_>) {
73//!     ex.spawn(async {
74//!         assert_eq!(1 + 1, 2);
75//!     }).await;
76//! }
77//! ```
78//!
79//! [`smol`]: https://crates.io/crates/smol
80//! [`smol::Executor`]: https://docs.rs/smol/latest/smol/struct.Executor.html
81//! [`smol::LocalExecutor`]: https://docs.rs/smol/latest/smol/struct.LocalExecutor.html
82//! [`macro_rules_attribute::apply`]: https://docs.rs/macro_rules_attribute/latest/macro_rules_attribute/attr.apply.html
83
84#![forbid(unsafe_code)]
85
86#[doc(no_inline)]
87pub use async_executor::{Executor, LocalExecutor};
88
89/// Turn a main function into one that runs inside of a self-contained executor.
90///
91/// The function created by this macro spawns an executor, spawns threads to run that executor
92/// on (if applicable), and then blocks the current thread on the future.
93///
94/// ## Examples
95///
96/// Like [`tokio::main`], this function is not limited to wrapping the program's entry point.
97/// In a mostly synchronous program, it can wrap a self-contained `async` function in its
98/// own executor.
99///
100/// ```
101/// use macro_rules_attribute::apply;
102/// use smol_macros::{main, Executor};
103///
104/// fn do_something_sync() -> u32 {
105///     1 + 1
106/// }
107///
108/// #[apply(main!)]
109/// async fn do_something_async(ex: &Executor<'_>) -> u32 {
110///     ex.spawn(async { 1 + 1 }).await
111/// }
112///
113/// fn main() {
114///     let x = do_something_sync();
115///     let y = do_something_async();
116///     assert_eq!(x + y, 4);
117/// }
118/// ```
119///
120/// The first parameter to the `main` function can be an executor. It can be one of the following:
121///
122/// - Nothing.
123/// - `&`[`Executor`]
124/// - `&`[`LocalExecutor`]
125/// - `Arc<`[`Executor`]`>`
126/// - `Rc<`[`LocalExecutor`]`>`
127///
128/// [`tokio::main`]: https://docs.rs/tokio/latest/tokio/attr.main.html
129/// [`Executor`]: https://docs.rs/smol/latest/smol/struct.Executor.html
130/// [`LocalExecutor`]: https://docs.rs/smol/latest/smol/struct.LocalExecutor.html
131#[macro_export]
132macro_rules! main {
133    (
134        $(#[$attr:meta])*
135        async fn $name:ident () $(-> $ret:ty)? $bl:block
136    ) => {
137        $(#[$attr])*
138        fn $name () $(-> $ret)? {
139            $crate::__private::block_on(async {
140                $bl
141            })
142        }
143    };
144
145    (
146        $(#[$post_attr:meta])*
147        async fn $name:ident ($ex:ident : & $exty:ty)
148        $(-> $ret:ty)? $bl:block
149    ) => {
150        $(#[$post_attr])*
151        fn $name () $(-> $ret)? {
152            <$exty as $crate::__private::MainExecutor>::with_main(|ex| {
153                $crate::__private::block_on(ex.run(async move {
154                    let $ex = ex;
155                    $bl
156                }))
157            })
158        }
159    };
160
161    (
162        $(#[$post_attr:meta])*
163        async fn $name:ident ($ex:ident : $exty:ty)
164        $(-> $ret:ty)? $bl:block
165    ) => {
166        $crate::main! {
167            $(#[$post_attr])*
168            async fn $name(ex: &$exty) $(-> $ret)? {
169                let $ex = ex.clone();
170                $bl
171            }
172        }
173    }
174}
175
176/// Wrap a test in an asynchronous executor.
177///
178/// This is equivalent to the [`main`] macro, but adds the `#[test]` attribute.
179///
180/// ## Examples
181///
182/// ```
183/// use macro_rules_attribute::apply;
184/// use smol_macros::test;
185///
186/// #[apply(test!)]
187/// async fn do_test() {
188///     assert_eq!(1 + 1, 2);
189/// }
190/// ```
191#[macro_export]
192macro_rules! test {
193    // Special case to get around bug in macro engine.
194    (
195        $(#[$post_attr:meta])*
196        async fn $name:ident ($exname:ident : & $exty:ty)
197        $(-> $ret:ty)? $bl:block
198    ) => {
199        $crate::main! {
200            $(#[$post_attr])*
201            #[core::prelude::v1::test]
202            async fn $name($exname: &$exty) $(-> $ret)? $bl
203        }
204    };
205
206    (
207        $(#[$post_attr:meta])*
208        async fn $name:ident ($($pname:ident : $pty:ty),* $(,)?)
209        $(-> $ret:ty)? $bl:block
210    ) => {
211        $crate::main! {
212            $(#[$post_attr])*
213            #[core::prelude::v1::test]
214            async fn $name($($pname: $pty),*) $(-> $ret)? $bl
215        }
216    };
217}
218
219#[doc(hidden)]
220pub mod __private {
221    pub use async_io::block_on;
222    pub use std::rc::Rc;
223
224    use crate::{Executor, LocalExecutor};
225    use event_listener::Event;
226    use std::sync::atomic::{AtomicBool, Ordering};
227    use std::sync::Arc;
228    use std::thread;
229
230    /// Something that can be set up as an executor.
231    #[doc(hidden)]
232    pub trait MainExecutor: Sized {
233        /// Create this type and pass it into `main`.
234        fn with_main<T, F: FnOnce(&Self) -> T>(f: F) -> T;
235    }
236
237    impl MainExecutor for Arc<Executor<'_>> {
238        #[inline]
239        fn with_main<T, F: FnOnce(&Self) -> T>(f: F) -> T {
240            let ex = Arc::new(Executor::new());
241            with_thread_pool(&ex, || f(&ex))
242        }
243    }
244
245    impl MainExecutor for Executor<'_> {
246        #[inline]
247        fn with_main<T, F: FnOnce(&Self) -> T>(f: F) -> T {
248            let ex = Executor::new();
249            with_thread_pool(&ex, || f(&ex))
250        }
251    }
252
253    impl MainExecutor for Rc<LocalExecutor<'_>> {
254        #[inline]
255        fn with_main<T, F: FnOnce(&Self) -> T>(f: F) -> T {
256            f(&Rc::new(LocalExecutor::new()))
257        }
258    }
259
260    impl MainExecutor for LocalExecutor<'_> {
261        fn with_main<T, F: FnOnce(&Self) -> T>(f: F) -> T {
262            f(&LocalExecutor::new())
263        }
264    }
265
266    /// Run a function that takes an `Executor` inside of a thread pool.
267    #[inline]
268    fn with_thread_pool<T>(ex: &Executor<'_>, f: impl FnOnce() -> T) -> T {
269        let stopper = WaitForStop::new();
270
271        // Create a thread for each CPU.
272        thread::scope(|scope| {
273            let num_threads = thread::available_parallelism().map_or(1, |num| num.get());
274            for i in 0..num_threads {
275                let ex = &ex;
276                let stopper = &stopper;
277
278                thread::Builder::new()
279                    .name(format!("smol-macros-{i}"))
280                    .spawn_scoped(scope, || {
281                        block_on(ex.run(stopper.wait()));
282                    })
283                    .expect("failed to spawn thread");
284            }
285
286            let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f));
287
288            stopper.stop();
289
290            match result {
291                Ok(value) => value,
292                Err(err) => std::panic::resume_unwind(err),
293            }
294        })
295    }
296
297    /// Wait for the executor to stop.
298    struct WaitForStop {
299        /// Whether or not we need to stop.
300        stopped: AtomicBool,
301
302        /// Wait for the stop.
303        events: Event,
304    }
305
306    impl WaitForStop {
307        /// Create a new wait for stop.
308        #[inline]
309        fn new() -> Self {
310            Self {
311                stopped: AtomicBool::new(false),
312                events: Event::new(),
313            }
314        }
315
316        /// Wait for the event to stop.
317        #[inline]
318        async fn wait(&self) {
319            loop {
320                if self.stopped.load(Ordering::Relaxed) {
321                    return;
322                }
323
324                event_listener::listener!(&self.events => listener);
325
326                if self.stopped.load(Ordering::Acquire) {
327                    return;
328                }
329
330                listener.await;
331            }
332        }
333
334        /// Stop the waiter.
335        #[inline]
336        fn stop(&self) {
337            self.stopped.store(true, Ordering::SeqCst);
338            self.events.notify_additional(std::usize::MAX);
339        }
340    }
341}