Skip to content

Commit 7902054

Browse files
stbuehlercramertj
authored andcommitted
Use ManuallyDrop for WakerRef to use same vtable as normal Waker
This makes `Waker::will_wake` work between `task::waker_ref` and `Wakers` cloned from it (or created by `task::waker`). As `WakerRef` no longer stores an actual waker reference, `WakerRef::new` takes now a reference instead of ownership. Replaces `waker_vtable!` with a `waker_vtable<W>()` function, hopefully making the returned vtable pointer (more) stable. This also removes the safety guard "wake_unreachable" - but after all it should have been unreachable in the first place.
1 parent 9711c45 commit 7902054

File tree

5 files changed

+58
-62
lines changed

5 files changed

+58
-62
lines changed

futures-util/src/compat/compat03as01.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,9 @@ impl Current {
201201

202202
let ptr = current_to_ptr(self);
203203
let vtable = &RawWakerVTable::new(clone, wake, wake, drop);
204-
unsafe {
205-
WakerRef::new(task03::Waker::from_raw(RawWaker::new(ptr, vtable)))
206-
}
204+
WakerRef::new_unowned(std::mem::ManuallyDrop::new(unsafe {
205+
task03::Waker::from_raw(RawWaker::new(ptr, vtable))
206+
}))
207207
}
208208
}
209209

futures-util/src/task/mod.rs

-14
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,6 @@
11
//! Task notification
22
33
cfg_target_has_atomic! {
4-
/// A macro for creating a `RawWaker` vtable for a type that implements
5-
/// the `ArcWake` trait.
6-
#[cfg(feature = "alloc")]
7-
macro_rules! waker_vtable {
8-
($ty:ident) => {
9-
&RawWakerVTable::new(
10-
clone_arc_raw::<$ty>,
11-
wake_arc_raw::<$ty>,
12-
wake_by_ref_arc_raw::<$ty>,
13-
drop_arc_raw::<$ty>,
14-
)
15-
};
16-
}
17-
184
#[cfg(feature = "alloc")]
195
mod arc_wake;
206
#[cfg(feature = "alloc")]

futures-util/src/task/waker.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@ use core::mem;
33
use core::task::{Waker, RawWaker, RawWakerVTable};
44
use alloc::sync::Arc;
55

6+
pub(super) fn waker_vtable<W: ArcWake>() -> &'static RawWakerVTable {
7+
&RawWakerVTable::new(
8+
clone_arc_raw::<W>,
9+
wake_arc_raw::<W>,
10+
wake_by_ref_arc_raw::<W>,
11+
drop_arc_raw::<W>,
12+
)
13+
}
14+
615
/// Creates a [`Waker`] from an `Arc<impl ArcWake>`.
716
///
817
/// The returned [`Waker`] will call
@@ -14,7 +23,7 @@ where
1423
let ptr = Arc::into_raw(wake) as *const ();
1524

1625
unsafe {
17-
Waker::from_raw(RawWaker::new(ptr, waker_vtable!(W)))
26+
Waker::from_raw(RawWaker::new(ptr, waker_vtable::<W>()))
1827
}
1928
}
2029

@@ -29,9 +38,9 @@ unsafe fn increase_refcount<T: ArcWake>(data: *const ()) {
2938
}
3039

3140
// used by `waker_ref`
32-
pub(super) unsafe fn clone_arc_raw<T: ArcWake>(data: *const ()) -> RawWaker {
41+
unsafe fn clone_arc_raw<T: ArcWake>(data: *const ()) -> RawWaker {
3342
increase_refcount::<T>(data);
34-
RawWaker::new(data, waker_vtable!(T))
43+
RawWaker::new(data, waker_vtable::<T>())
3544
}
3645

3746
unsafe fn wake_arc_raw<T: ArcWake>(data: *const ()) {
@@ -40,7 +49,7 @@ unsafe fn wake_arc_raw<T: ArcWake>(data: *const ()) {
4049
}
4150

4251
// used by `waker_ref`
43-
pub(super) unsafe fn wake_by_ref_arc_raw<T: ArcWake>(data: *const ()) {
52+
unsafe fn wake_by_ref_arc_raw<T: ArcWake>(data: *const ()) {
4453
// Retain Arc, but don't touch refcount by wrapping in ManuallyDrop
4554
let arc = mem::ManuallyDrop::new(Arc::<T>::from_raw(data as *const T));
4655
ArcWake::wake_by_ref(&arc);

futures-util/src/task/waker_ref.rs

+30-41
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,40 @@
1-
use super::arc_wake::ArcWake;
2-
use super::waker::{clone_arc_raw, wake_by_ref_arc_raw};
1+
use super::arc_wake::{ArcWake};
2+
use super::waker::waker_vtable;
33
use alloc::sync::Arc;
4+
use core::mem::ManuallyDrop;
45
use core::marker::PhantomData;
56
use core::ops::Deref;
6-
use core::task::{Waker, RawWaker, RawWakerVTable};
7+
use core::task::{Waker, RawWaker};
78

89
/// A [`Waker`] that is only valid for a given lifetime.
910
///
1011
/// Note: this type implements [`Deref<Target = Waker>`](std::ops::Deref),
1112
/// so it can be used to get a `&Waker`.
1213
#[derive(Debug)]
1314
pub struct WakerRef<'a> {
14-
waker: Waker,
15+
waker: ManuallyDrop<Waker>,
1516
_marker: PhantomData<&'a ()>,
1617
}
1718

18-
impl WakerRef<'_> {
19-
/// Create a new [`WakerRef`] from a [`Waker`].
19+
impl<'a> WakerRef<'a> {
20+
/// Create a new [`WakerRef`] from a [`Waker`] reference.
21+
pub fn new(waker: &'a Waker) -> Self {
22+
// copy the underlying (raw) waker without calling a clone,
23+
// as we won't call Waker::drop either.
24+
let waker = ManuallyDrop::new(unsafe { core::ptr::read(waker) });
25+
WakerRef {
26+
waker,
27+
_marker: PhantomData,
28+
}
29+
}
30+
31+
/// Create a new [`WakerRef`] from a [`Waker`] that must not be dropped.
2032
///
21-
/// Note: this function is safe, but it is generally only used
22-
/// from `unsafe` contexts that need to create a `Waker`
23-
/// that is guaranteed not to outlive a particular lifetime.
24-
pub fn new(waker: Waker) -> Self {
33+
/// Note: this if for rare cases where the caller created a [`Waker`] in
34+
/// an unsafe way (that will be valid only for a lifetime to be determined
35+
/// by the caller), and the [`Waker`] doesn't need to or must not be
36+
/// destroyed.
37+
pub fn new_unowned(waker: ManuallyDrop<Waker>) -> Self {
2538
WakerRef {
2639
waker,
2740
_marker: PhantomData,
@@ -37,21 +50,6 @@ impl Deref for WakerRef<'_> {
3750
}
3851
}
3952

40-
#[inline]
41-
unsafe fn noop(_data: *const ()) {}
42-
43-
unsafe fn wake_unreachable(_data: *const ()) {
44-
// With only a reference, calling `wake_arc_raw()` would be unsound,
45-
// since the `WakerRef` didn't increment the refcount of the `ArcWake`,
46-
// and `wake_arc_raw` would *decrement* it.
47-
//
48-
// This should never be reachable, since `WakerRef` only provides a `Deref`
49-
// to the inner `Waker`.
50-
//
51-
// Still, safer to panic here than to call `wake_arc_raw`.
52-
unreachable!("WakerRef::wake");
53-
}
54-
5553
/// Creates a reference to a [`Waker`] from a reference to `Arc<impl ArcWake>`.
5654
///
5755
/// The resulting [`Waker`] will call
@@ -61,21 +59,12 @@ pub fn waker_ref<W>(wake: &Arc<W>) -> WakerRef<'_>
6159
where
6260
W: ArcWake
6361
{
64-
// This uses the same mechanism as Arc::into_raw, without needing a reference.
65-
// This is potentially not stable
66-
let ptr = &*wake as &W as *const W as *const ();
67-
68-
// Similar to `waker_vtable`, but with a no-op `drop` function.
69-
// Clones of the resulting `RawWaker` will still be dropped normally.
70-
let vtable = &RawWakerVTable::new(
71-
clone_arc_raw::<W>,
72-
wake_unreachable,
73-
wake_by_ref_arc_raw::<W>,
74-
noop,
75-
);
62+
// simply copy the pointer instead of using Arc::into_raw,
63+
// as we don't actually keep a refcount by using ManuallyDrop.<
64+
let ptr = (&**wake as *const W) as *const ();
7665

77-
let waker = unsafe {
78-
Waker::from_raw(RawWaker::new(ptr, vtable))
79-
};
80-
WakerRef::new(waker)
66+
let waker = ManuallyDrop::new(unsafe {
67+
Waker::from_raw(RawWaker::new(ptr, waker_vtable::<W>()))
68+
});
69+
WakerRef::new_unowned(waker)
8170
}

futures/tests/arc_wake.rs

+12
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,15 @@ fn proper_refcount_on_wake_panic() {
6363
drop(w1);
6464
assert_eq!(1, Arc::strong_count(&some_w)); // some_w
6565
}
66+
67+
#[test]
68+
fn waker_ref_wake_same() {
69+
let some_w = Arc::new(CountingWaker::new());
70+
71+
let w1: Waker = task::waker(some_w.clone());
72+
let w2 = task::waker_ref(&some_w);
73+
let w3 = w2.clone();
74+
75+
assert!(w1.will_wake(&w2));
76+
assert!(w2.will_wake(&w3));
77+
}

0 commit comments

Comments
 (0)