use core::sync::atomic::{AtomicBool, Ordering};
use lock_api::{GuardSend, RawMutex, RawMutexFair};
pub struct RawOneShotMutex {
lock: AtomicBool,
}
impl RawOneShotMutex {
pub const fn new() -> Self {
Self::INIT
}
}
impl Default for RawOneShotMutex {
fn default() -> Self {
Self::new()
}
}
unsafe impl RawMutex for RawOneShotMutex {
#[allow(clippy::declare_interior_mutable_const)]
const INIT: Self = Self {
lock: AtomicBool::new(false),
};
type GuardMarker = GuardSend;
#[inline]
fn lock(&self) {
assert!(
self.try_lock(),
"called `lock` on a `RawOneShotMutex` that is already locked"
);
}
#[inline]
fn try_lock(&self) -> bool {
self.lock
.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_ok()
}
#[inline]
unsafe fn unlock(&self) {
self.lock.store(false, Ordering::Release);
}
#[inline]
fn is_locked(&self) -> bool {
self.lock.load(Ordering::Relaxed)
}
}
unsafe impl RawMutexFair for RawOneShotMutex {
#[inline]
unsafe fn unlock_fair(&self) {
unsafe { self.unlock() }
}
#[inline]
unsafe fn bump(&self) {}
}
pub type OneShotMutex<T> = lock_api::Mutex<RawOneShotMutex, T>;
pub type OneShotMutexGuard<'a, T> = lock_api::MutexGuard<'a, RawOneShotMutex, T>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lock() {
let mutex = OneShotMutex::new(42);
let mut guard = mutex.lock();
assert_eq!(*guard, 42);
*guard += 1;
drop(guard);
let guard = mutex.lock();
assert_eq!(*guard, 43);
}
#[test]
#[should_panic]
fn lock_panic() {
let mutex = OneShotMutex::new(42);
let _guard = mutex.lock();
let _guard2 = mutex.lock();
}
#[test]
fn try_lock() {
let mutex = OneShotMutex::new(42);
let mut guard = mutex.try_lock().unwrap();
assert_eq!(*guard, 42);
assert!(mutex.try_lock().is_none());
*guard += 1;
drop(guard);
let guard = mutex.try_lock().unwrap();
assert_eq!(*guard, 43);
}
}