-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement a More Ergonomic & Efficient Mutex for the Embedded #45
Comments
When we want mutable access to the value behind mutex, essentially it boils down to two of the most common scenarios:
Here are some types I've come up with in my code: 1.
|
I abstract the usage as: #[cfg(feature = "critical-section")]
mod critical_section;
#[cfg(feature = "critical-section")]
pub(crate) use critical_section::Mutex;
#[cfg(all(not(feature = "critical-section"), feature = "std"))]
mod std_mutex;
#[cfg(all(not(feature = "critical-section"), feature = "std"))]
pub(crate) use std_mutex::Mutex;
// std_mutext.rs
pub(crate) struct Mutex<T>(std::sync::Mutex<T>);
impl<T> Mutex<T> {
#[must_use]
#[inline]
pub(crate) const fn new(t: T) -> Self {
Self(std::sync::Mutex::new(t))
}
pub(crate) fn with<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut T) -> R,
{
let mut this = self.0.lock().unwrap_or_else(PoisonError::into_inner);
f(&mut this)
}
}
// critical_section.rs
pub(crate) struct Mutex<T>(critical_section::Mutex<RefCell<T>>);
impl<T> Mutex<T> {
#[must_use]
#[inline]
pub(crate) const fn new(t: T) -> Self {
Self(critical_section::Mutex::new(t))
}
pub(crate) fn with<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut T) -> R,
{
critical_section::with(|cs| {
let mut this = self
.0
.borrow(cs)
.try_borrow_mut()
.expect("borrow with token 'cs' exactly once");
f(&mut this)
})
}
} It seems work well. |
Mutexes are mostly used for static variables, and also a lot of times the variable needs to be initialized later.
The current Mutex implementation doesn't feel very ergonomic:
Mutex<RefCell<Option<T>>>
withvar.borrow(cs).borrow_mut().replace(value)
for initialization andvar.borrow(cs).borrow_mut().as_mut().unwrap()
for mutable access. These are very long expressions for simple purposes.RefCell
comes with an extraisize
32bit space withOption
adding anotheru8
4bit overhead. Each time accessing a mutable reference to a variable basically involves twounwraps
, once for checking uniqueness and once for checking initialization, which is unnecessary.I am thinking of an easier to use + more efficient implementation:
We can use a single enum to keep track of the state of the mutex cell. So the value can be
None
),The mutex can be either initialized with a value or not.
Value can be initialized once if it was not initialized (otherwise panic).
Locking the mutex returns None if
try_lock
fails or if the value is uninitialized.The LockGuard restore lock state back to
Unlock
on drop.Miscellaneous.
Usage
Now we can write like
Of course we should not use the name
Mutex
directly since we need to have backward compatibility. I am thinking of naming itMutexCell
or perhapsMutexOption
. If anyone finds it useful maybe we can PR it into the library.The text was updated successfully, but these errors were encountered: