Skip to content

Commit 9bf607b

Browse files
committed
feat(sync): implement constance::sync::Mutex using a semaphore
1 parent 8421c56 commit 9bf607b

File tree

2 files changed

+195
-31
lines changed

2 files changed

+195
-31
lines changed

src/constance/src/sync.rs

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,4 @@
11
//! Safe synchronization primitives.
2-
use core::{cell::UnsafeCell, fmt, marker::PhantomData};
3-
4-
use crate::{
5-
kernel::{cfg::CfgBuilder, Hunk},
6-
prelude::*,
7-
};
8-
9-
pub struct Mutex<System, T> {
10-
hunk: Hunk<System, UnsafeCell<T>>,
11-
_phantom: PhantomData<(System, T)>,
12-
}
13-
14-
impl<System: Kernel, T: fmt::Debug + 'static> fmt::Debug for Mutex<System, T> {
15-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
16-
// TODO: Display the contents if unlocked
17-
f.debug_struct("Mutex").field("hunk", &self.hunk).finish()
18-
}
19-
}
20-
21-
impl<System: Kernel, T: 'static + Init> Mutex<System, T> {
22-
/// Construct a `Mutex`.
23-
///
24-
/// This is a configuration function. Call this method from your app's
25-
/// configuration function.
26-
pub const fn new(b: &mut CfgBuilder<System>) -> Self {
27-
Self {
28-
hunk: Hunk::<_, UnsafeCell<T>>::build().finish(b),
29-
_phantom: PhantomData,
30-
}
31-
}
32-
}
2+
pub mod mutex;
3+
#[doc(no_inline)]
4+
pub use self::mutex::Mutex;

src/constance/src/sync/mutex.rs

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
use core::{cell::UnsafeCell, fmt, marker::PhantomData};
2+
3+
use crate::{
4+
kernel::{cfg::CfgBuilder, Hunk, PollSemaphoreError, Semaphore, WaitSemaphoreError},
5+
prelude::*,
6+
};
7+
8+
/// A mutual exclusion primitive useful for protecting shared data from
9+
/// concurrent access.
10+
///
11+
/// This type is currently implemented using [`Semaphore`]. It will be
12+
/// upgraded to a real mutex (with priority inversion prevention) in a
13+
/// future version of Constance.
14+
///
15+
/// [`Semaphore`]: crate::kernel::Semaphore
16+
pub struct Mutex<System, T> {
17+
hunk: Hunk<System, UnsafeCell<T>>,
18+
sem: Semaphore<System>,
19+
_phantom: PhantomData<(System, T)>,
20+
}
21+
22+
/// An RAII implementation of a "scoped lock" of a mutex. When this structure
23+
/// is dropped, the lock will be released.
24+
///
25+
/// This structure is created by the [`lock`] and [`try_lock`] methods of
26+
/// [`Mutex`].
27+
///
28+
/// [`lock`]: Mutex::lock
29+
/// [`try_lock`]: Mutex::try_lock
30+
pub struct MutexGuard<'a, System: Kernel, T: 'static> {
31+
mutex: &'a Mutex<System, T>,
32+
_no_send_sync: PhantomData<*mut ()>,
33+
}
34+
35+
unsafe impl<System: Kernel, T: 'static + Sync> Sync for MutexGuard<'_, System, T> {}
36+
37+
/// Error type of [`Mutex::lock`].
38+
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
39+
#[repr(i8)]
40+
pub enum LockError {
41+
/// CPU Lock is active, the current context is not [waitable], or the
42+
/// current context is not [a task context].
43+
///
44+
/// [waitable]: crate#contexts
45+
/// [a task context]: crate#contexts
46+
BadContext = WaitSemaphoreError::BadContext as i8,
47+
/// The wait operation was interrupted by [`Task::interrupt`].
48+
///
49+
/// [`Task::interrupt`]: crate::kernel::Task::interrupt
50+
Interrupted = WaitSemaphoreError::Interrupted as i8,
51+
}
52+
53+
/// Error type of [`Mutex::try_lock`].
54+
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
55+
#[repr(i8)]
56+
pub enum TryLockError {
57+
/// CPU Lock is active.
58+
BadContext = PollSemaphoreError::BadContext as i8,
59+
/// The lock could not be acquire at this time because the operation would
60+
/// otherwise block.
61+
WouldBlock = PollSemaphoreError::Timeout as i8,
62+
}
63+
64+
impl<System: Kernel, T: 'static + Init> Mutex<System, T> {
65+
/// Construct a `Mutex`. The content is initialized with [`Init`].
66+
///
67+
/// This is a configuration function. Call this method from your app's
68+
/// configuration function.
69+
pub const fn new(b: &mut CfgBuilder<System>) -> Self {
70+
Self {
71+
hunk: Hunk::<_, UnsafeCell<T>>::build().finish(b),
72+
sem: Semaphore::build().initial(1).maximum(1).finish(b),
73+
_phantom: PhantomData,
74+
}
75+
}
76+
}
77+
78+
impl<System: Kernel, T: 'static> Mutex<System, T> {
79+
/// Acquire the mutex, blocking the current thread until it is able to do
80+
/// so.
81+
pub fn lock(&self) -> Result<MutexGuard<'_, System, T>, LockError> {
82+
self.sem.wait_one().map_err(|e| match e {
83+
WaitSemaphoreError::BadId => unreachable!(),
84+
WaitSemaphoreError::BadContext => LockError::BadContext,
85+
WaitSemaphoreError::Interrupted => LockError::Interrupted,
86+
})?;
87+
Ok(MutexGuard {
88+
mutex: self,
89+
_no_send_sync: PhantomData,
90+
})
91+
}
92+
93+
/// Attempt to acquire the mutex.
94+
pub fn try_lock(&self) -> Result<MutexGuard<'_, System, T>, TryLockError> {
95+
// A real mutex can't be locked by an interrupt handler. We emulate it
96+
// by a semaphore at this time, so we need to check whether this
97+
// condition is violated
98+
if !System::is_task_context() {
99+
return Err(TryLockError::BadContext);
100+
}
101+
102+
self.sem.poll_one().map_err(|e| match e {
103+
PollSemaphoreError::BadId => unreachable!(),
104+
PollSemaphoreError::BadContext => TryLockError::BadContext,
105+
PollSemaphoreError::Timeout => TryLockError::WouldBlock,
106+
})?;
107+
Ok(MutexGuard {
108+
mutex: self,
109+
_no_send_sync: PhantomData,
110+
})
111+
}
112+
113+
/// Get a raw pointer to the contained data.
114+
#[inline]
115+
pub fn get_ptr(&self) -> *mut T {
116+
self.hunk.get()
117+
}
118+
}
119+
120+
impl<System: Kernel, T: fmt::Debug + 'static> fmt::Debug for Mutex<System, T> {
121+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
122+
match self.try_lock() {
123+
Ok(guard) => f.debug_struct("Mutex").field("data", &&*guard).finish(),
124+
Err(TryLockError::BadContext) => {
125+
struct BadContextPlaceholder;
126+
impl fmt::Debug for BadContextPlaceholder {
127+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128+
f.write_str("<CPU context active>")
129+
}
130+
}
131+
132+
f.debug_struct("Mutex")
133+
.field("data", &BadContextPlaceholder)
134+
.finish()
135+
}
136+
Err(TryLockError::WouldBlock) => {
137+
struct LockedPlaceholder;
138+
impl fmt::Debug for LockedPlaceholder {
139+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
140+
f.write_str("<locked>")
141+
}
142+
}
143+
144+
f.debug_struct("Mutex")
145+
.field("data", &LockedPlaceholder)
146+
.finish()
147+
}
148+
}
149+
}
150+
}
151+
152+
impl<System: Kernel, T: fmt::Debug + 'static> fmt::Debug for MutexGuard<'_, System, T> {
153+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
154+
fmt::Debug::fmt(&**self, f)
155+
}
156+
}
157+
158+
impl<System: Kernel, T: fmt::Display + 'static> fmt::Display for MutexGuard<'_, System, T> {
159+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
160+
fmt::Display::fmt(&**self, f)
161+
}
162+
}
163+
164+
/// The destructor of `MutexGuard` that releases the lock. It will panic if
165+
/// CPU Lock is active.
166+
impl<System: Kernel, T: 'static> Drop for MutexGuard<'_, System, T> {
167+
#[inline]
168+
fn drop(&mut self) {
169+
self.mutex.sem.signal_one().unwrap();
170+
}
171+
}
172+
173+
impl<System: Kernel, T: 'static> core::ops::Deref for MutexGuard<'_, System, T> {
174+
type Target = T;
175+
#[inline]
176+
fn deref(&self) -> &Self::Target {
177+
// Safety: `MutexGuard` represents a permit acquired from the semaphore,
178+
// which grants the bearer an exclusive access to the underlying
179+
// data
180+
unsafe { &*self.mutex.hunk.get() }
181+
}
182+
}
183+
184+
impl<System: Kernel, T: 'static> core::ops::DerefMut for MutexGuard<'_, System, T> {
185+
#[inline]
186+
fn deref_mut(&mut self) -> &mut Self::Target {
187+
// Safety: `MutexGuard` represents a permit acquired from the semaphore,
188+
// which grants the bearer an exclusive access to the underlying
189+
// data
190+
unsafe { &mut *self.mutex.hunk.get() }
191+
}
192+
}

0 commit comments

Comments
 (0)