use core::{
cell::UnsafeCell,
fmt,
marker::PhantomData,
mem::{self, ManuallyDrop},
ops::{Deref, DerefMut},
ptr::NonNull,
time::Duration,
};
use crate::{
sync::{
poison::{self, LockResult, TryLockError, TryLockResult},
RawMutex, RawMutexTimed,
},
sys::sync as sys,
time::Instant,
};
use super::PoisonError;
type DefaultMutex = cfg_select! {
feature = "kernel" => sys::Mutex,
pbp => sys::LwMutex,
_ => sys::SemaMutex,
};
pub struct Mutex<T: ?Sized, M = DefaultMutex> {
inner: M,
poison: poison::Flag,
data: UnsafeCell<T>,
}
unsafe impl<T: ?Sized + Send, M> Send for Mutex<T, M> {}
unsafe impl<T: ?Sized + Send, M> Sync for Mutex<T, M> {}
impl<T: ?Sized, M> core::panic::UnwindSafe for Mutex<T, M> {}
impl<T: ?Sized, M> core::panic::RefUnwindSafe for Mutex<T, M> {}
#[must_use = "if unused the Mutex will immediately unlock"]
#[clippy::has_significant_drop]
pub struct MutexGuard<'a, T: ?Sized + 'a, M: RawMutex = DefaultMutex> {
lock: &'a Mutex<T, M>,
poison: poison::Guard,
}
impl<T: ?Sized, M: RawMutex> !Send for MutexGuard<'_, T, M> {}
unsafe impl<T: ?Sized + Sync, M: RawMutex> Sync for MutexGuard<'_, T, M> {}
#[must_use = "if unused the Mutex will immediately unlock"]
#[clippy::has_significant_drop]
pub struct MappedMutexGuard<'a, T: ?Sized + 'a, M: RawMutex = DefaultMutex> {
data: NonNull<T>,
inner: &'a M,
poison_flag: &'a poison::Flag,
poison: poison::Guard,
_variance: PhantomData<&'a mut T>,
}
impl<T: ?Sized, M: RawMutex> !Send for MappedMutexGuard<'_, T, M> {}
unsafe impl<T: ?Sized + Sync, M: RawMutex> Sync for MappedMutexGuard<'_, T, M> {}
impl<T> Mutex<T> {
#[inline]
pub const fn new(t: T) -> Mutex<T> {
Mutex {
inner: DefaultMutex::new(),
poison: poison::Flag::new(),
data: UnsafeCell::new(t),
}
}
}
impl<T, M: RawMutex> Mutex<T, M> {
#[inline]
pub const fn new_with(value: T, raw_mutex: M) -> Self {
Self {
inner: raw_mutex,
poison: poison::Flag::new(),
data: UnsafeCell::new(value),
}
}
pub fn get_cloned(&self) -> Result<T, PoisonError<()>>
where
T: Clone,
{
match self.lock_checked() {
Ok(guard) => Ok((*guard).clone()),
Err(_) => Err(PoisonError::new(())),
}
}
pub fn set(&self, value: T) -> Result<(), PoisonError<T>> {
if mem::needs_drop::<T>() {
self.replace(value).map(drop)
} else {
match self.lock_checked() {
Ok(mut guard) => {
*guard = value;
Ok(())
},
Err(_) => Err(PoisonError::new(value)),
}
}
}
pub fn replace(&self, value: T) -> LockResult<T> {
match self.lock_checked() {
Ok(mut guard) => Ok(mem::replace(&mut *guard, value)),
Err(_) => Err(PoisonError::new(value)),
}
}
}
impl<T: ?Sized, M: RawMutex> Mutex<T, M> {
#[track_caller]
pub fn lock(&self) -> MutexGuard<'_, T, M> {
self.inner.lock();
match unsafe { MutexGuard::new(self) } {
Ok(guard) => guard,
Err(err) => panic!("{err}"),
}
}
pub fn try_lock(&self) -> TryLockResult<MutexGuard<'_, T, M>> {
if self.inner.try_lock() {
Ok(unsafe { MutexGuard::new(self)? })
} else {
Err(TryLockError::WouldBlock)
}
}
pub fn lock_checked(&self) -> LockResult<MutexGuard<'_, T, M>> {
self.inner.lock();
unsafe { MutexGuard::new(self) }
}
pub fn lock_unchecked(&self) -> MutexGuard<'_, T, M> {
self.inner.lock();
match unsafe { MutexGuard::new(self) } {
Ok(guard) => guard,
Err(err) => err.into_inner(),
}
}
pub fn try_unchecked(&self) -> TryLockResult<MutexGuard<'_, T, M>> {
match self.try_lock() {
Ok(guard) => Ok(guard),
Err(TryLockError::Poisoned(err)) => Ok(err.into_inner()),
Err(err) => Err(err),
}
}
#[inline]
pub fn is_poisoned(&self) -> bool {
self.poison.get()
}
#[inline]
pub fn clear_poison(&self) {
self.poison.clear();
}
#[track_caller]
pub fn into_inner(self) -> T
where
T: Sized,
{
let data = self.data.into_inner();
match poison::map_result(self.poison.borrow(), |()| data) {
Ok(data) => data,
Err(err) => panic!("{err}"),
}
}
pub fn into_inner_checked(self) -> LockResult<T>
where
T: Sized,
{
let data = self.data.into_inner();
poison::map_result(self.poison.borrow(), |()| data)
}
#[track_caller]
pub fn get_mut(&mut self) -> &mut T {
let data = self.data.get_mut();
match poison::map_result(self.poison.borrow(), |()| data) {
Ok(data) => data,
Err(err) => panic!("{err}"),
}
}
pub fn get_mut_checked(&mut self) -> LockResult<&mut T> {
let data = self.data.get_mut();
poison::map_result(self.poison.borrow(), |()| data)
}
pub fn into_inner_unchecked(self) -> T
where
T: Sized,
{
self.data.into_inner()
}
pub fn get_mut_unchecked(&mut self) -> &mut T {
self.data.get_mut()
}
pub const fn data_ptr(&self) -> *mut T {
self.data.get()
}
}
impl<T: ?Sized, M: RawMutexTimed> Mutex<T, M> {
#[inline]
#[track_caller]
pub fn try_lock_for(&self, timeout: Duration) -> Option<MutexGuard<'_, T, M>> {
if self.inner.try_lock_for(timeout) {
unsafe { MutexGuard::new(self).ok() }
} else {
None
}
}
#[inline]
#[track_caller]
pub fn try_lock_until(&self, timeout: Instant) -> Option<MutexGuard<'_, T, M>> {
if self.inner.try_lock_until(timeout) {
unsafe { MutexGuard::new(self).ok() }
} else {
None
}
}
}
impl<T> From<T> for Mutex<T> {
fn from(t: T) -> Self {
Mutex::new(t)
}
}
impl<T> Default for Mutex<T>
where
T: Default,
{
fn default() -> Mutex<T> {
Mutex::new(Default::default())
}
}
impl<T: ?Sized + fmt::Debug, M: RawMutex> fmt::Debug for Mutex<T, M> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
crate::println!("GOT HERE 1");
let mut d = f.debug_struct("Mutex");
match self.try_lock() {
Ok(guard) => {
crate::println!("GOT HERE 2");
d.field("data", &&*guard);
},
Err(TryLockError::Poisoned(err)) => {
d.field("data", &&**err.get_ref());
},
Err(TryLockError::WouldBlock) => {
d.field("data", &format_args!("<locked>"));
},
}
d.field("poisoned", &self.poison.get());
d.finish_non_exhaustive()
}
}
impl<'mutex, T: ?Sized, M: RawMutex> MutexGuard<'mutex, T, M> {
unsafe fn new(lock: &'mutex Mutex<T, M>) -> LockResult<MutexGuard<'mutex, T, M>> {
poison::map_result(lock.poison.guard(), |guard| MutexGuard {
lock,
poison: guard,
})
}
}
impl<T: ?Sized, M: RawMutex> Deref for MutexGuard<'_, T, M> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.lock.data.get() }
}
}
impl<T: ?Sized, M: RawMutex> DerefMut for MutexGuard<'_, T, M> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.lock.data.get() }
}
}
impl<T: ?Sized, M: RawMutex> Drop for MutexGuard<'_, T, M> {
#[inline]
fn drop(&mut self) {
unsafe {
self.lock.poison.done(&self.poison);
self.lock.inner.unlock();
}
}
}
impl<T, M> fmt::Debug for MutexGuard<'_, T, M>
where
T: ?Sized + fmt::Debug,
M: RawMutex,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<T, M> fmt::Display for MutexGuard<'_, T, M>
where
T: ?Sized + fmt::Display,
M: RawMutex,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
pub fn guard_lock<'a, T, M>(guard: &MutexGuard<'a, T, M>) -> &'a M
where
T: ?Sized,
M: RawMutex,
{
&guard.lock.inner
}
pub fn guard_poison<'a, T, M>(guard: &MutexGuard<'a, T, M>) -> &'a poison::Flag
where
T: ?Sized,
M: RawMutex,
{
&guard.lock.poison
}
impl<'a, T: ?Sized, M: RawMutex> MutexGuard<'a, T, M> {
pub fn map<U, F>(orig: Self, f: F) -> MappedMutexGuard<'a, U, M>
where
F: FnOnce(&mut T) -> &mut U,
U: ?Sized,
{
let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() }));
let orig = ManuallyDrop::new(orig);
MappedMutexGuard {
data,
inner: &orig.lock.inner,
poison_flag: &orig.lock.poison,
poison: orig.poison.clone(),
_variance: PhantomData,
}
}
#[doc(alias = "filter_map")]
pub fn try_map<U, F>(orig: Self, f: F) -> Result<MappedMutexGuard<'a, U, M>, Self>
where
F: FnOnce(&mut T) -> Option<&mut U>,
U: ?Sized,
{
match f(unsafe { &mut *orig.lock.data.get() }) {
Some(data) => {
let data = NonNull::from(data);
let orig = ManuallyDrop::new(orig);
Ok(MappedMutexGuard {
data,
inner: &orig.lock.inner,
poison_flag: &orig.lock.poison,
poison: orig.poison.clone(),
_variance: PhantomData,
})
},
None => Err(orig),
}
}
}
impl<T: ?Sized, M: RawMutex> Deref for MappedMutexGuard<'_, T, M> {
type Target = T;
fn deref(&self) -> &T {
unsafe { self.data.as_ref() }
}
}
impl<T: ?Sized, M: RawMutex> DerefMut for MappedMutexGuard<'_, T, M> {
fn deref_mut(&mut self) -> &mut T {
unsafe { self.data.as_mut() }
}
}
impl<T: ?Sized, M: RawMutex> Drop for MappedMutexGuard<'_, T, M> {
#[inline]
fn drop(&mut self) {
unsafe {
self.poison_flag.done(&self.poison);
self.inner.unlock();
}
}
}
impl<T, M> fmt::Debug for MappedMutexGuard<'_, T, M>
where
T: ?Sized + fmt::Debug,
M: RawMutex,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<T, M> fmt::Display for MappedMutexGuard<'_, T, M>
where
T: ?Sized + fmt::Display,
M: RawMutex,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<'a, T: ?Sized, M: RawMutex> MappedMutexGuard<'a, T, M> {
pub fn map<U, F>(mut orig: Self, f: F) -> MappedMutexGuard<'a, U, M>
where
F: FnOnce(&mut T) -> &mut U,
U: ?Sized,
{
let data = NonNull::from(f(unsafe { orig.data.as_mut() }));
let orig = ManuallyDrop::new(orig);
MappedMutexGuard {
data,
inner: orig.inner,
poison_flag: orig.poison_flag,
poison: orig.poison.clone(),
_variance: PhantomData,
}
}
#[doc(alias = "filter_map")]
pub fn try_map<U, F>(mut orig: Self, f: F) -> Result<MappedMutexGuard<'a, U, M>, Self>
where
F: FnOnce(&mut T) -> Option<&mut U>,
U: ?Sized,
{
match f(unsafe { orig.data.as_mut() }) {
Some(data) => {
let data = NonNull::from(data);
let orig = ManuallyDrop::new(orig);
Ok(MappedMutexGuard {
data,
inner: orig.inner,
poison_flag: orig.poison_flag,
poison: orig.poison.clone(),
_variance: PhantomData,
})
},
None => Err(orig),
}
}
}