fix: concurrent access to discovery sync data listeners(#6686) - #7197
juicewcode wants to merge 8 commits into
Conversation
Replace the non-thread-safe ArrayList in DiscoveryDataChangedEventSyncListener with CopyOnWriteArrayList and add a deterministic concurrency test covering listener registration during onChange iteration.
Aias00
left a comment
There was a problem hiding this comment.
Right fix for a real concurrency hazard.
onChange iterates discoverySyncDataList while addListener can append to it on another thread (a discovery handler being registered mid-sync). With a plain ArrayList that produces ConcurrentModificationException — or, worse, silently skips a listener. CopyOnWriteArrayList is the correct choice here because the read path (iteration on every change event) dominates and writes happen rarely, so the copy cost is irrelevant.
Verified:
discoverySyncDataListis only mutated throughaddListener, so the snapshot semantics of COW give exactly whatonChangeneeds: a stable view for the duration of one event.- The new test reproduces the race properly rather than just asserting the field type — it parks
keyValueParser.parseValuemid-flight, callsaddListenerfrom the main thread while the worker is insideonChange, and then asserts the worker completes without throwing. That's a genuine regression test for CME. - Latch cleanup in the
finallyblock avoids hanging the suite if the assertion fails.
Non-blocking note: COW iteration is a snapshot, so a listener added during an onChange won't be picked up by that particular event. That's the correct trade-off (better to skip one event than to throw), but it's worth a brief comment on the field so the semantics are explicit.
Fixes #6686
Summary
ArrayListwithCopyOnWriteArrayListinDiscoveryDataChangedEventSyncListener.Test
testOnChangeIsSafeWhenListenerIsAddedConcurrently.onChangewhile processing a listener, adds another listener concurrently, then verifies thatonChangecompletes without throwing an exception.