rt: share vtable between waker and waker ref (#2045)

The `Waker::will_wake` compares both a data pointer and a vtable to
decide if wakers are equivalent. To avoid false negatives during
comparison, use the same vtable for a waker stored in `WakerRef`.
This commit is contained in:
Tomasz Miąsko
2020-01-06 10:39:48 -08:00
committed by Carl Lerche
parent 3540c5b9ee
commit 5930acef73
3 changed files with 44 additions and 40 deletions
-4
View File
@@ -300,10 +300,6 @@ where
self.drop_waker(); self.drop_waker();
} }
pub(super) fn wake_by_local_ref(&self) {
self.wake_by_ref();
}
pub(super) fn wake_by_ref(&self) { pub(super) fn wake_by_ref(&self) {
if self.header().state.transition_to_notified() { if self.header().state.transition_to_notified() {
unsafe { unsafe {
+18
View File
@@ -641,3 +641,21 @@ fn shutdown_from_task_after_notified() {
assert_ready_err!(handle.poll()); assert_ready_err!(handle.poll());
} }
#[test]
fn waker_ref_will_wake_clone() {
use std::task::Poll::Ready;
let (task, handle) = task::joinable(poll_fn(|cx| {
let waker = cx.waker().clone();
assert!(cx.waker().will_wake(&waker));
Ready(())
}));
let mut handle = spawn(handle);
let mock = mock().bind(&task).release_local();
let mock = &mut || Some(From::from(&mock));
assert_none!(task.run(mock));
assert_ready_ok!(handle.poll());
}
+26 -36
View File
@@ -3,11 +3,12 @@ use crate::task::{Header, Schedule};
use std::future::Future; use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::ops; use std::ops;
use std::task::{RawWaker, RawWakerVTable, Waker}; use std::task::{RawWaker, RawWakerVTable, Waker};
pub(super) struct WakerRef<'a, S: 'static> { pub(super) struct WakerRef<'a, S: 'static> {
waker: Waker, waker: ManuallyDrop<Waker>,
_p: PhantomData<(&'a Header, S)>, _p: PhantomData<(&'a Header, S)>,
} }
@@ -18,16 +19,15 @@ where
T: Future, T: Future,
S: Schedule, S: Schedule,
{ {
let ptr = meta as *const _ as *const (); // `Waker::will_wake` uses the VTABLE pointer as part of the check. This
// means that `will_wake` will always return false when using the current
let vtable = &RawWakerVTable::new( // task's waker. (discussion at rust-lang/rust#66281).
clone_waker::<T, S>, //
wake_unreachable, // To fix this, we use a single vtable. Since we pass in a reference at this
wake_by_local_ref::<T, S>, // point and not an *owned* waker, we must ensure that `drop` is never
noop, // called on this waker instance. This is done by wrapping it with
); // `ManuallyDrop` and then never calling drop.
let waker = unsafe { ManuallyDrop::new(Waker::from_raw(raw_waker::<T, S>(meta))) };
let waker = unsafe { Waker::from_raw(RawWaker::new(ptr, vtable)) };
WakerRef { WakerRef {
waker, waker,
@@ -50,15 +50,7 @@ where
{ {
let meta = ptr as *const Header; let meta = ptr as *const Header;
(*meta).state.ref_inc(); (*meta).state.ref_inc();
raw_waker::<T, S>(meta)
let vtable = &RawWakerVTable::new(
clone_waker::<T, S>,
wake_by_val::<T, S>,
wake_by_ref::<T, S>,
drop_waker::<T, S>,
);
RawWaker::new(ptr, vtable)
} }
unsafe fn drop_waker<T, S>(ptr: *const ()) unsafe fn drop_waker<T, S>(ptr: *const ())
@@ -70,11 +62,6 @@ where
harness.drop_waker(); harness.drop_waker();
} }
// `wake()` cannot be called on the ref variaant.
unsafe fn wake_unreachable(_data: *const ()) {
unreachable!();
}
unsafe fn wake_by_val<T, S>(ptr: *const ()) unsafe fn wake_by_val<T, S>(ptr: *const ())
where where
T: Future, T: Future,
@@ -84,16 +71,6 @@ where
harness.wake_by_val(); harness.wake_by_val();
} }
// This function can only be called when on the runtime.
unsafe fn wake_by_local_ref<T, S>(ptr: *const ())
where
T: Future,
S: Schedule,
{
let harness = Harness::<T, S>::from_raw(ptr as *mut _);
harness.wake_by_local_ref();
}
// Wake without consuming the waker // Wake without consuming the waker
unsafe fn wake_by_ref<T, S>(ptr: *const ()) unsafe fn wake_by_ref<T, S>(ptr: *const ())
where where
@@ -104,4 +81,17 @@ where
harness.wake_by_ref(); harness.wake_by_ref();
} }
unsafe fn noop(_ptr: *const ()) {} fn raw_waker<T, S>(meta: *const Header) -> RawWaker
where
T: Future,
S: Schedule,
{
let ptr = meta as *const ();
let vtable = &RawWakerVTable::new(
clone_waker::<T, S>,
wake_by_val::<T, S>,
wake_by_ref::<T, S>,
drop_waker::<T, S>,
);
RawWaker::new(ptr, vtable)
}