Skip to main content

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}