r/wg21
P3181R1 - Atomic stores and object lifetimes WG21
Posted by u/concurrency_nerd_99 · 5 hr. ago

Document: P3181R1
Authors: Hans Boehm, Dave Clausen, David Goldblatt
Date: 2026-02-22
Audience: SG1
Target: C++26

The current object lifetime rules say the last update to an object must happen-before its destruction. Sounds reasonable. The problem is that a release_store to an atomic satisfies this, but fence(release) followed by a relaxed_store does not - even though the fence+relaxed pattern is intuitively at least as strong. The paper argues this is overly conservative, doesn't match any real hardware, and breaks the mental model that standalone fences are a valid alternative to integrated ordering.

The practical fallout hits reference counting. If you decrement a refcount with fetch_sub(1, acq_rel) and then delete on zero, you're fine. Rewrite that as fence(release); fetch_sub(1, relaxed); fence(acquire) and it's technically UB. The proposed fix modifies [basic.life] to relax the "before" definition so that an atomic store counts as before end of lifetime if a load observes its value and that load happens-before the destruction. First discussed in Tokyo and St. Louis, now with concrete wording.

▲ 18 points (85% upvoted) · 10 comments
sorted by: best
u/daily_ub_enjoyer 34 points 4 hr. ago

This is one of those papers where I understand every word individually but the sentences make me feel inadequate.

"the store in A happens-before the deletion in the release-store case; in the fence case it does not"

I'll just stick to mutex, thanks.

u/lockfree_or_bust 27 points 4 hr. ago

Hans Boehm writing a paper about the C++ memory model. The year is 2026. Some things are eternal.

u/needs_more_seq_cst 14 points 3 hr. ago

He literally designed the memory model. If he found a bug in it I'd call that a warranty claim.

u/fence_evangelist 22 points 3 hr. ago

The refcount example is the whole paper in six lines:

int count = x->refcount.fetch_sub(1, acq_rel);
if (count == 1) delete x;  // fine

Rewrite with fences:

atomic_thread_fence(release);
int count = x->refcount.fetch_sub(1, relaxed);
atomic_thread_fence(acquire);
if (count == 1) delete x;  // technically UB

These should be equivalent. Every hardware implementation treats them as equivalent. The standard says they're not. The paper's fix is small and correct. Ship it.

u/mm_practitioner 19 points 3 hr. ago

The interesting design choice is modifying [basic.life]'s definition of "before" and "after" rather than inventing a new happens-before flavor. The paper makes a solid case for why the alternative would be worse - a new HB variant that doesn't compose with sequenced-before on the left would effectively turn relaxed stores into release stores, which nobody wants.

The proposed wording is surprisingly local: if a load observes the value stored by an atomic store AND that load happens-before end of lifetime, then the store is considered "before" end of lifetime. It doesn't touch the happens-before relation itself. Clean.

u/relaxed_semantics 9 points 2 hr. ago

Right. And the coherence / modification-order argument in the paper is basically saying "this is already what every real implementation does." The safety proof works as long as non-atomic stores behave like relaxed atomics at the hardware level, and the paper's own admission is that the last architecture where that wasn't trivially true was Itanium. The Boehm memory model notes have more background on why fences ended up with these weird edge cases.

u/former_boost_contributor 16 points 2 hr. ago

The "synchronize your own destruction" property is underappreciated. If you embed a mutex or condition variable inside a ref-counted object, the last thread to touch it needs to unlock and then destroy. If fences don't provide that guarantee, you lose the ability to use fence+relaxed as a drop-in for release/acquire in refcount code.

We hit exactly this pattern in Boost.SmartPtr. The fix in this paper makes the standard match what every correct implementation already assumes.

u/the_real_itanium_user 7 points 1 hr. ago

I love that the paper namedrops Itanium as the one architecture where the safety argument doesn't trivially hold. IA-64 keeps showing up in memory model papers like a ghost at the feast.

u/template_wizard_42 5 points 1 hr. ago

Serious question: does anyone in 2026 actually use standalone fences instead of just putting the ordering on the atomic operation? Every time I try I end up with a bug that takes three days to find.

u/compile_this_if_you_can 3 points 42 minutes ago

Still waiting for the day when the memory model is simple enough that I can explain it to an intern without a whiteboard and two hours.