Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Waiting for all Runnables submitted to SWT UI thread with Display::asyncExec() to finish

Is there a way to wait for all Runnables submitted to the SWT UI Thread via asyncExec(...) to finish?

Background:

I have a long-running operation, which among other things is triggering events that in turn submit Runnables to the SWT UI thread via the asyncExec(...) instance method of Display.

The progress of the long-running operation is shown in a ProgressMonitorDialog, and I would like to close the dialog only after the UI thread has finished executing the Runnables.

Changing the calls from asyncExec(...) to syncExec(...) is not an option, as the latter is not desired when the events are triggered from other contexts.

like image 908
Juho Ojala Avatar asked Dec 28 '22 10:12

Juho Ojala


2 Answers

org.eclipse.swt.widgets.Display.readAndDispatch() will process an event from the event queue and return false if there are no more events to process. But you probably don't want to use this as it processes an event.

asyncExec(*) is a FIFO queue (although OS graphics events supersede the asyncExecs), so you could do most of your long-running op processing and then place a final asyncExec in the queue:

final boolean[] done = new boolean[1];
Runnable r = new Runnable() {
  public void run() {
    done[0] = true;
  }
};
// now wait for the event somehow.  The brute force method:
while (!done[0]) {
  Thread.sleep(200);
}

In theory, all of the other asyncExecs spawned from your long running op will be finished by the time you get to the last one.

EDIT: potential other option

Create your own org.eclipse.core.runtime.jobs.Job and then join() it at the end:

public static class RefCountJob extends Job {
    public RefCountJob() {
        super("REF_COUNT");
    }

    int count = 0;

    public void increment() {
        count++;
    }

    public void decrement() {
        count--;
    }

    @Override
    protected IStatus run(IProgressMonitor monitor) {
        monitor.beginTask("WAITING", IProgressMonitor.UNKNOWN);
        while (count > 0) {
            Thread.sleep(200);
            monitor.worked(1);
        }
        monitor.done();
        return Status.OK_STATUS;
    }
}

To use it, increment() it every time you are going to fire off events, and have them decrement it when they're done (You have to make sure they decrement it no matter what exception is thrown :-)

RefCountJob ref = new RefCountJob();
// ... do stuff, everybody increments and decrements ref
ref.increment();
// ... do more stuff
ref.increment();
// at the end of your long-running job
ref.schedule();
ref.join();
like image 129
Paul Webster Avatar answered Dec 31 '22 13:12

Paul Webster


Thanks, I ended up with the following. I think it is a pretty clean solution. By the way I would upvote your answer if I had enough reputation for that :)

public class SWTThreadingUtils
{

    public static void waitForAsyncExecsToFinish(Display display)
    {
        Object waitObj = new Object();

        display.asyncExec(new DummyRunnable(waitObj));
        synchronized (waitObj) 
        {
            try {
                waitObj.wait();

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }


    private static class DummyRunnable implements Runnable
    {
        private Object waitObj;

        public DummyRunnable(Object waitObj)
        {
            this.waitObj = waitObj;
        }

        @Override
        public void run()
        {
            synchronized (waitObj)
            {
                waitObj.notify();
            }
        }    
    }

}
like image 39
Juho Ojala Avatar answered Dec 31 '22 13:12

Juho Ojala