Skip to content

Commit 75a34de

Browse files
committed
zephyr: Implement sync::Mutex
Implement std-style Mutex on top of the sys::Mutex. This style of Mutex protects a piece of data that can only be accessed by locking the mutex. RAII is used to ensure the lock is unlocked, based on scoping. Signed-off-by: David Brown <[email protected]>
1 parent 136304e commit 75a34de

File tree

1 file changed

+123
-0
lines changed

1 file changed

+123
-0
lines changed

zephyr/src/sync.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@
55
//! [`crossbeam-channel`](https://docs.rs/crossbeam-channel/latest/crossbeam_channel/), in as much
66
//! as it makes sense.
77
8+
use core::{
9+
cell::UnsafeCell,
10+
fmt,
11+
marker::PhantomData,
12+
ops::{Deref, DerefMut},
13+
};
14+
15+
use crate::time::Forever;
16+
use crate::sys::sync as sys;
17+
818
pub mod atomic {
919
//! Re-export portable atomic.
1020
//!
@@ -22,3 +32,116 @@ pub mod atomic {
2232

2333
#[cfg(CONFIG_RUST_ALLOC)]
2434
pub use portable_atomic_util::Arc;
35+
36+
/// Until poisoning is implemented, mutexes never return an error, and we just get back the guard.
37+
pub type LockResult<Guard> = Result<Guard, ()>;
38+
39+
/// A mutual exclusion primitive useful for protecting shared data.
40+
///
41+
/// This mutex will block threads waiting for the lock to become available. This is modeled after
42+
/// [`std::sync::Mutex`](https://doc.rust-lang.org/stable/std/sync/struct.Mutex.html), and attempts
43+
/// to implement that API as closely as makes sense on Zephyr. Currently, it has the following
44+
/// differences:
45+
/// - Poisoning: This does not yet implement poisoning, as there is no way to recover from panic at
46+
/// this time on Zephyr.
47+
/// - Allocation: `new` is not yet provided, and will be provided once kernel object pools are
48+
/// implemented. Please use `new_from` which takes a reference to a statically allocated
49+
/// `sys::Mutex`.
50+
pub struct Mutex<T: ?Sized> {
51+
inner: sys::Mutex,
52+
// poison: ...
53+
data: UnsafeCell<T>,
54+
}
55+
56+
// At least if correctly done, the Mutex provides for Send and Sync as long as the inner data
57+
// supports Send.
58+
unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
59+
unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
60+
61+
impl<T> fmt::Debug for Mutex<T> {
62+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
63+
write!(f, "Mutex {:?}", self.inner)
64+
}
65+
}
66+
67+
/// An RAII implementation of a "scoped lock" of a mutex. When this structure is dropped (faslls
68+
/// out of scope), the lock will be unlocked.
69+
///
70+
/// The data protected by the mutex can be accessed through this guard via its [`Deref`] and
71+
/// [`DerefMut`] implementations.
72+
///
73+
/// This structure is created by the [`lock`] and [`try_lock`] methods on [`Mutex`].
74+
///
75+
/// Taken directly from
76+
/// [`std::sync::MutexGuard`](https://doc.rust-lang.org/stable/std/sync/struct.MutexGuard.html).
77+
pub struct MutexGuard<'a, T: ?Sized + 'a> {
78+
lock: &'a Mutex<T>,
79+
// until <https://github.com/rust-lang/rust/issues/68318> is implemented, we have to mark unsend
80+
// explicitly. This can be done by holding Phantom data with an unsafe cell in it.
81+
_nosend: PhantomData<UnsafeCell<()>>,
82+
}
83+
84+
// Make sure the guard doesn't get sent.
85+
// Negative trait bounds are unstable, see marker above.
86+
// impl<T: ?Sized> !Send for MutexGuard<'_, T> {}
87+
unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}
88+
89+
impl<T> Mutex<T> {
90+
/// Construct a new wrapped Mutex, using the given underlying sys mutex. This is different that
91+
/// `std::sync::Mutex` in that in Zephyr, objects are frequently allocated statically, and the
92+
/// sys Mutex will be taken by this structure. It is safe to share the underlying Mutex between
93+
/// different items, but without careful use, it is easy to deadlock, so it is not recommended.
94+
pub const fn new_from(t: T, raw_mutex: sys::Mutex) -> Mutex<T> {
95+
Mutex { inner: raw_mutex, data: UnsafeCell::new(t) }
96+
}
97+
}
98+
99+
impl<T: ?Sized> Mutex<T> {
100+
/// Acquires a mutex, blocking the current thread until it is able to do so.
101+
///
102+
/// This function will block the local thread until it is available to acquire the mutex. Upon
103+
/// returning, the thread is the only thread with the lock held. An RAII guard is returned to
104+
/// allow scoped unlock of the lock. When the guard goes out of scope, the mutex will be
105+
/// unlocked.
106+
///
107+
/// In `std`, an attempt to lock a mutex by a thread that already holds the mutex is
108+
/// unspecified. Zephyr explicitly supports this behavior, by simply incrementing a lock
109+
/// count.
110+
pub fn lock(&self) -> LockResult<MutexGuard<'_, T>> {
111+
// With `Forever`, should never return an error.
112+
self.inner.lock(Forever).unwrap();
113+
unsafe {
114+
MutexGuard::new(self)
115+
}
116+
}
117+
}
118+
119+
impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> {
120+
unsafe fn new(lock: &'mutex Mutex<T>) -> LockResult<MutexGuard<'mutex, T>> {
121+
// poison todo
122+
Ok(MutexGuard { lock, _nosend: PhantomData })
123+
}
124+
}
125+
126+
impl<T: ?Sized> Deref for MutexGuard<'_, T> {
127+
type Target = T;
128+
129+
fn deref(&self) -> &T {
130+
unsafe {
131+
&*self.lock.data.get()
132+
}
133+
}
134+
}
135+
136+
impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
137+
fn deref_mut(&mut self) -> &mut T {
138+
unsafe { &mut *self.lock.data.get() }
139+
}
140+
}
141+
142+
impl<T: ?Sized> Drop for MutexGuard<'_, T> {
143+
#[inline]
144+
fn drop(&mut self) {
145+
self.lock.inner.unlock().unwrap();
146+
}
147+
}

0 commit comments

Comments
 (0)