Skip to content

Commit 07728ff

Browse files
committed
zephyr: work: Replace WorkQueueBuilder with define_work_queue
Instead of a Struct that has to be carefully used, and requires alloc, create a macro to statically declare work queues. Signed-off-by: David Brown <[email protected]>
1 parent 5d970f3 commit 07728ff

File tree

2 files changed

+92
-58
lines changed

2 files changed

+92
-58
lines changed

zephyr/src/lib.rs

+1-8
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,10 @@
3939
//! level operation that is still quite useful in regular code.
4040
//! - [`timer`]: Rust interfaces to Zephyr timers. These timers can be used either by registering a
4141
//! callback, or polled or waited for for an elapsed time.
42-
//! - [`work`]: Zephyr work queues for Rust. The [`work::WorkQueueBuilder`] and resulting
42+
//! - [`work`]: Zephyr work queues for Rust. The [`define_work_queue`] macro and resulting
4343
//! [`work::WorkQueue`] allow creation of Zephyr work queues to be used from Rust. The
4444
//! [`work::Work`] item had an action that will be invoked by the work queue, and can be manually
4545
//! submitted when needed.
46-
//! - [`kio`]: An implementation of an async executor built around triggerable work queues in
47-
//! Zephyr. Although there is a bit more overhead to this executor, it is compatible with many of
48-
//! the Zephyr synchronization types, and many of these [`sys::sync::Semaphore`], and
49-
//! [`sync::channel`] will provide `_async` variants of most of the blocking operations. These
50-
//! will return a `Future`, and can be used from async code started by the [`spawn`] function.
51-
//! In addition, because Zephyr's work queues do not work well with Zephyr's Mutex type, this is
52-
//! also a [`kio::sync::Mutex`] type that works with async.
5346
//! - [`logging`]: A logging backend for Rust on Zephyr. This will log to either `printk` or
5447
//! through Zephyr's logging framework.
5548
//!

zephyr/src/work.rs

+91-50
Original file line numberDiff line numberDiff line change
@@ -126,60 +126,26 @@
126126
//!
127127
//! ## The work queues themselves
128128
//!
129-
//! Workqueues themselves are built using [`WorkQueueBuilder`]. This needs a statically defined
130-
//! stack. Typical usage will be along the lines of:
131-
//! ```rust
132-
//! kobj_define! {
133-
//! WORKER_STACK: ThreadStack<2048>;
134-
//! }
135-
//! // ...
136-
//! let main_worker = Box::new(
137-
//! WorkQueueBuilder::new()
138-
//! .set_priority(2).
139-
//! .set_name(c"mainloop")
140-
//! .set_no_yield(true)
141-
//! .start(MAIN_LOOP_STACK.init_once(()).unwrap())
142-
//! );
143-
//!
144-
//! let _ = zephyr::kio::spawn(
145-
//! mainloop(), // Async or function returning Future.
146-
//! &main_worker,
147-
//! c"w:mainloop",
148-
//! );
129+
//! Work Queues should be declared with the `define_work_queue!` macro, this macro requires the name
130+
//! of the symbol for the work queue, the stack size, and then zero or more optional arguments,
131+
//! defined by the fields in the [`WorkQueueDeclArgs`] struct. For example:
149132
//!
150-
//! ...
151-
//!
152-
//! // Leak the Box so that the worker is never freed.
153-
//! let _ = Box::leak(main_worker);
133+
//! ```rust
134+
//! define_work_queue!(MY_WORKQ, 2048, no_yield = true, priority = 2);
154135
//! ```
155136
//!
156-
//! It is important that WorkQueues never be dropped. It has a Drop implementation that invokes
157-
//! panic. Zephyr provides no mechanism to stop work queue threads, so dropping would result in
158-
//! undefined behavior.
159-
//!
160-
//! # Current Status
161-
//!
162-
//! Although Zephyr has 3 types of work queues, the `k_work_poll` is sufficient to implement all of
163-
//! the behavior, and this implementation only implements this type. Non Future work could be built
164-
//! around the other work types.
165-
//!
166-
//! As such, this means that manually constructed work is still built using `Future`. The `_async`
167-
//! primitives throughout this crate can be used just as readily by hand-written Futures as by async
168-
//! code. Notable, the use of [`Signal`] will likely be common, along with possible timeouts.
169-
//!
170-
//! [`sys::sync::Semaphore`]: crate::sys::sync::Semaphore
171-
//! [`sync::channel`]: crate::sync::channel
172-
//! [`sync::Mutex`]: crate::sync::Mutex
173-
//! [`kio::sync::Mutex`]: crate::kio::sync::Mutex
174-
//! [`kio::spawn`]: crate::kio::spawn
175-
//! [`join`]: futures::JoinHandle::join
176-
//! [`join_async`]: futures::JoinHandle::join_async
137+
//! Then, in code, the work queue can be started, and used to issue work.
138+
//! ```rust
139+
//! let my_workq = MY_WORKQ.start();
140+
//! let action = Work::new(action_item);
141+
//! action.submit(my_workq);
142+
//! ```
177143
178144
extern crate alloc;
179145

180146
use core::{
181147
cell::{RefCell, UnsafeCell},
182-
ffi::{c_int, c_uint},
148+
ffi::{c_char, c_int, c_uint},
183149
mem,
184150
pin::Pin,
185151
sync::atomic::Ordering,
@@ -196,6 +162,30 @@ use zephyr_sys::{
196162

197163
use crate::{error::to_result_void, object::Fixed, simpletls::SimpleTls};
198164

165+
/// The WorkQueue decl args as a struct, so we can have a default, and the macro can fill in those
166+
/// specified by the user.
167+
pub struct WorkQueueDeclArgs {
168+
/// Should this work queue call yield after each queued item.
169+
pub no_yield: bool,
170+
/// Is this work queue thread "essential".
171+
///
172+
/// Threads marked essential will panic if they stop running.
173+
pub essential: bool,
174+
/// Zephyr thread priority for the work queue thread.
175+
pub priority: c_int,
176+
}
177+
178+
impl WorkQueueDeclArgs {
179+
/// Like `Default::default`, but const.
180+
pub const fn default_values() -> Self {
181+
Self {
182+
no_yield: false,
183+
essential: false,
184+
priority: 0,
185+
}
186+
}
187+
}
188+
199189
/// A static declaration of a work-queue. This associates a work queue, with a stack, and an atomic
200190
/// to determine if it has been initialized.
201191
// TODO: Remove the pub on the fields, and make a constructor.
@@ -215,14 +205,18 @@ impl<const SIZE: usize> WorkQueueDecl<SIZE> {
215205
/// Static constructor. Mostly for use by the macro.
216206
pub const fn new(
217207
stack: &'static crate::thread::ThreadStack<SIZE>,
218-
config: k_work_queue_config,
219-
priority: c_int,
208+
name: *const c_char,
209+
args: WorkQueueDeclArgs,
220210
) -> Self {
221211
Self {
222212
queue: unsafe { mem::zeroed() },
223213
stack,
224-
config,
225-
priority,
214+
config: k_work_queue_config {
215+
name,
216+
no_yield: args.no_yield,
217+
essential: args.essential,
218+
},
219+
priority: args.priority,
226220
started: AtomicBool::new(false),
227221
}
228222
}
@@ -442,6 +436,24 @@ impl SubmitResult {
442436
}
443437
}
444438

439+
/*
440+
pub trait Queueable: Send + Sync {
441+
fn as_ptr(&self) -> *const ();
442+
}
443+
444+
impl<T: Send + Sync> Queueable for Arc<T> {
445+
fn as_ptr(&self) -> *const () {
446+
todo!()
447+
}
448+
}
449+
450+
impl<T: Send + Sync> Queueable for &'static T {
451+
fn as_ptr(&self) -> *const () {
452+
todo!()
453+
}
454+
}
455+
*/
456+
445457
/// A simple action that just does something with its data.
446458
///
447459
/// This is similar to a Future, except there is no concept of it completing. It manages its
@@ -583,3 +595,32 @@ impl<T: SimpleAction + Send> Work<T> {
583595
&self.action
584596
}
585597
}
598+
599+
/// Declare a static work queue.
600+
///
601+
/// This declares a static work queue (of type [`WorkQueueDecl`]). This will have a single method
602+
/// `.start()` which can be used to start the work queue, as well as return the persistent handle
603+
/// that can be used to enqueue to it.
604+
#[macro_export]
605+
macro_rules! define_work_queue {
606+
($name:ident, $stack_size:expr) => {
607+
$crate::define_work_queue!($name, $stack_size,);
608+
};
609+
($name:ident, $stack_size:expr, $($key:ident = $value:expr),* $(,)?) => {
610+
static $name: $crate::work::WorkQueueDecl<$stack_size> = {
611+
#[link_section = concat!(".noinit.workq.", stringify!($name))]
612+
static _ZEPHYR_STACK: $crate::thread::ThreadStack<$stack_size> =
613+
$crate::thread::ThreadStack::new();
614+
const _ZEPHYR_C_NAME: &[u8] = concat!(stringify!($name), "\0").as_bytes();
615+
const _ZEPHYR_ARGS: $crate::work::WorkQueueDeclArgs = $crate::work::WorkQueueDeclArgs {
616+
$($key: $value,)*
617+
..$crate::work::WorkQueueDeclArgs::default_values()
618+
};
619+
$crate::work::WorkQueueDecl::new(
620+
&_ZEPHYR_STACK,
621+
_ZEPHYR_C_NAME.as_ptr() as *const ::core::ffi::c_char,
622+
_ZEPHYR_ARGS,
623+
)
624+
};
625+
};
626+
}

0 commit comments

Comments
 (0)