// Copyright 2024 tison // // 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 RwLock { /// 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> { 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, } unsafe impl Send for RwLockReadGuard<'_, T> {} unsafe impl Sync for RwLockReadGuard<'_, T> {} impl Drop for RwLockReadGuard<'_, T> { fn drop(&mut self) { self.lock.s.release(1); } } impl fmt::Debug for RwLockReadGuard<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } impl fmt::Display for RwLockReadGuard<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&**self, f) } } impl 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(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(orig: Self, f: F) -> Result, 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), } } }