[3.1 Alpha] Project is not closing correctly

Hi all,

After moving my project to 3.1 I am unable to close the application of my project correctly. I am executing the following method via Nifty:

public static void quit() {
    GameManager.g_niftyDisplay.getNifty().exit();
    g_simpleApplication.stop();
}

The application is simply not stopping at all. :confused: Has anyone any ideas if something specific has been changed which is causing this behaviour?

What do you mean, does not stop?
The openGL window closes but the application is still running?
I ported a testgame of mine using nifty to 3.1 alpha. I can check my behaviour in a few hours.

Okay, so my quit button in my JME 3.1 test project basically just executes

g_simpleApplication.stop();

if I exit the nifty first, I actually get a nullpointer in the inputmanager before reaching application.stop().
Why do you explicitly exit nifty first?

If I explicitly exit the nifty before calling application.stop() in my larger jme 3.0 project, I get a nullpointer during during Screen.endScreen. We simply call application.stop() there as well.

I’ve got no nullpointer, but with 3.0 the project closed without problems and in 3.1 only the frame itself is closing but the thread is still running.

Is your jme context in a window?
Our mapeditor (swing based, ewww) had a swing thread that we had to terminate by hand.

As mentioned my projects behave differently and I cannot see the difference without further details.
If you could build a minimal example and upload it to github or so, then I would love to have a look at it.

We are using the default SimpleApplication and default jme thread to launch our project. I could upload you a snippet but wouldn’t make sense unless you get a full picture of each component we are using. Maybe there have been a big change to the sound manager of jme which has to be terminated differently (or something else).

Sound is something we have not implemented yet.
Go check for that.
Shouldn’t VisualVM tell you what the remaining thread is?

According to my debugger, that’s still running after closing the window:

Oh yeah, what OS are you running?
I am on Ubuntu Linux 14.04. If you’re on windows the awt thread can behave differently.
I remember the open dialogs bahave completely different between win/mac/linux.
(Not just the window itself, but when it opens and if the other window is disabled or not)

Currently I am running Windows 10. Any ideas how to “fix” this?

Hmm I don’t have a magical answer for you, sorry.
I would try it on different OS to see if it’s windows specific, or even windows 10 specific.
Don’t know what they changed for Win 10. Haven’t installed it yet.

As I said I would create a minimal example with all the JME components (Sound, etc) you have in place,
and then see if it closes.

If it does, you know that your problem lies elsewhere.

Any chance you can post the call stack for “Thread-4”?

Just to make sure I used the right way to call the stack for “Thread-4”:

public static void quit() {
    g_simpleApplication.stop();
    Thread thread = getThread("Thread-4");
    System.out.println(Arrays.toString(thread.getStackTrace()));
}

private static Thread getThread(String strCashireID) {
    ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
    Thread[] threads = new Thread[threadGroup.activeCount()];
    threadGroup.enumerate(threads);
    for (Thread thread : threads) {
        if (thread != null && thread.getName().equals(strCashireID)) {
            return thread;
        }
    }
    return null;
}

And that’s the output:

[java.lang.Object.wait(Native Method),
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143),   
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164), 
com.jme3.util.BufferUtils$ClearReferences.run(BufferUtils.java:1394)]

The memory tracking system was written by @Empire_Phoenix, as you can see from this commit:
https://code.google.com/p/jmonkeyengine/source/detail?r=9669

Any idea why this thread stays awake even after the application has closed?

Are you sure it’s thread-4 that’s hold the app open? In the source code you reference, that thread is clearly marked as a daemon thread. It shouldn’t be keeping the JVM from exiting.

private static class ClearReferences extends Thread {

    ClearReferences() {
        this.setDaemon(true);
    }

    @Override
    public void run() {
        try {
            while (true) {
                Reference<? extends Buffer> toclean = BufferUtils.removeCollected.remove();
                BufferUtils.trackedBuffers.remove(toclean);
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Maybe something like this might give some clues.

   public static void main( String[] args )
   {
      startSomeThreads();
      Map<Thread,StackTraceElement[]> allThreads = Thread.getAllStackTraces();
      for( Map.Entry<Thread,StackTraceElement[]> thread : allThreads.entrySet() ) {
         System.out.println( thread + " is daemon = " + thread.getKey().isDaemon() );
         for( StackTraceElement stack : thread.getValue() )
            System.out.println( "    "+stack );
      }
   }

Are you actually enabling the tracking?
Cause by default it is disabled, so if no explicit call to BufferUtils.setTrackDirectMemotyEnabled(true) is done, it should not even start that thread.

Also this is older than 3.1 it is from 2012, so it would have probably caused issues for others as well. (or noone does use the tracking)

@Empire_Phoenix I did enabled it, but even after disabling it the certain behaviour still appears.

@gameboy1234 Here’s the output:

Thread[LWJGL Timer,5,main]=[Ljava.lang.StackTraceElement;@45f27ee2 is daemon = true
java.lang.Thread.sleep(Native Method)
org.lwjgl.opengl.Sync$1.run(Sync.java:116)
java.lang.Thread.run(Thread.java:745)

Thread[DestroyJavaVM,5,main]=[Ljava.lang.StackTraceElement;@7216c66e is daemon = false

Thread[D3D Screen Updater,7,system]=[Ljava.lang.StackTraceElement;@4cf55566 is daemon = true
java.lang.Object.wait(Native Method)
sun.java2d.d3d.D3DScreenUpdateManager.run(D3DScreenUpdateManager.java:423)
java.lang.Thread.run(Thread.java:745)

Thread[Timer-0,5,main]=[Ljava.lang.StackTraceElement;@14bab42a is daemon = false
java.lang.Object.wait(Native Method)
java.util.TimerThread.mainLoop(Timer.java:552)
java.util.TimerThread.run(Timer.java:505)

Thread[Reference Handler,10,system]=[Ljava.lang.StackTraceElement;@42e9dd31 is daemon = true
java.lang.Object.wait(Native Method)
java.lang.Object.wait(Object.java:502)
java.lang.ref.Reference$ReferenceHandler.run(Reference.java:157)

Thread[TimerQueue,5,system]=[Ljava.lang.StackTraceElement;@60846b4 is daemon = true
sun.misc.Unsafe.park(Native Method)
java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
   java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
java.util.concurrent.DelayQueue.take(DelayQueue.java:211)
javax.swing.TimerQueue.run(TimerQueue.java:171)
java.lang.Thread.run(Thread.java:745)

Thread[Signal Dispatcher,9,system]=[Ljava.lang.StackTraceElement;@60b06d76 is daemon = true

Thread[jME3 Audio Decoder,6,main]=[Ljava.lang.StackTraceElement;@216a9eae is daemon = true
java.lang.Thread.sleep(Native Method)
com.jme3.audio.openal.ALAudioRenderer.run(ALAudioRenderer.java:255)
java.lang.Thread.run(Thread.java:745)

Thread[Java2D Disposer,10,system]=[Ljava.lang.StackTraceElement;@2d66dfe7 is daemon = true
java.lang.Object.wait(Native Method)
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
sun.java2d.Disposer.run(Disposer.java:148)
java.lang.Thread.run(Thread.java:745)

Thread[jME3 Main,5,main]=[Ljava.lang.StackTraceElement;@699f1119 is daemon = false
java.lang.Thread.dumpThreads(Native Method)
java.lang.Thread.getAllStackTraces(Thread.java:1603)
Main.GameManager.getThread2(GameManager.java:111)
Main.GameManager.quit(GameManager.java:95)
GUI._ESC._POPUP.PopupExit.internalMethod(PopupExit.java:51)
GUI._ESC.ESCPopupController.popupControl(ESCPopupController.java:63)
GUI._ESC.ESCMainScreenController.windowControl(ESCMainScreenController.java:88)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:497)
de.lessvoid.nifty.NiftyMethodInvoker.callMethod(NiftyMethodInvoker.java:157)
de.lessvoid.nifty.NiftyMethodInvoker.performInvoke(NiftyMethodInvoker.java:102)
de.lessvoid.nifty.Nifty$DelayedMethodInvoke.perform(Nifty.java:1465)
de.lessvoid.nifty.Nifty.invokeMethods(Nifty.java:1441)
de.lessvoid.nifty.Nifty.handleDynamicElements(Nifty.java:434)
de.lessvoid.nifty.Nifty.access$1600(Nifty.java:85)
de.lessvoid.nifty.Nifty$NiftyInputConsumerImpl.processEvent(Nifty.java:1711)
de.lessvoid.nifty.Nifty$NiftyInputConsumerImpl.processMouseEvent(Nifty.java:1649)
com.jme3.niftygui.InputSystemJme.handleMouseEvent(InputSystemJme.java:123)
com.jme3.niftygui.InputSystemJme.onMouseButtonEventQueued(InputSystemJme.java:231)
com.jme3.niftygui.InputSystemJme.forwardEvents(InputSystemJme.java:295)
de.lessvoid.nifty.Nifty.update(Nifty.java:363)
com.jme3.niftygui.InputSystemJme.endInput(InputSystemJme.java:112)
com.jme3.input.InputManager.processQueue(InputManager.java:844)
com.jme3.input.InputManager.update(InputManager.java:908)
com.jme3.app.Application.update(Application.java:690)
com.jme3.app.SimpleApplication.update(SimpleApplication.java:234)
com.jme3.system.lwjgl.LwjglAbstractDisplay.runLoop(LwjglAbstractDisplay.java:152)
com.jme3.system.lwjgl.LwjglDisplay.runLoop(LwjglDisplay.java:192)
com.jme3.system.lwjgl.LwjglAbstractDisplay.run(LwjglAbstractDisplay.java:233)
java.lang.Thread.run(Thread.java:745)

Thread[Finalizer,8,system]=[Ljava.lang.StackTraceElement;@aa71a81 is daemon = true
java.lang.Object.wait(Native Method)
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

Thread[AWT-Windows,6,system]=[Ljava.lang.StackTraceElement;@7fcd7a95 is daemon = true
sun.awt.windows.WToolkit.eventLoop(Native Method)
sun.awt.windows.WToolkit.run(WToolkit.java:306)
java.lang.Thread.run(Thread.java:745)

Thread[Attach Listener,5,system]=[Ljava.lang.StackTraceElement;@64cf0b6c is daemon = true

“Timer-0” isn’t a daemon, it might be causing problems. It looks like a standard java.util.timer thread. Do you start that? Can you cancel()/stop()/interrupt() it to get it to stop running?

If you’re the one starting that timer, there’s specific ctors to use to start the internal thread as a daemon. I assume they’re there specifically for this problem.

Timer()
Creates a new timer.

Timer(boolean isDaemon)
Creates a new timer whose associated thread may be specified to run as a daemon.

Timer(String name)
Creates a new timer whose associated thread has the specified name.

Timer(String name, boolean isDaemon)
Creates a new timer whose associated thread has the specified name, and may be specified to run as a daemon.