mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-06 03:38:23 +00:00
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