diff options
| author | León Orell Valerian Liehr <me@fmease.dev> | 2025-01-27 04:34:52 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-01-27 04:34:52 +0100 |
| commit | bd2f6d7ee8f98c42333b4269d5f1a6bcb93712fc (patch) | |
| tree | f854280186e142c145923945f6c0ecdca1010637 | |
| parent | f7b58ea3e579be8dded1701005c2c8b3309f9ab7 (diff) | |
| parent | 9c972c0ea58716a7fcf434cdf96c7b451f82fcaf (diff) | |
| download | rust-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.rs | 26 |
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); /// } /// } /// ``` |
