Skip to content

Commit 0b380f1

Browse files
Cache TaskLayout in Header.
This also allows debuggers to reliably decode tasks from raw memory blobs.
1 parent e8b536c commit 0b380f1

File tree

2 files changed

+45
-26
lines changed

2 files changed

+45
-26
lines changed

src/header.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use core::fmt;
33
use core::sync::atomic::{AtomicUsize, Ordering};
44
use core::task::Waker;
55

6-
use crate::raw::TaskVTable;
6+
use crate::raw::{TaskLayout, TaskVTable};
77
use crate::state::*;
88
use crate::utils::abort_on_panic;
99

@@ -26,6 +26,10 @@ pub(crate) struct Header {
2626
/// In addition to the actual waker virtual table, it also contains pointers to several other
2727
/// methods necessary for bookkeeping the heap-allocated task.
2828
pub(crate) vtable: &'static TaskVTable,
29+
30+
/// The memory layout of the task. Caching this in the header speeds things up slightly,
31+
/// and it also enables debuggers to decode raw task memory blobs.
32+
pub(crate) layout: TaskLayout,
2933
}
3034

3135
impl Header {

src/raw.rs

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use alloc::alloc::Layout;
22
use core::cell::UnsafeCell;
3+
use core::convert::TryInto;
34
use core::future::Future;
45
use core::mem::{self, ManuallyDrop};
56
use core::pin::Pin;
@@ -45,16 +46,22 @@ pub(crate) struct TaskVTable {
4546
#[derive(Clone, Copy)]
4647
pub(crate) struct TaskLayout {
4748
/// Memory layout of the whole task.
48-
pub(crate) layout: Layout,
49+
pub(crate) size: u32,
50+
pub(crate) align: u32,
4951

5052
/// Offset into the task at which the schedule function is stored.
51-
pub(crate) offset_s: usize,
53+
pub(crate) offset_schedule: u32,
5254

53-
/// Offset into the task at which the future is stored.
54-
pub(crate) offset_f: usize,
55+
/// Offset into the task at which the future or the output is stored.
56+
pub(crate) offset_future_or_output: u32,
57+
}
5558

56-
/// Offset into the task at which the output is stored.
57-
pub(crate) offset_r: usize,
59+
impl TaskLayout {
60+
#[inline]
61+
fn layout(self) -> Layout {
62+
// This is safe because `size` and `align` always come for a valid `Layout` value.
63+
unsafe { Layout::from_size_align_unchecked(self.size as usize, self.align as usize) }
64+
}
5865
}
5966

6067
/// Raw pointers to the fields inside a task.
@@ -101,15 +108,14 @@ where
101108

102109
unsafe {
103110
// Allocate enough space for the entire task.
104-
let ptr = match NonNull::new(alloc::alloc::alloc(task_layout.layout) as *mut ()) {
111+
let ptr = match NonNull::new(alloc::alloc::alloc(task_layout.layout()) as *mut ()) {
105112
None => abort(),
106113
Some(p) => p,
107114
};
108115

109-
let raw = Self::from_ptr(ptr.as_ptr());
110-
111-
// Write the header as the first field of the task.
112-
(raw.header as *mut Header).write(Header {
116+
// Write the header as the first field of the task. The header must be initialized
117+
// before `Self::from_ptr()` can be called.
118+
(ptr.as_ptr() as *mut Header).write(Header {
113119
state: AtomicUsize::new(SCHEDULED | TASK | REFERENCE),
114120
awaiter: UnsafeCell::new(None),
115121
vtable: &TaskVTable {
@@ -121,8 +127,11 @@ where
121127
run: Self::run,
122128
clone_waker: Self::clone_waker,
123129
},
130+
layout: task_layout,
124131
});
125132

133+
let raw = Self::from_ptr(ptr.as_ptr());
134+
126135
// Write the schedule function as the third field of the task.
127136
(raw.schedule as *mut S).write(schedule);
128137

@@ -136,15 +145,18 @@ where
136145
/// Creates a `RawTask` from a raw task pointer.
137146
#[inline]
138147
pub(crate) fn from_ptr(ptr: *const ()) -> Self {
139-
let task_layout = Self::task_layout();
140148
let p = ptr as *const u8;
149+
let p_header = p as *const Header;
141150

142151
unsafe {
152+
let offset_schedule = (*p_header).layout.offset_schedule as usize;
153+
let offset_future_or_output = (*p_header).layout.offset_future_or_output as usize;
154+
143155
Self {
144-
header: p as *const Header,
145-
schedule: p.add(task_layout.offset_s) as *const S,
146-
future: p.add(task_layout.offset_f) as *mut F,
147-
output: p.add(task_layout.offset_r) as *mut T,
156+
header: p_header,
157+
schedule: p.add(offset_schedule) as *const S,
158+
future: p.add(offset_future_or_output) as *mut F,
159+
output: p.add(offset_future_or_output) as *mut T,
148160
}
149161
}
150162
}
@@ -165,16 +177,20 @@ where
165177

166178
// Compute the layout for `Header` followed `S` and `union { F, T }`.
167179
let layout = layout_header;
168-
let (layout, offset_s) = extend(layout, layout_s);
180+
let (layout, offset_schedule) = extend(layout, layout_s);
169181
let (layout, offset_union) = extend(layout, layout_union);
170-
let offset_f = offset_union;
171-
let offset_r = offset_union;
182+
183+
// Converting to `u32` is unlikely to fail.
184+
let size: u32 = layout.size().try_into().unwrap();
185+
let align: u32 = layout.align().try_into().unwrap();
186+
let offset_schedule: u32 = offset_schedule.try_into().unwrap();
187+
let offset_future_or_output: u32 = offset_union.try_into().unwrap();
172188

173189
TaskLayout {
174-
layout,
175-
offset_s,
176-
offset_f,
177-
offset_r,
190+
size,
191+
align,
192+
offset_schedule,
193+
offset_future_or_output,
178194
}
179195
}
180196

@@ -416,7 +432,6 @@ where
416432
#[inline]
417433
unsafe fn destroy(ptr: *const ()) {
418434
let raw = Self::from_ptr(ptr);
419-
let task_layout = Self::task_layout();
420435

421436
// We need a safeguard against panics because destructors can panic.
422437
abort_on_panic(|| {
@@ -425,7 +440,7 @@ where
425440
});
426441

427442
// Finally, deallocate the memory reserved by the task.
428-
alloc::alloc::dealloc(ptr as *mut u8, task_layout.layout);
443+
alloc::alloc::dealloc(ptr as *mut u8, (*raw.header).layout.layout());
429444
}
430445

431446
/// Runs a task.

0 commit comments

Comments
 (0)