aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2021-08-25 00:27:21 +0200
committerGitHub <[email protected]>2021-08-25 00:27:21 +0200
commit09ffdf63f18e263962a8ea9f86c1caf4360cacee (patch)
treeca42ee12d495e965e42bf4bd90cc1b1a442e00db
parent354ec75b74e9f64c6d2fb4f7ec86b2e92b8d0009 (diff)
parentae179d49af2c2758d5e6d9aab9ffd7ecd5ab70ae (diff)
Merge pull request #372 from embassy-rs/executor-structure
executor: improve module structure
-rw-r--r--embassy/src/executor/arch/arm.rs76
-rw-r--r--embassy/src/executor/mod.rs186
-rw-r--r--embassy/src/executor/raw/mod.rs (renamed from embassy/src/executor/raw.rs)47
-rw-r--r--embassy/src/executor/raw/run_queue.rs (renamed from embassy/src/executor/run_queue.rs)2
-rw-r--r--embassy/src/executor/raw/timer_queue.rs (renamed from embassy/src/executor/timer_queue.rs)2
-rw-r--r--embassy/src/executor/raw/util.rs (renamed from embassy/src/executor/util.rs)0
-rw-r--r--embassy/src/executor/raw/waker.rs (renamed from embassy/src/executor/waker.rs)2
-rw-r--r--embassy/src/executor/spawner.rs128
8 files changed, 230 insertions, 213 deletions
diff --git a/embassy/src/executor/arch/arm.rs b/embassy/src/executor/arch/arm.rs
new file mode 100644
index 000000000..4fd734cd7
--- /dev/null
+++ b/embassy/src/executor/arch/arm.rs
@@ -0,0 +1,76 @@
1use core::marker::PhantomData;
2use core::ptr;
3
4use super::{raw, Spawner};
5use crate::interrupt::{Interrupt, InterruptExt};
6
7pub struct Executor {
8 inner: raw::Executor,
9 not_send: PhantomData<*mut ()>,
10}
11
12impl Executor {
13 pub fn new() -> Self {
14 Self {
15 inner: raw::Executor::new(|_| cortex_m::asm::sev(), ptr::null_mut()),
16 not_send: PhantomData,
17 }
18 }
19
20 /// Runs the executor.
21 ///
22 /// This function never returns.
23 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
24 init(unsafe { self.inner.spawner() });
25
26 loop {
27 unsafe { self.inner.run_queued() };
28 cortex_m::asm::wfe();
29 }
30 }
31}
32
33fn pend_by_number(n: u16) {
34 #[derive(Clone, Copy)]
35 struct N(u16);
36 unsafe impl cortex_m::interrupt::InterruptNumber for N {
37 fn number(self) -> u16 {
38 self.0
39 }
40 }
41 cortex_m::peripheral::NVIC::pend(N(n))
42}
43
44pub struct InterruptExecutor<I: Interrupt> {
45 irq: I,
46 inner: raw::Executor,
47 not_send: PhantomData<*mut ()>,
48}
49
50impl<I: Interrupt> InterruptExecutor<I> {
51 pub fn new(irq: I) -> Self {
52 let ctx = irq.number() as *mut ();
53 Self {
54 irq,
55 inner: raw::Executor::new(|ctx| pend_by_number(ctx as u16), ctx),
56 not_send: PhantomData,
57 }
58 }
59
60 /// Start the executor.
61 ///
62 /// `init` is called in the interrupt context, then the interrupt is
63 /// configured to run the executor.
64 pub fn start(&'static mut self, init: impl FnOnce(Spawner) + Send) {
65 self.irq.disable();
66
67 init(unsafe { self.inner.spawner() });
68
69 self.irq.set_handler(|ctx| unsafe {
70 let executor = &*(ctx as *const raw::Executor);
71 executor.run_queued();
72 });
73 self.irq.set_handler_context(&self.inner as *const _ as _);
74 self.irq.enable();
75 }
76}
diff --git a/embassy/src/executor/mod.rs b/embassy/src/executor/mod.rs
index f3c877290..e0ac566f1 100644
--- a/embassy/src/executor/mod.rs
+++ b/embassy/src/executor/mod.rs
@@ -1,183 +1,7 @@
1use core::marker::PhantomData; 1#[path = "arch/arm.rs"]
2use core::ptr::NonNull; 2mod arch;
3use core::{mem, ptr};
4
5pub mod raw; 3pub mod raw;
6mod run_queue; 4mod spawner;
7#[cfg(feature = "time")]
8mod timer_queue;
9mod util;
10mod waker;
11
12use crate::interrupt::{Interrupt, InterruptExt};
13
14#[must_use = "Calling a task function does nothing on its own. You must pass the returned SpawnToken to Executor::spawn()"]
15pub struct SpawnToken<F> {
16 raw_task: Option<NonNull<raw::TaskHeader>>,
17 phantom: PhantomData<*mut F>,
18}
19
20impl<F> Drop for SpawnToken<F> {
21 fn drop(&mut self) {
22 // TODO deallocate the task instead.
23 panic!("SpawnToken instances may not be dropped. You must pass them to Executor::spawn()")
24 }
25}
26
27#[derive(Copy, Clone, Debug)]
28#[cfg_attr(feature = "defmt", derive(defmt::Format))]
29pub enum SpawnError {
30 Busy,
31}
32
33/// Handle to spawn tasks into an executor.
34///
35/// This Spawner can spawn any task (Send and non-Send ones), but it can
36/// only be used in the executor thread (it is not Send itself).
37///
38/// If you want to spawn tasks from another thread, use [SendSpawner].
39#[derive(Copy, Clone)]
40pub struct Spawner {
41 executor: &'static raw::Executor,
42 not_send: PhantomData<*mut ()>,
43}
44
45impl Spawner {
46 pub fn spawn<F>(&self, token: SpawnToken<F>) -> Result<(), SpawnError> {
47 let task = token.raw_task;
48 mem::forget(token);
49
50 match task {
51 Some(task) => {
52 unsafe { self.executor.spawn(task) };
53 Ok(())
54 }
55 None => Err(SpawnError::Busy),
56 }
57 }
58
59 /// Used by the `embassy_macros::main!` macro to throw an error when spawn
60 /// fails. This is here to allow conditional use of `defmt::unwrap!`
61 /// without introducing a `defmt` feature in the `embassy_macros` package,
62 /// which would require use of `-Z namespaced-features`.
63 pub fn must_spawn<F>(&self, token: SpawnToken<F>) -> () {
64 unwrap!(self.spawn(token));
65 }
66
67 /// Convert this Spawner to a SendSpawner. This allows you to send the
68 /// spawner to other threads, but the spawner loses the ability to spawn
69 /// non-Send tasks.
70 pub fn make_send(&self) -> SendSpawner {
71 SendSpawner {
72 executor: self.executor,
73 not_send: PhantomData,
74 }
75 }
76}
77
78/// Handle to spawn tasks into an executor from any thread.
79///
80/// 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
81/// only be used in the executor thread (it is not Send itself).
82///
83/// If you want to spawn tasks from another thread, use [SendSpawner].
84#[derive(Copy, Clone)]
85pub struct SendSpawner {
86 executor: &'static raw::Executor,
87 not_send: PhantomData<*mut ()>,
88}
89
90unsafe impl Send for SendSpawner {}
91unsafe impl Sync for SendSpawner {}
92
93/// Handle to spawn tasks to an executor.
94///
95/// This Spawner can spawn any task (Send and non-Send ones), but it can
96/// only be used in the executor thread (it is not Send itself).
97///
98/// If you want to spawn tasks from another thread, use [SendSpawner].
99impl SendSpawner {
100 pub fn spawn<F: Send>(&self, token: SpawnToken<F>) -> Result<(), SpawnError> {
101 let header = token.raw_task;
102 mem::forget(token);
103
104 match header {
105 Some(header) => {
106 unsafe { self.executor.spawn(header) };
107 Ok(())
108 }
109 None => Err(SpawnError::Busy),
110 }
111 }
112}
113
114pub struct Executor {
115 inner: raw::Executor,
116 not_send: PhantomData<*mut ()>,
117}
118
119impl Executor {
120 pub fn new() -> Self {
121 Self {
122 inner: raw::Executor::new(|_| cortex_m::asm::sev(), ptr::null_mut()),
123 not_send: PhantomData,
124 }
125 }
126
127 /// Runs the executor.
128 ///
129 /// This function never returns.
130 pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
131 init(unsafe { self.inner.spawner() });
132
133 loop {
134 unsafe { self.inner.run_queued() };
135 cortex_m::asm::wfe();
136 }
137 }
138}
139
140fn pend_by_number(n: u16) {
141 #[derive(Clone, Copy)]
142 struct N(u16);
143 unsafe impl cortex_m::interrupt::InterruptNumber for N {
144 fn number(self) -> u16 {
145 self.0
146 }
147 }
148 cortex_m::peripheral::NVIC::pend(N(n))
149}
150
151pub struct InterruptExecutor<I: Interrupt> {
152 irq: I,
153 inner: raw::Executor,
154 not_send: PhantomData<*mut ()>,
155}
156
157impl<I: Interrupt> InterruptExecutor<I> {
158 pub fn new(irq: I) -> Self {
159 let ctx = irq.number() as *mut ();
160 Self {
161 irq,
162 inner: raw::Executor::new(|ctx| pend_by_number(ctx as u16), ctx),
163 not_send: PhantomData,
164 }
165 }
166
167 /// Start the executor.
168 ///
169 /// `init` is called in the interrupt context, then the interrupt is
170 /// configured to run the executor.
171 pub fn start(&'static mut self, init: impl FnOnce(Spawner) + Send) {
172 self.irq.disable();
173
174 init(unsafe { self.inner.spawner() });
175 5
176 self.irq.set_handler(|ctx| unsafe { 6pub use arch::*;
177 let executor = &*(ctx as *const raw::Executor); 7pub use spawner::*;
178 executor.run_queued();
179 });
180 self.irq.set_handler_context(&self.inner as *const _ as _);
181 self.irq.enable();
182 }
183}
diff --git a/embassy/src/executor/raw.rs b/embassy/src/executor/raw/mod.rs
index fac46d1f4..235a09198 100644
--- a/embassy/src/executor/raw.rs
+++ b/embassy/src/executor/raw/mod.rs
@@ -1,24 +1,27 @@
1mod run_queue;
2#[cfg(feature = "time")]
3mod timer_queue;
4mod util;
5mod waker;
6
1use atomic_polyfill::{AtomicU32, Ordering}; 7use atomic_polyfill::{AtomicU32, Ordering};
2use core::cell::Cell; 8use core::cell::Cell;
3use core::future::Future; 9use core::future::Future;
4use core::marker::PhantomData;
5use core::pin::Pin; 10use core::pin::Pin;
6use core::ptr::NonNull; 11use core::ptr::NonNull;
7use core::task::{Context, Poll}; 12use core::task::{Context, Poll};
8use core::{mem, ptr}; 13use core::{mem, ptr};
9 14
10use super::run_queue::{RunQueue, RunQueueItem}; 15use self::run_queue::{RunQueue, RunQueueItem};
11use super::util::UninitCell; 16use self::util::UninitCell;
12use super::waker;
13use super::SpawnToken; 17use super::SpawnToken;
14
15#[cfg(feature = "time")]
16use super::timer_queue::{TimerQueue, TimerQueueItem};
17#[cfg(feature = "time")] 18#[cfg(feature = "time")]
18use crate::time::driver::{self, AlarmHandle}; 19use crate::time::driver::{self, AlarmHandle};
19#[cfg(feature = "time")] 20#[cfg(feature = "time")]
20use crate::time::Instant; 21use crate::time::Instant;
21 22
23pub use self::waker::task_from_waker;
24
22/// Task is spawned (has a future) 25/// Task is spawned (has a future)
23pub(crate) const STATE_SPAWNED: u32 = 1 << 0; 26pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
24/// Task is in the executor run queue 27/// Task is in the executor run queue
@@ -36,7 +39,7 @@ pub struct TaskHeader {
36 #[cfg(feature = "time")] 39 #[cfg(feature = "time")]
37 pub(crate) expires_at: Cell<Instant>, 40 pub(crate) expires_at: Cell<Instant>,
38 #[cfg(feature = "time")] 41 #[cfg(feature = "time")]
39 pub(crate) timer_queue_item: TimerQueueItem, 42 pub(crate) timer_queue_item: timer_queue::TimerQueueItem,
40} 43}
41 44
42impl TaskHeader { 45impl TaskHeader {
@@ -50,7 +53,7 @@ impl TaskHeader {
50 #[cfg(feature = "time")] 53 #[cfg(feature = "time")]
51 expires_at: Cell::new(Instant::from_ticks(0)), 54 expires_at: Cell::new(Instant::from_ticks(0)),
52 #[cfg(feature = "time")] 55 #[cfg(feature = "time")]
53 timer_queue_item: TimerQueueItem::new(), 56 timer_queue_item: timer_queue::TimerQueueItem::new(),
54 } 57 }
55 } 58 }
56 59
@@ -105,20 +108,14 @@ impl<F: Future + 'static> Task<F> {
105 } 108 }
106 } 109 }
107 110
108 SpawnToken { 111 SpawnToken::new_failed()
109 raw_task: None,
110 phantom: PhantomData,
111 }
112 } 112 }
113 113
114 pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<F> { 114 pub fn spawn(&'static self, future: impl FnOnce() -> F) -> SpawnToken<F> {
115 if self.spawn_allocate() { 115 if self.spawn_allocate() {
116 unsafe { self.spawn_initialize(future) } 116 unsafe { self.spawn_initialize(future) }
117 } else { 117 } else {
118 SpawnToken { 118 SpawnToken::new_failed()
119 raw_task: None,
120 phantom: PhantomData,
121 }
122 } 119 }
123 } 120 }
124 121
@@ -135,10 +132,7 @@ impl<F: Future + 'static> Task<F> {
135 self.raw.poll_fn.write(Self::poll); 132 self.raw.poll_fn.write(Self::poll);
136 self.future.write(future()); 133 self.future.write(future());
137 134
138 return SpawnToken { 135 SpawnToken::new(NonNull::new_unchecked(&self.raw as *const TaskHeader as _))
139 raw_task: Some(NonNull::new_unchecked(&self.raw as *const TaskHeader as _)),
140 phantom: PhantomData,
141 };
142 } 136 }
143 137
144 unsafe fn poll(p: NonNull<TaskHeader>) { 138 unsafe fn poll(p: NonNull<TaskHeader>) {
@@ -169,7 +163,7 @@ pub struct Executor {
169 signal_ctx: *mut (), 163 signal_ctx: *mut (),
170 164
171 #[cfg(feature = "time")] 165 #[cfg(feature = "time")]
172 timer_queue: TimerQueue, 166 pub(crate) timer_queue: timer_queue::TimerQueue,
173 #[cfg(feature = "time")] 167 #[cfg(feature = "time")]
174 alarm: AlarmHandle, 168 alarm: AlarmHandle,
175} 169}
@@ -187,7 +181,7 @@ impl Executor {
187 signal_ctx, 181 signal_ctx,
188 182
189 #[cfg(feature = "time")] 183 #[cfg(feature = "time")]
190 timer_queue: TimerQueue::new(), 184 timer_queue: timer_queue::TimerQueue::new(),
191 #[cfg(feature = "time")] 185 #[cfg(feature = "time")]
192 alarm, 186 alarm,
193 } 187 }
@@ -249,15 +243,10 @@ impl Executor {
249 } 243 }
250 244
251 pub unsafe fn spawner(&'static self) -> super::Spawner { 245 pub unsafe fn spawner(&'static self) -> super::Spawner {
252 super::Spawner { 246 super::Spawner::new(self)
253 executor: self,
254 not_send: PhantomData,
255 }
256 } 247 }
257} 248}
258 249
259pub use super::waker::task_from_waker;
260
261pub unsafe fn wake_task(task: NonNull<TaskHeader>) { 250pub unsafe fn wake_task(task: NonNull<TaskHeader>) {
262 task.as_ref().enqueue(); 251 task.as_ref().enqueue();
263} 252}
diff --git a/embassy/src/executor/run_queue.rs b/embassy/src/executor/raw/run_queue.rs
index 083916139..8e8bc8ff3 100644
--- a/embassy/src/executor/run_queue.rs
+++ b/embassy/src/executor/raw/run_queue.rs
@@ -2,7 +2,7 @@ use atomic_polyfill::{AtomicPtr, Ordering};
2use core::ptr; 2use core::ptr;
3use core::ptr::NonNull; 3use core::ptr::NonNull;
4 4
5use super::raw::TaskHeader; 5use super::TaskHeader;
6 6
7pub(crate) struct RunQueueItem { 7pub(crate) struct RunQueueItem {
8 next: AtomicPtr<TaskHeader>, 8 next: AtomicPtr<TaskHeader>,
diff --git a/embassy/src/executor/timer_queue.rs b/embassy/src/executor/raw/timer_queue.rs
index 76bc27ad4..e96910bb0 100644
--- a/embassy/src/executor/timer_queue.rs
+++ b/embassy/src/executor/raw/timer_queue.rs
@@ -4,7 +4,7 @@ use core::cmp::min;
4use core::ptr; 4use core::ptr;
5use core::ptr::NonNull; 5use core::ptr::NonNull;
6 6
7use super::raw::{TaskHeader, STATE_TIMER_QUEUED}; 7use super::{TaskHeader, STATE_TIMER_QUEUED};
8use crate::time::Instant; 8use crate::time::Instant;
9 9
10pub(crate) struct TimerQueueItem { 10pub(crate) struct TimerQueueItem {
diff --git a/embassy/src/executor/util.rs b/embassy/src/executor/raw/util.rs
index ca15b6955..ca15b6955 100644
--- a/embassy/src/executor/util.rs
+++ b/embassy/src/executor/raw/util.rs
diff --git a/embassy/src/executor/waker.rs b/embassy/src/executor/raw/waker.rs
index ea5b501f1..e53190f17 100644
--- a/embassy/src/executor/waker.rs
+++ b/embassy/src/executor/raw/waker.rs
@@ -2,7 +2,7 @@ use core::mem;
2use core::ptr::NonNull; 2use core::ptr::NonNull;
3use core::task::{RawWaker, RawWakerVTable, Waker}; 3use core::task::{RawWaker, RawWakerVTable, Waker};
4 4
5use super::raw::TaskHeader; 5use super::TaskHeader;
6 6
7const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop); 7const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop);
8 8
diff --git a/embassy/src/executor/spawner.rs b/embassy/src/executor/spawner.rs
new file mode 100644
index 000000000..36100aecb
--- /dev/null
+++ b/embassy/src/executor/spawner.rs
@@ -0,0 +1,128 @@
1use core::marker::PhantomData;
2use core::mem;
3use core::ptr::NonNull;
4
5use super::raw;
6
7#[must_use = "Calling a task function does nothing on its own. You must pass the returned SpawnToken to Executor::spawn()"]
8pub struct SpawnToken<F> {
9 raw_task: Option<NonNull<raw::TaskHeader>>,
10 phantom: PhantomData<*mut F>,
11}
12
13impl<F> SpawnToken<F> {
14 pub(crate) unsafe fn new(raw_task: NonNull<raw::TaskHeader>) -> Self {
15 Self {
16 raw_task: Some(raw_task),
17 phantom: PhantomData,
18 }
19 }
20
21 pub(crate) fn new_failed() -> Self {
22 Self {
23 raw_task: None,
24 phantom: PhantomData,
25 }
26 }
27}
28
29impl<F> Drop for SpawnToken<F> {
30 fn drop(&mut self) {
31 // TODO deallocate the task instead.
32 panic!("SpawnToken instances may not be dropped. You must pass them to Executor::spawn()")
33 }
34}
35
36#[derive(Copy, Clone, Debug)]
37#[cfg_attr(feature = "defmt", derive(defmt::Format))]
38pub enum SpawnError {
39 Busy,
40}
41
42/// Handle to spawn tasks into an executor.
43///
44/// This Spawner can spawn any task (Send and non-Send ones), but it can
45/// only be used in the executor thread (it is not Send itself).
46///
47/// If you want to spawn tasks from another thread, use [SendSpawner].
48#[derive(Copy, Clone)]
49pub struct Spawner {
50 executor: &'static raw::Executor,
51 not_send: PhantomData<*mut ()>,
52}
53
54impl Spawner {
55 pub(crate) unsafe fn new(executor: &'static raw::Executor) -> Self {
56 Self {
57 executor,
58 not_send: PhantomData,
59 }
60 }
61
62 pub fn spawn<F>(&self, token: SpawnToken<F>) -> Result<(), SpawnError> {
63 let task = token.raw_task;
64 mem::forget(token);
65
66 match task {
67 Some(task) => {
68 unsafe { self.executor.spawn(task) };
69 Ok(())
70 }
71 None => Err(SpawnError::Busy),
72 }
73 }
74
75 /// Used by the `embassy_macros::main!` macro to throw an error when spawn
76 /// fails. This is here to allow conditional use of `defmt::unwrap!`
77 /// without introducing a `defmt` feature in the `embassy_macros` package,
78 /// which would require use of `-Z namespaced-features`.
79 pub fn must_spawn<F>(&self, token: SpawnToken<F>) -> () {
80 unwrap!(self.spawn(token));
81 }
82
83 /// Convert this Spawner to a SendSpawner. This allows you to send the
84 /// spawner to other threads, but the spawner loses the ability to spawn
85 /// non-Send tasks.
86 pub fn make_send(&self) -> SendSpawner {
87 SendSpawner {
88 executor: self.executor,
89 not_send: PhantomData,
90 }
91 }
92}
93
94/// Handle to spawn tasks into an executor from any thread.
95///
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
97/// only be used in the executor thread (it is not Send itself).
98///
99/// If you want to spawn tasks from another thread, use [SendSpawner].
100#[derive(Copy, Clone)]
101pub struct SendSpawner {
102 executor: &'static raw::Executor,
103 not_send: PhantomData<*mut ()>,
104}
105
106unsafe impl Send for SendSpawner {}
107unsafe impl Sync for SendSpawner {}
108
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 {
116 pub fn spawn<F: Send>(&self, token: SpawnToken<F>) -> Result<(), SpawnError> {
117 let header = token.raw_task;
118 mem::forget(token);
119
120 match header {
121 Some(header) => {
122 unsafe { self.executor.spawn(header) };
123 Ok(())
124 }
125 None => Err(SpawnError::Busy),
126 }
127 }
128}