We are about to switch to a new forum software. Until then we have removed the registration on this forum.
I'm designing this generative visuals system that uses OSC data from a musician to produce these kind of abstract "asteroids" in real time. I'm running into this Concurrent Modification Exception which I'm assuming has to do with the fact that the OSC Event is another thread (or something? happening alongside my draw loop) and so maybe when iterating through my asteroid objects an asteroid is added, causing an error. I thought the synchronize trick would work, no such luck. Any help is appreciated!
Note: Just tried it with also protecting/removing the .remove() portion of the code, same error (although perhaps less frequent?)
// OSC STUFF
volatile boolean isBusy = false;
/* incoming osc message are forwarded to the oscEvent method. */
void oscEvent(OscMessage theOscMessage) {
if (theOscMessage.addrPattern().equals("/peter/rhythm")) {
if (!isBusy)
asteroids.add(new Asteroid(0));
}
}
// DRAW STUFF
void wormhole() {
pg.background(bgColor, bgTrans);
// there are stars from last scene, animate those suckers
for (Star s : stars) {
s.display();
}
isBusy = true;
// update, display asteroids
synchronized (asteroids) {
for (Asteroid a : asteroids) {
a.update();
a.display();
}
}
isBusy = false;
}
// DELETING ASTEROIDS STUFF FROM DRAW() maybe this needs synch too?
// destroy anything that is offscreen
for (int i = asteroids.size() - 1; i >= 0; i--) {
Asteroid a = asteroids.get(i);
if (a.dead()) {
asteroids.remove(i);
}
}
// ASTEROID class
float asteroidTrans = 120; // global transparency modifier for asteroids
class Asteroid {
PVector pos;
color cFill, cStroke;
float r; // rotation
float rotateSpeed = .005;
float sw = 2; //strokeWeight
float ld = 5; // line distance
float p; // phase
float pRate = .3; // phase rate
float pAdjust; // use this number to add to s [or any other value)
float speed = 1;
float s = 1; // starting size
float growthRate = .05; // growth per frame
int type = 0; // type of asteroid
// properties
// phase pulsates size of asteroid
// corona gives it a circular outline
// death color makes it change space bg color on impact w screen
// behaviors
boolean ROTATE, PHASE;
// shape types
boolean BOX, SPHERE, LINES, CORONA, TRI;
// on death/ birth
boolean DEATH_COLOR;
// extras
Asteroid(int type) {
this.type = type;
pos = new PVector(random(width), random(height), far);
// randomize its initial rotation
r = random(TWO_PI);
switch (type) {
case 0: // black cube
cFill = color(0);
cStroke = color(255);
ROTATE = true;
BOX = true;
break;
case 1: // colorful, pulsating circles, on death make bg that color
sw = 0;
// random colors
cFill = color(random(255), random(255), random(255), random(20, 255));
cStroke = cFill;
ROTATE = true;
PHASE = true;
SPHERE = true;
CORONA = true;
DEATH_COLOR = true;
break;
case 2: // line creature..?
LINES = true;
PHASE = true;
//CORONA = true;
//pRate = .001;
ld = 20;
sw = 1;
cStroke = color(255);
break;
case 3: // triangles
TRI = true;
ROTATE = true;
PHASE = true;
cFill = color(255);
rotateSpeed = .01;
break;
}
}
void update() {
// all asteroid types move towards you (and grow)
pos.z += speed * hyperspaceModifier;
s += growthRate * hyperspaceModifier;
// rotating asteroids
if (ROTATE) {
r += rotateSpeed * hyperspaceModifier;
}
if (PHASE) {
p += pRate; // inc phase
pAdjust += sin(p) * .5;
}
}
void display() {
// TRIANGLE
if (TRI) {
pg.pushMatrix();
pg.translate(pos.x, pos.y, pos.z);
if (ROTATE) {
pg.rotateZ(r);
}
pg.rotateX(3 * PI / 2);
pg.stroke(255);
if (PHASE) {
float r = map(sin(p), -1, 1, 0, 255);
float g = map(cos(p), -1, 1, 0, 255);
float b = map(tan(p), -1, 1, 0, 255);
cFill = color(r, g, b);
}
pg.fill(cFill);
pg.beginShape(TRIANGLES);
pg.vertex(-12, -12, -12);
pg.vertex( 12, -12, -12);
pg.vertex( 0, 0, 12);
pg.vertex( 12, -12, -12);
pg.vertex( 12, 12, -12);
pg.vertex( 0, 0, 12);
pg.vertex( 12, 12, -12);
pg.vertex(-12, 12, -12);
pg.vertex( 0, 0, 12);
pg.vertex(-12, 12, -12);
pg.vertex(-12, -12, -12);
pg.vertex( 0, 0, 12);
pg.endShape();
//pg.triangle(0, -10, 8, 10, -8, 10);
pg.popMatrix();
}
// LINE
if (LINES) {
pg.pushMatrix();
pg.translate(pos.x, pos.y, pos.z);
pg.stroke(cStroke);
pg.strokeWeight(sw);
int segments = 8;
PVector[] linePV = new PVector[segments];
linePV[0] = new PVector(0, 0, 0);
for (int i = 1; i < segments; i++) {
float x = cos( p + (segments / TWO_PI * i)) * ld * (i * .1);
float y = sin( p + (segments / TWO_PI * i)) * ld * (i * .1);
float z = linePV[i - 1].z - ld;
linePV[i] = new PVector(x, y, z);
pg.line(linePV[i - 1].x, linePV[i - 1].y, linePV[i - 1].z, x, y, z);
}
pg.popMatrix();
}
// CORONA
if (CORONA) {
pg.pushMatrix();
pg.translate(pos.x, pos.y, pos.z);
pg.stroke(cStroke, asteroidTrans);
pg.noFill();
pg.strokeWeight(sw + 4 + pAdjust);
pg.ellipse(0, 0, (s + pAdjust) * 1.3, (s + pAdjust) * 1.3);
pg.popMatrix();
}
// BOX
if (BOX) {
pg.pushMatrix();
pg.translate(pos.x, pos.y, pos.z);
if (ROTATE) rotate();
pg.fill(cFill, asteroidTrans);
pg.strokeWeight(sw);
pg.stroke(cStroke);
pg.box(s + pAdjust);
pg.popMatrix();
}
// SPHERE
if (SPHERE) {
pg.pushMatrix();
pg.translate(pos.x, pos.y, pos.z);
pg.strokeWeight(sw);
pg.stroke(cStroke);
pg.fill(cFill, asteroidTrans);
//pg.sphereDetail(7);
//if (ROTATE) rotate();
//pg.sphere(s + pAdjust);
pg.ellipse(0, 0, s + pAdjust, s + pAdjust);
pg.popMatrix();
}
}
boolean dead() {
// if object gets totally offscreen...
if (pos.z > 1000) {
// DEATH COLOR asteroids change bg color on impact
if (DEATH_COLOR) {
bgColor = cFill;
}
return true; // destroy object
} else {
return false;
}
}
// if this asteroid is a rotater
void rotate() {
pg.rotateY(r);
pg.rotateZ(r * 2);
pg.rotateX(r * 3);
}
}
Answers
Ah, the classic multi-threading CM Exception.
There are definitely more effective ways of getting around this problem that I haven't thought of, but the easiest option would be to replace the type of the variable asteroids to a CopyOnWriteArrayList, since I'm assuming asteroids is a regular ArrayList.
You can use this thread-safe ArrayList by placing this at the top of your code:
volatile
isBusy doesn't cut for protecting asteroids against ConcurrentModificationException at all! :-Ssynchronized
in all places that its size() is modified.synchronized
@ line 24 wasn't so necessary for no change in size() happens there!synchronized
blocks for the add() & remove() parts:And a similar online example as well: :bz
http://studio.ProcessingTogether.com/sp/pad/export/ro.9K-kjhuONcJDZ/latest
Ok I'm having the same issue trying to bring in OSC data then call a class. I tried both import java.util.concurrent.CopyOnWriteArrayList; and synchronized to no avail. I'm not sure my implementation of synchronized was correct though.
Any pointers in the right directions would be super helpful!
Here is a basic example:
The overall idea being that i am bringing data in via OSC and then passing that data to the ball class to make an instance each time data is passed in.
Set a flag in oscevent(), add stuff at end of draw if flag is set?
DUDE!!! Rock and Roll :D:D:D that totally works. And thanks for the quick reply.
I now see the implementation of the synchronized in the draw loop and the osc receiver. And basically that will synchronize any writing to or removing from the array? Very cool! Thanks.
If you need to mutate a lot of different lists/state in response to OSC messages you can also put the OSC messages into a list and then synchronize on that list to take care of this issue across the whole sketch.