Files
Notes/notes-service/vendor/mea/src/rwlock/read_guard.rs
T
2026-08-01 16:11:49 +03:00

222 lines
7.0 KiB
Rust

// Copyright 2024 tison <wander4096@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::fmt;
use std::mem::ManuallyDrop;
use std::ops::Deref;
use std::ptr::NonNull;
use crate::rwlock::MappedRwLockReadGuard;
use crate::rwlock::RwLock;
impl<T: ?Sized> RwLock<T> {
/// Locks this `RwLock` with shared read access, causing the current task to yield until the
/// lock has been acquired.
///
/// The calling task will yield until there are no writers which hold the lock. There may be
/// other readers inside the lock when the task resumes.
///
/// Note that under the priority policy of [`RwLock`], read locks are not granted until prior
/// write locks, to prevent starvation. Therefore, deadlock may occur if a read lock is held
/// by the current task, a write lock attempt is made, and then a subsequent read lock attempt
/// is made by the current task.
///
/// Returns an RAII guard which will drop this read access of the `RwLock` when dropped.
///
/// # Cancel safety
///
/// This method uses a queue to fairly distribute locks in the order they were requested.
/// Cancelling a call to `read` makes you lose your place in the queue.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use std::sync::Arc;
///
/// use mea::rwlock::RwLock;
///
/// let lock = Arc::new(RwLock::new(1));
/// let lock_clone = lock.clone();
///
/// let n = lock.read().await;
/// assert_eq!(*n, 1);
///
/// tokio::spawn(async move {
/// // while the outer read lock is held, we acquire a read lock, too
/// let r = lock_clone.read().await;
/// assert_eq!(*r, 1);
/// })
/// .await
/// .unwrap();
/// # }
/// ```
pub async fn read(&self) -> RwLockReadGuard<'_, T> {
self.s.acquire(1).await;
RwLockReadGuard { lock: self }
}
/// Attempts to acquire this `RwLock` with shared read access.
///
/// If the access couldn't be acquired immediately, returns `None`. Otherwise, an RAII guard is
/// returned which will release read access when dropped.
///
/// # Examples
///
/// ```
/// use std::sync::Arc;
///
/// use mea::rwlock::RwLock;
///
/// let lock = Arc::new(RwLock::new(1));
///
/// let v = lock.try_read().unwrap();
/// assert_eq!(*v, 1);
/// drop(v);
///
/// let v = lock.try_write().unwrap();
/// assert!(lock.try_read().is_none());
/// ```
pub fn try_read(&self) -> Option<RwLockReadGuard<'_, T>> {
if self.s.try_acquire(1) {
Some(RwLockReadGuard { lock: self })
} else {
None
}
}
}
/// RAII structure used to release the shared read access of a lock when dropped.
///
/// This structure is created by the [`RwLock::read`] method.
///
/// See the [module level documentation](crate::rwlock) for more.
#[must_use = "if unused the RwLock will immediately unlock"]
pub struct RwLockReadGuard<'a, T: ?Sized> {
pub(super) lock: &'a RwLock<T>,
}
unsafe impl<T: ?Sized + Sync> Send for RwLockReadGuard<'_, T> {}
unsafe impl<T: ?Sized + Send + Sync> Sync for RwLockReadGuard<'_, T> {}
impl<T: ?Sized> Drop for RwLockReadGuard<'_, T> {
fn drop(&mut self) {
self.lock.s.release(1);
}
}
impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLockReadGuard<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<T: ?Sized + fmt::Display> fmt::Display for RwLockReadGuard<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
impl<T: ?Sized> Deref for RwLockReadGuard<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { &*self.lock.c.get() }
}
}
impl<'a, T: ?Sized> RwLockReadGuard<'a, T> {
/// Makes a new [`MappedRwLockReadGuard`] for a component of the locked data.
///
/// This operation cannot fail as the `RwLockReadGuard` passed in already locked the rwlock.
///
/// This is an associated function that needs to be used as `RwLockReadGuard::map(...)`.
///
/// A method would interfere with methods of the same name on the contents of the locked data.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use mea::rwlock::RwLock;
/// use mea::rwlock::RwLockReadGuard;
///
/// #[derive(Debug, Clone)]
/// struct Foo(String);
///
/// let rwlock = RwLock::new(Foo("hello".to_owned()));
///
/// let guard = rwlock.read().await;
/// let mapped_guard = RwLockReadGuard::map(guard, |f| &f.0);
///
/// assert_eq!(&*mapped_guard, "hello");
/// # }
/// ```
pub fn map<U, F>(orig: Self, f: F) -> MappedRwLockReadGuard<'a, U>
where
F: FnOnce(&T) -> &U,
U: ?Sized,
{
let d = NonNull::from(f(&*orig));
let orig = ManuallyDrop::new(orig);
MappedRwLockReadGuard::new(d, &orig.lock.s)
}
/// Attempts to make a new [`MappedRwLockReadGuard`] for a component of the
/// locked data. The original guard is returned if the closure returns `None`.
///
/// This operation cannot fail as the `RwLockReadGuard` passed in already locked the rwlock.
///
/// This is an associated function that needs to be used as `RwLockReadGuard::filter_map(...)`.
///
/// A method would interfere with methods of the same name on the contents of the locked data.
///
/// # Examples
///
/// ```
/// # #[tokio::main]
/// # async fn main() {
/// use mea::rwlock::RwLock;
/// use mea::rwlock::RwLockReadGuard;
///
/// #[derive(Debug, Clone)]
/// struct Foo(String);
///
/// let rwlock = RwLock::new(Foo("hello".to_owned()));
///
/// let guard = rwlock.read().await;
/// let mapped_guard =
/// RwLockReadGuard::filter_map(guard, |f| if f.0.len() > 3 { Some(&f.0) } else { None })
/// .expect("should have mapped");
///
/// assert_eq!(&*mapped_guard, "hello");
/// # }
/// ```
pub fn filter_map<U, F>(orig: Self, f: F) -> Result<MappedRwLockReadGuard<'a, U>, Self>
where
F: FnOnce(&T) -> Option<&U>,
U: ?Sized,
{
match f(&*orig) {
Some(d) => {
let d = NonNull::from(d);
let orig = ManuallyDrop::new(orig);
Ok(MappedRwLockReadGuard::new(d, &orig.lock.s))
}
None => Err(orig),
}
}
}