use crate::future::Future; use crate::runtime::task::core::{Core, Trailer}; use crate::runtime::task::{Cell, Harness, Header, Id, Schedule, State}; use std::ptr::NonNull; use std::task::{Poll, Waker}; /// Raw task handle #[derive(Clone)] pub(crate) struct RawTask { ptr: NonNull
, } pub(super) struct Vtable { /// Polls the future. pub(super) poll: unsafe fn(NonNull
), /// Schedules the task for execution on the runtime. pub(super) schedule: unsafe fn(NonNull
), /// Deallocates the memory. pub(super) dealloc: unsafe fn(NonNull
), /// Reads the task output, if complete. pub(super) try_read_output: unsafe fn(NonNull
, *mut (), &Waker), /// The join handle has been dropped. pub(super) drop_join_handle_slow: unsafe fn(NonNull
), /// An abort handle has been dropped. pub(super) drop_abort_handle: unsafe fn(NonNull
), /// Scheduler is being shutdown. pub(super) shutdown: unsafe fn(NonNull
), /// The number of bytes that the `trailer` field is offset from the header. pub(super) trailer_offset: usize, /// The number of bytes that the `scheduler` field is offset from the header. pub(super) scheduler_offset: usize, /// The number of bytes that the `id` field is offset from the header. pub(super) id_offset: usize, } /// Get the vtable for the requested `T` and `S` generics. pub(super) fn vtable() -> &'static Vtable { &Vtable { poll: poll::, schedule: schedule::, dealloc: dealloc::, try_read_output: try_read_output::, drop_join_handle_slow: drop_join_handle_slow::, drop_abort_handle: drop_abort_handle::, shutdown: shutdown::, trailer_offset: OffsetHelper::::TRAILER_OFFSET, scheduler_offset: OffsetHelper::::SCHEDULER_OFFSET, id_offset: OffsetHelper::::ID_OFFSET, } } /// Calling `get_trailer_offset` directly in vtable doesn't work because it /// prevents the vtable from being promoted to a static reference. /// /// See this thread for more info: /// struct OffsetHelper(T, S); impl OffsetHelper { // Pass `size_of`/`align_of` as arguments rather than calling them directly // inside `get_trailer_offset` because trait bounds on generic parameters // of const fn are unstable on our MSRV. const TRAILER_OFFSET: usize = get_trailer_offset( std::mem::size_of::
(), std::mem::size_of::>(), std::mem::align_of::>(), std::mem::align_of::(), ); // The `scheduler` is the first field of `Core`, so it has the same // offset as `Core`. const SCHEDULER_OFFSET: usize = get_core_offset( std::mem::size_of::
(), std::mem::align_of::>(), ); const ID_OFFSET: usize = get_id_offset( std::mem::size_of::
(), std::mem::align_of::>(), std::mem::size_of::(), std::mem::align_of::(), ); } /// Compute the offset of the `Trailer` field in `Cell` using the /// `#[repr(C)]` algorithm. /// /// Pseudo-code for the `#[repr(C)]` algorithm can be found here: /// const fn get_trailer_offset( header_size: usize, core_size: usize, core_align: usize, trailer_align: usize, ) -> usize { let mut offset = header_size; let core_misalign = offset % core_align; if core_misalign > 0 { offset += core_align - core_misalign; } offset += core_size; let trailer_misalign = offset % trailer_align; if trailer_misalign > 0 { offset += trailer_align - trailer_misalign; } offset } /// Compute the offset of the `Core` field in `Cell` using the /// `#[repr(C)]` algorithm. /// /// Pseudo-code for the `#[repr(C)]` algorithm can be found here: /// const fn get_core_offset(header_size: usize, core_align: usize) -> usize { let mut offset = header_size; let core_misalign = offset % core_align; if core_misalign > 0 { offset += core_align - core_misalign; } offset } /// Compute the offset of the `Id` field in `Cell` using the /// `#[repr(C)]` algorithm. /// /// Pseudo-code for the `#[repr(C)]` algorithm can be found here: /// const fn get_id_offset( header_size: usize, core_align: usize, scheduler_size: usize, id_align: usize, ) -> usize { let mut offset = get_core_offset(header_size, core_align); offset += scheduler_size; let id_misalign = offset % id_align; if id_misalign > 0 { offset += id_align - id_misalign; } offset } impl RawTask { pub(super) fn new(task: T, scheduler: S, id: Id) -> RawTask where T: Future, S: Schedule, { let ptr = Box::into_raw(Cell::<_, S>::new(task, scheduler, State::new(), id)); let ptr = unsafe { NonNull::new_unchecked(ptr.cast()) }; RawTask { ptr } } pub(super) unsafe fn from_raw(ptr: NonNull
) -> RawTask { RawTask { ptr } } pub(super) fn header_ptr(&self) -> NonNull
{ self.ptr } pub(super) fn trailer_ptr(&self) -> NonNull { unsafe { Header::get_trailer(self.ptr) } } /// Returns a reference to the task's header. pub(super) fn header(&self) -> &Header { unsafe { self.ptr.as_ref() } } /// Returns a reference to the task's trailer. pub(super) fn trailer(&self) -> &Trailer { unsafe { &*self.trailer_ptr().as_ptr() } } /// Returns a reference to the task's state. pub(super) fn state(&self) -> &State { &self.header().state } /// Safety: mutual exclusion is required to call this function. pub(crate) fn poll(self) { let vtable = self.header().vtable; unsafe { (vtable.poll)(self.ptr) } } pub(super) fn schedule(self) { let vtable = self.header().vtable; unsafe { (vtable.schedule)(self.ptr) } } pub(super) fn dealloc(self) { let vtable = self.header().vtable; unsafe { (vtable.dealloc)(self.ptr); } } /// Safety: `dst` must be a `*mut Poll>` where `T` /// is the future stored by the task. pub(super) unsafe fn try_read_output(self, dst: *mut (), waker: &Waker) { let vtable = self.header().vtable; (vtable.try_read_output)(self.ptr, dst, waker); } pub(super) fn drop_join_handle_slow(self) { let vtable = self.header().vtable; unsafe { (vtable.drop_join_handle_slow)(self.ptr) } } pub(super) fn drop_abort_handle(self) { let vtable = self.header().vtable; unsafe { (vtable.drop_abort_handle)(self.ptr) } } pub(super) fn shutdown(self) { let vtable = self.header().vtable; unsafe { (vtable.shutdown)(self.ptr) } } /// Increment the task's reference count. /// /// Currently, this is used only when creating an `AbortHandle`. pub(super) fn ref_inc(self) { self.header().state.ref_inc(); } /// Get the queue-next pointer /// /// This is for usage by the injection queue /// /// Safety: make sure only one queue uses this and access is synchronized. pub(crate) unsafe fn get_queue_next(self) -> Option { self.header() .queue_next .with(|ptr| *ptr) .map(|p| RawTask::from_raw(p)) } /// Sets the queue-next pointer /// /// This is for usage by the injection queue /// /// Safety: make sure only one queue uses this and access is synchronized. pub(crate) unsafe fn set_queue_next(self, val: Option) { self.header().set_next(val.map(|task| task.ptr)); } } impl Copy for RawTask {} unsafe fn poll(ptr: NonNull
) { let harness = Harness::::from_raw(ptr); harness.poll(); } unsafe fn schedule(ptr: NonNull
) { use crate::runtime::task::{Notified, Task}; let scheduler = Header::get_scheduler::(ptr); scheduler .as_ref() .schedule(Notified(Task::from_raw(ptr.cast()))); } unsafe fn dealloc(ptr: NonNull
) { let harness = Harness::::from_raw(ptr); harness.dealloc(); } unsafe fn try_read_output( ptr: NonNull
, dst: *mut (), waker: &Waker, ) { let out = &mut *(dst as *mut Poll>); let harness = Harness::::from_raw(ptr); harness.try_read_output(out, waker); } unsafe fn drop_join_handle_slow(ptr: NonNull
) { let harness = Harness::::from_raw(ptr); harness.drop_join_handle_slow(); } unsafe fn drop_abort_handle(ptr: NonNull
) { let harness = Harness::::from_raw(ptr); harness.drop_reference(); } unsafe fn shutdown(ptr: NonNull
) { let harness = Harness::::from_raw(ptr); harness.shutdown(); }