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}