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
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