From a21854f153935eba58cfacccf1aad076ad72c2c0 Mon Sep 17 00:00:00 2001 From: Pierre Avital Date: Tue, 28 Mar 2023 21:56:12 +0200 Subject: [PATCH 1/5] implement stable-wakers RFC --- library/core/src/task/wake.rs | 314 ++++++++++++++++++++++++++++------ 1 file changed, 265 insertions(+), 49 deletions(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 808825326aef5..fe80bd4b83a6f 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -1,7 +1,7 @@ -#![stable(feature = "futures_api", since = "1.36.0")] +![stable(feature = "futures_api", since = "1.36.0")] -use crate::fmt; -use crate::marker::{PhantomData, Unpin}; +use core::fmt; +use core::marker::{PhantomData, Unpin}; /// A `RawWaker` allows the implementor of a task executor to create a [`Waker`] /// which provides customized wakeup behavior. @@ -11,7 +11,8 @@ use crate::marker::{PhantomData, Unpin}; /// It consists of a data pointer and a [virtual function pointer table (vtable)][vtable] /// that customizes the behavior of the `RawWaker`. #[derive(PartialEq, Debug)] -#[stable(feature = "futures_api", since = "1.36.0")] +[stable(feature = "futures_api", since = "1.36.0")] +#[repr(C)] pub struct RawWaker { /// A data pointer, which can be used to store arbitrary data as required /// by the executor. This could be e.g. a type-erased pointer to an `Arc` @@ -36,9 +37,9 @@ impl RawWaker { /// from a `RawWaker`. For each operation on the `Waker`, the associated /// function in the `vtable` of the underlying `RawWaker` will be called. #[inline] - #[rustc_promotable] - #[stable(feature = "futures_api", since = "1.36.0")] - #[rustc_const_stable(feature = "futures_api", since = "1.36.0")] + [rustc_promotable] + [stable(feature = "futures_api", since = "1.36.0")] + [rustc_const_stable(feature = "futures_api", since = "1.36.0")] #[must_use] pub const fn new(data: *const (), vtable: &'static RawWakerVTable) -> RawWaker { RawWaker { data, vtable } @@ -47,7 +48,7 @@ impl RawWaker { /// Get the `data` pointer used to create this `RawWaker`. #[inline] #[must_use] - #[unstable(feature = "waker_getters", issue = "87021")] + [unstable(feature = "waker_getters", issue = "87021")] pub fn data(&self) -> *const () { self.data } @@ -55,12 +56,14 @@ impl RawWaker { /// Get the `vtable` pointer used to create this `RawWaker`. #[inline] #[must_use] - #[unstable(feature = "waker_getters", issue = "87021")] + [unstable(feature = "waker_getters", issue = "87021")] pub fn vtable(&self) -> &'static RawWakerVTable { self.vtable } } +const RAW_WAKER_VTABLE_V1_PADDING: usize = 0; + /// A virtual function pointer table (vtable) that specifies the behavior /// of a [`RawWaker`]. /// @@ -77,9 +80,10 @@ impl RawWaker { /// 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. -#[stable(feature = "futures_api", since = "1.36.0")] +[stable(feature = "futures_api", since = "1.36.0")] #[derive(PartialEq, Copy, Clone, Debug)] -pub struct RawWakerVTable { +#[repr(C)] +struct RawWakerVTableV1 { /// This function will be called when the [`RawWaker`] gets cloned, e.g. when /// the [`Waker`] in which the [`RawWaker`] is stored gets cloned. /// @@ -110,6 +114,72 @@ pub struct RawWakerVTable { /// resources that are associated with this instance of a [`RawWaker`] and /// associated task. drop: unsafe fn(*const ()), + + /// This function guarantees permits calling `clone` with the C calling convention, guaranteeing stability. + clone_adapter: + Option RawWaker, *const ()) -> RawWaker>, + /// This function guarantees permits calling the other function pointers with the C calling convention, guaranteeing stability. + other_adapter: Option, + padding: [*const (); RAW_WAKER_VTABLE_V1_PADDING], // Reserved space for future changes to the v-table, the need for it and amount are debatable. +} + +#[derive(PartialEq, Copy, Clone, Debug)] +#[repr(C)] +struct RawWakerVTableV2 { + /// This function will be called when the [`RawWaker`] gets cloned, e.g. when + /// the [`Waker`] 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 + /// task. Calling `wake` on the resulting [`RawWaker`] should result in a wakeup + /// of the same task that would have been awoken by the original [`RawWaker`]. + clone: unsafe extern "C" fn(*const ()) -> RawWaker, + + /// This function will be called when `wake` is called on the [`Waker`]. + /// It must wake up the task associated with this [`RawWaker`]. + /// + /// The implementation of this function must make sure to release any + /// resources that are associated with this instance of a [`RawWaker`] and + /// associated task. + wake: unsafe extern "C" fn(*const ()), + + /// This function will be called when `wake_by_ref` is called on the [`Waker`]. + /// It must wake up the task associated with this [`RawWaker`]. + /// + /// This function is similar to `wake`, but must not consume the provided data + /// pointer. + wake_by_ref: unsafe extern "C" fn(*const ()), + + /// This function gets called when a [`Waker`] gets dropped. + /// + /// The implementation of this function must make sure to release any + /// resources that are associated with this instance of a [`RawWaker`] and + /// associated task. + drop: unsafe extern "C" fn(*const ()), + padding: [*const (); 2 + RAW_WAKER_VTABLE_V1_PADDING], // Reserved space for future changes to the v-table, the need for it and amount are debatable. +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union RawWakerVTable { + v1: RawWakerVTableV1, + v2: RawWakerVTableV2, +} + +impl PartialEq for RawWakerVTable { + fn eq(&self, other: &Self) -> bool { + unsafe { self.v2 == other.v2 } + } +} +impl fmt::Debug for RawWakerVTable { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + unsafe { + match self { + RawWakerVTable { v1 } if v1.other_adapter.is_none() => self.v1.fmt(f), + RawWakerVTable { v2 } => v2.fmt(f), + } + } + } } impl RawWakerVTable { @@ -122,7 +192,7 @@ impl RawWakerVTable { /// 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. /// - /// # `clone` + / `clone` /// /// This function will be called when the [`RawWaker`] gets cloned, e.g. when /// the [`Waker`] in which the [`RawWaker`] is stored gets cloned. @@ -132,7 +202,7 @@ impl RawWakerVTable { /// task. Calling `wake` on the resulting [`RawWaker`] should result in a wakeup /// of the same task that would have been awoken by the original [`RawWaker`]. /// - /// # `wake` + / `wake` /// /// This function will be called when `wake` is called on the [`Waker`]. /// It must wake up the task associated with this [`RawWaker`]. @@ -141,7 +211,7 @@ impl RawWakerVTable { /// resources that are associated with this instance of a [`RawWaker`] and /// associated task. /// - /// # `wake_by_ref` + / `wake_by_ref` /// /// This function will be called when `wake_by_ref` is called on the [`Waker`]. /// It must wake up the task associated with this [`RawWaker`]. @@ -149,23 +219,104 @@ impl RawWakerVTable { /// This function is similar to `wake`, but must not consume the provided data /// pointer. /// - /// # `drop` + / `drop` /// /// This function gets called when a [`Waker`] gets dropped. /// /// The implementation of this function must make sure to release any /// resources that are associated with this instance of a [`RawWaker`] and /// associated task. - #[rustc_promotable] - #[stable(feature = "futures_api", since = "1.36.0")] - #[rustc_const_stable(feature = "futures_api", since = "1.36.0")] + [rustc_promotable] + [stable(feature = "futures_api", since = "1.36.0")] + [rustc_const_stable(feature = "futures_api", since = "1.36.0")] + #[deprecated = "This constructor makes slower wakers, use new_with_c_abi instead"] pub const fn new( clone: unsafe fn(*const ()) -> RawWaker, wake: unsafe fn(*const ()), wake_by_ref: unsafe fn(*const ()), drop: unsafe fn(*const ()), ) -> Self { - Self { clone, wake, wake_by_ref, drop } + unsafe extern "C" fn clone_adapter( + clone: unsafe fn(*const ()) -> RawWaker, + data: *const (), + ) -> RawWaker { + clone(data) + } + unsafe extern "C" fn other_adapter(other: unsafe fn(*const ()), data: *const ()) { + other(data) + } + Self { + v1: RawWakerVTableV1 { + clone, + wake, + wake_by_ref, + drop, + clone_adapter: Some(clone_adapter), + other_adapter: Some(other_adapter), + padding: [core::ptr::null(); RAW_WAKER_VTABLE_V1_PADDING], + }, + } + } + + /// 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. + /// + / `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 implementation of this function must retain all resources that are + /// required for this additional instance of a [`RawWaker`] and associated + /// task. Calling `wake` on the resulting [`RawWaker`] should result in a wakeup + /// of the same task that would have been awoken by the original [`RawWaker`]. + /// + / `wake` + /// + /// This function will be called when `wake` is called on the [`Waker`]. + /// It must wake up the task associated with this [`RawWaker`]. + /// + /// The implementation of this function must make sure to release any + /// resources that are associated with this instance of a [`RawWaker`] and + /// associated task. + /// + / `wake_by_ref` + /// + /// This function will be called when `wake_by_ref` is called on the [`Waker`]. + /// It must wake up the task associated with this [`RawWaker`]. + /// + /// This function is similar to `wake`, but must not consume the provided data + /// pointer. + /// + / `drop` + /// + /// This function gets called when a [`Waker`] gets dropped. + /// + /// The implementation of this function must make sure to release any + /// resources that are associated with this instance of a [`RawWaker`] and + /// associated task. + [rustc_promotable] + pub const fn new_with_c_abi( + clone: unsafe extern "C" fn(*const ()) -> RawWaker, + wake: unsafe extern "C" fn(*const ()), + wake_by_ref: unsafe extern "C" fn(*const ()), + drop: unsafe extern "C" fn(*const ()), + ) -> Self { + Self { + v2: RawWakerVTableV2 { + clone, + wake, + wake_by_ref, + drop, + padding: [core::ptr::null(); 2 + RAW_WAKER_VTABLE_V1_PADDING], + }, + } } } @@ -173,8 +324,8 @@ impl RawWakerVTable { /// /// Currently, `Context` only serves to provide access to a [`&Waker`](Waker) /// which can be used to wake the current task. -#[stable(feature = "futures_api", since = "1.36.0")] -#[lang = "Context"] +[stable(feature = "futures_api", since = "1.36.0")] +[lang = "Context"] pub struct Context<'a> { waker: &'a Waker, // Ensure we future-proof against variance changes by forcing @@ -189,28 +340,34 @@ pub struct Context<'a> { impl<'a> Context<'a> { /// Create a new `Context` from a [`&Waker`](Waker). - #[stable(feature = "futures_api", since = "1.36.0")] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + [stable(feature = "futures_api", since = "1.36.0")] + [rustc_const_unstable(feature = "const_waker", issue = "102012")] #[must_use] #[inline] pub const fn from_waker(waker: &'a Waker) -> Self { - Context { waker, _marker: PhantomData, _marker2: PhantomData } + Context { + waker, + _marker: PhantomData, + _marker2: PhantomData, + } } /// Returns a reference to the [`Waker`] for the current task. - #[stable(feature = "futures_api", since = "1.36.0")] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + [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 } } -#[stable(feature = "futures_api", since = "1.36.0")] +[stable(feature = "futures_api", since = "1.36.0")] impl fmt::Debug for Context<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Context").field("waker", &self.waker).finish() + f.debug_struct("Context") + .field("waker", &self.waker) + .finish() } } @@ -233,16 +390,16 @@ impl fmt::Debug for Context<'_> { /// [`Future::poll()`]: core::future::Future::poll /// [`Poll::Pending`]: core::task::Poll::Pending #[repr(transparent)] -#[stable(feature = "futures_api", since = "1.36.0")] +[stable(feature = "futures_api", since = "1.36.0")] pub struct Waker { waker: RawWaker, } -#[stable(feature = "futures_api", since = "1.36.0")] +[stable(feature = "futures_api", since = "1.36.0")] impl Unpin for Waker {} -#[stable(feature = "futures_api", since = "1.36.0")] +[stable(feature = "futures_api", since = "1.36.0")] unsafe impl Send for Waker {} -#[stable(feature = "futures_api", since = "1.36.0")] +[stable(feature = "futures_api", since = "1.36.0")] unsafe impl Sync for Waker {} impl Waker { @@ -262,22 +419,36 @@ impl Waker { /// executor’s choice which task to run and the executor may choose to run the /// current task again. /// - /// [`poll()`]: crate::future::Future::poll + /// [`poll()`]: core::future::Future::poll #[inline] - #[stable(feature = "futures_api", since = "1.36.0")] + [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 vtable = self.waker.vtable; let data = self.waker.data; // Don't call `drop` -- the waker will be consumed by `wake`. - crate::mem::forget(self); + core::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) }; + unsafe { + match *vtable { + RawWakerVTable { + v1: + RawWakerVTableV1 { + wake, + other_adapter: Some(other_adapter), + .. + }, + } => (other_adapter)(wake, data), + RawWakerVTable { + v2: RawWakerVTableV2 { wake, .. }, + } => (wake)(data), + } + }; } /// Wake up the task associated with this `Waker` without consuming the `Waker`. @@ -286,13 +457,28 @@ impl Waker { /// 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")] + [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. + let RawWaker { data, vtable } = self.waker; // SAFETY: see `wake` - unsafe { (self.waker.vtable.wake_by_ref)(self.waker.data) } + unsafe { + match *vtable { + RawWakerVTable { + v1: + RawWakerVTableV1 { + wake_by_ref, + other_adapter: Some(other_adapter), + .. + }, + } => (other_adapter)(wake_by_ref, data), + RawWakerVTable { + v2: RawWakerVTableV2 { wake_by_ref, .. }, + } => (wake_by_ref)(data), + } + } } /// Returns `true` if this `Waker` and another `Waker` would awake the same task. @@ -304,7 +490,7 @@ impl Waker { /// This function is primarily used for optimization purposes. #[inline] #[must_use] - #[stable(feature = "futures_api", since = "1.36.0")] + [stable(feature = "futures_api", since = "1.36.0")] pub fn will_wake(&self, other: &Waker) -> bool { self.waker == other.waker } @@ -316,8 +502,8 @@ impl Waker { /// Therefore this method is unsafe. #[inline] #[must_use] - #[stable(feature = "futures_api", since = "1.36.0")] - #[rustc_const_unstable(feature = "const_waker", issue = "102012")] + [stable(feature = "futures_api", since = "1.36.0")] + [rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const unsafe fn from_raw(waker: RawWaker) -> Waker { Waker { waker } } @@ -325,37 +511,67 @@ impl Waker { /// Get a reference to the underlying [`RawWaker`]. #[inline] #[must_use] - #[unstable(feature = "waker_getters", issue = "87021")] + [unstable(feature = "waker_getters", issue = "87021")] pub fn as_raw(&self) -> &RawWaker { &self.waker } } -#[stable(feature = "futures_api", since = "1.36.0")] +[stable(feature = "futures_api", since = "1.36.0")] impl Clone for Waker { #[inline] fn clone(&self) -> Self { + let RawWaker { data, vtable } = self.waker; Waker { // 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) }, + waker: unsafe { + match *vtable { + RawWakerVTable { + v1: + RawWakerVTableV1 { + clone, + clone_adapter: Some(clone_adapter), + .. + }, + } => (clone_adapter)(clone, data), + RawWakerVTable { + v2: RawWakerVTableV2 { clone, .. }, + } => (clone)(data), + } + }, } } } -#[stable(feature = "futures_api", since = "1.36.0")] +[stable(feature = "futures_api", since = "1.36.0")] impl Drop for Waker { #[inline] fn drop(&mut self) { + let RawWaker { data, vtable } = self.waker; // SAFETY: This is safe because `Waker::from_raw` is the only way // to initialize `drop` and `data` requiring the user to acknowledge // that the contract of `RawWaker` is upheld. - unsafe { (self.waker.vtable.drop)(self.waker.data) } + unsafe { + match *vtable { + RawWakerVTable { + v1: + RawWakerVTableV1 { + drop, + other_adapter: Some(other_adapter), + .. + }, + } => (other_adapter)(drop, data), + RawWakerVTable { + v2: RawWakerVTableV2 { drop, .. }, + } => (drop)(data), + } + } } } -#[stable(feature = "futures_api", since = "1.36.0")] +[stable(feature = "futures_api", since = "1.36.0")] impl fmt::Debug for Waker { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let vtable_ptr = self.waker.vtable as *const RawWakerVTable; From 257e871a52f3fea56afc892bfb107e8243c384f3 Mon Sep 17 00:00:00 2001 From: Pierre Avital Date: Tue, 28 Mar 2023 22:01:11 +0200 Subject: [PATCH 2/5] botched attribute macros earlier, fixed now --- library/core/src/task/wake.rs | 80 +++++++++++++++++------------------ 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index fe80bd4b83a6f..87f0c3207e62b 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -1,4 +1,4 @@ -![stable(feature = "futures_api", since = "1.36.0")] +#![stable(feature = "futures_api", since = "1.36.0")] use core::fmt; use core::marker::{PhantomData, Unpin}; @@ -11,7 +11,7 @@ use core::marker::{PhantomData, Unpin}; /// It consists of a data pointer and a [virtual function pointer table (vtable)][vtable] /// that customizes the behavior of the `RawWaker`. #[derive(PartialEq, Debug)] -[stable(feature = "futures_api", since = "1.36.0")] +#[stable(feature = "futures_api", since = "1.36.0")] #[repr(C)] pub struct RawWaker { /// A data pointer, which can be used to store arbitrary data as required @@ -37,9 +37,9 @@ impl RawWaker { /// from a `RawWaker`. For each operation on the `Waker`, the associated /// function in the `vtable` of the underlying `RawWaker` will be called. #[inline] - [rustc_promotable] - [stable(feature = "futures_api", since = "1.36.0")] - [rustc_const_stable(feature = "futures_api", since = "1.36.0")] + #[rustc_promotable] + #[stable(feature = "futures_api", since = "1.36.0")] + #[rustc_const_stable(feature = "futures_api", since = "1.36.0")] #[must_use] pub const fn new(data: *const (), vtable: &'static RawWakerVTable) -> RawWaker { RawWaker { data, vtable } @@ -48,7 +48,7 @@ impl RawWaker { /// Get the `data` pointer used to create this `RawWaker`. #[inline] #[must_use] - [unstable(feature = "waker_getters", issue = "87021")] + #[unstable(feature = "waker_getters", issue = "87021")] pub fn data(&self) -> *const () { self.data } @@ -56,7 +56,7 @@ impl RawWaker { /// Get the `vtable` pointer used to create this `RawWaker`. #[inline] #[must_use] - [unstable(feature = "waker_getters", issue = "87021")] + #[unstable(feature = "waker_getters", issue = "87021")] pub fn vtable(&self) -> &'static RawWakerVTable { self.vtable } @@ -80,7 +80,7 @@ const RAW_WAKER_VTABLE_V1_PADDING: usize = 0; /// 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. -[stable(feature = "futures_api", since = "1.36.0")] +#[stable(feature = "futures_api", since = "1.36.0")] #[derive(PartialEq, Copy, Clone, Debug)] #[repr(C)] struct RawWakerVTableV1 { @@ -192,7 +192,7 @@ impl RawWakerVTable { /// 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. /// - / `clone` + /# `clone` /// /// This function will be called when the [`RawWaker`] gets cloned, e.g. when /// the [`Waker`] in which the [`RawWaker`] is stored gets cloned. @@ -202,7 +202,7 @@ impl RawWakerVTable { /// task. Calling `wake` on the resulting [`RawWaker`] should result in a wakeup /// of the same task that would have been awoken by the original [`RawWaker`]. /// - / `wake` + /# `wake` /// /// This function will be called when `wake` is called on the [`Waker`]. /// It must wake up the task associated with this [`RawWaker`]. @@ -211,7 +211,7 @@ impl RawWakerVTable { /// resources that are associated with this instance of a [`RawWaker`] and /// associated task. /// - / `wake_by_ref` + /# `wake_by_ref` /// /// This function will be called when `wake_by_ref` is called on the [`Waker`]. /// It must wake up the task associated with this [`RawWaker`]. @@ -219,16 +219,16 @@ impl RawWakerVTable { /// This function is similar to `wake`, but must not consume the provided data /// pointer. /// - / `drop` + /# `drop` /// /// This function gets called when a [`Waker`] gets dropped. /// /// The implementation of this function must make sure to release any /// resources that are associated with this instance of a [`RawWaker`] and /// associated task. - [rustc_promotable] - [stable(feature = "futures_api", since = "1.36.0")] - [rustc_const_stable(feature = "futures_api", since = "1.36.0")] + #[rustc_promotable] + #[stable(feature = "futures_api", since = "1.36.0")] + #[rustc_const_stable(feature = "futures_api", since = "1.36.0")] #[deprecated = "This constructor makes slower wakers, use new_with_c_abi instead"] pub const fn new( clone: unsafe fn(*const ()) -> RawWaker, @@ -267,7 +267,7 @@ impl RawWakerVTable { /// 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. /// - / `clone` + /# `clone` /// /// This function will be called when the [`RawWaker`] gets cloned, e.g. when /// the [`Waker`] in which the [`RawWaker`] is stored gets cloned. @@ -277,7 +277,7 @@ impl RawWakerVTable { /// task. Calling `wake` on the resulting [`RawWaker`] should result in a wakeup /// of the same task that would have been awoken by the original [`RawWaker`]. /// - / `wake` + /# `wake` /// /// This function will be called when `wake` is called on the [`Waker`]. /// It must wake up the task associated with this [`RawWaker`]. @@ -286,7 +286,7 @@ impl RawWakerVTable { /// resources that are associated with this instance of a [`RawWaker`] and /// associated task. /// - / `wake_by_ref` + /# `wake_by_ref` /// /// This function will be called when `wake_by_ref` is called on the [`Waker`]. /// It must wake up the task associated with this [`RawWaker`]. @@ -294,14 +294,14 @@ impl RawWakerVTable { /// This function is similar to `wake`, but must not consume the provided data /// pointer. /// - / `drop` + /# `drop` /// /// This function gets called when a [`Waker`] gets dropped. /// /// The implementation of this function must make sure to release any /// resources that are associated with this instance of a [`RawWaker`] and /// associated task. - [rustc_promotable] + #[rustc_promotable] pub const fn new_with_c_abi( clone: unsafe extern "C" fn(*const ()) -> RawWaker, wake: unsafe extern "C" fn(*const ()), @@ -324,8 +324,8 @@ impl RawWakerVTable { /// /// Currently, `Context` only serves to provide access to a [`&Waker`](Waker) /// which can be used to wake the current task. -[stable(feature = "futures_api", since = "1.36.0")] -[lang = "Context"] +#[stable(feature = "futures_api", since = "1.36.0")] +#[lang = "Context"] pub struct Context<'a> { waker: &'a Waker, // Ensure we future-proof against variance changes by forcing @@ -340,8 +340,8 @@ pub struct Context<'a> { impl<'a> Context<'a> { /// Create a new `Context` from a [`&Waker`](Waker). - [stable(feature = "futures_api", since = "1.36.0")] - [rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[stable(feature = "futures_api", since = "1.36.0")] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] #[must_use] #[inline] pub const fn from_waker(waker: &'a Waker) -> Self { @@ -353,8 +353,8 @@ impl<'a> Context<'a> { } /// Returns a reference to the [`Waker`] for the current task. - [stable(feature = "futures_api", since = "1.36.0")] - [rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[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 { @@ -362,7 +362,7 @@ impl<'a> Context<'a> { } } -[stable(feature = "futures_api", since = "1.36.0")] +#[stable(feature = "futures_api", since = "1.36.0")] impl fmt::Debug for Context<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Context") @@ -390,16 +390,16 @@ impl fmt::Debug for Context<'_> { /// [`Future::poll()`]: core::future::Future::poll /// [`Poll::Pending`]: core::task::Poll::Pending #[repr(transparent)] -[stable(feature = "futures_api", since = "1.36.0")] +#[stable(feature = "futures_api", since = "1.36.0")] pub struct Waker { waker: RawWaker, } -[stable(feature = "futures_api", since = "1.36.0")] +#[stable(feature = "futures_api", since = "1.36.0")] impl Unpin for Waker {} -[stable(feature = "futures_api", since = "1.36.0")] +#[stable(feature = "futures_api", since = "1.36.0")] unsafe impl Send for Waker {} -[stable(feature = "futures_api", since = "1.36.0")] +#[stable(feature = "futures_api", since = "1.36.0")] unsafe impl Sync for Waker {} impl Waker { @@ -421,7 +421,7 @@ impl Waker { /// /// [`poll()`]: core::future::Future::poll #[inline] - [stable(feature = "futures_api", since = "1.36.0")] + #[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. @@ -457,7 +457,7 @@ impl Waker { /// 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")] + #[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. @@ -490,7 +490,7 @@ impl Waker { /// This function is primarily used for optimization purposes. #[inline] #[must_use] - [stable(feature = "futures_api", since = "1.36.0")] + #[stable(feature = "futures_api", since = "1.36.0")] pub fn will_wake(&self, other: &Waker) -> bool { self.waker == other.waker } @@ -502,8 +502,8 @@ impl Waker { /// Therefore this method is unsafe. #[inline] #[must_use] - [stable(feature = "futures_api", since = "1.36.0")] - [rustc_const_unstable(feature = "const_waker", issue = "102012")] + #[stable(feature = "futures_api", since = "1.36.0")] + #[rustc_const_unstable(feature = "const_waker", issue = "102012")] pub const unsafe fn from_raw(waker: RawWaker) -> Waker { Waker { waker } } @@ -511,13 +511,13 @@ impl Waker { /// Get a reference to the underlying [`RawWaker`]. #[inline] #[must_use] - [unstable(feature = "waker_getters", issue = "87021")] + #[unstable(feature = "waker_getters", issue = "87021")] pub fn as_raw(&self) -> &RawWaker { &self.waker } } -[stable(feature = "futures_api", since = "1.36.0")] +#[stable(feature = "futures_api", since = "1.36.0")] impl Clone for Waker { #[inline] fn clone(&self) -> Self { @@ -545,7 +545,7 @@ impl Clone for Waker { } } -[stable(feature = "futures_api", since = "1.36.0")] +#[stable(feature = "futures_api", since = "1.36.0")] impl Drop for Waker { #[inline] fn drop(&mut self) { @@ -571,7 +571,7 @@ impl Drop for Waker { } } -[stable(feature = "futures_api", since = "1.36.0")] +#[stable(feature = "futures_api", since = "1.36.0")] impl fmt::Debug for Waker { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let vtable_ptr = self.waker.vtable as *const RawWakerVTable; From ffa028dfe53a0c86c764a3c44798e8f10245504a Mon Sep 17 00:00:00 2001 From: Pierre Avital Date: Wed, 29 Mar 2023 09:44:33 +0200 Subject: [PATCH 3/5] add stability annotations --- library/alloc/src/lib.rs | 1 + library/alloc/src/task.rs | 24 ++++-- library/core/src/task/wake.rs | 143 +++++++++++++++------------------- 3 files changed, 83 insertions(+), 85 deletions(-) diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 55e18b0495601..e9dff17ed8ef7 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -150,6 +150,7 @@ #![feature(slice_ptr_get)] #![feature(slice_ptr_len)] #![feature(slice_range)] +#![feature(stable_wakers)] #![feature(str_internals)] #![feature(strict_provenance)] #![feature(trusted_len)] diff --git a/library/alloc/src/task.rs b/library/alloc/src/task.rs index 9d8e309a978d9..c406f11dfc72f 100644 --- a/library/alloc/src/task.rs +++ b/library/alloc/src/task.rs @@ -121,33 +121,45 @@ impl From> for RawWaker { #[inline(always)] fn raw_waker(waker: Arc) -> RawWaker { // Increment the reference count of the arc to clone it. - unsafe fn clone_waker(waker: *const ()) -> RawWaker { + unsafe extern "C" fn clone_waker( + waker: *const (), + ) -> RawWaker { unsafe { Arc::increment_strong_count(waker as *const W) }; RawWaker::new( waker as *const (), - &RawWakerVTable::new(clone_waker::, wake::, wake_by_ref::, drop_waker::), + &RawWakerVTable::new_with_c_abi( + clone_waker::, + wake::, + wake_by_ref::, + drop_waker::, + ), ) } // Wake by value, moving the Arc into the Wake::wake function - unsafe fn wake(waker: *const ()) { + unsafe extern "C" fn wake(waker: *const ()) { let waker = unsafe { Arc::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 ()) { + unsafe extern "C" fn wake_by_ref(waker: *const ()) { let waker = unsafe { ManuallyDrop::new(Arc::from_raw(waker as *const W)) }; ::wake_by_ref(&waker); } // Decrement the reference count of the Arc on drop - unsafe fn drop_waker(waker: *const ()) { + unsafe extern "C" fn drop_waker(waker: *const ()) { unsafe { Arc::decrement_strong_count(waker as *const W) }; } RawWaker::new( Arc::into_raw(waker) as *const (), - &RawWakerVTable::new(clone_waker::, wake::, wake_by_ref::, drop_waker::), + &RawWakerVTable::new_with_c_abi( + clone_waker::, + wake::, + wake_by_ref::, + drop_waker::, + ), ) } diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 87f0c3207e62b..e82b7ecdd0679 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -63,23 +63,6 @@ impl RawWaker { } const RAW_WAKER_VTABLE_V1_PADDING: usize = 0; - -/// A virtual function pointer table (vtable) that specifies the behavior -/// of a [`RawWaker`]. -/// -/// The pointer passed to all functions inside the vtable is the `data` pointer -/// from the enclosing [`RawWaker`] object. -/// -/// The functions inside this struct are only intended to be called on the `data` -/// pointer of a properly constructed [`RawWaker`] object from inside the -/// [`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. #[stable(feature = "futures_api", since = "1.36.0")] #[derive(PartialEq, Copy, Clone, Debug)] #[repr(C)] @@ -159,18 +142,42 @@ struct RawWakerVTableV2 { padding: [*const (); 2 + RAW_WAKER_VTABLE_V1_PADDING], // Reserved space for future changes to the v-table, the need for it and amount are debatable. } +/// A virtual function pointer table (vtable) that specifies the behavior +/// of a [`RawWaker`]. +/// +/// The pointer passed to all functions inside the vtable is the `data` pointer +/// from the enclosing [`RawWaker`] object. +/// +/// The functions inside this struct are only intended to be called on the `data` +/// pointer of a properly constructed [`RawWaker`] object from inside the +/// [`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. #[repr(C)] #[derive(Clone, Copy)] +#[stable(feature = "futures_api", since = "1.36.0")] pub union RawWakerVTable { v1: RawWakerVTableV1, v2: RawWakerVTableV2, } +#[stable(feature = "futures_api", since = "1.36.0")] +unsafe impl Send for RawWakerVTable {} +#[stable(feature = "futures_api", since = "1.36.0")] +unsafe impl Sync for RawWakerVTable {} +#[stable(feature = "futures_api", since = "1.36.0")] impl PartialEq for RawWakerVTable { fn eq(&self, other: &Self) -> bool { unsafe { self.v2 == other.v2 } } } + +#[stable(feature = "futures_api", since = "1.36.0")] impl fmt::Debug for RawWakerVTable { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { unsafe { @@ -182,6 +189,21 @@ impl fmt::Debug for RawWakerVTable { } } +#[allow(improper_ctypes_definitions)] +/// # Safety +/// This function must only be called with function pointers sourced from the same shared object +unsafe extern "C" fn clone_adapter( + clone: unsafe fn(*const ()) -> RawWaker, + data: *const (), +) -> RawWaker { + unsafe { (clone)(data) } +} +#[allow(improper_ctypes_definitions)] +/// # Safety +/// This function must only be called with function pointers sourced from the same shared object +unsafe extern "C" fn other_adapter(other: unsafe fn(*const ()), data: *const ()) { + unsafe { (other)(data) } +} impl RawWakerVTable { /// Creates a new `RawWakerVTable` from the provided `clone`, `wake`, /// `wake_by_ref`, and `drop` functions. @@ -192,7 +214,7 @@ impl RawWakerVTable { /// 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. /// - /# `clone` + /// # `clone` /// /// This function will be called when the [`RawWaker`] gets cloned, e.g. when /// the [`Waker`] in which the [`RawWaker`] is stored gets cloned. @@ -202,7 +224,7 @@ impl RawWakerVTable { /// task. Calling `wake` on the resulting [`RawWaker`] should result in a wakeup /// of the same task that would have been awoken by the original [`RawWaker`]. /// - /# `wake` + /// # `wake` /// /// This function will be called when `wake` is called on the [`Waker`]. /// It must wake up the task associated with this [`RawWaker`]. @@ -211,7 +233,7 @@ impl RawWakerVTable { /// resources that are associated with this instance of a [`RawWaker`] and /// associated task. /// - /# `wake_by_ref` + /// # `wake_by_ref` /// /// This function will be called when `wake_by_ref` is called on the [`Waker`]. /// It must wake up the task associated with this [`RawWaker`]. @@ -219,7 +241,7 @@ impl RawWakerVTable { /// This function is similar to `wake`, but must not consume the provided data /// pointer. /// - /# `drop` + /// # `drop` /// /// This function gets called when a [`Waker`] gets dropped. /// @@ -229,22 +251,17 @@ impl RawWakerVTable { #[rustc_promotable] #[stable(feature = "futures_api", since = "1.36.0")] #[rustc_const_stable(feature = "futures_api", since = "1.36.0")] - #[deprecated = "This constructor makes slower wakers, use new_with_c_abi instead"] + #[deprecated( + since = "TBD", + note = "This constructor makes slower wakers", + suggestion = "new_with_c_abi" + )] pub const fn new( clone: unsafe fn(*const ()) -> RawWaker, wake: unsafe fn(*const ()), wake_by_ref: unsafe fn(*const ()), drop: unsafe fn(*const ()), ) -> Self { - unsafe extern "C" fn clone_adapter( - clone: unsafe fn(*const ()) -> RawWaker, - data: *const (), - ) -> RawWaker { - clone(data) - } - unsafe extern "C" fn other_adapter(other: unsafe fn(*const ()), data: *const ()) { - other(data) - } Self { v1: RawWakerVTableV1 { clone, @@ -267,7 +284,7 @@ impl RawWakerVTable { /// 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. /// - /# `clone` + /// # `clone` /// /// This function will be called when the [`RawWaker`] gets cloned, e.g. when /// the [`Waker`] in which the [`RawWaker`] is stored gets cloned. @@ -277,7 +294,7 @@ impl RawWakerVTable { /// task. Calling `wake` on the resulting [`RawWaker`] should result in a wakeup /// of the same task that would have been awoken by the original [`RawWaker`]. /// - /# `wake` + /// # `wake` /// /// This function will be called when `wake` is called on the [`Waker`]. /// It must wake up the task associated with this [`RawWaker`]. @@ -286,7 +303,7 @@ impl RawWakerVTable { /// resources that are associated with this instance of a [`RawWaker`] and /// associated task. /// - /# `wake_by_ref` + /// # `wake_by_ref` /// /// This function will be called when `wake_by_ref` is called on the [`Waker`]. /// It must wake up the task associated with this [`RawWaker`]. @@ -294,7 +311,7 @@ impl RawWakerVTable { /// This function is similar to `wake`, but must not consume the provided data /// pointer. /// - /# `drop` + /// # `drop` /// /// This function gets called when a [`Waker`] gets dropped. /// @@ -302,6 +319,8 @@ impl RawWakerVTable { /// resources that are associated with this instance of a [`RawWaker`] and /// associated task. #[rustc_promotable] + #[unstable(feature = "stable_wakers", issue = "109706")] + #[rustc_const_unstable(feature = "stable_wakers", issue = "109706")] pub const fn new_with_c_abi( clone: unsafe extern "C" fn(*const ()) -> RawWaker, wake: unsafe extern "C" fn(*const ()), @@ -345,11 +364,7 @@ impl<'a> Context<'a> { #[must_use] #[inline] pub const fn from_waker(waker: &'a Waker) -> Self { - Context { - waker, - _marker: PhantomData, - _marker2: PhantomData, - } + Context { waker, _marker: PhantomData, _marker2: PhantomData } } /// Returns a reference to the [`Waker`] for the current task. @@ -365,9 +380,7 @@ impl<'a> Context<'a> { #[stable(feature = "futures_api", since = "1.36.0")] impl fmt::Debug for Context<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Context") - .field("waker", &self.waker) - .finish() + f.debug_struct("Context").field("waker", &self.waker).finish() } } @@ -437,16 +450,9 @@ impl Waker { unsafe { match *vtable { RawWakerVTable { - v1: - RawWakerVTableV1 { - wake, - other_adapter: Some(other_adapter), - .. - }, + v1: RawWakerVTableV1 { wake, other_adapter: Some(other_adapter), .. }, } => (other_adapter)(wake, data), - RawWakerVTable { - v2: RawWakerVTableV2 { wake, .. }, - } => (wake)(data), + RawWakerVTable { v2: RawWakerVTableV2 { wake, .. } } => (wake)(data), } }; } @@ -467,16 +473,9 @@ impl Waker { unsafe { match *vtable { RawWakerVTable { - v1: - RawWakerVTableV1 { - wake_by_ref, - other_adapter: Some(other_adapter), - .. - }, + v1: RawWakerVTableV1 { wake_by_ref, other_adapter: Some(other_adapter), .. }, } => (other_adapter)(wake_by_ref, data), - RawWakerVTable { - v2: RawWakerVTableV2 { wake_by_ref, .. }, - } => (wake_by_ref)(data), + RawWakerVTable { v2: RawWakerVTableV2 { wake_by_ref, .. } } => (wake_by_ref)(data), } } } @@ -529,16 +528,9 @@ impl Clone for Waker { waker: unsafe { match *vtable { RawWakerVTable { - v1: - RawWakerVTableV1 { - clone, - clone_adapter: Some(clone_adapter), - .. - }, + v1: RawWakerVTableV1 { clone, clone_adapter: Some(clone_adapter), .. }, } => (clone_adapter)(clone, data), - RawWakerVTable { - v2: RawWakerVTableV2 { clone, .. }, - } => (clone)(data), + RawWakerVTable { v2: RawWakerVTableV2 { clone, .. } } => (clone)(data), } }, } @@ -556,16 +548,9 @@ impl Drop for Waker { unsafe { match *vtable { RawWakerVTable { - v1: - RawWakerVTableV1 { - drop, - other_adapter: Some(other_adapter), - .. - }, + v1: RawWakerVTableV1 { drop, other_adapter: Some(other_adapter), .. }, } => (other_adapter)(drop, data), - RawWakerVTable { - v2: RawWakerVTableV2 { drop, .. }, - } => (drop)(data), + RawWakerVTable { v2: RawWakerVTableV2 { drop, .. } } => (drop)(data), } } } From 47f3542fb430640d2d76dc7b5360ab73450d7d3f Mon Sep 17 00:00:00 2001 From: Pierre Avital Date: Wed, 29 Mar 2023 10:04:24 +0200 Subject: [PATCH 4/5] update safety comments --- library/core/src/task/wake.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index e82b7ecdd0679..34705935a7108 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -180,9 +180,11 @@ impl PartialEq for RawWakerVTable { #[stable(feature = "futures_api", since = "1.36.0")] impl fmt::Debug for RawWakerVTable { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // SAFETY: Matching on unions is always unsafe. + // The determinant of this union is that `v2`'s padding must always be null pointers, i.e. v1's adapter fields must be `Some(_)` unsafe { match self { - RawWakerVTable { v1 } if v1.other_adapter.is_none() => self.v1.fmt(f), + RawWakerVTable { v1 } if v1.other_adapter.is_some() => self.v1.fmt(f), RawWakerVTable { v2 } => v2.fmt(f), } } @@ -196,12 +198,14 @@ unsafe extern "C" fn clone_adapter( clone: unsafe fn(*const ()) -> RawWaker, data: *const (), ) -> RawWaker { + // SAFETY: The safety constraints are passed up to the caller unsafe { (clone)(data) } } #[allow(improper_ctypes_definitions)] /// # Safety /// This function must only be called with function pointers sourced from the same shared object unsafe extern "C" fn other_adapter(other: unsafe fn(*const ()), data: *const ()) { + // SAFETY: The safety constraints are passed up to the caller unsafe { (other)(data) } } impl RawWakerVTable { @@ -447,6 +451,8 @@ impl Waker { // 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. + // Matching on unions is always unsafe. + // The determinant of this union is that `v2`'s padding must always be null pointers, i.e. v1's adapter fields must be `Some(_)` unsafe { match *vtable { RawWakerVTable { @@ -470,6 +476,8 @@ impl Waker { let RawWaker { data, vtable } = self.waker; // SAFETY: see `wake` + // Matching on unions is always unsafe. + // The determinant of this union is that `v2`'s padding must always be null pointers, i.e. v1's adapter fields must be `Some(_)` unsafe { match *vtable { RawWakerVTable { @@ -525,6 +533,8 @@ impl Clone for Waker { // 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. + // Matching on unions is always unsafe. + // The determinant of this union is that `v2`'s padding must always be null pointers, i.e. v1's adapter fields must be `Some(_)` waker: unsafe { match *vtable { RawWakerVTable { @@ -545,6 +555,8 @@ impl Drop for Waker { // SAFETY: This is safe because `Waker::from_raw` is the only way // to initialize `drop` and `data` requiring the user to acknowledge // that the contract of `RawWaker` is upheld. + // Matching on unions is always unsafe. + // The determinant of this union is that `v2`'s padding must always be null pointers, i.e. v1's adapter fields must be `Some(_)` unsafe { match *vtable { RawWakerVTable { From a7e8f91c4400d0dabd1735690bb4cfa55ca752f5 Mon Sep 17 00:00:00 2001 From: Pierre Avital Date: Wed, 29 Mar 2023 10:12:50 +0200 Subject: [PATCH 5/5] missed a safety comment --- library/core/src/task/wake.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/core/src/task/wake.rs b/library/core/src/task/wake.rs index 34705935a7108..f9c23c8e1cd22 100644 --- a/library/core/src/task/wake.rs +++ b/library/core/src/task/wake.rs @@ -173,6 +173,7 @@ unsafe impl Sync for RawWakerVTable {} #[stable(feature = "futures_api", since = "1.36.0")] impl PartialEq for RawWakerVTable { fn eq(&self, other: &Self) -> bool { + // SAFETY: Comparison is just a bunch of pointer-equality: checking exact variants is unnecessary. unsafe { self.v2 == other.v2 } } }