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}