generic_container/impls/
arc_checked_mutex.rs

1use alloc::sync::Arc;
2
3use thread_checked_lock::{
4    HandlePoisonResult as _, LockError, ThreadCheckedMutex, ThreadCheckedMutexGuard,
5};
6
7use crate::container_traits::{
8    FragileTryContainer, FragileTryMutContainer, TryContainer, TryMutContainer,
9};
10
11
12/// A version of [`thread_checked_lock::LockError`] which does not allow a poison error to be
13/// recovered into data.
14#[derive(Debug, Clone, Copy)]
15pub enum ErasedLockError {
16    /// See [`LockError::Poisoned`]. However, the original poison error's data was already dropped.
17    Poisoned,
18    /// See [`LockError::LockedByCurrentThread`].
19    LockedByCurrentThread,
20}
21
22impl ErasedLockError {
23    /// Panics if the error was caused by poison, and otherwise returns the error unchanged.
24    ///
25    /// # Panics
26    /// Panics if the error is the [`Poisoned`] variant.
27    ///
28    /// [`Poisoned`]: ErasedLockError::Poisoned
29    #[inline]
30    #[must_use]
31    pub fn panic_if_poison(self) -> Self {
32        match self {
33            #[expect(
34                clippy::panic,
35                reason = "library users will frequently want to panic on poison",
36            )]
37            Self::Poisoned              => panic!("ErasedLockError was poison"),
38            Self::LockedByCurrentThread => Self::LockedByCurrentThread,
39        }
40    }
41}
42
43impl<T> From<LockError<T>> for ErasedLockError {
44    #[inline]
45    fn from(value: LockError<T>) -> Self {
46        match value {
47            LockError::Poisoned(_)           => Self::Poisoned,
48            LockError::LockedByCurrentThread => Self::LockedByCurrentThread,
49        }
50    }
51}
52
53impl<T: ?Sized> FragileTryContainer<T> for Arc<ThreadCheckedMutex<T>> {
54    type Ref<'a>  = ThreadCheckedMutexGuard<'a, T> where T: 'a;
55    type RefError = ErasedLockError;
56
57    #[inline]
58    fn new_container(t: T) -> Self where T: Sized {
59        Self::new(ThreadCheckedMutex::new(t))
60    }
61
62    /// Attempt to retrieve the inner `T` from the container.
63    /// Behaves identically to [`Arc::into_inner`].
64    ///
65    /// Ignores any poison errors.
66    #[inline]
67    fn into_inner(self) -> Option<T> where T: Sized {
68        let result = Self::into_inner(self)?
69            .into_inner()
70            .ignore_poison();
71
72        // The result could only possibly be due to poison, so its `Err` is now uninhabited
73        match result {
74            Ok(t) => Some(t),
75            #[expect(unreachable_code, reason = "yeah, that's the point")]
76            Err(poisonless_poison) => match poisonless_poison.poison.into_inner() {},
77        }
78    }
79
80    /// Attempt to immutably access the inner `T`.
81    ///
82    /// # Errors
83    ///
84    /// This function fails if and only if [`ThreadCheckedMutex::lock`] fails.
85    ///
86    /// A poison error is not ignored, nor does it trigger a panic.
87    #[inline]
88    fn try_get_ref(&self) -> Result<Self::Ref<'_>, Self::RefError> {
89        self.lock().map_err(Into::into)
90    }
91}
92
93impl<T: ?Sized> TryContainer<T> for Arc<ThreadCheckedMutex<T>> {}
94
95impl<T: ?Sized> FragileTryMutContainer<T> for Arc<ThreadCheckedMutex<T>> {
96    type RefMut<'a>  = ThreadCheckedMutexGuard<'a, T> where T: 'a;
97    type RefMutError = ErasedLockError;
98
99    /// Attempt to mutably access the inner `T`.
100    ///
101    /// # Errors
102    ///
103    /// This function fails if and only if [`ThreadCheckedMutex::lock`] fails.
104    ///
105    /// A poison error is not ignored, nor does it trigger a panic.
106    #[inline]
107    fn try_get_mut(&mut self) -> Result<Self::RefMut<'_>, Self::RefMutError> {
108        self.lock().map_err(Into::into)
109    }
110}
111
112impl<T: ?Sized> TryMutContainer<T> for Arc<ThreadCheckedMutex<T>> {}