|
1 | 1 | use alloc::alloc::Layout as StdLayout; |
2 | | -use core::cell::UnsafeCell; |
3 | 2 | use core::future::Future; |
4 | 3 | use core::mem::{self, ManuallyDrop}; |
5 | 4 | use core::pin::Pin; |
6 | 5 | use core::ptr::NonNull; |
7 | 6 | use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; |
8 | 7 |
|
9 | | -#[cfg(not(feature = "portable-atomic"))] |
10 | | -use core::sync::atomic::AtomicUsize; |
11 | 8 | use core::sync::atomic::Ordering; |
12 | | -#[cfg(feature = "portable-atomic")] |
13 | | -use portable_atomic::AtomicUsize; |
14 | 9 |
|
15 | 10 | use crate::header::Header; |
16 | 11 | use crate::runnable::{Schedule, ScheduleInfo}; |
@@ -99,7 +94,7 @@ impl<F, T, S, M> Clone for RawTask<F, T, S, M> { |
99 | 94 | } |
100 | 95 |
|
101 | 96 | impl<F, T, S, M> RawTask<F, T, S, M> { |
102 | | - const TASK_LAYOUT: TaskLayout = Self::eval_task_layout(); |
| 97 | + pub(crate) const TASK_LAYOUT: TaskLayout = Self::eval_task_layout(); |
103 | 98 |
|
104 | 99 | /// Computes the memory layout for a task. |
105 | 100 | #[inline] |
@@ -131,81 +126,81 @@ impl<F, T, S, M> RawTask<F, T, S, M> { |
131 | 126 | } |
132 | 127 | } |
133 | 128 |
|
| 129 | +/// Allocates a task with the given `future` and `schedule` function. |
| 130 | +/// |
| 131 | +/// It is assumed that initially only the `Runnable` and the `Task` exist. |
| 132 | +/// |
| 133 | +/// Use a macro to brute force inlining to minimize stack copies of potentially |
| 134 | +/// large futures. |
| 135 | +macro_rules! allocate_task { |
| 136 | + ($f:tt, $s:tt, $m:tt, $builder:ident, $schedule:ident, $raw:ident => $future:block) => {{ |
| 137 | + let allocation = |
| 138 | + alloc::alloc::alloc(RawTask::<$f, <$f as Future>::Output, $s, $m>::TASK_LAYOUT.layout); |
| 139 | + // Allocate enough space for the entire task. |
| 140 | + let ptr = NonNull::new(allocation as *mut ()).unwrap_or_else(|| crate::utils::abort()); |
| 141 | + |
| 142 | + let $raw = RawTask::<$f, <$f as Future>::Output, $s, $m>::from_ptr(ptr.as_ptr()); |
| 143 | + |
| 144 | + let crate::Builder { |
| 145 | + metadata, |
| 146 | + #[cfg(feature = "std")] |
| 147 | + propagate_panic, |
| 148 | + } = $builder; |
| 149 | + |
| 150 | + // Write the header as the first field of the task. |
| 151 | + ($raw.header as *mut Header<$m>).write(Header { |
| 152 | + #[cfg(not(feature = "portable-atomic"))] |
| 153 | + state: core::sync::atomic::AtomicUsize::new(SCHEDULED | TASK | REFERENCE), |
| 154 | + #[cfg(feature = "portable-atomic")] |
| 155 | + state: portable_atomic::AtomicUsize::new(SCHEDULED | TASK | REFERENCE), |
| 156 | + awaiter: core::cell::UnsafeCell::new(None), |
| 157 | + vtable: &RawTask::<$f, <$f as Future>::Output, $s, $m>::TASK_VTABLE, |
| 158 | + metadata, |
| 159 | + #[cfg(feature = "std")] |
| 160 | + propagate_panic, |
| 161 | + }); |
| 162 | + |
| 163 | + // Write the schedule function as the third field of the task. |
| 164 | + ($raw.schedule as *mut S).write($schedule); |
| 165 | + |
| 166 | + // Explicitly avoid using abort_on_panic here to avoid extra stack |
| 167 | + // copies of the future on lower optimization levels. |
| 168 | + let bomb = crate::utils::Bomb; |
| 169 | + |
| 170 | + // Generate the future, now that the metadata has been pinned in place. |
| 171 | + // Write the future as the fourth field of the task. |
| 172 | + $raw.future.write($future); |
| 173 | + // (&(*raw.header).metadata) |
| 174 | + |
| 175 | + mem::forget(bomb); |
| 176 | + ptr |
| 177 | + }}; |
| 178 | +} |
| 179 | + |
| 180 | +pub(crate) use allocate_task; |
| 181 | + |
134 | 182 | impl<F, T, S, M> RawTask<F, T, S, M> |
135 | 183 | where |
136 | 184 | F: Future<Output = T>, |
137 | 185 | S: Schedule<M>, |
138 | 186 | { |
139 | | - const RAW_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new( |
| 187 | + pub(crate) const RAW_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new( |
140 | 188 | Self::clone_waker, |
141 | 189 | Self::wake, |
142 | 190 | Self::wake_by_ref, |
143 | 191 | Self::drop_waker, |
144 | 192 | ); |
145 | 193 |
|
146 | | - /// Allocates a task with the given `future` and `schedule` function. |
147 | | - /// |
148 | | - /// It is assumed that initially only the `Runnable` and the `Task` exist. |
149 | | - pub(crate) fn allocate<'a, Gen: FnOnce(&'a M) -> F>( |
150 | | - future: Gen, |
151 | | - schedule: S, |
152 | | - builder: crate::Builder<M>, |
153 | | - ) -> NonNull<()> |
154 | | - where |
155 | | - F: 'a, |
156 | | - M: 'a, |
157 | | - { |
158 | | - // Compute the layout of the task for allocation. Abort if the computation fails. |
159 | | - // |
160 | | - // n.b. notgull: task_layout now automatically aborts instead of panicking |
161 | | - let task_layout = Self::task_layout(); |
162 | | - |
163 | | - unsafe { |
164 | | - // Allocate enough space for the entire task. |
165 | | - let ptr = match NonNull::new(alloc::alloc::alloc(task_layout.layout) as *mut ()) { |
166 | | - None => abort(), |
167 | | - Some(p) => p, |
168 | | - }; |
169 | | - |
170 | | - let raw = Self::from_ptr(ptr.as_ptr()); |
171 | | - |
172 | | - let crate::Builder { |
173 | | - metadata, |
174 | | - #[cfg(feature = "std")] |
175 | | - propagate_panic, |
176 | | - } = builder; |
177 | | - |
178 | | - // Write the header as the first field of the task. |
179 | | - (raw.header as *mut Header<M>).write(Header { |
180 | | - state: AtomicUsize::new(SCHEDULED | TASK | REFERENCE), |
181 | | - awaiter: UnsafeCell::new(None), |
182 | | - vtable: &TaskVTable { |
183 | | - schedule: Self::schedule, |
184 | | - drop_future: Self::drop_future, |
185 | | - get_output: Self::get_output, |
186 | | - drop_ref: Self::drop_ref, |
187 | | - destroy: Self::destroy, |
188 | | - run: Self::run, |
189 | | - clone_waker: Self::clone_waker, |
190 | | - layout_info: &Self::TASK_LAYOUT, |
191 | | - }, |
192 | | - metadata, |
193 | | - #[cfg(feature = "std")] |
194 | | - propagate_panic, |
195 | | - }); |
196 | | - |
197 | | - // Write the schedule function as the third field of the task. |
198 | | - (raw.schedule as *mut S).write(schedule); |
199 | | - |
200 | | - // Generate the future, now that the metadata has been pinned in place. |
201 | | - let future = abort_on_panic(|| future(&(*raw.header).metadata)); |
202 | | - |
203 | | - // Write the future as the fourth field of the task. |
204 | | - raw.future.write(future); |
205 | | - |
206 | | - ptr |
207 | | - } |
208 | | - } |
| 194 | + pub(crate) const TASK_VTABLE: TaskVTable = TaskVTable { |
| 195 | + schedule: Self::schedule, |
| 196 | + drop_future: Self::drop_future, |
| 197 | + get_output: Self::get_output, |
| 198 | + drop_ref: Self::drop_ref, |
| 199 | + destroy: Self::destroy, |
| 200 | + run: Self::run, |
| 201 | + clone_waker: Self::clone_waker, |
| 202 | + layout_info: &Self::TASK_LAYOUT, |
| 203 | + }; |
209 | 204 |
|
210 | 205 | /// Creates a `RawTask` from a raw task pointer. |
211 | 206 | #[inline] |
|
0 commit comments