@@ -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,45 @@ 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+ /// // `eprintln!` does not do any thread parking internally so we can safely call it here.
1086+ /// eprintln!("Parking thread");
10721087/// thread::park();
10731088/// // We *could* get here spuriously, i.e., way before the 10ms below are over!
10741089/// // But that is no problem, we are in a loop until the flag is set anyway.
1075- /// println !("Thread unparked");
1090+ /// eprintln !("Thread unparked");
10761091/// }
10771092/// println!("Flag received");
10781093/// });
10791094///
10801095/// // Let some time pass for the thread to be spawned.
10811096/// thread::sleep(Duration::from_millis(10));
10821097///
1098+ /// // Ensure the thread is about to park.
1099+ /// // This is crucial! It guarantees that the `unpark` below is not consumed
1100+ /// // by some other code in the parked thread (e.g. inside `println!`).
1101+ /// while !QUEUED.load(Ordering::Acquire) {}
1102+ ///
10831103/// // Set the flag, and let the thread wake up.
1084- /// // There is no race condition here, if `unpark`
1104+ /// // There is no race condition here: if `unpark`
10851105/// // happens first, `park` will return immediately.
1106+ /// // There is also no other `park` that could consume this token,
1107+ /// // since we waited until the other thread got queued.
10861108/// // Hence there is no risk of a deadlock.
1087- /// flag .store(true, Ordering::Relaxed );
1109+ /// FLAG .store(true, Ordering::Release );
10881110/// println!("Unpark the thread");
10891111/// parked_thread.thread().unpark();
10901112///
@@ -1494,10 +1516,14 @@ impl Thread {
14941516 /// ```
14951517 /// use std::thread;
14961518 /// use std::time::Duration;
1519+ /// use std::sync::atomic::{AtomicBool, Ordering};
1520+ ///
1521+ /// static QUEUED: AtomicBool = AtomicBool::new(false);
14971522 ///
14981523 /// let parked_thread = thread::Builder::new()
14991524 /// .spawn(|| {
15001525 /// println!("Parking thread");
1526+ /// QUEUED.store(true, Ordering::Release);
15011527 /// thread::park();
15021528 /// println!("Thread unparked");
15031529 /// })
@@ -1506,6 +1532,11 @@ impl Thread {
15061532 /// // Let some time pass for the thread to be spawned.
15071533 /// thread::sleep(Duration::from_millis(10));
15081534 ///
1535+ /// // Wait until the other thread is queued.
1536+ /// // This is crucial! It guarantees that the `unpark` below is not consumed
1537+ /// // by some other code in the parked thread (e.g. inside `println!`).
1538+ /// while !QUEUED.load(Ordering::Acquire) {}
1539+ ///
15091540 /// println!("Unpark the thread");
15101541 /// parked_thread.thread().unpark();
15111542 ///
0 commit comments