about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2020-06-15 12:01:07 +0200
committerGitHub <noreply@github.com>2020-06-15 12:01:07 +0200
commit7c8b9413b8ce457b53e2c4ab5f3b9f1ce96195f7 (patch)
tree171d59b4523d9443224613956d0c148358ae358d
parent344095715fb9e8004c91a1b5d857bd18c5d07a6f (diff)
parentc010e711ca5ec02012afb83c0d99aec9d26a9eea (diff)
downloadrust-7c8b9413b8ce457b53e2c4ab5f3b9f1ce96195f7.tar.gz
rust-7c8b9413b8ce457b53e2c4ab5f3b9f1ce96195f7.zip
Rollup merge of #73104 - poliorcetics:explicit-mutex-drop-example, r=dtolnay
Example about explicit mutex dropping

Fixes #67457.

Following the remarks made in #73074, I added an example on the main `Mutex` type, with a situation where there is mutable data and a computation result.

In my testing it is effectively needed to explicitly drop the lock, else it deadlocks.

r? @dtolnay because you were the one to review the previous PR.
-rw-r--r--src/libstd/sync/mutex.rs54
1 files changed, 54 insertions, 0 deletions
diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs
index 797b22fdd12..8478457eabf 100644
--- a/src/libstd/sync/mutex.rs
+++ b/src/libstd/sync/mutex.rs
@@ -107,6 +107,60 @@ use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult};
 ///
 /// *guard += 1;
 /// ```
+///
+/// It is sometimes necessary to manually drop the mutex guard to unlock it
+/// sooner than the end of the enclosing scope.
+///
+/// ```
+/// use std::sync::{Arc, Mutex};
+/// use std::thread;
+///
+/// const N: usize = 3;
+///
+/// let data_mutex = Arc::new(Mutex::new(vec![1, 2, 3, 4]));
+/// let res_mutex = Arc::new(Mutex::new(0));
+///
+/// let mut threads = Vec::with_capacity(N);
+/// (0..N).for_each(|_| {
+///     let data_mutex_clone = Arc::clone(&data_mutex);
+///     let res_mutex_clone = Arc::clone(&res_mutex);
+///
+///     threads.push(thread::spawn(move || {
+///         let mut data = data_mutex_clone.lock().unwrap();
+///         // This is the result of some important and long-ish work.
+///         let result = data.iter().fold(0, |acc, x| acc + x * 2);
+///         data.push(result);
+///         drop(data);
+///         *res_mutex_clone.lock().unwrap() += result;
+///     }));
+/// });
+///
+/// let mut data = data_mutex.lock().unwrap();
+/// // This is the result of some important and long-ish work.
+/// let result = data.iter().fold(0, |acc, x| acc + x * 2);
+/// data.push(result);
+/// // We drop the `data` explicitly because it's not necessary anymore and the
+/// // thread still has work to do. This allow other threads to start working on
+/// // the data immediately, without waiting for the rest of the unrelated work
+/// // to be done here.
+/// //
+/// // It's even more important here than in the threads because we `.join` the
+/// // threads after that. If we had not dropped the mutex guard, a thread could
+/// // be waiting forever for it, causing a deadlock.
+/// drop(data);
+/// // Here the mutex guard is not assigned to a variable and so, even if the
+/// // scope does not end after this line, the mutex is still released: there is
+/// // no deadlock.
+/// *res_mutex.lock().unwrap() += result;
+///
+/// threads.into_iter().for_each(|thread| {
+///     thread
+///         .join()
+///         .expect("The thread creating or execution failed !")
+/// });
+///
+/// assert_eq!(*res_mutex.lock().unwrap(), 800);
+/// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 #[cfg_attr(not(test), rustc_diagnostic_item = "mutex_type")]
 pub struct Mutex<T: ?Sized> {