0

I have a long operation that cannot be moved from the SWT UI thread. I know, it's gross, but that's how it is. I would like to display a progress bar of some sort for said long operation. I have tinkered around with org.eclipse.jface.dialogs.ProgressMonitorDialog. I'm not giving it an IRunnableWithProgress because, like I said, the long op is in the UI thread. And thus, we have something like...

Shell shell = new Shell();
ProgressMonitorDialog progressDialog = new ProgressMonitorDialog(shell);
progressDialog.open();
IProgressMonitor progressMonitor = progressDialog.getProgressMonitor();
int workLoad = 10;
progressMonitor.beginTask(message, workLoad);
for(int i = 0; i < workLoad; ++i) {
    progressMonitor.worked(1);
    progressMonitor.subTask("Subtask " + (i+1) + " of "+ workLoad + "...");
    if(progressMonitor.isCanceled()) {
        break;
    }
    // Do some work.
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
progressMonitor.done();
progressDialog.close();

This works, but obviously does not update smoothly and at times is laggy to moving and clicking cancel. I thought maybe I could manually update the UI in this instance by creating a thread containing the requisite...

while(!shell.isDisposed()) {
    if(!display.readAndDispatch()) {
        display.sleep();
    }
}

...in the run method, but much to my chagrin I bumped into...

Exception in thread "Thread-16" org.eclipse.swt.SWTException: Invalid thread access

Turns out you can't do that in just any old thread, those calls have to originate in the UI thread. Back to square one.

Does anyone know if I can trick the system? Like make my little thread appear to be the UI thread? Or just any other suggestions would be cool.

Thanks, jb

brock
  • 439
  • 1
  • 5
  • 17
  • 2
    Why do you think your task cannot be run by a non-ui-thread? Usually there is no need to 'trick the system'; long runnng tasks with progress monitoring are nothing special. – isnot2bad Aug 07 '14 at 22:11
  • Long running background threads can still update the UI by calling `Display.asyncExec` – greg-449 Aug 08 '14 at 07:19
  • @isnot2bad - Because it can't. Just roll with it. The question is simple. I need a way to keep the UI responsive while running a long operation in the UI thread. – brock Aug 08 '14 at 13:54
  • @greg-449 - I think you misunderstood me. Yes, long running "background" threads can update the UI via async/syncExec. This is well known. However, my "long operation" is in the UI thread and cannot be moved. Hence, my question. – brock Aug 08 '14 at 13:57
  • 1
    Why is you long operation in the UI thread? What is stopping you moving it to a background thread and updating the UI as and when required? You are not going to get a responsive UI running this in the UI thread. – greg-449 Aug 08 '14 at 14:49
  • I assume your question should rather be: _How can I move my long running operation to a background thread to keep my UI responsive._ – isnot2bad Aug 08 '14 at 15:31
  • @isnot2bad Why can't you just accept the question at face value? Make it a thought experiment if you have to. Forget about the complete and utter wrongness of this approach for just one minute and answer the question I asked. Please. Aside from periodically calling Display.readAndDispatch() while doing the long op, is there any other way to keep the UI responsive? Can it be done. Yes or no. – brock Aug 08 '14 at 21:22
  • Of course we could simply accept the question as it is, but that would not be very professional. Why trying to find a solution for a problem that shouldn't exist? Either your question is theoretical, then it's not worth the trouble. Or it is practical, then we should also find a practical solution, which - in that case - means finding a way to run your op in the background. Everything else is crap. And no, there is no other way than calling 'readAndDispatch' periodically. – isnot2bad Aug 08 '14 at 21:33
  • The progress dialog is launched from, and the long op is running in, complex c++ _legacy_ code that cannot be re-worked to move it into a background java or c++ thread. After launching the progress dialog, control must return to c++ to execute the long op and update the progress dialog as it goes. Thus, finding a way to keep the event loop going (as I can in c++ because there is no such restriction on which thread is updating GUI objects) was the simplest way forward. I understand why SWT doesn't allow us to do these silly things, it's safer, but in very special circumstances, annoying. – brock Aug 11 '14 at 15:24

1 Answers1

2

The only way to have a responsive UI is to call Display.readAndDispatch() in the UI thread, period. Normally, RCP takes care of it for you, or the main method of your application, but if you are running something on the UI thread, they can't, so you have to.

Note that there can only be one UI thread, so if you could "make my little thread appear to be the UI thread" your original long-running task would not be running in the UI thread after all.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • Just to clarify, "my little thread" would not contain the long op and only contain the readAndDispatch loop and exit when the long op (still in the UI thread) was finished. I realize how I'm trying to do things is ass-backwards, but that is a limitation I have to deal with. Thanks for the straight answer. – brock Aug 11 '14 at 15:11
  • To clarify: to call `readAndDispatch` it would have to become the UI thread. And you can't have two UI threads at once, so the original UI thread would have to become a background thread. So far as I know, this is actually impossible (except by disposing existing `Display` and creating another one, which wouldn't work for this case), but this is what would have to happen. – Alexey Romanov Aug 11 '14 at 18:56