From 9de1d7c10d4732068d43219253fa8ceccd675754 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Thu, 21 Jul 2022 22:49:08 -0400 Subject: [PATCH 1/6] document memory orderings of `thread::{park, unpark}` --- library/std/src/thread/mod.rs | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index d28c7b58b20ba..32453b4d4a3cb 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -845,7 +845,7 @@ pub fn sleep(dur: Duration) { /// A call to `park` does not guarantee that the thread will remain parked /// forever, and callers should be prepared for this possibility. /// -/// # park and unpark +/// # `park` and `unpark` /// /// Every thread is equipped with some basic low-level blocking support, via the /// [`thread::park`][`park`] function and [`thread::Thread::unpark`][`unpark`] @@ -866,14 +866,6 @@ pub fn sleep(dur: Duration) { /// if it wasn't already. Because the token is initially absent, [`unpark`] /// followed by [`park`] will result in the second call returning immediately. /// -/// In other words, each [`Thread`] acts a bit like a spinlock that can be -/// locked and unlocked using `park` and `unpark`. -/// -/// 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 /// find it, and then `park`ing in a loop. When some desired condition is met, another @@ -887,6 +879,23 @@ pub fn sleep(dur: Duration) { /// /// * It can be implemented very efficiently on many platforms. /// +/// # Memory Orderings +/// +/// Calls to `park` _synchronize-with_ calls to `unpark`, meaning that memory +/// operations performed before a call to `unpark` are made visible to the thread that +/// consumes the token and returns from `park`. Note that all `park` and `unpark` +/// operations for a given thread form a total order and `park` synchronizes-with +/// _all_ prior `unpark` operations. +/// +/// In atomic ordering terms, `unpark` performs a `Release` operation and `park` +/// performs the corresponding `Acquire` operation. Calls to `unpark` for the same +/// thread form a [release sequence]. +/// +/// 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. +/// /// # Examples /// /// ``` @@ -926,6 +935,7 @@ pub fn sleep(dur: Duration) { /// /// [`unpark`]: Thread::unpark /// [`thread::park_timeout`]: park_timeout +/// [release sequence]: https://en.cppreference.com/w/cpp/atomic/memory_order#Release_sequence #[stable(feature = "rust1", since = "1.0.0")] pub fn park() { // SAFETY: park_timeout is called on the parker owned by this thread. From 1bae661dbc0b9fd3397e0df00b176bf13f346cff Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Wed, 5 Oct 2022 16:33:04 -0400 Subject: [PATCH 2/6] tidy Co-authored-by: yvt --- library/std/src/thread/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 32453b4d4a3cb..afa4e2fb16854 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -880,7 +880,7 @@ pub fn sleep(dur: Duration) { /// * It can be implemented very efficiently on many platforms. /// /// # Memory Orderings -/// +/// /// Calls to `park` _synchronize-with_ calls to `unpark`, meaning that memory /// operations performed before a call to `unpark` are made visible to the thread that /// consumes the token and returns from `park`. Note that all `park` and `unpark` @@ -890,7 +890,7 @@ pub fn sleep(dur: Duration) { /// In atomic ordering terms, `unpark` performs a `Release` operation and `park` /// performs the corresponding `Acquire` operation. Calls to `unpark` for the same /// thread form a [release sequence]. -/// +/// /// 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 From 6e8a0136f1d5d9e7e2f7d6bf1f599975f8c0c4c2 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Sun, 1 Jan 2023 14:34:22 -0500 Subject: [PATCH 3/6] improve wording of `thread::park` docs --- library/std/src/thread/mod.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index afa4e2fb16854..07662e90c52bc 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -891,10 +891,9 @@ pub fn sleep(dur: Duration) { /// performs the corresponding `Acquire` operation. Calls to `unpark` for the same /// thread form a [release sequence]. /// -/// 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. +/// Note that being unblocked does not imply synchronization with a call to `unpark`, +/// the wakeup could also be spurious. For example, a valid, but inefficient, +/// implementation could have `park` and `unpark` return immediately without doing anything. /// /// # Examples /// From e9868ef8df9817802a0aaf0695e83c1e45783842 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Tue, 11 Apr 2023 13:20:41 -0400 Subject: [PATCH 4/6] clarify wording around spurious wakeups from `thread::park` --- library/std/src/thread/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 07662e90c52bc..0f8b9c7668815 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -891,8 +891,8 @@ pub fn sleep(dur: Duration) { /// performs the corresponding `Acquire` operation. Calls to `unpark` for the same /// thread form a [release sequence]. /// -/// Note that being unblocked does not imply synchronization with a call to `unpark`, -/// the wakeup could also be spurious. For example, a valid, but inefficient, +/// Note that being unblocked does not imply a call was made to `unpark`, because +/// wakeups can also be spurious. For example, a valid, but inefficient, /// implementation could have `park` and `unpark` return immediately without doing anything. /// /// # Examples From bf27f12d941b2aea125bd629c1f30feaacf41cc6 Mon Sep 17 00:00:00 2001 From: Ibraheem Ahmed Date: Tue, 20 Jun 2023 20:05:31 -0400 Subject: [PATCH 5/6] relaxed orderings in `thread::park` example --- library/std/src/thread/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 0f8b9c7668815..e9d94ba425a3a 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -893,7 +893,8 @@ pub fn sleep(dur: Duration) { /// /// Note that being unblocked does not imply a call was made to `unpark`, because /// wakeups can also be spurious. For example, a valid, but inefficient, -/// implementation could have `park` and `unpark` return immediately without doing anything. +/// implementation could have `park` and `unpark` return immediately without doing anything, +/// making *all* wakeups spurious. /// /// # Examples /// @@ -908,7 +909,7 @@ pub fn sleep(dur: Duration) { /// 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) { +/// while !flag2.load(Ordering::Relaxed) { /// println!("Parking thread"); /// thread::park(); /// // We *could* get here spuriously, i.e., way before the 10ms below are over! @@ -925,7 +926,7 @@ pub fn sleep(dur: Duration) { /// // 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); +/// flag.store(true, Ordering::Relaxed); /// println!("Unpark the thread"); /// parked_thread.thread().unpark(); /// From 3acb1d2b9b5f0ab85cb787250326c0571503f78c Mon Sep 17 00:00:00 2001 From: Mara Bos Date: Wed, 21 Jun 2023 12:43:22 +0200 Subject: [PATCH 6/6] "Memory Orderings" -> "Memory Ordering" Co-authored-by: yvt --- library/std/src/thread/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index e9d94ba425a3a..b9b14b2dc5f66 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -879,7 +879,7 @@ pub fn sleep(dur: Duration) { /// /// * It can be implemented very efficiently on many platforms. /// -/// # Memory Orderings +/// # Memory Ordering /// /// Calls to `park` _synchronize-with_ calls to `unpark`, meaning that memory /// operations performed before a call to `unpark` are made visible to the thread that