From e6982b558368502411fd3089ce5860f8164af96c Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 6 Jan 2020 22:18:30 +0100 Subject: [PATCH] Add waker and conversion to raw pointer --- src/join_handle.rs | 13 ++++++++++++- src/raw.rs | 4 ++++ src/task.rs | 37 ++++++++++++++++++++++++++++++++++++- tests/basic.rs | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 88 insertions(+), 2 deletions(-) diff --git a/src/join_handle.rs b/src/join_handle.rs index 49d529b..f4710cc 100644 --- a/src/join_handle.rs +++ b/src/join_handle.rs @@ -4,7 +4,7 @@ use core::marker::{PhantomData, Unpin}; use core::pin::Pin; use core::ptr::NonNull; use core::sync::atomic::Ordering; -use core::task::{Context, Poll}; +use core::task::{Context, Poll, Waker}; use crate::header::Header; use crate::state::*; @@ -92,6 +92,17 @@ impl JoinHandle { &*raw } } + + /// Returns a waker associated with the task. + pub fn waker(&self) -> Waker { + let ptr = self.raw_task.as_ptr(); + let header = ptr as *const Header; + + unsafe { + let raw_waker = ((*header).vtable.clone_waker)(ptr); + Waker::from_raw(raw_waker) + } + } } impl Drop for JoinHandle { diff --git a/src/raw.rs b/src/raw.rs index 6af184f..c783d26 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -35,6 +35,9 @@ pub(crate) struct TaskVTable { /// Runs the task. pub(crate) run: unsafe fn(*const ()), + + /// Creates a new waker associated with the task. + pub(crate) clone_waker: unsafe fn(ptr: *const ()) -> RawWaker, } /// Memory layout of a task. @@ -131,6 +134,7 @@ where drop_task: Self::drop_task, destroy: Self::destroy, run: Self::run, + clone_waker: Self::clone_waker, }, }); diff --git a/src/task.rs b/src/task.rs index 80953f4..b26c082 100644 --- a/src/task.rs +++ b/src/task.rs @@ -4,7 +4,7 @@ use core::marker::PhantomData; use core::mem::{self, ManuallyDrop}; use core::pin::Pin; use core::ptr::NonNull; -use core::task::{Context, Poll}; +use core::task::{Context, Poll, Waker}; use crate::header::Header; use crate::raw::RawTask; @@ -264,6 +264,41 @@ impl Task { &*raw } } + + /// Converts this task into a raw pointer to the tag. + pub fn into_raw(self) -> *const T { + let offset = Header::offset_tag::(); + let ptr = self.raw_task.as_ptr(); + mem::forget(self); + + unsafe { (ptr as *mut u8).add(offset) as *const T } + } + + /// Converts a raw pointer to the tag into a task. + /// + /// This method should only be used with raw pointers returned from [`into_raw`]. + /// + /// [`into_raw`]: #method.into_raw + pub unsafe fn from_raw(raw: *const T) -> Task { + let offset = Header::offset_tag::(); + let ptr = (raw as *mut u8).sub(offset) as *mut (); + + Task { + raw_task: NonNull::new_unchecked(ptr), + _marker: PhantomData, + } + } + + /// Returns a waker associated with this task. + pub fn waker(&self) -> Waker { + let ptr = self.raw_task.as_ptr(); + let header = ptr as *const Header; + + unsafe { + let raw_waker = ((*header).vtable.clone_waker)(ptr); + Waker::from_raw(raw_waker) + } + } } impl Drop for Task { diff --git a/tests/basic.rs b/tests/basic.rs index 8426d2a..432e14c 100644 --- a/tests/basic.rs +++ b/tests/basic.rs @@ -332,3 +332,39 @@ fn drop_inside_schedule() { ); task.schedule(); } + +#[test] +fn waker() { + let (s, r) = channel::unbounded(); + let schedule = move |t| s.send(t).unwrap(); + let (task, handle) = async_task::spawn( + future::poll_fn(|_| Poll::<()>::Pending), + schedule, + Box::new(0), + ); + + assert!(r.is_empty()); + let w = task.waker(); + task.run(); + w.wake(); + + let task = r.recv().unwrap(); + task.run(); + handle.waker().wake(); + + r.recv().unwrap(); +} + +#[test] +fn raw() { + let (task, _handle) = async_task::spawn(async {}, |_| panic!(), Box::new(AtomicUsize::new(7))); + + let a = task.into_raw(); + let task = unsafe { + (*a).fetch_add(1, Ordering::SeqCst); + Task::from_raw(a) + }; + + assert_eq!(task.tag().load(Ordering::SeqCst), 8); + task.run(); +}