ove/
sync.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//! Synchronization primitives for oveRTOS.
8//!
9//! Provides RAII wrappers for mutexes, recursive mutexes, semaphores, events,
10//! and condition variables. All types implement `Send + Sync` and work in both
11//! heap and zero-heap modes.
12
13use core::fmt;
14
15use crate::bindings;
16use crate::error::{Error, Result};
17
18// ---------------------------------------------------------------------------
19// Mutex
20// ---------------------------------------------------------------------------
21
22/// RAII wrapper around `ove_mutex_t`.
23pub struct Mutex {
24    handle: bindings::ove_mutex_t,
25}
26
27impl Mutex {
28    /// Create a new mutex via heap allocation (only in heap mode).
29    #[cfg(not(zero_heap))]
30    pub fn new() -> Result<Self> {
31        let mut handle: bindings::ove_mutex_t = core::ptr::null_mut();
32        let rc = unsafe { bindings::ove_mutex_create(&mut handle) };
33        Error::from_code(rc)?;
34        Ok(Self { handle })
35    }
36
37    /// Create from caller-provided static storage.
38    ///
39    /// # Safety
40    /// Caller must ensure `storage` outlives the `Mutex` and is not
41    /// shared with another primitive.
42    #[cfg(zero_heap)]
43    pub unsafe fn from_static(
44        storage: *mut bindings::ove_mutex_storage_t,
45    ) -> Result<Self> {
46        let mut handle: bindings::ove_mutex_t = core::ptr::null_mut();
47        let rc = unsafe { bindings::ove_mutex_init(&mut handle, storage) };
48        Error::from_code(rc)?;
49        Ok(Self { handle })
50    }
51
52    /// Lock with a timeout in milliseconds. Use `WAIT_FOREVER` for no timeout.
53    ///
54    /// # Errors
55    /// Returns [`Error::Timeout`] if the mutex cannot be acquired within `timeout_ms`.
56    pub fn lock(&self, timeout_ms: u32) -> Result<()> {
57        let rc = unsafe { bindings::ove_mutex_lock(self.handle, timeout_ms) };
58        Error::from_code(rc)
59    }
60
61    /// Unlock the mutex.
62    pub fn unlock(&self) {
63        unsafe { bindings::ove_mutex_unlock(self.handle) }
64    }
65
66    /// Lock and return an RAII guard that auto-unlocks on drop.
67    ///
68    /// # Errors
69    /// Returns [`Error::Timeout`] if the lock cannot be acquired within `timeout_ms`.
70    pub fn guard(&self, timeout_ms: u32) -> Result<MutexGuard<'_>> {
71        self.lock(timeout_ms)?;
72        Ok(MutexGuard { mutex: self })
73    }
74
75    /// Get the raw handle (for use with CondVar).
76    pub(crate) fn raw(&self) -> bindings::ove_mutex_t {
77        self.handle
78    }
79}
80
81impl fmt::Debug for Mutex {
82    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83        f.debug_struct("Mutex")
84            .field("handle", &format_args!("{:p}", self.handle))
85            .finish()
86    }
87}
88
89impl Drop for Mutex {
90    fn drop(&mut self) {
91        if self.handle.is_null() { return; }
92        #[cfg(not(zero_heap))]
93        unsafe { bindings::ove_mutex_destroy(self.handle) }
94        #[cfg(zero_heap)]
95        unsafe { bindings::ove_mutex_deinit(self.handle) }
96    }
97}
98
99// SAFETY: Wraps a ove handle. Lock/unlock are thread-safe RTOS calls.
100// Create/destroy are single-threaded (lifecycle guarantee).
101unsafe impl Send for Mutex {}
102unsafe impl Sync for Mutex {}
103
104/// RAII guard that unlocks a `Mutex` when dropped.
105pub struct MutexGuard<'a> {
106    mutex: &'a Mutex,
107}
108
109impl fmt::Debug for MutexGuard<'_> {
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        f.debug_struct("MutexGuard")
112            .field("mutex", &format_args!("{:p}", self.mutex.handle))
113            .finish()
114    }
115}
116
117impl Drop for MutexGuard<'_> {
118    fn drop(&mut self) {
119        self.mutex.unlock();
120    }
121}
122
123// ---------------------------------------------------------------------------
124// RecursiveMutex
125// ---------------------------------------------------------------------------
126
127/// RAII wrapper around a recursive mutex.
128pub struct RecursiveMutex {
129    handle: bindings::ove_mutex_t,
130}
131
132impl RecursiveMutex {
133    /// Create a new recursive mutex via heap allocation (only in heap mode).
134    #[cfg(not(zero_heap))]
135    pub fn new() -> Result<Self> {
136        let mut handle: bindings::ove_mutex_t = core::ptr::null_mut();
137        let rc = unsafe { bindings::ove_recursive_mutex_create(&mut handle) };
138        Error::from_code(rc)?;
139        Ok(Self { handle })
140    }
141
142    /// Create from caller-provided static storage.
143    ///
144    /// # Safety
145    /// Caller must ensure `storage` outlives the `RecursiveMutex` and is not
146    /// shared with another primitive.
147    #[cfg(zero_heap)]
148    pub unsafe fn from_static(
149        storage: *mut bindings::ove_mutex_storage_t,
150    ) -> Result<Self> {
151        let mut handle: bindings::ove_mutex_t = core::ptr::null_mut();
152        let rc = unsafe { bindings::ove_recursive_mutex_init(&mut handle, storage) };
153        Error::from_code(rc)?;
154        Ok(Self { handle })
155    }
156
157    /// Lock with a timeout in milliseconds.
158    ///
159    /// The same thread may lock the mutex multiple times; each lock must be
160    /// paired with a corresponding [`unlock`](RecursiveMutex::unlock).
161    ///
162    /// # Errors
163    /// Returns [`Error::Timeout`] if the lock cannot be acquired within `timeout_ms`.
164    pub fn lock(&self, timeout_ms: u32) -> Result<()> {
165        let rc = unsafe { bindings::ove_recursive_mutex_lock(self.handle, timeout_ms) };
166        Error::from_code(rc)
167    }
168
169    /// Unlock the recursive mutex.
170    pub fn unlock(&self) {
171        unsafe { bindings::ove_recursive_mutex_unlock(self.handle) }
172    }
173
174    /// Lock and return an RAII guard that auto-unlocks on drop.
175    ///
176    /// # Errors
177    /// Returns [`Error::Timeout`] if the lock cannot be acquired within `timeout_ms`.
178    pub fn guard(&self, timeout_ms: u32) -> Result<RecursiveMutexGuard<'_>> {
179        self.lock(timeout_ms)?;
180        Ok(RecursiveMutexGuard { mutex: self })
181    }
182}
183
184impl fmt::Debug for RecursiveMutex {
185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186        f.debug_struct("RecursiveMutex")
187            .field("handle", &format_args!("{:p}", self.handle))
188            .finish()
189    }
190}
191
192impl Drop for RecursiveMutex {
193    fn drop(&mut self) {
194        if self.handle.is_null() { return; }
195        #[cfg(not(zero_heap))]
196        unsafe { bindings::ove_recursive_mutex_destroy(self.handle) }
197        #[cfg(zero_heap)]
198        unsafe { bindings::ove_mutex_deinit(self.handle) }
199    }
200}
201
202// SAFETY: Wraps a ove handle. Lock/unlock are thread-safe RTOS calls.
203// Create/destroy are single-threaded (lifecycle guarantee).
204unsafe impl Send for RecursiveMutex {}
205unsafe impl Sync for RecursiveMutex {}
206
207/// RAII guard that unlocks a `RecursiveMutex` when dropped.
208pub struct RecursiveMutexGuard<'a> {
209    mutex: &'a RecursiveMutex,
210}
211
212impl fmt::Debug for RecursiveMutexGuard<'_> {
213    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214        f.debug_struct("RecursiveMutexGuard")
215            .field("mutex", &format_args!("{:p}", self.mutex.handle))
216            .finish()
217    }
218}
219
220impl Drop for RecursiveMutexGuard<'_> {
221    fn drop(&mut self) {
222        self.mutex.unlock();
223    }
224}
225
226// ---------------------------------------------------------------------------
227// Semaphore
228// ---------------------------------------------------------------------------
229
230/// Counting semaphore.
231pub struct Semaphore {
232    handle: bindings::ove_sem_t,
233}
234
235impl Semaphore {
236    /// Create a counting semaphore via heap allocation (only in heap mode).
237    #[cfg(not(zero_heap))]
238    pub fn new(initial: u32, max: u32) -> Result<Self> {
239        let mut handle: bindings::ove_sem_t = core::ptr::null_mut();
240        let rc = unsafe { bindings::ove_sem_create(&mut handle, initial, max) };
241        Error::from_code(rc)?;
242        Ok(Self { handle })
243    }
244
245    /// Create from caller-provided static storage.
246    ///
247    /// # Safety
248    /// Caller must ensure `storage` outlives the `Semaphore`.
249    #[cfg(zero_heap)]
250    pub unsafe fn from_static(
251        storage: *mut bindings::ove_sem_storage_t,
252        initial: u32,
253        max: u32,
254    ) -> Result<Self> {
255        let mut handle: bindings::ove_sem_t = core::ptr::null_mut();
256        let rc = unsafe { bindings::ove_sem_init(&mut handle, storage, initial, max) };
257        Error::from_code(rc)?;
258        Ok(Self { handle })
259    }
260
261    /// Decrement (take) the semaphore, blocking up to `timeout_ms` if the count is zero.
262    ///
263    /// # Errors
264    /// Returns [`Error::Timeout`] if the semaphore cannot be taken within `timeout_ms`.
265    pub fn take(&self, timeout_ms: u32) -> Result<()> {
266        let rc = unsafe { bindings::ove_sem_take(self.handle, timeout_ms) };
267        Error::from_code(rc)
268    }
269
270    /// Post/give the semaphore.
271    pub fn give(&self) {
272        unsafe { bindings::ove_sem_give(self.handle) }
273    }
274}
275
276impl fmt::Debug for Semaphore {
277    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278        f.debug_struct("Semaphore")
279            .field("handle", &format_args!("{:p}", self.handle))
280            .finish()
281    }
282}
283
284impl Drop for Semaphore {
285    fn drop(&mut self) {
286        if self.handle.is_null() { return; }
287        #[cfg(not(zero_heap))]
288        unsafe { bindings::ove_sem_destroy(self.handle) }
289        #[cfg(zero_heap)]
290        unsafe { bindings::ove_sem_deinit(self.handle) }
291    }
292}
293
294// SAFETY: Wraps a ove handle. Take/give are thread-safe RTOS calls.
295// Create/destroy are single-threaded (lifecycle guarantee).
296unsafe impl Send for Semaphore {}
297unsafe impl Sync for Semaphore {}
298
299// ---------------------------------------------------------------------------
300// Event
301// ---------------------------------------------------------------------------
302
303/// Binary event (signal/wait).
304pub struct Event {
305    handle: bindings::ove_event_t,
306}
307
308impl Event {
309    /// Create a new event via heap allocation (only in heap mode).
310    #[cfg(not(zero_heap))]
311    pub fn new() -> Result<Self> {
312        let mut handle: bindings::ove_event_t = core::ptr::null_mut();
313        let rc = unsafe { bindings::ove_event_create(&mut handle) };
314        Error::from_code(rc)?;
315        Ok(Self { handle })
316    }
317
318    /// Create from caller-provided static storage.
319    ///
320    /// # Safety
321    /// Caller must ensure `storage` outlives the `Event`.
322    #[cfg(zero_heap)]
323    pub unsafe fn from_static(
324        storage: *mut bindings::ove_event_storage_t,
325    ) -> Result<Self> {
326        let mut handle: bindings::ove_event_t = core::ptr::null_mut();
327        let rc = unsafe { bindings::ove_event_init(&mut handle, storage) };
328        Error::from_code(rc)?;
329        Ok(Self { handle })
330    }
331
332    /// Block until the event is signalled or the timeout expires.
333    ///
334    /// # Errors
335    /// Returns [`Error::Timeout`] if the event is not signalled within `timeout_ms`.
336    pub fn wait(&self, timeout_ms: u32) -> Result<()> {
337        let rc = unsafe { bindings::ove_event_wait(self.handle, timeout_ms) };
338        Error::from_code(rc)
339    }
340
341    /// Signal the event.
342    pub fn signal(&self) {
343        unsafe { bindings::ove_event_signal(self.handle) }
344    }
345
346    /// Signal the event from an ISR context.
347    pub fn signal_from_isr(&self) {
348        unsafe { bindings::ove_event_signal_from_isr(self.handle) }
349    }
350}
351
352impl fmt::Debug for Event {
353    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354        f.debug_struct("Event")
355            .field("handle", &format_args!("{:p}", self.handle))
356            .finish()
357    }
358}
359
360impl Drop for Event {
361    fn drop(&mut self) {
362        if self.handle.is_null() { return; }
363        #[cfg(not(zero_heap))]
364        unsafe { bindings::ove_event_destroy(self.handle) }
365        #[cfg(zero_heap)]
366        unsafe { bindings::ove_event_deinit(self.handle) }
367    }
368}
369
370// SAFETY: Wraps a ove handle. Wait/signal are thread-safe RTOS calls.
371// Create/destroy are single-threaded (lifecycle guarantee).
372unsafe impl Send for Event {}
373unsafe impl Sync for Event {}
374
375// ---------------------------------------------------------------------------
376// CondVar
377// ---------------------------------------------------------------------------
378
379/// Condition variable.
380pub struct CondVar {
381    handle: bindings::ove_condvar_t,
382}
383
384impl CondVar {
385    /// Create a new condition variable via heap allocation (only in heap mode).
386    #[cfg(not(zero_heap))]
387    pub fn new() -> Result<Self> {
388        let mut handle: bindings::ove_condvar_t = core::ptr::null_mut();
389        let rc = unsafe { bindings::ove_condvar_create(&mut handle) };
390        Error::from_code(rc)?;
391        Ok(Self { handle })
392    }
393
394    /// Create from caller-provided static storage.
395    ///
396    /// # Safety
397    /// Caller must ensure `storage` outlives the `CondVar`.
398    #[cfg(zero_heap)]
399    pub unsafe fn from_static(
400        storage: *mut bindings::ove_condvar_storage_t,
401    ) -> Result<Self> {
402        let mut handle: bindings::ove_condvar_t = core::ptr::null_mut();
403        let rc = unsafe { bindings::ove_condvar_init(&mut handle, storage) };
404        Error::from_code(rc)?;
405        Ok(Self { handle })
406    }
407
408    /// Atomically release `mutex` and block until signalled or `timeout_ms` elapses.
409    ///
410    /// On return (successful or not), `mutex` is re-acquired before this function returns.
411    ///
412    /// # Errors
413    /// Returns [`Error::Timeout`] if neither [`signal`](CondVar::signal) nor
414    /// [`broadcast`](CondVar::broadcast) fires within `timeout_ms`.
415    pub fn wait(&self, mutex: &Mutex, timeout_ms: u32) -> Result<()> {
416        let rc =
417            unsafe { bindings::ove_condvar_wait(self.handle, mutex.raw(), timeout_ms) };
418        Error::from_code(rc)
419    }
420
421    /// Wake one waiter.
422    pub fn signal(&self) {
423        unsafe { bindings::ove_condvar_signal(self.handle) }
424    }
425
426    /// Wake all waiters.
427    pub fn broadcast(&self) {
428        unsafe { bindings::ove_condvar_broadcast(self.handle) }
429    }
430}
431
432impl fmt::Debug for CondVar {
433    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
434        f.debug_struct("CondVar")
435            .field("handle", &format_args!("{:p}", self.handle))
436            .finish()
437    }
438}
439
440impl Drop for CondVar {
441    fn drop(&mut self) {
442        if self.handle.is_null() { return; }
443        #[cfg(not(zero_heap))]
444        unsafe { bindings::ove_condvar_destroy(self.handle) }
445        #[cfg(zero_heap)]
446        unsafe { bindings::ove_condvar_deinit(self.handle) }
447    }
448}
449
450// SAFETY: Wraps a ove handle. Wait/signal/broadcast are thread-safe RTOS calls.
451// Create/destroy are single-threaded (lifecycle guarantee).
452unsafe impl Send for CondVar {}
453unsafe impl Sync for CondVar {}