use core::{
cell::UnsafeCell,
marker::PhantomData,
ops::{Deref, DerefMut},
panic::Location,
ptr,
sync::atomic::{AtomicPtr, AtomicU64, Ordering},
};
use crate::interface::LockMetadata;
pub type LockSubclass = u32;
#[repr(C)]
pub struct RawMutex {
wait_queue: AtomicPtr<()>,
owner_id: AtomicU64,
metadata: LockMetadata,
}
impl RawMutex {
#[track_caller]
pub const fn new() -> Self {
Self {
wait_queue: AtomicPtr::new(ptr::null_mut()),
owner_id: AtomicU64::new(0),
metadata: LockMetadata::new(),
}
}
#[inline(always)]
fn addr(&self) -> usize {
self as *const Self as usize
}
#[inline(always)]
#[track_caller]
fn acquire(&self, subclass: u32, is_try: bool) -> bool {
crate::interface::mutex_acquire(
&self.wait_queue,
&self.owner_id,
&self.metadata,
self.addr(),
subclass,
is_try,
Location::caller(),
)
}
#[inline(always)]
#[track_caller]
fn lock(&self) {
assert!(
self.acquire(0, false),
"blocking mutex acquisition returned failure"
);
}
#[inline(always)]
#[track_caller]
fn lock_nested(&self, subclass: u32) {
assert!(
self.acquire(subclass, false),
"blocking nested mutex acquisition returned failure"
);
}
#[inline(always)]
#[track_caller]
fn try_lock(&self) -> bool {
self.acquire(0, true)
}
#[inline(always)]
unsafe fn unlock(&self) {
crate::interface::mutex_release(&self.wait_queue, &self.owner_id, self.addr());
}
pub fn is_owned_by_current(&self) -> bool {
crate::interface::mutex_is_owned_by_current(&self.owner_id)
}
pub fn is_locked(&self) -> bool {
crate::interface::mutex_is_locked(&self.owner_id)
}
#[cfg(all(test, feature = "host-test", not(target_os = "none")))]
pub(crate) fn host_wait_queue_installed(&self) -> bool {
!self.wait_queue.load(Ordering::Acquire).is_null()
}
#[doc(hidden)]
pub unsafe fn force_unlock(&self) {
crate::interface::mutex_force_release(&self.wait_queue, &self.owner_id, self.addr());
}
}
impl Default for RawMutex {
fn default() -> Self {
Self::new()
}
}
impl Drop for RawMutex {
fn drop(&mut self) {
assert!(!self.is_locked(), "dropping a locked mutex");
let wait_queue = self.wait_queue.swap(ptr::null_mut(), Ordering::AcqRel);
if !wait_queue.is_null() {
crate::interface::mutex_drop_wait_queue(wait_queue);
}
}
}
pub struct Mutex<T: ?Sized> {
raw: RawMutex,
data: UnsafeCell<T>,
}
unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
impl<T> Mutex<T> {
#[track_caller]
pub const fn new(value: T) -> Self {
Self {
raw: RawMutex::new(),
data: UnsafeCell::new(value),
}
}
pub fn into_inner(self) -> T {
let Self { raw, data } = self;
drop(raw);
data.into_inner()
}
}
impl<T: ?Sized> Mutex<T> {
#[track_caller]
pub fn lock(&self) -> MutexGuard<'_, T> {
self.raw.lock();
MutexGuard::new(self)
}
#[track_caller]
pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
self.raw.try_lock().then(|| MutexGuard::new(self))
}
#[doc(hidden)]
pub unsafe fn force_unlock(&self) {
unsafe { self.raw.force_unlock() };
}
pub fn is_locked(&self) -> bool {
self.raw.is_locked()
}
pub fn get_mut(&mut self) -> &mut T {
self.data.get_mut()
}
#[doc(hidden)]
pub unsafe fn raw(&self) -> &RawMutex {
&self.raw
}
}
impl<T: Default> Default for Mutex<T> {
fn default() -> Self {
Self::new(T::default())
}
}
pub struct MutexGuard<'a, T: ?Sized> {
mutex: &'a Mutex<T>,
_not_send: PhantomData<*mut ()>,
}
impl<'a, T: ?Sized> MutexGuard<'a, T> {
fn new(mutex: &'a Mutex<T>) -> Self {
Self {
mutex,
_not_send: PhantomData,
}
}
}
impl<T: ?Sized> Deref for MutexGuard<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.mutex.data.get() }
}
}
impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.mutex.data.get() }
}
}
impl<T: ?Sized> Drop for MutexGuard<'_, T> {
fn drop(&mut self) {
unsafe { self.mutex.raw.unlock() };
}
}
pub trait LockdepMutexExt<T: ?Sized> {
fn lock_nested(&self, subclass: LockSubclass) -> MutexGuard<'_, T>;
}
impl<T: ?Sized> LockdepMutexExt<T> for Mutex<T> {
#[track_caller]
fn lock_nested(&self, subclass: LockSubclass) -> MutexGuard<'_, T> {
self.raw.lock_nested(subclass);
MutexGuard::new(self)
}
}