From 2d46ae7c37a779fe993687e753b7c544bb26dc38 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 22 Nov 2018 10:54:04 +0100 Subject: [PATCH 1/3] expand thread::park explanation --- src/libstd/thread/mod.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 8a845efd41362..99f8fa390d227 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -806,9 +806,13 @@ const NOTIFIED: usize = 2; /// In other words, each [`Thread`] acts a bit like a spinlock that can be /// locked and unlocked using `park` and `unpark`. /// +/// Notice that it would be a valid (but inefficient) implementation to make both [`park`] and +/// [`unpark`] NOPs that return immediately. Being unblocked does not imply +/// any synchronization with someone that unparked this thread, it could also be spurious. +/// /// The API is typically used by acquiring a handle to the current thread, /// placing that handle in a shared data structure so that other threads can -/// find it, and then `park`ing. When some desired condition is met, another +/// find it, and then `park`ing in a loop. When some desired condition is met, another /// thread calls [`unpark`] on the handle. /// /// The motivation for this design is twofold: @@ -829,6 +833,7 @@ const NOTIFIED: usize = 2; /// .spawn(|| { /// println!("Parking thread"); /// thread::park(); +/// // We *could* get here spuriously, i.e., way before the 10ms below are over! /// println!("Thread unparked"); /// }) /// .unwrap(); From 7b6ad7a960dd330e8c8401a598bd2f2ec6901100 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 23 Nov 2018 11:04:16 +0100 Subject: [PATCH 2/3] make park/unpark example more realistic --- src/libstd/thread/mod.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 99f8fa390d227..4a5ba9b800ebe 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -827,22 +827,33 @@ const NOTIFIED: usize = 2; /// /// ``` /// use std::thread; +/// use std::sync::{Arc, atomic::{Ordering, AtomicBool}}; /// use std::time::Duration; /// -/// let parked_thread = thread::Builder::new() -/// .spawn(|| { +/// let flag = Arc::new(AtomicBool::new(false)); +/// let flag2 = Arc::clone(&flag); +/// +/// let parked_thread = thread::spawn(move || { +/// // We want to wait until the flag is set. We *could* just spin, but using +/// // park/unpark is more efficient. +/// while !flag2.load(Ordering::Acquire) { /// println!("Parking thread"); /// thread::park(); /// // We *could* get here spuriously, i.e., way before the 10ms below are over! +/// // But that is no problem, we are in a loop until the flag is set anyway. /// println!("Thread unparked"); -/// }) -/// .unwrap(); +/// } +/// println!("Flag received"); +/// }); /// /// // Let some time pass for the thread to be spawned. /// thread::sleep(Duration::from_millis(10)); /// +/// // Set the flag, and let the thread wake up. /// // There is no race condition here, if `unpark` /// // happens first, `park` will return immediately. +/// // Hence there is no risk of a deadlock. +/// flag.store(true, Ordering::Release); /// println!("Unpark the thread"); /// parked_thread.thread().unpark(); /// From 76cd8f05945e5edd8272e2e8c8f41ffd0182f403 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 3 Dec 2018 15:14:31 +0100 Subject: [PATCH 3/3] improve wording --- src/libstd/thread/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 4a5ba9b800ebe..68d3f19cdeae4 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -806,9 +806,10 @@ const NOTIFIED: usize = 2; /// In other words, each [`Thread`] acts a bit like a spinlock that can be /// locked and unlocked using `park` and `unpark`. /// -/// Notice that it would be a valid (but inefficient) implementation to make both [`park`] and -/// [`unpark`] NOPs that return immediately. Being unblocked does not imply -/// any synchronization with someone that unparked this thread, it could also be spurious. +/// Notice that being unblocked does not imply any synchronization with someone +/// that unparked this thread, it could also be spurious. +/// For example, it would be a valid, but inefficient, implementation to make both [`park`] and +/// [`unpark`] return immediately without doing anything. /// /// The API is typically used by acquiring a handle to the current thread, /// placing that handle in a shared data structure so that other threads can