Headless app states not cleaned up after uncaught exception

Hi,
I think I have found a bug in the engine. The cleanup() method is not called for my AppStates if I have an uncaught exception. In jME the main threads set a handler via Thread.setDefaultUncaughtExceptionHandler() in initInThread() of the contexts. But the NullContext lacks of the deinitInThread(); call after an exception has occured.

For that reason I cannot shut down my server because there are some states which terminates threads and makes some necessary file writes :confused:

LwjglWindow.java:

Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
	@Override
	public void uncaughtException(Thread thread, Throwable thrown) {
		listener.handleError("Uncaught exception thrown in " + thread.toString(), thrown);
		if (needClose.get()) {
			// listener.handleError() has requested the
			// context to close. Satisfy request.
			deinitInThread();
		}
	}
});

NullContext.java:

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
	public void uncaughtException(Thread thread, Throwable thrown) {
		listener.handleError("Uncaught exception thrown in "+thread.toString(), thrown);
	}
});

It works as expected if I add the missing lines to the NullConext class

Test case:

import java.util.logging.Level;

import com.jme3.app.SimpleApplication;
import com.jme3.app.state.AbstractAppState;
import com.jme3.system.AppSettings;
import com.jme3.system.JmeContext;
import com.jme3.system.JmeContext.Type;

public class AppTest extends SimpleApplication
{
	private float time;
	private final Type contextType;

	public AppTest(JmeContext.Type contextType)
	{
		this.contextType = contextType;
		AppSettings settings = new AppSettings(true);
		settings.setResizable(true);

		setSettings(settings);
		setShowSettings(false);

		java.util.logging.Logger.getLogger("com.jme3").setLevel(Level.SEVERE);

		start(contextType);
	}

	public static void main(String[] args)
	{
		new AppTest(Type.Display);
	}

	@Override
	public void destroy()
	{
		super.destroy();
		System.out.println("destroy " + contextType);
	}

	@Override
	public void simpleUpdate(float tpf)
	{
		super.simpleUpdate(tpf);

		if (contextType == Type.Display)
		{
			if ((time += tpf) > 3)
			{
				System.out.println("stop " + contextType);
				// stop();
				throw new RuntimeException();
			}
		}
		else
		{
			if ((time += tpf) > 6)
			{
				System.out.println("stop " + contextType);
				// stop();
				throw new RuntimeException();
			}
		}
	}

	@Override
	public void simpleInitApp()
	{
		stateManager.attach(new AbstractAppState()
		{
			@Override
			public void cleanup()
			{
				super.cleanup();
				System.out.println("cleanup " + contextType);
			}
		});

		if (contextType == Type.Display)
		{
			new AppTest(Type.Headless);
		}
	}
}

Output:

stop Display
cleanup Display
destroy Display
stop Headless

Output should be:

stop Display
cleanup Display
destroy Display
stop Headless
cleanup Headless
destroy Headless

best regards
Alrik

2 Likes

I think this might be intended behaviour.
An uncaught exception is something that does not normally occur (AKA isn’t supposed to) and thus the program is simply killed.
Also, why do you need the uncaught exception?

1 Like

Well, it sounds like headless is behaving differently than a regular app.

Still, personally I think using a full up headless JME app for a server is a bad idea. I know others like it but I think it’s like taking a giant 18 wheel tractor-trailer rig to the store for groceries.

Edit: it also makes it waaay too easy to accidentally mix view stuff with game object stuff… since AppStates are inherently “view” stuff.

I use the scenegraph on server side to perform checks (such as raycasts) against cheating or projectile collision checks. And I don’t think that the behavior of headless mode is the intended behavior. Because if an exception occurs in the main loop, I have no control to make necessary shutdown operations.

And why should the headless mode behaves differently than a regular app? Even more: if I should not use the headless mode for a server for what else should I use it?

It shouldn’t. It’s definitely a bug.

It’s like three lines of code to give you what JME gives you in a headless app: calling your code periodically, basically. If you want something more advanced then there are many utility libraries that can provide something like a game loop. (this is mine: https://github.com/Simsilica/SiO2/blob/master/src/main/java/com/simsilica/sim/GameSystemManager.java)

You are already deeply embedded in treating the scene graph like game objects so maybe it makes sense in your case. In general, 90% of what JME does is useless in headless mode… so I (and others) choose not to drag all of that along for the ride.