aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2021-08-26 00:20:52 +0200
committerDario Nieuwenhuis <[email protected]>2021-08-31 23:59:28 +0200
commit297de612e5ea01a2b6cc4921ff7c2e133dba4ac2 (patch)
tree0aa880ea46a707e4621563555b158b40e19992c9
parente56c6166dcac9132cde1769e5ef8d60e03963329 (diff)
Improve executor naming. Add docs.
-rw-r--r--embassy-macros/src/lib.rs8
-rw-r--r--embassy/src/executor/arch/arm.rs75
-rw-r--r--embassy/src/executor/arch/std.rs23
-rw-r--r--embassy/src/executor/mod.rs4
-rw-r--r--embassy/src/executor/raw/mod.rs139
-rw-r--r--embassy/src/executor/raw/run_queue.rs30
-rw-r--r--embassy/src/executor/raw/waker.rs11
-rw-r--r--embassy/src/executor/spawner.rs41
-rw-r--r--embassy/src/time/driver.rs7
-rw-r--r--embassy/src/time/mod.rs2
-rw-r--r--examples/nrf/src/bin/raw_spawn.rs6
11 files changed, 290 insertions, 56 deletions
diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs
index ddcee0cb1..54cf6fb8a 100644
--- a/embassy-macros/src/lib.rs
+++ b/embassy-macros/src/lib.rs
@@ -115,12 +115,12 @@ pub fn task(args: TokenStream, item: TokenStream) -> TokenStream {
115 let result = quote! { 115 let result = quote! {
116 #(#attrs)* 116 #(#attrs)*
117 #visibility fn #name(#args) -> #embassy_path::executor::SpawnToken<#impl_ty> { 117 #visibility fn #name(#args) -> #embassy_path::executor::SpawnToken<#impl_ty> {
118 use #embassy_path::executor::raw::Task; 118 use #embassy_path::executor::raw::TaskStorage;
119 #task_fn 119 #task_fn
120 type F = #impl_ty; 120 type F = #impl_ty;
121 const NEW_TASK: Task<F> = Task::new(); 121 const NEW_TASK: TaskStorage<F> = TaskStorage::new();
122 static POOL: [Task<F>; #pool_size] = [NEW_TASK; #pool_size]; 122 static POOL: [TaskStorage<F>; #pool_size] = [NEW_TASK; #pool_size];
123 unsafe { Task::spawn_pool(&POOL, move || task(#arg_names)) } 123 unsafe { TaskStorage::spawn_pool(&POOL, move || task(#arg_names)) }
124 } 124 }
125 }; 125 };
126 result.into() 126 result.into()
diff --git a/embassy/src/executor/arch/arm.rs b/embassy/src/executor/arch/arm.rs
index 4fd734cd7..d23a595ff 100644
--- a/embassy/src/executor/arch/arm.rs
+++ b/embassy/src/executor/arch/arm.rs
@@ -4,12 +4,23 @@ use core::ptr;
4use super::{raw, Spawner}; 4use super::{raw, Spawner};
5use crate::interrupt::{Interrupt, InterruptExt}; 5use crate::interrupt::{Interrupt, InterruptExt};
6 6
7/// Thread mode executor, using WFE/SEV.
8///
9/// This is the simplest and most common kind of executor. It runs on
10/// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction
11/// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction
12/// is executed, to make the `WFE` exit from sleep and poll the task.
13///
14/// This executor allows for ultra low power consumption for chips where `WFE`
15/// triggers low-power sleep without extra steps. If your chip requires extra steps,
16/// you may use [`raw::Executor`] directly to program custom behavior.
7pub struct Executor { 17pub struct Executor {
8 inner: raw::Executor, 18 inner: raw::Executor,
9 not_send: PhantomData<*mut ()>, 19 not_send: PhantomData<*mut ()>,
10} 20}
11 21
12impl Executor { 22impl Executor {
23 /// Create a new Executor.
13 pub fn new() -> Self { 24 pub fn new() -> Self {
14 Self { 25 Self {
15 inner: raw::Executor::new(|_| cortex_m::asm::sev(), ptr::null_mut()), 26 inner: raw::Executor::new(|_| cortex_m::asm::sev(), ptr::null_mut()),
@@ -17,14 +28,29 @@ impl Executor {
17 } 28 }
18 } 29 }
19 30
20 /// Runs the executor. 31 /// Run the executor.
32 ///
33 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
34 /// this executor. Use it to spawn the initial task(s). After `init` returns,
35 /// the executor starts running the tasks.
36 ///
37 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
38 /// for example by passing it as an argument to the initial tasks.
39 ///
40 /// This function requires `&'static mut self`. This means you have to store the
41 /// Executor instance in a place where it'll live forever and grants you mutable
42 /// access. There's a few ways to do this:
43 ///
44 /// - a [Forever](crate::util::Forever) (safe)
45 /// - a `static mut` (unsafe)
46 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
21 /// 47 ///
22 /// This function never returns. 48 /// This function never returns.
23 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 49 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
24 init(unsafe { self.inner.spawner() }); 50 init(self.inner.spawner());
25 51
26 loop { 52 loop {
27 unsafe { self.inner.run_queued() }; 53 unsafe { self.inner.poll() };
28 cortex_m::asm::wfe(); 54 cortex_m::asm::wfe();
29 } 55 }
30 } 56 }
@@ -41,6 +67,27 @@ fn pend_by_number(n: u16) {
41 cortex_m::peripheral::NVIC::pend(N(n)) 67 cortex_m::peripheral::NVIC::pend(N(n))
42} 68}
43 69
70/// Interrupt mode executor.
71///
72/// This executor runs tasks in interrupt mode. The interrupt handler is set up
73/// to poll tasks, and when a task is woken the interrupt is pended from software.
74///
75/// This allows running async tasks at a priority higher than thread mode. One
76/// use case is to leave thread mode free for non-async tasks. Another use case is
77/// to run multiple executors: one in thread mode for low priority tasks and another in
78/// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower
79/// priority ones.
80///
81/// It is even possible to run multiple interrupt mode executors at different priorities,
82/// by assigning different priorities to the interrupts. For an example on how to do this,
83/// See the 'multiprio' example for 'embassy-nrf'.
84///
85/// To use it, you have to pick an interrupt that won't be used by the hardware.
86/// Some chips reserve some interrupts for this purpose, sometimes named "software interrupts" (SWI).
87/// If this is not the case, you may use an interrupt from any unused peripheral.
88///
89/// It is somewhat more complex to use, it's recommended to use the thread-mode
90/// [`Executor`] instead, if it works for your use case.
44pub struct InterruptExecutor<I: Interrupt> { 91pub struct InterruptExecutor<I: Interrupt> {
45 irq: I, 92 irq: I,
46 inner: raw::Executor, 93 inner: raw::Executor,
@@ -48,6 +95,7 @@ pub struct InterruptExecutor<I: Interrupt> {
48} 95}
49 96
50impl<I: Interrupt> InterruptExecutor<I> { 97impl<I: Interrupt> InterruptExecutor<I> {
98 /// Create a new Executor.
51 pub fn new(irq: I) -> Self { 99 pub fn new(irq: I) -> Self {
52 let ctx = irq.number() as *mut (); 100 let ctx = irq.number() as *mut ();
53 Self { 101 Self {
@@ -59,16 +107,29 @@ impl<I: Interrupt> InterruptExecutor<I> {
59 107
60 /// Start the executor. 108 /// Start the executor.
61 /// 109 ///
62 /// `init` is called in the interrupt context, then the interrupt is 110 /// The `init` closure is called from interrupt mode, with a [`Spawner`] that spawns tasks on
63 /// configured to run the executor. 111 /// this executor. Use it to spawn the initial task(s). After `init` returns,
112 /// the interrupt is configured so that the executor starts running the tasks.
113 /// Once the executor is started, `start` returns.
114 ///
115 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
116 /// for example by passing it as an argument to the initial tasks.
117 ///
118 /// This function requires `&'static mut self`. This means you have to store the
119 /// Executor instance in a place where it'll live forever and grants you mutable
120 /// access. There's a few ways to do this:
121 ///
122 /// - a [Forever](crate::util::Forever) (safe)
123 /// - a `static mut` (unsafe)
124 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
64 pub fn start(&'static mut self, init: impl FnOnce(Spawner) + Send) { 125 pub fn start(&'static mut self, init: impl FnOnce(Spawner) + Send) {
65 self.irq.disable(); 126 self.irq.disable();
66 127
67 init(unsafe { self.inner.spawner() }); 128 init(self.inner.spawner());
68 129
69 self.irq.set_handler(|ctx| unsafe { 130 self.irq.set_handler(|ctx| unsafe {
70 let executor = &*(ctx as *const raw::Executor); 131 let executor = &*(ctx as *const raw::Executor);
71 executor.run_queued(); 132 executor.poll();
72 }); 133 });
73 self.irq.set_handler_context(&self.inner as *const _ as _); 134 self.irq.set_handler_context(&self.inner as *const _ as _);
74 self.irq.enable(); 135 self.irq.enable();
diff --git a/embassy/src/executor/arch/std.rs b/embassy/src/executor/arch/std.rs
index fb7880542..b93ab8a79 100644
--- a/embassy/src/executor/arch/std.rs
+++ b/embassy/src/executor/arch/std.rs
@@ -3,6 +3,7 @@ use std::sync::{Condvar, Mutex};
3 3
4use super::{raw, Spawner}; 4use super::{raw, Spawner};
5 5
6/// Single-threaded std-based executor.
6pub struct Executor { 7pub struct Executor {
7 inner: raw::Executor, 8 inner: raw::Executor,
8 not_send: PhantomData<*mut ()>, 9 not_send: PhantomData<*mut ()>,
@@ -10,6 +11,7 @@ pub struct Executor {
10} 11}
11 12
12impl Executor { 13impl Executor {
14 /// Create a new Executor.
13 pub fn new() -> Self { 15 pub fn new() -> Self {
14 let signaler = &*Box::leak(Box::new(Signaler::new())); 16 let signaler = &*Box::leak(Box::new(Signaler::new()));
15 Self { 17 Self {
@@ -25,14 +27,29 @@ impl Executor {
25 } 27 }
26 } 28 }
27 29
28 /// Runs the executor. 30 /// Run the executor.
31 ///
32 /// The `init` closure is called with a [`Spawner`] that spawns tasks on
33 /// this executor. Use it to spawn the initial task(s). After `init` returns,
34 /// the executor starts running the tasks.
35 ///
36 /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`),
37 /// for example by passing it as an argument to the initial tasks.
38 ///
39 /// This function requires `&'static mut self`. This means you have to store the
40 /// Executor instance in a place where it'll live forever and grants you mutable
41 /// access. There's a few ways to do this:
42 ///
43 /// - a [Forever](crate::util::Forever) (safe)
44 /// - a `static mut` (unsafe)
45 /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
29 /// 46 ///
30 /// This function never returns. 47 /// This function never returns.
31 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { 48 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
32 init(unsafe { self.inner.spawner() }); 49 init(self.inner.spawner());
33 50
34 loop { 51 loop {
35 unsafe { self.inner.run_queued() }; 52 unsafe { self.inner.poll() };
36 self.signaler.wait() 53 self.signaler.wait()
37 } 54 }
38 } 55 }
diff --git a/embassy/src/executor/mod.rs b/embassy/src/executor/mod.rs
index 5ffbe689e..1f6bdd277 100644
--- a/embassy/src/executor/mod.rs
+++ b/embassy/src/executor/mod.rs
@@ -1,3 +1,7 @@
1//! Async task executor.
2
3#![deny(missing_docs)]
4
1#[cfg_attr(feature = "std", path = "arch/std.rs")] 5#[cfg_attr(feature = "std", path = "arch/std.rs")]
2#[cfg_attr(not(feature = "std"), path = "arch/arm.rs")] 6#[cfg_attr(not(feature = "std"), path = "arch/arm.rs")]
3mod arch; 7mod arch;
diff --git a/embassy/src/executor/raw/mod.rs b/embassy/src/executor/raw/mod.rs
index 235a09198..05fb29758 100644
--- a/embassy/src/executor/raw/mod.rs
+++ b/embassy/src/executor/raw/mod.rs
@@ -1,3 +1,12 @@
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 in [`crate::executor`] and the [`crate::task`] macro, which are fully safe.
9
1mod run_queue; 10mod run_queue;
2#[cfg(feature = "time")] 11#[cfg(feature = "time")]
3mod timer_queue; 12mod timer_queue;
@@ -30,6 +39,10 @@ pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1;
30#[cfg(feature = "time")] 39#[cfg(feature = "time")]
31pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; 40pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2;
32 41
42/// Raw task header for use in task pointers.
43///
44/// This is an opaque struct, used for raw pointers to tasks, for use
45/// with funtions like [`wake_task`] and [`task_from_waker`].
33pub struct TaskHeader { 46pub struct TaskHeader {
34 pub(crate) state: AtomicU32, 47 pub(crate) state: AtomicU32,
35 pub(crate) run_queue_item: RunQueueItem, 48 pub(crate) run_queue_item: RunQueueItem,
@@ -85,15 +98,29 @@ impl TaskHeader {
85 } 98 }
86} 99}
87 100
101/// Raw storage in which a task can be spawned.
102///
103/// This struct holds the necessary memory to spawn one task whose future is `F`.
104/// At a given time, the `Task` may be in spawned or not-spawned state. You may spawn it
105/// with [`Task::spawn()`], which will fail if it is already spawned.
106///
107/// A `TaskStorage` must live forever, it may not be deallocated even after the task has finished
108/// running. Hence the relevant methods require `&'static self`. It may be reused, however.
109///
110/// Internally, the [embassy::task](crate::task) macro allocates an array of `TaskStorage`s
111/// in a `static`. The most common reason to use the raw `Task` is to have control of where
112/// the memory for the task is allocated: on the stack, or on the heap with e.g. `Box::leak`, etc.
113
88// repr(C) is needed to guarantee that the Task is located at offset 0 114// repr(C) is needed to guarantee that the Task is located at offset 0
89// This makes it safe to cast between Task and Task pointers. 115// This makes it safe to cast between Task and Task pointers.
90#[repr(C)] 116#[repr(C)]
91pub struct Task<F: Future + 'static> { 117pub struct TaskStorage<F: Future + 'static> {
92 raw: TaskHeader, 118 raw: TaskHeader,
93 future: UninitCell<F>, // Valid if STATE_SPAWNED 119 future: UninitCell<F>, // Valid if STATE_SPAWNED
94} 120}
95 121
96impl<F: Future + 'static> Task<F> { 122impl<F: Future + 'static> TaskStorage<F> {
123 /// Create a new Task, in not-spawned state.
97 pub const fn new() -> Self { 124 pub const fn new() -> Self {
98 Self { 125 Self {
99 raw: TaskHeader::new(), 126 raw: TaskHeader::new(),
@@ -101,6 +128,12 @@ impl<F: Future + 'static> Task<F> {
101 } 128 }
102 } 129 }
103 130
131 /// Try to spawn a task in a pool.
132 ///
133 /// See [`Self::spawn()`] for details.
134 ///
135 /// This will loop over the pool and spawn the task in the first storage that
136 /// is currently free. If none is free,
104 pub fn spawn_pool(pool: &'static [Self], future: impl FnOnce() -> F) -> SpawnToken<F> { 137 pub fn spawn_pool(pool: &'static [Self], future: impl FnOnce() -> F) -> SpawnToken<F> {
105 for task in pool { 138 for task in pool {
106 if task.spawn_allocate() { 139 if task.spawn_allocate() {
@@ -111,6 +144,19 @@ impl<F: Future + 'static> Task<F> {
111 SpawnToken::new_failed() 144 SpawnToken::new_failed()
112 } 145 }
113 146
147 /// Try to spawn the task.
148 ///
149 /// The `future` closure constructs the future. It's only called if spawning is
150 /// actually possible. It is a closure instead of a simple `future: F` param to ensure
151 /// the future is constructed in-place, avoiding a temporary copy in the stack thanks to
152 /// NRVO optimizations.
153 ///
154 /// This function will fail if the task is already spawned and has not finished running.
155 /// In this case, the error is delayed: a "poisoned" SpawnToken is returned, which will
156 /// cause [`Executor::spawn()`] to return the error.
157 ///
158 /// Once the task has finished running, you may spawn it again. It is allowed to spawn it
159 /// on a different executor.
114 pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<F> { 160 pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<F> {
115 if self.spawn_allocate() { 161 if self.spawn_allocate() {
116 unsafe { self.spawn_initialize(future) } 162 unsafe { self.spawn_initialize(future) }
@@ -136,7 +182,7 @@ impl<F: Future + 'static> Task<F> {
136 } 182 }
137 183
138 unsafe fn poll(p: NonNull<TaskHeader>) { 184 unsafe fn poll(p: NonNull<TaskHeader>) {
139 let this = &*(p.as_ptr() as *const Task<F>); 185 let this = &*(p.as_ptr() as *const TaskStorage<F>);
140 186
141 let future = Pin::new_unchecked(this.future.as_mut()); 187 let future = Pin::new_unchecked(this.future.as_mut());
142 let waker = waker::from_task(p); 188 let waker = waker::from_task(p);
@@ -155,8 +201,27 @@ impl<F: Future + 'static> Task<F> {
155 } 201 }
156} 202}
157 203
158unsafe impl<F: Future + 'static> Sync for Task<F> {} 204unsafe impl<F: Future + 'static> Sync for TaskStorage<F> {}
159 205
206/// Raw executor.
207///
208/// This is the core of the Embassy executor. It is low-level, requiring manual
209/// handling of wakeups and task polling. If you can, prefer using one of the
210/// higher level executors in [`crate::executor`].
211///
212/// The raw executor leaves it up to you to handle wakeups and scheduling:
213///
214/// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks
215/// that "want to run").
216/// - You must supply a `signal_fn`. The executor will call it to notify you it has work
217/// to do. You must arrange for `poll()` to be called as soon as possible.
218///
219/// `signal_fn` can be called from *any* context: any thread, any interrupt priority
220/// level, etc. It may be called synchronously from any `Executor` method call as well.
221/// You must deal with this correctly.
222///
223/// In particular, you must NOT call `poll` directly from `signal_fn`, as this violates
224/// the requirement for `poll` to not be called reentrantly.
160pub struct Executor { 225pub struct Executor {
161 run_queue: RunQueue, 226 run_queue: RunQueue,
162 signal_fn: fn(*mut ()), 227 signal_fn: fn(*mut ()),
@@ -169,6 +234,12 @@ pub struct Executor {
169} 234}
170 235
171impl Executor { 236impl Executor {
237 /// Create a new executor.
238 ///
239 /// When the executor has work to do, it will call `signal_fn` with
240 /// `signal_ctx` as argument.
241 ///
242 /// See [`Executor`] docs for details on `signal_fn`.
172 pub fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { 243 pub fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self {
173 #[cfg(feature = "time")] 244 #[cfg(feature = "time")]
174 let alarm = unsafe { unwrap!(driver::allocate_alarm()) }; 245 let alarm = unsafe { unwrap!(driver::allocate_alarm()) };
@@ -187,23 +258,51 @@ impl Executor {
187 } 258 }
188 } 259 }
189 260
190 pub fn set_signal_ctx(&mut self, signal_ctx: *mut ()) { 261 /// Enqueue a task in the task queue
191 self.signal_ctx = signal_ctx; 262 ///
192 } 263 /// # Safety
193 264 /// - `task` must be a valid pointer to a spawned task.
194 unsafe fn enqueue(&self, item: *mut TaskHeader) { 265 /// - `task` must be set up to run in this executor.
195 if self.run_queue.enqueue(item) { 266 /// - `task` must NOT be already enqueued (in this executor or another one).
267 unsafe fn enqueue(&self, task: *mut TaskHeader) {
268 if self.run_queue.enqueue(task) {
196 (self.signal_fn)(self.signal_ctx) 269 (self.signal_fn)(self.signal_ctx)
197 } 270 }
198 } 271 }
199 272
200 pub unsafe fn spawn(&'static self, task: NonNull<TaskHeader>) { 273 /// Spawn a task in this executor.
274 ///
275 /// # Safety
276 ///
277 /// `task` must be a valid pointer to an initialized but not-already-spawned task.
278 ///
279 /// It is OK to use `unsafe` to call this from a thread that's not the executor thread.
280 /// In this case, the task's Future must be Send. This is because this is effectively
281 /// sending the task to the executor thread.
282 pub(super) unsafe fn spawn(&'static self, task: NonNull<TaskHeader>) {
201 let task = task.as_ref(); 283 let task = task.as_ref();
202 task.executor.set(self); 284 task.executor.set(self);
203 self.enqueue(task as *const _ as _); 285 self.enqueue(task as *const _ as _);
204 } 286 }
205 287
206 pub unsafe fn run_queued(&'static self) { 288 /// Poll all queued tasks in this executor.
289 ///
290 /// This loops over all tasks that are queued to be polled (i.e. they're
291 /// freshly spawned or they've been woken). Other tasks are not polled.
292 ///
293 /// You must call `poll` after receiving a call to `signal_fn`. It is OK
294 /// to call `poll` even when not requested by `signal_fn`, but it wastes
295 /// energy.
296 ///
297 /// # Safety
298 ///
299 /// You must NOT call `poll` reentrantly on the same executor.
300 ///
301 /// In particular, note that `poll` may call `signal_fn` synchronously. Therefore, you
302 /// must NOT directly call `poll()` from your `signal_fn`. Instead, `signal_fn` has to
303 /// somehow schedule for `poll()` to be called later, at a time you know for sure there's
304 /// no `poll()` already running.
305 pub unsafe fn poll(&'static self) {
207 #[cfg(feature = "time")] 306 #[cfg(feature = "time")]
208 self.timer_queue.dequeue_expired(Instant::now(), |p| { 307 self.timer_queue.dequeue_expired(Instant::now(), |p| {
209 p.as_ref().enqueue(); 308 p.as_ref().enqueue();
@@ -235,18 +334,26 @@ impl Executor {
235 334
236 #[cfg(feature = "time")] 335 #[cfg(feature = "time")]
237 { 336 {
238 // If this is in the past, set_alarm will immediately trigger the alarm, 337 // If this is already in the past, set_alarm will immediately trigger the alarm.
239 // which will make the wfe immediately return so we do another loop iteration. 338 // This will cause `signal_fn` to be called, which will cause `poll()` to be called again,
339 // so we immediately do another poll loop iteration.
240 let next_expiration = self.timer_queue.next_expiration(); 340 let next_expiration = self.timer_queue.next_expiration();
241 driver::set_alarm(self.alarm, next_expiration.as_ticks()); 341 driver::set_alarm(self.alarm, next_expiration.as_ticks());
242 } 342 }
243 } 343 }
244 344
245 pub unsafe fn spawner(&'static self) -> super::Spawner { 345 /// Get a spawner that spawns tasks in this executor.
346 ///
347 /// It is OK to call this method multiple times to obtain multiple
348 /// `Spawner`s. You may also copy `Spawner`s.
349 pub fn spawner(&'static self) -> super::Spawner {
246 super::Spawner::new(self) 350 super::Spawner::new(self)
247 } 351 }
248} 352}
249 353
354/// Wake a task by raw pointer.
355///
356/// You can obtain task pointers from `Waker`s using [`task_from_waker`].
250pub unsafe fn wake_task(task: NonNull<TaskHeader>) { 357pub unsafe fn wake_task(task: NonNull<TaskHeader>) {
251 task.as_ref().enqueue(); 358 task.as_ref().enqueue();
252} 359}
diff --git a/embassy/src/executor/raw/run_queue.rs b/embassy/src/executor/raw/run_queue.rs
index 8e8bc8ff3..d0258c717 100644
--- a/embassy/src/executor/raw/run_queue.rs
+++ b/embassy/src/executor/raw/run_queue.rs
@@ -39,13 +39,17 @@ impl RunQueue {
39 } 39 }
40 40
41 /// Enqueues an item. Returns true if the queue was empty. 41 /// Enqueues an item. Returns true if the queue was empty.
42 pub(crate) unsafe fn enqueue(&self, item: *mut TaskHeader) -> bool { 42 ///
43 /// # Safety
44 ///
45 /// `item` must NOT be already enqueued in any queue.
46 pub(crate) unsafe fn enqueue(&self, task: *mut TaskHeader) -> bool {
43 let mut prev = self.head.load(Ordering::Acquire); 47 let mut prev = self.head.load(Ordering::Acquire);
44 loop { 48 loop {
45 (*item).run_queue_item.next.store(prev, Ordering::Relaxed); 49 (*task).run_queue_item.next.store(prev, Ordering::Relaxed);
46 match self 50 match self
47 .head 51 .head
48 .compare_exchange_weak(prev, item, Ordering::AcqRel, Ordering::Acquire) 52 .compare_exchange_weak(prev, task, Ordering::AcqRel, Ordering::Acquire)
49 { 53 {
50 Ok(_) => break, 54 Ok(_) => break,
51 Err(next_prev) => prev = next_prev, 55 Err(next_prev) => prev = next_prev,
@@ -55,17 +59,25 @@ impl RunQueue {
55 prev.is_null() 59 prev.is_null()
56 } 60 }
57 61
58 pub(crate) unsafe fn dequeue_all(&self, on_task: impl Fn(NonNull<TaskHeader>)) { 62 /// Empty the queue, then call `on_task` for each task that was in the queue.
59 let mut task = self.head.swap(ptr::null_mut(), Ordering::AcqRel); 63 /// NOTE: It is OK for `on_task` to enqueue more tasks. In this case they're left in the queue
64 /// and will be processed by the *next* call to `dequeue_all`, *not* the current one.
65 pub(crate) fn dequeue_all(&self, on_task: impl Fn(NonNull<TaskHeader>)) {
66 // Atomically empty the queue.
67 let mut ptr = self.head.swap(ptr::null_mut(), Ordering::AcqRel);
60 68
61 while !task.is_null() { 69 // Iterate the linked list of tasks that were previously in the queue.
70 while let Some(task) = NonNull::new(ptr) {
62 // If the task re-enqueues itself, the `next` pointer will get overwritten. 71 // If the task re-enqueues itself, the `next` pointer will get overwritten.
63 // Therefore, first read the next pointer, and only then process the task. 72 // Therefore, first read the next pointer, and only then process the task.
64 let next = (*task).run_queue_item.next.load(Ordering::Relaxed); 73 let next = unsafe { task.as_ref() }
74 .run_queue_item
75 .next
76 .load(Ordering::Relaxed);
65 77
66 on_task(NonNull::new_unchecked(task)); 78 on_task(task);
67 79
68 task = next 80 ptr = next
69 } 81 }
70 } 82 }
71} 83}
diff --git a/embassy/src/executor/raw/waker.rs b/embassy/src/executor/raw/waker.rs
index e53190f17..7cd847697 100644
--- a/embassy/src/executor/raw/waker.rs
+++ b/embassy/src/executor/raw/waker.rs
@@ -22,6 +22,17 @@ pub(crate) unsafe fn from_task(p: NonNull<TaskHeader>) -> Waker {
22 Waker::from_raw(RawWaker::new(p.as_ptr() as _, &VTABLE)) 22 Waker::from_raw(RawWaker::new(p.as_ptr() as _, &VTABLE))
23} 23}
24 24
25/// Get a task pointer from a waker.
26///
27/// This can used as an optimization in wait queues to store task pointers
28/// (1 word) instead of full Wakers (2 words). This saves a bit of RAM and helps
29/// avoid dynamic dispatch.
30///
31/// You can use the returned task pointer to wake the task with [`wake_task`](super::wake_task).
32///
33/// # Panics
34///
35/// Panics if the waker is not created by the Embassy executor.
25pub unsafe fn task_from_waker(waker: &Waker) -> NonNull<TaskHeader> { 36pub unsafe fn task_from_waker(waker: &Waker) -> NonNull<TaskHeader> {
26 let hack: &WakerHack = mem::transmute(waker); 37 let hack: &WakerHack = mem::transmute(waker);
27 if hack.vtable != &VTABLE { 38 if hack.vtable != &VTABLE {
diff --git a/embassy/src/executor/spawner.rs b/embassy/src/executor/spawner.rs
index 36100aecb..908e139ae 100644
--- a/embassy/src/executor/spawner.rs
+++ b/embassy/src/executor/spawner.rs
@@ -4,7 +4,17 @@ use core::ptr::NonNull;
4 4
5use super::raw; 5use super::raw;
6 6
7#[must_use = "Calling a task function does nothing on its own. You must pass the returned SpawnToken to Executor::spawn()"] 7/// Token to spawn a newly-created task in an executor.
8///
9/// When calling a task function (like `#[embassy::task] async fn my_task() { ... }`), the returned
10/// value is a `SpawnToken` that represents an instance of the task, ready to spawn. You must
11/// then spawn it into an executor, typically with [`Spawner::spawn()`].
12///
13/// # Panics
14///
15/// Dropping a SpawnToken instance panics. You may not "abort" spawning a task in this way.
16/// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it.
17#[must_use = "Calling a task function does nothing on its own. You must spawn the returned SpawnToken, typically with Spawner::spawn()"]
8pub struct SpawnToken<F> { 18pub struct SpawnToken<F> {
9 raw_task: Option<NonNull<raw::TaskHeader>>, 19 raw_task: Option<NonNull<raw::TaskHeader>>,
10 phantom: PhantomData<*mut F>, 20 phantom: PhantomData<*mut F>,
@@ -29,13 +39,19 @@ impl<F> SpawnToken<F> {
29impl<F> Drop for SpawnToken<F> { 39impl<F> Drop for SpawnToken<F> {
30 fn drop(&mut self) { 40 fn drop(&mut self) {
31 // TODO deallocate the task instead. 41 // TODO deallocate the task instead.
32 panic!("SpawnToken instances may not be dropped. You must pass them to Executor::spawn()") 42 panic!("SpawnToken instances may not be dropped. You must pass them to Spawner::spawn()")
33 } 43 }
34} 44}
35 45
46/// Error returned when spawning a task.
36#[derive(Copy, Clone, Debug)] 47#[derive(Copy, Clone, Debug)]
37#[cfg_attr(feature = "defmt", derive(defmt::Format))] 48#[cfg_attr(feature = "defmt", derive(defmt::Format))]
38pub enum SpawnError { 49pub enum SpawnError {
50 /// Too many instances of this task are already running.
51 ///
52 /// By default, a task marked with `#[embassy::task]` can only have one instance
53 /// running at a time. You may allow multiple instances to run in parallel with
54 /// `#[embassy::task(pool_size = 4)]`, at the cost of higher RAM usage.
39 Busy, 55 Busy,
40} 56}
41 57
@@ -52,13 +68,16 @@ pub struct Spawner {
52} 68}
53 69
54impl Spawner { 70impl Spawner {
55 pub(crate) unsafe fn new(executor: &'static raw::Executor) -> Self { 71 pub(crate) fn new(executor: &'static raw::Executor) -> Self {
56 Self { 72 Self {
57 executor, 73 executor,
58 not_send: PhantomData, 74 not_send: PhantomData,
59 } 75 }
60 } 76 }
61 77
78 /// Spawn a task into an executor.
79 ///
80 /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy::task]).
62 pub fn spawn<F>(&self, token: SpawnToken<F>) -> Result<(), SpawnError> { 81 pub fn spawn<F>(&self, token: SpawnToken<F>) -> Result<(), SpawnError> {
63 let task = token.raw_task; 82 let task = token.raw_task;
64 mem::forget(token); 83 mem::forget(token);
@@ -93,10 +112,11 @@ impl Spawner {
93 112
94/// Handle to spawn tasks into an executor from any thread. 113/// Handle to spawn tasks into an executor from any thread.
95/// 114///
96/// This Spawner can be used from any thread (it implements Send and Sync, so after any task (Send and non-Send ones), but it can 115/// This Spawner can be used from any thread (it is Send), but it can
97/// only be used in the executor thread (it is not Send itself). 116/// only spawn Send tasks. The reason for this is spawning is effectively
117/// "sending" the tasks to the executor thread.
98/// 118///
99/// If you want to spawn tasks from another thread, use [SendSpawner]. 119/// If you want to spawn non-Send tasks, use [Spawner].
100#[derive(Copy, Clone)] 120#[derive(Copy, Clone)]
101pub struct SendSpawner { 121pub struct SendSpawner {
102 executor: &'static raw::Executor, 122 executor: &'static raw::Executor,
@@ -106,13 +126,10 @@ pub struct SendSpawner {
106unsafe impl Send for SendSpawner {} 126unsafe impl Send for SendSpawner {}
107unsafe impl Sync for SendSpawner {} 127unsafe impl Sync for SendSpawner {}
108 128
109/// Handle to spawn tasks to an executor.
110///
111/// This Spawner can spawn any task (Send and non-Send ones), but it can
112/// only be used in the executor thread (it is not Send itself).
113///
114/// If you want to spawn tasks from another thread, use [SendSpawner].
115impl SendSpawner { 129impl SendSpawner {
130 /// Spawn a task into an executor.
131 ///
132 /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy::task]).
116 pub fn spawn<F: Send>(&self, token: SpawnToken<F>) -> Result<(), SpawnError> { 133 pub fn spawn<F: Send>(&self, token: SpawnToken<F>) -> Result<(), SpawnError> {
117 let header = token.raw_task; 134 let header = token.raw_task;
118 mem::forget(token); 135 mem::forget(token);
diff --git a/embassy/src/time/driver.rs b/embassy/src/time/driver.rs
index 1b8949ae1..a21a29d46 100644
--- a/embassy/src/time/driver.rs
+++ b/embassy/src/time/driver.rs
@@ -95,12 +95,15 @@ pub trait Driver: Send + Sync + 'static {
95 /// The callback may be called from any context (interrupt or thread mode). 95 /// The callback may be called from any context (interrupt or thread mode).
96 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); 96 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ());
97 97
98 /// Sets an alarm at the given timestamp. When the current timestamp reaches that 98 /// Sets an alarm at the given timestamp. When the current timestamp reaches the alarm
99 /// timestamp, the provided callback funcion will be called. 99 /// timestamp, the provided callback funcion will be called.
100 /// 100 ///
101 /// If `timestamp` is already in the past, the alarm callback must be immediately fired.
102 /// In this case, it is allowed (but not mandatory) to call the alarm callback synchronously from `set_alarm`.
103 ///
101 /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp. 104 /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp.
102 /// 105 ///
103 /// Only one alarm can be active at a time. This overwrites any previously-set alarm if any. 106 /// Only one alarm can be active at a time for each AlarmHandle. This overwrites any previously-set alarm if any.
104 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64); 107 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64);
105} 108}
106 109
diff --git a/embassy/src/time/mod.rs b/embassy/src/time/mod.rs
index e50742040..6ce18d475 100644
--- a/embassy/src/time/mod.rs
+++ b/embassy/src/time/mod.rs
@@ -40,6 +40,8 @@
40//! 40//!
41//! For more details, check the [`driver`] module. 41//! For more details, check the [`driver`] module.
42 42
43#![deny(missing_docs)]
44
43mod delay; 45mod delay;
44pub mod driver; 46pub mod driver;
45mod duration; 47mod duration;
diff --git a/examples/nrf/src/bin/raw_spawn.rs b/examples/nrf/src/bin/raw_spawn.rs
index 326dd9aac..d0bd68674 100644
--- a/examples/nrf/src/bin/raw_spawn.rs
+++ b/examples/nrf/src/bin/raw_spawn.rs
@@ -8,7 +8,7 @@ use example_common::*;
8use core::mem; 8use core::mem;
9use cortex_m_rt::entry; 9use cortex_m_rt::entry;
10 10
11use embassy::executor::raw::Task; 11use embassy::executor::raw::TaskStorage;
12use embassy::executor::Executor; 12use embassy::executor::Executor;
13use embassy::time::{Duration, Timer}; 13use embassy::time::{Duration, Timer};
14use embassy::util::Forever; 14use embassy::util::Forever;
@@ -36,8 +36,8 @@ fn main() -> ! {
36 let _p = embassy_nrf::init(Default::default()); 36 let _p = embassy_nrf::init(Default::default());
37 let executor = EXECUTOR.put(Executor::new()); 37 let executor = EXECUTOR.put(Executor::new());
38 38
39 let run1_task = Task::new(); 39 let run1_task = TaskStorage::new();
40 let run2_task = Task::new(); 40 let run2_task = TaskStorage::new();
41 41
42 // Safety: these variables do live forever if main never returns. 42 // Safety: these variables do live forever if main never returns.
43 let run1_task = unsafe { make_static(&run1_task) }; 43 let run1_task = unsafe { make_static(&run1_task) };