Reputation: 173
I have been working with Java threads in order to provide a GUI platform for running processes in a pipeline. I've managed to work through a number of issues with SwingWorker
, but this one is seemingly incomprehensible.
My SwingWorker
looks like:
SwingWorker<Boolean,Object> worker = new SwingWorker<Boolean,Object>() {
@Override
public Boolean doInBackground() {
return launchBlockingPipelineProcess(process, instance, project, logger, state);
}
@Override
protected void done(){
boolean success = false;
try{
success = get();
if (!success){
state.setTaskFailed(true);
}
if (process.getStatus().equals(Status.Interrupted)){
state.setTaskInterrupted(true);
}
}catch (Exception ex){
state.setTaskFailed(true);
}
processCompleted(process, success, state);
}
};
I use this to run a Java process; a simplified version of the launching code is:
try{
Class<?> target_class = Class.forName(main_class);
CommandInstance instance = (CommandInstance)target_class.newInstance();
CommandFunctions.ProcessState state = instance.execute(args);
}catch (InvocationTargetException e){
throw new PipelineException("Java process '" + this.getName() + "." + uid + "' encountered exception: " + e.getCause().getMessage());
}catch (Exception e){
throw new PipelineException("JavaProcess encountered exception invoking target: " + e.getMessage());
}
The process itself has a try-catch block around code which loads some data from a file. However, despite being wrapped in two layers of try-catch blocks (actually three, if you count the done()
method), when the loader throws an Exception it prints a stack trace, the Exception is not caught, and the SwingWorker
thread hangs (the code halts so I can no longer force an interrupt).
This sort of Exception hanging has occurred in other circumstances as well; most puzzlingly, in other -- seemingly identical -- cases the Exception is caught and the thread exits gracefully.
I haven't been able to find much about this online, although I'll continue to search. I am no expert in Swing threading, so I was hoping someone might have an insight about this sort of issue. I'd love it to be a very stupid error on my part :)
EDIT: @Adrian, here is the stack trace. It seems to stall half way... very strange:
java.io.EOFException
at java.io.RandomAccessFile.readFully(RandomAccessFile.java:399)
at mgui.io.standard.nifti.Nifti1Dataset.readVolBlob(Nifti1Dataset.java:2179)
at mgui.io.standard.nifti.Nifti1Dataset.readDoubleVol(Nifti1Dataset.java:1916)
at mgui.io.standard.nifti.NiftiVolumeLoader.setGrid3DBlocking(NiftiVolumeLoader.java:186)
at mgui.io.domestic.shapes.VolumeFileLoader.setGrid3D(VolumeFileLoader.java:237)
at mgui.io.domestic.shapes.VolumeFileLoader.getGrid3D(VolumeFileLoader.java:139)
at mgui.io.domestic.shapes.VolumeFileLoader.getGrid3D(VolumeFileLoader.java:97)
at minc.MincFunctions.create_volume_atlas_masks(MincFunctions.java:5240)
at minc.MincFunctions.run_command(MincFunctions.java:153)
at mgui.command.CommandInstance.execute(CommandInstance.java:87)
at mgui.pipelines.JavaProcess.run(JavaProcess.java:141)
at mgui.pipelines.PipelineFunctions.launchBlockingPipelineProcess(PipelineFunctions.java:238)
at mgui.pipelines.PipelineFunctions.launchPipelineProcess(PipelineFunctions.ja
EDIT2: Debugging in Eclipse, I can halt at a breakpoint on the line where it is thrown (well, one step before); the stack trace at that point is:
Nifti1Dataset.readVolBlob(short) line: 2179
Nifti1Dataset.readDoubleVol(short) line: 1916
NiftiVolumeLoader.setGrid3DBlocking(Grid3D, int, ProgressUpdater) line: 186
NiftiVolumeLoader(VolumeFileLoader).setGrid3D(Grid3D, int, ProgressUpdater) line: 237
NiftiVolumeLoader(VolumeFileLoader).getGrid3D(VolumeInputOptions, int, ProgressUpdater) line: 139
NiftiVolumeLoader(VolumeFileLoader).getGrid3D(int) line: 97
MincFunctions.create_volume_atlas_masks() line: 5278
MincFunctions.run_command(String) line: 153
MincFunctions(CommandInstance).execute(String[]) line: 87
JavaProcess.run(String[], long) line: 141
PipelineFunctions.launchBlockingPipelineProcess(PipelineProcessInstance, String, InterfaceProject, String, PipelineState) line: 238
PipelineFunctions.launchPipelineProcess(PipelineProcessInstance, String, InterfaceProject, String, boolean, PipelineState) line: 78
PipelineFunctions.launchPipelineProcess(PipelineProcessInstance, boolean, PipelineState) line: 52
PipelineProcessInstance.launch(boolean) line: 187
InterfacePipeline.launch(boolean) line: 388
PipelineLauncher.doInBackground() line: 57
PipelineLauncher.doInBackground() line: 1
SwingWorker$1.call() line: 277
FutureTask$Sync.innerRun() line: 303
SwingWorker$2(FutureTask<V>).run() line: 138
PipelineLauncher(SwingWorker<T,V>).run() line: 316
ThreadPoolExecutor$Worker.runTask(Runnable) line: 886
ThreadPoolExecutor$Worker.run() line: 908
Thread.run() line: 662
Upvotes: 3
Views: 1093
Reputation: 173
@Adrian, thanks for the suggestion; I do not think I am making any UI updates directly from the SwingWorker
thread; I do make updates to JTree
nodes to indicate the success or failure of a process, but I use the publish/process mechanism for this, which should ensure that all UI updates are called from the EDT:
These are listener handlers called from the executing pipeline:
@Override
public void pipelineTaskTerminated(DynamicPipelineEvent event, PipelineTask task) {
publish(task);
}
@Override
public void pipelineTaskUpdated(DynamicPipelineEvent event, PipelineTask task) {
// Here we can publish the update to a task status
publish(task);
}
And here is the process method:
@Override
protected void process(List<PipelineTask> tasks) {
// Update task listeners on the Event-Dispatch Thread
for (int i = 0; i < tasks.size(); i++){
PipelineTask task = tasks.get(i);
InterfacePipeline pipeline = task.getPipeline();
if (pipeline != null){
task.fireStatusChanged();
}
}
}
Finally, here is how the JTree
handles the event:
public void taskStatusChanged(PipelineTaskEvent e){
if (model == null) return;
model.nodeStructureChanged(this);
tree.repaint();
}
As a (potentially informative) aside, this call to the JTree
also does not result in an update of the UI, despite its being called from the EDT. The only way to force the tree to update is by clicking on the node itself. Possibly an issue for a new thread, but it may be related.
I'll go through the code and experiment by eliminating ANY potential UI updates from the thread, and see if the problem can be prevented that way... stay tuned :)
EDIT: Even removing these updates altogether I get the same Exception thrown...
Upvotes: 0
Reputation: 5681
I solved a similar table problem by not interfering when the AWT thread was painting the UI.
I used invokeLater()
to synchronize my thread with the UI thread.
See EventQueue for more details.
Upvotes: 0
Reputation: 109813
yes is possible take Exception(s)
from SwingWorker
's methods done()
, but required strictly naming every thread, more in this thread, especially answer by @trashgod, and I haven't found another possibility of how to it another way
Upvotes: 2