Skip to content

Memory ordering fixes in StreamTracker.#329

Merged
szmyd merged 2 commits into
eBay:dev/v14.xfrom
szmyd:dev/v14.x
Jul 10, 2026
Merged

Memory ordering fixes in StreamTracker.#329
szmyd merged 2 commits into
eBay:dev/v14.xfrom
szmyd:dev/v14.x

Conversation

@szmyd

@szmyd szmyd commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

szmyd added 2 commits July 7, 2026 17:20
These bits are a publication mechanism. A set bit routinely means "the data
this bit indexes is now valid": StreamTracker's slot payload, an allocated
blk. But safe_bits set bits with fetch_or(relaxed) and read them with
load(relaxed), so a thread that observes a set bit has no acquire edge to the
data that bit names, and its read of that data is a data race.

This is reachable today. StreamTracker::do_update() placement-news the entry
and then sets its active bit, both under a SHARED lock -- and two shared_lock
holders establish no ordering between them. LogDev::prepare_flush() walks
foreach_contiguous_active() and reads log_record payloads from the flush
thread while LogDev::append_async() concurrently create()s them; the appender
holds m_stream_tracker_mtx shared, prepare_flush takes only m_flush_mtx, which
appenders never take. Nothing orders the payload stores against the flusher's
read. Benign on x86 -- fetch_or lowers to a locked RMW and TSO forbids
store-store reordering -- but on aarch64 fetch_or(relaxed) is a bare
ldxr/stxr with no barrier and store-store reordering is permitted, so the bit
can become visible over a half-written entry.

Give safe_bits the ordering its role requires: release on set/set_if/or_with/
and_with, acquire on get. On x86-64 the codegen is byte-for-byte identical
(fetch_or is already a lock-prefixed RMW; an acquire load and a release store
are plain movs), so this costs nothing on our primary target and is correct on
weakly ordered ones.

Why not std::atomic_thread_fence around the bit ops instead: it is equally
correct and equally invisible to ThreadSanitizer, which does not model fences
(compiler-rt's AtomicFence is a documented no-op for happens-before, and GCC
warns -Wtsan). Fence-based publication makes TSAN report a false race on every
payload scan. That gets suppressed, and the suppression then hides the real
races. Verified against a concurrent reader/writer stress over StreamTracker:
relaxed bits report 12 races, fences report the same 12, release/acquire
reports none.

Also drops the now-redundant ordering comments' fence references in
stream_tracker.hpp and documents that the slot bit is what publishes the slot.
@szmyd szmyd merged commit 0e050a3 into eBay:dev/v14.x Jul 10, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant