Handles uncaught errors (OutOfMemoryError, StackOverflowError etc) by freeing memory, showing an error message and force quit.

This commit is contained in:
Stian Grenborgen 2022-09-14 20:14:04 +02:00
parent eabb0ea56b
commit 90e4df323e
4 changed files with 57 additions and 0 deletions

View File

@ -2789,6 +2789,7 @@ model.unit.hardyPioneer.noMoreTools=%location%: Your Hardy Pioneer has used up a
# -10-- Client
# main: main() and support in FreeCol.java
error.seriousError=An error occurred that is forcing FreeCol to exit. Please refer to the log file for more information.
error.couldNotFind=An error occurred because file %name% could not be found
error.couldNotLoad=An error occurred while trying to load the game from file %name%
error.couldNotLoadDifficulty=An error occurred while trying to load difficulty options from file %name%.

View File

@ -71,6 +71,9 @@ public final class FreeColClient {
private static final Logger logger = Logger.getLogger(FreeColClient.class.getName());
@SuppressWarnings("unused")
private byte[] memoryToBeFreedOnOutOfMemory = new byte[10 * 1024 * 1024];
private final ConnectController connectController;
private final PreGameController preGameController;
@ -167,6 +170,7 @@ public final class FreeColClient {
if (FreeCol.getHeadless() && savedGame == null && spec == null) {
FreeCol.fatal(logger, Messages.message("client.headlessRequires"));
}
mapEditor = false;
// Look for base data directory and get base resources loaded.
@ -246,6 +250,8 @@ public final class FreeColClient {
// Initialize Sound (depends on client options)
this.soundController = new SoundController(this, sound);
overrideDefaultUncaughtExceptionHandler();
/*
* Please do NOT move preloading before mods are loaded -- as that
* might cause some images to be loaded from base and other images
@ -1010,4 +1016,31 @@ public final class FreeColClient {
}
FreeCol.quit(0);
}
private void overrideDefaultUncaughtExceptionHandler() {
// This overrides the handler in FreeCol:
Thread.setDefaultUncaughtExceptionHandler((Thread thread, Throwable e) -> {
// Free enough space to ensure we can perform the next operations.
if (e instanceof Error) {
memoryToBeFreedOnOutOfMemory = null;
gui.emergencyPurge();
}
final boolean seriousError = (e instanceof Error);
try {
logger.log(Level.WARNING, "Uncaught exception from thread: " + thread, e);
if (seriousError) {
gui.showErrorPanel(Messages.message("error.seriousError"), () -> {
System.exit(1);
});
}
} catch (Throwable t) {
if (seriousError) {
t.printStackTrace();
System.exit(1);
}
}
});
}
}

View File

@ -2506,4 +2506,14 @@ public class GUI extends FreeColClientHolder {
public boolean canGameChangingModsBeAdded() {
return true;
}
/**
* A method to be called only on serious Errors in order
* to ensure sufficient memory for displaying an error
* message.
*/
public void emergencyPurge() {
}
}

View File

@ -56,6 +56,7 @@ import net.sf.freecol.FreeCol;
import net.sf.freecol.client.ClientOptions;
import net.sf.freecol.client.FreeColClient;
import net.sf.freecol.client.control.MapTransform;
import net.sf.freecol.client.control.SoundController;
import net.sf.freecol.client.gui.animation.Animation;
import net.sf.freecol.client.gui.animation.Animations;
// Special dialogs and panels
@ -1373,6 +1374,18 @@ public class SwingGUI extends GUI {
refresh();
}
/**
* {@inheritDoc}
*/
@Override
public void emergencyPurge() {
imageCache.clear();
final SoundController sc = getFreeColClient().getSoundController();
sc.setDefaultPlaylist(List.of());
sc.playMusic(null);
}
// Highest level panel and dialog handling