Repository navigation
Fix AtomicInt.compareExchange reporting success for failed exchanges - #1442
Conversation
If the value changed between the initial load and compare_exchange_strong, the exchange failed and wrote the current value to `expected`, but the function returned the stale `original`, which equals the caller's expected value. Callers therefore saw a successful exchange that never happened. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
This is giving me a bit of a headache. Claude insists that AtomicInt is now more similar to AtomicObject, but it actually looks more different to me now... But I also don't really understand why we have a loop in these functions. |
|
It looks like the int This is Lines 422 to 437 in 1689e5b As a side note, it looks like the old int |
|
I didn't update the For exposing std::atomic specifically there are a couple of reasons why ideally I'd not want to do that.
|
|
On an unrelated note, I've just noticed I forgot the finalisers on the new typed atomic objects so the |
|
Another comment, I was also a bit confused by the loop in the CAS stuff. But I didn't really look into it much and just assumed it's due to the C++ api returning |
|
I'll merge this because it is definitely better, but yes there's still more to explore here. |
AtomicInt_obj::compareExchange(added in #1441) can report a successful exchange that didn't happen.It loads
original, then callscompare_exchange_strong(expected, replacement). If another thread changes the value between the load and the exchange, the exchange fails and writes the current value intoexpected.originalkeeps the stale value, which is equal to the caller'sexpected, so the loop exits and returns it. The caller reads that as success.This PR passes a copy of
expectedtocompare_exchange_strongand returns it. On failure, that copy holds the current value, so the result is the original value in both cases.AtomicObject_obj::compareExchangedoesn't have this problem because it passesoriginalitself.Reproduction
Four threads each run 200,000 iterations of
old = a.load(); if (a.compareExchange(old, old + 1) == old) ++successes;. Each reported success should be one real increment.This surfaced in hxcoro, where a work-stealing queue uses
compareExchangeto claim slots. Two threads could both claim the same slot, which dispatched a coroutine twice.🤖 Generated with Claude Code