1mod coroutine;
10mod inbox;
11mod waker;
12
13use alloc::{boxed::Box, rc::Rc, sync::Arc};
14use core::{
15 cell::{Cell, RefCell},
16 fmt,
17 future::Future,
18 marker::PhantomData,
19 ptr,
20 sync::atomic::{AtomicUsize, Ordering},
21 task::{Context, Poll, Waker},
22};
23
24pub use coroutine::{CoroutineHeader, CoroutineId};
25
26use self::{
27 coroutine::{COMPLETE, Coroutine, POLLING, RUN_QUEUED, release_reference, retain_reference},
28 inbox::{InboxKind, IntrusiveInbox},
29 waker::coroutine_waker,
30};
31use crate::{
32 runtime::{cpu::IrqGuardToken, task_runtime},
33 thread::{TaskError, ThreadId, ThreadWakeHandle},
34};
35
36pub const DEFAULT_POLL_BATCH: usize = 64;
38
39pub fn wake_waker_sync(waker: Waker) {
46 debug_assert!(!task_runtime::in_hard_irq());
47 waker::wake_sync(waker);
48}
49
50const NOTIFIED: usize = 1 << 0;
51const PARKING: usize = 1 << 1;
52const PARKED: usize = 1 << 2;
53
54pub struct LocalExecutor {
61 shared: Arc<SharedExecutor>,
62 ready_pending: Cell<*mut CoroutineHeader>,
63 active: Cell<*mut CoroutineHeader>,
64 next_generation: Cell<u64>,
65 _owner_thread_only: PhantomData<Rc<()>>,
66}
67
68impl LocalExecutor {
69 pub fn new(owner_wake: ThreadWakeHandle) -> Result<Self, TaskError> {
81 if crate::runtime::task_runtime::in_hard_irq() {
82 return Err(TaskError::UnsafeContext);
83 }
84 let expected = owner_wake.thread_id();
85 let actual = crate::thread::current::current_thread_id()?;
86 if actual != expected {
87 return Err(TaskError::ExecutorOwnerMismatch {
88 expected: expected.as_u64(),
89 actual: actual.as_u64(),
90 });
91 }
92 Ok(Self {
93 shared: Arc::new(SharedExecutor::new(owner_wake)),
94 ready_pending: Cell::new(ptr::null_mut()),
95 active: Cell::new(ptr::null_mut()),
96 next_generation: Cell::new(1),
97 _owner_thread_only: PhantomData,
98 })
99 }
100
101 pub fn owner_thread(&self) -> ThreadId {
103 self.shared.owner_thread
104 }
105
106 pub fn spawn<F>(&self, future: F) -> CoroutineId
112 where
113 F: Future<Output = ()> + 'static,
114 {
115 self.assert_owner_context();
116 unsafe {
117 self.spawn_scoped(future).1
119 }
120 }
121
122 pub fn run<F, P>(&self, future: F, mut park: P) -> F::Output
131 where
132 F: Future,
133 P: FnMut(&ExecutorParkCondition<'_>),
134 {
135 self.assert_owner_context();
136 let output = RefCell::new(None);
137 let root = async {
138 output.replace(Some(future.await));
139 };
140 let (header, _) = unsafe {
141 self.spawn_scoped(root)
144 };
145 retain_reference(unsafe {
146 &*header
148 });
149 let guard = ScopedRunGuard {
150 executor: self,
151 header,
152 };
153
154 while output.borrow().is_none() {
155 let batch = self.run_ready_batch();
156 if output.borrow().is_some() || batch.has_more() {
157 continue;
158 }
159 let Some(token) = self.prepare_park() else {
160 continue;
161 };
162 let condition = ExecutorParkCondition { executor: self };
163 park(&condition);
164 let _owner_work = token.finish();
165 unsafe {
166 coroutine::schedule(header);
169 }
170 }
171
172 let result = output
173 .borrow_mut()
174 .take()
175 .unwrap_or_else(|| unreachable!("completed root future must publish output"));
176 drop(guard);
177 result
178 }
179
180 pub fn run_ready_batch(&self) -> PollBatch {
185 self.assert_owner_context();
186 self.shared
187 .park_state
188 .fetch_and(!NOTIFIED, Ordering::AcqRel);
189 let mut cursor = self.take_ready_snapshot();
190 let mut polled = 0;
191 let mut completed = 0;
192
193 while !cursor.is_null() && polled < DEFAULT_POLL_BATCH {
194 let header = cursor;
195 cursor = unsafe {
196 IntrusiveInbox::take_next(header, InboxKind::Ready)
199 };
200 let did_complete = unsafe {
201 self.poll_ready_coroutine(header)
203 };
204 polled += usize::from(did_complete.was_polled());
205 completed += usize::from(did_complete.was_completed());
206 }
207
208 self.ready_pending.set(cursor);
209 PollBatch {
210 polled,
211 completed,
212 has_more: self.has_ready(),
213 }
214 }
215
216 pub fn has_ready(&self) -> bool {
218 !self.ready_pending.get().is_null() || !self.shared.ready.is_empty()
219 }
220
221 pub fn prepare_park(&self) -> Option<ParkToken<'_>> {
227 self.assert_owner_context();
228 if self.has_owner_work() {
229 return None;
230 }
231
232 let previous = self.shared.park_state.fetch_or(PARKING, Ordering::AcqRel);
233 if previous & (NOTIFIED | PARKING | PARKED) != 0 || self.has_owner_work() {
234 self.cancel_park_attempt();
235 return None;
236 }
237
238 if self
239 .shared
240 .park_state
241 .compare_exchange(PARKING, PARKED, Ordering::AcqRel, Ordering::Acquire)
242 .is_err()
243 || self.has_owner_work()
244 {
245 self.cancel_park_attempt();
246 return None;
247 }
248
249 Some(ParkToken {
250 executor: self,
251 active: true,
252 _owner_thread_only: PhantomData,
253 })
254 }
255
256 fn has_owner_work(&self) -> bool {
257 self.has_ready()
258 }
259
260 fn cancel_park_attempt(&self) {
261 self.shared
262 .park_state
263 .fetch_and(!(PARKING | PARKED | NOTIFIED), Ordering::AcqRel);
264 }
265
266 fn finish_park(&self) -> bool {
267 let state = self.shared.park_state.swap(0, Ordering::AcqRel);
268 state & NOTIFIED != 0 || self.has_owner_work()
269 }
270
271 fn assert_owner_context(&self) {
272 if crate::runtime::task_runtime::in_hard_irq() {
273 crate::runtime::task_runtime::fatal_invariant(
274 0x4558_0005,
275 self.owner_thread().as_u64() as usize,
276 );
277 }
278 match crate::thread::current::current_thread_id() {
279 Ok(actual) if actual == self.owner_thread() => {}
280 Ok(actual) => {
281 crate::runtime::task_runtime::fatal_invariant(0x4558_0003, actual.as_u64() as usize)
282 }
283 Err(_) => crate::runtime::task_runtime::fatal_invariant(
284 0x4558_0006,
285 self.owner_thread().as_u64() as usize,
286 ),
287 }
288 }
289}
290
291pub struct ExecutorParkCondition<'executor> {
297 executor: &'executor LocalExecutor,
298}
299
300impl ExecutorParkCondition<'_> {
301 pub fn should_abort(&self) -> bool {
306 self.executor.shared.park_state.load(Ordering::Acquire) & NOTIFIED != 0
307 || self.executor.has_owner_work()
308 }
309}
310
311impl fmt::Debug for ExecutorParkCondition<'_> {
312 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
313 formatter
314 .debug_struct("ExecutorParkCondition")
315 .field("owner_thread", &self.executor.owner_thread())
316 .field("should_abort", &self.should_abort())
317 .finish()
318 }
319}
320
321impl Drop for LocalExecutor {
322 fn drop(&mut self) {
323 self.assert_owner_context();
324 self.shutdown();
325 }
326}
327
328#[derive(Clone, Copy, Debug, Eq, PartialEq)]
330pub struct PollBatch {
331 polled: usize,
332 completed: usize,
333 has_more: bool,
334}
335
336impl PollBatch {
337 pub const fn polled(self) -> usize {
339 self.polled
340 }
341
342 pub const fn completed(self) -> usize {
344 self.completed
345 }
346
347 pub const fn has_more(self) -> bool {
349 self.has_more
350 }
351}
352
353#[must_use = "the token must be held across the task-system park operation"]
358pub struct ParkToken<'executor> {
359 executor: &'executor LocalExecutor,
360 active: bool,
361 _owner_thread_only: PhantomData<Rc<()>>,
362}
363
364impl ParkToken<'_> {
365 pub fn finish(mut self) -> bool {
367 self.active = false;
368 self.executor.finish_park()
369 }
370}
371
372impl Drop for ParkToken<'_> {
373 fn drop(&mut self) {
374 if self.active {
375 self.executor.cancel_park_attempt();
376 }
377 }
378}
379
380pub(super) struct SharedExecutor {
381 owner_thread: ThreadId,
382 owner_wake: ThreadWakeHandle,
383 ready: IntrusiveInbox,
384 park_state: AtomicUsize,
385 ready_publication: AtomicUsize,
386}
387
388const READY_PUBLICATION_CLOSED: usize = 1usize << (usize::BITS - 1);
389const READY_PUBLISHER_COUNT_MASK: usize = READY_PUBLICATION_CLOSED - 1;
390
391impl SharedExecutor {
392 fn new(owner_wake: ThreadWakeHandle) -> Self {
393 Self {
394 owner_thread: owner_wake.thread_id(),
395 owner_wake,
396 ready: IntrusiveInbox::new(InboxKind::Ready),
397 park_state: AtomicUsize::new(0),
398 ready_publication: AtomicUsize::new(0),
399 }
400 }
401
402 pub(super) fn publish_ready(
403 &self,
404 header: *mut CoroutineHeader,
405 intent: crate::thread::WakeIntent,
406 ) -> bool {
407 let Some(_publisher) = self.begin_ready_publish_guard() else {
408 return false;
409 };
410 unsafe {
411 self.ready.push(header);
414 }
415 self.notify_owner(intent);
416 true
417 }
418
419 fn begin_ready_publish_guard(&self) -> Option<ReadyPublishGuard<'_>> {
420 let irq_token = crate::runtime::enter_irq_guard(crate::runtime::IrqGuardSource::Executor);
421 if self.begin_ready_publish() {
422 Some(ReadyPublishGuard {
423 executor: self,
424 irq_token,
425 _not_send: PhantomData,
426 })
427 } else {
428 unsafe { task_runtime::irq_guard_exit(irq_token) };
431 None
432 }
433 }
434
435 fn begin_ready_publish(&self) -> bool {
436 let mut state = self.ready_publication.load(Ordering::Acquire);
437 loop {
438 if state & READY_PUBLICATION_CLOSED != 0 {
439 return false;
440 }
441 if state & READY_PUBLISHER_COUNT_MASK == READY_PUBLISHER_COUNT_MASK {
442 crate::runtime::task_runtime::fatal_invariant(
443 0x4558_0007,
444 self.owner_thread.as_u64() as usize,
445 );
446 }
447 match self.ready_publication.compare_exchange_weak(
448 state,
449 state + 1,
450 Ordering::AcqRel,
451 Ordering::Acquire,
452 ) {
453 Ok(_) => return true,
454 Err(updated) => state = updated,
455 }
456 }
457 }
458
459 fn finish_ready_publish(&self) {
460 let previous = self.ready_publication.fetch_sub(1, Ordering::Release);
461 debug_assert_ne!(previous & READY_PUBLISHER_COUNT_MASK, 0);
462 }
463
464 fn notify_owner(&self, intent: crate::thread::WakeIntent) {
465 let previous = self.park_state.fetch_or(NOTIFIED, Ordering::AcqRel);
466 if previous & PARKED != 0 {
467 let _result = if intent.is_sync() {
468 self.owner_wake.wake_sync()
469 } else {
470 self.owner_wake.wake()
471 };
472 }
473 }
474
475 fn close_and_wait_for_publishers(&self) {
476 self.ready_publication
477 .fetch_or(READY_PUBLICATION_CLOSED, Ordering::AcqRel);
478 while self.ready_publication.load(Ordering::Acquire) != READY_PUBLICATION_CLOSED {
479 core::hint::spin_loop();
480 }
481 }
482}
483
484struct ReadyPublishGuard<'executor> {
485 executor: &'executor SharedExecutor,
486 irq_token: IrqGuardToken,
487 _not_send: PhantomData<*mut ()>,
488}
489
490impl Drop for ReadyPublishGuard<'_> {
491 fn drop(&mut self) {
492 self.executor.finish_ready_publish();
493 unsafe { task_runtime::irq_guard_exit(self.irq_token) };
496 }
497}
498
499struct ScopedRunGuard<'executor> {
500 executor: &'executor LocalExecutor,
501 header: *mut CoroutineHeader,
502}
503
504struct ReadyQueueReference {
505 header: *mut CoroutineHeader,
506 polling: bool,
507}
508
509struct OwnedCoroutineReference {
510 header: *mut CoroutineHeader,
511}
512
513impl OwnedCoroutineReference {
514 unsafe fn new(header: *mut CoroutineHeader) -> Self {
521 Self { header }
522 }
523}
524
525impl Drop for OwnedCoroutineReference {
526 fn drop(&mut self) {
527 unsafe {
528 release_reference(self.header);
530 }
531 }
532}
533
534impl ReadyQueueReference {
535 const fn new(header: *mut CoroutineHeader) -> Self {
536 Self {
537 header,
538 polling: false,
539 }
540 }
541
542 fn mark_polling(&mut self) {
543 self.polling = true;
544 }
545
546 fn finish_polling(&mut self) {
547 unsafe {
548 (*self.header).state.fetch_and(!POLLING, Ordering::AcqRel);
551 }
552 self.polling = false;
553 }
554}
555
556impl Drop for ReadyQueueReference {
557 fn drop(&mut self) {
558 if self.polling {
559 unsafe {
560 (*self.header).state.fetch_and(!POLLING, Ordering::AcqRel);
563 }
564 }
565 unsafe {
566 release_reference(self.header);
569 }
570 }
571}
572
573impl Drop for ScopedRunGuard<'_> {
574 fn drop(&mut self) {
575 let _scoped_reference = unsafe {
576 OwnedCoroutineReference::new(self.header)
579 };
580 self.executor.cancel_coroutine(self.header);
581 }
582}
583
584#[derive(Clone, Copy)]
585enum PollDisposition {
586 Skipped,
587 Pending,
588 Completed,
589}
590
591impl PollDisposition {
592 const fn was_polled(self) -> bool {
593 !matches!(self, Self::Skipped)
594 }
595
596 const fn was_completed(self) -> bool {
597 matches!(self, Self::Completed)
598 }
599}
600
601mod polling;
602
603mod membership;
604
605mod shutdown;
606
607mod block_on;
608pub use block_on::{BlockOnError, block_on, block_on_timeout};