Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Let the compiler do memory layout computation for tasks at compile time #27

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
83 changes: 28 additions & 55 deletions src/raw.rs
Expand Up @@ -3,13 +3,13 @@ use core::cell::UnsafeCell;
use core::future::Future;
use core::mem::{self, ManuallyDrop};
use core::pin::Pin;
use core::ptr::NonNull;
use core::ptr::{addr_of, addr_of_mut, NonNull};
use core::sync::atomic::{AtomicUsize, Ordering};
use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};

use crate::header::Header;
use crate::state::*;
use crate::utils::{abort, abort_on_panic, extend};
use crate::utils::{abort, abort_on_panic};
use crate::Runnable;

/// The vtable for a task.
Expand Down Expand Up @@ -38,23 +38,25 @@ pub(crate) struct TaskVTable {

/// Memory layout of a task.
///
/// This struct contains the following information:
/// This type defines the layout of the raw memory blob we allocate for each
/// task.
///
/// 1. How to allocate and deallocate the task.
/// 2. How to access the fields inside the task.
#[derive(Clone, Copy)]
pub(crate) struct TaskLayout {
/// Memory layout of the whole task.
pub(crate) layout: Layout,

/// Offset into the task at which the schedule function is stored.
pub(crate) offset_s: usize,

/// Offset into the task at which the future is stored.
pub(crate) offset_f: usize,
/// Note: It is advantageous for (crash dump) debugging to have an actual type
/// that defines the layout, because that type will be described in
/// debuginfo, which in turn allows debuggers to decode a task's raw
/// memory blob. Without that type definiton, we cannot compute task
/// layouts at runtime because debuginfo does not contain information
/// about type alignments on all platforms.
#[repr(C)]
pub(crate) struct TaskLayout<F, T, S> {
header: Header,
schedule: ManuallyDrop<S>,
future_or_output: FutureOrOutputLayout<F, T>,
}

/// Offset into the task at which the output is stored.
pub(crate) offset_r: usize,
union FutureOrOutputLayout<F, T> {
future: ManuallyDrop<F>,
output: ManuallyDrop<T>,
}

/// Raw pointers to the fields inside a task.
Expand Down Expand Up @@ -96,12 +98,9 @@ where
///
/// It is assumed that initially only the `Runnable` and the `Task` exist.
pub(crate) fn allocate(future: F, schedule: S) -> NonNull<()> {
// Compute the layout of the task for allocation. Abort if the computation fails.
let task_layout = abort_on_panic(|| Self::task_layout());

unsafe {
// Allocate enough space for the entire task.
let ptr = match NonNull::new(alloc::alloc::alloc(task_layout.layout) as *mut ()) {
let ptr = match NonNull::new(alloc::alloc::alloc(Self::task_layout()) as *mut ()) {
None => abort(),
Some(p) => p,
};
Expand Down Expand Up @@ -136,46 +135,21 @@ where
/// Creates a `RawTask` from a raw task pointer.
#[inline]
pub(crate) fn from_ptr(ptr: *const ()) -> Self {
let task_layout = Self::task_layout();
let p = ptr as *const u8;
let task_layout = ptr as *mut TaskLayout<F, T, S>;

unsafe {
Self {
header: p as *const Header,
schedule: p.add(task_layout.offset_s) as *const S,
future: p.add(task_layout.offset_f) as *mut F,
output: p.add(task_layout.offset_r) as *mut T,
header: addr_of!((*task_layout).header),
schedule: addr_of!((*task_layout).schedule) as *const S,
future: addr_of_mut!((*task_layout).future_or_output.future) as *mut F,
output: addr_of_mut!((*task_layout).future_or_output.output) as *mut T,
}
}
}

/// Returns the memory layout for a task.
#[inline]
fn task_layout() -> TaskLayout {
// Compute the layouts for `Header`, `S`, `F`, and `T`.
let layout_header = Layout::new::<Header>();
let layout_s = Layout::new::<S>();
let layout_f = Layout::new::<F>();
let layout_r = Layout::new::<T>();

// Compute the layout for `union { F, T }`.
let size_union = layout_f.size().max(layout_r.size());
let align_union = layout_f.align().max(layout_r.align());
let layout_union = unsafe { Layout::from_size_align_unchecked(size_union, align_union) };

// Compute the layout for `Header` followed `S` and `union { F, T }`.
let layout = layout_header;
let (layout, offset_s) = extend(layout, layout_s);
let (layout, offset_union) = extend(layout, layout_union);
let offset_f = offset_union;
let offset_r = offset_union;

TaskLayout {
layout,
offset_s,
offset_f,
offset_r,
}
fn task_layout() -> Layout {
Layout::new::<TaskLayout<F, T, S>>()
}

/// Wakes a waker.
Expand Down Expand Up @@ -416,7 +390,6 @@ where
#[inline]
unsafe fn destroy(ptr: *const ()) {
let raw = Self::from_ptr(ptr);
let task_layout = Self::task_layout();

// We need a safeguard against panics because destructors can panic.
abort_on_panic(|| {
Expand All @@ -425,7 +398,7 @@ where
});

// Finally, deallocate the memory reserved by the task.
alloc::alloc::dealloc(ptr as *mut u8, task_layout.layout);
alloc::alloc::dealloc(ptr as *mut u8, Self::task_layout());
}

/// Runs a task.
Expand Down
28 changes: 0 additions & 28 deletions src/utils.rs
@@ -1,4 +1,3 @@
use core::alloc::Layout;
use core::mem;

/// Aborts the process.
Expand Down Expand Up @@ -35,30 +34,3 @@ pub(crate) fn abort_on_panic<T>(f: impl FnOnce() -> T) -> T {
mem::forget(bomb);
t
}

/// Returns the layout for `a` followed by `b` and the offset of `b`.
///
/// This function was adapted from the currently unstable `Layout::extend()`:
/// https://doc.rust-lang.org/nightly/std/alloc/struct.Layout.html#method.extend
#[inline]
pub(crate) fn extend(a: Layout, b: Layout) -> (Layout, usize) {
let new_align = a.align().max(b.align());
let pad = padding_needed_for(a, b.align());

let offset = a.size().checked_add(pad).unwrap();
let new_size = offset.checked_add(b.size()).unwrap();

let layout = Layout::from_size_align(new_size, new_align).unwrap();
(layout, offset)
}

/// Returns the padding after `layout` that aligns the following address to `align`.
///
/// This function was adapted from the currently unstable `Layout::padding_needed_for()`:
/// https://doc.rust-lang.org/nightly/std/alloc/struct.Layout.html#method.padding_needed_for
#[inline]
pub(crate) fn padding_needed_for(layout: Layout, align: usize) -> usize {
let len = layout.size();
let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);
len_rounded_up.wrapping_sub(len)
}