Skip to content

Commit a259232

Browse files
committed
add feature gates on types and impls + more docs
1 parent 435b42e commit a259232

File tree

2 files changed

+119
-7
lines changed

2 files changed

+119
-7
lines changed

library/std/src/sync/nonpoison/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,33 @@
22
//!
33
//! The difference from the locks in the [`poison`] module is that the locks in this module will not
44
//! become poisoned when a thread panics while holding a guard.
5+
//!
6+
//! [`poison`]: super::poison
7+
8+
use crate::fmt;
59

610
/// A type alias for the result of a nonblocking locking method.
11+
#[unstable(feature = "sync_nonpoison", issue = "134645")]
712
pub type TryLockResult<Guard> = Result<Guard, WouldBlock>;
813

914
/// A lock could not be acquired at this time because the operation would otherwise block.
15+
#[unstable(feature = "sync_nonpoison", issue = "134645")]
1016
pub struct WouldBlock;
1117

18+
#[unstable(feature = "sync_nonpoison", issue = "134645")]
19+
impl fmt::Debug for WouldBlock {
20+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21+
"WouldBlock".fmt(f)
22+
}
23+
}
24+
25+
#[unstable(feature = "sync_nonpoison", issue = "134645")]
26+
impl fmt::Display for WouldBlock {
27+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28+
"try_lock failed because the operation would block".fmt(f)
29+
}
30+
}
31+
1232
#[unstable(feature = "sync_nonpoison", issue = "134645")]
1333
pub use self::mutex::MappedMutexGuard;
1434
#[unstable(feature = "sync_nonpoison", issue = "134645")]

library/std/src/sync/nonpoison/mutex.rs

Lines changed: 99 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ use crate::sys::sync as sys;
1212
/// For more information about mutexes, check out the documentation for the poisoning variant of
1313
/// this lock (which can be found at [`poison::Mutex`])
1414
///
15-
/// # Example
15+
/// # Examples
1616
///
1717
/// ```
1818
/// #![feature(nonpoison_mutex)]
19+
///
1920
/// use std::sync::{Arc, nonpoison::Mutex};
2021
/// use std::thread;
2122
/// use std::sync::mpsc::channel;
@@ -48,6 +49,100 @@ use crate::sys::sync as sys;
4849
/// rx.recv().unwrap();
4950
/// ```
5051
///
52+
/// Note that this `Mutex` does **not** propagate threads that panic while holding the lock via
53+
/// poisoning. If you need this functionality, see [`poison::Mutex`].
54+
///
55+
/// ```
56+
/// #![feature(nonpoison_mutex)]
57+
///
58+
/// use std::thread;
59+
/// use std::sync::{Arc, nonpoison::Mutex};
60+
///
61+
/// let mutex = Arc::new(Mutex::new(0u32));
62+
/// let mut handles = Vec::new();
63+
///
64+
/// for n in 0..10 {
65+
/// let m = Arc::clone(&mutex);
66+
/// let handle = thread::spawn(move || {
67+
/// let mut guard = m.lock();
68+
/// *guard += 1;
69+
/// panic!("panic from thread {n} {guard}")
70+
/// });
71+
/// handles.push(handle);
72+
/// }
73+
///
74+
/// for h in handles {
75+
/// let _ = h.join();
76+
/// }
77+
///
78+
/// println!("Finished, locked {} times", mutex.lock());
79+
/// ```
80+
///
81+
/// To unlock a mutex guard sooner than the end of the enclosing scope,
82+
/// either create an inner scope or drop the guard manually.
83+
///
84+
/// ```
85+
/// #![feature(nonpoison_mutex)]
86+
///
87+
/// use std::sync::{Arc, nonpoison::Mutex};
88+
/// use std::thread;
89+
///
90+
/// const N: usize = 3;
91+
///
92+
/// let data_mutex = Arc::new(Mutex::new(vec![1, 2, 3, 4]));
93+
/// let res_mutex = Arc::new(Mutex::new(0));
94+
///
95+
/// let mut threads = Vec::with_capacity(N);
96+
/// (0..N).for_each(|_| {
97+
/// let data_mutex_clone = Arc::clone(&data_mutex);
98+
/// let res_mutex_clone = Arc::clone(&res_mutex);
99+
///
100+
/// threads.push(thread::spawn(move || {
101+
/// // Here we use a block to limit the lifetime of the lock guard.
102+
/// let result = {
103+
/// let mut data = data_mutex_clone.lock();
104+
/// // This is the result of some important and long-ish work.
105+
/// let result = data.iter().fold(0, |acc, x| acc + x * 2);
106+
/// data.push(result);
107+
/// result
108+
/// // The mutex guard gets dropped here, together with any other values
109+
/// // created in the critical section.
110+
/// };
111+
/// // The guard created here is a temporary dropped at the end of the statement, i.e.
112+
/// // the lock would not remain being held even if the thread did some additional work.
113+
/// *res_mutex_clone.lock() += result;
114+
/// }));
115+
/// });
116+
///
117+
/// let mut data = data_mutex.lock();
118+
/// // This is the result of some important and long-ish work.
119+
/// let result = data.iter().fold(0, |acc, x| acc + x * 2);
120+
/// data.push(result);
121+
/// // We drop the `data` explicitly because it's not necessary anymore and the
122+
/// // thread still has work to do. This allows other threads to start working on
123+
/// // the data immediately, without waiting for the rest of the unrelated work
124+
/// // to be done here.
125+
/// //
126+
/// // It's even more important here than in the threads because we `.join` the
127+
/// // threads after that. If we had not dropped the mutex guard, a thread could
128+
/// // be waiting forever for it, causing a deadlock.
129+
/// // As in the threads, a block could have been used instead of calling the
130+
/// // `drop` function.
131+
/// drop(data);
132+
/// // Here the mutex guard is not assigned to a variable and so, even if the
133+
/// // scope does not end after this line, the mutex is still released: there is
134+
/// // no deadlock.
135+
/// *res_mutex.lock() += result;
136+
///
137+
/// threads.into_iter().for_each(|thread| {
138+
/// thread
139+
/// .join()
140+
/// .expect("The thread creating or execution failed !")
141+
/// });
142+
///
143+
/// assert_eq!(*res_mutex.lock(), 800);
144+
/// ```
145+
///
51146
/// [`poison::Mutex`]: crate::sync::poison::Mutex
52147
#[unstable(feature = "nonpoison_mutex", issue = "134645")]
53148
#[cfg_attr(not(test), rustc_diagnostic_item = "NonPoisonMutex")]
@@ -233,9 +328,6 @@ impl<T: ?Sized> Mutex<T> {
233328
/// If the mutex could not be acquired because it is already locked, then
234329
/// this call will return [`None`].
235330
///
236-
/// TODO(connor): This should return a `TryLockResult` as specified in
237-
/// <https://github.com/rust-lang/rust/issues/134645>
238-
///
239331
/// # Examples
240332
///
241333
/// ```
@@ -248,7 +340,7 @@ impl<T: ?Sized> Mutex<T> {
248340
///
249341
/// thread::spawn(move || {
250342
/// let mut lock = c_mutex.try_lock();
251-
/// if let Some(ref mut mutex) = lock {
343+
/// if let Ok(ref mut mutex) = lock {
252344
/// **mutex = 10;
253345
/// } else {
254346
/// println!("try_lock failed");
@@ -325,10 +417,10 @@ impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> {
325417
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
326418
let mut d = f.debug_struct("Mutex");
327419
match self.try_lock() {
328-
Some(guard) => {
420+
Ok(guard) => {
329421
d.field("data", &&*guard);
330422
}
331-
None => {
423+
Err(WouldBlock) => {
332424
d.field("data", &format_args!("<locked>"));
333425
}
334426
}

0 commit comments

Comments
 (0)