Concurrent Modification Exception question

Hello,

I’ve been figuring out the cause of my issue (adding to an array while simultaneously deleting from it), but can’t figure out how to work around it…yet.

I’ve repurposed some code from Shiffman’s Nature of Code to react on a clientEvent() and add a new box to a Box2D array. It will work fine for a random amount of time, but always hangs with a ConcurrentModificationException error.

clientEvent() seems to clash with displaying the boxes in the draw().

Could anyone give me a nudge in the right direction? Keep moving things around, but can’t quite hit on a solution today.


import processing.net.*;
import shiffman.box2d.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;

Client myClientBin1;


int counter = 0;

PImage img1;
PImage img2;
PImage img3;

// A reference to our box2d world
Box2DProcessing box2d;

// A list we'll use to track fixed objects
ArrayList<Boundary> boundaries;
// A list for all of our rectangles
ArrayList<Box> boxes;
ArrayList<BoxB> boxesB;

void setup() {
  size(800,600);
  myClientBin1 = new Client(this, "139.XXX.XXX.XXX", 1999);
  smooth();
  
  img1 = loadImage("1.png");
  img2 = loadImage("2.png");
  img3 = loadImage("background.jpg");
  
  // Initialize box2d physics and create the world
  box2d = new Box2DProcessing(this);
  box2d.createWorld();
  // We are setting a custom gravity
  box2d.setGravity(0, -20);

  // Create ArrayLists	
  boxes = new ArrayList<Box>();
  boxesB = new ArrayList<BoxB>();
  boundaries = new ArrayList<Boundary>();

  // Fixed boundaries
  boundaries.add(new Boundary(width/2,height,width,0));
}

void draw() {
  background(img3);
  stroke(0);
  fill(0);
  ellipse(width/2, 40, 100,40);
  text(frameRate, 750, 20);
  // We must always step through time!
  box2d.step();

  // Display all the boxes
  for (BoxB bB: boxesB) {
    bB.display();
  }
  for (Box b: boxes) {
    b.display();
  }


  // Boxes that leave the screen, we delete them
  // (note they have to be deleted from both the box2d world and our list
  for (int i = boxes.size()-1; i >= 0; i--) {
    Box b = boxes.get(i);
    if (b.done()) {
      boxes.remove(i);
    }
  }
  for (int i = boxesB.size()-1; i >= 0; i--) {
    BoxB bB = boxesB.get(i);
    if (bB.done()) {
      boxesB.remove(i);
    }
  }
}

void clientEvent(Client someClient) {
    float boxVersion = random(1,3);
    if (int(boxVersion) == 1){
        Box p = new Box((width/2)-16,30);
        delay(1);
        boxes.add(p);
    }
    else{
        BoxB q = new BoxB((width/2)-16,30);
        delay(1);
        boxesB.add(q);
    } 
}

1 Like

This solution didn’t work

Nice! I had seen that reply on another thread, but it hadn’t worked (the way I’d tried).
Just saw your nudge before falling asleep. Woke up this morning with a solution. Thanks GoToLoop.

Now stopping draw() where the error occurred previously. Letting the business be done with the arrays, and then loop()-ing again. Testing now…seems stable.

  noLoop();
  // Display all the boxes
  for (BoxB bB: boxesB) {
    bB.display();
  }
  for (Box b: boxes) {
    b.display();
  }
  loop();

I take that back. Just crashed again with same error.

Back to the drawing board…

Any other nudges out there?

Neither loop(), noLoop() or redraw() got any supernatural power to instantaneously pause, interrupt or resume an already called back draw()!

They’re respectively equivalent to:

  1. looping = true;
  2. looping = false;
  3. redraw = true;

As you can see now, they merely mutate 2 Processing system-wide boolean fields.

Which can only affect whether the next draw() is called back or not.

Current draw() is impervious to any of them.

Regardless, the noLoop()/redraw() combo (notice it’s not noLoop()/loop()) can only be applied to cases where the canvas can await to be updated until a next event arrives.

It seems like your sketch here is supposed to be a fluid 60 FPS continuous animation.

Therefore it can’t use the simplest noLoop()/redraw() event-based approach after all.

So let’s go on to the 2nd simplest approach now, which is boolean flag-based.

As the name implies, you’re gonna need a boolean global field for it.

Let’s call it: boolean makeNewBox;

Now, rather than add() a new Box or BoxB inside clientEvent() (which is btW called back by another Thread), we do it inside draw() (which is called back by the “Animation” Thread) each time makeNewBox becomes true.

This time, the only thing we do inside clientEvent() is assigning true to makeNewBox in order to flag the next draw() callback:

void clientEvent(final Client c) {
  makeNewBox = true;
}

Inside draw() we now continuously check makeNewBox.

Once it becomes true, we immediately set it back to false.

And then we do what previously was done inside clientEvent():

if (makeNewBox) {
  makeNewBox = false;

  final int x = (width >> 1) - 16, y = 30;

  if (random(1) < .5)  boxes.add(new Box(x, y));
  else                 boxesB.add(new BoxB(x, y));
}
3 Likes

Very nice.

Learned a lot about loop()/redraw()/loop(), and their limitations.

Was trying to return the random number from the clientEvent(), but this method is much better. Neat code.

Appreciate the hand-holding! Time was tight.

1 Like