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
//! # Agnostik
//!
//! Agnostik is a layer between your application and the executor that is used to execute futures.
//! It allows you to switch between the executors smoothly and without having to change much code.
//!
//! You can use agnostik in every library that requires an executor, but wants to let the user decide
//! which executor should be used. You can also use agnostik in an application, if you plan to use multiple executors,
//! or want to switch between executors.
//!
//! ## Features
//!
//! - Run futures and wait for them to finish
//! - Spawn futures using the underlying executor
//! - Spawn blocking tasks in threads that are able to execute blocking methods
//!
//! Every feature I just said, can be used with every executor provided by agnostik, or
//! you can integrate your own executor with Agnostik.
//!
//! ## Get started
//!
//! Check the [tests](https://github.com/bastion-rs/agnostik/tree/master/tests) for simple examples.
//!
//! If you have [cargo-edit](https://github.com/killercup/cargo-edit) installed, you can just execute
//! this:
//! ```text
//! cargo add agnostik
//! ```
//!
//! otherwise, add this to your Cargo.toml file
//! ```text
//! agnostik = "0.1.0"
//! ```
//!
//! ## Usage
//!
//! ### Switching executors
//!
//! You can choose the executor, by using cargo features.
//! The default runtime is the `bastion-executor`.
//! To use another executor you just have to disable the default features, and choose one of the valid features.
//! Valid features are:
//! - `runtime_bastion` (default) to use the [Bastion Executor](https://crates.io/crates/bastion-executor)
//! - `runtime_tokio` to use the [Tokio](https://tokio.rs) runtime
//! - `runtime_asyncstd` to use the [AsyncStd](https://async.rs) runtime
//! - `runtime_nostd` (coming soon) to use Agnostik in a no_std environment
//!
//! E.g. to use the Tokio runtime, add the following line to your Cargo.toml
//! ```text
//! agnostik = { version = "0.1.0", default-features = false, features = ["runtime_tokio"]}
//! ```
//!
//! ### Examples
//!
//! Agnostiks API is very easy and only has a few methods to use.
//! Here's an example with the bastion-executor.
//!
//! ```ignore
//! use agnostik::prelude::*;
//!
//! fn main() {
//!     let runtime = Agnostik::bastion();
//!
//!     let future = runtime.spawn(async {
//!         println!("Hello from bastions executor!");
//!     })
//!     runtime.block_on(future)
//!
//!     let future = runtime.spawn_blocking(|| {
//!         expensive_blocking_method();
//!     })
//!     runtime.block_on(future)
//! }
//! ```
//!
//! There's also a global executor instance that can be used to spawn futures
//! without creating and storing your own executor.
//!
//! ```
//! fn main() {
//!     let future = agnostik::spawn(async { println!("Hello from bastion executor!"); 1 });
//!     let result = agnostik::block_on(future);
//!     assert_eq!(result, 1);
//! }
//! ```
//!
//! If you want to use another exceutor, you just have to replace the `Agnostik::bastion()`
//! method call, with the method that corresponds to your executor.
//!
//! Use
//! - `Agnostik::bastion()` for bastion
//! - `Agnostik::async_std()` for async std
//! - `Agnostik::tokio()` for tokio. **Warning:** See "How to use tokio runtime"
//! - `Agnostik::tokio_with_runtime(runtime)` if you want to use your own `tokio::runtime::Runtime` object. **Warning:** See "How to use tokio runtime"
//! - `Agnostik::no_std()` (coming soon) to create an exeutor that works in a nostd environment
//!
//! ### How to use tokio runtime
//!
//! It's not supported to use the `tokio::main` macro together with agnostik,
//! because Agnostik requires a `Runtime` object, which is created by calling `Runtime::new()`.
//! If your are using the `tokio::main` macro, there will be a panic, because you can't create a runtime
//! inside a runtime.
//!
//! Here's how to fix it:
//!
//! ```ignore
//! use agnostik::prelude::*;
//!
//! #[tokio::main]
//! async fn main() {
//!     let runtime = Agnostik::tokio();
//!
//!     let result = runtime.spawn(async_task()).await;
//!
//!     println!("The result is {}", result)
//! }
//! ```
//!
//! This would fail with a panic.
//! How to do it correctly:
//!
//! ```ignore
//! use agnostik::prelude::*;
//! use tokio::runtime::Runtime;
//!
//! fn main() {
//!     // see tokio docs for more methods to create a runtime
//!     let runtime = Runtime::new().expect("Failed to create a runtime"); // 1
//!     let runtime = Agnostik::tokio_with_runtime(runtime); // 2
//!
//!     let result = runtime.spawn(async_task());
//!     let result = runtime.block_on(result);
//!
//!     println!("The result is {}", result)
//! }
//! ```
//!
//! You can replace 1 and 2 with `Agnostik::tokio()`, because this method call will
//! create a Runtime object using `Runtime::new()`.
#![warn(rust_2018_idioms)]
#![warn(missing_docs)]

#[cfg(feature = "runtime_bastion")]
static EXECUTOR: Lazy<executors::BastionExecutor> = Lazy::new(|| executors::BastionExecutor);

#[cfg(feature = "runtime_asyncstd")]
static EXECUTOR: Lazy<executors::AsyncStdExecutor> = Lazy::new(|| executors::AsyncStdExecutor);

#[cfg(feature = "runtime_tokio")]
static EXECUTOR: Lazy<executors::TokioExecutor> = Lazy::new(|| executors::TokioExecutor::new());

#[cfg(feature = "runtime_smol")]
static EXECUTOR: Lazy<executors::SmolExecutor> = Lazy::new(|| executors::SmolExecutor);

pub mod executors;
pub mod join_handle;

use join_handle::JoinHandle;
use once_cell::sync::Lazy;
use std::future::Future;

/// This trait represents a generic executor that can spawn a future, spawn a blocking task,
/// and wait for a future to finish.
pub trait AgnostikExecutor {
    /// Spawns an asynchronous task using the underlying executor.
    fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
    where
        F: Future + Send + 'static,
        F::Output: Send + 'static;

    /// Runs the provided closure on a thread, which can execute blocking tasks asynchronously.
    fn spawn_blocking<F, T>(&self, task: F) -> JoinHandle<T>
    where
        F: FnOnce() -> T + Send + 'static,
        T: Send + 'static;

    /// Blocks until the future has finished.
    fn block_on<F>(&self, future: F) -> F::Output
    where
        F: Future + Send + 'static,
        F::Output: Send + 'static;
}

/// This trait represents an executor that is capable of spawning futures onto the same thread.
pub trait LocalAgnostikExecutor: AgnostikExecutor {
    /// Spawns a future that doesn't implement [Send].
    ///
    /// The spawned future will be executed on the same thread that called `spawn_local`.
    ///
    /// [Send]: https://doc.rust-lang.org/std/marker/trait.Send.html
    fn spawn_local<F>(&self, future: F) -> JoinHandle<F::Output>
    where
        F: Future + 'static,
        F::Output: 'static;
}

/// This struct doesn't have any functionality.
/// It's only use is to have a nice API to create executors
/// for different runtimes.
pub struct Agnostik;

impl Agnostik {
    #[cfg(feature = "runtime_bastion")]
    /// Returns an [AgnostikExecutor], that will use [bastion-executor] to spawn futures.
    ///
    /// [bastion-executor]: https://docs.rs/bastion-executor
    /// [AgnostikExecutor]: ./trait.AgnostikExecutor.html
    pub fn bastion() -> impl AgnostikExecutor {
        executors::BastionExecutor::new()
    }

    #[cfg(feature = "runtime_asyncstd")]
    /// Returns an [LocalAgnostikExecutor], that will use the [AsyncStd] runtime to spawn futures.
    ///
    /// [AsyncStd]: https://docs.rs/async_std
    /// [LocalAgnostikExecutor]: ./trait.LocalAgnostikExecutor.html
    pub fn async_std() -> impl LocalAgnostikExecutor {
        executors::AsyncStdExecutor::new()
    }

    #[cfg(feature = "runtime_tokio")]
    /// Returns an [LocalAgnostikExecutor], that will use the [Tokio] runtime to spawn futures.
    ///
    /// **Attention:** This method will create a new [Runtime] object using the [Runtime::new]
    /// method and will panic if it fails to create the [Runtime] object.
    /// If you want to use your own [Runtime] object, use [tokio_with_runtime] instead.
    ///
    /// [Tokio]: https://docs.rs/tokio
    /// [Runtime]: https://docs.rs/tokio/0.2.13/tokio/runtime/struct.Runtime.html
    /// [Runtime::new]: https://docs.rs/tokio/0.2.13/tokio/runtime/struct.Runtime.html#method.new
    /// [tokio_with_runtime]: #method.tokio_with_runtime
    /// [LocalAgnostikExecutor]: ../trait.LocalAgnostikExecutor.html
    pub fn tokio() -> impl LocalAgnostikExecutor {
        executors::TokioExecutor::new()
    }

    #[cfg(feature = "runtime_tokio")]
    /// Returns an [LocalAgnostikExecutor], that will use the [Tokio] runtime to spawn futures.
    /// It will use the given [Runtime] object to spawn, and block_on futures. The spawn_blocking method
    /// will use the [tokio::task::spawn_blocking] method.
    ///
    /// [tokio::task::spawn_blocking]: https://docs.rs/tokio/0.2.13/tokio/task/fn.spawn_blocking.html
    /// [Tokio]: https://docs.rs/tokio
    /// [Runtime]: https://docs.rs/tokio/0.2.13/tokio/runtime/struct.Runtime.html
    /// [tokio_with_runtime]: ./fn.tokio_with_runtime.html
    /// [LocalAgnostikExecutor]: ../trait.LocalAgnostikExecutor.html
    pub fn tokio_with_runtime(
        runtime: tokio_crate::runtime::Runtime,
    ) -> impl LocalAgnostikExecutor {
        executors::TokioExecutor::with_runtime(runtime)
    }

    /// Returns an [LocalAgnostikExecutor] that will use the [smol] runtime, to spawn and run futures.
    ///
    /// [smol]: https://docs.rs/smol
    /// [LocalAgnostikExecutor]: ../trait.LocalAgnostikExecutor.html
    #[cfg(feature = "runtime_smol")]
    pub fn smol() -> impl LocalAgnostikExecutor {
        executors::SmolExecutor
    }
}

/// `spawn` will use the global executor instance, which is determined by the cargo features,
/// to spawn the given future.
pub fn spawn<F>(future: F) -> JoinHandle<F::Output>
where
    F: Future + Send + 'static,
    F::Output: Send + 'static,
{
    EXECUTOR.spawn(future)
}

/// `spawn_blocking` will use the global executor instance, which is determined by the cargo features,
/// to spawn the given blocking task.
pub fn spawn_blocking<F, T>(task: F) -> JoinHandle<T>
where
    F: FnOnce() -> T + Send + 'static,
    T: Send + 'static,
{
    EXECUTOR.spawn_blocking(task)
}

/// `block_on` will use the global executor instance, which is determined by the cargo features,
/// to block until the given future has finished.
pub fn block_on<F>(future: F) -> F::Output
where
    F: Future + Send + 'static,
    F::Output: Send + 'static,
{
    EXECUTOR.block_on(future)
}

/// `spawn_local` will use the global executor instance, which is determined by the cargo features,
/// to spawn a `!Send` future.
#[cfg(any(feature = "runtime_tokio", feature = "runtime_smol"))]
pub fn spawn_local<F>(future: F) -> JoinHandle<F::Output>
where
    F: Future + 'static,
    F::Output: 'static,
{
    EXECUTOR.spawn_local(future)
}

/// This method will set the [`tokio Runtime`] in the global executor.
///
/// [`tokio Runtime`]: https://docs.rs/tokio/0.2.21/tokio/runtime/struct.Runtime.html
#[cfg(feature = "runtime_tokio")]
pub fn set_runtime(runtime: tokio_crate::runtime::Runtime) {
    EXECUTOR.set_runtime(runtime)
}

/// Returns a reference to the global executor.
#[cfg(not(any(feature = "runtime_tokio", feature = "runtime_smol")))]
pub fn executor() -> &'static impl AgnostikExecutor {
    &*EXECUTOR
}

/// Returns a reference to the global executor
#[cfg(any(feature = "runtime_tokio", feature = "runtime_smol"))]
pub fn executor() -> &'static impl LocalAgnostikExecutor {
    &*EXECUTOR
}

#[allow(unused)]
/// A prelude for the agnostik crate.
pub mod prelude {
    pub use crate::{
        block_on, spawn, spawn_blocking, Agnostik, AgnostikExecutor, LocalAgnostikExecutor,
    };
}