use crate::{Clock, Waiter, primitives};
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::sync::{LockResult, PoisonError, TryLockError, TryLockResult};
use std::time::Instant;
pub struct Mutex<T: ?Sized> {
inner: primitives::Mutex<T>,
}
impl<T> Mutex<T> {
#[cfg(not(all(test, loom)))]
pub const fn new(value: T) -> Self {
Self {
inner: primitives::Mutex::new(value),
}
}
#[cfg(all(test, loom))]
pub fn new(value: T) -> Self {
Self {
inner: primitives::Mutex::new(value),
}
}
pub fn into_inner(self) -> LockResult<T> {
self.inner.into_inner()
}
}
impl<T: ?Sized> Mutex<T> {
pub fn lock(&self) -> LockResult<MutexGuard<'_, T>> {
match self.inner.lock() {
Ok(inner) => Ok(MutexGuard { inner, mutex: self }),
Err(err) => Err(PoisonError::new(MutexGuard {
inner: err.into_inner(),
mutex: self,
})),
}
}
pub fn try_lock(&self) -> TryLockResult<MutexGuard<'_, T>> {
match self.inner.try_lock() {
Ok(inner) => Ok(MutexGuard { inner, mutex: self }),
Err(TryLockError::WouldBlock) => Err(TryLockError::WouldBlock),
Err(TryLockError::Poisoned(err)) => {
Err(TryLockError::Poisoned(PoisonError::new(MutexGuard {
inner: err.into_inner(),
mutex: self,
})))
}
}
}
#[cfg(not(all(test, loom)))]
pub fn is_poisoned(&self) -> bool {
self.inner.is_poisoned()
}
#[cfg(not(all(test, loom)))]
pub fn clear_poison(&self) {
self.inner.clear_poison();
}
pub fn get_mut(&mut self) -> LockResult<&mut T> {
self.inner.get_mut()
}
}
impl<T: Default> Default for Mutex<T> {
fn default() -> Self {
Self::new(T::default())
}
}
impl<T> From<T> for Mutex<T> {
fn from(value: T) -> Self {
Self::new(value)
}
}
impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
#[must_use = "if unused the Mutex will immediately unlock"]
pub struct MutexGuard<'a, T: ?Sized + 'a> {
inner: primitives::MutexGuard<'a, T>,
mutex: &'a Mutex<T>,
}
impl<T: ?Sized> Deref for MutexGuard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
&self.inner
}
}
impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.inner
}
}
impl<T: ?Sized + fmt::Debug> fmt::Debug for MutexGuard<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<T: ?Sized + fmt::Display> fmt::Display for MutexGuard<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
pub struct Condvar {
waiter: Waiter,
}
impl Condvar {
#[must_use]
pub fn new(clock: &Clock) -> Self {
Self {
waiter: clock.waiter(),
}
}
pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> LockResult<MutexGuard<'a, T>> {
self.wait_inner(guard, None).0
}
pub fn wait_while<'a, T, F>(
&self,
mut guard: MutexGuard<'a, T>,
mut condition: F,
) -> LockResult<MutexGuard<'a, T>>
where
F: FnMut(&mut T) -> bool,
{
while condition(&mut *guard) {
guard = self.wait(guard)?;
}
Ok(guard)
}
pub fn wait_deadline<'a, T>(
&self,
guard: MutexGuard<'a, T>,
deadline: Instant,
) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> {
let (guard, notified) = self.wait_inner(guard, Some(deadline));
let result = WaitTimeoutResult(!notified);
match guard {
Ok(guard) => Ok((guard, result)),
Err(err) => Err(PoisonError::new((err.into_inner(), result))),
}
}
pub fn notify_one(&self) {
self.waiter.signal.notify_one();
}
pub fn notify_all(&self) {
self.waiter.signal.notify_all();
}
fn wait_inner<'a, T>(
&self,
guard: MutexGuard<'a, T>,
deadline: Option<Instant>,
) -> (LockResult<MutexGuard<'a, T>>, bool) {
let MutexGuard { inner, mutex } = guard;
let mut state = self.waiter.signal.lock();
let start = state.start();
#[cfg(test)]
if let Some(hook) = self
.waiter
.clock
.paused
.as_ref()
.and_then(|paused| paused.take_hook(false))
{
drop(state);
hook(self.waiter.timer(deadline));
state = self.waiter.signal.lock();
}
drop(inner);
let (state, notified) = self.waiter.park(state, start, deadline);
drop(state);
(mutex.lock(), notified)
}
}
impl fmt::Debug for Condvar {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Condvar")
.field("clock", &self.waiter.clock)
.finish_non_exhaustive()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct WaitTimeoutResult(
bool,
);
impl WaitTimeoutResult {
#[must_use]
pub fn timed_out(&self) -> bool {
self.0
}
}
#[cfg(all(test, not(loom)))]
#[cfg_attr(coverage_nightly, coverage(off))]
#[path = "tests/sync.rs"]
mod tests;