diff options
| author | Ralf Jung <post@ralfj.de> | 2020-06-15 12:01:07 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-06-15 12:01:07 +0200 |
| commit | 7c8b9413b8ce457b53e2c4ab5f3b9f1ce96195f7 (patch) | |
| tree | 171d59b4523d9443224613956d0c148358ae358d | |
| parent | 344095715fb9e8004c91a1b5d857bd18c5d07a6f (diff) | |
| parent | c010e711ca5ec02012afb83c0d99aec9d26a9eea (diff) | |
| download | rust-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.rs | 54 |
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> { |
