Skip to main content

leodos_libcfs/os/
sync.rs

1//! Safe, idiomatic wrappers for OSAL synchronization primitives.
2//!
3//! This module provides safe wrappers for Mutexes, Binary Semaphores, and
4//! Counting Semaphores. It uses RAII guards for mutexes to ensure they are
5//! always released, preventing deadlocks.
6
7use crate::error::Result;
8use crate::ffi;
9use crate::os::id::OsalId;
10use crate::os::time::OsTime;
11use crate::os::util::c_name_from_str;
12use crate::string_from_c_buf;
13use crate::status::check;
14use core::mem::MaybeUninit;
15use core::ops::Drop;
16use heapless::String;
17
18/// Properties of a mutex, returned by `Mutex::get_info`.
19#[derive(Debug, Clone)]
20pub struct MutexProp {
21    /// The registered name of the mutex.
22    pub name: String<{ ffi::OS_MAX_API_NAME as usize }>,
23    /// The OSAL ID of the task that created the mutex.
24    pub creator: OsalId,
25}
26
27/// A mutual exclusion primitive useful for protecting shared data.
28///
29/// This mutex will block tasks waiting for the lock to become available.
30#[derive(Debug)]
31pub struct Mutex {
32    id: ffi::osal_id_t,
33}
34
35impl Mutex {
36    /// Creates a new OSAL mutex in the unlocked (available) state.
37    ///
38    /// # Arguments
39    /// * `name`: A unique string to identify the mutex.
40    pub fn new(name: &str) -> Result<Self> {
41        let c_name = c_name_from_str(name)?;
42        let mut sem_id = MaybeUninit::uninit();
43        let status = unsafe { ffi::OS_MutSemCreate(sem_id.as_mut_ptr(), c_name.as_ptr(), 0) };
44        check(status)?;
45        Ok(Self {
46            id: unsafe { sem_id.assume_init() },
47        })
48    }
49
50    /// Acquires a mutex, blocking the current task until it is able to do so.
51    ///
52    /// This function returns a `MutexGuard` when the lock has been acquired.
53    /// The guard ensures that the lock is automatically released when it goes out of scope.
54    pub fn lock(&'_ self) -> Result<MutexGuard<'_>> {
55        check(unsafe { ffi::OS_MutSemTake(self.id) })?;
56        Ok(MutexGuard { mutex: self })
57    }
58
59    /// Finds an existing mutex ID by its name.
60    pub fn get_id_by_name(name: &str) -> Result<OsalId> {
61        let c_name = c_name_from_str(name)?;
62        let mut sem_id = MaybeUninit::uninit();
63        check(unsafe { ffi::OS_MutSemGetIdByName(sem_id.as_mut_ptr(), c_name.as_ptr()) })?;
64        Ok(OsalId(unsafe { sem_id.assume_init() }))
65    }
66
67    /// Retrieves information about this mutex.
68    pub fn get_info(&self) -> Result<MutexProp> {
69        let mut prop = MaybeUninit::<ffi::OS_mut_sem_prop_t>::uninit();
70        check(unsafe { ffi::OS_MutSemGetInfo(self.id, prop.as_mut_ptr()) })?;
71        let prop = unsafe { prop.assume_init() };
72
73        Ok(MutexProp {
74            name: string_from_c_buf(&prop.name)?,
75            creator: OsalId(prop.creator),
76        })
77    }
78}
79
80impl Drop for Mutex {
81    fn drop(&mut self) {
82        let _ = unsafe { ffi::OS_MutSemDelete(self.id) };
83    }
84}
85
86/// An RAII implementation of a scoped lock for a mutex.
87///
88/// When this structure is dropped (falls out of scope), the lock will be released.
89#[derive(Debug)]
90#[must_use = "if unused the Mutex will immediately unlock"]
91pub struct MutexGuard<'a> {
92    mutex: &'a Mutex,
93}
94
95impl<'a> Drop for MutexGuard<'a> {
96    /// Releases the lock on the associated mutex.
97    fn drop(&mut self) {
98        let _ = unsafe { ffi::OS_MutSemGive(self.mutex.id) };
99    }
100}
101
102/// Initial state of a binary semaphore.
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104pub enum SemState {
105    /// The semaphore starts empty (taken).
106    Empty,
107    /// The semaphore starts full (available).
108    Full,
109}
110
111impl SemState {
112    fn as_u32(self) -> u32 {
113        match self {
114            SemState::Empty => 0,
115            SemState::Full => 1,
116        }
117    }
118}
119
120/// Properties of a binary semaphore, returned by `BinSem::get_info`.
121#[derive(Debug, Clone)]
122pub struct BinSemProp {
123    /// The registered name of the binary semaphore.
124    pub name: String<{ ffi::OS_MAX_API_NAME as usize }>,
125    /// The OSAL ID of the task that created the semaphore.
126    pub creator: OsalId,
127    /// The current value of the semaphore (typically 0 or 1).
128    pub value: i32,
129}
130
131/// A binary semaphore, often used for signaling between tasks.
132#[derive(Debug)]
133pub struct BinSem {
134    id: ffi::osal_id_t,
135}
136
137impl BinSem {
138    /// Creates a new binary semaphore.
139    ///
140    /// # Arguments
141    /// * `name`: A unique string to identify the semaphore.
142    /// * `initial_value`: The initial state of the semaphore.
143    pub fn new(name: &str, initial_value: SemState) -> Result<Self> {
144        let c_name = c_name_from_str(name)?;
145        let mut sem_id = MaybeUninit::uninit();
146        let status = unsafe {
147            ffi::OS_BinSemCreate(sem_id.as_mut_ptr(), c_name.as_ptr(), initial_value.as_u32(), 0)
148        };
149        check(status)?;
150        Ok(Self {
151            id: unsafe { sem_id.assume_init() },
152        })
153    }
154
155    /// Unlocks (gives) the semaphore.
156    pub fn give(&self) -> Result<()> {
157        check(unsafe { ffi::OS_BinSemGive(self.id) })?;
158        Ok(())
159    }
160
161    /// Blocks until the semaphore can be locked (taken).
162    pub fn take(&self) -> Result<()> {
163        check(unsafe { ffi::OS_BinSemTake(self.id) })?;
164        Ok(())
165    }
166
167    /// Unblocks all tasks pending on the specified semaphore.
168    ///
169    /// This function does not change the state of the semaphore.
170    pub fn flush(&self) -> Result<()> {
171        check(unsafe { ffi::OS_BinSemFlush(self.id) })?;
172        Ok(())
173    }
174
175    /// Blocks until the semaphore can be taken, with a timeout.
176    ///
177    /// # Arguments
178    /// * `timeout_ms`: Timeout in milliseconds.
179    pub fn timed_wait(&self, timeout_ms: u32) -> Result<()> {
180        check(unsafe { ffi::OS_BinSemTimedWait(self.id, timeout_ms) })?;
181        Ok(())
182    }
183
184    /// Finds an existing binary semaphore ID by its name.
185    pub fn get_id_by_name(name: &str) -> Result<OsalId> {
186        let c_name = c_name_from_str(name)?;
187        let mut sem_id = MaybeUninit::uninit();
188        check(unsafe { ffi::OS_BinSemGetIdByName(sem_id.as_mut_ptr(), c_name.as_ptr()) })?;
189        Ok(OsalId(unsafe { sem_id.assume_init() }))
190    }
191
192    /// Retrieves information about this binary semaphore.
193    pub fn get_info(&self) -> Result<BinSemProp> {
194        let mut prop = MaybeUninit::<ffi::OS_bin_sem_prop_t>::uninit();
195        check(unsafe { ffi::OS_BinSemGetInfo(self.id, prop.as_mut_ptr()) })?;
196        let prop = unsafe { prop.assume_init() };
197
198        Ok(BinSemProp {
199            name: string_from_c_buf(&prop.name)?,
200            creator: OsalId(prop.creator),
201            value: prop.value,
202        })
203    }
204}
205
206impl Drop for BinSem {
207    fn drop(&mut self) {
208        let _ = unsafe { ffi::OS_BinSemDelete(self.id) };
209    }
210}
211
212/// Properties of a counting semaphore, returned by `CountSem::get_info`.
213#[derive(Debug, Clone)]
214pub struct CountSemProp {
215    /// The registered name of the counting semaphore.
216    pub name: String<{ ffi::OS_MAX_API_NAME as usize }>,
217    /// The OSAL ID of the task that created the semaphore.
218    pub creator: OsalId,
219    /// The current count of the semaphore.
220    pub value: i32,
221}
222
223/// A counting semaphore.
224#[derive(Debug)]
225pub struct CountSem {
226    id: ffi::osal_id_t,
227}
228
229impl CountSem {
230    /// Creates a new counting semaphore.
231    ///
232    /// For portability, keep `initial_value` within `short int`
233    /// range (0–32767). Some RTOS impose upper limits.
234    ///
235    /// # Arguments
236    /// * `name`: A unique string to identify the semaphore.
237    /// * `initial_value`: The initial count of the semaphore.
238    pub fn new(name: &str, initial_value: u32) -> Result<Self> {
239        let c_name = c_name_from_str(name)?;
240        let mut sem_id = MaybeUninit::uninit();
241        let status = unsafe {
242            ffi::OS_CountSemCreate(sem_id.as_mut_ptr(), c_name.as_ptr(), initial_value, 0)
243        };
244        check(status)?;
245        Ok(Self {
246            id: unsafe { sem_id.assume_init() },
247        })
248    }
249
250    /// Increments (gives) the semaphore's count.
251    pub fn give(&self) -> Result<()> {
252        check(unsafe { ffi::OS_CountSemGive(self.id) })?;
253        Ok(())
254    }
255
256    /// Blocks until the semaphore's count is non-zero, then decrements it.
257    pub fn take(&self) -> Result<()> {
258        check(unsafe { ffi::OS_CountSemTake(self.id) })?;
259        Ok(())
260    }
261
262    /// Blocks until the semaphore can be taken, with a timeout.
263    ///
264    /// # Arguments
265    /// * `timeout_ms`: Timeout in milliseconds.
266    pub fn timed_wait(&self, timeout_ms: u32) -> Result<()> {
267        check(unsafe { ffi::OS_CountSemTimedWait(self.id, timeout_ms) })?;
268        Ok(())
269    }
270
271    /// Finds an existing counting semaphore ID by its name.
272    pub fn get_id_by_name(name: &str) -> Result<OsalId> {
273        let c_name = c_name_from_str(name)?;
274        let mut sem_id = MaybeUninit::uninit();
275        check(unsafe { ffi::OS_CountSemGetIdByName(sem_id.as_mut_ptr(), c_name.as_ptr()) })?;
276        Ok(OsalId(unsafe { sem_id.assume_init() }))
277    }
278
279    /// Retrieves information about this counting semaphore.
280    pub fn get_info(&self) -> Result<CountSemProp> {
281        let mut prop = MaybeUninit::<ffi::OS_count_sem_prop_t>::uninit();
282        check(unsafe { ffi::OS_CountSemGetInfo(self.id, prop.as_mut_ptr()) })?;
283        let prop = unsafe { prop.assume_init() };
284
285        Ok(CountSemProp {
286            name: string_from_c_buf(&prop.name)?,
287            creator: OsalId(prop.creator),
288            value: prop.value,
289        })
290    }
291}
292
293impl Drop for CountSem {
294    fn drop(&mut self) {
295        let _ = unsafe { ffi::OS_CountSemDelete(self.id) };
296    }
297}
298
299/// Properties of a condition variable, returned by `CondVar::get_info`.
300#[derive(Debug, Clone)]
301pub struct CondVarProp {
302    /// The registered name of the condition variable.
303    pub name: String<{ ffi::OS_MAX_API_NAME as usize }>,
304    /// The OSAL ID of the task that created the condition variable.
305    pub creator: OsalId,
306}
307
308/// A condition variable, for more complex synchronization with a `Mutex`.
309#[derive(Debug)]
310pub struct CondVar {
311    id: ffi::osal_id_t,
312}
313
314impl CondVar {
315    /// Creates a new condition variable.
316    pub fn new(name: &str) -> Result<Self> {
317        let c_name = c_name_from_str(name)?;
318        let mut var_id = MaybeUninit::uninit();
319        check(unsafe { ffi::OS_CondVarCreate(var_id.as_mut_ptr(), c_name.as_ptr(), 0) })?;
320        Ok(Self {
321            id: unsafe { var_id.assume_init() },
322        })
323    }
324
325    /// Signals the condition variable, waking up one waiting task.
326    pub fn signal(&self) -> Result<()> {
327        check(unsafe { ffi::OS_CondVarSignal(self.id) })?;
328        Ok(())
329    }
330
331    /// Broadcasts the condition variable, waking up all waiting tasks.
332    pub fn broadcast(&self) -> Result<()> {
333        check(unsafe { ffi::OS_CondVarBroadcast(self.id) })?;
334        Ok(())
335    }
336
337    /// Waits for the condition variable to be signaled.
338    ///
339    /// Note: OSAL condition variables have their own internal mutex
340    /// (`OS_CondVarLock`/`OS_CondVarUnlock`). The `_guard` param
341    /// here is consumed to enforce discipline, but the underlying
342    /// OSAL wait operates on the condvar's internal mutex, not the
343    /// external one.
344    pub fn wait(&self, _guard: MutexGuard) -> Result<()> {
345        check(unsafe { ffi::OS_CondVarWait(self.id) })?;
346        Ok(())
347    }
348
349    /// Atomically unlocks the mutex and waits for the condition variable, with a timeout.
350    pub fn timed_wait(&self, _guard: MutexGuard, abstime: OsTime) -> Result<()> {
351        check(unsafe { ffi::OS_CondVarTimedWait(self.id, &abstime.0) })?;
352        Ok(())
353    }
354
355    /// Finds an existing condition variable ID by its name.
356    pub fn get_id_by_name(name: &str) -> Result<OsalId> {
357        let c_name = c_name_from_str(name)?;
358        let mut var_id = MaybeUninit::uninit();
359        check(unsafe { ffi::OS_CondVarGetIdByName(var_id.as_mut_ptr(), c_name.as_ptr()) })?;
360        Ok(OsalId(unsafe { var_id.assume_init() }))
361    }
362
363    /// Retrieves information about this condition variable.
364    pub fn get_info(&self) -> Result<CondVarProp> {
365        let mut prop = MaybeUninit::<ffi::OS_condvar_prop_t>::uninit();
366        check(unsafe { ffi::OS_CondVarGetInfo(self.id, prop.as_mut_ptr()) })?;
367        let prop = unsafe { prop.assume_init() };
368
369        Ok(CondVarProp {
370            name: string_from_c_buf(&prop.name)?,
371            creator: OsalId(prop.creator),
372        })
373    }
374}
375
376impl Drop for CondVar {
377    fn drop(&mut self) {
378        let _ = unsafe { ffi::OS_CondVarDelete(self.id) };
379    }
380}