edge_executor/lib.rs
1#![no_std]
2
3use core::future::{poll_fn, Future};
4use core::marker::PhantomData;
5use core::task::{Context, Poll};
6
7use alloc::rc::Rc;
8
9use async_task::Runnable;
10
11pub use async_task::{FallibleTask, Task};
12
13use atomic_waker::AtomicWaker;
14use futures_lite::FutureExt;
15
16#[cfg(not(feature = "portable-atomic"))]
17use alloc::sync::Arc;
18#[cfg(feature = "portable-atomic")]
19use portable_atomic_util::Arc;
20
21use once_cell::sync::OnceCell;
22
23#[cfg(feature = "std")]
24pub use futures_lite::future::block_on;
25
26pub use queue::*;
27
28extern crate alloc;
29
30mod queue;
31
32/// An async executor.
33///
34/// # Examples
35///
36/// A multi-threaded executor:
37///
38/// ```ignore
39/// use async_channel::unbounded;
40/// use easy_parallel::Parallel;
41///
42/// use edge_executor::{Executor, block_on};
43///
44/// let ex: Executor = Default::default();
45/// let (signal, shutdown) = unbounded::<()>();
46///
47/// Parallel::new()
48/// // Run four executor threads.
49/// .each(0..4, |_| block_on(ex.run(shutdown.recv())))
50/// // Run the main future on the current thread.
51/// .finish(|| block_on(async {
52/// println!("Hello world!");
53/// drop(signal);
54/// }));
55/// ```
56pub struct Executor<'a, Q = BoundQueue> {
57 state: OnceCell<Arc<State<Q>>>,
58 queue_ctor: fn() -> Q,
59 _marker: PhantomData<core::cell::UnsafeCell<&'a ()>>,
60}
61
62impl<'a, Q: ExecutorQueue> Executor<'a, Q> {
63 /// Creates a new executor.
64 ///
65 /// # Examples
66 ///
67 /// ```
68 /// use edge_executor::Executor;
69 ///
70 /// let ex: Executor = Default::default();
71 /// ```
72 pub const fn new() -> Self {
73 Self::new_with(Q::new)
74 }
75
76 /// Creates a new executor with the provided queue constructor.
77 ///
78 /// # Examples
79 ///
80 /// ```
81 /// use edge_executor::{Executor, UnboundQueue};
82 ///
83 /// let ex = Executor::new_with(|| { let queue: UnboundQueue = UnboundQueue::with_capacity(100); queue });
84 /// ```
85 pub const fn new_with(queue_ctor: fn() -> Q) -> Self {
86 Self {
87 state: OnceCell::new(),
88 queue_ctor,
89 _marker: PhantomData,
90 }
91 }
92
93 /// Spawns a task onto the executor.
94 ///
95 /// # Examples
96 ///
97 /// ```
98 /// use edge_executor::Executor;
99 ///
100 /// let ex: Executor = Default::default();
101 ///
102 /// let task = ex.spawn(async {
103 /// println!("Hello world");
104 /// });
105 /// ```
106 ///
107 /// Note that if the executor's queue size is equal to the number of currently
108 /// spawned and running tasks, spawning this additional task might cause the executor to panic
109 /// later, when the task is scheduled for polling.
110 pub fn spawn<F>(&self, fut: F) -> Task<F::Output>
111 where
112 F: Future + Send + 'a,
113 F::Output: Send + 'a,
114 {
115 unsafe { self.spawn_unchecked(fut) }
116 }
117
118 /// Attempts to run a task if at least one is scheduled.
119 ///
120 /// Running a scheduled task means simply polling its future once.
121 ///
122 /// # Examples
123 ///
124 /// ```
125 /// use edge_executor::Executor;
126 ///
127 /// let ex: Executor = Default::default();
128 /// assert!(!ex.try_tick()); // no tasks to run
129 ///
130 /// let task = ex.spawn(async {
131 /// println!("Hello world");
132 /// });
133 /// assert!(ex.try_tick()); // a task was found
134 /// ```
135 pub fn try_tick(&self) -> bool {
136 if let Some(runnable) = self.try_runnable() {
137 runnable.run();
138
139 true
140 } else {
141 false
142 }
143 }
144
145 /// Runs a single task asynchronously.
146 ///
147 /// Running a task means simply polling its future once.
148 ///
149 /// If no tasks are scheduled when this method is called, it will wait until one is scheduled.
150 ///
151 /// # Examples
152 ///
153 /// ```
154 /// use edge_executor::{Executor, block_on};
155 ///
156 /// let ex: Executor = Default::default();
157 ///
158 /// let task = ex.spawn(async {
159 /// println!("Hello world");
160 /// });
161 /// block_on(ex.tick()); // runs the task
162 /// ```
163 pub async fn tick(&self) {
164 self.runnable().await.run();
165 }
166
167 /// Runs the executor asynchronously until the given future completes.
168 ///
169 /// # Examples
170 ///
171 /// ```
172 /// use edge_executor::{Executor, block_on};
173 ///
174 /// let ex: Executor = Default::default();
175 ///
176 /// let task = ex.spawn(async { 1 + 2 });
177 /// let res = block_on(ex.run(async { task.await * 2 }));
178 ///
179 /// assert_eq!(res, 6);
180 /// ```
181 pub async fn run<F>(&self, fut: F) -> F::Output
182 where
183 F: Future,
184 {
185 unsafe { self.run_unchecked(fut).await }
186 }
187
188 /// Waits for the next runnable task to run.
189 async fn runnable(&self) -> Runnable {
190 poll_fn(|ctx| self.poll_runnable(ctx)).await
191 }
192
193 /// Polls the first task scheduled for execution by the executor.
194 fn poll_runnable(&self, ctx: &Context<'_>) -> Poll<Runnable> {
195 self.state().waker.register(ctx.waker());
196
197 if let Some(runnable) = self.try_runnable() {
198 Poll::Ready(runnable)
199 } else {
200 Poll::Pending
201 }
202 }
203
204 /// Pops the first task scheduled for execution by the executor.
205 ///
206 /// Returns
207 /// - `None` - if no task was scheduled for execution
208 /// - `Some(Runnnable)` - the first task scheduled for execution. Calling `Runnable::run` will
209 /// execute the task. In other words, it will poll its future.
210 fn try_runnable(&self) -> Option<Runnable> {
211 self.state().queue.pop()
212 }
213
214 unsafe fn spawn_unchecked<F>(&self, fut: F) -> Task<F::Output>
215 where
216 F: Future,
217 {
218 let schedule = {
219 let state = self.state().clone();
220
221 move |runnable| {
222 state.queue.push(runnable);
223
224 if let Some(waker) = state.waker.take() {
225 waker.wake();
226 }
227 }
228 };
229
230 let (runnable, task) = unsafe { async_task::spawn_unchecked(fut, schedule) };
231
232 runnable.schedule();
233
234 task
235 }
236
237 async unsafe fn run_unchecked<F>(&self, fut: F) -> F::Output
238 where
239 F: Future,
240 {
241 let run_forever = async {
242 loop {
243 self.tick().await;
244 }
245 };
246
247 run_forever.or(fut).await
248 }
249
250 /// Returns a reference to the inner state.
251 fn state(&self) -> &Arc<State<Q>> {
252 self.state
253 .get_or_init(|| Arc::new(State::new((self.queue_ctor)())))
254 }
255}
256
257impl<Q: ExecutorQueue> Default for Executor<'_, Q> {
258 fn default() -> Self {
259 Self::new()
260 }
261}
262
263unsafe impl<Q: Send> Send for Executor<'_, Q> {}
264unsafe impl<Q: Sync> Sync for Executor<'_, Q> {}
265
266/// A thread-local executor.
267///
268/// The executor can only be run on the thread that created it.
269///
270/// # Examples
271///
272/// ```
273/// use edge_executor::{LocalExecutor, block_on};
274///
275/// let local_ex: LocalExecutor = Default::default();
276///
277/// block_on(local_ex.run(async {
278/// println!("Hello world!");
279/// }));
280/// ```
281pub struct LocalExecutor<'a, Q = BoundQueue> {
282 executor: Executor<'a, Q>,
283 _marker: PhantomData<Rc<()>>,
284}
285
286impl<'a, Q: ExecutorQueue> LocalExecutor<'a, Q> {
287 /// Creates a single-threaded executor.
288 ///
289 /// # Examples
290 ///
291 /// ```
292 /// use edge_executor::LocalExecutor;
293 ///
294 /// let local_ex: LocalExecutor = Default::default();
295 /// ```
296 pub const fn new() -> Self {
297 Self::new_with(Q::new)
298 }
299
300 /// Creates a single-threaded executor with the provided queue constructor.
301 ///
302 /// # Examples
303 ///
304 /// ```
305 /// use edge_executor::{LocalExecutor, UnboundQueue};
306 ///
307 /// let local_ex = LocalExecutor::new_with(|| { let queue: UnboundQueue = UnboundQueue::with_capacity(100); queue });
308 /// ```
309 pub const fn new_with(queue_ctor: fn() -> Q) -> Self {
310 Self {
311 executor: Executor::<Q>::new_with(queue_ctor),
312 _marker: PhantomData,
313 }
314 }
315
316 /// Spawns a task onto the executor.
317 ///
318 /// # Examples
319 ///
320 /// ```
321 /// use edge_executor::LocalExecutor;
322 ///
323 /// let local_ex: LocalExecutor = Default::default();
324 ///
325 /// let task = local_ex.spawn(async {
326 /// println!("Hello world");
327 /// });
328 /// ```
329 ///
330 /// Note that if the executor's queue size is equal to the number of currently
331 /// spawned and running tasks, spawning this additional task might cause the executor to panic
332 /// later, when the task is scheduled for polling.
333 pub fn spawn<F>(&self, fut: F) -> Task<F::Output>
334 where
335 F: Future + 'a,
336 F::Output: 'a,
337 {
338 unsafe { self.executor.spawn_unchecked(fut) }
339 }
340
341 /// Attempts to run a task if at least one is scheduled.
342 ///
343 /// Running a scheduled task means simply polling its future once.
344 ///
345 /// # Examples
346 ///
347 /// ```
348 /// use edge_executor::LocalExecutor;
349 ///
350 /// let local_ex: LocalExecutor = Default::default();
351 /// assert!(!local_ex.try_tick()); // no tasks to run
352 ///
353 /// let task = local_ex.spawn(async {
354 /// println!("Hello world");
355 /// });
356 /// assert!(local_ex.try_tick()); // a task was found
357 /// ```
358 pub fn try_tick(&self) -> bool {
359 self.executor.try_tick()
360 }
361
362 /// Runs a single task asynchronously.
363 ///
364 /// Running a task means simply polling its future once.
365 ///
366 /// If no tasks are scheduled when this method is called, it will wait until one is scheduled.
367 ///
368 /// # Examples
369 ///
370 /// ```
371 /// use edge_executor::{LocalExecutor, block_on};
372 ///
373 /// let local_ex: LocalExecutor = Default::default();
374 ///
375 /// let task = local_ex.spawn(async {
376 /// println!("Hello world");
377 /// });
378 /// block_on(local_ex.tick()); // runs the task
379 /// ```
380 pub async fn tick(&self) {
381 self.executor.tick().await
382 }
383
384 /// Runs the executor asynchronously until the given future completes.
385 ///
386 /// # Examples
387 ///
388 /// ```
389 /// use edge_executor::{LocalExecutor, block_on};
390 ///
391 /// let local_ex: LocalExecutor = Default::default();
392 ///
393 /// let task = local_ex.spawn(async { 1 + 2 });
394 /// let res = block_on(local_ex.run(async { task.await * 2 }));
395 ///
396 /// assert_eq!(res, 6);
397 /// ```
398 pub async fn run<F>(&self, fut: F) -> F::Output
399 where
400 F: Future,
401 {
402 unsafe { self.executor.run_unchecked(fut) }.await
403 }
404}
405
406impl<'a, Q: ExecutorQueue> Default for LocalExecutor<'a, Q> {
407 fn default() -> Self {
408 Self::new()
409 }
410}
411
412struct State<Q> {
413 queue: Q,
414 waker: AtomicWaker,
415}
416
417impl<Q> State<Q> {
418 const fn new(queue: Q) -> Self {
419 Self {
420 queue,
421 waker: AtomicWaker::new(),
422 }
423 }
424}