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