zephyr/sys/sync/
semaphore.rs

1// Copyright (c) 2024 Linaro LTD
2// SPDX-License-Identifier: Apache-2.0
3
4//! Zephyr Semaphore support
5//!
6//! This is a thin wrapper around Zephyr's `k_sem`.  This is one of the few of the `sys` primitives
7//! in Zephyr that is actually perfectly usable on its own, without needing additional wrappers.
8//!
9//! Zephyr implements counting semaphores, with both an upper and lower bound on the count.  Note
10//! that calling 'give' on a semaphore that is at the maximum count will discard the 'give'
11//! operation, which in situation where counting is actually desired, will result in the count being
12//! incorrect.
13
14use core::ffi::c_uint;
15use core::fmt;
16
17use crate::object::{ObjectInit, ZephyrObject};
18use crate::{
19    error::{to_result_void, Result},
20    raw::{k_sem, k_sem_count_get, k_sem_give, k_sem_init, k_sem_reset, k_sem_take},
21    time::Timeout,
22};
23
24pub use crate::raw::K_SEM_MAX_LIMIT;
25
26/// General Zephyr Semaphores
27pub struct Semaphore(pub(crate) ZephyrObject<k_sem>);
28
29/// By nature, Semaphores are both Sync and Send.  Safety is handled by the underlying Zephyr
30/// implementation (which is why Clone is also implemented).
31unsafe impl Sync for Semaphore {}
32unsafe impl Send for Semaphore {}
33
34impl Semaphore {
35    /// Create a new semaphore.
36    ///
37    /// Create a new dynamically allocated Semaphore.  This semaphore can only be used from system
38    /// threads.  The arguments are as described in [the
39    /// docs](https://docs.zephyrproject.org/latest/kernel/services/synchronization/semaphores.html).
40    ///
41    /// Note that this API has changed, and it now doesn't return a Result, since the Result time
42    /// generally doesn't work (in stable rust) with const.
43    pub const fn new(initial_count: c_uint, limit: c_uint) -> Semaphore {
44        // Due to delayed init, we need to replicate the object checks in the C `k_sem_init`.
45
46        if limit == 0 || initial_count > limit {
47            panic!("Invalid semaphore initialization");
48        }
49
50        let this = <ZephyrObject<k_sem>>::new_raw();
51
52        unsafe {
53            let addr = this.get_uninit();
54            (*addr).count = initial_count;
55            (*addr).limit = limit;
56        }
57
58        // to_result_void(k_sem_init(item.get(), initial_count, limit))?;
59        Semaphore(this)
60    }
61
62    /// Take a semaphore.
63    ///
64    /// Can be called from ISR if called with [`NoWait`].
65    ///
66    /// [`NoWait`]: crate::time::NoWait
67    pub fn take<T>(&self, timeout: T) -> Result<()>
68    where
69        T: Into<Timeout>,
70    {
71        let timeout: Timeout = timeout.into();
72        let ret = unsafe { k_sem_take(self.0.get(), timeout.0) };
73        to_result_void(ret)
74    }
75
76    /// Give a semaphore.
77    ///
78    /// This routine gives to the semaphore, unless the semaphore is already at its maximum
79    /// permitted count.
80    pub fn give(&self) {
81        unsafe { k_sem_give(self.0.get()) }
82    }
83
84    /// Resets a semaphor's count to zero.
85    ///
86    /// This resets the count to zero.  Any outstanding [`take`] calls will be aborted with
87    /// `Error(EAGAIN)`.
88    ///
89    /// [`take`]: Self::take
90    pub fn reset(&self) {
91        unsafe { k_sem_reset(self.0.get()) }
92    }
93
94    /// Get a semaphore's count.
95    ///
96    /// Returns the current count.
97    pub fn count_get(&self) -> usize {
98        unsafe { k_sem_count_get(self.0.get()) as usize }
99    }
100}
101
102impl ObjectInit<k_sem> for ZephyrObject<k_sem> {
103    fn init(item: *mut k_sem) {
104        // SAFEFY: Get the initial values used in new.  The address may have changed, but only due
105        // to a move.
106        unsafe {
107            let count = (*item).count;
108            let limit = (*item).limit;
109
110            if k_sem_init(item, count, limit) != 0 {
111                // Note that with delayed init, we cannot do anything with invalid values.  We're
112                // replicated the check in `new` above, so would only catch semantic changes in the
113                // implementation of `k_sem_init`.
114                unreachable!();
115            }
116        }
117    }
118}
119
120impl fmt::Debug for Semaphore {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        write!(f, "sys::Semaphore")
123    }
124}