@@ -1021,13 +1021,23 @@ impl Drop for PanicGuard {
10211021///   specifying a maximum time to block the thread for. 
10221022/// 
10231023/// * The [`unpark`] method on a [`Thread`] atomically makes the token available 
1024- ///   if it wasn't already. Because the token is initially absent, [`unpark`] 
1025- ///   followed by [`park`] will result in the second call returning immediately. 
1026- /// 
1027- /// The API is typically used by acquiring a handle to the current thread, 
1028- /// placing that handle in a shared data structure so that other threads can 
1029- /// find it, and then `park`ing in a loop. When some desired condition is met, another 
1030- /// thread calls [`unpark`] on the handle. 
1024+ ///   if it wasn't already. Because the token can be held by a thread even if it is currently not 
1025+ ///   parked, [`unpark`] followed by [`park`] will result in the second call returning immediately. 
1026+ ///   However, note that to rely on this guarantee, you need to make sure that your `unpark` happens 
1027+ ///   after all `park` that may be done by other data structures! 
1028+ /// 
1029+ /// The API is typically used by acquiring a handle to the current thread, placing that handle in a 
1030+ /// shared data structure so that other threads can find it, and then `park`ing in a loop. When some 
1031+ /// desired condition is met, another thread calls [`unpark`] on the handle. The last bullet point 
1032+ /// above guarantees that even if the `unpark` occurs before the thread is finished `park`ing, it 
1033+ /// will be woken up properly. 
1034+ /// 
1035+ /// Note that the coordination via the shared data structure is crucial: If you `unpark` a thread 
1036+ /// without first establishing that it is about to be `park`ing within your code, that `unpark` may 
1037+ /// get consumed by a *different* `park` in the same thread, leading to a deadlock. This also means 
1038+ /// you must not call unknown code between setting up for parking and calling `park`; for instance, 
1039+ /// if you invoke `println!`, that may itself call `park` and thus consume your `unpark` and cause a 
1040+ /// deadlock. 
10311041/// 
10321042/// The motivation for this design is twofold: 
10331043/// 
@@ -1058,33 +1068,47 @@ impl Drop for PanicGuard {
10581068/// 
10591069/// ``` 
10601070/// use std::thread; 
1061- /// use std::sync::{Arc,  atomic::{Ordering, AtomicBool} }; 
1071+ /// use std::sync::atomic::{Ordering, AtomicBool}; 
10621072/// use std::time::Duration; 
10631073/// 
1064- /// let flag = Arc::new( AtomicBool::new(false) ); 
1065- /// let flag2 = Arc::clone(&flag ); 
1074+ /// static QUEUED: AtomicBool =  AtomicBool::new(false); 
1075+ /// static FLAG: AtomicBool = AtomicBool::new(false ); 
10661076/// 
10671077/// let parked_thread = thread::spawn(move || { 
1078+ ///     println!("Thread spawned"); 
1079+ ///     // Signal that we are going to `park`. Between this store and our `park`, there may 
1080+ ///     // be no other `park`, or else that `park` could consume our `unpark` token! 
1081+ ///     QUEUED.store(true, Ordering::Release); 
10681082///     // We want to wait until the flag is set. We *could* just spin, but using 
10691083///     // park/unpark is more efficient. 
1070- ///     while !flag2 .load(Ordering::Relaxed ) { 
1071- ///         println!("Parking  thread");  
1084+ ///     while !FLAG .load(Ordering::Acquire ) { 
1085+ ///         // We can *not* use ` println!` here since that could use  thread parking internally.  
10721086///         thread::park(); 
10731087///         // We *could* get here spuriously, i.e., way before the 10ms below are over! 
10741088///         // But that is no problem, we are in a loop until the flag is set anyway. 
1075- ///         println!("Thread unparked"); 
10761089///     } 
10771090///     println!("Flag received"); 
10781091/// }); 
10791092/// 
10801093/// // Let some time pass for the thread to be spawned. 
10811094/// thread::sleep(Duration::from_millis(10)); 
10821095/// 
1096+ /// // Ensure the thread is about to park. 
1097+ /// // This is crucial! It guarantees that the `unpark` below is not consumed 
1098+ /// // by some other code in the parked thread (e.g. inside `println!`). 
1099+ /// while !QUEUED.load(Ordering::Acquire) { 
1100+ ///     // Spinning is of course inefficient; in practice, this would more likely be 
1101+ ///     // a dequeue where we have no work to do if there's nobody queued. 
1102+ ///     std::hint::spin_loop(); 
1103+ /// } 
1104+ /// 
10831105/// // Set the flag, and let the thread wake up. 
1084- /// // There is no race condition here,  if `unpark` 
1106+ /// // There is no race condition here:  if `unpark` 
10851107/// // happens first, `park` will return immediately. 
1108+ /// // There is also no other `park` that could consume this token, 
1109+ /// // since we waited until the other thread got queued. 
10861110/// // Hence there is no risk of a deadlock. 
1087- /// flag .store(true, Ordering::Relaxed ); 
1111+ /// FLAG .store(true, Ordering::Release ); 
10881112/// println!("Unpark the thread"); 
10891113/// parked_thread.thread().unpark(); 
10901114/// 
@@ -1494,10 +1518,14 @@ impl Thread {
14941518/// ``` 
14951519/// use std::thread; 
14961520/// use std::time::Duration; 
1521+ /// use std::sync::atomic::{AtomicBool, Ordering}; 
1522+ /// 
1523+ /// static QUEUED: AtomicBool = AtomicBool::new(false); 
14971524/// 
14981525/// let parked_thread = thread::Builder::new() 
14991526///     .spawn(|| { 
15001527///         println!("Parking thread"); 
1528+ ///         QUEUED.store(true, Ordering::Release); 
15011529///         thread::park(); 
15021530///         println!("Thread unparked"); 
15031531///     }) 
@@ -1506,6 +1534,15 @@ impl Thread {
15061534/// // Let some time pass for the thread to be spawned. 
15071535/// thread::sleep(Duration::from_millis(10)); 
15081536/// 
1537+ /// // Wait until the other thread is queued. 
1538+ /// // This is crucial! It guarantees that the `unpark` below is not consumed 
1539+ /// // by some other code in the parked thread (e.g. inside `println!`). 
1540+ /// while !QUEUED.load(Ordering::Acquire) { 
1541+ ///     // Spinning is of course inefficient; in practice, this would more likely be 
1542+ ///     // a dequeue where we have no work to do if there's nobody queued. 
1543+ ///     std::hint::spin_loop(); 
1544+ /// } 
1545+ /// 
15091546/// println!("Unpark the thread"); 
15101547/// parked_thread.thread().unpark(); 
15111548/// 
0 commit comments