I've been using Java for a few years, but my thread knowledge is rubbish. I've Googled pretty heavily and found some good information about general use of ProgressMonitorDialog
but nothing like my exact circumstances.
I'm currently using a ProgressMonitorDialog
as a wrapper around an instance of IRunnableWithProgress
, which in turn is a wrapper around a Thread
. This works fine but now I'm trying to make the cancel button trigger an interrupt on the running thread, which I can handle to gracefully terminate the operation.
One important thing to note is that I have two plugins; "Data" and "UI". The data plugin contains all of the real work, and must be independent from the UI or any Eclipse plugins. The UI plugin should be as thin as possible.
Here's a distilled version of the code I've got so far.
Data:
public class Data {
public static Thread createThread() {
return new Thread() {
@Override
public void run() {
Thing t = new Thing();
t.operationA();
t.operationB();
t.operationC();
}
}
}
}
UI:
public class UI {
public void doOperation() {
try {
new ProgressMonitorDialog(getShell()).run(true, true, new MyOperation());
}
catch (Exception e) {
e.printStatckTrace();
}
}
public class MyOperation implements IRunnableWithProgress {
@Override
public void run(IProgressMonitor monitor) throws InterruptedException, InvocationTargetException {
monitor.beginTask("Task", 2);
try {
Thread myThread = Data.createThread();
myThread.start();
monitor.worked(1);
while (myThread.isAlive() && !monitor.isCanceled()) {}
if (monitor.isCanceled()) {
myThread.interrupt();
}
monitor.worked(1);
}
finally {
monitor.done();
}
}
}
}
So when the cancel button is clicked, myThread.interrupt()
is called. Now the thread needs to respond to the interrupt. Data.createThread()
now looks something like this:
public static Thread createThread() {
return new Thread() {
@Override
public void run() {
Thing t = new Thing();
t.operationA();
if (Thread.currentThread.isInterrupted()) {
// Tidy up
return;
}
t.operationB();
if (Thread.currentThread.isInterrupted()) {
// Tidy up
return;
}
t.operationC();
if (Thread.currentThread.isInterrupted()) {
// Tidy up
return;
}
}
}
}
It might be rather verbose polling the interrupted state like this, but I can't see this causing any problems.
But, what if Thing.operationA()
wasn't atomic, and could be interrupted within that function:
public class Thing {
public void operationA() {
atomic1();
// How would I detect and handle a change to the interrupted state here?
atomic2();
}
public void operationB() {
// One atomic operation
}
public void operationC() {
// One atomic operation
}
}
How would I detect and handle a change to the interrupted state between atomic1()
and atomic2()
? Is it as simple as polling Thread.currentThread.isInterrupted()
again? Or will I need to pass around some volatile
object to track the interrupted state? Should I be throwing InterruptedException
somewhere?
My second question is about tracking and reporting progress. I understand how IProgressMonitor.worked()
should be used. As already seen, my Data thread contains 3 operations. Is it possible to pass that information up to the UI so I can track the progress in the ProgressMonitorDialog
?
Ideally, something like this:
public static Thread createThread() {
return new Thread() {
@Override
public void run(IProgressMonitor monitor) {
Thing t = new Thing();
t.operationA();
monitor.worked(1);
if (Thread.currentThread.isInterrupted()) {
// Tidy up
return;
}
t.operationB();
monitor.worked(1);
if (Thread.currentThread.isInterrupted()) {
// Tidy up
return;
}
t.operationC();
monitor.worked(1);
if (Thread.currentThread.isInterrupted()) {
// Tidy up
return;
}
}
}
}
However as stated, Data cannot depend on Eclipse and therefore passing the IProgressMonitor
doesn't work in this case.
Could I have a variable tracking progress in my thread, and then call something like myThread.getProgress()
asynchronously from the UI thread to update the progress bar with new work? I'm not sure how feasible this is (it popped into my head as I was writing this question) so I'll try that next.
Lots of information and question marks in here, sorry if my style is a bit scattered. I could elaborate more if needs be but this is already a wall of text. Any information, advice or ideas appreciated.
Between atomic1()
and atomic2()
you do need to check for Thread.currentThread.isInterrupted()
to cleanup in case of canceling. No need to throw an exception if you handle what is needed.
As for progress tracking, you can create your own listener object in the Data plugin and allow passing it to the thread. the UI will instantiate it and pass it to the thread. this way the Data can pass progress events to the UI without dependencies.