osal_rs/async_primitives/semaphore.rs
1/***************************************************************************
2 *
3 * osal-rs
4 * Copyright (C) 2026 Antonio Salsi <passy.linux@zresa.it>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
18 *
19 ***************************************************************************/
20
21//! Async wrapper for OSAL semaphores.
22//!
23//! [`AsyncSemaphore`] exposes a `wait_async()` method that returns a `Future`.
24//! The synchronous `signal()` wakes any task sleeping in `wait_async`.
25
26use core::future::Future;
27use core::pin::Pin;
28use core::task::{Context, Poll};
29use core::time::Duration;
30
31use alloc::sync::Arc;
32
33use crate::os::Semaphore;
34use crate::os::types::UBaseType;
35use crate::traits::SemaphoreFn;
36use crate::utils::OsalRsBool;
37
38use super::waker_slot::WakerSlot;
39
40/// An async-capable semaphore wrapping an `Arc<Semaphore>`.
41pub struct AsyncSemaphore {
42 inner: Arc<Semaphore>,
43 waker: Arc<WakerSlot>,
44}
45
46impl AsyncSemaphore {
47 /// Creates an `AsyncSemaphore` with `max_count` and `initial_count`.
48 ///
49 /// # Errors
50 ///
51 /// Returns the underlying OSAL error if the semaphore cannot be created.
52 pub fn new(max_count: u32, initial_count: u32) -> crate::utils::Result<Self> {
53 let inner = Semaphore::new(
54 max_count as UBaseType,
55 initial_count as UBaseType,
56 )?;
57 Ok(Self {
58 inner: Arc::new(inner),
59 waker: Arc::new(WakerSlot::new()),
60 })
61 }
62
63 /// Signals (increments) the semaphore and wakes any async waiter.
64 pub fn signal(&self) -> OsalRsBool {
65 let result = self.inner.signal();
66 self.waker.wake();
67 result
68 }
69
70 /// Returns a `Future` that resolves when the semaphore count becomes > 0.
71 pub fn wait_async(&self) -> WaitFuture<'_> {
72 WaitFuture { sem: self }
73 }
74}
75
76/// Future returned by [`AsyncSemaphore::wait_async`].
77pub struct WaitFuture<'a> {
78 sem: &'a AsyncSemaphore,
79}
80
81impl<'a> Future for WaitFuture<'a> {
82 type Output = OsalRsBool;
83
84 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
85 // Non-blocking attempt.
86 if self.sem.inner.wait(Duration::ZERO) == OsalRsBool::True {
87 return Poll::Ready(OsalRsBool::True);
88 }
89
90 // Store waker, then retry to close the TOCTOU window.
91 self.sem.waker.store(cx.waker());
92
93 if self.sem.inner.wait(Duration::ZERO) == OsalRsBool::True {
94 Poll::Ready(OsalRsBool::True)
95 } else {
96 Poll::Pending
97 }
98 }
99}