Files
amethyst/commons/src
Claude c25cf6524f refactor(commons): make the list observables lock-free and common
Both matching-filter observables were a ConcurrentSkipListSet coordinated with
a ConcurrentHashMap.compute per-key critical section — lock-free, and with no
Kotlin/Native equivalent, which is what forced the expect/actual and the two
throwing iOS stubs. Copy-on-write over an atomic reference gives the same
lock-freedom with none of that: one immutable State behind an AtomicReference,
each mutation building the next state and publishing it with a compare-and-set,
retrying if another thread won. It is the shape quartz already uses for its
native ConcurrentMap actual, and it compiles everywhere, so the expect/actual,
the jvmAndroid actuals and the iOS stubs are all gone.

Two hand-written workarounds go with them. The emitted snapshot no longer
de-duplicates by idHex — that pass existed only because a weakly-consistent
skip-list iterator can transiently surface a key twice, and a reader of an
immutable state cannot. And emitting is now one map over the published state
rather than a walk of a concurrent structure plus a HashSet, so the per-event
cost drops even though the copy is O(n): the previous code already paid O(n)
per emit, on top of the insert.

The sort key is still snapshotted into an immutable Entry at insertion, so a
replaceable event mutating created_at on the same AddressableNote instance
still cannot move an entry — the test that pins that behaviour, and both 8
thread x 5000 iteration concurrency tests, pass unchanged.

ensureMintDirectoryBackfilled loses its lock too: a CAS on the claim flag means
exactly one caller sweeps, and a second returns immediately instead of blocking
for the length of a full cache scan. It may see a partially filled index, which
is what it already saw before any backfill ran.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkULS5SVq4GHDdoCzajKi8
2026-09-21 17:57:14 +00:00
..