Android: Cocos2dxSound has ConcurrentModificationException

java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:792)
at java.util.HashMap$EntryIterator.next(HashMap.java:829)
at java.util.HashMap$EntryIterator.next(HashMap.java:827)
at org.cocos2dx.lib.Cocos2dxSound.setEffectsVolume(Unknown Source)
at org.cocos2dx.lib.Cocos2dxHelper.setEffectsVolume(Unknown Source)
at org.cocos2dx.lib.Cocos2dxRenderer.nativeRender(Native Method)
at org.cocos2dx.lib.Cocos2dxRenderer.onDrawFrame(Unknown Source)
at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1463)
at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1217)

Any body has meet this problem too?

Yes, just yesterday I have added Crashlytics.com to my Android game Arrow Mania 2 to get more detail on crashes. I’m using Cocos2d-x 2.2.3.

My most common crash is a java.util.ConcurrentModificationException at:
java.util.HashMap$HashIterator.nextEntry (HashMap.java:806)
java.util.HashMap$EntryIterator.next (HashMap.java:841)
org.cocos2dx.lib.Cocos2dxSound.setEffectsVolume (Cocos2dxSound.java:246)

I’ve had this crash 12 time from 8 different users in the past 12 hours. They are using various devices including SCH-I535 (Android 4.3), GT-I9300 (4.3), GT-S7580 (4.2.2), SGH-T999V (4.3), SGH-T999 (4.1.2), C2105 (4.2.2), Huawei Y301A1 (4.1.1).
So the part causing the crash, a concurrent access to the same data, is in Cocos2dxSound.java function setEffectsVolume(float) line 246 here:

// change the volume of playing sounds
		if (!this.mPathStreamIDsMap.isEmpty()) {
			final Iterator<Entry<String, ArrayList<Integer>>> iter = this.mPathStreamIDsMap.entrySet().iterator();
			while (iter.hasNext()) {
				final Entry<String, ArrayList<Integer>> entry = iter.next();     // <- line 246
				for (final int pStreamID : entry.getValue()) {
					this.mSoundPool.setVolume(pStreamID, this.mLeftVolume, this.mRightVolume);
				}
			}
		}

Anyway, this seems to be crashing a lot and doesn’t seem to be device specific, I have 3000 active users a day at the moment.

Will be looking into it further, plan to add some Crashlytics “custom keys” so I can see when exactly it happens (after setting sound samples up, etc……. might help but probably won’t). But will be in a couple of days as I’m busy with other things.

If anyone else knows how to fix this please say.

I was in desperate need to fix this main crash issue so I took the extreme option of removing all calls to setEffectsVolume! ([[see v1.19 of Arrow Mania 2 here]https://play.google.com/store/apps/details?id=com.gmtdev.arrowmania2]).

I still allow the user to set the music volume, so they can still set the general volume using the systems volume control (buttons on their phone for example) and adjust how loud they want the music. So no real problems there.

Unfortunately, I now get a concurrent access exception on the stopEffect command on some devices. Not as many as when I was calling setEffectsVolume, about 4 crash reports a day compared to 20 before. But it is in effect the same crash. So far on a GT-I9070 (Galaxy S), SM-T210R (Galaxy TAB), GT-P3110 (Galaxy TAB2) and a GT-S5830B (Galaxy Ace).

So strange, seems the majority of problems with Cocos2d-x on Android stem from Cocosdension and a subset of Samsung devices.

I was thinking of porting to V3 but it seems to be using Cocosdension too so probably not much point.

And yet another concurrent modification exception crash is caused at the line

“for ( SoundInfoForLoadedCompleted info : mEffecToPlayWhenLoadedArray) {”
in OnLoadCompletedListener of Cocos2dxSound.java

I got this on an “HTC One S” (Android 4.1.1).

An unrelated (as in not sound) crash I have fixed which stopped two of the latest Sony from even loading, the devices “Sony Xperia SL LT26ii, Android 4.0”; Seems the Cocos2dxTextInputWraper (org.cocos2dx.lib.Cocos2dxTextInputWraper.onEditorAction) stops these devices running Cocos2D-X apps. As I’m not using text input I simply removed the TextInputWrapper and now Arrow Mania 2 boots on these devices too.

Removed like so in Cocos2dxGLSurfaceView.java

	public void setCocos2dxEditText(final Cocos2dxEditText pCocos2dxEditText) {
		this.mCocos2dxEditText = pCocos2dxEditText;
//		if (null != this.mCocos2dxEditText && null != Cocos2dxGLSurfaceView.sCocos2dxTextInputWraper) 
//		{
//			this.mCocos2dxEditText.setOnEditorActionListener(Cocos2dxGLSurfaceView.sCocos2dxTextInputWraper);
//			this.mCocos2dxEditText.setCocos2dxGLSurfaceView(this);
//			this.requestFocus();
//		}
	}

I’m getting the same concurrent modification error. Has anyone found a workaround or a fix for this?

Cocos2dx use a thread for drawing(GL thread) .You can setBackgroundVolume and EffectsVolume on the GL thread.On Android side,if you need to call setBackgroundVolume or EffectsVolume ,you have to put it into Cocos2dxHelper.runOnGLThread() or make some changes in CoCos2dxHelper (change mPathStreamIDsMap to ConcurrentHashMap).
The easiest way as I said,put all set background volume or effects into Cocos2dxHelper.runOnGLThread() .

Following degea9’s advice, I changed all HashMap appearances to ConcurrentHashMap in Cocos2dxSound.java:

private final ConcurrentHashMap<String, ArrayList<Integer>> mPathStreamIDsMap = new ConcurrentHashMap<String, ArrayList<Integer>>();
private final ConcurrentHashMap<String, Integer> mPathSoundIDMap = new ConcurrentHashMap<String, Integer>();

also an import is needed:
import java.util.concurrent.ConcurrentHashMap;

And this fixed all of my issues regarding ConcurrentModificationException.
ConcurrentHashMap is thread-safe, which explains everything.

Cheers

Thank you for your feedback and @degea9’s solution.
I created an issue here

Patch is here: