ove/workqueue.rs
1// Copyright (C) 2026 Kamil Lulko <kamil.lulko@gmail.com>
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4//
5// This file is part of oveRTOS.
6
7//! Work queue and deferred work items for oveRTOS.
8//!
9//! A [`Workqueue`] runs a dedicated thread that processes [`Work`] items in order.
10//! Work items can be submitted immediately or after a delay, and can be cancelled
11//! before they execute.
12
13use crate::bindings;
14use crate::error::{Error, Result};
15
16// SAFETY (module-wide contract for the `unsafe { bindings::ove_*(...) }` FFI
17// calls below): any handle passed to the C API is non-null and refers to a
18// live RTOS object — wrapper constructors establish validity via
19// `Error::from_code`, and `Drop` (or an explicit `deinit`) is the only place
20// a handle is released. Pointer and slice arguments reference caller-owned
21// memory valid for the duration of the call; the C side copies whatever it
22// retains and does not alias them past return (verified against the
23// signatures in `include/ove/*.h`). Blocks that deviate — `transmute`, raw
24// pointer casts from user data, slice reconstruction via `from_raw_parts`,
25// or storing a callback across the FFI boundary — carry their own
26// `// SAFETY:` comment.
27
28/// A workqueue that executes deferred work items on a dedicated thread.
29pub struct Workqueue {
30 handle: bindings::ove_workqueue_t,
31}
32
33impl Workqueue {
34 /// Create a workqueue via heap allocation (only in heap mode).
35 ///
36 /// `name` must be a `&'static CStr`; the easiest source is a
37 /// `c"..."` literal (Rust 1.77+). The compiler enforces null
38 /// termination — `&[u8]` without `\0` was the previous shape and
39 /// was undefined-behaviour-prone.
40 #[cfg(not(zero_heap))]
41 pub fn new(
42 name: &'static core::ffi::CStr,
43 priority: crate::Priority,
44 stack_size: usize,
45 ) -> Result<Self> {
46 let mut handle: bindings::ove_workqueue_t = core::ptr::null_mut();
47 let rc = unsafe {
48 bindings::ove_workqueue_create(
49 &mut handle,
50 name.as_ptr(),
51 priority as bindings::ove_prio_t,
52 stack_size,
53 )
54 };
55 Error::from_code(rc)?;
56 Ok(Self { handle })
57 }
58
59 /// Create from caller-provided static storage and stack.
60 ///
61 /// # Safety
62 /// - `storage` must outlive the `Workqueue` and not be shared.
63 /// - `stack` must point to at least `stack_size` bytes and outlive
64 /// the `Workqueue`.
65 #[cfg(zero_heap)]
66 pub unsafe fn from_static(
67 storage: *mut bindings::ove_workqueue_storage_t,
68 name: &'static core::ffi::CStr,
69 priority: crate::Priority,
70 stack_size: usize,
71 stack: *mut core::ffi::c_void,
72 ) -> Result<Self> {
73 let mut handle: bindings::ove_workqueue_t = core::ptr::null_mut();
74 let rc = unsafe {
75 bindings::ove_workqueue_init(
76 &mut handle,
77 storage,
78 name.as_ptr(),
79 priority as bindings::ove_prio_t,
80 stack_size,
81 stack,
82 )
83 };
84 Error::from_code(rc)?;
85 Ok(Self { handle })
86 }
87
88 /// Return the underlying `ove_workqueue_t` handle for advanced
89 /// FFI interop. Normal app code should pass the [`Workqueue`]
90 /// itself to [`Work::submit`] / [`Work::submit_delayed`].
91 pub fn handle(&self) -> bindings::ove_workqueue_t {
92 self.handle
93 }
94}
95
96crate::ove_handle_impl!(Workqueue, ove_workqueue_destroy, ove_workqueue_deinit);
97
98/// A work item that can be submitted to a [`Workqueue`].
99pub struct Work {
100 handle: bindings::ove_work_t,
101}
102
103impl Work {
104 /// Create a work item via heap allocation (only in heap mode).
105 ///
106 /// The handler receives the raw `ove_work_t` handle (the C API does
107 /// not provide a user_data parameter for work handlers).
108 ///
109 /// # Errors
110 /// Returns [`Error::NoMemory`] if heap allocation fails.
111 #[cfg(not(zero_heap))]
112 pub fn new(handler: bindings::ove_work_fn) -> Result<Self> {
113 let mut handle: bindings::ove_work_t = core::ptr::null_mut();
114 let rc = unsafe { bindings::ove_work_init(&mut handle, handler) };
115 Error::from_code(rc)?;
116 Ok(Self { handle })
117 }
118
119 /// Create from caller-provided static storage.
120 ///
121 /// # Safety
122 /// Caller must ensure `storage` outlives the `Work` item.
123 #[cfg(zero_heap)]
124 pub unsafe fn from_static(
125 storage: *mut bindings::ove_work_storage_t,
126 handler: bindings::ove_work_fn,
127 ) -> Result<Self> {
128 let mut handle: bindings::ove_work_t = core::ptr::null_mut();
129 let rc = unsafe { bindings::ove_work_init_static(&mut handle, storage, handler) };
130 Error::from_code(rc)?;
131 Ok(Self { handle })
132 }
133
134 /// Submit this work item to `wq` for immediate execution.
135 ///
136 /// # Errors
137 /// Returns an error if the workqueue is shutting down or the item is already pending.
138 pub fn submit(&self, wq: &Workqueue) -> Result<()> {
139 let rc = unsafe { bindings::ove_work_submit(wq.handle(), self.handle) };
140 Error::from_code(rc)
141 }
142
143 /// Submit this work item to `wq` for execution after `delay_ms` milliseconds.
144 ///
145 /// # Errors
146 /// Returns an error if the workqueue is shutting down or the item is already pending.
147 pub fn submit_delayed(&self, wq: &Workqueue, delay_ms: u32) -> Result<()> {
148 let rc = unsafe { bindings::ove_work_submit_delayed(wq.handle(), self.handle, delay_ms) };
149 Error::from_code(rc)
150 }
151
152 /// Cancel this work item if it is pending or delayed.
153 ///
154 /// Has no effect if the item is not currently queued.
155 ///
156 /// # Errors
157 /// Returns an error if the underlying RTOS call fails.
158 pub fn cancel(&self) -> Result<()> {
159 let rc = unsafe { bindings::ove_work_cancel(self.handle) };
160 Error::from_code(rc)
161 }
162}
163
164impl Drop for Work {
165 fn drop(&mut self) {
166 if self.handle.is_null() {
167 return;
168 }
169 #[cfg(not(zero_heap))]
170 unsafe {
171 bindings::ove_work_free(self.handle);
172 }
173 #[cfg(zero_heap)]
174 {
175 self.handle = core::ptr::null_mut();
176 }
177 }
178}
179
180// SAFETY: `Work` wraps `ove_work_t`. Submission and cancellation are
181// substrate-synchronised; the handler trampoline runs on the workqueue
182// thread, never the constructing thread, so cross-thread transfer is
183// the expected access pattern.
184unsafe impl Send for Work {}
185unsafe impl Sync for Work {}