zephyr/sys/sync/
mutex.rs

1// Copyright (c) 2024 Linaro LTD
2// SPDX-License-Identifier: Apache-2.0
3
4//! Zephyr `k_mutex` wrapper.
5//!
6//! This module implements a thing wrapper around the `k_mutex` type in Zephyr.  It works with the
7//! kernel [`object`] system, to allow the mutexes to be defined statically.
8//!
9//! [`object`]: crate::object
10
11use crate::object::{ObjectInit, StaticKernelObject, ZephyrObject};
12use crate::sys::K_FOREVER;
13use crate::{
14    error::{to_result_void, Result},
15    raw::{
16        k_condvar, k_condvar_broadcast, k_condvar_init, k_condvar_signal, k_condvar_wait, k_mutex,
17        k_mutex_init, k_mutex_lock, k_mutex_unlock,
18    },
19    time::Timeout,
20};
21use core::fmt;
22
23/// A Zephyr `k_mutux` usable from safe Rust code.
24///
25/// This merely wraps a pointer to the kernel object.  It implements clone, send and sync as it is
26/// safe to have multiple instances of these, as well as use them across multiple threads.
27///
28/// Note that these are Safe in the sense that memory safety is guaranteed.  Attempts to
29/// recursively lock, or incorrect nesting can easily result in deadlock.
30///
31/// Safety: Typically, the Mutex type in Rust does not implement Clone, and must be shared between
32/// threads using Arc.  However, these sys Mutexes are wrappers around static kernel objects, and
33/// Drop doesn't make sense for them.  In addition, Arc requires alloc, and one possible place to
34/// make use of the sys Mutex is to be able to do so in an environment without alloc.
35///
36/// This mutex type of only of limited use to application programs.  It can be used as a simple
37/// binary semaphore, although it has strict semantics, requiring the release to be called by the
38/// same thread that called lock.  It can be used to protect data that Rust itself is either not
39/// managing, or is managing in an unsafe way.
40///
41/// For a Mutex type that is useful in a Rust type of manner, please see the regular [`sync::Mutex`]
42/// type.
43///
44/// [`sync::Mutex`]: http://example.com/TODO
45pub struct Mutex {
46    /// The raw Zephyr mutex.
47    item: ZephyrObject<k_mutex>,
48}
49
50impl Mutex {
51    /// Create a new Mutex in an unlocked state.
52    ///
53    /// Create a new dynamically allocated Mutex.  The Mutex can only be used from system threads.
54    pub const fn new() -> Mutex {
55        Mutex {
56            item: <ZephyrObject<k_mutex>>::new_raw(),
57        }
58    }
59
60    /// Lock a Zephyr Mutex.
61    ///
62    /// Will wait for the lock, returning status, with `Ok(())` indicating the lock has been
63    /// acquired, and an error indicating a timeout (Zephyr returns different errors depending on
64    /// the reason).
65    pub fn lock<T>(&self, timeout: T) -> Result<()>
66    where
67        T: Into<Timeout>,
68    {
69        let timeout: Timeout = timeout.into();
70        to_result_void(unsafe { k_mutex_lock(self.item.get(), timeout.0) })
71    }
72
73    /// Unlock a Zephyr Mutex.
74    ///
75    /// The mutex must already be locked by the calling thread.  Mutexes may not be unlocked in
76    /// ISRs.
77    pub fn unlock(&self) -> Result<()> {
78        to_result_void(unsafe { k_mutex_unlock(self.item.get()) })
79    }
80}
81
82impl ObjectInit<k_mutex> for ZephyrObject<k_mutex> {
83    fn init(item: *mut k_mutex) {
84        // SAFETY: ZephyrObject handles initialization and move prevention.
85        unsafe {
86            k_mutex_init(item);
87        }
88    }
89}
90
91/// A static Zephyr `k_mutex`
92///
93/// This is intended to be used from within the `kobj_define!` macro.  It declares a static
94/// `k_mutex` that will be properly registered with the Zephyr object system.  Call [`init_once`] to
95/// get the [`Mutex`] that it represents.
96///
97/// [`init_once`]: StaticMutex::init_once
98pub type StaticMutex = StaticKernelObject<k_mutex>;
99
100unsafe impl Sync for Mutex {}
101unsafe impl Send for Mutex {}
102
103// Sync and Send are meaningful, as the underlying Zephyr API can use these values from any thread.
104// Care must be taken to use these in a safe manner.
105unsafe impl Sync for StaticMutex {}
106unsafe impl Send for StaticMutex {}
107
108impl fmt::Debug for Mutex {
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110        // SAFETY: Address is just gotten to print for diagnostics.
111        write!(f, "sys::Mutex {:?}", unsafe { self.item.get() })
112    }
113}
114
115/// A Condition Variable
116///
117/// Lightweight wrappers for Zephyr's `k_condvar`.
118pub struct Condvar {
119    item: ZephyrObject<k_condvar>,
120}
121
122#[doc(hidden)]
123pub type StaticCondvar = StaticKernelObject<k_condvar>;
124
125unsafe impl Sync for StaticKernelObject<k_condvar> {}
126
127unsafe impl Sync for Condvar {}
128unsafe impl Send for Condvar {}
129
130impl Condvar {
131    /// Create a new Condvar.
132    ///
133    /// Create a new dynamically allocated Condvar.  The Condvar can only be used from system threads.
134    pub const fn new() -> Condvar {
135        Condvar {
136            item: <ZephyrObject<k_condvar>>::new_raw(),
137        }
138    }
139
140    /// Wait for someone else using this mutex/condvar pair to notify.
141    ///
142    /// Note that this requires the lock to be held by use, but as this is a low-level binding to
143    /// Zephyr's interfaces, this is not enforced.  See [`sync::Condvar`] for a safer and easier to
144    /// use interface.
145    ///
146    /// [`sync::Condvar`]: http://www.example.com/TODO
147    // /// [`sync::Condvar`]: crate::sync::Condvar
148    pub fn wait(&self, lock: &Mutex) {
149        unsafe {
150            k_condvar_wait(self.item.get(), lock.item.get(), K_FOREVER);
151        }
152    }
153
154    // TODO: timeout.
155
156    /// Wake a single thread waiting on this condition variable.
157    pub fn notify_one(&self) {
158        unsafe {
159            k_condvar_signal(self.item.get());
160        }
161    }
162
163    /// Wake all threads waiting on this condition variable.
164    pub fn notify_all(&self) {
165        unsafe {
166            k_condvar_broadcast(self.item.get());
167        }
168    }
169}
170
171impl ObjectInit<k_condvar> for ZephyrObject<k_condvar> {
172    fn init(item: *mut k_condvar) {
173        // SAFETY: ZephyrObject handles initialization and move prevention.
174        unsafe {
175            k_condvar_init(item);
176        }
177    }
178}
179
180impl fmt::Debug for Condvar {
181    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182        // SAFETY: Just getting the address to print.
183        write!(f, "sys::Condvar {:?}", unsafe { self.item.get() })
184    }
185}