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<Header>,
}
pub(
super)
struct Vtable {
/// Polls the future.
pub(
super) poll:
unsafe fn(NonNull<Header>),
/// Schedules the task for execution on the runtime.
pub(
super) schedule:
unsafe fn(NonNull<Header>),
/// Deallocates the memory.
pub(
super) dealloc:
unsafe fn(NonNull<Header>),
/// Reads the task output, if complete.
pub(
super) try_read_output:
unsafe fn(NonNull<Header>, *
mut (), &Waker),
/// The join handle has been dropped.
pub(
super) drop_join_handle_slow:
unsafe fn(NonNull<Header>),
/// An abort handle has been dropped.
pub(
super) drop_abort_handle:
unsafe fn(NonNull<Header>),
/// Scheduler is being shutdown.
pub(
super) shutdown:
unsafe fn(NonNull<Header>),
/// 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<T: Future, S: Schedule>() -> &
'static Vtable {
&Vtable {
poll: poll::<T, S>,
schedule: schedule::<S>,
dealloc: dealloc::<T, S>,
try_read_output: try_read_output::<T, S>,
drop_join_handle_slow: drop_join_handle_slow::<T, S>,
drop_abort_handle: drop_abort_handle::<T, S>,
shutdown: shutdown::<T, S>,
trailer_offset: OffsetHelper::<T, S>::TRAILER_OFFSET,
scheduler_offset: OffsetHelper::<T, S>::SCHEDULER_OFFSET,
id_offset: OffsetHelper::<T, S>::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:
/// <https://users.rust-lang.org/t/custom-vtables-with-integers/78508>
struct OffsetHelper<T, S>(T, S);
impl<T: Future, S: Schedule> OffsetHelper<T, S> {
// 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::<Header>(),
std::mem::size_of::<Core<T, S>>(),
std::mem::align_of::<Core<T, S>>(),
std::mem::align_of::<Trailer>(),
);
// 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::<Header>(),
std::mem::align_of::<Core<T, S>>(),
);
const ID_OFFSET: usize = get_id_offset(
std::mem::size_of::<Header>(),
std::mem::align_of::<Core<T, S>>(),
std::mem::size_of::<S>(),
std::mem::align_of::<Id>(),
);
}
/// Compute the offset of the `Trailer` field in `Cell<T, S>` using the
/// `#[repr(C)]` algorithm.
///
/// Pseudo-code for the `#[repr(C)]` algorithm can be found here:
/// <https://doc.rust-lang.org/reference/type-layout.html#reprc-structs>
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<T, S>` field in `Cell<T, S>` using the
/// `#[repr(C)]` algorithm.
///
/// Pseudo-code for the `#[repr(C)]` algorithm can be found here:
/// <https://doc.rust-lang.org/reference/type-layout.html#reprc-structs>
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<T, S>` using the
/// `#[repr(C)]` algorithm.
///
/// Pseudo-code for the `#[repr(C)]` algorithm can be found here:
/// <https://doc.rust-lang.org/reference/type-layout.html#reprc-structs>
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<T, S>(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<Header>) -> RawTask {
RawTask { ptr }
}
pub(
super)
fn header_ptr(&
self) -> NonNull<Header> {
self.ptr
}
pub(
super)
fn trailer_ptr(&
self) -> NonNull<Trailer> {
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<super::Result<T::Output>>` 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<RawTask> {
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<RawTask>) {
self.header().set_next(val.map(|task| task.ptr));
}
}
impl Copy
for RawTask {}
unsafe fn poll<T: Future, S: Schedule>(ptr: NonNull<Header>) {
let harness = Harness::<T, S>::from_raw(ptr);
harness.poll();
}
unsafe fn schedule<S: Schedule>(ptr: NonNull<Header>) {
use crate::runtime::task::{Notified, Task};
let scheduler = Header::get_scheduler::<S>(ptr);
scheduler
.as_ref()
.schedule(Notified(Task::from_raw(ptr.cast())));
}
unsafe fn dealloc<T: Future, S: Schedule>(ptr: NonNull<Header>) {
let harness = Harness::<T, S>::from_raw(ptr);
harness.dealloc();
}
unsafe fn try_read_output<T: Future, S: Schedule>(
ptr: NonNull<Header>,
dst: *
mut (),
waker: &Waker,
) {
let out = &
mut *(dst
as *
mut Poll<
super::Result<T::Output>>);
let harness = Harness::<T, S>::from_raw(ptr);
harness.try_read_output(out, waker);
}
unsafe fn drop_join_handle_slow<T: Future, S: Schedule>(ptr: NonNull<Header>) {
let harness = Harness::<T, S>::from_raw(ptr);
harness.drop_join_handle_slow();
}
unsafe fn drop_abort_handle<T: Future, S: Schedule>(ptr: NonNull<Header>) {
let harness = Harness::<T, S>::from_raw(ptr);
harness.drop_reference();
}
unsafe fn shutdown<T: Future, S: Schedule>(ptr: NonNull<Header>) {
let harness = Harness::<T, S>::from_raw(ptr);
harness.shutdown();
}