Skip to content

Commit bcf49dd

Browse files
committed
tokenparts, mutex: Add and use ValueInThread wrapper
This allows locking a mutex without the overhead of a check whether code is running in thread mode. Contributes-To: #18
1 parent d915c36 commit bcf49dd

File tree

3 files changed

+63
-11
lines changed

3 files changed

+63
-11
lines changed

src/mutex.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,10 @@ impl<T> Mutex<T> {
4242
/// Get an accessor to the mutex when the mutex is available
4343
#[doc(alias = "mutex_lock")]
4444
pub fn lock(&self) -> MutexGuard<T> {
45-
assert!(
46-
crate::interrupt::irq_is_in() == false,
47-
"Mutex::lock may only be called outside of interrupt contexts.",
48-
);
49-
// unsafe: All preconditions of the C function are met (not-NULL through taking a &self,
50-
// being initialized through RAII guarantees, thread context was checked before).
51-
unsafe { riot_sys::mutex_lock(crate::inline_cast_mut(self.mutex.get())) };
52-
MutexGuard { mutex: &self }
45+
crate::thread::InThread::new()
46+
.expect("Mutex::lock may only be called outside of interrupt contexts")
47+
.promote(&self)
48+
.lock()
5349
}
5450

5551
/// Get an accessor to the mutex if the mutex is available
@@ -93,6 +89,20 @@ impl<T> Mutex<T> {
9389
}
9490
}
9591

92+
impl<T> crate::thread::ValueInThread<&Mutex<T>> {
93+
/// Get an accessor to the mutex when the mutex is available
94+
///
95+
/// Through the [crate::thread::ValueInThread], this is already guaranteed to run in a thread
96+
/// context, so no additional check is performed.
97+
#[doc(alias = "mutex_lock")]
98+
pub fn lock(&self) -> MutexGuard<T> {
99+
// unsafe: All preconditions of the C function are met (not-NULL through taking a &self,
100+
// being initialized through RAII guarantees, thread context is in the InThread).
101+
unsafe { riot_sys::mutex_lock(crate::inline_cast_mut(self.mutex.get())) };
102+
MutexGuard { mutex: &self }
103+
}
104+
}
105+
96106
unsafe impl<T: Send> Send for Mutex<T> {}
97107
unsafe impl<T: Send> Sync for Mutex<T> {}
98108

src/thread.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub use riot_c::*;
2323
mod tokenparts;
2424
#[cfg(doc)]
2525
pub use tokenparts::TokenParts;
26-
pub use tokenparts::{InIrq, InThread, StartToken, TerminationToken};
26+
pub use tokenparts::{InIrq, InThread, StartToken, TerminationToken, ValueInThread};
2727

2828
mod stack_stats;
2929
pub use stack_stats::{StackStats, StackStatsError};

src/thread/tokenparts.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,13 +185,13 @@ impl<const MQ: bool> TokenParts<true, MQ, true> {
185185
}
186186

187187
/// Zero-size statement that the current code is not running in an interrupt
188-
#[derive(Copy, Clone)]
188+
#[derive(Copy, Clone, Debug)]
189189
pub struct InThread {
190190
_not_send: PhantomData<*const ()>,
191191
}
192192

193193
/// Zero-size statement that the current code is running in an interrupt
194-
#[derive(Copy, Clone)]
194+
#[derive(Copy, Clone, Debug)]
195195
pub struct InIrq {
196196
_not_send: PhantomData<*const ()>,
197197
}
@@ -213,6 +213,15 @@ impl InThread {
213213
false => Ok(unsafe { InThread::new_unchecked() }),
214214
}
215215
}
216+
217+
/// Wrap a `value` in a [`ValueInThread`]. This makes it non-Send, but may make additional
218+
/// (safe) methods on it, using the knowledge that it is still being used inside a thread.
219+
pub fn promote<T>(self, value: T) -> ValueInThread<T> {
220+
ValueInThread {
221+
value,
222+
in_thread: self,
223+
}
224+
}
216225
}
217226

218227
impl InIrq {
@@ -230,3 +239,36 @@ impl InIrq {
230239
}
231240
}
232241
}
242+
243+
/// A value combined with an [`InThread`'] marker
244+
///
245+
/// This does barely implement anything on its own, but the module implementing `T` might provide
246+
/// extra methods.
247+
pub struct ValueInThread<T> {
248+
value: T,
249+
in_thread: InThread,
250+
}
251+
252+
impl<T> ValueInThread<T> {
253+
/// Extract the wrapped value
254+
///
255+
/// This does not produce the original `in_thread` value; these are easy enough to re-obtain or
256+
/// to keep a copy of around.
257+
pub fn into_inner(self) -> T {
258+
self.value
259+
}
260+
}
261+
262+
impl<T> core::ops::Deref for ValueInThread<T> {
263+
type Target = T;
264+
265+
fn deref(&self) -> &T {
266+
&self.value
267+
}
268+
}
269+
270+
impl<T> core::ops::DerefMut for ValueInThread<T> {
271+
fn deref_mut(&mut self) -> &mut T {
272+
&mut self.value
273+
}
274+
}

0 commit comments

Comments
 (0)