| 1 | use std::future::Future; |
| 2 | use std::panic::catch_unwind; |
| 3 | use std::thread; |
| 4 | |
| 5 | use async_executor::{Executor, Task}; |
| 6 | use async_io::block_on; |
| 7 | use async_lock::OnceCell; |
| 8 | use futures_lite::future; |
| 9 | |
| 10 | /// Spawns a task onto the global executor (single-threaded by default). |
| 11 | /// |
| 12 | /// There is a global executor that gets lazily initialized on first use. It is included in this |
| 13 | /// library for convenience when writing unit tests and small programs, but it is otherwise |
| 14 | /// more advisable to create your own [`Executor`]. |
| 15 | /// |
| 16 | /// By default, the global executor is run by a single background thread, but you can also |
| 17 | /// configure the number of threads by setting the `SMOL_THREADS` environment variable. |
| 18 | /// |
| 19 | /// Since the executor is kept around forever, `drop` is not called for tasks when the program |
| 20 | /// exits. |
| 21 | /// |
| 22 | /// # Examples |
| 23 | /// |
| 24 | /// ``` |
| 25 | /// let task = smol::spawn(async { |
| 26 | /// 1 + 2 |
| 27 | /// }); |
| 28 | /// |
| 29 | /// smol::block_on(async { |
| 30 | /// assert_eq!(task.await, 3); |
| 31 | /// }); |
| 32 | /// ``` |
| 33 | pub fn spawn<T: Send + 'static>(future: impl Future<Output = T> + Send + 'static) -> Task<T> { |
| 34 | static GLOBAL: OnceCell<Executor<'_>> = OnceCell::new(); |
| 35 | |
| 36 | fn global() -> &'static Executor<'static> { |
| 37 | GLOBAL.get_or_init_blocking(|| { |
| 38 | let num_threads = { |
| 39 | // Parse SMOL_THREADS or default to 1. |
| 40 | std::env::var("SMOL_THREADS" ) |
| 41 | .ok() |
| 42 | .and_then(|s| s.parse().ok()) |
| 43 | .unwrap_or(1) |
| 44 | }; |
| 45 | |
| 46 | for n in 1..=num_threads { |
| 47 | thread::Builder::new() |
| 48 | .name(format!("smol- {}" , n)) |
| 49 | .spawn(|| loop { |
| 50 | catch_unwind(|| block_on(global().run(future::pending::<()>()))).ok(); |
| 51 | }) |
| 52 | .expect("cannot spawn executor thread" ); |
| 53 | } |
| 54 | |
| 55 | // Prevent spawning another thread by running the process driver on this thread. |
| 56 | let ex = Executor::new(); |
| 57 | #[cfg (not(target_os = "espidf" ))] |
| 58 | ex.spawn(async_process::driver()).detach(); |
| 59 | ex |
| 60 | }) |
| 61 | } |
| 62 | |
| 63 | global().spawn(future) |
| 64 | } |
| 65 | |