Repository: tokio-rs/tokio
File: tokio/src/sync/rwlock.rs (Rust)

Code before:
    ///
    /// ```
    /// use std::sync::Arc;
    /// use tokio::sync::RwLock;
    ///
    /// #[tokio::main]
    /// async fn main() {
    ///     let rwlock = Arc::new(RwLock::new(1));
    ///     let mut write_lock = rwlock.write().await;
    ///
    ///     let blocking_task = tokio::task::spawn_blocking({
    ///         let rwlock = Arc::clone(&rwlock);
    ///         move || {
    ///             // This shall block until the `write_lock` is released.
    ///             let read_lock = rwlock.blocking_read();
    ///             assert_eq!(*read_lock, 0);
    ///         }
    ///     });
    ///
    ///     *write_lock -= 1;
    ///     drop(write_lock); // release the lock.
    ///
    ///     // `.read()` and `.blocking_read()` don't block each other.
    ///     let another_read_lock = rwlock.read().await;
    ///     assert_eq!(*another_read_lock, 0);
    ///     drop(another_read_lock);
    ///
    ///     // Await the completion of the blocking task.
    ///     blocking_task.await.unwrap();
    ///
    ///     // Assert uncontended.
    ///     assert!(rwlock.try_write().is_ok());
    /// }
    /// ```
    #[cfg(feature = "sync")]
    pub fn blocking_read(&self) -> RwLockReadGuard<'_, T> {
        crate::future::block_on(self.read())
    }

    /// 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.
    ///
    /// This method is identical to [`RwLock::read`], except that the returned
    /// guard references the `RwLock` with an [`Arc`] rather than by borrowing
    /// it. Therefore, the `RwLock` must be wrapped in an `Arc` to call this
    /// method, and the guard will live for the `'static` lifetime, as it keeps
    /// the `RwLock` alive by holding an `Arc`.
    ///

Review comment (suggestion):
Although the code here is correct, it is dangerous to put this phrase in the documentation. Acquiring a read lock while holding another read lock is certainly not safe because of the fairness policy.

Code after:
    /// # Examples
    ///
    /// ```
    /// use std::sync::Arc;
    /// use tokio::sync::RwLock;
    ///
    /// #[tokio::main]
    /// async fn main() {
    ///     let rwlock = Arc::new(RwLock::new(1));
    ///     let mut write_lock = rwlock.write().await;
    ///
    ///     let blocking_task = tokio::task::spawn_blocking({
    ///         let rwlock = Arc::clone(&rwlock);
    ///         move || {
    ///             // This shall block until the `write_lock` is released.
    ///             let read_lock = rwlock.blocking_read();
    ///             assert_eq!(*read_lock, 0);
    ///         }
    ///     });
    ///
    ///     *write_lock -= 1;
    ///     drop(write_lock); // release the lock.
    ///
    ///     // Await the completion of the blocking task.
    ///     blocking_task.await.unwrap();
    ///
    ///     // Assert uncontended.
    ///     assert!(rwlock.try_write().is_ok());
    /// }
    /// ```
    #[cfg(feature = "sync")]
    pub fn blocking_read(&self) -> RwLockReadGuard<'_, T> {
        crate::future::block_on(self.read())
    }

    /// 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.
    ///
    /// This method is identical to [`RwLock::read`], except that the returned
    /// guard references the `RwLock` with an [`Arc`] rather than by borrowing
    /// it. Therefore, the `RwLock` must be wrapped in an `Arc` to call this
    /// method, and the guard will live for the `'static` lifetime, as it keeps
    /// the `RwLock` alive by holding an `Arc`.
    ///
    /// 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