Skip to content

Commit d5bd8be

Browse files
authored
Add metadata to tasks (#33)
* Add metadata to tasks * Make sure to drop the header. * Revamp so that it uses a generator instead * Fix heap allocation * Slightly more elegant strategy * Add a test for using metadata. * Use non-driven block_on() instead.
1 parent 230b0a4 commit d5bd8be

File tree

6 files changed

+500
-129
lines changed

6 files changed

+500
-129
lines changed

src/header.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::utils::abort_on_panic;
1010
/// The header of a task.
1111
///
1212
/// This header is stored in memory at the beginning of the heap-allocated task.
13-
pub(crate) struct Header {
13+
pub(crate) struct Header<M> {
1414
/// Current state of the task.
1515
///
1616
/// Contains flags representing the current state and the reference count.
@@ -26,9 +26,14 @@ 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+
/// Metadata associated with the task.
31+
///
32+
/// This metadata may be provided to the user.
33+
pub(crate) metadata: M,
2934
}
3035

31-
impl Header {
36+
impl<M> Header<M> {
3237
/// Notifies the awaiter blocked on this task.
3338
///
3439
/// If the awaiter is the same as the current waker, it will not be notified.
@@ -145,7 +150,7 @@ impl Header {
145150
}
146151
}
147152

148-
impl fmt::Debug for Header {
153+
impl<M: fmt::Debug> fmt::Debug for Header<M> {
149154
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150155
let state = self.state.load(Ordering::SeqCst);
151156

@@ -157,6 +162,7 @@ impl fmt::Debug for Header {
157162
.field("awaiter", &(state & AWAITER != 0))
158163
.field("task", &(state & TASK != 0))
159164
.field("ref_count", &(state / REFERENCE))
165+
.field("metadata", &self.metadata)
160166
.finish()
161167
}
162168
}

src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ mod state;
9292
mod task;
9393
mod utils;
9494

95-
pub use crate::runnable::{spawn, spawn_unchecked, Runnable};
95+
pub use crate::runnable::{spawn, spawn_unchecked, Builder, Runnable};
9696
pub use crate::task::{FallibleTask, Task};
9797

9898
#[cfg(feature = "std")]

src/raw.rs

+37-19
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use alloc::alloc::Layout as StdLayout;
22
use core::cell::UnsafeCell;
33
use core::future::Future;
4+
use core::marker::PhantomData;
45
use core::mem::{self, ManuallyDrop};
56
use core::pin::Pin;
67
use core::ptr::NonNull;
@@ -64,9 +65,9 @@ pub(crate) struct TaskLayout {
6465
}
6566

6667
/// Raw pointers to the fields inside a task.
67-
pub(crate) struct RawTask<F, T, S> {
68+
pub(crate) struct RawTask<F, T, S, M> {
6869
/// The task header.
69-
pub(crate) header: *const Header,
70+
pub(crate) header: *const Header<M>,
7071

7172
/// The schedule function.
7273
pub(crate) schedule: *const S,
@@ -78,22 +79,22 @@ pub(crate) struct RawTask<F, T, S> {
7879
pub(crate) output: *mut T,
7980
}
8081

81-
impl<F, T, S> Copy for RawTask<F, T, S> {}
82+
impl<F, T, S, M> Copy for RawTask<F, T, S, M> {}
8283

83-
impl<F, T, S> Clone for RawTask<F, T, S> {
84+
impl<F, T, S, M> Clone for RawTask<F, T, S, M> {
8485
fn clone(&self) -> Self {
8586
*self
8687
}
8788
}
8889

89-
impl<F, T, S> RawTask<F, T, S> {
90+
impl<F, T, S, M> RawTask<F, T, S, M> {
9091
const TASK_LAYOUT: Option<TaskLayout> = Self::eval_task_layout();
9192

9293
/// Computes the memory layout for a task.
9394
#[inline]
9495
const fn eval_task_layout() -> Option<TaskLayout> {
9596
// Compute the layouts for `Header`, `S`, `F`, and `T`.
96-
let layout_header = Layout::new::<Header>();
97+
let layout_header = Layout::new::<Header<M>>();
9798
let layout_s = Layout::new::<S>();
9899
let layout_f = Layout::new::<F>();
99100
let layout_r = Layout::new::<T>();
@@ -119,10 +120,10 @@ impl<F, T, S> RawTask<F, T, S> {
119120
}
120121
}
121122

122-
impl<F, T, S> RawTask<F, T, S>
123+
impl<F, T, S, M> RawTask<F, T, S, M>
123124
where
124125
F: Future<Output = T>,
125-
S: Fn(Runnable),
126+
S: Fn(Runnable<M>),
126127
{
127128
const RAW_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(
128129
Self::clone_waker,
@@ -134,7 +135,15 @@ where
134135
/// Allocates a task with the given `future` and `schedule` function.
135136
///
136137
/// It is assumed that initially only the `Runnable` and the `Task` exist.
137-
pub(crate) fn allocate(future: F, schedule: S) -> NonNull<()> {
138+
pub(crate) fn allocate<'a, Gen: FnOnce(&'a M) -> F>(
139+
future: Gen,
140+
schedule: S,
141+
metadata: M,
142+
) -> NonNull<()>
143+
where
144+
F: 'a,
145+
M: 'a,
146+
{
138147
// Compute the layout of the task for allocation. Abort if the computation fails.
139148
//
140149
// n.b. notgull: task_layout now automatically aborts instead of panicking
@@ -150,7 +159,7 @@ where
150159
let raw = Self::from_ptr(ptr.as_ptr());
151160

152161
// Write the header as the first field of the task.
153-
(raw.header as *mut Header).write(Header {
162+
(raw.header as *mut Header<M>).write(Header {
154163
state: AtomicUsize::new(SCHEDULED | TASK | REFERENCE),
155164
awaiter: UnsafeCell::new(None),
156165
vtable: &TaskVTable {
@@ -163,11 +172,15 @@ where
163172
clone_waker: Self::clone_waker,
164173
layout_info: &Self::TASK_LAYOUT,
165174
},
175+
metadata,
166176
});
167177

168178
// Write the schedule function as the third field of the task.
169179
(raw.schedule as *mut S).write(schedule);
170180

181+
// Generate the future, now that the metadata has been pinned in place.
182+
let future = abort_on_panic(|| future(&(*raw.header).metadata));
183+
171184
// Write the future as the fourth field of the task.
172185
raw.future.write(future);
173186

@@ -183,7 +196,7 @@ where
183196

184197
unsafe {
185198
Self {
186-
header: p as *const Header,
199+
header: p as *const Header<M>,
187200
schedule: p.add(task_layout.offset_s) as *const S,
188201
future: p.add(task_layout.offset_f) as *mut F,
189202
output: p.add(task_layout.offset_r) as *mut T,
@@ -319,6 +332,7 @@ where
319332
// still alive.
320333
let task = Runnable {
321334
ptr: NonNull::new_unchecked(ptr as *mut ()),
335+
_marker: PhantomData,
322336
};
323337
(*raw.schedule)(task);
324338
}
@@ -410,6 +424,7 @@ where
410424

411425
let task = Runnable {
412426
ptr: NonNull::new_unchecked(ptr as *mut ()),
427+
_marker: PhantomData,
413428
};
414429
(*raw.schedule)(task);
415430
}
@@ -442,6 +457,9 @@ where
442457

443458
// We need a safeguard against panics because destructors can panic.
444459
abort_on_panic(|| {
460+
// Drop the header along with the metadata.
461+
(raw.header as *mut Header<M>).drop_in_place();
462+
445463
// Drop the schedule function.
446464
(raw.schedule as *mut S).drop_in_place();
447465
});
@@ -625,15 +643,15 @@ where
625643
return false;
626644

627645
/// A guard that closes the task if polling its future panics.
628-
struct Guard<F, T, S>(RawTask<F, T, S>)
646+
struct Guard<F, T, S, M>(RawTask<F, T, S, M>)
629647
where
630648
F: Future<Output = T>,
631-
S: Fn(Runnable);
649+
S: Fn(Runnable<M>);
632650

633-
impl<F, T, S> Drop for Guard<F, T, S>
651+
impl<F, T, S, M> Drop for Guard<F, T, S, M>
634652
where
635653
F: Future<Output = T>,
636-
S: Fn(Runnable),
654+
S: Fn(Runnable<M>),
637655
{
638656
fn drop(&mut self) {
639657
let raw = self.0;
@@ -648,7 +666,7 @@ where
648666
if state & CLOSED != 0 {
649667
// The thread that closed the task didn't drop the future because it
650668
// was running so now it's our responsibility to do so.
651-
RawTask::<F, T, S>::drop_future(ptr);
669+
RawTask::<F, T, S, M>::drop_future(ptr);
652670

653671
// Mark the task as not running and not scheduled.
654672
(*raw.header)
@@ -662,7 +680,7 @@ where
662680
}
663681

664682
// Drop the task reference.
665-
RawTask::<F, T, S>::drop_ref(ptr);
683+
RawTask::<F, T, S, M>::drop_ref(ptr);
666684

667685
// Notify the awaiter that the future has been dropped.
668686
if let Some(w) = awaiter {
@@ -680,7 +698,7 @@ where
680698
) {
681699
Ok(state) => {
682700
// Drop the future because the task is now closed.
683-
RawTask::<F, T, S>::drop_future(ptr);
701+
RawTask::<F, T, S, M>::drop_future(ptr);
684702

685703
// Take the awaiter out.
686704
let mut awaiter = None;
@@ -689,7 +707,7 @@ where
689707
}
690708

691709
// Drop the task reference.
692-
RawTask::<F, T, S>::drop_ref(ptr);
710+
RawTask::<F, T, S, M>::drop_ref(ptr);
693711

694712
// Notify the awaiter that the future has been dropped.
695713
if let Some(w) = awaiter {

0 commit comments

Comments
 (0)