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}