Skip to content

Commit

Permalink
refactor: make waker mandatory.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
tvallotton committed Jan 13, 2024
1 parent c61eab8 commit 5e55bad
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 87 deletions.
16 changes: 10 additions & 6 deletions library/alloc/src/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,14 +173,16 @@ fn raw_waker<W: Wake + Send + Sync + 'static>(waker: Arc<W>) -> 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;
Expand Down Expand Up @@ -225,10 +227,12 @@ fn raw_waker<W: Wake + Send + Sync + 'static>(waker: Arc<W>) -> RawWaker {
/// // we exit, since there are no more tasks remaining on the queue
/// return;
/// };
/// let waker = Waker::noop();
/// // cast the Rc<Task> 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
Expand Down
101 changes: 20 additions & 81 deletions library/core/src/task/wake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]
Expand All @@ -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")]
Expand All @@ -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 });
Expand All @@ -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<fn(&'a ()) -> &'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> {
Expand All @@ -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`.
Expand All @@ -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: 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 }
}
}

Expand Down Expand Up @@ -598,8 +536,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
Expand Down Expand Up @@ -736,8 +673,10 @@ impl LocalWaker {
/// use std::future::Future;
/// use std::task;
///
/// let waker = task::Waker::noop();
/// let local_waker = task::LocalWaker::noop();
/// let mut cx = task::ContextBuilder::from_local_waker(&local_waker)
/// let mut cx = task::ContextBuilder::from_waker(&waker)
/// .local_waker(&local_waker)
/// .build();
///
/// let mut future = Box::pin(async { 10 });
Expand Down

0 comments on commit 5e55bad

Please sign in to comment.