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
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 /// # Safety
317 ///
318 /// After calling this, the semaphore handle becomes invalid.
319 ///
320 /// # Examples
321 ///
322 /// ```ignore
323 /// let mut sem = Semaphore::new(1, 1).unwrap();
324 /// sem.delete();
325 /// ```
326 fn delete(&mut self) {
327 vSemaphoreDelete!(self.0);
328 self.0 = null_mut();
329 }
330
331
332}
333
334
335impl Drop for Semaphore {
336 fn drop(&mut self) {
337 if self.0.is_null() {
338 return;
339 }
340 self.delete();
341 }
342}
343
344impl Deref for Semaphore {
345 type Target = SemaphoreHandle;
346
347 fn deref(&self) -> &Self::Target {
348 &self.0
349 }
350}
351
352impl Debug for Semaphore {
353 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
354 f.debug_struct("Semaphore")
355 .field("handle", &self.0)
356 .finish()
357 }
358}
359
360impl Display for Semaphore {
361 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
362 write!(f, "Semaphore {{ handle: {:?} }}", self.0)
363 }
364}