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();
}
pub(super) fn wake_by_local_ref(&self) {
self.wake_by_ref();
}
pub(super) fn wake_by_ref(&self) {
if self.header().state.transition_to_notified() {
unsafe {
+18
View File
@@ -641,3 +641,21 @@ fn shutdown_from_task_after_notified() {
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::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::ops;
use std::task::{RawWaker, RawWakerVTable, Waker};
pub(super) struct WakerRef<'a, S: 'static> {
waker: Waker,
waker: ManuallyDrop<Waker>,
_p: PhantomData<(&'a Header, S)>,
}
@@ -18,16 +19,15 @@ where
T: Future,
S: Schedule,
{
let ptr = meta as *const _ as *const ();
let vtable = &RawWakerVTable::new(
clone_waker::<T, S>,
wake_unreachable,
wake_by_local_ref::<T, S>,
noop,
);
let waker = unsafe { Waker::from_raw(RawWaker::new(ptr, vtable)) };
// `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
// task's waker. (discussion at rust-lang/rust#66281).
//
// To fix this, we use a single vtable. Since we pass in a reference at this
// point and not an *owned* waker, we must ensure that `drop` is never
// 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))) };
WakerRef {
waker,
@@ -50,15 +50,7 @@ where
{
let meta = ptr as *const Header;
(*meta).state.ref_inc();
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)
raw_waker::<T, S>(meta)
}
unsafe fn drop_waker<T, S>(ptr: *const ())
@@ -70,11 +62,6 @@ where
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 ())
where
T: Future,
@@ -84,16 +71,6 @@ where
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
unsafe fn wake_by_ref<T, S>(ptr: *const ())
where
@@ -104,4 +81,17 @@ where
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)
}