JOGL shutdown issues on Mac

Hi all!

We are having a shutdown issue with the JOGL on Mac OS X Leopard. You can only ever run our JOGL applet once in a browser without a complete browser shutdown and restart. And when you kill the window you get thread exceptions. Here’s the dumps FWIW and thanks for ANY help, we are hosed right now

End of first applet:
eb 4, 2010 7:51:51 PM imi.demos.AppletBase stop
INFO: stop
Feb 4, 2010 7:51:51 PM imi.demos.AppletBase destroy
INFO: destroy
Exception in thread “pool-2-thread-1” java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:127)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1175)
at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:431)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:369)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:947)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
at java.lang.Thread.run(Thread.java:637)
Exception in thread “pool-2-thread-4” java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:127)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1175)
at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:431)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:369)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:947)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
at java.lang.Thread.run(Thread.java:637)
Exception in thread “pool-2-thread-5” java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:127)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1175)
at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:431)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:369)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:947)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
at java.lang.Thread.run(Thread.java:637)
Exception in thread “pool-2-thread-2” java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:127)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1175)
at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:431)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:369)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:947)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
at java.lang.Thread.run(Thread.java:637)
Exception in thread “pool-2-thread-3” java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:127)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1175)
at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:431)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:369)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:947)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:907)
at java.lang.Thread.run(Thread.java:637)
Mac-Pro:~ macuser$

Exception when started the next applet in the session:

Feb 4, 2010 7:51:27 PM imi.demos.AppletBase start
INFO: start
Exception in thread “Thread-20” java.lang.IllegalStateException: There is already an active OpenGL canvas.
at com.jme.system.jogl.JOGLDisplaySystem.createCanvas(JOGLDisplaySystem.java:310)
at org.jdesktop.mtgame.Renderer.createCanvas(Renderer.java:583)
at org.jdesktop.mtgame.Renderer.addRenderBuffer(Renderer.java:605)
at org.jdesktop.mtgame.RenderManager.addRenderBuffer(RenderManager.java:180)
at imi.demos.AppletBase$RenderPanel.(AppletBase.java:374)
at imi.demos.AppletBase$AppletInitializer.run(AppletBase.java:202)
at java.lang.Thread.run(Thread.java:637)

Report to moderator 65.34.30.186

What’s causing the IllegalMonitorStateExceptions? Are you trying to call dispose() on the canvas in the wrong thread? Please post your destroy() method.

We’ll posted the complete destroy() code path in a bit, it will take some time to put together because we are using MTGame on custom jME on JOGL :slight_smile: But we’ll post it because it is a serious show stopper for our application on Mac OS (for any browser, it happens in appletviewer, it’s a core Mac Java exit/timing issue).

Thanks so much for any help.

BTW, this code stack we have is in open source up on Google Code, so all help goes to a public project :slight_smile:

Thanks for the help :slight_smile:

mtgame WorldManager:

public void shutdown() {
    physicsManager.quit();      // we are not using that one
    processorManager.quit();
    renderManager.quit();
}

ProcessorManager:

/**
 * The main run loop
 */
public void run() {
    ProcessorComponent[] runList = null;
    
    initController();
    while (!done) {
        
        // Pause running if flag is set
        while (!running) {
            try {
                Thread.sleep(333, 0);
            } catch (InterruptedException e) {
                System.out.println(e);
            }
        }
        
        // Gather the list of processor components to execute
        // This includes any chained processors
        runList = waitForProcessorsTriggered();
        dispatchTasks(runList);
        
        // Now, let the renderer complete the commit phase
        worldManager.runCommitList(runList);
        armProcessors(runList);
    }

    for (int i=0; i<processorThreads.length; i++) {
        processorThreads[i].quit();
    }
}

void quit() {
    done = true;
}

ProcessorThread:

synchronized void quit() {
    done = true;
    notify();
}

public void run() {
    ProcessorComponent pc = null;
    
    initProcessor();
    while (!done) {            
        // This synchonized method will block until there's something to do.
        pc = getNextProcessorComponent();
        if (pc == null && done) {
            break;
        }
        
        // Now compute this process and all of it's chains.
        try {
            pc.compute(pc.getCurrentTriggerCollection());
        } catch (Exception e) {
            System.out.println("MTGame: Exception Caught in Processor compute: " + e);
            e.printStackTrace();
        }

        pc = pc.getNextInChain();
        while (pc != null) {
            try {
                pc.compute(pc.getCurrentTriggerCollection());
            } catch (Exception e) {
                System.out.println("MTGame: Exception Caught in Processor compute: " + e);
                e.printStackTrace();
            }
            pc = pc.getNextInChain();
        }
    }
}

synchronized ProcessorComponent getNextProcessorComponent() {
ProcessorComponent pc = null;

    if (queue.isEmpty() && !done) {
        waiting = true;
        processorManager.notifyDone(this);
        while (queue.isEmpty() && !done) {
            try {
                wait();
            } catch (InterruptedException e) {
                System.out.println(e);
            }
        }
    }
    if (!done) {
        pc = queue.removeFirst();
    }
    return (pc);
}

RenderManager:

/**
 * This call causes the renderer to cleanly exit.  It cannot
 * be called from a render callback.
 */
public void quit() {
    renderer.quit();
}

Renderer:

void quit() {
    done = true;
    while (!finished) {
        try {
            Thread.sleep(333, 0);
        } catch (InterruptedException e) {
            System.out.println(e);
        }
    }
}


public void run() {
    long processTime = -1;
    long frameStartTime = -1;
    long renderTime = -1;
    long totalTime = -1;
    int statCount = 0;
    long updateTime = 0;
    long frameRenderTime = 0;
    long commitTime = 0;
    
                
    initRenderer();   
    while (!done) {
        // Pause running if flag is set
        while (!running) {
            try {
                Thread.sleep(333, 0);
            } catch (InterruptedException e) {
                System.out.println(e);
            }             
        }
        
        // Snapshot the current time
        frameStartTime = System.nanoTime();

        processInternalUpdates();
           
        // Ready to update and render.  
        bufferController.startFrame(jmeRenderer);
        if (bufferController.anyBuffers()) {
            currentScreenBuffer = bufferController.getCurrentOnscreenBuffer();
            if (currentScreenBuffer != null &&
                currentScreenBuffer.makeCurrent(displaySystem, jmeRenderer)) {
                /**
                 * Let the processor manager notify processors of any LOD changes
                 */
                worldManager.getProcessorManager().updateProcessorComponentLODs(currentScreenBuffer.getCameraComponent().getCamera());

                /*
                 * This block of code handles calling entity processes which are
                 * locked to the renderer - like the current camera.
                 */
                runProcessorsTriggered();
                
                /**
                 * This allows anyone that needs to do some updating in the render
                 * thread be called
                 */
                processRenderUpdates();

                /**
                 * Process the RenderComponent LOD's.  Do it here, so any changes
                 * can take effect this frame.
                 */
                processRenderComponentLODs(currentScreenBuffer.getCameraComponent().getCamera());

                synchronized (jmeSGLock) {
                    processJMEUpdates(totalTime / 1000000000.0f);
                }
                runPhysicsSystems(totalTime / 100000000.0f);

                updateTime = System.nanoTime();
                //System.out.println("Update Time: " + (updateTime - frameStartTime)/1000000);
                // Finally, render the scene
                bufferController.renderScene(displaySystem, jmeRenderer, this);
                bufferController.endFrame(jmeRenderer);
                frameRenderTime = System.nanoTime();
                //System.out.println("Render Time: " + (frameRenderTime - updateTime)/1000000);
                currentScreenBuffer.release();
            }
        }
        /*
         * Now we track some times, and process the commit lists
         */
        
        // Snapshot the time it took to render
        renderTime = System.nanoTime() - frameStartTime;
        //Calculate the amount of time left to process commits
        processTime = desiredFrameTime - renderTime;
        
        // Process the commit list

        if (bufferController.anyBuffers()) {
            currentScreenBuffer = bufferController.getCurrentOnscreenBuffer();
            if (currentScreenBuffer != null &&
                currentScreenBuffer.makeCurrent(displaySystem, jmeRenderer)) {

                synchronized (pickLock) {
                    processCommitList(processTime);
                    if (processTime < 0) {
                        //System.out.println("NEED TO ADAPT TO NEGATIVE PROCESS TIME");
                    }
                }
                currentScreenBuffer.release();
            }
        }

        commitTime = System.nanoTime();

        // Let the processes know that we want to do a frame tick
        renderManager.triggerNewFrame();

        //System.out.println("Max Memory: " + Runtime.getRuntime().maxMemory());
        //System.out.println("Total Memory: " + Runtime.getRuntime().totalMemory());
        //System.out.println("Free Memory: " + Runtime.getRuntime().freeMemory());
      
        // Decide if we need to sleep
        totalTime = System.nanoTime() - frameStartTime;

// System.out.println("-----------------------------------------------");
// System.out.println("Desired FR: " + desiredFrameRate);
// System.out.println("Update Time: " + (updateTime - frameStartTime)/1000000);
// System.out.println("Render Time: " + (frameRenderTime - updateTime)/1000000);
// System.out.println("Commit Time: " + (commitTime - frameRenderTime)/1000000);
// System.out.println("Total Time: " + totalTime/1000000);
// System.out.println("Desire Time: " + desiredFrameTime/1000000);
if (totalTime < desiredFrameTime) {
// Sleep to hit the frame rate
try {
int sleeptime = (int)(desiredFrameTime - totalTime);
int numMillis = sleeptime/1000000;
int numNanos = sleeptime - (numMillis*1000000);
//System.out.println("Sleeping for " + numMillis + ", " + numNanos);
Thread.sleep(numMillis, numNanos);
} catch (InterruptedException e) {
System.out.println(e);
}
}

        if (frameRateListener != null) {
            listenerCountdown--;
            if (listenerCountdown == 0) {
                long currentTime = System.nanoTime();
                long elapsedTime = currentTime - listenerStarttime;
                float flTime = elapsedTime/1000000000.0f;
                float framerate = ((float)frameRateListenerFrequency)/flTime;
                frameRateListener.currentFramerate(framerate);

                listenerCountdown = frameRateListenerFrequency;
                listenerStarttime = currentTime;
            }
        }
    }
    // Clear out some updates
    processInternalUpdates();
    finished = true;
}

/**
 * Add a RenderUpdater to the list of objects to update in the render thread.
 */
void addRenderUpdater(RenderUpdater ru, Object obj, boolean wait) {
    if (finished) {
        return;
    }

    if (wait && Thread.currentThread() == this) {
        ru.update(obj);
    } else {
        RenderUpdaterOp ruop = new RenderUpdaterOp(ru, obj, wait);
        synchronized (renderUpdateList) {
            renderUpdateList.add(ruop);
        }
        if (wait) {
            while (!ruop.done  && !done) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    System.out.println(e);
                }
            }
        }
    }
}

jmeRenderer?? Do you use JME? Which version? If you use JME 2.0 with JOGL, please use the fixes that I posted on their forum (and thank cylab for his help), it is very impacting under Linux and Mac. Let me know if something is still wrong.

Yes we do (use jME, although highly modified and slowly being stripped out) so thanks much for the lead! And we’ll post progress here.

org.jdesktop.mtgame? Are you using Project Wonderland? The threading model is then different, I hope our fixes solve your problem but I have never tested them with the renderer manager of MTGame.

We are using mtgame and a newer version of project wonderland’s avatar animation system.

Could you please link us to the fixes? There are multiple forums :slight_smile:

We are not using Project Wonderland per se, that is we are using the MTGame stack, and our extensions, which are ports and reimplementations of tech on that framework.

Project Wonderland includes a host of other stuff like there networking, camera model, module arch, etc.

We are working with just the MTGame/jME/JOGL client-side stack, which with our additions (as posted to the community at our blog and Google Code) make it more like the usual client-side 3D game engine. This is source stack with which we incubate and make contributions to the Project Wonderland, as well as develop our own apps on.

The fixes are there:
http://www.jmonkeyengine.com/forum/index.php?topic=11536.0