126
126
//!
127
127
//! ## The work queues themselves
128
128
//!
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:
149
132
//!
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);
154
135
//! ```
155
136
//!
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
+ //! ```
177
143
178
144
extern crate alloc;
179
145
180
146
use core:: {
181
147
cell:: { RefCell , UnsafeCell } ,
182
- ffi:: { c_int, c_uint} ,
148
+ ffi:: { c_char , c_int, c_uint} ,
183
149
mem,
184
150
pin:: Pin ,
185
151
sync:: atomic:: Ordering ,
@@ -196,6 +162,30 @@ use zephyr_sys::{
196
162
197
163
use crate :: { error:: to_result_void, object:: Fixed , simpletls:: SimpleTls } ;
198
164
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
+
199
189
/// A static declaration of a work-queue. This associates a work queue, with a stack, and an atomic
200
190
/// to determine if it has been initialized.
201
191
// TODO: Remove the pub on the fields, and make a constructor.
@@ -215,14 +205,18 @@ impl<const SIZE: usize> WorkQueueDecl<SIZE> {
215
205
/// Static constructor. Mostly for use by the macro.
216
206
pub const fn new (
217
207
stack : & ' static crate :: thread:: ThreadStack < SIZE > ,
218
- config : k_work_queue_config ,
219
- priority : c_int ,
208
+ name : * const c_char ,
209
+ args : WorkQueueDeclArgs ,
220
210
) -> Self {
221
211
Self {
222
212
queue : unsafe { mem:: zeroed ( ) } ,
223
213
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 ,
226
220
started : AtomicBool :: new ( false ) ,
227
221
}
228
222
}
@@ -442,6 +436,24 @@ impl SubmitResult {
442
436
}
443
437
}
444
438
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
+
445
457
/// A simple action that just does something with its data.
446
458
///
447
459
/// 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> {
583
595
& self . action
584
596
}
585
597
}
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