mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-12 01:07:46 +00:00
EOSERelayList backs SingleSubEoseManager with a plain mutableMapOf, and addOrUpdate is called from SubscriptionListener callbacks — i.e. from each relay's own socket-reader thread. A client holding a few hundred relays therefore had that many concurrent writers to one unsynchronized map. EOSEAccountFast wraps its lists in a lock for exactly this reason; a bare list handed to SingleSubEoseManager had nothing. The race predates this branch but the branch made it load-bearing: both merged managers (notifications and account metadata, across every logged-in account and every relay they read) now run through SingleSubEoseManager, and the EOSE refetch fix added a second writer in remove(). Writes are serialized with KmpLock, the same primitive ComposeSubscriptionManager uses. Reads still go through the live map from since(), deliberately — but the two merged managers no longer depend on that. They were reading `since` immediately after clearing a relay and relying on the mutation being visible through it, so hardening since() into a snapshot later would have silently disabled the refetch with no test to notice. Growth now zeroes the cursor for that pass explicitly, in addition to clearing it. Verified: both iOS targets, JVM, Android, desktop and the full suite build and pass; a cold start on emulator-5554 shows no fatal exceptions, no ConcurrentModificationException, and 0 nos.lol refusals. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>