Skip to main content

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 {}