Skip to main content

osal_rs/freertos/
semaphore.rs

1/***************************************************************************
2 *
3 * osal-rs
4 * Copyright (C) 2026 Antonio Salsi <passy.linux@zresa.it>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
18 *
19 ***************************************************************************/
20
21//! Counting semaphore synchronization primitives for FreeRTOS.
22//!
23//! This module provides counting semaphores for resource management and signaling
24//! between threads and ISRs. Semaphores maintain a count and can be used to
25//! coordinate access to shared resources or signal event completion.
26
27use core::fmt::{Debug, Display};
28use core::ops::Deref;
29use core::ptr::null_mut;
30
31use super::ffi::{SemaphoreHandle, pdFAIL, pdFALSE};
32use super::system::System;
33use super::types::{BaseType, UBaseType};
34use crate::traits::{SemaphoreFn, SystemFn, ToTick};
35use crate::utils::{Error, Result, OsalRsBool};
36
37/// A counting semaphore for resource management and signaling.
38///
39/// Semaphores maintain a count that can be incremented (signaled) and decremented (waited).
40/// They are useful for:
41/// - Resource counting (e.g., managing a pool of N resources)
42/// - Event signaling between threads or from ISRs
43/// - Producer-consumer synchronization
44///
45/// # Examples
46///
47/// ## Basic binary semaphore (mutex alternative)
48///
49/// ```ignore
50/// use osal_rs::os::{Semaphore, SemaphoreFn};
51/// use core::time::Duration;
52/// 
53/// // Create a binary semaphore (max_count = 1)
54/// let sem = Semaphore::new(1, 1).unwrap();
55/// 
56/// // Wait (take) the semaphore
57/// if sem.wait(Duration::from_millis(100)).into() {
58///     // Critical section
59///     println!("Acquired semaphore");
60///     
61///     // Signal (give) the semaphore
62///     sem.signal();
63/// }
64/// ```
65///
66/// ## Resource pool management
67///
68/// ```ignore
69/// use osal_rs::os::{Semaphore, SemaphoreFn, Thread};
70/// use alloc::sync::Arc;
71/// use core::time::Duration;
72/// 
73/// // Create semaphore for 5 resources
74/// let resources = Arc::new(Semaphore::new(5, 5).unwrap());
75/// 
76/// let sem_clone = resources.clone();
77/// let worker = Thread::new("worker", 2048, 5, move || {
78///     loop {
79///         // Wait for an available resource
80///         if sem_clone.wait(Duration::from_secs(1)).into() {
81///             println!("Resource acquired");
82///             
83///             // Use resource...
84///             Duration::from_millis(500).sleep();
85///             
86///             // Release resource
87///             sem_clone.signal();
88///         }
89///     }
90/// }).unwrap();
91/// ```
92///
93/// ## Event signaling from ISR
94///
95/// ```ignore
96/// use osal_rs::os::{Semaphore, SemaphoreFn};
97/// use alloc::sync::Arc;
98/// 
99/// let event_sem = Arc::new(Semaphore::new(1, 0).unwrap());  // Initially unavailable
100/// let sem_clone = event_sem.clone();
101/// 
102/// // In interrupt handler:
103/// // sem_clone.signal_from_isr();  // Signal event occurred
104/// 
105/// // In thread:
106/// if event_sem.wait(1000).into() {
107///     println!("Event received!");
108/// }
109/// ```
110///
111/// ## Counting events
112///
113/// ```ignore
114/// use osal_rs::os::{Semaphore, SemaphoreFn};
115/// use core::time::Duration;
116/// 
117/// // Create semaphore with max_count=10, initially empty
118/// let counter = Semaphore::new(10, 0).unwrap();
119/// 
120/// // Signal 3 times
121/// counter.signal();
122/// counter.signal();
123/// counter.signal();
124/// 
125/// // Process 3 events
126/// for _ in 0..3 {
127///     if counter.wait(Duration::from_millis(10)).into() {
128///         println!("Processing event");
129///     }
130/// }
131/// ```
132pub struct Semaphore (SemaphoreHandle);
133
134unsafe impl Send for Semaphore {}
135unsafe impl Sync for Semaphore {}
136
137impl Semaphore {
138        /// Creates a new counting semaphore.
139    ///
140    /// # Parameters
141    ///
142    /// * `max_count` - Maximum count value the semaphore can reach
143    /// * `initial_count` - Initial count value
144    ///
145    /// # Returns
146    ///
147    /// * `Ok(Semaphore)` - Semaphore created successfully
148    /// * `Err(Error::OutOfMemory)` - Failed to allocate semaphore
149    ///
150    /// # Examples
151    ///
152    /// ```ignore
153    /// use osal_rs::os::{Semaphore, SemaphoreFn};
154    /// 
155    /// // Binary semaphore
156    /// let binary_sem = Semaphore::new(1, 1).unwrap();
157    /// 
158    /// // Counting semaphore for 5 resources
159    /// let counting_sem = Semaphore::new(5, 5).unwrap();
160    /// ```
161    pub fn new(max_count: UBaseType, initial_count: UBaseType) -> Result<Self> {
162        let handle = xSemaphoreCreateCounting!(max_count, initial_count);
163        if handle.is_null() {
164            Err(Error::OutOfMemory)
165        } else {
166            Ok(Self (handle))
167        }
168    }
169
170    /// Creates a counting semaphore with maximum possible count.
171    ///
172    /// Sets `max_count` to `UBaseType::MAX`.
173    ///
174    /// # Parameters
175    ///
176    /// * `initial_count` - Initial count value
177    ///
178    /// # Returns
179    ///
180    /// * `Ok(Semaphore)` - Semaphore created successfully
181    /// * `Err(Error::OutOfMemory)` - Failed to allocate
182    ///
183    /// # Examples
184    ///
185    /// ```ignore
186    /// use osal_rs::os::{Semaphore, SemaphoreFn};
187    /// 
188    /// let sem = Semaphore::new_with_count(0).unwrap();
189    /// ```
190    pub fn new_with_count(initial_count: UBaseType) -> Result<Self> {
191        let handle = xSemaphoreCreateCounting!(UBaseType::MAX, initial_count);
192        if handle.is_null() {
193            Err(Error::OutOfMemory)
194        } else {
195            Ok(Self (handle))
196        }
197    }
198
199}
200
201impl SemaphoreFn for Semaphore {
202
203    /// Waits to acquire the semaphore (decrements count).
204    ///
205    /// Blocks until semaphore is available or timeout expires.
206    ///
207    /// # Parameters
208    ///
209    /// * `ticks_to_wait` - Maximum time to wait (supports `Duration` via `ToTick`)
210    ///
211    /// # Returns
212    ///
213    /// * `OsalRsBool::True` - Semaphore acquired
214    /// * `OsalRsBool::False` - Timeout or error
215    ///
216    /// # Examples
217    ///
218    /// ```ignore
219    /// use osal_rs::os::{Semaphore, SemaphoreFn};
220    /// use core::time::Duration;
221    /// 
222    /// let sem = Semaphore::new(1, 1).unwrap();
223    /// if sem.wait(Duration::from_millis(100)).into() {
224    ///     // Critical section
225    ///     sem.signal();
226    /// }
227    /// ```
228    fn wait(&self, ticks_to_wait: impl ToTick) -> OsalRsBool {
229        if xSemaphoreTake!(self.0, ticks_to_wait.to_ticks()) != pdFAIL {
230            OsalRsBool::True
231        } else {
232            OsalRsBool::False
233        }
234    }
235
236    /// Waits to acquire the semaphore from ISR context (non-blocking).
237    ///
238    /// # Returns
239    ///
240    /// * `OsalRsBool::True` - Semaphore acquired
241    /// * `OsalRsBool::False` - Semaphore not available
242    ///
243    /// # Examples
244    ///
245    /// ```ignore
246    /// // In ISR:
247    /// if sem.wait_from_isr().into() {
248    ///     // Handle event
249    /// }
250    /// ```
251    fn wait_from_isr(&self) -> OsalRsBool {
252        let mut higher_priority_task_woken: BaseType = pdFALSE;
253        if xSemaphoreTakeFromISR!(self.0, &mut higher_priority_task_woken) != pdFAIL {
254
255            System::yield_from_isr(higher_priority_task_woken);
256
257            OsalRsBool::True
258        } else {
259
260            OsalRsBool::False
261        }
262    }
263    
264    /// Signals (releases) the semaphore (increments count).
265    ///
266    /// # Returns
267    ///
268    /// * `OsalRsBool::True` - Semaphore signaled successfully
269    /// * `OsalRsBool::False` - Error (e.g., count already at maximum)
270    ///
271    /// # Examples
272    ///
273    /// ```ignore
274    /// use osal_rs::os::{Semaphore, SemaphoreFn};
275    /// 
276    /// let sem = Semaphore::new(1, 0).unwrap();
277    /// sem.signal();  // Make semaphore available
278    /// ```
279    fn signal(&self) -> OsalRsBool {
280        if xSemaphoreGive!(self.0) != pdFAIL {
281            OsalRsBool::True
282        } else {
283            OsalRsBool::False
284        }
285    }
286    
287    /// Signals the semaphore from ISR context.
288    ///
289    /// Automatically yields to higher priority tasks if needed.
290    ///
291    /// # Returns
292    ///
293    /// * `OsalRsBool::True` - Semaphore signaled successfully
294    /// * `OsalRsBool::False` - Error
295    ///
296    /// # Examples
297    ///
298    /// ```ignore
299    /// // In ISR:
300    /// sem.signal_from_isr();
301    /// ```
302    fn signal_from_isr(&self) -> OsalRsBool {
303        let mut higher_priority_task_woken: BaseType = pdFALSE;
304        if xSemaphoreGiveFromISR!(self.0, &mut higher_priority_task_woken) != pdFAIL {
305            
306            System::yield_from_isr(higher_priority_task_woken);
307
308            OsalRsBool::True
309        } else {
310            OsalRsBool::False
311        }
312    }
313    
314    /// Deletes the semaphore and frees its resources.
315    ///
316    /// After calling this, the semaphore handle is set to null and should not be used.
317    ///
318    /// # Safety
319    ///
320    /// Ensure no threads are waiting on this semaphore before deleting it.
321    ///
322    /// # Examples
323    ///
324    /// ```ignore
325    /// use osal_rs::os::{Semaphore, SemaphoreFn};
326    /// 
327    /// let mut sem = Semaphore::new(1, 1).unwrap();
328    /// // Use the semaphore...
329    /// sem.delete();
330    /// ```
331    fn delete(&mut self) {
332        vSemaphoreDelete!(self.0);
333        self.0 = null_mut();
334    }
335
336
337}
338
339
340/// Automatically deletes the semaphore when it goes out of scope.
341/// 
342/// This ensures proper cleanup of FreeRTOS resources.
343impl Drop for Semaphore {
344    fn drop(&mut self) {
345        if self.0.is_null() {
346            return;
347        }
348        self.delete();
349    }
350}
351
352/// Allows dereferencing to the underlying FreeRTOS semaphore handle.
353impl Deref for Semaphore {
354    type Target = SemaphoreHandle;
355
356    fn deref(&self) -> &Self::Target {
357        &self.0
358    }
359}
360
361/// Formats the semaphore for debugging purposes.
362impl Debug for Semaphore {
363    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
364        f.debug_struct("Semaphore")
365            .field("handle", &self.0)
366            .finish()
367    }
368}
369
370/// Formats the semaphore for display purposes.
371impl Display for Semaphore {
372    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
373        write!(f, "Semaphore {{ handle: {:?} }}", self.0)
374    }
375}