1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
// See: https://github.com/rust-lang/rust/issues/44732#issuecomment-488766871 //! #![ cfg_attr( feature = "external_doc", feature(external_doc) ) ] #![ cfg_attr( feature = "external_doc", doc(include = "../README.md") ) ] #![ doc ( html_root_url = "https://docs.rs/naja_async_runtime" ) ] #![ deny ( missing_docs ) ] #![ forbid ( unsafe_code ) ] #![ allow ( clippy::suspicious_else_formatting ) ] #![ warn ( missing_debug_implementations , missing_docs , nonstandard_style , rust_2018_idioms , trivial_casts , trivial_numeric_casts , unused_extern_crates , unused_qualifications , single_use_lifetimes , unreachable_pub , variant_size_differences , )] mod import { pub(crate) use { once_cell :: { unsync::OnceCell } , std :: { cfg, fmt, future::Future, error::Error as StdError } , }; #[ cfg(any( feature = "bindgen", feature = "localpool", feature = "juliex", feature = "threadpool", feature = "tokio_ct" )) ] // pub(crate) use futures::future::FutureExt; #[ cfg(any( feature = "localpool", feature = "tokio_ct" )) ] // pub(crate) use std::cell::RefCell; #[ cfg( feature = "bindgen" ) ] // pub(crate) use { wasm_bindgen_futures :: { spawn_local } , }; #[ cfg( feature = "localpool" ) ] // pub(crate) use { futures :: { task::LocalSpawnExt, executor::{ LocalPool as FutLocalPool, LocalSpawner } } , }; #[ cfg( feature = "threadpool" ) ] // pub(crate) use { futures :: { task::SpawnExt, executor::{ ThreadPool as FutThreadPool } } , }; #[ cfg(any( feature = "juliex", feature = "threadpool" )) ] // pub(crate) use { once_cell :: { sync::OnceCell as SyncOnceCell } , }; #[ cfg( feature = "tokio_ct" ) ] // pub(crate) use { tokio :: { runtime::current_thread::Runtime as TokioCtRuntime } , }; } ///////////////// // --- API --- // ///////////////// mod error ; mod config ; mod executor ; pub use error::*; pub use config::*; #[ cfg( feature = "localpool" ) ] pub use executor::localpool ; #[ cfg( feature = "async_std" ) ] pub use executor::async_std ; #[ cfg( feature = "tokio_ct" ) ] pub use executor::tokio_ct ; #[ cfg(all( feature = "macros", feature = "juliex" )) ] pub use naja_runtime_macros::juliex ; #[ cfg(all( feature = "macros", feature = "threadpool" )) ] pub use naja_runtime_macros::threadpool ; #[ cfg(all( feature = "macros", feature = "async_std" )) ] pub use naja_runtime_macros::async_std ; #[ cfg(all( feature = "macros", feature = "localpool" )) ] pub use naja_runtime_macros::localpool ; #[ cfg(all( feature = "macros", feature = "bindgen" )) ] pub use naja_runtime_macros::bindgen ; #[ cfg(all( feature = "macros", feature = "tokio_ct" )) ] pub use naja_runtime_macros::tokio_ct ; use { import :: { * } , executor :: { Executor } , }; std::thread_local! ( pub(crate) static EXEC: OnceCell<Executor> = OnceCell::new(); ); /// Set the executor to use by on this thread. Run this before calls to [`spawn`]\(_*\). /// /// If you are a library author, don't call this unless you create the thread, otherwise it's up to client code to /// decide which executor to use. Just call [`spawn`]. /// /// ### Errors /// /// This method will fail with [`ErrorKind::DoubleExecutorInit`] if you call it twice on the same thread. There is /// [`init_allow_same`] which will not return an error if you try to init with the same executor twice. /// /// ### Example #[cfg_attr(feature = "localpool", doc = r##" ```rust use async_runtime as rt; // Will be used for spawning on the current thread only, if you create other threads, // you have to init them too, and you can choose a different executor on different threads. // rt::init( rt::Config::LocalPool ).expect( "Set thread executor" ); rt::spawn( async {} ).expect( "spawn future" ); rt::localpool::run(); ``` "##)] // pub fn init( config: Config ) -> Result< (), Error > { EXEC.with( |exec| -> Result< (), Error > { exec.set( Executor::new( config ) ).map_err( |_| ErrorKind::DoubleExecutorInit.into() ) }) } /// Set the executor to use for this thread. The difference with [`init`] is that this will not return /// a [`ErrorKind::DoubleExecutorInit`] error if you init with the same executor twice. It will still err /// if you try to set 2 different executors for this thread. /// /// This can sometimes be convenient for example if you would like to make two async fn sync in the /// same thread with macro attributes (they use this method). You should rarely need this. // pub fn init_allow_same( config: Config ) -> Result< (), Error > { if let Some(cfg) = current_rt() { if config == cfg { return Ok(()) }} init( config ) } /// Spawn a future to be run on the thread specified executor (set with [`init`]). /// /// This method returns a result. I understand that this is an inconveniece, but this is a interface that /// abstracts out over all supported executors. Some of them don't have an infallible spawn method, so we return /// a result even though on most executors spawning is infallible. /// /// Most of the time failing to spawn is rather fatal, so often using "expect" is fine. In /// application code you will know which executor you use, so you know if it's fallible, but library authors /// need to take into account that this might be fallible and consider how to recover from it. /// /// Note that _localpool_ and _bindgen_ will box the future. /// /// [`spawn`] requires a `Send` bound on the future. See [`spawn_local`] if you have to spasn `!Send` futures. /// /// [`spawn`] requires a `()` Output on the future. If you need to wait for the future to finish and/or recover /// a result from the computation, see: [`spawn_handle`]. /// /// ### Errors /// /// - This method is infallible on: _juliex_, _async-std_, _bindgen_. /// - On the _localpool_ executor, this method can return a [`ErrorKind::Spawn`] if the executor has been shut down. /// See the [docs for the futures library](https://rust-lang-nursery.github.io/futures-api-docs/0.3.0-alpha.18/futures/task/struct.SpawnError.html). I haven't really found a way to trigger this error. /// You can call [localpool::run] and spawn again afterwards. /// - If you call this without an initialized executor, [`ErrorKind::NoExecutorInitialized`] is returned. /// /// ### Example #[ cfg_attr( feature = "localpool", doc = r##" ``` use async_runtime as rt; rt::init( rt::Config::LocalPool ).expect( "no double executor init" ); rt::spawn( async { println!( "async execution" ); }).expect( "spawn on localpool" ); ``` "##)] // pub fn spawn( fut: impl Future< Output=() > + 'static + Send ) -> Result< (), Error > { EXEC.with( move |exec| -> Result< (), Error > { match exec.get() { Some(e) => e.spawn( fut ) , None => Err( ErrorKind::NoExecutorInitialized.into() ) , } }) } /// Spawn a future to be run on the current thread. This will return an error if the current executor is a threadpool. /// Currently works with _bindgen_ and _localpool_. /// /// Does exactly the same as [spawn], but does not require the future to be [Send]. If your /// future is [Send], you can just use [spawn]. It will spawn on the executor the current thread is configured with /// either way.. /// /// Note that _localpool_ and _bindgen_ will box the future. /// /// __Warning__: If you are a library author and you use this, you oblige client code to configure the thread in which /// this runs with a single threaded executor. /// /// ### Errors /// /// - When using with a threaded executor, this method will return a [`ErrorKind::SpawnLocalOnThreadPool`]. Since /// the signature doesn't require [`Send`] on the future, it can never be sent on a threadpool. /// - When using _localpool_, this method can return a spawn error if the executor has been shut down. /// See the [docs for the futures library](https://rust-lang-nursery.github.io/futures-api-docs/0.3.0-alpha.18/futures/task/struct.SpawnError.html). I haven't really found a way to trigger this error. /// You can call [localpool::run] and spawn again afterwards. /// - If you call this without an initialized executor, [`ErrorKind::NoExecutorInitialized`] is returned. // pub fn spawn_local( fut: impl Future< Output=() > + 'static ) -> Result< (), Error > { EXEC.with( move |exec| { match exec.get() { Some(e) => e.spawn_local( fut ) , None => Err( ErrorKind::NoExecutorInitialized.into() ) , } }) } /// Spawn a future and recover the output or just `.await` it to make sure it's finished. /// Since different executors return different types, we have to Box the returned future. /// /// Note that if you drop the handle, your future will not be polled. /// /// If on a threadpool and your future panics, the panic will be swallowed. /// /// async-std always /// returns a `JoinHandle`. You could call async-std's spawn method directly, knowing that worker threads might /// not be set up to end further calls to [`spawn`] to the async-std executor. Only do this if the spawned future /// will not call [`spawn`] and friends. /// /// ### Example #[ cfg_attr( all( feature = "juliex", feature = "macros" ), doc = r##" ``` use async_runtime as rt; #[ rt::juliex ] // async fn main() { let handle = rt::spawn_handle( async { "hello" }).expect( "spawn on localpool" ); assert_eq!( "hello", handle.await ); } ``` "##)] /// /// ### Errors /// - If you call this without an initialized executor, [`ErrorKind::NoExecutorInitialized`] is returned. // pub fn spawn_handle<T: Send + 'static>( fut: impl Future< Output=T > + Send + 'static ) -> Result< Box< dyn Future< Output=T > + Unpin + Send + 'static >, Error > { EXEC.with( move |exec| { match exec.get() { Some(e) => e.spawn_handle( fut ) , None => Err( ErrorKind::NoExecutorInitialized.into() ) , } }) } /// Spawn a future and recover the output for `!Send` futures. This does the same as [`spawn_handle`] /// except the future does not have to be `Send`. Note that `Future::Output` still has to be `Send`. /// We use [`FutureExt::remote_handle`](https://rust-lang-nursery.github.io/futures-api-docs/0.3.0-alpha.18/futures/future/trait.FutureExt.html#method.remote_handle) behind the scenes, and that requires the output to be Send, /// even though it shoulnd't have to be. /// /// Note that if you drop the handle, your future will not be polled. /// /// ### Errors /// - If you call this without an initialized executor, [`ErrorKind::NoExecutorInitialized`] is returned. // pub fn spawn_handle_local<T: 'static + Send>( fut: impl Future< Output=T > + 'static ) -> Result< Box< dyn Future< Output=T > + 'static + Unpin >, Error > { EXEC.with( move |exec| { match exec.get() { Some(e) => e.spawn_handle_local( fut ) , None => Err( ErrorKind::NoExecutorInitialized.into() ) , } }) } /// Which executor is configured for the current thread? /// /// If you are a library author you can use this to generate a clean error message /// if you have a hard requirement for a certain executor. // pub fn current_rt() -> Option<Config> { EXEC.with( move |exec| { exec.get().map( |e| e.config() ) }) } /// Block the current thread until the given future resolves and return the Output. /// This just forwards to `futures::executor::block_on` under the hood. /// /// **Warning** - this method does not play well with the rest of the executors. If you /// use the localpool executor, only the future you pass here will be polled. Futures /// that are "spawned" will just not run. /// /// If you use juliex or async_std, the threadpool will continue working, but block_on will not wait /// until all your futures have finished. As soon as the future you block on finishes, /// if `block_on` is the last statement of your program, the program will just end, regardless /// of other futures still on the threadpool. /// /// In general you shouldn't block the thread when you are in an async context. /// /// **Note:** This method is not available on WASM, since WASM currently does not allow blocking /// the current thread. // #[ cfg(not( target_arch = "wasm32" )) ] // pub fn block_on< F: Future >( fut: F ) -> F::Output { futures::executor::block_on( fut ) }