Coverage Report

Created: 2026-06-28 08:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/mea-0.6.4/src/rwlock/mod.rs
Line
Count
Source
1
// Copyright 2024 tison <wander4096@gmail.com>
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
//! A reader-writer lock that allows multiple readers or a single writer at a time.
16
//!
17
//! This type of lock allows a number of readers or at most one writer at any point in time. The
18
//! write portion of this lock typically allows modification of the underlying data (exclusive
19
//! access) and the read portion of this lock typically allows for read-only access (shared access).
20
//!
21
//! In comparison, a [`Mutex`] does not distinguish between readers or writers that acquire the
22
//! lock, therefore causing any tasks waiting for the lock to become available to yield. An RwLock
23
//! will allow any number of readers to acquire the lock as long as a writer is not holding the
24
//! lock.
25
//!
26
//! The priority policy of Tokio's read-write lock is fair (or [write-preferring]), in order to
27
//! ensure that readers cannot starve writers. Fairness is ensured using a first-in, first-out queue
28
//! for the tasks awaiting the lock; if a task that wishes to acquire the write lock is at the head
29
//! of the queue, read locks will not be given out until the write lock has been released. This is
30
//! in contrast to the Rust standard library's `std::sync::RwLock`, where the priority policy is
31
//! dependent on the operating system's implementation.
32
//!
33
//! The type parameter `T` represents the data that this lock protects. It is required that `T`
34
//! satisfies [`Send`] to be shared across threads. The RAII guards returned from the locking
35
//! methods implement [`Deref`] (and [`DerefMut`] for the `write` method) to allow access to the
36
//! content of the lock.
37
//!
38
//! # Examples
39
//!
40
//! ```
41
//! # #[tokio::main]
42
//! # async fn main() {
43
//! use mea::rwlock::RwLock;
44
//!
45
//! let lock = RwLock::new(5);
46
//!
47
//! // many reader locks can be held at once
48
//! {
49
//!     let r1 = lock.read().await;
50
//!     let r2 = lock.read().await;
51
//!     assert_eq!(*r1, 5);
52
//!     assert_eq!(*r2, 5);
53
//! } // read locks are dropped at this point
54
//!
55
//! // only one write lock may be held, however
56
//! {
57
//!     let mut w = lock.write().await;
58
//!     *w += 1;
59
//!     assert_eq!(*w, 6);
60
//! } // write lock is dropped here
61
//!
62
//! # }
63
//! ```
64
//!
65
//! [`Mutex`]: crate::mutex::Mutex
66
//! [`Deref`]: std::ops::Deref
67
//! [`DerefMut`]: std::ops::DerefMut
68
//! [write-preferring]: https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock#Priority_policies
69
70
use std::cell::UnsafeCell;
71
use std::fmt;
72
use std::num::NonZeroUsize;
73
74
use crate::internal::Semaphore;
75
76
mod mapped_read_guard;
77
pub use mapped_read_guard::MappedRwLockReadGuard;
78
mod mapped_write_guard;
79
pub use mapped_write_guard::MappedRwLockWriteGuard;
80
mod owned_mapped_read_guard;
81
pub use owned_mapped_read_guard::OwnedMappedRwLockReadGuard;
82
mod owned_mapped_write_guard;
83
pub use owned_mapped_write_guard::OwnedMappedRwLockWriteGuard;
84
mod owned_read_guard;
85
pub use owned_read_guard::OwnedRwLockReadGuard;
86
mod owned_write_guard;
87
pub use owned_write_guard::OwnedRwLockWriteGuard;
88
mod read_guard;
89
pub use read_guard::RwLockReadGuard;
90
mod write_guard;
91
pub use write_guard::RwLockWriteGuard;
92
93
#[cfg(test)]
94
mod test;
95
96
/// A reader-writer lock that allows multiple readers or a single writer at a time.
97
///
98
/// See the [module level documentation](self) for more.
99
pub struct RwLock<T: ?Sized> {
100
    /// Maximum number of concurrent readers.
101
    ///
102
    /// This is ensured to be non-zero.
103
    max_readers: usize,
104
    /// Semaphore to coordinate read and write access to T
105
    s: Semaphore,
106
    /// The inner data.
107
    c: UnsafeCell<T>,
108
}
109
110
unsafe impl<T: ?Sized + Send> Send for RwLock<T> {}
111
unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {}
112
113
impl<T> From<T> for RwLock<T> {
114
0
    fn from(t: T) -> Self {
115
0
        Self::new(t)
116
0
    }
117
}
118
119
impl<T: Default> Default for RwLock<T> {
120
0
    fn default() -> Self {
121
0
        Self::new(T::default())
122
0
    }
123
}
124
125
impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLock<T> {
126
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127
0
        let mut d = f.debug_struct("RwLock");
128
0
        match self.try_read() {
129
0
            Some(inner) => d.field("data", &&*inner),
130
0
            None => d.field("data", &format_args!("<locked>")),
131
        };
132
0
        d.finish()
133
0
    }
134
}
135
136
impl<T> RwLock<T> {
137
    /// Creates a new reader-writer lock in an unlocked state ready for use.
138
    ///
139
    /// # Examples
140
    ///
141
    /// ```
142
    /// use mea::rwlock::RwLock;
143
    ///
144
    /// let rwlock = RwLock::new(5);
145
    /// ```
146
0
    pub const fn new(t: T) -> RwLock<T> {
147
        // large enough while not touch the edge
148
0
        RwLock::with_max_readers(t, NonZeroUsize::new(usize::MAX >> 1).unwrap())
149
0
    }
150
151
    /// Creates a new reader-writer lock in an unlocked state, and allows a maximum of
152
    /// `max_readers` concurrent readers.
153
    ///
154
    /// This method is typically used for debugging and testing purposes.
155
    ///
156
    /// # Examples
157
    ///
158
    /// ```
159
    /// use std::num::NonZeroUsize;
160
    ///
161
    /// use mea::rwlock::RwLock;
162
    ///
163
    /// let max_readers = NonZeroUsize::new(1024).expect("max_readers must be non-zero");
164
    /// let rwlock = RwLock::with_max_readers(5, max_readers);
165
    /// ```
166
0
    pub const fn with_max_readers(t: T, max_readers: NonZeroUsize) -> RwLock<T> {
167
0
        let max_readers = max_readers.get();
168
0
        let s = Semaphore::new(max_readers);
169
0
        let c = UnsafeCell::new(t);
170
0
        RwLock { max_readers, c, s }
171
0
    }
172
173
    /// Consumes the lock, returning the underlying data.
174
    ///
175
    /// # Examples
176
    ///
177
    /// ```
178
    /// use mea::rwlock::RwLock;
179
    ///
180
    /// let lock = RwLock::new(1);
181
    /// let n = lock.into_inner();
182
    /// assert_eq!(n, 1);
183
    /// ```
184
0
    pub fn into_inner(self) -> T {
185
0
        self.c.into_inner()
186
0
    }
187
}
188
189
impl<T: ?Sized> RwLock<T> {
190
    /// Returns a mutable reference to the underlying data.
191
    ///
192
    /// Since this call borrows the `RwLock` mutably, no actual locking needs to take place: the
193
    /// mutable borrow statically guarantees no locks exist.
194
    ///
195
    /// # Examples
196
    ///
197
    /// ```
198
    /// use mea::rwlock::RwLock;
199
    ///
200
    /// let mut lock = RwLock::new(1);
201
    /// let n = lock.get_mut();
202
    /// *n = 2;
203
    /// ```
204
0
    pub fn get_mut(&mut self) -> &mut T {
205
0
        self.c.get_mut()
206
0
    }
207
}