diff --git a/tokio/src/sync/mutex.rs b/tokio/src/sync/mutex.rs index 2476726bdea..be0cfb4f98f 100644 --- a/tokio/src/sync/mutex.rs +++ b/tokio/src/sync/mutex.rs @@ -364,12 +364,21 @@ impl Mutex { } } - /// Blocking lock this mutex. When the lock has been acquired, function returns a + /// Blockingly locks this `Mutex`. When the lock has been acquired, function returns a /// [`MutexGuard`]. /// /// This method is intended for use cases where you /// need to use this mutex in asynchronous code as well as in synchronous code. /// + /// # Panics + /// + /// This function panics if called within an asynchronous execution context. + /// + /// - Consider using [`spawn_blocking()`][crate::runtime::Handle::spawn_blocking] + /// (or [`block_in_place()`][crate::task::block_in_place]) to call, when + /// within an asynchronrous execution context, any function that may + /// call one of these `blocking_` operations. + /// /// # Examples /// /// ``` @@ -379,16 +388,24 @@ impl Mutex { /// #[tokio::main] /// async fn main() { /// let mutex = Arc::new(Mutex::new(1)); + /// let lock = mutex.lock().await; /// /// let mutex1 = Arc::clone(&mutex); - /// let sync_code = tokio::task::spawn_blocking(move || { + /// let blocking_task = tokio::task::spawn_blocking(move || { + /// // This shall block until the `lock` is released. /// let mut n = mutex1.blocking_lock(); /// *n = 2; /// }); /// - /// sync_code.await.unwrap(); + /// assert_eq!(*lock, 1); + /// // Release the lock. + /// drop(lock); + /// + /// // Await the completion of the blocking task. + /// blocking_task.await.unwrap(); /// - /// let n = mutex.lock().await; + /// // Assert uncontended. + /// let n = mutex.try_lock().unwrap(); /// assert_eq!(*n, 2); /// } /// diff --git a/tokio/src/sync/rwlock.rs b/tokio/src/sync/rwlock.rs index 6991f0e8b6a..73d3198ea74 100644 --- a/tokio/src/sync/rwlock.rs +++ b/tokio/src/sync/rwlock.rs @@ -420,7 +420,7 @@ impl RwLock { /// /// // Drop the guard after the spawned task finishes. /// drop(n); - ///} + /// } /// ``` pub async fn read(&self) -> RwLockReadGuard<'_, T> { #[cfg(all(tokio_unstable, feature = "tracing"))] @@ -459,6 +459,62 @@ impl RwLock { } } + /// Blockingly locks this `RwLock` with shared read access. + /// + /// This method is intended for use cases where you + /// need to use this rwlock in asynchronous code as well as in synchronous code. + /// + /// Returns an RAII guard which will drop the read access of this `RwLock` when dropped. + /// + /// # Panics + /// + /// This function panics if called within an asynchronous execution context. + /// + /// - Consider using [`spawn_blocking()`][crate::runtime::Handle::spawn_blocking] + /// (or [`block_in_place()`][crate::task::block_in_place]) to call, when + /// within an asynchronrous execution context, any function that may + /// call one of these `blocking_` operations. + /// + /// # 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. + /// + /// // `.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. /// @@ -739,6 +795,59 @@ impl RwLock { } } + /// Blockingly locks this `RwLock` with exclusive write access. + /// + /// This method is intended for use cases where you + /// need to use this rwlock in asynchronous code as well as in synchronous code. + /// + /// Returns an RAII guard which will drop the write access of this `RwLock` when dropped. + /// + /// # Panics + /// + /// This function panics if called within an asynchronous execution context. + /// + /// - Consider using [`spawn_blocking()`][crate::runtime::Handle::spawn_blocking] + /// (or [`block_in_place()`][crate::task::block_in_place]) to call, when + /// within an asynchronrous execution context, any function that may + /// call one of these `blocking_` operations. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// use tokio::{sync::RwLock}; + /// + /// #[tokio::main] + /// async fn main() { + /// let rwlock = Arc::new(RwLock::new(1)); + /// let read_lock = rwlock.read().await; + /// + /// let blocking_task = tokio::task::spawn_blocking({ + /// let rwlock = Arc::clone(&rwlock); + /// move || { + /// // This shall block until the `read_lock` is released. + /// let mut write_lock = rwlock.blocking_write(); + /// *write_lock = 2; + /// } + /// }); + /// + /// assert_eq!(*read_lock, 1); + /// // Release the last outstanding read lock. + /// drop(read_lock); + /// + /// // Await the completion of the blocking task. + /// blocking_task.await.unwrap(); + /// + /// // Assert uncontended. + /// let read_lock = rwlock.try_read().unwrap(); + /// assert_eq!(*read_lock, 2); + /// } + /// ``` + #[cfg(feature = "sync")] + pub fn blocking_write(&self) -> RwLockWriteGuard<'_, T> { + crate::future::block_on(self.write()) + } + /// Locks this `RwLock` with exclusive write access, causing the current /// task to yield until the lock has been acquired. ///