matrix_sdk_common/
locks.rs

1// Copyright 2025 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Simplified locks hat panic instead of returning a `Result` when the lock is
16//! poisoned.
17
18use std::{
19    fmt,
20    sync::{Mutex as StdMutex, MutexGuard, RwLock as StdRwLock, RwLockReadGuard, RwLockWriteGuard},
21};
22
23use serde::{Deserialize, Serialize};
24
25/// A wrapper around `std::sync::Mutex` that panics on poison.
26///
27/// This `Mutex` works similarly to the standard library's `Mutex`, except its
28/// `lock` method does not return a `Result`. Instead, if the mutex is poisoned,
29/// it will panic.
30///
31/// # Examples
32///
33/// ```
34/// use matrix_sdk_common::locks::Mutex;
35///
36/// let mutex = Mutex::new(42);
37///
38/// {
39///     let mut guard = mutex.lock();
40///     *guard = 100;
41/// }
42///
43/// assert_eq!(*mutex.lock(), 100);
44/// ```
45#[derive(Default)]
46pub struct Mutex<T: ?Sized>(StdMutex<T>);
47
48impl<T> Mutex<T> {
49    /// Creates a new `Mutex` wrapping the given value.
50    pub const fn new(t: T) -> Self {
51        Self(StdMutex::new(t))
52    }
53}
54
55impl<T: ?Sized> Mutex<T> {
56    /// Acquires the lock, panicking if the lock is poisoned.
57    ///
58    /// This method blocks the current thread until the lock is acquired.
59    pub fn lock(&self) -> MutexGuard<'_, T> {
60        self.0.lock().expect("The Mutex should never be poisoned")
61    }
62}
63
64impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        self.0.fmt(f)
67    }
68}
69
70/// A wrapper around [`std::sync::RwLock`] that panics on poison.
71///
72/// This `RwLock` works similarly to the standard library's `RwLock`, except its
73/// `read` and `write` methods do not return a `Result`. Instead, if the lock is
74/// poisoned, it will panic.
75///
76/// # Examples
77///
78/// ```
79/// use matrix_sdk_common::locks::RwLock;
80///
81/// let lock = RwLock::new(42);
82///
83/// {
84///     let read_guard = lock.read();
85///     assert_eq!(*read_guard, 42);
86/// }
87/// {
88///     let mut write_guard = lock.write();
89///     *write_guard = 100;
90/// }
91/// assert_eq!(*lock.read(), 100);
92/// ```
93#[derive(Default, Serialize, Deserialize)]
94#[serde(transparent)]
95pub struct RwLock<T: ?Sized>(StdRwLock<T>);
96
97impl<T> RwLock<T> {
98    /// Creates a new `RwLock` wrapping the given value.
99    pub const fn new(t: T) -> Self {
100        Self(StdRwLock::new(t))
101    }
102}
103
104impl<T: ?Sized> RwLock<T> {
105    /// Acquires a mutable write lock, panicking if the lock is poisoned.
106    ///
107    /// This method blocks the current thread until the lock is acquired.
108    pub fn write(&self) -> RwLockWriteGuard<'_, T> {
109        self.0.write().expect("The RwLock should never be poisoned")
110    }
111
112    /// Acquires a shared read lock, panicking if the lock is poisoned.
113    ///
114    /// This method blocks the current thread until the lock is acquired.
115    pub fn read(&self) -> RwLockReadGuard<'_, T> {
116        self.0.read().expect("The RwLock should never be poisoned")
117    }
118}
119
120impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLock<T> {
121    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122        self.0.fmt(f)
123    }
124}
125
126impl<T> From<T> for RwLock<T> {
127    fn from(value: T) -> Self {
128        Self::new(value)
129    }
130}