Added uncaught exception handling for background threads

This commit is contained in:
Vhati 2014-09-27 03:02:48 -04:00
parent 2370df4567
commit b99b81ff89
6 changed files with 66 additions and 10 deletions

View file

@ -16,6 +16,7 @@ Changelog
- Added an error popup when the jar is double-clicked - Added an error popup when the jar is double-clicked
- Added *.xml.rawappend suffix to preserve whitespace in 'misc.xml' - Added *.xml.rawappend suffix to preserve whitespace in 'misc.xml'
- Minor optimizations to reduce memory usage - Minor optimizations to reduce memory usage
- Added uncaught exception handling for background threads
1.4: 1.4:
- Cleaned up some dodgy code when initially prompting for FTL's location - Cleaned up some dodgy code when initially prompting for FTL's location

View file

@ -45,9 +45,19 @@ public class SlipstreamCLI {
private static File backupDir = new File( "./backup/" ); private static File backupDir = new File( "./backup/" );
private static File modsDir = new File( "./mods/" ); private static File modsDir = new File( "./mods/" );
private static Thread.UncaughtExceptionHandler exceptionHandler = null;
public static void main( String[] args ) { public static void main( String[] args ) {
exceptionHandler = new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException( Thread t, Throwable e ) {
log.error( "Uncaught exception in thread: "+ t.toString(), e );
System.exit( 1 );
}
};
BasicParser parser = new BasicParser(); BasicParser parser = new BasicParser();
Options options = new Options(); Options options = new Options();
@ -266,6 +276,7 @@ public class SlipstreamCLI {
SilentPatchObserver patchObserver = new SilentPatchObserver(); SilentPatchObserver patchObserver = new SilentPatchObserver();
ModPatchThread patchThread = new ModPatchThread( modFiles, dataDat, resDat, globalPanic, patchObserver ); ModPatchThread patchThread = new ModPatchThread( modFiles, dataDat, resDat, globalPanic, patchObserver );
patchThread.setDefaultUncaughtExceptionHandler( exceptionHandler );
deleteHook.addWatchedThread( patchThread ); deleteHook.addWatchedThread( patchThread );
patchThread.start(); patchThread.start();
@ -325,7 +336,7 @@ public class SlipstreamCLI {
config.load( new InputStreamReader( in, "UTF-8" ) ); config.load( new InputStreamReader( in, "UTF-8" ) );
} }
} }
catch (IOException e) { catch ( IOException e ) {
log.error( "Error loading config.", e ); log.error( "Error loading config.", e );
} }
finally { finally {
@ -420,7 +431,7 @@ public class SlipstreamCLI {
private static String getVersionMessage() { private static String getVersionMessage() {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
buf.append( String.format( "%s %s\n", FTLModManager.APP_NAME, FTLModManager.APP_VERSION ) ); buf.append( String.format( "%s %s\n", FTLModManager.APP_NAME, FTLModManager.APP_VERSION ) );
buf.append( "Copyright (C) 2013 David Millis\n" ); buf.append( "Copyright (C) 2014 David Millis\n" );
buf.append( "\n" ); buf.append( "\n" );
buf.append( "This program is free software; you can redistribute it and/or modify\n" ); buf.append( "This program is free software; you can redistribute it and/or modify\n" );
buf.append( "it under the terms of the GNU General Public License as published by\n" ); buf.append( "it under the terms of the GNU General Public License as published by\n" );

View file

@ -23,6 +23,8 @@ public class DatExtractDialog extends ProgressDialog {
private File extractDir; private File extractDir;
private File[] datFiles; private File[] datFiles;
private DatExtractThread workerThread = null;
public DatExtractDialog( Frame owner, File extractDir, File[] datFiles ) { public DatExtractDialog( Frame owner, File extractDir, File[] datFiles ) {
super( owner, false ); super( owner, false );
@ -34,6 +36,18 @@ public class DatExtractDialog extends ProgressDialog {
this.setSize( 400, 160 ); this.setSize( 400, 160 );
this.setMinimumSize( this.getPreferredSize() ); this.setMinimumSize( this.getPreferredSize() );
this.setLocationRelativeTo( owner ); this.setLocationRelativeTo( owner );
workerThread = new DatExtractThread( extractDir, datFiles );
}
/**
* Returns the worker thread that does the extracting.
*
* This method is provided so other classes can customize the thread
* before calling extract().
*/
public Thread getWorkerThread() {
return workerThread;
} }
/** /**
@ -43,8 +57,7 @@ public class DatExtractDialog extends ProgressDialog {
public void extract() { public void extract() {
if ( started ) return; if ( started ) return;
DatExtractThread t = new DatExtractThread( extractDir, datFiles ); workerThread.start();
t.start();
started = true; started = true;
} }
@ -53,10 +66,11 @@ public class DatExtractDialog extends ProgressDialog {
super.setTaskOutcome( outcome, e ); super.setTaskOutcome( outcome, e );
if ( !this.isShowing() ) return; if ( !this.isShowing() ) return;
if ( succeeded ) if ( succeeded ) {
setStatusText( "All resources extracted successfully." ); setStatusText( "All resources extracted successfully." );
else } else {
setStatusText( String.format( "Error extracting dats: %s", e ) ); setStatusText( String.format( "Error extracting dats: %s", e ) );
}
} }

View file

@ -85,7 +85,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
public class ManagerFrame extends JFrame implements ActionListener, ModsScanObserver, Nerfable, Statusbar { public class ManagerFrame extends JFrame implements ActionListener, ModsScanObserver, Nerfable, Statusbar, Thread.UncaughtExceptionHandler {
private static final Logger log = LogManager.getLogger(ManagerFrame.class); private static final Logger log = LogManager.getLogger(ManagerFrame.class);
@ -406,6 +406,7 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
); );
initThread.setDaemon( true ); initThread.setDaemon( true );
initThread.setPriority( Thread.MIN_PRIORITY ); initThread.setPriority( Thread.MIN_PRIORITY );
initThread.setDefaultUncaughtExceptionHandler( this );
initThread.start(); initThread.start();
} }
@ -508,6 +509,7 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
ModsScanThread scanThread = new ModsScanThread( modFiles, localModDB, this ); ModsScanThread scanThread = new ModsScanThread( modFiles, localModDB, this );
scanThread.setDaemon( true ); scanThread.setDaemon( true );
scanThread.setPriority( Thread.MIN_PRIORITY ); scanThread.setPriority( Thread.MIN_PRIORITY );
scanThread.setDefaultUncaughtExceptionHandler( this );
scanThread.start(); scanThread.start();
} }
@ -685,6 +687,7 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
log.info( "Patching..." ); log.info( "Patching..." );
log.info( "" ); log.info( "" );
ModPatchThread patchThread = new ModPatchThread( modFiles, dataDat, resDat, false, patchDlg ); ModPatchThread patchThread = new ModPatchThread( modFiles, dataDat, resDat, false, patchDlg );
patchThread.setDefaultUncaughtExceptionHandler( this );
patchThread.start(); patchThread.start();
patchDlg.setVisible( true ); patchDlg.setVisible( true );
@ -763,6 +766,7 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
File[] datFiles = new File[] {dataDatFile, resDatFile}; File[] datFiles = new File[] {dataDatFile, resDatFile};
DatExtractDialog extractDlg = new DatExtractDialog( this, extractDir, datFiles ); DatExtractDialog extractDlg = new DatExtractDialog( this, extractDir, datFiles );
extractDlg.getWorkerThread().setDefaultUncaughtExceptionHandler( this );
extractDlg.extract(); extractDlg.extract();
extractDlg.setVisible( true ); extractDlg.setVisible( true );
} }
@ -919,6 +923,31 @@ public class ManagerFrame extends JFrame implements ActionListener, ModsScanObse
} }
@Override
public void uncaughtException( Thread t, Throwable e ) {
log.error( "Uncaught exception in thread: "+ t.toString(), e );
final String threadString = t.toString();
final String errString = e.toString();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
String message = "An unexpected error has occurred.\n";
message += "\n";
message += "Thread: "+ threadString +"\n";
message += "Error: "+ errString +"\n";
message += "\n";
message += "See the log for details.\n";
message += "\n";
message += "If this interrupted patching, FTL's resources were probably corrupted.\n";
message += "Restart Slipstream and patch without mods to restore vanilla backups.";
JOptionPane.showMessageDialog( ManagerFrame.this, message, "Error", JOptionPane.ERROR_MESSAGE );
}
});
}
private class SpawnGameTask implements Runnable { private class SpawnGameTask implements Runnable {
private final File exeFile; private final File exeFile;

View file

@ -68,9 +68,10 @@ public class ModPatchDialog extends ProgressDialog implements ModPatchObserver {
super.setTaskOutcome( outcome, e ); super.setTaskOutcome( outcome, e );
if ( !this.isShowing() ) return; if ( !this.isShowing() ) return;
if ( succeeded == true ) if ( succeeded == true ) {
setStatusText( "Patching completed." ); setStatusText( "Patching completed." );
else } else {
setStatusText( String.format( "Patching failed: %s", e ) ); setStatusText( String.format( "Patching failed: %s", e ) );
}
} }
} }

View file

@ -168,7 +168,7 @@ public class ProgressDialog extends JDialog implements ActionListener {
/** /**
* Triggers a responce to the immediate task ending. (Thread-safe) * Triggers a response to the immediate task ending. (Thread-safe)
* *
* If anything went wrong, e may be non-null. * If anything went wrong, e may be non-null.
*/ */