StandardGame Thread Explosion - attach and detach Child error?

I'll post the full code below - my point cloud is building semiappropriately, but after a bit, the engine skips a beat and the CPU runs out of control (the whole thing crashes as well).



This is the error:

Nov 16, 2006 12:17:39 AM com.jme.scene.Node attachChild
INFO: Child (p) attached to this node (RootNode)
Nov 16, 2006 12:17:40 AM com.jme.scene.Node detachChildAt
INFO: Child removed.
Nov 16, 2006 12:17:40 AM com.jmex.game.DefaultUncaughtExceptionHandler uncaughtException
SEVERE: Main game loop broken by uncaught exception
java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
   at java.util.ArrayList.RangeCheck(ArrayList.java:546)
   at java.util.ArrayList.get(ArrayList.java:321)
   at com.jme.scene.Node.draw(Node.java:509)
   at com.jme.scene.Spatial.onDraw(Spatial.java:215)
   at com.jme.renderer.lwjgl.LWJGLRenderer.draw(LWJGLRenderer.java:1182)
   at com.jmex.game.state.DebugGameState.render(DebugGameState.java:247)
   at com.jmex.game.state.GameStateNode.render(GameStateNode.java:83)
   at com.jmex.game.StandardGame.render(StandardGame.java:318)
   at com.jmex.game.StandardGame.run(StandardGame.java:179)
   at java.lang.Thread.run(Thread.java:613)
Nov 16, 2006 12:17:40 AM com.jme.scene.Point <init>
INFO: Point created.
Nov 16, 2006 12:17:40 AM com.jme.scene.Node attachChild
INFO: Child (p) attached to this node (RootNode)
Nov 16, 2006 12:17:40 AM com.jme.scene.Node detachChildAt
INFO: Child removed.



And here is the code - is there a better method to perform an update on all of the points?  Thanks!

package jmetest.LaserTest;

import java.util.Random;

import com.jme.math.Vector3f;
import com.jme.scene.Point;
import com.jme.scene.shape.Box;
import com.jme.scene.state.LightState;
import com.jme.system.DisplaySystem;
import com.jmex.game.StandardGame;
import com.jmex.game.state.DebugGameState;
import com.jmex.game.state.GameStateManager;
//import com.jme.bounding.*;

/**
 * Laser Draw Test
 *
 * @author Jeff Kramer
 */


public class LaserTest {
   static final int NUMPOINTS = 25000;
   
   
   public static void main(String[] args) throws Exception {
        DebugGameState gameState = StartGame("game");
       
       
       
        Random r = new Random();
       
        Vector3f[] vertexes = new Vector3f[NUMPOINTS];
        int iTrack = 0;
        int RotSize = 5000;
        double LaserScanTime = 0.053; //seconds per scan
        int LaserValues = 361;
        int LaserRange = 10; //in m
        double RotRate = 1.5; //Rev/Sec
        double RotPush = LaserScanTime*RotRate*RotSize; //points/scan
        double RotStep = (RotPush/LaserValues)*((2*Math.PI)/RotSize); //rads/dp
        double phi = 0;
        while (true){
           
           for (int i=0; i<=LaserValues; i++) {
              for (int time=0; time<500000;time++){} //Timer
              double v3d[] = JSci.maths.CoordinateMath.sphericalToCartesian(r.nextDouble()*LaserRange, phi, (i*Math.PI)/360);
              float v3f[] = {(float)v3d[0], (float)v3d[1], (float)v3d[2]};
              vertexes[iTrack] = new Vector3f(v3f[0], v3f[1], v3f[2]);
              phi = phi+RotStep;
              if (iTrack < NUMPOINTS-1) {
                 iTrack++;
              } else {
                 iTrack = 0;
              }
              //System.out.print("iTrack = " + iTrack + "n");
           }
           DrawScene(gameState, vertexes);
        }
       
    }
   
    public static DebugGameState StartGame (String GameName) {
      StandardGame game = new StandardGame(GameName); //Create the Game
      game.start(); //Start the thread
      
      DebugGameState DGS = new DebugGameState(); //create game state
      GameStateManager.getInstance().attachChild(DGS); //Attach it
      DGS.setActive(true); //Turn it on!
      //Box Push
      Box box = new Box("TestBox", new Vector3f(), 0.1f, 0.1f, 0.1f);      // Create a Box
        box.setRandomColors();   // Set random colors on it
        box.updateRenderState();   // Update the render state so the colors appear (the game is already running, so this must always be done)
        DGS.getRootNode().attachChild(box);   // Attach the box to rootNode in DebugGameState
        //DGS.getRootNode().detachChild(box); // Detach the box
      
      //Lighting Section
      LightState lightState = DisplaySystem.getDisplaySystem().getRenderer().createLightState();
      lightState.setEnabled(false);
      DGS.getRootNode().setRenderState(lightState);
      DGS.getRootNode().setLightCombineMode(LightState.REPLACE);
       
       return DGS;   
    }
   
    public static boolean DrawScene(DebugGameState DGS, Vector3f[] vPoints) {
       DGS.getRootNode().detachChildNamed("p");
       Point A = new Point("p", vPoints, null, null, null);
       DGS.getRootNode().attachChild(A);
       return true;
    }
   
   
}

I fear this will take more internal knowledge than I currently have of jME to resolve.  Upon first glance it looks to be a thread safety issue popping up, but I don't know for sure.

To me it looks like you have two threads…  the main thread and the StandardGame thread.  Your main thread is attaching and removing Points in the DrawScene method.  The StandardGame thread is drawing the scene.  Correct so far?  Unfortunately, since they are not synched, sometimes this may happen concurrantly, so when the for loop in jME's Node class is going through it's children, it may feel it has X children, but between that check and the retrieval of that child, your other thread can yank that child out, leaving it to cry foul! when there isn't enough children to grab one from that index anymore.



You should either move the updating of the verts into the GameState update, or if that would stall out the render process too much, then put the actual apply of the data in your GameState update method.  DarkFrog could illustrate how to put stuff in the GameState update (no code in front of me at the moment.)



Also, I would urge you to change your updating loop to use set(x,y,z) instead of generating brand new Vector3f objects (precreate the objects before the while loop), and don't needlessly create that float[3] array in there either.  :slight_smile:



Hope that all helps.

Thanks renanse, that makes a lot of sense.  I agree this might be a good place to put directly in the OpenGL thread.  You could also try out StandardGame's new locking feature with very minor additions to your code by simply calling game.lock() before you do anything and then game.unlock() when you're done.  This will block the OpenGL thread temporarily at the beginning of the update() so you can do any work that might cause concurrency issues and then when you unlock() the update() continues on.  Much nicer for situations like this where you would otherwise have to pass a Callable into the queue each time.  The only real downside is that it can slow your thread down a bit since the lock() invocation will block until it gets to the beginning of the update() on the OpenGL thread (which shouldn't take very long if your FPS isn't like 1).

I'll try the blocking and let you know how it goes - thanks!

This works perfectly.  Thank you! :slight_smile:

Qworg said:

This works perfectly.  Thank you! :)


The game.lock() and game.unlock()?  Honestly, I threw the code in without actually using it (I made sure current code didn't break, but didn't test utilizing the feature) so this is good to hear. ;)