sync: add blocking lock methods to RwLock (#4425)

This commit is contained in:
Daniel Henry-Mantilla
2022-01-27 15:15:18 +01:00
committed by GitHub
parent 8f77ee8609
commit 5af9e0db2b
2 changed files with 129 additions and 5 deletions
+22 -4
View File
@@ -364,12 +364,22 @@ impl<T: ?Sized> Mutex<T> {
} }
} }
/// 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`]. /// [`MutexGuard`].
/// ///
/// This method is intended for use cases where you /// This method is intended for use cases where you
/// need to use this mutex in asynchronous code as well as in synchronous code. /// 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.
///
/// - If you find yourself in an asynchronous execution context and needing
/// to call some (synchronous) function which performs one of these
/// `blocking_` operations, then consider wrapping that call inside
/// [`spawn_blocking()`][crate::runtime::Handle::spawn_blocking]
/// (or [`block_in_place()`][crate::task::block_in_place]).
///
/// # Examples /// # Examples
/// ///
/// ``` /// ```
@@ -379,16 +389,24 @@ impl<T: ?Sized> Mutex<T> {
/// #[tokio::main] /// #[tokio::main]
/// async fn main() { /// async fn main() {
/// let mutex = Arc::new(Mutex::new(1)); /// let mutex = Arc::new(Mutex::new(1));
/// let lock = mutex.lock().await;
/// ///
/// let mutex1 = Arc::clone(&mutex); /// 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(); /// let mut n = mutex1.blocking_lock();
/// *n = 2; /// *n = 2;
/// }); /// });
/// ///
/// sync_code.await.unwrap(); /// assert_eq!(*lock, 1);
/// // Release the lock.
/// drop(lock);
/// ///
/// let n = mutex.lock().await; /// // Await the completion of the blocking task.
/// blocking_task.await.unwrap();
///
/// // Assert uncontended.
/// let n = mutex.try_lock().unwrap();
/// assert_eq!(*n, 2); /// assert_eq!(*n, 2);
/// } /// }
/// ///
+107 -1
View File
@@ -420,7 +420,7 @@ impl<T: ?Sized> RwLock<T> {
/// ///
/// // Drop the guard after the spawned task finishes. /// // Drop the guard after the spawned task finishes.
/// drop(n); /// drop(n);
///} /// }
/// ``` /// ```
pub async fn read(&self) -> RwLockReadGuard<'_, T> { pub async fn read(&self) -> RwLockReadGuard<'_, T> {
#[cfg(all(tokio_unstable, feature = "tracing"))] #[cfg(all(tokio_unstable, feature = "tracing"))]
@@ -459,6 +459,58 @@ impl<T: ?Sized> RwLock<T> {
} }
} }
/// 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.
///
/// - If you find yourself in an asynchronous execution context and needing
/// to call some (synchronous) function which performs one of these
/// `blocking_` operations, then consider wrapping that call inside
/// [`spawn_blocking()`][crate::runtime::Handle::spawn_blocking]
/// (or [`block_in_place()`][crate::task::block_in_place]).
///
/// # 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 /// Locks this `RwLock` with shared read access, causing the current task
/// to yield until the lock has been acquired. /// to yield until the lock has been acquired.
/// ///
@@ -739,6 +791,60 @@ impl<T: ?Sized> RwLock<T> {
} }
} }
/// 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.
///
/// - If you find yourself in an asynchronous execution context and needing
/// to call some (synchronous) function which performs one of these
/// `blocking_` operations, then consider wrapping that call inside
/// [`spawn_blocking()`][crate::runtime::Handle::spawn_blocking]
/// (or [`block_in_place()`][crate::task::block_in_place]).
///
/// # 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 /// Locks this `RwLock` with exclusive write access, causing the current
/// task to yield until the lock has been acquired. /// task to yield until the lock has been acquired.
/// ///