1 | use crate::future::Future; |
2 | use crate::runtime::task::core::{Core, Trailer}; |
3 | use crate::runtime::task::{Cell, Harness, Header, Id, Schedule, State}; |
4 | |
5 | use std::ptr::NonNull; |
6 | use std::task::{Poll, Waker}; |
7 | |
8 | /// Raw task handle |
9 | #[derive (Clone)] |
10 | pub(crate) struct RawTask { |
11 | ptr: NonNull<Header>, |
12 | } |
13 | |
14 | pub(super) struct Vtable { |
15 | /// Polls the future. |
16 | pub(super) poll: unsafe fn(NonNull<Header>), |
17 | |
18 | /// Schedules the task for execution on the runtime. |
19 | pub(super) schedule: unsafe fn(NonNull<Header>), |
20 | |
21 | /// Deallocates the memory. |
22 | pub(super) dealloc: unsafe fn(NonNull<Header>), |
23 | |
24 | /// Reads the task output, if complete. |
25 | pub(super) try_read_output: unsafe fn(NonNull<Header>, *mut (), &Waker), |
26 | |
27 | /// The join handle has been dropped. |
28 | pub(super) drop_join_handle_slow: unsafe fn(NonNull<Header>), |
29 | |
30 | /// An abort handle has been dropped. |
31 | pub(super) drop_abort_handle: unsafe fn(NonNull<Header>), |
32 | |
33 | /// Scheduler is being shutdown. |
34 | pub(super) shutdown: unsafe fn(NonNull<Header>), |
35 | |
36 | /// The number of bytes that the `trailer` field is offset from the header. |
37 | pub(super) trailer_offset: usize, |
38 | |
39 | /// The number of bytes that the `scheduler` field is offset from the header. |
40 | pub(super) scheduler_offset: usize, |
41 | |
42 | /// The number of bytes that the `id` field is offset from the header. |
43 | pub(super) id_offset: usize, |
44 | } |
45 | |
46 | /// Get the vtable for the requested `T` and `S` generics. |
47 | pub(super) fn vtable<T: Future, S: Schedule>() -> &'static Vtable { |
48 | &Vtable { |
49 | poll: poll::<T, S>, |
50 | schedule: schedule::<S>, |
51 | dealloc: dealloc::<T, S>, |
52 | try_read_output: try_read_output::<T, S>, |
53 | drop_join_handle_slow: drop_join_handle_slow::<T, S>, |
54 | drop_abort_handle: drop_abort_handle::<T, S>, |
55 | shutdown: shutdown::<T, S>, |
56 | trailer_offset: OffsetHelper::<T, S>::TRAILER_OFFSET, |
57 | scheduler_offset: OffsetHelper::<T, S>::SCHEDULER_OFFSET, |
58 | id_offset: OffsetHelper::<T, S>::ID_OFFSET, |
59 | } |
60 | } |
61 | |
62 | /// Calling `get_trailer_offset` directly in vtable doesn't work because it |
63 | /// prevents the vtable from being promoted to a static reference. |
64 | /// |
65 | /// See this thread for more info: |
66 | /// <https://users.rust-lang.org/t/custom-vtables-with-integers/78508> |
67 | struct OffsetHelper<T, S>(T, S); |
68 | impl<T: Future, S: Schedule> OffsetHelper<T, S> { |
69 | // Pass `size_of`/`align_of` as arguments rather than calling them directly |
70 | // inside `get_trailer_offset` because trait bounds on generic parameters |
71 | // of const fn are unstable on our MSRV. |
72 | const TRAILER_OFFSET: usize = get_trailer_offset( |
73 | std::mem::size_of::<Header>(), |
74 | std::mem::size_of::<Core<T, S>>(), |
75 | std::mem::align_of::<Core<T, S>>(), |
76 | std::mem::align_of::<Trailer>(), |
77 | ); |
78 | |
79 | // The `scheduler` is the first field of `Core`, so it has the same |
80 | // offset as `Core`. |
81 | const SCHEDULER_OFFSET: usize = get_core_offset( |
82 | std::mem::size_of::<Header>(), |
83 | std::mem::align_of::<Core<T, S>>(), |
84 | ); |
85 | |
86 | const ID_OFFSET: usize = get_id_offset( |
87 | std::mem::size_of::<Header>(), |
88 | std::mem::align_of::<Core<T, S>>(), |
89 | std::mem::size_of::<S>(), |
90 | std::mem::align_of::<Id>(), |
91 | ); |
92 | } |
93 | |
94 | /// Compute the offset of the `Trailer` field in `Cell<T, S>` using the |
95 | /// `#[repr(C)]` algorithm. |
96 | /// |
97 | /// Pseudo-code for the `#[repr(C)]` algorithm can be found here: |
98 | /// <https://doc.rust-lang.org/reference/type-layout.html#reprc-structs> |
99 | const fn get_trailer_offset( |
100 | header_size: usize, |
101 | core_size: usize, |
102 | core_align: usize, |
103 | trailer_align: usize, |
104 | ) -> usize { |
105 | let mut offset: usize = header_size; |
106 | |
107 | let core_misalign: usize = offset % core_align; |
108 | if core_misalign > 0 { |
109 | offset += core_align - core_misalign; |
110 | } |
111 | offset += core_size; |
112 | |
113 | let trailer_misalign: usize = offset % trailer_align; |
114 | if trailer_misalign > 0 { |
115 | offset += trailer_align - trailer_misalign; |
116 | } |
117 | |
118 | offset |
119 | } |
120 | |
121 | /// Compute the offset of the `Core<T, S>` field in `Cell<T, S>` using the |
122 | /// `#[repr(C)]` algorithm. |
123 | /// |
124 | /// Pseudo-code for the `#[repr(C)]` algorithm can be found here: |
125 | /// <https://doc.rust-lang.org/reference/type-layout.html#reprc-structs> |
126 | const fn get_core_offset(header_size: usize, core_align: usize) -> usize { |
127 | let mut offset: usize = header_size; |
128 | |
129 | let core_misalign: usize = offset % core_align; |
130 | if core_misalign > 0 { |
131 | offset += core_align - core_misalign; |
132 | } |
133 | |
134 | offset |
135 | } |
136 | |
137 | /// Compute the offset of the `Id` field in `Cell<T, S>` using the |
138 | /// `#[repr(C)]` algorithm. |
139 | /// |
140 | /// Pseudo-code for the `#[repr(C)]` algorithm can be found here: |
141 | /// <https://doc.rust-lang.org/reference/type-layout.html#reprc-structs> |
142 | const fn get_id_offset( |
143 | header_size: usize, |
144 | core_align: usize, |
145 | scheduler_size: usize, |
146 | id_align: usize, |
147 | ) -> usize { |
148 | let mut offset: usize = get_core_offset(header_size, core_align); |
149 | offset += scheduler_size; |
150 | |
151 | let id_misalign: usize = offset % id_align; |
152 | if id_misalign > 0 { |
153 | offset += id_align - id_misalign; |
154 | } |
155 | |
156 | offset |
157 | } |
158 | |
159 | impl RawTask { |
160 | pub(super) fn new<T, S>(task: T, scheduler: S, id: Id) -> RawTask |
161 | where |
162 | T: Future, |
163 | S: Schedule, |
164 | { |
165 | let ptr = Box::into_raw(Cell::<_, S>::new(task, scheduler, State::new(), id)); |
166 | let ptr = unsafe { NonNull::new_unchecked(ptr.cast()) }; |
167 | |
168 | RawTask { ptr } |
169 | } |
170 | |
171 | pub(super) unsafe fn from_raw(ptr: NonNull<Header>) -> RawTask { |
172 | RawTask { ptr } |
173 | } |
174 | |
175 | pub(super) fn header_ptr(&self) -> NonNull<Header> { |
176 | self.ptr |
177 | } |
178 | |
179 | pub(super) fn trailer_ptr(&self) -> NonNull<Trailer> { |
180 | unsafe { Header::get_trailer(self.ptr) } |
181 | } |
182 | |
183 | /// Returns a reference to the task's header. |
184 | pub(super) fn header(&self) -> &Header { |
185 | unsafe { self.ptr.as_ref() } |
186 | } |
187 | |
188 | /// Returns a reference to the task's trailer. |
189 | pub(super) fn trailer(&self) -> &Trailer { |
190 | unsafe { &*self.trailer_ptr().as_ptr() } |
191 | } |
192 | |
193 | /// Returns a reference to the task's state. |
194 | pub(super) fn state(&self) -> &State { |
195 | &self.header().state |
196 | } |
197 | |
198 | /// Safety: mutual exclusion is required to call this function. |
199 | pub(crate) fn poll(self) { |
200 | let vtable = self.header().vtable; |
201 | unsafe { (vtable.poll)(self.ptr) } |
202 | } |
203 | |
204 | pub(super) fn schedule(self) { |
205 | let vtable = self.header().vtable; |
206 | unsafe { (vtable.schedule)(self.ptr) } |
207 | } |
208 | |
209 | pub(super) fn dealloc(self) { |
210 | let vtable = self.header().vtable; |
211 | unsafe { |
212 | (vtable.dealloc)(self.ptr); |
213 | } |
214 | } |
215 | |
216 | /// Safety: `dst` must be a `*mut Poll<super::Result<T::Output>>` where `T` |
217 | /// is the future stored by the task. |
218 | pub(super) unsafe fn try_read_output(self, dst: *mut (), waker: &Waker) { |
219 | let vtable = self.header().vtable; |
220 | (vtable.try_read_output)(self.ptr, dst, waker); |
221 | } |
222 | |
223 | pub(super) fn drop_join_handle_slow(self) { |
224 | let vtable = self.header().vtable; |
225 | unsafe { (vtable.drop_join_handle_slow)(self.ptr) } |
226 | } |
227 | |
228 | pub(super) fn drop_abort_handle(self) { |
229 | let vtable = self.header().vtable; |
230 | unsafe { (vtable.drop_abort_handle)(self.ptr) } |
231 | } |
232 | |
233 | pub(super) fn shutdown(self) { |
234 | let vtable = self.header().vtable; |
235 | unsafe { (vtable.shutdown)(self.ptr) } |
236 | } |
237 | |
238 | /// Increment the task's reference count. |
239 | /// |
240 | /// Currently, this is used only when creating an `AbortHandle`. |
241 | pub(super) fn ref_inc(self) { |
242 | self.header().state.ref_inc(); |
243 | } |
244 | |
245 | /// Get the queue-next pointer |
246 | /// |
247 | /// This is for usage by the injection queue |
248 | /// |
249 | /// Safety: make sure only one queue uses this and access is synchronized. |
250 | pub(crate) unsafe fn get_queue_next(self) -> Option<RawTask> { |
251 | self.header() |
252 | .queue_next |
253 | .with(|ptr| *ptr) |
254 | .map(|p| RawTask::from_raw(p)) |
255 | } |
256 | |
257 | /// Sets the queue-next pointer |
258 | /// |
259 | /// This is for usage by the injection queue |
260 | /// |
261 | /// Safety: make sure only one queue uses this and access is synchronized. |
262 | pub(crate) unsafe fn set_queue_next(self, val: Option<RawTask>) { |
263 | self.header().set_next(val.map(|task| task.ptr)); |
264 | } |
265 | } |
266 | |
267 | impl Copy for RawTask {} |
268 | |
269 | unsafe fn poll<T: Future, S: Schedule>(ptr: NonNull<Header>) { |
270 | let harness: Harness = Harness::<T, S>::from_raw(ptr); |
271 | harness.poll(); |
272 | } |
273 | |
274 | unsafe fn schedule<S: Schedule>(ptr: NonNull<Header>) { |
275 | use crate::runtime::task::{Notified, Task}; |
276 | |
277 | let scheduler: NonNull = Header::get_scheduler::<S>(me:ptr); |
278 | scheduler |
279 | .as_ref() |
280 | .schedule(task:Notified(Task::from_raw(ptr:ptr.cast()))); |
281 | } |
282 | |
283 | unsafe fn dealloc<T: Future, S: Schedule>(ptr: NonNull<Header>) { |
284 | let harness: Harness = Harness::<T, S>::from_raw(ptr); |
285 | harness.dealloc(); |
286 | } |
287 | |
288 | unsafe fn try_read_output<T: Future, S: Schedule>( |
289 | ptr: NonNull<Header>, |
290 | dst: *mut (), |
291 | waker: &Waker, |
292 | ) { |
293 | let out: &mut Poll::Output, …>> = &mut *(dst as *mut Poll<super::Result<T::Output>>); |
294 | |
295 | let harness: Harness = Harness::<T, S>::from_raw(ptr); |
296 | harness.try_read_output(dst:out, waker); |
297 | } |
298 | |
299 | unsafe fn drop_join_handle_slow<T: Future, S: Schedule>(ptr: NonNull<Header>) { |
300 | let harness: Harness = Harness::<T, S>::from_raw(ptr); |
301 | harness.drop_join_handle_slow(); |
302 | } |
303 | |
304 | unsafe fn drop_abort_handle<T: Future, S: Schedule>(ptr: NonNull<Header>) { |
305 | let harness: Harness = Harness::<T, S>::from_raw(ptr); |
306 | harness.drop_reference(); |
307 | } |
308 | |
309 | unsafe fn shutdown<T: Future, S: Schedule>(ptr: NonNull<Header>) { |
310 | let harness: Harness = Harness::<T, S>::from_raw(ptr); |
311 | harness.shutdown(); |
312 | } |
313 | |