fix: ConcurrentModificationException in getAudioDeviceStatusMap#260
fix: ConcurrentModificationException in getAudioDeviceStatusMap#260qiixiao wants to merge 2 commits into
Conversation
Fix ConcurrentModificationException caused by iterating audioDevices while it's being modified on another thread. Copy audioDevices to a new ArrayList before iterating to avoid concurrent modification.
There was a problem hiding this comment.
Pull request overview
This PR aims to prevent a ConcurrentModificationException when building the audio-device status payload (getAudioDeviceStatusMap) by iterating over a snapshot of audioDevices rather than the live set.
Changes:
- Iterate over a copied collection of
audioDeviceswhen buildingavailableAudioDeviceList.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| WritableMap data = Arguments.createMap(); | ||
| String audioDevicesJson = "["; | ||
| for (AudioDevice s: audioDevices) { | ||
| for (AudioDevice s: new ArrayList<>(audioDevices)) { |
| WritableMap data = Arguments.createMap(); | ||
| String audioDevicesJson = "["; | ||
| for (AudioDevice s: audioDevices) { | ||
| for (AudioDevice s: new ArrayList<>(audioDevices)) { |
There was a problem hiding this comment.
Thanks for the review — both points are valid.
- Compile error: right, java.util.ArrayList was missing. Fixed by adding the import.
- Snapshot doesn't fully prevent CME: agreed. The root cause is that audioDevices is a plain HashSet mutated from multiple threads —start() / chooseAudioRoute() run on the RN bridge thread while updateAudioDeviceState() runs on the UI thread, so even constructing the snapshot iterates a concurrently-modified set.
To fix it properly (and keep the change minimal), I switched audioDevices from HashSet to ConcurrentHashMap.newKeySet(). Iteration / snapshot construction is then weakly consistent and never throws CME. Since the field was previously reassigned (audioDevices = newAudioDevices), I changed that to clear() + addAll() so the concurrent set is updated in place rather than swapped out from under concurrent readers.
The new ArrayList<>(audioDevices) snapshot is retained so the JSON payload reflects a single point-in-time view rather than a live set.
switch audioDevices to a concurrent set and update in place
|
switch audioDevices to a concurrent set and update in place |
Fix ConcurrentModificationException caused by iterating audioDevices while it's being modified on another thread.
Copy audioDevices to a new ArrayList before iterating to avoid concurrent modification.