about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeón Orell Valerian Liehr <me@fmease.dev>2025-01-27 04:34:52 +0100
committerGitHub <noreply@github.com>2025-01-27 04:34:52 +0100
commitbd2f6d7ee8f98c42333b4269d5f1a6bcb93712fc (patch)
treef854280186e142c145923945f6c0ecdca1010637
parentf7b58ea3e579be8dded1701005c2c8b3309f9ab7 (diff)
parent9c972c0ea58716a7fcf434cdf96c7b451f82fcaf (diff)
downloadrust-bd2f6d7ee8f98c42333b4269d5f1a6bcb93712fc.tar.gz
rust-bd2f6d7ee8f98c42333b4269d5f1a6bcb93712fc.zip
Rollup merge of #136079 - RalfJung:compiler-fence-example, r=jhpratt
compiler_fence: fix example

The old example was wrong, an acquire fence is required in the signal handler. To make the point more clear, I changed the "data" variable to use non-atomic accesses.

Fixes https://github.com/rust-lang/rust/issues/133014
-rw-r--r--library/core/src/sync/atomic.rs26
1 files changed, 13 insertions, 13 deletions
diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs
index fda26a67299..859ac163230 100644
--- a/library/core/src/sync/atomic.rs
+++ b/library/core/src/sync/atomic.rs
@@ -3727,33 +3727,33 @@ pub fn fence(order: Ordering) {
 ///
 /// # Examples
 ///
-/// Without `compiler_fence`, the `assert_eq!` in following code
-/// is *not* guaranteed to succeed, despite everything happening in a single thread.
-/// To see why, remember that the compiler is free to swap the stores to
-/// `IMPORTANT_VARIABLE` and `IS_READY` since they are both
-/// `Ordering::Relaxed`. If it does, and the signal handler is invoked right
-/// after `IS_READY` is updated, then the signal handler will see
-/// `IS_READY=1`, but `IMPORTANT_VARIABLE=0`.
-/// Using a `compiler_fence` remedies this situation.
+/// Without the two `compiler_fence` calls, the read of `IMPORTANT_VARIABLE` in `signal_handler`
+/// is *undefined behavior* due to a data race, despite everything happening in a single thread.
+/// This is because the signal handler is considered to run concurrently with its associated
+/// thread, and explicit synchronization is required to pass data between a thread and its
+/// signal handler. The code below uses two `compiler_fence` calls to establish the usual
+/// release-acquire synchronization pattern (see [`fence`] for an image).
 ///
 /// ```
-/// use std::sync::atomic::{AtomicBool, AtomicUsize};
+/// use std::sync::atomic::AtomicBool;
 /// use std::sync::atomic::Ordering;
 /// use std::sync::atomic::compiler_fence;
 ///
-/// static IMPORTANT_VARIABLE: AtomicUsize = AtomicUsize::new(0);
+/// static mut IMPORTANT_VARIABLE: usize = 0;
 /// static IS_READY: AtomicBool = AtomicBool::new(false);
 ///
 /// fn main() {
-///     IMPORTANT_VARIABLE.store(42, Ordering::Relaxed);
-///     // prevent earlier writes from being moved beyond this point
+///     unsafe { IMPORTANT_VARIABLE = 42 };
+///     // Marks earlier writes as being released with future relaxed stores.
 ///     compiler_fence(Ordering::Release);
 ///     IS_READY.store(true, Ordering::Relaxed);
 /// }
 ///
 /// fn signal_handler() {
 ///     if IS_READY.load(Ordering::Relaxed) {
-///         assert_eq!(IMPORTANT_VARIABLE.load(Ordering::Relaxed), 42);
+///         // Acquires writes that were released with relaxed stores that we read from.
+///         compiler_fence(Ordering::Acquire);
+///         assert_eq!(unsafe { IMPORTANT_VARIABLE }, 42);
 ///     }
 /// }
 /// ```