├── .gitignore ├── Cargo.toml ├── README.md └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "direct-executor" 3 | description = "An executor that directly executes futures, suitable for embedded environments" 4 | documentation = "https://docs.rs/direct-executor" 5 | repository = "https://github.com/dflemstr/direct-executor" 6 | keywords = ["executor", "futures", "io", "async"] 7 | license = "MIT OR Apache-2.0" 8 | categories = ["asynchronous", "embedded", "no-std"] 9 | version = "0.3.1-alpha.0" 10 | authors = ["David Flemström "] 11 | edition = "2018" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | pin-utils = "0.1.0-alpha.4" 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `direct-executor` 2 | 3 | An executor that directly executes futures, with an optional customizable wait operation. 4 | 5 | [Documentation](https://docs.rs/direct-executor) 6 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # `direct-executor` 2 | //! 3 | //! An executor that directly executes futures, with an optional customizable wait operation. 4 | #![no_std] 5 | #![deny( 6 | missing_docs, 7 | missing_debug_implementations, 8 | missing_copy_implementations, 9 | trivial_casts, 10 | trivial_numeric_casts, 11 | unstable_features, 12 | unused_import_braces, 13 | unused_qualifications, 14 | clippy::all 15 | )] 16 | 17 | use core::future; 18 | use core::task; 19 | 20 | /// Runs the provided future by spin-looping until polling succeeds. 21 | /// 22 | /// This is equivalent to `run(future, || core::sync::atomic::spin_loop_hint())`. 23 | pub fn run_spinning(future: F) -> F::Output 24 | where 25 | F: future::Future, 26 | { 27 | run(future, || core::sync::atomic::spin_loop_hint()) 28 | } 29 | 30 | /// Runs the provided future until polling succeeds, calling the provided `wait` closure in between 31 | /// each polling attempt. 32 | /// 33 | /// A common pattern is to let `wait` simply be some delay function (like `sleep()`), or in certain 34 | /// environments (such as on embedded devices), it might make sense to call `wfi` to wait for 35 | /// peripheral interrupts, if you know that to be the source of future completion. 36 | pub fn run(future: F, wait: impl FnMut()) -> F::Output 37 | where 38 | F: future::Future, 39 | { 40 | run_with_wake(future, wait, || {}) 41 | } 42 | 43 | /// Runs the provided future until polling succeeds, calling the provided `wait` closure in between 44 | /// each polling attempt. 45 | /// 46 | /// When this thread is supposed to wake up again, the provided `wake` closure will be called. This 47 | /// allows the user to provide custom "unpark" functionality, if necessary. 48 | /// 49 | /// A common pattern is to let `wait` simply be some delay function (like `sleep()`), or in certain 50 | /// environments (such as on embedded devices), it might make sense to call `wfi` to wait for 51 | /// peripheral interrupts, if you know that to be the source of future completion. 52 | pub fn run_with_wake(future: F, mut wait: impl FnMut(), wake: fn()) -> F::Output 53 | where 54 | F: future::Future, 55 | { 56 | pin_utils::pin_mut!(future); 57 | 58 | let raw_waker = raw_waker(&wake); 59 | let waker = unsafe { task::Waker::from_raw(raw_waker) }; 60 | let mut context = task::Context::from_waker(&waker); 61 | 62 | loop { 63 | if let task::Poll::Ready(result) = future.as_mut().poll(&mut context) { 64 | return result; 65 | } 66 | wait(); 67 | } 68 | } 69 | 70 | static VTABLE: task::RawWakerVTable = task::RawWakerVTable::new(clone, wake, wake, drop); 71 | 72 | fn raw_waker(wake_ptr: *const fn()) -> task::RawWaker { 73 | task::RawWaker::new(wake_ptr as *const (), &VTABLE) 74 | } 75 | 76 | fn clone(wake_ptr: *const ()) -> task::RawWaker { 77 | raw_waker(wake_ptr as *const fn()) 78 | } 79 | 80 | fn wake(wake_ptr: *const ()) { 81 | let wake = unsafe { (wake_ptr as *const fn()).as_ref() }.unwrap(); 82 | wake(); 83 | } 84 | 85 | fn drop(_wake_ptr: *const ()) {} 86 | --------------------------------------------------------------------------------