KTOR-9373 Make ConcurrentMap iteration safe on Native#5407
KTOR-9373 Make ConcurrentMap iteration safe on Native#5407e5l wants to merge 4 commits intorelease/3.xfrom
Conversation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThis pull request adds a new test for concurrent GET requests during CIO engine shutdown and updates synchronization in ConcurrentMapNative to protect read operations (size, isEmpty, entries, keys, values) with locks rather than exposing direct delegate access. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Return snapshot copies from entries/keys/values to match JVM's ConcurrentHashMap weakly-consistent iteration semantics. Also synchronize size and isEmpty() which were reading without the lock. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9591665 to
6110914
Compare
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix ConcurrentModificationException in ConcurrentMap.entries on Native: use LinkedHashMap(delegate).entries to create entries that don't hold references to the original backing map's EntryRef objects
There was a problem hiding this comment.
🧹 Nitpick comments (1)
ktor-utils/posix/src/io/ktor/util/collections/ConcurrentMapNative.kt (1)
83-83:toString()readsdelegatewithout holding the lock.This could produce inconsistent output if another thread modifies the map concurrently. Consider synchronizing for consistency:
- override fun toString(): String = "ConcurrentMapNative by $delegate" + override fun toString(): String = synchronized(lock) { "ConcurrentMapNative by $delegate" }This is low risk since
toString()is typically only used for debugging, but synchronizing would be consistent with the other accessors.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@ktor-utils/posix/src/io/ktor/util/collections/ConcurrentMapNative.kt` at line 83, toString() reads the shared property delegate without holding the map's lock, risking inconsistent output; update ConcurrentMapNative.toString() to acquire the same lock used by other accessors (the instance lock/Mutex used throughout the class), capture the delegate (or a snapshot) inside that critical section, and then build and return the string from that protected value so toString() is consistent with other synchronized accesses.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@ktor-utils/posix/src/io/ktor/util/collections/ConcurrentMapNative.kt`:
- Line 83: toString() reads the shared property delegate without holding the
map's lock, risking inconsistent output; update ConcurrentMapNative.toString()
to acquire the same lock used by other accessors (the instance lock/Mutex used
throughout the class), capture the delegate (or a snapshot) inside that critical
section, and then build and return the string from that protected value so
toString() is consistent with other synchronized accesses.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 8d2ca67c-84a7-4bd3-a77b-61800d32e039
📒 Files selected for processing (2)
ktor-client/ktor-client-cio/common/test/CIOEngineTest.ktktor-utils/posix/src/io/ktor/util/collections/ConcurrentMapNative.kt
|
Addressed CI failure (iteration 1):
|
|
CI failure analysis (iteration 2): All 5 failing checks are unrelated to this PR's changes (ConcurrentMapNative.kt + CIOEngineTest.kt): Native Windows X64 (3 failures):
Native macOS Arm64 (2 failures):
Native macOS X64 (1 failure):
Native Linux X64 (2 failures):
Build All Core — composite failure from the above 4 native platform failures, plus additional JVM flaky tests (CacheLegacyStorageTest, CIOHttpRequestLifecycleTest, Netty/Jetty tests) No actionable changes for this PR. The |
Summary
ConcurrentMap.entries/keys/valuesreturned live views of the underlyingLinkedHashMap— the lock was only held during property access, not during iteration. Concurrent modification (e.g.removefrom another thread) during iteration causedConcurrentModificationException.entries/keys/values, matching JVMConcurrentHashMap's weakly-consistent iteration semantics. Also synchronizesizeandisEmpty()which were reading without the lock.Test plan
CIOEngine.close()with active endpoints (the original crash site)ktor-utilsandktor-client-ciocontinue to pass🤖 Generated with Claude Code