osal_rs/freertos/
semaphore.rs

1/***************************************************************************
2 *
3 * osal-rs
4 * Copyright (C) 2023/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};
35use crate::{vSemaphoreDelete, xSemaphoreCreateCounting, xSemaphoreGive, xSemaphoreGiveFromISR, xSemaphoreTake, xSemaphoreTakeFromISR};
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
137
138impl SemaphoreFn for Semaphore {
139    /// Creates a new counting semaphore.
140    ///
141    /// # Parameters
142    ///
143    /// * `max_count` - Maximum count value the semaphore can reach
144    /// * `initial_count` - Initial count value
145    ///
146    /// # Returns
147    ///
148    /// * `Ok(Semaphore)` - Semaphore created successfully
149    /// * `Err(Error::OutOfMemory)` - Failed to allocate semaphore
150    ///
151    /// # Examples
152    ///
153    /// ```ignore
154    /// use osal_rs::os::{Semaphore, SemaphoreFn};
155    /// 
156    /// // Binary semaphore
157    /// let binary_sem = Semaphore::new(1, 1).unwrap();
158    /// 
159    /// // Counting semaphore for 5 resources
160    /// let counting_sem = Semaphore::new(5, 5).unwrap();
161    /// ```
162    fn new(max_count: UBaseType, initial_count: UBaseType) -> Result<Self> {
163        let handle = xSemaphoreCreateCounting!(max_count, initial_count);
164        if handle.is_null() {
165            Err(Error::OutOfMemory)
166        } else {
167            Ok(Self (handle))
168        }
169    }
170
171    /// Creates a counting semaphore with maximum possible count.
172    ///
173    /// Sets `max_count` to `UBaseType::MAX`.
174    ///
175    /// # Parameters
176    ///
177    /// * `initial_count` - Initial count value
178    ///
179    /// # Returns
180    ///
181    /// * `Ok(Semaphore)` - Semaphore created successfully
182    /// * `Err(Error::OutOfMemory)` - Failed to allocate
183    ///
184    /// # Examples
185    ///
186    /// ```ignore
187    /// use osal_rs::os::{Semaphore, SemaphoreFn};
188    /// 
189    /// let sem = Semaphore::new_with_count(0).unwrap();
190    /// ```
191    fn new_with_count(initial_count: UBaseType) -> Result<Self> {
192        let handle = xSemaphoreCreateCounting!(UBaseType::MAX, initial_count);
193        if handle.is_null() {
194            Err(Error::OutOfMemory)
195        } else {
196            Ok(Self (handle))
197        }
198    }
199
200    /// Waits to acquire the semaphore (decrements count).
201    ///
202    /// Blocks until semaphore is available or timeout expires.
203    ///
204    /// # Parameters
205    ///
206    /// * `ticks_to_wait` - Maximum time to wait (supports `Duration` via `ToTick`)
207    ///
208    /// # Returns
209    ///
210    /// * `OsalRsBool::True` - Semaphore acquired
211    /// * `OsalRsBool::False` - Timeout or error
212    ///
213    /// # Examples
214    ///
215    /// ```ignore
216    /// use osal_rs::os::{Semaphore, SemaphoreFn};
217    /// use core::time::Duration;
218    /// 
219    /// let sem = Semaphore::new(1, 1).unwrap();
220    /// if sem.wait(Duration::from_millis(100)).into() {
221    ///     // Critical section
222    ///     sem.signal();
223    /// }
224    /// ```
225    fn wait(&self, ticks_to_wait: impl ToTick) -> OsalRsBool {
226        if xSemaphoreTake!(self.0, ticks_to_wait.to_ticks()) != pdFAIL {
227            OsalRsBool::True
228        } else {
229            OsalRsBool::False
230        }
231    }
232
233    /// Waits to acquire the semaphore from ISR context (non-blocking).
234    ///
235    /// # Returns
236    ///
237    /// * `OsalRsBool::True` - Semaphore acquired
238    /// * `OsalRsBool::False` - Semaphore not available
239    ///
240    /// # Examples
241    ///
242    /// ```ignore
243    /// // In ISR:
244    /// if sem.wait_from_isr().into() {
245    ///     // Handle event
246    /// }
247    /// ```
248    fn wait_from_isr(&self) -> OsalRsBool {
249        let mut higher_priority_task_woken: BaseType = pdFALSE;
250        if xSemaphoreTakeFromISR!(self.0, &mut higher_priority_task_woken) != pdFAIL {
251
252            System::yield_from_isr(higher_priority_task_woken);
253
254            OsalRsBool::True
255        } else {
256
257            OsalRsBool::False
258        }
259    }
260    
261    /// Signals (releases) the semaphore (increments count).
262    ///
263    /// # Returns
264    ///
265    /// * `OsalRsBool::True` - Semaphore signaled successfully
266    /// * `OsalRsBool::False` - Error (e.g., count already at maximum)
267    ///
268    /// # Examples
269    ///
270    /// ```ignore
271    /// use osal_rs::os::{Semaphore, SemaphoreFn};
272    /// 
273    /// let sem = Semaphore::new(1, 0).unwrap();
274    /// sem.signal();  // Make semaphore available
275    /// ```
276    fn signal(&self) -> OsalRsBool {
277        if xSemaphoreGive!(self.0) != pdFAIL {
278            OsalRsBool::True
279        } else {
280            OsalRsBool::False
281        }
282    }
283    
284    /// Signals the semaphore from ISR context.
285    ///
286    /// Automatically yields to higher priority tasks if needed.
287    ///
288    /// # Returns
289    ///
290    /// * `OsalRsBool::True` - Semaphore signaled successfully
291    /// * `OsalRsBool::False` - Error
292    ///
293    /// # Examples
294    ///
295    /// ```ignore
296    /// // In ISR:
297    /// sem.signal_from_isr();
298    /// ```
299    fn signal_from_isr(&self) -> OsalRsBool {
300        let mut higher_priority_task_woken: BaseType = pdFALSE;
301        if xSemaphoreGiveFromISR!(self.0, &mut higher_priority_task_woken) != pdFAIL {
302            
303            System::yield_from_isr(higher_priority_task_woken);
304
305            OsalRsBool::True
306        } else {
307            OsalRsBool::False
308        }
309    }
310    
311    /// Deletes the semaphore and frees its resources.
312    ///
313    /// # Safety
314    ///
315    /// After calling this, the semaphore handle becomes invalid.
316    ///
317    /// # Examples
318    ///
319    /// ```ignore
320    /// let mut sem = Semaphore::new(1, 1).unwrap();
321    /// sem.delete();
322    /// ```
323    fn delete(&mut self) {
324        vSemaphoreDelete!(self.0);
325        self.0 = null_mut();
326    }
327
328
329}
330
331
332impl Drop for Semaphore {
333    fn drop(&mut self) {
334        if self.0.is_null() {
335            return;
336        }
337        self.delete();
338    }
339}
340
341impl Deref for Semaphore {
342    type Target = SemaphoreHandle;
343
344    fn deref(&self) -> &Self::Target {
345        &self.0
346    }
347}
348
349impl Debug for Semaphore {
350    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
351        f.debug_struct("Semaphore")
352            .field("handle", &self.0)
353            .finish()
354    }
355}
356
357impl Display for Semaphore {
358    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
359        write!(f, "Semaphore {{ handle: {:?} }}", self.0)
360    }
361}