embassy_executor/raw/
mod.rs

1//! Raw executor.
2//!
3//! This module exposes "raw" Executor and Task structs for more low level control.
4//!
5//! ## WARNING: here be dragons!
6//!
7//! Using this module requires respecting subtle safety contracts. If you can, prefer using the safe
8//! [executor wrappers](crate::Executor) and the [`embassy_executor::task`](embassy_executor_macros::task) macro, which are fully safe.
9
10#[cfg_attr(target_has_atomic = "ptr", path = "run_queue_atomics.rs")]
11#[cfg_attr(not(target_has_atomic = "ptr"), path = "run_queue_critical_section.rs")]
12mod run_queue;
13
14#[cfg_attr(all(cortex_m, target_has_atomic = "32"), path = "state_atomics_arm.rs")]
15#[cfg_attr(all(not(cortex_m), target_has_atomic = "8"), path = "state_atomics.rs")]
16#[cfg_attr(not(target_has_atomic = "8"), path = "state_critical_section.rs")]
17mod state;
18
19#[cfg(feature = "trace")]
20pub mod trace;
21pub(crate) mod util;
22#[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")]
23mod waker;
24
25use core::future::Future;
26use core::marker::PhantomData;
27use core::mem;
28use core::pin::Pin;
29use core::ptr::NonNull;
30#[cfg(not(feature = "arch-avr"))]
31use core::sync::atomic::AtomicPtr;
32use core::sync::atomic::Ordering;
33use core::task::{Context, Poll, Waker};
34
35use embassy_executor_timer_queue::TimerQueueItem;
36#[cfg(feature = "arch-avr")]
37use portable_atomic::AtomicPtr;
38
39use self::run_queue::{RunQueue, RunQueueItem};
40use self::state::State;
41use self::util::{SyncUnsafeCell, UninitCell};
42pub use self::waker::task_from_waker;
43use super::SpawnToken;
44
45#[no_mangle]
46extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static mut TimerQueueItem {
47    unsafe { task_from_waker(waker).timer_queue_item() }
48}
49
50/// Raw task header for use in task pointers.
51///
52/// A task can be in one of the following states:
53///
54/// - Not spawned: the task is ready to spawn.
55/// - `SPAWNED`: the task is currently spawned and may be running.
56/// - `RUN_ENQUEUED`: the task is enqueued to be polled. Note that the task may be `!SPAWNED`.
57///    In this case, the `RUN_ENQUEUED` state will be cleared when the task is next polled, without
58///    polling the task's future.
59///
60/// A task's complete life cycle is as follows:
61///
62/// ```text
63/// ┌────────────┐   ┌────────────────────────┐
64/// │Not spawned │◄─5┤Not spawned|Run enqueued│
65/// │            ├6─►│                        │
66/// └─────┬──────┘   └──────▲─────────────────┘
67///       1                 │
68///       │    ┌────────────┘
69///       │    4
70/// ┌─────▼────┴─────────┐
71/// │Spawned|Run enqueued│
72/// │                    │
73/// └─────┬▲─────────────┘
74///       2│
75///       │3
76/// ┌─────▼┴─────┐
77/// │  Spawned   │
78/// │            │
79/// └────────────┘
80/// ```
81///
82/// Transitions:
83/// - 1: Task is spawned - `AvailableTask::claim -> Executor::spawn`
84/// - 2: During poll - `RunQueue::dequeue_all -> State::run_dequeue`
85/// - 3: Task wakes itself, waker wakes task, or task exits - `Waker::wake -> wake_task -> State::run_enqueue`
86/// - 4: A run-queued task exits - `TaskStorage::poll -> Poll::Ready`
87/// - 5: Task is dequeued. The task's future is not polled, because exiting the task replaces its `poll_fn`.
88/// - 6: A task is waken when it is not spawned - `wake_task -> State::run_enqueue`
89pub(crate) struct TaskHeader {
90    pub(crate) state: State,
91    pub(crate) run_queue_item: RunQueueItem,
92    pub(crate) executor: AtomicPtr<SyncExecutor>,
93    poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>,
94
95    /// Integrated timer queue storage. This field should not be accessed outside of the timer queue.
96    pub(crate) timer_queue_item: TimerQueueItem,
97    #[cfg(feature = "trace")]
98    pub(crate) name: Option<&'static str>,
99    #[cfg(feature = "trace")]
100    pub(crate) id: u32,
101    #[cfg(feature = "trace")]
102    all_tasks_next: AtomicPtr<TaskHeader>,
103}
104
105/// This is essentially a `&'static TaskStorage<F>` where the type of the future has been erased.
106#[derive(Debug, Clone, Copy, PartialEq)]
107pub struct TaskRef {
108    ptr: NonNull<TaskHeader>,
109}
110
111unsafe impl Send for TaskRef where &'static TaskHeader: Send {}
112unsafe impl Sync for TaskRef where &'static TaskHeader: Sync {}
113
114impl TaskRef {
115    fn new<F: Future + 'static>(task: &'static TaskStorage<F>) -> Self {
116        Self {
117            ptr: NonNull::from(task).cast(),
118        }
119    }
120
121    /// Safety: The pointer must have been obtained with `Task::as_ptr`
122    pub(crate) unsafe fn from_ptr(ptr: *const TaskHeader) -> Self {
123        Self {
124            ptr: NonNull::new_unchecked(ptr as *mut TaskHeader),
125        }
126    }
127
128    pub(crate) fn header(self) -> &'static TaskHeader {
129        unsafe { self.ptr.as_ref() }
130    }
131
132    /// Returns a reference to the executor that the task is currently running on.
133    pub unsafe fn executor(self) -> Option<&'static Executor> {
134        let executor = self.header().executor.load(Ordering::Relaxed);
135        executor.as_ref().map(|e| Executor::wrap(e))
136    }
137
138    /// Returns a mutable reference to the timer queue item.
139    ///
140    /// Safety
141    ///
142    /// This function must only be called in the context of the integrated timer queue.
143    pub unsafe fn timer_queue_item(mut self) -> &'static mut TimerQueueItem {
144        unsafe { &mut self.ptr.as_mut().timer_queue_item }
145    }
146
147    /// The returned pointer is valid for the entire TaskStorage.
148    pub(crate) fn as_ptr(self) -> *const TaskHeader {
149        self.ptr.as_ptr()
150    }
151}
152
153/// Raw storage in which a task can be spawned.
154///
155/// This struct holds the necessary memory to spawn one task whose future is `F`.
156/// At a given time, the `TaskStorage` may be in spawned or not-spawned state. You
157/// may spawn it with [`TaskStorage::spawn()`], which will fail if it is already spawned.
158///
159/// A `TaskStorage` must live forever, it may not be deallocated even after the task has finished
160/// running. Hence the relevant methods require `&'static self`. It may be reused, however.
161///
162/// Internally, the [embassy_executor::task](embassy_executor_macros::task) macro allocates an array of `TaskStorage`s
163/// in a `static`. The most common reason to use the raw `Task` is to have control of where
164/// the memory for the task is allocated: on the stack, or on the heap with e.g. `Box::leak`, etc.
165
166// repr(C) is needed to guarantee that the Task is located at offset 0
167// This makes it safe to cast between TaskHeader and TaskStorage pointers.
168#[repr(C)]
169pub struct TaskStorage<F: Future + 'static> {
170    raw: TaskHeader,
171    future: UninitCell<F>, // Valid if STATE_SPAWNED
172}
173
174unsafe fn poll_exited(_p: TaskRef) {
175    // Nothing to do, the task is already !SPAWNED and dequeued.
176}
177
178impl<F: Future + 'static> TaskStorage<F> {
179    const NEW: Self = Self::new();
180
181    /// Create a new TaskStorage, in not-spawned state.
182    pub const fn new() -> Self {
183        Self {
184            raw: TaskHeader {
185                state: State::new(),
186                run_queue_item: RunQueueItem::new(),
187                executor: AtomicPtr::new(core::ptr::null_mut()),
188                // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss`
189                poll_fn: SyncUnsafeCell::new(None),
190
191                timer_queue_item: TimerQueueItem::new(),
192                #[cfg(feature = "trace")]
193                name: None,
194                #[cfg(feature = "trace")]
195                id: 0,
196                #[cfg(feature = "trace")]
197                all_tasks_next: AtomicPtr::new(core::ptr::null_mut()),
198            },
199            future: UninitCell::uninit(),
200        }
201    }
202
203    /// Try to spawn the task.
204    ///
205    /// The `future` closure constructs the future. It's only called if spawning is
206    /// actually possible. It is a closure instead of a simple `future: F` param to ensure
207    /// the future is constructed in-place, avoiding a temporary copy in the stack thanks to
208    /// NRVO optimizations.
209    ///
210    /// This function will fail if the task is already spawned and has not finished running.
211    /// In this case, the error is delayed: a "poisoned" SpawnToken is returned, which will
212    /// cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error.
213    ///
214    /// Once the task has finished running, you may spawn it again. It is allowed to spawn it
215    /// on a different executor.
216    pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> {
217        let task = AvailableTask::claim(self);
218        match task {
219            Some(task) => task.initialize(future),
220            None => SpawnToken::new_failed(),
221        }
222    }
223
224    unsafe fn poll(p: TaskRef) {
225        let this = &*p.as_ptr().cast::<TaskStorage<F>>();
226
227        let future = Pin::new_unchecked(this.future.as_mut());
228        let waker = waker::from_task(p);
229        let mut cx = Context::from_waker(&waker);
230        match future.poll(&mut cx) {
231            Poll::Ready(_) => {
232                #[cfg(feature = "trace")]
233                let exec_ptr: *const SyncExecutor = this.raw.executor.load(Ordering::Relaxed);
234
235                // As the future has finished and this function will not be called
236                // again, we can safely drop the future here.
237                this.future.drop_in_place();
238
239                // We replace the poll_fn with a despawn function, so that the task is cleaned up
240                // when the executor polls it next.
241                this.raw.poll_fn.set(Some(poll_exited));
242
243                // Make sure we despawn last, so that other threads can only spawn the task
244                // after we're done with it.
245                this.raw.state.despawn();
246
247                #[cfg(feature = "trace")]
248                trace::task_end(exec_ptr, &p);
249            }
250            Poll::Pending => {}
251        }
252
253        // the compiler is emitting a virtual call for waker drop, but we know
254        // it's a noop for our waker.
255        mem::forget(waker);
256    }
257
258    #[doc(hidden)]
259    #[allow(dead_code)]
260    fn _assert_sync(self) {
261        fn assert_sync<T: Sync>(_: T) {}
262
263        assert_sync(self)
264    }
265}
266
267/// An uninitialized [`TaskStorage`].
268pub struct AvailableTask<F: Future + 'static> {
269    task: &'static TaskStorage<F>,
270}
271
272impl<F: Future + 'static> AvailableTask<F> {
273    /// Try to claim a [`TaskStorage`].
274    ///
275    /// This function returns `None` if a task has already been spawned and has not finished running.
276    pub fn claim(task: &'static TaskStorage<F>) -> Option<Self> {
277        task.raw.state.spawn().then(|| Self { task })
278    }
279
280    fn initialize_impl<S>(self, future: impl FnOnce() -> F) -> SpawnToken<S> {
281        unsafe {
282            self.task.raw.poll_fn.set(Some(TaskStorage::<F>::poll));
283            self.task.future.write_in_place(future);
284
285            let task = TaskRef::new(self.task);
286
287            SpawnToken::new(task)
288        }
289    }
290
291    /// Initialize the [`TaskStorage`] to run the given future.
292    pub fn initialize(self, future: impl FnOnce() -> F) -> SpawnToken<F> {
293        self.initialize_impl::<F>(future)
294    }
295
296    /// Initialize the [`TaskStorage`] to run the given future.
297    ///
298    /// # Safety
299    ///
300    /// `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn`
301    /// is an `async fn`, NOT a hand-written `Future`.
302    #[doc(hidden)]
303    pub unsafe fn __initialize_async_fn<FutFn>(self, future: impl FnOnce() -> F) -> SpawnToken<FutFn> {
304        // When send-spawning a task, we construct the future in this thread, and effectively
305        // "send" it to the executor thread by enqueuing it in its queue. Therefore, in theory,
306        // send-spawning should require the future `F` to be `Send`.
307        //
308        // The problem is this is more restrictive than needed. Once the future is executing,
309        // it is never sent to another thread. It is only sent when spawning. It should be
310        // enough for the task's arguments to be Send. (and in practice it's super easy to
311        // accidentally make your futures !Send, for example by holding an `Rc` or a `&RefCell` across an `.await`.)
312        //
313        // We can do it by sending the task args and constructing the future in the executor thread
314        // on first poll. However, this cannot be done in-place, so it'll waste stack space for a copy
315        // of the args.
316        //
317        // Luckily, an `async fn` future contains just the args when freshly constructed. So, if the
318        // args are Send, it's OK to send a !Send future, as long as we do it before first polling it.
319        //
320        // (Note: this is how the generators are implemented today, it's not officially guaranteed yet,
321        // but it's possible it'll be guaranteed in the future. See zulip thread:
322        // https://rust-lang.zulipchat.com/#narrow/stream/187312-wg-async/topic/.22only.20before.20poll.22.20Send.20futures )
323        //
324        // The `FutFn` captures all the args, so if it's Send, the task can be send-spawned.
325        // This is why we return `SpawnToken<FutFn>` below.
326        //
327        // This ONLY holds for `async fn` futures. The other `spawn` methods can be called directly
328        // by the user, with arbitrary hand-implemented futures. This is why these return `SpawnToken<F>`.
329        self.initialize_impl::<FutFn>(future)
330    }
331}
332
333/// Raw storage that can hold up to N tasks of the same type.
334///
335/// This is essentially a `[TaskStorage<F>; N]`.
336pub struct TaskPool<F: Future + 'static, const N: usize> {
337    pool: [TaskStorage<F>; N],
338}
339
340impl<F: Future + 'static, const N: usize> TaskPool<F, N> {
341    /// Create a new TaskPool, with all tasks in non-spawned state.
342    pub const fn new() -> Self {
343        Self {
344            pool: [TaskStorage::NEW; N],
345        }
346    }
347
348    fn spawn_impl<T>(&'static self, future: impl FnOnce() -> F) -> SpawnToken<T> {
349        match self.pool.iter().find_map(AvailableTask::claim) {
350            Some(task) => task.initialize_impl::<T>(future),
351            None => SpawnToken::new_failed(),
352        }
353    }
354
355    /// Try to spawn a task in the pool.
356    ///
357    /// See [`TaskStorage::spawn()`] for details.
358    ///
359    /// This will loop over the pool and spawn the task in the first storage that
360    /// is currently free. If none is free, a "poisoned" SpawnToken is returned,
361    /// which will cause [`Spawner::spawn()`](super::Spawner::spawn) to return the error.
362    pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<impl Sized> {
363        self.spawn_impl::<F>(future)
364    }
365
366    /// Like spawn(), but allows the task to be send-spawned if the args are Send even if
367    /// the future is !Send.
368    ///
369    /// Not covered by semver guarantees. DO NOT call this directly. Intended to be used
370    /// by the Embassy macros ONLY.
371    ///
372    /// SAFETY: `future` must be a closure of the form `move || my_async_fn(args)`, where `my_async_fn`
373    /// is an `async fn`, NOT a hand-written `Future`.
374    #[doc(hidden)]
375    pub unsafe fn _spawn_async_fn<FutFn>(&'static self, future: FutFn) -> SpawnToken<impl Sized>
376    where
377        FutFn: FnOnce() -> F,
378    {
379        // See the comment in AvailableTask::__initialize_async_fn for explanation.
380        self.spawn_impl::<FutFn>(future)
381    }
382}
383
384#[derive(Clone, Copy)]
385pub(crate) struct Pender(*mut ());
386
387unsafe impl Send for Pender {}
388unsafe impl Sync for Pender {}
389
390impl Pender {
391    pub(crate) fn pend(self) {
392        extern "Rust" {
393            fn __pender(context: *mut ());
394        }
395        unsafe { __pender(self.0) };
396    }
397}
398
399pub(crate) struct SyncExecutor {
400    run_queue: RunQueue,
401    pender: Pender,
402}
403
404impl SyncExecutor {
405    pub(crate) fn new(pender: Pender) -> Self {
406        Self {
407            run_queue: RunQueue::new(),
408            pender,
409        }
410    }
411
412    /// Enqueue a task in the task queue
413    ///
414    /// # Safety
415    /// - `task` must be a valid pointer to a spawned task.
416    /// - `task` must be set up to run in this executor.
417    /// - `task` must NOT be already enqueued (in this executor or another one).
418    #[inline(always)]
419    unsafe fn enqueue(&self, task: TaskRef, l: state::Token) {
420        #[cfg(feature = "trace")]
421        trace::task_ready_begin(self, &task);
422
423        if self.run_queue.enqueue(task, l) {
424            self.pender.pend();
425        }
426    }
427
428    pub(super) unsafe fn spawn(&'static self, task: TaskRef) {
429        task.header()
430            .executor
431            .store((self as *const Self).cast_mut(), Ordering::Relaxed);
432
433        #[cfg(feature = "trace")]
434        trace::task_new(self, &task);
435
436        state::locked(|l| {
437            self.enqueue(task, l);
438        })
439    }
440
441    /// # Safety
442    ///
443    /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created.
444    pub(crate) unsafe fn poll(&'static self) {
445        #[cfg(feature = "trace")]
446        trace::poll_start(self);
447
448        self.run_queue.dequeue_all(|p| {
449            let task = p.header();
450
451            #[cfg(feature = "trace")]
452            trace::task_exec_begin(self, &p);
453
454            // Run the task
455            task.poll_fn.get().unwrap_unchecked()(p);
456
457            #[cfg(feature = "trace")]
458            trace::task_exec_end(self, &p);
459        });
460
461        #[cfg(feature = "trace")]
462        trace::executor_idle(self)
463    }
464}
465
466/// Raw executor.
467///
468/// This is the core of the Embassy executor. It is low-level, requiring manual
469/// handling of wakeups and task polling. If you can, prefer using one of the
470/// [higher level executors](crate::Executor).
471///
472/// The raw executor leaves it up to you to handle wakeups and scheduling:
473///
474/// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks
475///   that "want to run").
476/// - You must supply a pender function, as shown below. The executor will call it to notify you
477///   it has work to do. You must arrange for `poll()` to be called as soon as possible.
478/// - Enabling `arch-xx` features will define a pender function for you. This means that you
479///   are limited to using the executors provided to you by the architecture/platform
480///   implementation. If you need a different executor, you must not enable `arch-xx` features.
481///
482/// The pender can be called from *any* context: any thread, any interrupt priority
483/// level, etc. It may be called synchronously from any `Executor` method call as well.
484/// You must deal with this correctly.
485///
486/// In particular, you must NOT call `poll` directly from the pender callback, as this violates
487/// the requirement for `poll` to not be called reentrantly.
488///
489/// The pender function must be exported with the name `__pender` and have the following signature:
490///
491/// ```rust
492/// #[export_name = "__pender"]
493/// fn pender(context: *mut ()) {
494///    // schedule `poll()` to be called
495/// }
496/// ```
497///
498/// The `context` argument is a piece of arbitrary data the executor will pass to the pender.
499/// You can set the `context` when calling [`Executor::new()`]. You can use it to, for example,
500/// differentiate between executors, or to pass a pointer to a callback that should be called.
501#[repr(transparent)]
502pub struct Executor {
503    pub(crate) inner: SyncExecutor,
504
505    _not_sync: PhantomData<*mut ()>,
506}
507
508impl Executor {
509    pub(crate) unsafe fn wrap(inner: &SyncExecutor) -> &Self {
510        mem::transmute(inner)
511    }
512
513    /// Create a new executor.
514    ///
515    /// When the executor has work to do, it will call the pender function and pass `context` to it.
516    ///
517    /// See [`Executor`] docs for details on the pender.
518    pub fn new(context: *mut ()) -> Self {
519        Self {
520            inner: SyncExecutor::new(Pender(context)),
521            _not_sync: PhantomData,
522        }
523    }
524
525    /// Spawn a task in this executor.
526    ///
527    /// # Safety
528    ///
529    /// `task` must be a valid pointer to an initialized but not-already-spawned task.
530    ///
531    /// It is OK to use `unsafe` to call this from a thread that's not the executor thread.
532    /// In this case, the task's Future must be Send. This is because this is effectively
533    /// sending the task to the executor thread.
534    pub(super) unsafe fn spawn(&'static self, task: TaskRef) {
535        self.inner.spawn(task)
536    }
537
538    /// Poll all queued tasks in this executor.
539    ///
540    /// This loops over all tasks that are queued to be polled (i.e. they're
541    /// freshly spawned or they've been woken). Other tasks are not polled.
542    ///
543    /// You must call `poll` after receiving a call to the pender. It is OK
544    /// to call `poll` even when not requested by the pender, but it wastes
545    /// energy.
546    ///
547    /// # Safety
548    ///
549    /// You must call `initialize` before calling this method.
550    ///
551    /// You must NOT call `poll` reentrantly on the same executor.
552    ///
553    /// In particular, note that `poll` may call the pender synchronously. Therefore, you
554    /// must NOT directly call `poll()` from the pender callback. Instead, the callback has to
555    /// somehow schedule for `poll()` to be called later, at a time you know for sure there's
556    /// no `poll()` already running.
557    pub unsafe fn poll(&'static self) {
558        self.inner.poll()
559    }
560
561    /// Get a spawner that spawns tasks in this executor.
562    ///
563    /// It is OK to call this method multiple times to obtain multiple
564    /// `Spawner`s. You may also copy `Spawner`s.
565    pub fn spawner(&'static self) -> super::Spawner {
566        super::Spawner::new(self)
567    }
568
569    /// Get a unique ID for this Executor.
570    pub fn id(&'static self) -> usize {
571        &self.inner as *const SyncExecutor as usize
572    }
573}
574
575/// Wake a task by `TaskRef`.
576///
577/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`].
578pub fn wake_task(task: TaskRef) {
579    let header = task.header();
580    header.state.run_enqueue(|l| {
581        // We have just marked the task as scheduled, so enqueue it.
582        unsafe {
583            let executor = header.executor.load(Ordering::Relaxed).as_ref().unwrap_unchecked();
584            executor.enqueue(task, l);
585        }
586    });
587}
588
589/// Wake a task by `TaskRef` without calling pend.
590///
591/// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`].
592pub fn wake_task_no_pend(task: TaskRef) {
593    let header = task.header();
594    header.state.run_enqueue(|l| {
595        // We have just marked the task as scheduled, so enqueue it.
596        unsafe {
597            let executor = header.executor.load(Ordering::Relaxed).as_ref().unwrap_unchecked();
598            executor.run_queue.enqueue(task, l);
599        }
600    });
601}