Skip to content

Commit ea78ad0

Browse files
committed
Ensure stores are serialized as well
1 parent d438fdb commit ea78ad0

1 file changed

Lines changed: 38 additions & 2 deletions

File tree

src/imp/xtensa.rs

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,36 @@ macro_rules! rmw {
8888
}};
8989
}
9090

91+
// Atomic store that is serialized against the emulated PSRAM RMWs.
92+
//
93+
// A naturally aligned store is itself atomic on these CPUs (only the
94+
// read-modify-write instruction misbehaves on PSRAM), but it must take the
95+
// same critical section as the RMW path. Otherwise, on the dual-core
96+
// ESP32 / ESP32-S3, a plain store on one core could land between the read
97+
// and write of an in-progress RMW on the other core and be lost.
98+
//
99+
// When the `critical-section` feature is disabled, RMWs on PSRAM panic, so no
100+
// store-vs-RMW race can occur and a plain native store stays correct.
101+
macro_rules! store {
102+
($self:ident, $val:ident, $order:ident) => {{
103+
#[cfg(feature = "critical-section")]
104+
{
105+
let p: *mut _ = $self.as_ptr();
106+
if in_psram(p) {
107+
critical_section::with(|_cs| {
108+
// SAFETY: inside a critical section we have exclusive
109+
// access to `p`, which is valid and aligned because it
110+
// came from `&self`. The store is serialized with the
111+
// RMW path, which takes the same critical section.
112+
unsafe { core::ptr::write_volatile(p, $val) }
113+
});
114+
return;
115+
}
116+
}
117+
$self.inner.store($val, $order);
118+
}};
119+
}
120+
91121
// ---------------------------------------------------------------------------
92122
// AtomicPtr
93123

@@ -113,13 +143,16 @@ impl<T> AtomicPtr<T> {
113143
#[cfg_attr(any(debug_assertions, miri), track_caller)]
114144
pub(crate) fn load(&self, order: Ordering) -> *mut T {
115145
crate::utils::assert_load_ordering(order);
146+
// No critical section needed: A naturally aligned load always
147+
// observes a complete value written by a store or by the RMW
148+
// path's single `write_volatile`.
116149
self.inner.load(order)
117150
}
118151
#[inline]
119152
#[cfg_attr(any(debug_assertions, miri), track_caller)]
120153
pub(crate) fn store(&self, ptr: *mut T, order: Ordering) {
121154
crate::utils::assert_store_ordering(order);
122-
self.inner.store(ptr, order);
155+
store!(self, ptr, order);
123156
}
124157

125158
#[inline]
@@ -363,13 +396,16 @@ macro_rules! atomic_int {
363396
#[cfg_attr(any(debug_assertions, miri), track_caller)]
364397
pub(crate) fn load(&self, order: Ordering) -> $int_type {
365398
crate::utils::assert_load_ordering(order);
399+
// No critical section needed: A naturally aligned load always
400+
// observes a complete value written by a store or by the RMW
401+
// path's single `write_volatile`.
366402
self.inner.load(order)
367403
}
368404
#[inline]
369405
#[cfg_attr(any(debug_assertions, miri), track_caller)]
370406
pub(crate) fn store(&self, val: $int_type, order: Ordering) {
371407
crate::utils::assert_store_ordering(order);
372-
self.inner.store(val, order);
408+
store!(self, val, order);
373409
}
374410

375411
#[inline]

0 commit comments

Comments
 (0)