//! Beul - It executes futures. //! //! This crate offers a single function: [`execute`], which will run a single future on the current //! thread. No fancy executor, no future primitives, just a simple executor. No dependencies, no //! unsafe rust. //! //! The design is based on the example `ThreadWaker` from the [`Wake`] documentation, which the //! reentrancy issues resolved by using a [`Condvar`]. //! //! Beul is Dutch for executioner. //! //! # Usage //! //! ``` //! beul::execute(async {}); //! ``` #![forbid(unsafe_code)] use std::future::Future; use std::sync::Arc; use std::sync::Condvar; use std::sync::Mutex; use std::sync::PoisonError; use std::task::Context; use std::task::Poll; use std::task::Wake; use std::task::Waker; #[derive(Default)] struct CondvarWake { park: Condvar, awoken: Mutex, } impl CondvarWake { pub fn park(&self) { let mut guard = self.awoken.lock().unwrap_or_else(PoisonError::into_inner); // Until we are awoken, we can park on the condvar. This also handles the case where we're // awoken while we're actually polling. while !*guard { guard = self .park .wait(guard) .unwrap_or_else(PoisonError::into_inner); } *guard = false; } } impl Wake for CondvarWake { fn wake(self: Arc) { self.wake_by_ref() } fn wake_by_ref(self: &Arc) { *self.awoken.lock().unwrap_or_else(PoisonError::into_inner) = true; self.park.notify_one(); } } /// Block on specified [`Future`]. pub fn execute(f: impl Future) -> T { // TODO: replace with std::pin::pin once it gets stabilized let mut pinned = Box::pin(f); let wake = Arc::new(CondvarWake::default()); let waker = Waker::from(Arc::clone(&wake)); let mut context = Context::from_waker(&waker); loop { match pinned.as_mut().poll(&mut context) { Poll::Ready(value) => return value, Poll::Pending => wake.park(), } } }