1use alloc::{
4 alloc::{Layout, dealloc},
5 sync::Arc,
6};
7use core::{
8 cell::{Cell, UnsafeCell},
9 future::Future,
10 marker::PhantomPinned,
11 pin::Pin,
12 ptr,
13 sync::atomic::{AtomicPtr, AtomicUsize, Ordering},
14 task::{Context, Poll},
15};
16
17use super::SharedExecutor;
18use crate::{
19 runtime::{delivery::inbox::InboxNode, task_runtime},
20 thread::{ThreadId, WakeIntent},
21};
22
23pub(super) const RUN_QUEUED: usize = 1 << 0;
24pub(super) const POLLING: usize = 1 << 1;
25pub(super) const COMPLETE: usize = 1 << 2;
26const FUTURE_EMPTY: usize = 1 << 3;
27
28const REFCOUNT_OVERFLOW_INVARIANT: u32 = 0x4558_0001;
29const EARLY_RECLAIM_INVARIANT: u32 = 0x4558_0002;
30
31type PollFuture = unsafe fn(*mut CoroutineHeader, &mut Context<'_>) -> Poll<()>;
32type DropFuture = unsafe fn(*mut CoroutineHeader);
33type Deallocate = unsafe fn(*mut CoroutineHeader);
34
35#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
37pub struct CoroutineId {
38 owner_thread: ThreadId,
39 generation: u64,
40}
41
42impl CoroutineId {
43 pub(super) const fn new(owner_thread: ThreadId, generation: u64) -> Self {
44 Self {
45 owner_thread,
46 generation,
47 }
48 }
49
50 pub const fn owner_thread(self) -> ThreadId {
52 self.owner_thread
53 }
54
55 pub const fn generation(self) -> u64 {
57 self.generation
58 }
59}
60
61#[repr(C)]
67pub struct CoroutineHeader {
68 reclaim: InboxNode,
69 id: CoroutineId,
70 pub(super) state: AtomicUsize,
71 references: AtomicUsize,
72 executor: Arc<SharedExecutor>,
73 ready_next: AtomicPtr<Self>,
74 owner_next: Cell<*mut Self>,
75 poll_future: PollFuture,
76 drop_future: DropFuture,
77 deallocate: Deallocate,
78 _pin: PhantomPinned,
79}
80
81impl CoroutineHeader {
82 pub const fn id(&self) -> CoroutineId {
84 self.id
85 }
86
87 pub const fn owner_thread(&self) -> ThreadId {
89 self.id.owner_thread()
90 }
91
92 pub(super) unsafe fn poll_raw(header: *mut Self, context: &mut Context<'_>) -> Poll<()> {
99 let poll_future = unsafe {
100 core::ptr::addr_of!((*header).poll_future).read()
103 };
104 unsafe {
105 poll_future(header, context)
108 }
109 }
110
111 pub(super) unsafe fn drop_future_raw(header: *mut Self) {
118 let drop_future = unsafe {
119 core::ptr::addr_of!((*header).drop_future).read()
121 };
122 unsafe {
123 drop_future(header);
125 }
126 }
127
128 pub(crate) unsafe fn deallocate_raw(header: *mut Self) {
135 let state = unsafe { (*header).state.load(Ordering::Acquire) };
136 if state & (COMPLETE | FUTURE_EMPTY) != (COMPLETE | FUTURE_EMPTY) {
137 task_runtime::fatal_invariant(EARLY_RECLAIM_INVARIANT, unsafe {
138 (*header).id.generation() as usize
139 });
140 }
141 let deallocate = unsafe {
142 (*header).deallocate
144 };
145 unsafe {
146 deallocate(header);
149 }
150 }
151
152 pub(crate) fn reclaim_node(self: Pin<&'static Self>) -> Pin<&'static InboxNode> {
153 unsafe {
154 self.map_unchecked(|header| &header.reclaim)
157 }
158 }
159
160 pub(crate) fn address(self: Pin<&'static Self>) -> usize {
161 (self.get_ref() as *const Self).addr()
162 }
163
164 pub(super) fn next(&self, kind: super::inbox::InboxKind) -> &AtomicPtr<Self> {
165 match kind {
166 super::inbox::InboxKind::Ready => &self.ready_next,
167 }
168 }
169
170 pub(super) fn owner_next(&self) -> *mut Self {
171 self.owner_next.get()
172 }
173
174 pub(super) fn set_owner_next(&self, next: *mut Self) {
175 self.owner_next.set(next);
176 }
177}
178
179unsafe impl Send for CoroutineHeader {}
182unsafe impl Sync for CoroutineHeader {}
185
186#[repr(C)]
187pub(super) struct Coroutine<F> {
188 header: CoroutineHeader,
189 future: UnsafeCell<Option<F>>,
190}
191
192impl<F> Coroutine<F>
193where
194 F: Future<Output = ()>,
195{
196 pub(super) fn new(id: CoroutineId, executor: Arc<SharedExecutor>, future: F) -> Self {
197 Self {
198 header: CoroutineHeader {
199 reclaim: InboxNode::new(crate::runtime::delivery::inbox::InboxKind::Reclaim),
200 id,
201 state: AtomicUsize::new(0),
202 references: AtomicUsize::new(1),
203 executor,
204 ready_next: AtomicPtr::new(ptr::null_mut()),
205 owner_next: Cell::new(ptr::null_mut()),
206 poll_future: poll_future::<F>,
207 drop_future: drop_future::<F>,
208 deallocate: deallocate::<F>,
209 _pin: PhantomPinned,
210 },
211 future: UnsafeCell::new(Some(future)),
212 }
213 }
214}
215
216pub(super) unsafe fn schedule(header: *mut CoroutineHeader) {
223 unsafe {
224 schedule_with_intent(header, WakeIntent::Normal);
226 }
227}
228
229pub(super) unsafe fn schedule_sync(header: *mut CoroutineHeader) {
237 unsafe {
238 schedule_with_intent(header, WakeIntent::Sync);
239 }
240}
241
242unsafe fn schedule_with_intent(header: *mut CoroutineHeader, intent: WakeIntent) {
243 let header_ref = unsafe {
244 &*header
246 };
247 let mut observed = header_ref.state.load(Ordering::Acquire);
248
249 loop {
250 if observed & (COMPLETE | RUN_QUEUED) != 0 {
251 return;
252 }
253 match header_ref.state.compare_exchange_weak(
254 observed,
255 observed | RUN_QUEUED,
256 Ordering::AcqRel,
257 Ordering::Acquire,
258 ) {
259 Ok(_) => break,
260 Err(updated) => observed = updated,
261 }
262 }
263
264 retain_reference(header_ref);
265 if !header_ref.executor.publish_ready(header, intent) {
266 header_ref.state.fetch_and(!RUN_QUEUED, Ordering::AcqRel);
267 unsafe {
268 release_reference(header);
271 }
272 }
273}
274
275pub(super) fn retain_reference(header: &CoroutineHeader) {
276 let mut references = header.references.load(Ordering::Relaxed);
277 loop {
278 let Some(next) = references.checked_add(1) else {
279 task_runtime::fatal_invariant(
280 REFCOUNT_OVERFLOW_INVARIANT,
281 header.id.generation() as usize,
282 );
283 };
284 if references == 0 {
285 task_runtime::fatal_invariant(
286 REFCOUNT_OVERFLOW_INVARIANT,
287 header.id.generation() as usize,
288 );
289 }
290 match header.references.compare_exchange_weak(
291 references,
292 next,
293 Ordering::Relaxed,
294 Ordering::Relaxed,
295 ) {
296 Ok(_) => return,
297 Err(updated) => references = updated,
298 }
299 }
300}
301
302pub(super) unsafe fn release_reference(header: *mut CoroutineHeader) {
309 let header_ref = unsafe {
310 &*header
313 };
314 let previous = header_ref.references.fetch_sub(1, Ordering::Release);
315 if previous == 0 {
316 task_runtime::fatal_invariant(
317 REFCOUNT_OVERFLOW_INVARIANT,
318 header_ref.id.generation() as usize,
319 );
320 }
321 if previous != 1 {
322 return;
323 }
324 core::sync::atomic::fence(Ordering::Acquire);
325
326 if !task_runtime::in_hard_irq() {
327 unsafe {
328 CoroutineHeader::deallocate_raw(header);
333 }
334 return;
335 }
336
337 let header = unsafe {
338 Pin::new_unchecked(header_ref)
342 };
343 crate::runtime::service::reclaim::publish_deferred_coroutine_reclaim(header);
344}
345
346unsafe fn poll_future<F>(header: *mut CoroutineHeader, context: &mut Context<'_>) -> Poll<()>
353where
354 F: Future<Output = ()>,
355{
356 let coroutine = header.cast::<Coroutine<F>>();
357 let future = unsafe {
358 &mut *(*coroutine).future.get()
361 };
362 match future.as_mut() {
363 Some(future) => unsafe {
364 Pin::new_unchecked(future).poll(context)
366 },
367 None => Poll::Ready(()),
368 }
369}
370
371unsafe fn drop_future<F>(header: *mut CoroutineHeader)
378where
379 F: Future<Output = ()>,
380{
381 let coroutine = header.cast::<Coroutine<F>>();
382 let future = unsafe {
383 &mut *(*coroutine).future.get()
385 };
386 let future = future.take();
387 unsafe {
388 (*header).state.fetch_or(FUTURE_EMPTY, Ordering::Release);
392 }
393 drop(future);
394}
395
396unsafe fn deallocate<F>(header: *mut CoroutineHeader)
403where
404 F: Future<Output = ()>,
405{
406 unsafe {
407 core::ptr::drop_in_place(core::ptr::addr_of_mut!((*header).executor));
412 dealloc(header.cast::<u8>(), Layout::new::<Coroutine<F>>());
413 }
414}