Skip to content

Commit

Permalink
Add blocking lock methods to RwLock
Browse files Browse the repository at this point in the history
  • Loading branch information
danielhenrymantilla committed Jan 25, 2022
1 parent 9e38ebc commit f5c1f0b
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 5 deletions.
25 changes: 21 additions & 4 deletions tokio/src/sync/mutex.rs
Expand Up @@ -364,12 +364,21 @@ 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`].
///
/// 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
///
/// ```
Expand All @@ -379,16 +388,24 @@ impl<T: ?Sized> Mutex<T> {
/// #[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);
/// }
///
Expand Down
111 changes: 110 additions & 1 deletion tokio/src/sync/rwlock.rs
Expand Up @@ -420,7 +420,7 @@ impl<T: ?Sized> RwLock<T> {
///
/// // Drop the guard after the spawned task finishes.
/// drop(n);
///}
/// }
/// ```
pub async fn read(&self) -> RwLockReadGuard<'_, T> {
#[cfg(all(tokio_unstable, feature = "tracing"))]
Expand Down Expand Up @@ -459,6 +459,62 @@ 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.
///
/// - 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.
///
Expand Down Expand Up @@ -739,6 +795,59 @@ 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.
///
/// - 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.
///
Expand Down

0 comments on commit f5c1f0b

Please sign in to comment.