[3.1 Alpha] Project is not closing correctly

Thank you for the explanation. :smiley:

Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.60-b23 mixed mode):

"Timer-0" #25 prio=5 os_prio=0 tid=0x0000000033dd5800 nid=0x3138 in Object.wait() [0x000000001b8af000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    at java.util.TimerThread.mainLoop(Unknown Source)
    - locked <0x00000000e4290ca0> (a java.util.TaskQueue)
    at java.util.TimerThread.run(Unknown Source)

"DestroyJavaVM" #22 prio=5 os_prio=0 tid=0x0000000002fa0800 nid=0x1aa0 waiting on condition [0x0000000000000000]
 java.lang.Thread.State: RUNNABLE

"TimerQueue" #20 daemon prio=5 os_prio=0 tid=0x000000001b11a800 nid=0x235c waiting on condition [0x0000000020f9f000]
  java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x00000000812f8468> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    at java.util.concurrent.locks.LockSupport.park(Unknown Source)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(Unknown Source)
    at java.util.concurrent.DelayQueue.take(Unknown Source)
    at javax.swing.TimerQueue.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

 "D3D Screen Updater" #19 daemon prio=7 os_prio=1 tid=0x000000001b10e800 nid=0x2430 in Object.wait() [0x0000000020e9f000]
 java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    at sun.java2d.d3d.D3DScreenUpdateManager.run(Unknown Source)
    - locked <0x00000000812f86d8> (a java.lang.Object)
    at java.lang.Thread.run(Unknown Source)

 "AWT-Windows" #13 daemon prio=6 os_prio=0 tid=0x000000001aecc000 nid=0x3650 runnable [0x000000001b9ae000]
java.lang.Thread.State: RUNNABLE
    at sun.awt.windows.WToolkit.eventLoop(Native Method)
    at sun.awt.windows.WToolkit.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

 "Java2D Disposer" #11 daemon prio=10 os_prio=2 tid=0x000000001aedc000 nid=0x1200 in Object.wait() [0x000000001b7af000]
java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    at java.lang.ref.ReferenceQueue.remove(Unknown Source)
    - locked <0x00000000812f7f20> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(Unknown Source)
    at sun.java2d.Disposer.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

"Service Thread" #10 daemon prio=9 os_prio=0 tid=0x000000001a25e800 nid=0x3318 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"C1 CompilerThread3" #9 daemon prio=9 os_prio=2 tid=0x000000001a1d4800 nid=0x27dc waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"C2 CompilerThread2" #8 daemon prio=9 os_prio=2 tid=0x000000001a1cf000 nid=0x3d0 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #7 daemon prio=9 os_prio=2 tid=0x000000001a1c8800 nid=0x1390 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #6 daemon prio=9 os_prio=2 tid=0x000000001a1c4800 nid=0x213c waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000000001a1c3000 nid=0x3104 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000000001a1c2000 nid=0x988 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x0000000003098800 nid=0x2d4c in Object.wait() [0x000000001a08e000]
java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    at java.lang.ref.ReferenceQueue.remove(Unknown Source)
    - locked <0x0000000081306760> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(Unknown Source)
    at java.lang.ref.Finalizer$FinalizerThread.run(Unknown Source)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000003092000 nid=0x2bd8 in Object.wait() [0x0000000019f8f000]
java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    at java.lang.Object.wait(Unknown Source)
    at java.lang.ref.Reference$ReferenceHandler.run(Unknown Source)
    - locked <0x000000008135b028> (a java.lang.ref.Reference$Lock)

"VM Thread" os_prio=2 tid=0x00000000180c9000 nid=0xe8 runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000002fb6800 nid=0x1274 runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000002fb8000 nid=0x17f8 runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000002fb9800 nid=0x2a54 runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000002fbc000 nid=0x19f4 runnable

"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x0000000002fbd000 nid=0x199c runnable

"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000002fbe800 nid=0x2c50 runnable

"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x0000000002fc2800 nid=0x3414 runnable

"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x0000000002fc3800 nid=0x2cec runnable

"VM Periodic Task Thread" os_prio=2 tid=0x000000001a272000 nid=0x1734 waiting on condition

JNI global references: 453

Heap
PSYoungGen      total 227840K, used 165435K [0x00000000d5980000, 0x00000000ea680000, 0x0000000100000000)
eden space 143872K, 65% used [0x00000000d5980000,0x00000000db6376e0,0x00000000de600000)
from space 83968K, 83% used [0x00000000e4280000,0x00000000e8757880,0x00000000e9480000)
to   space 94720K, 0% used [0x00000000de600000,0x00000000de600000,0x00000000e4280000)
ParOldGen       total 161792K, used 101889K [0x0000000080c00000, 0x000000008aa00000, 0x00000000d5980000)
object space 161792K, 62% used [0x0000000080c00000,0x0000000086f804e8,0x000000008aa00000)
Metaspace       used 23089K, capacity 23405K, committed 23808K, reserved 1071104K
class space    used 2616K, capacity 2724K, committed 2816K, reserved 1048576K

Well the good thing is, I cannot see the memory tracking anywhere in there anymore :smile:

Pssoble causes could be:

the timer
all other threads are daemons or necessary for the vm internals as far as I can see.

Does the basic starter app when you create a new app do this also?

If not, you could start from there and see what you have to add from your game to cause the hanging. Especially if you do anything with holding references to an AWT/Swing window or other Java2D resources.

All of my 3.1 based games close correctly so my guess is that the basic game template will also close correctly.

Well the timer thread is the only non-system, non-daemon thread in the entire list.

Do you have a timer task scheduled to be executed at a later time?

You mean without loading anything else? The only thing I found is: If I am in the start menu of my application (itā€™s my custom state and the first state of my project), I have no problems to close the app completely. Which means there anything in the game-state which causes this behaviour, but I donā€™t see which part could cause this problem. :confused: I init my GUI, several objects and their position based on a forced-based layout and init my data basis (like metadata for a market simulation). Nothing which could case something like this, I would say. There have to be anything in jme 3.1 which have been changed which is causing this, 3.0 has no problems executing this.

No I havenā€™t used a timer to schedule any task.

I mean exactly what I said. Create a new project, ie: a new basic starter app using the basic game template. But youā€™ve already confirmed that it works with a simplified app so there is no need to do that now. There is something your code is doing that is causing this hanging.

Yes, lots of things have changed in 3.1 that might be exploiting this issue in your app now. Now that you know that itā€™s something your code is doing then you can try to isolate it. Something in 3.0 was apparently hiding this somehow.

I mean, it could be a bug in 3.1 but itā€™s hard to say what it is and even harder to find when none of our apps exhibit the issue.

Iā€™ve found a possible clue. It seems like that Nifty is responsible for the hanging. My guess would be that the batch renderer is somehow the core problem (because thatā€™s the biggest part of nifty which has been changed, as far as I know). Well, if I reduced the amount of elements on my HUD screen I had no problems to close the app. After slightly increasing the amount, the problem appeared again.

Is it the number of elements or perhaps some specific elements?

Itā€™s the number of elements, I am sure of it. Iā€™ve tested it with a for loop and depending on the amount of iterations I have problems to exit the app or not.

Finally Iā€™ve got a test case with basic game template. This will make your game unable to close.:

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.niftygui.NiftyJmeDisplay;
import com.jme3.renderer.RenderManager;
import de.lessvoid.nifty.Nifty;
import de.lessvoid.nifty.builder.LayerBuilder;
import de.lessvoid.nifty.builder.PanelBuilder;
import de.lessvoid.nifty.builder.ScreenBuilder;
import de.lessvoid.nifty.controls.button.builder.ButtonBuilder;
import de.lessvoid.nifty.controls.label.builder.LabelBuilder;
import de.lessvoid.nifty.elements.Element;
import de.lessvoid.nifty.render.batch.BatchRenderConfiguration;
import de.lessvoid.nifty.screen.Screen;
import de.lessvoid.nifty.screen.ScreenController;
import de.lessvoid.nifty.tools.Color;

public class Main extends SimpleApplication implements ScreenController {

    private Nifty nifty;
    private NiftyJmeDisplay niftyDisplay;

    public static void main(String[] args) {
        Main app = new Main();
        app.start();
    }

    @Override
    public void simpleInitApp() {
        inputManager.deleteMapping(SimpleApplication.INPUT_MAPPING_EXIT);

        niftyDisplay = NiftyJmeDisplay.newNiftyJmeDisplay(
                this.assetManager,
                this.inputManager,
                this.audioRenderer,
                this.guiViewPort,
                new BatchRenderConfiguration());

        nifty = niftyDisplay.getNifty();
        viewPort = renderManager.createPostView("Test", guiViewPort.getCamera().clone());
        viewPort.addProcessor(niftyDisplay);
        nifty.isIgnoreKeyboardEvents();
        nifty.setDebugOptionPanelColors(false);

        createHUDScreen("TEST");
        initControls();

        for (int i = 0; i < 500; i++) {
            //both elements alone will break the game; haven't check all possible elements yet
            
            
            ButtonBuilder createButton = new ButtonBuilder("");
            createButton.id("Test-Content" + i);
            createButton.height("10%");
            createButton.width("10%");
            createButton.valignCenter();
            createButton.alignCenter();
            createButton.focusable(false);
            createButton.style("");
            createButton.backgroundColor(Color.WHITE);
            createButton.interactOnClick("windowControl(test)");
            createButton.childLayoutCenter();
            createButton.visibleToMouse(true);
            Element panelT = createButton.build(nifty, nifty.getScreen("TEST"), nifty.getScreen("TEST").findElementById("TEST-Layer0"));

            
            
//            LabelBuilder createLabel = new LabelBuilder();
//            createLabel.id("Test-Label" + i);
//            createLabel.text("Bla");
//            createLabel.textHAlignCenter();
//            createLabel.textVAlignCenter();
//            createLabel.alignCenter();
//            createLabel.valignCenter();
//            createLabel.height(100 + "%");
//            createLabel.width(100 + "%");
//            createLabel.wrap(true);
//            createLabel.font("aurulent-sans-16.fnt");
//            createLabel.build(nifty, nifty.getScreen("TEST"), nifty.getScreen("TEST").findElementById("TEST-Layer0"));
        }

        nifty.gotoScreen("TEST");
        inputManager.setCursorVisible(true);
    }

    private void createHUDScreen(String screenID) {
        ScreenBuilder screenBuilder = new ScreenBuilder(screenID);
        screenBuilder.controller(this);

        LayerBuilder layer0 = new LayerBuilder(screenID + "-Layer0");
        layer0.childLayoutCenter();
        screenBuilder.layer(layer0);

        LayerBuilder layer1 = new LayerBuilder(screenID + "-Layer1");
        layer1.childLayoutCenter();
        screenBuilder.layer(layer1);

        LayerBuilder layer2 = new LayerBuilder(screenID + "-Layer2");
        layer2.childLayoutAbsolute();
        screenBuilder.layer(layer2);

        LayerBuilder layer3 = new LayerBuilder(screenID + "-Layer3");
        layer3.childLayoutAbsolute();
        screenBuilder.layer(layer3);

        nifty.addScreen(screenID, screenBuilder.build(nifty));
    }

    private void initControls() {
        nifty.loadStyleFile("nifty-default-styles.xml");
        nifty.loadControlFile("nifty-default-controls.xml");
    }

    @Override
    public void simpleUpdate(float tpf) {
        //TODO: add update code
    }

    @Override
    public void simpleRender(RenderManager rm) {
        //TODO: add render code
    }

    @Override
    public void bind(Nifty nifty, Screen screen) {

    }

    @Override
    public void onStartScreen() {

    }

    @Override
    public void onEndScreen() {

    }
}

@pspeed Can you verify this on your side?

Your test case looks very nice for someone to help find what is happeningā€¦ but since I abandoned nifty like over 3 years ago, Iā€™m personally loathe to help debug it or to setup a project that uses it. I just donā€™t have the bandwidth right now.

Your test case should be perfect for someone who uses nifty, though.

Ok thanks ^^ It also seems I am not the only one with this problem. I have created an issue on the nifty github page, but I donā€™t think this will be fixed anytime soon. Which means jme 3.1 will be a no-go until this bug is fixed (it seems that 1.4.x of nifty is way more bugged than 1.3.x).

It seems that nifty is keeping two timer threads up after destruction, which prevent the application from closing.

I have to say, I do not use the nifty gui and I have the exact same problem at the moment, the game application never close after exiting.

Same thread 4, the ā€œRawInputEventQueuā€

Follow the instructions above for running from the command line and getting a thread dump after the app ā€˜hangsā€™. Some non-daemon thread is keeping your app open. Daemon threads wonā€™t do it.

Mega Hack:

in application:

@override
OnDestroy{
System.exit(0);
}

Unclean as fuck, but will terminate at least.

2 Likes

I seem to have an issue in cleanup code.

When I try to remove a FilterPostProcessor with samples from the viewport when I am exiting an application, I will get an exception. I will provide the stack trace once I get back home.

It happens sometime in game as well.

I added the issue report here:

Just curious: what do folks use if you arenā€™t using Nifty anymore?

In order of my preference:
-Lemur (because I wrote it and I write 3D GUIs and itā€™s directly JME scene graph components) http://hub.jmonkeyengine.org/c/user-code-projects/lemur
-JavaFX (if I didnā€™t have Lemur this is probably what Iā€™d use) I donā€™t have the link handy but you can search the forum.
-tonegodGUI - similarly search the forum