Skip to content

Commit a995839

Browse files
committed
zephyr: Implement executor-zephyr to use with Embassy
This implements an executor for Embassy that runs a set of async tasks on a single Zephyr thread. The executor will suspend the thread when there is no work to do, resuming when there is work. It is permissible to run multiple executors, on different threads, to support a hybrid async cooperative and priority-based scheduling. Signed-off-by: David Brown <[email protected]>
1 parent 60d7c53 commit a995839

File tree

3 files changed

+88
-0
lines changed

3 files changed

+88
-0
lines changed

zephyr/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ optional = true
5959
version = "0.6.2"
6060
optional = true
6161

62+
[dependencies.embassy-executor]
63+
version = "0.7.0"
64+
optional = true
65+
6266
# These are needed at build time.
6367
# Whether these need to be vendored is an open question. They are not
6468
# used by the core Zephyr tree, but are needed by zephyr applications.
@@ -72,3 +76,8 @@ time-driver = [
7276
"dep:embassy-time-queue-utils",
7377
"dep:embassy-sync",
7478
]
79+
80+
# Provide an embassy-based executor that runs on individual Zephyr threads.
81+
executor-zephyr = [
82+
"dep:embassy-executor",
83+
]

zephyr/src/embassy.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,8 @@
8686
8787
#[cfg(feature = "time-driver")]
8888
mod time_driver;
89+
90+
#[cfg(feature = "executor-zephyr")]
91+
pub use executor::Executor;
92+
#[cfg(feature = "executor-zephyr")]
93+
mod executor;

zephyr/src/embassy/executor.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//! An embassy executor tailored for Zephyr
2+
3+
use core::{marker::PhantomData, sync::atomic::Ordering};
4+
5+
use embassy_executor::{raw, Spawner};
6+
use zephyr_sys::{k_current_get, k_thread_resume, k_thread_suspend, k_tid_t};
7+
8+
use crate::{printkln, sync::atomic::AtomicBool};
9+
10+
/// Zephyr-thread based executor.
11+
pub struct Executor {
12+
inner: Option<raw::Executor>,
13+
id: k_tid_t,
14+
pend: AtomicBool,
15+
not_send: PhantomData<*mut ()>,
16+
}
17+
18+
impl Executor {
19+
/// Create a new Executor.
20+
pub fn new() -> Self {
21+
let id = unsafe { k_current_get() };
22+
23+
Self {
24+
inner: None,
25+
pend: AtomicBool::new(false),
26+
id,
27+
not_send: PhantomData,
28+
}
29+
}
30+
31+
/// Run the executor.
32+
pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
33+
let context = self as *mut _ as *mut ();
34+
self.inner.replace(raw::Executor::new(context));
35+
let inner = self.inner.as_mut().unwrap();
36+
init(inner.spawner());
37+
38+
loop {
39+
unsafe {
40+
// The raw executor's poll only runs things that were queued _before_ this poll
41+
// itself is actually run. This means, specifically, that if the polled execution
42+
// causes this, or other threads to enqueue, this will return without running them.
43+
// `__pender` _will_ be called, but it isn't "sticky" like `wfe/sev` are. To
44+
// simulate this, we will use the 'pend' atomic to count
45+
inner.poll();
46+
if !self.pend.swap(false, Ordering::SeqCst) {
47+
// printkln!("_suspend");
48+
k_thread_suspend(k_current_get());
49+
}
50+
}
51+
}
52+
}
53+
}
54+
55+
#[export_name = "__pender"]
56+
fn __pender(context: *mut ()) {
57+
unsafe {
58+
let myself = k_current_get();
59+
60+
let this = context as *const Executor;
61+
let other = (*this).id;
62+
63+
// If the other is a different thread, resume it.
64+
if other != myself {
65+
// printkln!("_resume");
66+
k_thread_resume(other);
67+
}
68+
// Otherwise, we need to make sure our own next suspend doesn't happen.
69+
// We need to also prevent a suspend from happening in the case where the only running
70+
// thread causes other work to become pending. The resume below will do nothing, as we
71+
// are just running.
72+
(*this).pend.store(true, Ordering::SeqCst);
73+
}
74+
}

0 commit comments

Comments
 (0)