From 60a08196b6f473da17fc280a8545f1b62097b4fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Thu, 30 Nov 2023 18:11:02 -0300 Subject: [PATCH 01/16] feat: add LocalWaker type, ContextBuilder type, and LocalWake trait. --- library/alloc/src/lib.rs | 1 + library/alloc/src/task.rs | 165 +++++++++++++++- library/core/src/task/mod.rs | 2 +- library/core/src/task/wake.rs | 361 +++++++++++++++++++++++++++++++--- 4 files changed, 504 insertions(+), 25 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 78629b39d34b1..c8b4cebdf8987 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -135,6 +135,7 @@ #![feature(iter_next_chunk)] #![feature(iter_repeat_n)] #![feature(layout_for_ptr)] +#![feature(local_waker)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_uninit_array)] #![feature(maybe_uninit_uninit_array_transpose)] diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 5d9772b878b03..16ae5241da8a3 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -7,8 +7,9 @@ //! `#[cfg(target_has_atomic = "ptr")]`. use core::mem::ManuallyDrop; -use core::task::{RawWaker, RawWakerVTable, Waker}; +use core::task::{LocalWaker, RawWaker, RawWakerVTable, Waker}; +use crate::rc::Rc; use crate::sync::Arc; /// The implementation of waking a task on an executor. @@ -152,3 +153,165 @@ fn raw_waker(waker: Arc) -> RawWaker { &RawWakerVTable::new(clone_waker::, wake::, wake_by_ref::, drop_waker::), ) } + +/// An analogous trait to `Wake` but used to construct a `LocalWaker`. This API +/// works in exactly the same way as `Wake`, except that it uses an `Rc` instead +/// of an `Arc`, and the result is a `LocalWaker` instead of a `Waker`. +/// +/// The benefits of using `LocalWaker` over `Waker` are that it allows the local waker +/// to hold data that does not implement `Send` and `Sync`. Additionally, it saves calls +/// to `Arc::clone`, which requires atomic synchronization. +/// + +/// # Examples +/// +/// A +/// +/// This is a simplified example of a `spawn` and a `block_on` function. The `spawn` function +/// is used to push new tasks onto the run queue, while the block on function will remove them +/// and poll them. When a task is woken, it will put itself back on the run queue to be polled by the executor. +/// +/// **Note:** A real world example would interlieve poll calls with calls to an io reactor to wait for events instead +/// of spinning on a loop. +/// +/// ```rust +/// use std::task::{LocalWake, ContextBuilder, LocalWaker}; +/// use std::future::Future; +/// use std::pin::Pin; +/// use std::rc::Rc; +/// use std::cell::RefCell; +/// use std::collections::VecDeque; +/// +/// +/// thread_local! { +/// // A queue containing all tasks ready to do progress +/// static RUN_QUEUE: RefCell>> = RefCell::default(); +/// } +/// +/// type BoxedFuture = Pin>>; +/// +/// struct Task(RefCell); +/// +/// impl LocalWake for Task { +/// fn wake(self: Rc) { +/// RUN_QUEUE.with_borrow_mut(|queue| { +/// queue.push_back(self) +/// }) +/// } +/// } +/// +/// fn spawn(future: F) +/// where +/// F: Future + 'static + Send + Sync +/// { +/// let task = Rc::new(Box::pin(future)); +/// RUN_QUEUE.with_borrow_mut(|queue| { +/// queue.push_back(task) +/// }); +/// } +/// +/// fn block_on(future: F) +/// where +/// F: Future + 'static + Sync + Send +/// { +/// spawn(future); +/// loop { +/// let Some(task) = RUN_QUEUE.with_borrow_mut(|queue|queue.pop_front()) else { +/// // we exit, since there are no more tasks remaining on the queue +/// return; +/// }; +/// // cast the Rc into a `LocalWaker` +/// let waker: LocalWaker = task.into(); +/// // Build the context using `ContextBuilder` +/// let mut cx = ContextBuilder::new() +/// .local_waker(&waker) +/// .build(); +/// +/// // Poll the task +/// task.0 +/// .borrow_mut() +/// .as_mut() +/// .poll(&mut cx); +/// } +/// } +/// ``` +/// +#[unstable(feature = "local_waker", issue = "none")] +pub trait LocalWake { + /// Wake this task. + #[unstable(feature = "local_waker", issue = "none")] + fn wake(self: Rc); + + /// Wake this task without consuming the local waker. + /// + /// If an executor supports a cheaper way to wake without consuming the + /// waker, it should override this method. By default, it clones the + /// [`Rc`] and calls [`wake`] on the clone. + /// + /// [`wake`]: Rc::wake + #[unstable(feature = "local_waker", issue = "none")] + fn wake_by_ref(self: &Rc) { + self.clone().wake(); + } +} + +#[unstable(feature = "local_waker", issue = "none")] +impl From> for LocalWaker { + /// Use a `Wake`-able type as a `LocalWaker`. + /// + /// No heap allocations or atomic operations are used for this conversion. + fn from(waker: Rc) -> LocalWaker { + // SAFETY: This is safe because raw_waker safely constructs + // a RawWaker from Rc. + unsafe { LocalWaker::from_raw(local_raw_waker(waker)) } + } +} +#[allow(ineffective_unstable_trait_impl)] +#[unstable(feature = "local_waker", issue = "none")] +impl From> for RawWaker { + /// Use a `Wake`-able type as a `RawWaker`. + /// + /// No heap allocations or atomic operations are used for this conversion. + fn from(waker: Rc) -> RawWaker { + local_raw_waker(waker) + } +} + +// NB: This private function for constructing a RawWaker is used, rather than +// inlining this into the `From> for RawWaker` impl, to ensure that +// the safety of `From> for Waker` does not depend on the correct +// trait dispatch - instead both impls call this function directly and +// explicitly. +#[inline(always)] +fn local_raw_waker(waker: Rc) -> RawWaker { + // Increment the reference count of the Rc to clone it. + unsafe fn clone_waker(waker: *const ()) -> RawWaker { + unsafe { Rc::increment_strong_count(waker as *const W) }; + RawWaker::new( + waker as *const (), + &RawWakerVTable::new(clone_waker::, wake::, wake_by_ref::, drop_waker::), + ) + } + + // Wake by value, moving the Rc into the LocalWake::wake function + unsafe fn wake(waker: *const ()) { + let waker = unsafe { Rc::from_raw(waker as *const W) }; + ::wake(waker); + } + + // Wake by reference, wrap the waker in ManuallyDrop to avoid dropping it + unsafe fn wake_by_ref(waker: *const ()) { + let waker = unsafe { ManuallyDrop::new(Rc::from_raw(waker as *const W)) }; + ::wake_by_ref(&waker); + } + + // Decrement the reference count of the Rc on drop + unsafe fn drop_waker(waker: *const ()) { + unsafe { Rc::decrement_strong_count(waker as *const W) }; + } + + RawWaker::new( + Rc::into_raw(waker) as *const (), + &RawWakerVTable::new(clone_waker::, wake::, wake_by_ref::, drop_waker::), + ) +} diff --git a/library/core/src/task/mod.rs b/library/core/src/task/mod.rs index 3f0080e3832e1..f1a789e32a7a7 100644 --- a/library/core/src/task/mod.rs +++ b/library/core/src/task/mod.rs @@ -8,7 +8,7 @@ pub use self::poll::Poll; mod wake; #[stable(feature = "futures_api", since = "1.36.0")] -pub use self::wake::{Context, RawWaker, RawWakerVTable, Waker}; +pub use self::wake::{Context, ContextBuilder, LocalWaker, RawWaker, RawWakerVTable, Waker}; mod ready; #[stable(feature = "ready_macro", since = "1.64.0")] diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 077852b0120c4..4dedcbe47a02f 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -1,5 +1,7 @@ #![stable(feature = "futures_api", since = "1.36.0")] +use crate::mem::transmute; + use crate::fmt; use crate::marker::PhantomData; use crate::ptr; @@ -60,6 +62,21 @@ impl RawWaker { pub fn vtable(&self) -> &'static RawWakerVTable { self.vtable } + + #[unstable(feature = "noop_waker", issue = "98286")] + const NOOP: RawWaker = { + const VTABLE: RawWakerVTable = RawWakerVTable::new( + // Cloning just returns a new no-op raw waker + |_| RawWaker::NOOP, + // `wake` does nothing + |_| {}, + // `wake_by_ref` does nothing + |_| {}, + // Dropping does nothing as we don't allocate anything + |_| {}, + ); + RawWaker::new(ptr::null(), &VTABLE) + }; } /// A virtual function pointer table (vtable) that specifies the behavior @@ -177,7 +194,8 @@ impl RawWakerVTable { #[stable(feature = "futures_api", since = "1.36.0")] #[lang = "Context"] pub struct Context<'a> { - waker: &'a Waker, + waker: Option<&'a Waker>, + local_waker: Option<&'a LocalWaker>, // Ensure we future-proof against variance changes by forcing // the lifetime to be invariant (argument-position lifetimes // are contravariant while return-position lifetimes are @@ -195,16 +213,36 @@ impl<'a> Context<'a> { #[must_use] #[inline] pub const fn from_waker(waker: &'a Waker) -> Self { - Context { waker, _marker: PhantomData, _marker2: PhantomData } + ContextBuilder::new().waker(waker).build() } /// Returns a reference to the [`Waker`] for the current task. + /// + /// Note that if the waker does not need to be sent across threads, it + /// is preferable to call `local_waker`, which is more portable and + /// potentially more efficient. + /// + /// # Panics + /// This function will panic if no `Waker` was set on the context. This happens if + /// the executor does not support working with thread safe wakers. An alternative + /// may be to call [`.local_waker()`](Context::local_waker) instead. #[stable(feature = "futures_api", since = "1.36.0")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] #[must_use] #[inline] pub const fn waker(&self) -> &'a Waker { - &self.waker + &self + .waker + .expect("no waker was set on this context, consider calling `local_waker` instead.") + } + /// Returns a reference to the [`LocalWaker`] for the current task. + #[unstable(feature = "local_waker", issue = "none")] + pub fn local_waker(&self) -> &'a LocalWaker { + // Safety: + // It is safe to transmute a `&Waker` into a `&LocalWaker` since both are a transparent + // wrapper around a local waker. Also, the Option<&Waker> here cannot be None since it is + // impossible to construct a Context without any waker set. + self.local_waker.unwrap_or_else(|| unsafe { transmute(self.waker) }) } } @@ -215,6 +253,94 @@ impl fmt::Debug for Context<'_> { } } +/// A Builder used to construct a `Context` instance +/// with support for `LocalWaker`. +/// +/// # Examples +/// ``` +/// #![feature(local_waker)] +/// #![feature(noop_waker)] +/// use std::task::{ContextBuilder, LocalWaker, Waker}; +/// +/// let local_waker = LocalWaker::noop(); +/// let waker = Waker::noop(); +/// +/// let context = ContextBuilder::default() +/// .local_waker(&local_waker) +/// .waker(&waker) +/// .build(); +/// ``` +#[unstable(feature = "local_waker", issue = "none")] +#[derive(Default, Debug)] +pub struct ContextBuilder<'a> { + waker: Option<&'a Waker>, + local_waker: Option<&'a LocalWaker>, +} + +impl<'a> ContextBuilder<'a> { + /// Creates a new empty `ContextBuilder`. + #[inline] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[unstable(feature = "local_waker", issue = "none")] + pub const fn new() -> Self { + ContextBuilder { waker: None, local_waker: None } + } + + /// This field is used to set the value of the waker on `Context`. + #[inline] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[unstable(feature = "local_waker", issue = "none")] + pub const fn waker(self, waker: &'a Waker) -> Self { + Self { waker: Some(waker), ..self } + } + + /// This method is used to set the value for the local waker on `Context`. + /// + /// # Examples + /// ``` + /// #![feature(local_waker)] + /// #![feature(noop_waker)] + /// + /// use std::task; + /// use std::pin; + /// use std::future::Future; + /// + /// let local_waker = task::LocalWaker::noop(); + /// + /// let mut context = task::ContextBuilder::new() + /// .local_waker(&local_waker) + /// .build(); + /// + /// let future = pin::pin!(async { 20 }); + /// + /// let poll = future.poll(&mut context); + /// + /// assert_eq!(poll, task::Poll::Ready(20)); + /// ``` + #[inline] + #[unstable(feature = "local_waker", issue = "none")] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const fn local_waker(self, local_waker: &'a LocalWaker) -> Self { + Self { local_waker: Some(local_waker), ..self } + } + + /// Builds the `Context`. + /// + /// # Panics + /// Panics if no `Waker` or `LocalWaker` is set. + #[inline] + #[unstable(feature = "local_waker", issue = "none")] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const fn build(self) -> Context<'a> { + let ContextBuilder { waker, local_waker } = self; + assert!( + waker.is_some() || local_waker.is_some(), + "at least one waker must be set with either the `local_waker` or `waker` methods on `ContextBuilder`." + ); + Context { waker, local_waker, _marker: PhantomData, _marker2: PhantomData } + } +} + /// A `Waker` is a handle for waking up a task by notifying its executor that it /// is ready to be run. /// @@ -229,7 +355,8 @@ impl fmt::Debug for Context<'_> { /// Implements [`Clone`], [`Send`], and [`Sync`]; therefore, a waker may be invoked /// from any thread, including ones not in any way managed by the executor. For example, /// this might be done to wake a future when a blocking function call completes on another -/// thread. +/// thread. If the waker does not need to be moved across threads, it is better to use +/// [`LocalWaker`], which the executor may use to skip unnecessary memory synchronization. /// /// Note that it is preferable to use `waker.clone_from(&new_waker)` instead /// of `*waker = new_waker.clone()`, as the former will avoid cloning the waker @@ -354,25 +481,8 @@ impl Waker { #[must_use] #[unstable(feature = "noop_waker", issue = "98286")] pub const fn noop() -> &'static Waker { - // Ideally all this data would be explicitly `static` because it is used by reference and - // only ever needs one copy. But `const fn`s (and `const` items) cannot refer to statics, - // even though their values can be promoted to static. (That might change; see #119618.) - // An alternative would be a `pub static NOOP: &Waker`, but associated static items are not - // currently allowed either, and making it non-associated would be unergonomic. - const VTABLE: RawWakerVTable = RawWakerVTable::new( - // Cloning just returns a new no-op raw waker - |_| RAW, - // `wake` does nothing - |_| {}, - // `wake_by_ref` does nothing - |_| {}, - // Dropping does nothing as we don't allocate anything - |_| {}, - ); - const RAW: RawWaker = RawWaker::new(ptr::null(), &VTABLE); - const WAKER_REF: &Waker = &Waker { waker: RAW }; - - WAKER_REF + const WAKER: &Waker = &Waker { waker: RawWaker::NOOP }; + WAKER } /// Get a reference to the underlying [`RawWaker`]. @@ -425,3 +535,208 @@ impl fmt::Debug for Waker { .finish() } } + +/// A `LocalWaker` is analogous to a [`Waker`], but it does not implement [`Send`] or [`Sync`]. +/// This handle encapsulates a [`RawWaker`] instance, which defines the +/// executor-specific wakeup behavior. +/// +/// Local wakers can be requested from a `Context` with the [`local_waker`] method. +/// +/// The typical life of a `LocalWaker` is that it is constructed by an executor, wrapped in a +/// [`Context`], then passed to [`Future::poll()`]. Then, if the future chooses to return +/// [`Poll::Pending`], it must also store the waker somehow and call [`Waker::wake()`] when +/// the future should be polled again. +/// +/// Implements [`Clone`], but neither [`Send`] nor [`Sync`]; therefore, a local waker may +/// not be moved to other threads. In general, when deciding to use wakers or local wakers, +/// local wakers are preferable unless the waker needs to be sent across threads. This is because +/// wakers can incur in additional cost related to memory synchronization, and not all executors +/// may support wakers. +/// +/// Note that it is preferable to use `local_waker.clone_from(&new_waker)` instead +/// of `*local_waker = new_waker.clone()`, as the former will avoid cloning the waker +/// unnecessarily if the two wakers [wake the same task](Self::will_wake). +/// +/// # Examples +/// +/// ``` +/// #![feature(local_waker)] +/// use std::future::{Future, poll_fn}; +/// use std::task::Poll; +/// +/// // a future that returns pending once. +/// fn yield_now() -> impl Future + Unpin { +/// let mut yielded = false; +/// poll_fn(move |cx| { +/// if !yielded { +/// yielded = true; +/// cx.local_waker().wake_by_ref(); +/// return Poll::Pending; +/// } +/// return Poll::Ready(()) +/// }) +/// } +/// # async { +/// yield_now().await; +/// # }; +/// ``` +/// +/// [`Future::poll()`]: core::future::Future::poll +/// [`Poll::Pending`]: core::task::Poll::Pending +/// [`local_waker`]: core::task::Context::local_waker +#[unstable(feature = "local_waker", issue = "none")] +#[repr(transparent)] +pub struct LocalWaker { + waker: RawWaker, +} + +#[unstable(feature = "local_waker", issue = "none")] +impl Unpin for LocalWaker {} + +impl LocalWaker { + /// Creates a new `LocalWaker` from [`RawWaker`]. + /// + /// The behavior of the returned `Waker` is undefined if the contract defined + /// in [`RawWaker`]'s and [`RawWakerVTable`]'s documentation is not upheld. + /// Therefore this method is unsafe. + #[inline] + #[must_use] + #[stable(feature = "futures_api", since = "1.36.0")] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const unsafe fn from_raw(waker: RawWaker) -> LocalWaker { + Self { waker } + } + + /// Wake up the task associated with this `LocalWaker`. + /// + /// As long as the executor keeps running and the task is not finished, it is + /// guaranteed that each invocation of [`wake()`](Self::wake) (or + /// [`wake_by_ref()`](Self::wake_by_ref)) will be followed by at least one + /// [`poll()`] of the task to which this `Waker` belongs. This makes + /// it possible to temporarily yield to other tasks while running potentially + /// unbounded processing loops. + /// + /// Note that the above implies that multiple wake-ups may be coalesced into a + /// single [`poll()`] invocation by the runtime. + /// + /// Also note that yielding to competing tasks is not guaranteed: it is the + /// executor’s choice which task to run and the executor may choose to run the + /// current task again. + /// + /// [`poll()`]: crate::future::Future::poll + #[inline] + #[stable(feature = "futures_api", since = "1.36.0")] + pub fn wake(self) { + // The actual wakeup call is delegated through a virtual function call + // to the implementation which is defined by the executor. + let wake = self.waker.vtable.wake; + let data = self.waker.data; + + // Don't call `drop` -- the waker will be consumed by `wake`. + crate::mem::forget(self); + + // SAFETY: This is safe because `Waker::from_raw` is the only way + // to initialize `wake` and `data` requiring the user to acknowledge + // that the contract of `RawWaker` is upheld. + unsafe { (wake)(data) }; + } + + /// Creates a new `LocalWaker` that does nothing when `wake` is called. + /// + /// This is mostly useful for writing tests that need a [`Context`] to poll + /// some futures, but are not expecting those futures to wake the waker or + /// do not need to do anything specific if it happens. + /// + /// # Examples + /// + /// ``` + /// #![feature(local_waker)] + /// #![feature(noop_waker)] + /// + /// use std::future::Future; + /// use std::task::{ContextBuilder, LocalWaker}; + /// + /// let mut cx = task::ContextBuilder::new() + /// .local_waker(LocalWaker::noop()) + /// .build(); + /// + /// let mut future = Box::pin(async { 10 }); + /// assert_eq!(future.as_mut().poll(&mut cx), task::Poll::Ready(10)); + /// ``` + #[inline] + #[must_use] + #[unstable(feature = "noop_waker", issue = "98286")] + pub const fn noop() -> &'static LocalWaker { + const WAKER: &LocalWaker = &LocalWaker { waker: RawWaker::NOOP }; + WAKER + } + + /// Get a reference to the underlying [`RawWaker`]. + #[inline] + #[must_use] + #[unstable(feature = "waker_getters", issue = "87021")] + pub fn as_raw(&self) -> &RawWaker { + &self.waker + } + + /// Returns `true` if this `LocalWaker` and another `LocalWaker` would awake the same task. + /// + /// This function works on a best-effort basis, and may return false even + /// when the `Waker`s would awaken the same task. However, if this function + /// returns `true`, it is guaranteed that the `Waker`s will awaken the same task. + /// + /// This function is primarily used for optimization purposes — for example, + /// this type's [`clone_from`](Self::clone_from) implementation uses it to + /// avoid cloning the waker when they would wake the same task anyway. + #[inline] + #[must_use] + #[stable(feature = "futures_api", since = "1.36.0")] + pub fn will_wake(&self, other: &LocalWaker) -> bool { + self.waker == other.waker + } + + /// Wake up the task associated with this `LocalWaker` without consuming the `LocalWaker`. + /// + /// This is similar to [`wake()`](Self::wake), but may be slightly less efficient in + /// the case where an owned `Waker` is available. This method should be preferred to + /// calling `waker.clone().wake()`. + #[inline] + #[stable(feature = "futures_api", since = "1.36.0")] + pub fn wake_by_ref(&self) { + // The actual wakeup call is delegated through a virtual function call + // to the implementation which is defined by the executor. + + // SAFETY: see `wake` + unsafe { (self.waker.vtable.wake_by_ref)(self.waker.data) } + } +} +#[unstable(feature = "local_waker", issue = "none")] +impl Clone for LocalWaker { + #[inline] + fn clone(&self) -> Self { + LocalWaker { + // SAFETY: This is safe because `Waker::from_raw` is the only way + // to initialize `clone` and `data` requiring the user to acknowledge + // that the contract of [`RawWaker`] is upheld. + waker: unsafe { (self.waker.vtable.clone)(self.waker.data) }, + } + } + + #[inline] + fn clone_from(&mut self, source: &Self) { + if !self.will_wake(source) { + *self = source.clone(); + } + } +} + +#[stable(feature = "futures_api", since = "1.36.0")] +impl fmt::Debug for LocalWaker { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let vtable_ptr = self.waker.vtable as *const RawWakerVTable; + f.debug_struct("LocalWaker") + .field("data", &self.waker.data) + .field("vtable", &vtable_ptr) + .finish() + } +} From 0cb5e2fe5f080f40059e673cec6f0bf27f4c7596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Thu, 30 Nov 2023 19:05:12 -0300 Subject: [PATCH 02/16] perf: move null check from local_wake() to build() --- library/core/src/task/wake.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 4dedcbe47a02f..fa2b730b31c44 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -195,7 +195,7 @@ impl RawWakerVTable { #[lang = "Context"] pub struct Context<'a> { waker: Option<&'a Waker>, - local_waker: Option<&'a LocalWaker>, + local_waker: &'a LocalWaker, // Ensure we future-proof against variance changes by forcing // the lifetime to be invariant (argument-position lifetimes // are contravariant while return-position lifetimes are @@ -238,11 +238,7 @@ impl<'a> Context<'a> { /// Returns a reference to the [`LocalWaker`] for the current task. #[unstable(feature = "local_waker", issue = "none")] pub fn local_waker(&self) -> &'a LocalWaker { - // Safety: - // It is safe to transmute a `&Waker` into a `&LocalWaker` since both are a transparent - // wrapper around a local waker. Also, the Option<&Waker> here cannot be None since it is - // impossible to construct a Context without any waker set. - self.local_waker.unwrap_or_else(|| unsafe { transmute(self.waker) }) + &self.local_waker } } @@ -325,7 +321,7 @@ impl<'a> ContextBuilder<'a> { } /// Builds the `Context`. - /// + /// /// # Panics /// Panics if no `Waker` or `LocalWaker` is set. #[inline] @@ -337,6 +333,16 @@ impl<'a> ContextBuilder<'a> { waker.is_some() || local_waker.is_some(), "at least one waker must be set with either the `local_waker` or `waker` methods on `ContextBuilder`." ); + let local_waker = match local_waker { + Some(local_waker) => local_waker, + None => { + // SAFETY: + // It is safe to transmute a `&Waker` into a `&LocalWaker` since both are a transparent + // wrapper around a local waker. Also, the Option<&Waker> here cannot be None because + // of the previous assert. + unsafe { transmute(self.waker) } + } + }; Context { waker, local_waker, _marker: PhantomData, _marker2: PhantomData } } } @@ -576,9 +582,10 @@ impl fmt::Debug for Waker { /// return Poll::Ready(()) /// }) /// } -/// # async { +/// # #[allow(unused_must_use)] +/// # async fn __() { /// yield_now().await; -/// # }; +/// # } /// ``` /// /// [`Future::poll()`]: core::future::Future::poll From 232cc2b4e4ba0a3c0e5e45fed64f0783201805f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Wed, 6 Dec 2023 12:11:28 -0300 Subject: [PATCH 03/16] refactor: remove in favor of and to make the API infallible. --- library/core/src/task/wake.rs | 81 +++++++++++++---------------------- 1 file changed, 29 insertions(+), 52 deletions(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index fa2b730b31c44..da36194d462d3 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -213,7 +213,7 @@ impl<'a> Context<'a> { #[must_use] #[inline] pub const fn from_waker(waker: &'a Waker) -> Self { - ContextBuilder::new().waker(waker).build() + ContextBuilder::from_waker(waker).build() } /// Returns a reference to the [`Waker`] for the current task. @@ -261,28 +261,43 @@ impl fmt::Debug for Context<'_> { /// let local_waker = LocalWaker::noop(); /// let waker = Waker::noop(); /// -/// let context = ContextBuilder::default() -/// .local_waker(&local_waker) +/// let context = ContextBuilder::from_local_waker(&local_waker) /// .waker(&waker) /// .build(); +/// +/// let future = pin::pin!(async { 20 }); +/// let poll = future.poll(&mut context); +/// assert_eq!(poll, task::Poll::Ready(20)); +/// /// ``` #[unstable(feature = "local_waker", issue = "none")] -#[derive(Default, Debug)] +#[derive(Debug)] pub struct ContextBuilder<'a> { waker: Option<&'a Waker>, - local_waker: Option<&'a LocalWaker>, + local_waker: &'a LocalWaker, } impl<'a> ContextBuilder<'a> { - /// Creates a new empty `ContextBuilder`. + /// Create a ContextBuilder from a Waker. + #[inline] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[unstable(feature = "local_waker", issue = "none")] + pub const fn from_waker(waker: &'a Waker) -> Self { + // SAFETY: LocalWaker is just Waker without thread safety + let local_waker = unsafe { transmute(waker) }; + Self { waker: Some(waker), local_waker } + } + + /// Create a ContextBuilder from a LocalWaker. #[inline] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] #[unstable(feature = "local_waker", issue = "none")] - pub const fn new() -> Self { - ContextBuilder { waker: None, local_waker: None } + pub const fn from_local_waker(local_waker: &'a LocalWaker) -> Self { + Self { local_waker, waker: None } } /// This field is used to set the value of the waker on `Context`. + #[inline] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] #[unstable(feature = "local_waker", issue = "none")] @@ -291,58 +306,19 @@ impl<'a> ContextBuilder<'a> { } /// This method is used to set the value for the local waker on `Context`. - /// - /// # Examples - /// ``` - /// #![feature(local_waker)] - /// #![feature(noop_waker)] - /// - /// use std::task; - /// use std::pin; - /// use std::future::Future; - /// - /// let local_waker = task::LocalWaker::noop(); - /// - /// let mut context = task::ContextBuilder::new() - /// .local_waker(&local_waker) - /// .build(); - /// - /// let future = pin::pin!(async { 20 }); - /// - /// let poll = future.poll(&mut context); - /// - /// assert_eq!(poll, task::Poll::Ready(20)); - /// ``` #[inline] #[unstable(feature = "local_waker", issue = "none")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn local_waker(self, local_waker: &'a LocalWaker) -> Self { - Self { local_waker: Some(local_waker), ..self } + Self { local_waker, ..self } } /// Builds the `Context`. - /// - /// # Panics - /// Panics if no `Waker` or `LocalWaker` is set. #[inline] #[unstable(feature = "local_waker", issue = "none")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn build(self) -> Context<'a> { let ContextBuilder { waker, local_waker } = self; - assert!( - waker.is_some() || local_waker.is_some(), - "at least one waker must be set with either the `local_waker` or `waker` methods on `ContextBuilder`." - ); - let local_waker = match local_waker { - Some(local_waker) => local_waker, - None => { - // SAFETY: - // It is safe to transmute a `&Waker` into a `&LocalWaker` since both are a transparent - // wrapper around a local waker. Also, the Option<&Waker> here cannot be None because - // of the previous assert. - unsafe { transmute(self.waker) } - } - }; Context { waker, local_waker, _marker: PhantomData, _marker2: PhantomData } } } @@ -549,8 +525,8 @@ impl fmt::Debug for Waker { /// Local wakers can be requested from a `Context` with the [`local_waker`] method. /// /// The typical life of a `LocalWaker` is that it is constructed by an executor, wrapped in a -/// [`Context`], then passed to [`Future::poll()`]. Then, if the future chooses to return -/// [`Poll::Pending`], it must also store the waker somehow and call [`Waker::wake()`] when +/// [`Context`] using [`ContextBuilder`], then passed to [`Future::poll()`]. Then, if the future chooses to return +/// [`Poll::Pending`], it must also store the waker somehow and call [`LocalWaker::wake()`] when /// the future should be polled again. /// /// Implements [`Clone`], but neither [`Send`] nor [`Sync`]; therefore, a local waker may @@ -564,7 +540,7 @@ impl fmt::Debug for Waker { /// unnecessarily if the two wakers [wake the same task](Self::will_wake). /// /// # Examples -/// +/// Usage of a local waker to implement a future /// ``` /// #![feature(local_waker)] /// use std::future::{Future, poll_fn}; @@ -582,12 +558,13 @@ impl fmt::Debug for Waker { /// return Poll::Ready(()) /// }) /// } +/// /// # #[allow(unused_must_use)] /// # async fn __() { /// yield_now().await; /// # } /// ``` -/// +/// /// [`Future::poll()`]: core::future::Future::poll /// [`Poll::Pending`]: core::task::Poll::Pending /// [`local_waker`]: core::task::Context::local_waker From 403718b19de6ad0979833fd2cf6074f2a1b50c8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Wed, 6 Dec 2023 14:09:16 -0300 Subject: [PATCH 04/16] feat: add try_waker and From<&mut Context> for ContextBuilder to allow the extention of contexts by futures --- library/alloc/src/task.rs | 20 +++++---- library/core/src/task/wake.rs | 79 +++++++++++++++++++++++++++++------ 2 files changed, 78 insertions(+), 21 deletions(-) diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 16ae5241da8a3..736a55700f63e 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -165,16 +165,15 @@ fn raw_waker(waker: Arc) -> RawWaker { /// # Examples /// -/// A -/// /// This is a simplified example of a `spawn` and a `block_on` function. The `spawn` function /// is used to push new tasks onto the run queue, while the block on function will remove them /// and poll them. When a task is woken, it will put itself back on the run queue to be polled by the executor. /// /// **Note:** A real world example would interlieve poll calls with calls to an io reactor to wait for events instead -/// of spinning on a loop. +/// of spinning on a loop. /// /// ```rust +/// #![feature(local_waker)] /// use std::task::{LocalWake, ContextBuilder, LocalWaker}; /// use std::future::Future; /// use std::pin::Pin; @@ -204,9 +203,9 @@ fn raw_waker(waker: Arc) -> RawWaker { /// where /// F: Future + 'static + Send + Sync /// { -/// let task = Rc::new(Box::pin(future)); +/// let task = RefCell::new(Box::pin(future)); /// RUN_QUEUE.with_borrow_mut(|queue| { -/// queue.push_back(task) +/// queue.push_back(Rc::new(Task(task))); /// }); /// } /// @@ -221,19 +220,22 @@ fn raw_waker(waker: Arc) -> RawWaker { /// return; /// }; /// // cast the Rc into a `LocalWaker` -/// let waker: LocalWaker = task.into(); +/// let waker: LocalWaker = task.clone().into(); /// // Build the context using `ContextBuilder` -/// let mut cx = ContextBuilder::new() -/// .local_waker(&waker) +/// let mut cx = ContextBuilder::from_local_waker(&waker) /// .build(); /// /// // Poll the task -/// task.0 +/// let _ = task.0 /// .borrow_mut() /// .as_mut() /// .poll(&mut cx); /// } /// } +/// +/// block_on(async { +/// println!("hello world"); +/// }); /// ``` /// #[unstable(feature = "local_waker", issue = "none")] diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index da36194d462d3..fa4590f18ec98 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -225,21 +225,32 @@ impl<'a> Context<'a> { /// # Panics /// This function will panic if no `Waker` was set on the context. This happens if /// the executor does not support working with thread safe wakers. An alternative - /// may be to call [`.local_waker()`](Context::local_waker) instead. + /// may be to call [`.local_waker()`](Context::local_waker) instead. For a fallible + /// version of this function see [`.try_waker()`](Context::try_waker). + #[inline] + #[must_use] #[stable(feature = "futures_api", since = "1.36.0")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] - #[must_use] - #[inline] pub const fn waker(&self) -> &'a Waker { &self .waker .expect("no waker was set on this context, consider calling `local_waker` instead.") } /// Returns a reference to the [`LocalWaker`] for the current task. + #[inline] #[unstable(feature = "local_waker", issue = "none")] - pub fn local_waker(&self) -> &'a LocalWaker { + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const fn local_waker(&self) -> &'a LocalWaker { &self.local_waker } + /// Returns a `Some(&Waker)` if a waker was defined on the `Context`, + /// otherwise it returns `None`. + #[inline] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[unstable(feature = "local_waker", issue = "none")] + pub const fn try_waker(&self) -> Option<&'a Waker> { + self.waker + } } #[stable(feature = "futures_api", since = "1.36.0")] @@ -256,18 +267,19 @@ impl fmt::Debug for Context<'_> { /// ``` /// #![feature(local_waker)] /// #![feature(noop_waker)] -/// use std::task::{ContextBuilder, LocalWaker, Waker}; -/// +/// use std::task::{ContextBuilder, LocalWaker, Waker, Poll}; +/// use std::future::Future; +/// /// let local_waker = LocalWaker::noop(); /// let waker = Waker::noop(); /// -/// let context = ContextBuilder::from_local_waker(&local_waker) +/// let mut cx = ContextBuilder::from_local_waker(&local_waker) /// .waker(&waker) /// .build(); /// -/// let future = pin::pin!(async { 20 }); -/// let poll = future.poll(&mut context); -/// assert_eq!(poll, task::Poll::Ready(20)); +/// let mut future = std::pin::pin!(async { 20 }); +/// let poll = future.as_mut().poll(&mut cx); +/// assert_eq!(poll, Poll::Ready(20)); /// /// ``` #[unstable(feature = "local_waker", issue = "none")] @@ -323,6 +335,50 @@ impl<'a> ContextBuilder<'a> { } } +/// Construct a `ContextBuilder`` from a `Context`. This is useful for +/// overriding values from a context. +/// +/// # Examples +/// An example of a future that allows to set a Waker on Context if none was defined. +/// This can be used to await futures that require a `Waker` even if the runtime does not +/// support `Waker`. +/// ```rust +/// #![feature(noop_waker, local_waker)] +/// use std::task::{Waker, ContextBuilder}; +/// use std::future::{poll_fn, Future}; +/// use std::pin::pin; +/// +/// async fn with_waker(f: F, waker: &Waker) -> F::Output +/// where +/// F: Future +/// { +/// let mut f = pin!(f); +/// poll_fn(move |cx| { +/// let has_waker = cx.try_waker().is_some(); +/// if has_waker { +/// return f.as_mut().poll(cx); +/// } +/// +/// let mut cx = ContextBuilder::from(cx) +/// .waker(&waker) +/// .build(); +/// f.as_mut().poll(&mut cx) +/// }).await +/// } +/// +/// # async fn __() { +/// with_waker(async { /* ... */ }, &Waker::noop()).await; +/// # } +/// ``` +#[unstable(feature = "local_waker", issue = "none")] +impl<'a> From<&mut Context<'a>> for ContextBuilder<'a> { + #[inline] + fn from(value: &mut Context<'a>) -> Self { + let Context { waker, local_waker, .. } = *value; + ContextBuilder { waker, local_waker } + } +} + /// A `Waker` is a handle for waking up a task by notifying its executor that it /// is ready to be run. /// @@ -559,12 +615,11 @@ impl fmt::Debug for Waker { /// }) /// } /// -/// # #[allow(unused_must_use)] /// # async fn __() { /// yield_now().await; /// # } /// ``` -/// +/// /// [`Future::poll()`]: core::future::Future::poll /// [`Poll::Pending`]: core::task::Poll::Pending /// [`local_waker`]: core::task::Context::local_waker From 2012d4b70370d86c9ed0c60a2ad6bf03d9fc5157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Wed, 6 Dec 2023 15:19:24 -0300 Subject: [PATCH 05/16] fix: make LocalWake available in targets that don't support atomics by removing a #[cfg(target_has_atomic = ptr)] --- library/alloc/src/lib.rs | 2 +- library/alloc/src/task.rs | 18 +++++++++++------- library/core/src/task/wake.rs | 4 ++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index c8b4cebdf8987..878aedce3a6a9 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -254,7 +254,7 @@ pub mod str; pub mod string; #[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] pub mod sync; -#[cfg(all(not(no_global_oom_handling), not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +#[cfg(all(not(no_global_oom_handling), not(no_rc), not(no_sync)))] pub mod task; #[cfg(test)] mod tests; diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 736a55700f63e..9db4c9195052b 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -2,15 +2,17 @@ //! Types and Traits for working with asynchronous tasks. //! -//! **Note**: This module is only available on platforms that support atomic -//! loads and stores of pointers. This may be detected at compile time using +//! **Note**: Some of the types in this module are only available +//! on platforms that support atomic loads and stores of pointers. +//! This may be detected at compile time using //! `#[cfg(target_has_atomic = "ptr")]`. use core::mem::ManuallyDrop; -use core::task::{LocalWaker, RawWaker, RawWakerVTable, Waker}; - +use core::task::{LocalWaker, RawWaker, RawWakerVTable}; use crate::rc::Rc; -use crate::sync::Arc; + +#[cfg(target_has_atomic = "ptr")] +use core::{task::Waker, sync::Arc}; /// The implementation of waking a task on an executor. /// @@ -74,6 +76,7 @@ use crate::sync::Arc; /// println!("Hi from inside a future!"); /// }); /// ``` +#[cfg(target_has_atomic = "ptr")] #[stable(feature = "wake_trait", since = "1.51.0")] pub trait Wake { /// Wake this task. @@ -92,7 +95,7 @@ pub trait Wake { self.clone().wake(); } } - +#[cfg(target_has_atomic = "ptr")] #[stable(feature = "wake_trait", since = "1.51.0")] impl From> for Waker { /// Use a `Wake`-able type as a `Waker`. @@ -104,7 +107,7 @@ impl From> for Waker { unsafe { Waker::from_raw(raw_waker(waker)) } } } - +#[cfg(target_has_atomic = "ptr")] #[stable(feature = "wake_trait", since = "1.51.0")] impl From> for RawWaker { /// Use a `Wake`-able type as a `RawWaker`. @@ -120,6 +123,7 @@ impl From> for RawWaker { // the safety of `From> for Waker` does not depend on the correct // trait dispatch - instead both impls call this function directly and // explicitly. +#[cfg(target_has_atomic = "ptr")] #[inline(always)] fn raw_waker(waker: Arc) -> RawWaker { // Increment the reference count of the arc to clone it. diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index fa4590f18ec98..5696b63e5faf7 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -347,7 +347,7 @@ impl<'a> ContextBuilder<'a> { /// use std::task::{Waker, ContextBuilder}; /// use std::future::{poll_fn, Future}; /// use std::pin::pin; -/// +/// /// async fn with_waker(f: F, waker: &Waker) -> F::Output /// where /// F: Future @@ -365,7 +365,7 @@ impl<'a> ContextBuilder<'a> { /// f.as_mut().poll(&mut cx) /// }).await /// } -/// +/// /// # async fn __() { /// with_waker(async { /* ... */ }, &Waker::noop()).await; /// # } From 0cb7a0a90ef5862b87083546accb12467b279eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Thu, 14 Dec 2023 23:15:01 -0300 Subject: [PATCH 06/16] chore: add tracking issue number to local waker feature --- library/alloc/src/task.rs | 22 ++++++++++++---------- library/core/src/task/wake.rs | 28 ++++++++++++++-------------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 9db4c9195052b..ce7aa3c44dc84 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -3,16 +3,18 @@ //! Types and Traits for working with asynchronous tasks. //! //! **Note**: Some of the types in this module are only available -//! on platforms that support atomic loads and stores of pointers. -//! This may be detected at compile time using +//! on platforms that support atomic loads and stores of pointers. +//! This may be detected at compile time using //! `#[cfg(target_has_atomic = "ptr")]`. +use crate::rc::Rc; use core::mem::ManuallyDrop; use core::task::{LocalWaker, RawWaker, RawWakerVTable}; -use crate::rc::Rc; #[cfg(target_has_atomic = "ptr")] -use core::{task::Waker, sync::Arc}; +use crate::sync::Arc; +#[cfg(target_has_atomic = "ptr")] +use core::task::Waker; /// The implementation of waking a task on an executor. /// @@ -174,7 +176,7 @@ fn raw_waker(waker: Arc) -> RawWaker { /// and poll them. When a task is woken, it will put itself back on the run queue to be polled by the executor. /// /// **Note:** A real world example would interlieve poll calls with calls to an io reactor to wait for events instead -/// of spinning on a loop. +/// of spinning on a loop. /// /// ```rust /// #![feature(local_waker)] @@ -242,10 +244,10 @@ fn raw_waker(waker: Arc) -> RawWaker { /// }); /// ``` /// -#[unstable(feature = "local_waker", issue = "none")] +#[unstable(feature = "local_waker", issue = "118959")] pub trait LocalWake { /// Wake this task. - #[unstable(feature = "local_waker", issue = "none")] + #[unstable(feature = "local_waker", issue = "118959")] fn wake(self: Rc); /// Wake this task without consuming the local waker. @@ -255,13 +257,13 @@ pub trait LocalWake { /// [`Rc`] and calls [`wake`] on the clone. /// /// [`wake`]: Rc::wake - #[unstable(feature = "local_waker", issue = "none")] + #[unstable(feature = "local_waker", issue = "118959")] fn wake_by_ref(self: &Rc) { self.clone().wake(); } } -#[unstable(feature = "local_waker", issue = "none")] +#[unstable(feature = "local_waker", issue = "118959")] impl From> for LocalWaker { /// Use a `Wake`-able type as a `LocalWaker`. /// @@ -273,7 +275,7 @@ impl From> for LocalWaker { } } #[allow(ineffective_unstable_trait_impl)] -#[unstable(feature = "local_waker", issue = "none")] +#[unstable(feature = "local_waker", issue = "118959")] impl From> for RawWaker { /// Use a `Wake`-able type as a `RawWaker`. /// diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 5696b63e5faf7..c02504b386e16 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -238,7 +238,7 @@ impl<'a> Context<'a> { } /// Returns a reference to the [`LocalWaker`] for the current task. #[inline] - #[unstable(feature = "local_waker", issue = "none")] + #[unstable(feature = "local_waker", issue = "118959")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn local_waker(&self) -> &'a LocalWaker { &self.local_waker @@ -247,7 +247,7 @@ impl<'a> Context<'a> { /// otherwise it returns `None`. #[inline] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] - #[unstable(feature = "local_waker", issue = "none")] + #[unstable(feature = "local_waker", issue = "118959")] pub const fn try_waker(&self) -> Option<&'a Waker> { self.waker } @@ -282,7 +282,7 @@ impl fmt::Debug for Context<'_> { /// assert_eq!(poll, Poll::Ready(20)); /// /// ``` -#[unstable(feature = "local_waker", issue = "none")] +#[unstable(feature = "local_waker", issue = "118959")] #[derive(Debug)] pub struct ContextBuilder<'a> { waker: Option<&'a Waker>, @@ -293,7 +293,7 @@ impl<'a> ContextBuilder<'a> { /// Create a ContextBuilder from a Waker. #[inline] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] - #[unstable(feature = "local_waker", issue = "none")] + #[unstable(feature = "local_waker", issue = "118959")] pub const fn from_waker(waker: &'a Waker) -> Self { // SAFETY: LocalWaker is just Waker without thread safety let local_waker = unsafe { transmute(waker) }; @@ -303,7 +303,7 @@ impl<'a> ContextBuilder<'a> { /// Create a ContextBuilder from a LocalWaker. #[inline] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] - #[unstable(feature = "local_waker", issue = "none")] + #[unstable(feature = "local_waker", issue = "118959")] pub const fn from_local_waker(local_waker: &'a LocalWaker) -> Self { Self { local_waker, waker: None } } @@ -312,14 +312,14 @@ impl<'a> ContextBuilder<'a> { #[inline] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] - #[unstable(feature = "local_waker", issue = "none")] + #[unstable(feature = "local_waker", issue = "118959")] pub const fn waker(self, waker: &'a Waker) -> Self { Self { waker: Some(waker), ..self } } /// This method is used to set the value for the local waker on `Context`. #[inline] - #[unstable(feature = "local_waker", issue = "none")] + #[unstable(feature = "local_waker", issue = "118959")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn local_waker(self, local_waker: &'a LocalWaker) -> Self { Self { local_waker, ..self } @@ -327,7 +327,7 @@ impl<'a> ContextBuilder<'a> { /// Builds the `Context`. #[inline] - #[unstable(feature = "local_waker", issue = "none")] + #[unstable(feature = "local_waker", issue = "118959")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn build(self) -> Context<'a> { let ContextBuilder { waker, local_waker } = self; @@ -370,7 +370,7 @@ impl<'a> ContextBuilder<'a> { /// with_waker(async { /* ... */ }, &Waker::noop()).await; /// # } /// ``` -#[unstable(feature = "local_waker", issue = "none")] +#[unstable(feature = "local_waker", issue = "118959")] impl<'a> From<&mut Context<'a>> for ContextBuilder<'a> { #[inline] fn from(value: &mut Context<'a>) -> Self { @@ -623,19 +623,19 @@ impl fmt::Debug for Waker { /// [`Future::poll()`]: core::future::Future::poll /// [`Poll::Pending`]: core::task::Poll::Pending /// [`local_waker`]: core::task::Context::local_waker -#[unstable(feature = "local_waker", issue = "none")] +#[unstable(feature = "local_waker", issue = "118959")] #[repr(transparent)] pub struct LocalWaker { waker: RawWaker, } -#[unstable(feature = "local_waker", issue = "none")] +#[unstable(feature = "local_waker", issue = "118959")] impl Unpin for LocalWaker {} impl LocalWaker { /// Creates a new `LocalWaker` from [`RawWaker`]. /// - /// The behavior of the returned `Waker` is undefined if the contract defined + /// The behavior of the returned `LocalWaker` is undefined if the contract defined /// in [`RawWaker`]'s and [`RawWakerVTable`]'s documentation is not upheld. /// Therefore this method is unsafe. #[inline] @@ -651,7 +651,7 @@ impl LocalWaker { /// As long as the executor keeps running and the task is not finished, it is /// guaranteed that each invocation of [`wake()`](Self::wake) (or /// [`wake_by_ref()`](Self::wake_by_ref)) will be followed by at least one - /// [`poll()`] of the task to which this `Waker` belongs. This makes + /// [`poll()`] of the task to which this `LocalWaker` belongs. This makes /// it possible to temporarily yield to other tasks while running potentially /// unbounded processing loops. /// @@ -749,7 +749,7 @@ impl LocalWaker { unsafe { (self.waker.vtable.wake_by_ref)(self.waker.data) } } } -#[unstable(feature = "local_waker", issue = "none")] +#[unstable(feature = "local_waker", issue = "118959")] impl Clone for LocalWaker { #[inline] fn clone(&self) -> Self { From f82437396fe2d21ae73134a6faa6c58bd03d96e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Thu, 14 Dec 2023 23:15:27 -0300 Subject: [PATCH 07/16] feat: impl AsRef for Waker. --- library/core/src/task/wake.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index c02504b386e16..e90994df0c720 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -769,6 +769,14 @@ impl Clone for LocalWaker { } } +#[unstable(feature = "local_waker", issue = "118959")] +impl AsRef for Waker { + fn as_ref(&self) -> &LocalWaker { + // SAFETY: LocalWaker is just Waker without thread safety + unsafe { transmute(self) } + } +} + #[stable(feature = "futures_api", since = "1.36.0")] impl fmt::Debug for LocalWaker { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { From 093f80ba7edc53a7e53c949fb0d083f00088d45c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Thu, 14 Dec 2023 23:38:58 -0300 Subject: [PATCH 08/16] chore: fix ci failures --- library/alloc/src/task.rs | 2 +- library/core/src/task/wake.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index ce7aa3c44dc84..611468a9feaab 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -256,7 +256,7 @@ pub trait LocalWake { /// waker, it should override this method. By default, it clones the /// [`Rc`] and calls [`wake`] on the clone. /// - /// [`wake`]: Rc::wake + /// [`wake`]: LocalWaker::wake #[unstable(feature = "local_waker", issue = "118959")] fn wake_by_ref(self: &Rc) { self.clone().wake(); diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index e90994df0c720..54528ad83a04e 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -269,7 +269,7 @@ impl fmt::Debug for Context<'_> { /// #![feature(noop_waker)] /// use std::task::{ContextBuilder, LocalWaker, Waker, Poll}; /// use std::future::Future; -/// +/// /// let local_waker = LocalWaker::noop(); /// let waker = Waker::noop(); /// @@ -347,7 +347,7 @@ impl<'a> ContextBuilder<'a> { /// use std::task::{Waker, ContextBuilder}; /// use std::future::{poll_fn, Future}; /// use std::pin::pin; -/// +/// /// async fn with_waker(f: F, waker: &Waker) -> F::Output /// where /// F: Future @@ -365,7 +365,7 @@ impl<'a> ContextBuilder<'a> { /// f.as_mut().poll(&mut cx) /// }).await /// } -/// +/// /// # async fn __() { /// with_waker(async { /* ... */ }, &Waker::noop()).await; /// # } From ad28f755d82308b1e30f504d241b810f396233d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Fri, 15 Dec 2023 10:20:33 -0300 Subject: [PATCH 09/16] fix: change issue number of waker_getters from #87021 to #96992. --- library/core/src/task/wake.rs | 2 +- src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 54528ad83a04e..3fc97a199a6bf 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -713,7 +713,7 @@ impl LocalWaker { /// Get a reference to the underlying [`RawWaker`]. #[inline] #[must_use] - #[unstable(feature = "waker_getters", issue = "87021")] + #[unstable(feature = "waker_getters", issue = "96992")] pub fn as_raw(&self) -> &RawWaker { &self.waker } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs index 1cb6ff8627a23..c3b806dec4dda 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs @@ -9630,9 +9630,9 @@ The tracking issue for this feature is: [#81944] label: "waker_getters", description: r##"# `waker_getters` -The tracking issue for this feature is: [#87021] +The tracking issue for this feature is: [#96992] -[#87021]: https://github.com/rust-lang/rust/issues/87021 +[#96992]: https://github.com/rust-lang/rust/issues/96992 ------------------------ "##, From 3e373f5ee7ab5cf6d9fe986f697fe27fe90c53ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Fri, 15 Dec 2023 10:30:43 -0300 Subject: [PATCH 10/16] chore: add and !Sync impls for LocalWaker as a stability guarantee. --- library/core/src/task/wake.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 3fc97a199a6bf..40f0b0c51446f 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -787,3 +787,8 @@ impl fmt::Debug for LocalWaker { .finish() } } + +#[unstable(feature = "local_waker", issue = "118959")] +impl !Send for LocalWaker {} +#[unstable(feature = "local_waker", issue = "118959")] +impl !Sync for LocalWaker {} From a8e71f225817d11acd9ba63721d539fb998ef319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Fri, 15 Dec 2023 15:10:15 -0300 Subject: [PATCH 11/16] doc: update thread safety explanation for RawWakerVTable and RawWaker. --- library/core/src/task/wake.rs | 62 ++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 40f0b0c51446f..3fa49d1ea0654 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -7,7 +7,7 @@ use crate::marker::PhantomData; use crate::ptr; /// A `RawWaker` allows the implementor of a task executor to create a [`Waker`] -/// which provides customized wakeup behavior. +/// or a [`LocalWaker`] which provides customized wakeup behavior. /// /// [vtable]: https://en.wikipedia.org/wiki/Virtual_method_table /// @@ -35,9 +35,18 @@ impl RawWaker { /// The value of this pointer will get passed to all functions that are part /// of the `vtable` as the first parameter. /// + /// It is important to consider that the `data` pointer must point to a + /// thread safe type such as an `[Arc]` + /// when used to construct a [`Waker`]. This restriction is lifted when + /// constructing a [`LocalWaker`], which allows using types that do not implement + /// [Send] + [Sync] like `[Rc]`. + /// /// The `vtable` customizes the behavior of a `Waker` which gets created /// from a `RawWaker`. For each operation on the `Waker`, the associated /// function in the `vtable` of the underlying `RawWaker` will be called. + /// + /// [`Arc`]: std::sync::Arc + /// [`Rc`]: std::rc::Rc #[inline] #[rustc_promotable] #[stable(feature = "futures_api", since = "1.36.0")] @@ -90,11 +99,19 @@ impl RawWaker { /// [`RawWaker`] implementation. Calling one of the contained functions using /// any other `data` pointer will cause undefined behavior. /// -/// These functions must all be thread-safe (even though [`RawWaker`] is -/// \![Send] + \![Sync]) -/// because [`Waker`] is [Send] + [Sync], and thus wakers may be moved to -/// arbitrary threads or invoked by `&` reference. For example, this means that if the -/// `clone` and `drop` functions manage a reference count, they must do so atomically. +/// # Thread safety +/// If the [`RawWaker`] will be used to construct a [`Waker`] then +/// these functions must all be thread-safe (even though [`RawWaker`] is +/// \![Send] + \![Sync]). This is because [`Waker`] is [Send] + [Sync], +/// and it may be moved to arbitrary threads or invoked by `&` reference. For example, +/// this means that if the `clone` and `drop` functions manage a reference count, +/// they must do so atomically. +/// +/// However, if the [`RawWaker`] will be used to construct a [`LocalWaker`] instead, then +/// these functions don't need to be thread safe. This means that \![Send] + \![Sync] +/// data can be stored in the data pointer, and reference counting does not need any atomic +/// synchronization. This is because [`LocalWaker`] is not thread safe itself, so it cannot +/// be sent across threads. #[stable(feature = "futures_api", since = "1.36.0")] #[derive(PartialEq, Copy, Clone, Debug)] pub struct RawWakerVTable { @@ -134,16 +151,22 @@ impl RawWakerVTable { /// Creates a new `RawWakerVTable` from the provided `clone`, `wake`, /// `wake_by_ref`, and `drop` functions. /// - /// These functions must all be thread-safe (even though [`RawWaker`] is - /// \![Send] + \![Sync]) - /// because [`Waker`] is [Send] + [Sync], and thus wakers may be moved to - /// arbitrary threads or invoked by `&` reference. For example, this means that if the - /// `clone` and `drop` functions manage a reference count, they must do so atomically. - /// + /// If the [`RawWaker`] will be used to construct a [`Waker`] then + /// these functions must all be thread-safe (even though [`RawWaker`] is + /// \![Send] + \![Sync]). This is because [`Waker`] is [Send] + [Sync], + /// and it may be moved to arbitrary threads or invoked by `&` reference. For example, + /// this means that if the `clone` and `drop` functions manage a reference count, + /// they must do so atomically. + /// + /// However, if the [`RawWaker`] will be used to construct a [`LocalWaker`] instead, then + /// these functions don't need to be thread safe. This means that \![Send] + \![Sync] + /// data can be stored in the data pointer, and reference counting does not need any atomic + /// synchronization. This is because [`LocalWaker`] is not thread safe itself, so it cannot + /// be sent across threads. /// # `clone` /// /// This function will be called when the [`RawWaker`] gets cloned, e.g. when - /// the [`Waker`] in which the [`RawWaker`] is stored gets cloned. + /// the [`Waker`]/[`LocalWaker`] in which the [`RawWaker`] is stored gets cloned. /// /// The implementation of this function must retain all resources that are /// required for this additional instance of a [`RawWaker`] and associated @@ -169,7 +192,7 @@ impl RawWakerVTable { /// /// # `drop` /// - /// This function gets called when a [`Waker`] gets dropped. + /// This function gets called when a [`Waker`]/[`LocalWaker`] gets dropped. /// /// The implementation of this function must make sure to release any /// resources that are associated with this instance of a [`RawWaker`] and @@ -335,13 +358,13 @@ impl<'a> ContextBuilder<'a> { } } -/// Construct a `ContextBuilder`` from a `Context`. This is useful for +/// Construct a [`ContextBuilder`] from a [`Context`]. This is useful for /// overriding values from a context. /// /// # Examples -/// An example of a future that allows to set a Waker on Context if none was defined. -/// This can be used to await futures that require a `Waker` even if the runtime does not -/// support `Waker`. +/// An example of a future that allows to set a [`Waker`] on Context if none was defined. +/// This can be used to await futures that require a [`Waker`] even if the runtime does not +/// support [`Waker`]. /// ```rust /// #![feature(noop_waker, local_waker)] /// use std::task::{Waker, ContextBuilder}; @@ -596,7 +619,7 @@ impl fmt::Debug for Waker { /// unnecessarily if the two wakers [wake the same task](Self::will_wake). /// /// # Examples -/// Usage of a local waker to implement a future +/// Usage of a local waker to implement a future analogous to `std::thread::yield_now()`. /// ``` /// #![feature(local_waker)] /// use std::future::{Future, poll_fn}; @@ -623,6 +646,7 @@ impl fmt::Debug for Waker { /// [`Future::poll()`]: core::future::Future::poll /// [`Poll::Pending`]: core::task::Poll::Pending /// [`local_waker`]: core::task::Context::local_waker + #[unstable(feature = "local_waker", issue = "118959")] #[repr(transparent)] pub struct LocalWaker { From c67a446e72764663892606b131ed35c6e32e7133 Mon Sep 17 00:00:00 2001 From: tvallotton <57121854+tvallotton@users.noreply.github.com> Date: Sat, 13 Jan 2024 10:44:01 -0300 Subject: [PATCH 12/16] fix: Apply suggestions from code review Co-authored-by: Mark Rousskov --- library/alloc/src/task.rs | 6 +++--- library/core/src/task/wake.rs | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 611468a9feaab..7dad2f3f82270 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -168,14 +168,14 @@ fn raw_waker(waker: Arc) -> RawWaker { /// to hold data that does not implement `Send` and `Sync`. Additionally, it saves calls /// to `Arc::clone`, which requires atomic synchronization. /// - +/// /// # Examples /// /// This is a simplified example of a `spawn` and a `block_on` function. The `spawn` function /// is used to push new tasks onto the run queue, while the block on function will remove them /// and poll them. When a task is woken, it will put itself back on the run queue to be polled by the executor. /// -/// **Note:** A real world example would interlieve poll calls with calls to an io reactor to wait for events instead +/// **Note:** A real world example would interleave poll calls with calls to an io reactor to wait for events instead /// of spinning on a loop. /// /// ```rust @@ -221,7 +221,7 @@ fn raw_waker(waker: Arc) -> RawWaker { /// { /// spawn(future); /// loop { -/// let Some(task) = RUN_QUEUE.with_borrow_mut(|queue|queue.pop_front()) else { +/// let Some(task) = RUN_QUEUE.with_borrow_mut(|queue| queue.pop_front()) else { /// // we exit, since there are no more tasks remaining on the queue /// return; /// }; diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 3fa49d1ea0654..da34e070e2922 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -332,7 +332,6 @@ impl<'a> ContextBuilder<'a> { } /// This field is used to set the value of the waker on `Context`. - #[inline] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] #[unstable(feature = "local_waker", issue = "118959")] @@ -598,6 +597,7 @@ impl fmt::Debug for Waker { } /// A `LocalWaker` is analogous to a [`Waker`], but it does not implement [`Send`] or [`Sync`]. +/// /// This handle encapsulates a [`RawWaker`] instance, which defines the /// executor-specific wakeup behavior. /// @@ -646,9 +646,8 @@ impl fmt::Debug for Waker { /// [`Future::poll()`]: core::future::Future::poll /// [`Poll::Pending`]: core::task::Poll::Pending /// [`local_waker`]: core::task::Context::local_waker - #[unstable(feature = "local_waker", issue = "118959")] -#[repr(transparent)] +#[cfg_attr(not(doc), repr(transparent))] // work around https://github.com/rust-lang/rust/issues/66401 pub struct LocalWaker { waker: RawWaker, } From eccb5e7c1bc3b36ed19bbca75ddf80470d3f0caa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Sat, 13 Jan 2024 11:14:04 -0300 Subject: [PATCH 13/16] docs: remove recommendations to use LocalWaker in stable API documentation --- library/core/src/task/wake.rs | 103 +++++++++++++++------------------- 1 file changed, 46 insertions(+), 57 deletions(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index da34e070e2922..e64a25960e05e 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -39,7 +39,7 @@ impl RawWaker { /// thread safe type such as an `[Arc]` /// when used to construct a [`Waker`]. This restriction is lifted when /// constructing a [`LocalWaker`], which allows using types that do not implement - /// [Send] + [Sync] like `[Rc]`. + /// [Send] + [Sync] like `[Rc]`. /// /// The `vtable` customizes the behavior of a `Waker` which gets created /// from a `RawWaker`. For each operation on the `Waker`, the associated @@ -240,16 +240,6 @@ impl<'a> Context<'a> { } /// Returns a reference to the [`Waker`] for the current task. - /// - /// Note that if the waker does not need to be sent across threads, it - /// is preferable to call `local_waker`, which is more portable and - /// potentially more efficient. - /// - /// # Panics - /// This function will panic if no `Waker` was set on the context. This happens if - /// the executor does not support working with thread safe wakers. An alternative - /// may be to call [`.local_waker()`](Context::local_waker) instead. For a fallible - /// version of this function see [`.try_waker()`](Context::try_waker). #[inline] #[must_use] #[stable(feature = "futures_api", since = "1.36.0")] @@ -396,7 +386,7 @@ impl<'a> ContextBuilder<'a> { impl<'a> From<&mut Context<'a>> for ContextBuilder<'a> { #[inline] fn from(value: &mut Context<'a>) -> Self { - let Context { waker, local_waker, .. } = *value; + let Context { waker, local_waker, _marker, _marker2 } = *value; ContextBuilder { waker, local_waker } } } @@ -415,8 +405,7 @@ impl<'a> From<&mut Context<'a>> for ContextBuilder<'a> { /// Implements [`Clone`], [`Send`], and [`Sync`]; therefore, a waker may be invoked /// from any thread, including ones not in any way managed by the executor. For example, /// this might be done to wake a future when a blocking function call completes on another -/// thread. If the waker does not need to be moved across threads, it is better to use -/// [`LocalWaker`], which the executor may use to skip unnecessary memory synchronization. +/// thread. /// /// Note that it is preferable to use `waker.clone_from(&new_waker)` instead /// of `*waker = new_waker.clone()`, as the former will avoid cloning the waker @@ -656,19 +645,6 @@ pub struct LocalWaker { impl Unpin for LocalWaker {} impl LocalWaker { - /// Creates a new `LocalWaker` from [`RawWaker`]. - /// - /// The behavior of the returned `LocalWaker` is undefined if the contract defined - /// in [`RawWaker`]'s and [`RawWakerVTable`]'s documentation is not upheld. - /// Therefore this method is unsafe. - #[inline] - #[must_use] - #[stable(feature = "futures_api", since = "1.36.0")] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] - pub const unsafe fn from_raw(waker: RawWaker) -> LocalWaker { - Self { waker } - } - /// Wake up the task associated with this `LocalWaker`. /// /// As long as the executor keeps running and the task is not finished, it is @@ -703,6 +679,37 @@ impl LocalWaker { unsafe { (wake)(data) }; } + /// Wake up the task associated with this `LocalWaker` without consuming the `LocalWaker`. + /// + /// This is similar to [`wake()`](Self::wake), but may be slightly less efficient in + /// the case where an owned `Waker` is available. This method should be preferred to + /// calling `waker.clone().wake()`. + #[inline] + #[stable(feature = "futures_api", since = "1.36.0")] + pub fn wake_by_ref(&self) { + // The actual wakeup call is delegated through a virtual function call + // to the implementation which is defined by the executor. + + // SAFETY: see `wake` + unsafe { (self.waker.vtable.wake_by_ref)(self.waker.data) } + } + + /// Returns `true` if this `LocalWaker` and another `LocalWaker` would awake the same task. + /// + /// This function works on a best-effort basis, and may return false even + /// when the `Waker`s would awaken the same task. However, if this function + /// returns `true`, it is guaranteed that the `Waker`s will awaken the same task. + /// + /// This function is primarily used for optimization purposes — for example, + /// this type's [`clone_from`](Self::clone_from) implementation uses it to + /// avoid cloning the waker when they would wake the same task anyway. + #[inline] + #[must_use] + #[stable(feature = "futures_api", since = "1.36.0")] + pub fn will_wake(&self, other: &LocalWaker) -> bool { + self.waker == other.waker + } + /// Creates a new `LocalWaker` that does nothing when `wake` is called. /// /// This is mostly useful for writing tests that need a [`Context`] to poll @@ -733,43 +740,25 @@ impl LocalWaker { WAKER } - /// Get a reference to the underlying [`RawWaker`]. - #[inline] - #[must_use] - #[unstable(feature = "waker_getters", issue = "96992")] - pub fn as_raw(&self) -> &RawWaker { - &self.waker - } - - /// Returns `true` if this `LocalWaker` and another `LocalWaker` would awake the same task. - /// - /// This function works on a best-effort basis, and may return false even - /// when the `Waker`s would awaken the same task. However, if this function - /// returns `true`, it is guaranteed that the `Waker`s will awaken the same task. + /// Creates a new `LocalWaker` from [`RawWaker`]. /// - /// This function is primarily used for optimization purposes — for example, - /// this type's [`clone_from`](Self::clone_from) implementation uses it to - /// avoid cloning the waker when they would wake the same task anyway. + /// The behavior of the returned `LocalWaker` is undefined if the contract defined + /// in [`RawWaker`]'s and [`RawWakerVTable`]'s documentation is not upheld. + /// Therefore this method is unsafe. #[inline] #[must_use] #[stable(feature = "futures_api", since = "1.36.0")] - pub fn will_wake(&self, other: &LocalWaker) -> bool { - self.waker == other.waker + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const unsafe fn from_raw(waker: RawWaker) -> LocalWaker { + Self { waker } } - /// Wake up the task associated with this `LocalWaker` without consuming the `LocalWaker`. - /// - /// This is similar to [`wake()`](Self::wake), but may be slightly less efficient in - /// the case where an owned `Waker` is available. This method should be preferred to - /// calling `waker.clone().wake()`. + /// Get a reference to the underlying [`RawWaker`]. #[inline] - #[stable(feature = "futures_api", since = "1.36.0")] - pub fn wake_by_ref(&self) { - // The actual wakeup call is delegated through a virtual function call - // to the implementation which is defined by the executor. - - // SAFETY: see `wake` - unsafe { (self.waker.vtable.wake_by_ref)(self.waker.data) } + #[must_use] + #[unstable(feature = "waker_getters", issue = "96992")] + pub fn as_raw(&self) -> &RawWaker { + &self.waker } } #[unstable(feature = "local_waker", issue = "118959")] From 7c6a9cbef103c0a9511aedad691a040ceb003917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Sat, 13 Jan 2024 11:18:18 -0300 Subject: [PATCH 14/16] chore: make method order consistent with waker --- library/core/src/task/wake.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index e64a25960e05e..6a5b5a3fb544d 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -710,6 +710,19 @@ impl LocalWaker { self.waker == other.waker } + /// Creates a new `LocalWaker` from [`RawWaker`]. + /// + /// The behavior of the returned `LocalWaker` is undefined if the contract defined + /// in [`RawWaker`]'s and [`RawWakerVTable`]'s documentation is not upheld. + /// Therefore this method is unsafe. + #[inline] + #[must_use] + #[stable(feature = "futures_api", since = "1.36.0")] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + pub const unsafe fn from_raw(waker: RawWaker) -> LocalWaker { + Self { waker } + } + /// Creates a new `LocalWaker` that does nothing when `wake` is called. /// /// This is mostly useful for writing tests that need a [`Context`] to poll @@ -740,19 +753,6 @@ impl LocalWaker { WAKER } - /// Creates a new `LocalWaker` from [`RawWaker`]. - /// - /// The behavior of the returned `LocalWaker` is undefined if the contract defined - /// in [`RawWaker`]'s and [`RawWakerVTable`]'s documentation is not upheld. - /// Therefore this method is unsafe. - #[inline] - #[must_use] - #[stable(feature = "futures_api", since = "1.36.0")] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] - pub const unsafe fn from_raw(waker: RawWaker) -> LocalWaker { - Self { waker } - } - /// Get a reference to the underlying [`RawWaker`]. #[inline] #[must_use] From 038c6e046c450082ebd087336d9b11979e0dd4f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Sat, 13 Jan 2024 14:57:13 -0300 Subject: [PATCH 15/16] refactor: make waker mandatory. This also removes * impl From<&Context> for ContextBuilder * Context::try_waker() The from implementation is removed because now that wakers are always supported, there are less incentives to override the current context. Before, the incentive was to add Waker support to a reactor that didn't have any. --- library/alloc/src/task.rs | 16 +++--- library/core/src/task/wake.rs | 99 +++++++---------------------------- 2 files changed, 28 insertions(+), 87 deletions(-) diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 7dad2f3f82270..357f7e0650ae7 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -173,14 +173,16 @@ fn raw_waker(waker: Arc) -> RawWaker { /// /// This is a simplified example of a `spawn` and a `block_on` function. The `spawn` function /// is used to push new tasks onto the run queue, while the block on function will remove them -/// and poll them. When a task is woken, it will put itself back on the run queue to be polled by the executor. +/// and poll them. When a task is woken, it will put itself back on the run queue to be polled +/// by the executor. /// -/// **Note:** A real world example would interleave poll calls with calls to an io reactor to wait for events instead -/// of spinning on a loop. +/// **Note:** This example trades correctness for simplicity. A real world example would interleave +/// poll calls with calls to an io reactor to wait for events instead of spinning on a loop. /// /// ```rust /// #![feature(local_waker)] -/// use std::task::{LocalWake, ContextBuilder, LocalWaker}; +/// #![feature(noop_waker)] +/// use std::task::{LocalWake, ContextBuilder, LocalWaker, Waker}; /// use std::future::Future; /// use std::pin::Pin; /// use std::rc::Rc; @@ -225,10 +227,12 @@ fn raw_waker(waker: Arc) -> RawWaker { /// // we exit, since there are no more tasks remaining on the queue /// return; /// }; +/// let waker = Waker::noop(); /// // cast the Rc into a `LocalWaker` -/// let waker: LocalWaker = task.clone().into(); +/// let local_waker: LocalWaker = task.clone().into(); /// // Build the context using `ContextBuilder` -/// let mut cx = ContextBuilder::from_local_waker(&waker) +/// let mut cx = ContextBuilder::from_waker(&waker) +/// .local_waker(&local_waker) /// .build(); /// /// // Poll the task diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 6a5b5a3fb544d..a746fe58f47de 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -217,7 +217,7 @@ impl RawWakerVTable { #[stable(feature = "futures_api", since = "1.36.0")] #[lang = "Context"] pub struct Context<'a> { - waker: Option<&'a Waker>, + waker: &'a Waker, local_waker: &'a LocalWaker, // Ensure we future-proof against variance changes by forcing // the lifetime to be invariant (argument-position lifetimes @@ -245,9 +245,7 @@ impl<'a> Context<'a> { #[stable(feature = "futures_api", since = "1.36.0")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn waker(&self) -> &'a Waker { - &self - .waker - .expect("no waker was set on this context, consider calling `local_waker` instead.") + &self.waker } /// Returns a reference to the [`LocalWaker`] for the current task. #[inline] @@ -256,14 +254,6 @@ impl<'a> Context<'a> { pub const fn local_waker(&self) -> &'a LocalWaker { &self.local_waker } - /// Returns a `Some(&Waker)` if a waker was defined on the `Context`, - /// otherwise it returns `None`. - #[inline] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] - #[unstable(feature = "local_waker", issue = "118959")] - pub const fn try_waker(&self) -> Option<&'a Waker> { - self.waker - } } #[stable(feature = "futures_api", since = "1.36.0")] @@ -286,8 +276,8 @@ impl fmt::Debug for Context<'_> { /// let local_waker = LocalWaker::noop(); /// let waker = Waker::noop(); /// -/// let mut cx = ContextBuilder::from_local_waker(&local_waker) -/// .waker(&waker) +/// let mut cx = ContextBuilder::from_waker(&waker) +/// .local_waker(&local_waker) /// .build(); /// /// let mut future = std::pin::pin!(async { 20 }); @@ -298,8 +288,16 @@ impl fmt::Debug for Context<'_> { #[unstable(feature = "local_waker", issue = "118959")] #[derive(Debug)] pub struct ContextBuilder<'a> { - waker: Option<&'a Waker>, + waker: &'a Waker, local_waker: &'a LocalWaker, + // Ensure we future-proof against variance changes by forcing + // the lifetime to be invariant (argument-position lifetimes + // are contravariant while return-position lifetimes are + // covariant). + _marker: PhantomData &'a ()>, + // Ensure `Context` is `!Send` and `!Sync` in order to allow + // for future `!Send` and / or `!Sync` fields. + _marker2: PhantomData<*mut ()>, } impl<'a> ContextBuilder<'a> { @@ -310,23 +308,7 @@ impl<'a> ContextBuilder<'a> { pub const fn from_waker(waker: &'a Waker) -> Self { // SAFETY: LocalWaker is just Waker without thread safety let local_waker = unsafe { transmute(waker) }; - Self { waker: Some(waker), local_waker } - } - - /// Create a ContextBuilder from a LocalWaker. - #[inline] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] - #[unstable(feature = "local_waker", issue = "118959")] - pub const fn from_local_waker(local_waker: &'a LocalWaker) -> Self { - Self { local_waker, waker: None } - } - - /// This field is used to set the value of the waker on `Context`. - #[inline] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] - #[unstable(feature = "local_waker", issue = "118959")] - pub const fn waker(self, waker: &'a Waker) -> Self { - Self { waker: Some(waker), ..self } + Self { waker: waker, local_waker, _marker: PhantomData, _marker2: PhantomData } } /// This method is used to set the value for the local waker on `Context`. @@ -342,52 +324,8 @@ impl<'a> ContextBuilder<'a> { #[unstable(feature = "local_waker", issue = "118959")] #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const fn build(self) -> Context<'a> { - let ContextBuilder { waker, local_waker } = self; - Context { waker, local_waker, _marker: PhantomData, _marker2: PhantomData } - } -} - -/// Construct a [`ContextBuilder`] from a [`Context`]. This is useful for -/// overriding values from a context. -/// -/// # Examples -/// An example of a future that allows to set a [`Waker`] on Context if none was defined. -/// This can be used to await futures that require a [`Waker`] even if the runtime does not -/// support [`Waker`]. -/// ```rust -/// #![feature(noop_waker, local_waker)] -/// use std::task::{Waker, ContextBuilder}; -/// use std::future::{poll_fn, Future}; -/// use std::pin::pin; -/// -/// async fn with_waker(f: F, waker: &Waker) -> F::Output -/// where -/// F: Future -/// { -/// let mut f = pin!(f); -/// poll_fn(move |cx| { -/// let has_waker = cx.try_waker().is_some(); -/// if has_waker { -/// return f.as_mut().poll(cx); -/// } -/// -/// let mut cx = ContextBuilder::from(cx) -/// .waker(&waker) -/// .build(); -/// f.as_mut().poll(&mut cx) -/// }).await -/// } -/// -/// # async fn __() { -/// with_waker(async { /* ... */ }, &Waker::noop()).await; -/// # } -/// ``` -#[unstable(feature = "local_waker", issue = "118959")] -impl<'a> From<&mut Context<'a>> for ContextBuilder<'a> { - #[inline] - fn from(value: &mut Context<'a>) -> Self { - let Context { waker, local_waker, _marker, _marker2 } = *value; - ContextBuilder { waker, local_waker } + let ContextBuilder { waker, local_waker, _marker, _marker2 } = self; + Context { waker, local_waker, _marker, _marker2 } } } @@ -600,8 +538,7 @@ impl fmt::Debug for Waker { /// Implements [`Clone`], but neither [`Send`] nor [`Sync`]; therefore, a local waker may /// not be moved to other threads. In general, when deciding to use wakers or local wakers, /// local wakers are preferable unless the waker needs to be sent across threads. This is because -/// wakers can incur in additional cost related to memory synchronization, and not all executors -/// may support wakers. +/// wakers can incur in additional cost related to memory synchronization. /// /// Note that it is preferable to use `local_waker.clone_from(&new_waker)` instead /// of `*local_waker = new_waker.clone()`, as the former will avoid cloning the waker @@ -738,7 +675,7 @@ impl LocalWaker { /// use std::future::Future; /// use std::task::{ContextBuilder, LocalWaker}; /// - /// let mut cx = task::ContextBuilder::new() + /// let mut cx = task::ContextBuilder::from_waker(Waker::noop()) /// .local_waker(LocalWaker::noop()) /// .build(); /// From 180c68bef5f42bd61f517e1a48db4152b6e1989e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Vallotton?= Date: Sat, 20 Jan 2024 10:25:00 -0300 Subject: [PATCH 16/16] doc: fix some doctests after rebase --- library/alloc/src/task.rs | 4 ++-- library/core/src/task/wake.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 357f7e0650ae7..87db8629ad09a 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -227,11 +227,11 @@ fn raw_waker(waker: Arc) -> RawWaker { /// // we exit, since there are no more tasks remaining on the queue /// return; /// }; -/// let waker = Waker::noop(); +/// /// // cast the Rc into a `LocalWaker` /// let local_waker: LocalWaker = task.clone().into(); /// // Build the context using `ContextBuilder` -/// let mut cx = ContextBuilder::from_waker(&waker) +/// let mut cx = ContextBuilder::from_waker(Waker::noop()) /// .local_waker(&local_waker) /// .build(); /// diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index a746fe58f47de..9ad71e394eacf 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -673,14 +673,14 @@ impl LocalWaker { /// #![feature(noop_waker)] /// /// use std::future::Future; - /// use std::task::{ContextBuilder, LocalWaker}; + /// use std::task::{ContextBuilder, LocalWaker, Waker, Poll}; /// - /// let mut cx = task::ContextBuilder::from_waker(Waker::noop()) + /// let mut cx = ContextBuilder::from_waker(Waker::noop()) /// .local_waker(LocalWaker::noop()) /// .build(); /// /// let mut future = Box::pin(async { 10 }); - /// assert_eq!(future.as_mut().poll(&mut cx), task::Poll::Ready(10)); + /// assert_eq!(future.as_mut().poll(&mut cx), Poll::Ready(10)); /// ``` #[inline] #[must_use]