diff --git a/quartz/src/appleMain/kotlin/com/vitorpamplona/quartz/utils/cache/LargeCache.apple.kt b/quartz/src/appleMain/kotlin/com/vitorpamplona/quartz/utils/cache/LargeCache.apple.kt index 9d29a6e2b..c64e575b3 100644 --- a/quartz/src/appleMain/kotlin/com/vitorpamplona/quartz/utils/cache/LargeCache.apple.kt +++ b/quartz/src/appleMain/kotlin/com/vitorpamplona/quartz/utils/cache/LargeCache.apple.kt @@ -85,7 +85,10 @@ actual class LargeCache : ICacheOperations { actual override fun size(): Int = concurrentMap.size actual override fun forEach(consumer: ICacheBiConsumer) { - concurrentMap.forEach { consumer.accept(it.key, it.value) } + // Take a snapshot of entries to avoid ConcurrentModificationException + // when the map is modified during iteration (e.g., NostrClient.syncFilters + // iterating while subscriptions are added from another coroutine). + concurrentMap.entries.toList().forEach { consumer.accept(it.key, it.value) } } actual override fun filter(consumer: CacheCollectors.BiFilter): List = diff --git a/quartz/src/linuxMain/kotlin/com/vitorpamplona/quartz/utils/cache/LargeCache.linux.kt b/quartz/src/linuxMain/kotlin/com/vitorpamplona/quartz/utils/cache/LargeCache.linux.kt index 74861078f..2e49f22a5 100644 --- a/quartz/src/linuxMain/kotlin/com/vitorpamplona/quartz/utils/cache/LargeCache.linux.kt +++ b/quartz/src/linuxMain/kotlin/com/vitorpamplona/quartz/utils/cache/LargeCache.linux.kt @@ -88,7 +88,8 @@ actual class LargeCache : ICacheOperations { actual override fun size(): Int = withMap { it.size } actual override fun forEach(consumer: ICacheBiConsumer) { - withMap { map -> map.forEach { consumer.accept(it.key, it.value) } } + // Snapshot entries to avoid ConcurrentModificationException + withMap { map -> map.entries.toList() }.forEach { consumer.accept(it.key, it.value) } } actual override fun filter(consumer: CacheCollectors.BiFilter): List = withMap { map -> map.filter { consumer.filter(it.key, it.value) }.values.toList() }