aboutsummaryrefslogtreecommitdiff
path: root/embassy-executor/src/executor/spawner.rs
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2022-08-17 23:40:16 +0200
committerDario Nieuwenhuis <[email protected]>2022-08-18 01:22:30 +0200
commit5daa173ce4b153a532b4daa9e94c7a248231f25b (patch)
tree2ef0b4d6f9b1c02dac2589e7b57982c20cbc0e66 /embassy-executor/src/executor/spawner.rs
parent1c5b54a4823d596db730eb476c3ab78110557214 (diff)
Split embassy-time from embassy-executor.
Diffstat (limited to 'embassy-executor/src/executor/spawner.rs')
-rw-r--r--embassy-executor/src/executor/spawner.rs202
1 files changed, 0 insertions, 202 deletions
diff --git a/embassy-executor/src/executor/spawner.rs b/embassy-executor/src/executor/spawner.rs
deleted file mode 100644
index 25a0d7dbb..000000000
--- a/embassy-executor/src/executor/spawner.rs
+++ /dev/null
@@ -1,202 +0,0 @@
1use core::marker::PhantomData;
2use core::mem;
3use core::ptr::NonNull;
4use core::task::Poll;
5
6use futures_util::future::poll_fn;
7
8use super::raw;
9
10/// Token to spawn a newly-created task in an executor.
11///
12/// When calling a task function (like `#[embassy_executor::task] async fn my_task() { ... }`), the returned
13/// value is a `SpawnToken` that represents an instance of the task, ready to spawn. You must
14/// then spawn it into an executor, typically with [`Spawner::spawn()`].
15///
16/// The generic parameter `S` determines whether the task can be spawned in executors
17/// in other threads or not. If `S: Send`, it can, which allows spawning it into a [`SendSpawner`].
18/// If not, it can't, so it can only be spawned into the current thread's executor, with [`Spawner`].
19///
20/// # Panics
21///
22/// Dropping a SpawnToken instance panics. You may not "abort" spawning a task in this way.
23/// Once you've invoked a task function and obtained a SpawnToken, you *must* spawn it.
24#[must_use = "Calling a task function does nothing on its own. You must spawn the returned SpawnToken, typically with Spawner::spawn()"]
25pub struct SpawnToken<S> {
26 raw_task: Option<NonNull<raw::TaskHeader>>,
27 phantom: PhantomData<*mut S>,
28}
29
30impl<S> SpawnToken<S> {
31 pub(crate) unsafe fn new(raw_task: NonNull<raw::TaskHeader>) -> Self {
32 Self {
33 raw_task: Some(raw_task),
34 phantom: PhantomData,
35 }
36 }
37
38 pub(crate) fn new_failed() -> Self {
39 Self {
40 raw_task: None,
41 phantom: PhantomData,
42 }
43 }
44}
45
46impl<S> Drop for SpawnToken<S> {
47 fn drop(&mut self) {
48 // TODO deallocate the task instead.
49 panic!("SpawnToken instances may not be dropped. You must pass them to Spawner::spawn()")
50 }
51}
52
53/// Error returned when spawning a task.
54#[derive(Copy, Clone, Debug)]
55#[cfg_attr(feature = "defmt", derive(defmt::Format))]
56pub enum SpawnError {
57 /// Too many instances of this task are already running.
58 ///
59 /// By default, a task marked with `#[embassy_executor::task]` can only have one instance
60 /// running at a time. You may allow multiple instances to run in parallel with
61 /// `#[embassy_executor::task(pool_size = 4)]`, at the cost of higher RAM usage.
62 Busy,
63}
64
65/// Handle to spawn tasks into an executor.
66///
67/// This Spawner can spawn any task (Send and non-Send ones), but it can
68/// only be used in the executor thread (it is not Send itself).
69///
70/// If you want to spawn tasks from another thread, use [SendSpawner].
71#[derive(Copy, Clone)]
72pub struct Spawner {
73 executor: &'static raw::Executor,
74 not_send: PhantomData<*mut ()>,
75}
76
77impl Spawner {
78 pub(crate) fn new(executor: &'static raw::Executor) -> Self {
79 Self {
80 executor,
81 not_send: PhantomData,
82 }
83 }
84
85 /// Get a Spawner for the current executor.
86 ///
87 /// This function is `async` just to get access to the current async
88 /// context. It returns instantly, it does not block/yield.
89 ///
90 /// # Panics
91 ///
92 /// Panics if the current executor is not an Embassy executor.
93 pub async fn for_current_executor() -> Self {
94 poll_fn(|cx| unsafe {
95 let task = raw::task_from_waker(cx.waker());
96 let executor = (*task.as_ptr()).executor.get();
97 Poll::Ready(Self::new(&*executor))
98 })
99 .await
100 }
101
102 /// Spawn a task into an executor.
103 ///
104 /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy_executor::task]`).
105 pub fn spawn<S>(&self, token: SpawnToken<S>) -> Result<(), SpawnError> {
106 let task = token.raw_task;
107 mem::forget(token);
108
109 match task {
110 Some(task) => {
111 unsafe { self.executor.spawn(task) };
112 Ok(())
113 }
114 None => Err(SpawnError::Busy),
115 }
116 }
117
118 // Used by the `embassy_macros::main!` macro to throw an error when spawn
119 // fails. This is here to allow conditional use of `defmt::unwrap!`
120 // without introducing a `defmt` feature in the `embassy_macros` package,
121 // which would require use of `-Z namespaced-features`.
122 /// Spawn a task into an executor, panicking on failure.
123 ///
124 /// # Panics
125 ///
126 /// Panics if the spawning fails.
127 pub fn must_spawn<S>(&self, token: SpawnToken<S>) {
128 unwrap!(self.spawn(token));
129 }
130
131 /// Convert this Spawner to a SendSpawner. This allows you to send the
132 /// spawner to other threads, but the spawner loses the ability to spawn
133 /// non-Send tasks.
134 pub fn make_send(&self) -> SendSpawner {
135 SendSpawner {
136 executor: self.executor,
137 }
138 }
139}
140
141/// Handle to spawn tasks into an executor from any thread.
142///
143/// This Spawner can be used from any thread (it is Send), but it can
144/// only spawn Send tasks. The reason for this is spawning is effectively
145/// "sending" the tasks to the executor thread.
146///
147/// If you want to spawn non-Send tasks, use [Spawner].
148#[derive(Copy, Clone)]
149pub struct SendSpawner {
150 executor: &'static raw::Executor,
151}
152
153unsafe impl Send for SendSpawner {}
154unsafe impl Sync for SendSpawner {}
155
156impl SendSpawner {
157 pub(crate) fn new(executor: &'static raw::Executor) -> Self {
158 Self { executor }
159 }
160
161 /// Get a Spawner for the current executor.
162 ///
163 /// This function is `async` just to get access to the current async
164 /// context. It returns instantly, it does not block/yield.
165 ///
166 /// # Panics
167 ///
168 /// Panics if the current executor is not an Embassy executor.
169 pub async fn for_current_executor() -> Self {
170 poll_fn(|cx| unsafe {
171 let task = raw::task_from_waker(cx.waker());
172 let executor = (*task.as_ptr()).executor.get();
173 Poll::Ready(Self::new(&*executor))
174 })
175 .await
176 }
177
178 /// Spawn a task into an executor.
179 ///
180 /// You obtain the `token` by calling a task function (i.e. one marked with `#[embassy_executor::task]`).
181 pub fn spawn<S: Send>(&self, token: SpawnToken<S>) -> Result<(), SpawnError> {
182 let header = token.raw_task;
183 mem::forget(token);
184
185 match header {
186 Some(header) => {
187 unsafe { self.executor.spawn(header) };
188 Ok(())
189 }
190 None => Err(SpawnError::Busy),
191 }
192 }
193
194 /// Spawn a task into an executor, panicking on failure.
195 ///
196 /// # Panics
197 ///
198 /// Panics if the spawning fails.
199 pub fn must_spawn<S: Send>(&self, token: SpawnToken<S>) {
200 unwrap!(self.spawn(token));
201 }
202}