about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlexis Bourget <alexis.bourget@gmail.com>2020-06-07 23:36:07 +0200
committerAlexis Bourget <alexis.bourget@gmail.com>2020-06-07 23:36:07 +0200
commit95c4899e55e7aab68f06e67660257d73e6a46eda (patch)
tree15096a246e46cebfee806d506b9bf2e478d4a74e
parentf9fdf642bab2bfcabe1829a4c82aaf055330661b (diff)
downloadrust-95c4899e55e7aab68f06e67660257d73e6a46eda.tar.gz
rust-95c4899e55e7aab68f06e67660257d73e6a46eda.zip
Added an example where explicitly dropping a lock is necessary/a good idea.
-rw-r--r--src/libstd/sync/mutex.rs61
1 files changed, 61 insertions, 0 deletions
diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs
index 797b22fdd12..6077e1a4029 100644
--- a/src/libstd/sync/mutex.rs
+++ b/src/libstd/sync/mutex.rs
@@ -107,6 +107,67 @@ use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult};
 ///
 /// *guard += 1;
 /// ```
+///
+/// It is sometimes a good idea (or even necessary) to manually drop the mutex
+/// to unlock it as soon as possible. If you need the resource until the end of
+/// the scope, this is not needed.
+///
+/// ```
+/// use std::sync::{Arc, Mutex};
+/// use std::thread;
+///
+/// const N: usize = 3;
+///
+/// // Some data to work with in multiple threads.
+/// let data_mutex = Arc::new(Mutex::new([1, 2, 3, 4]));
+/// // The result of all the work across all threads.
+/// let res_mutex = Arc::new(Mutex::new(0));
+///
+/// // Threads other than the main thread.
+/// let mut threads = Vec::with_capacity(N);
+/// (0..N).for_each(|_| {
+///     // Getting clones for the mutexes.
+///     let data_mutex_clone = Arc::clone(&data_mutex);
+///     let res_mutex_clone = Arc::clone(&res_mutex);
+///
+///     threads.push(thread::spawn(move || {
+///         let 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);
+///         // We drop the `data` explicitely 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.
+///         std::mem::drop(data);
+///         *res_mutex_clone.lock().unwrap() += result;
+///     }));
+/// });
+///
+/// let 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);
+/// // We drop the `data` explicitely 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 because we `.join` the threads after that.
+/// // If we had not dropped the lock, a thread could be waiting forever for
+/// // it, causing a deadlock.
+/// std::mem::drop(data);
+/// // Here the lock 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(), 80);
+/// ```
 #[stable(feature = "rust1", since = "1.0.0")]
 #[cfg_attr(not(test), rustc_diagnostic_item = "mutex_type")]
 pub struct Mutex<T: ?Sized> {