Threading issues
When working with a widget toolkit, it is important to understand the
underlying thread model that is used for reading and dispatching platform
GUI events. The implementation of the UI thread affects the rules that
applications must follow when using Java threads in their code.
Native event dispatching
Underneath any GUI application, regardless of its language or UI toolkit,
the OS platform detects GUI events and places them in application event queues.
Although the mechanics are slightly different on different OS platforms, the
basics are similar. As the user clicks the mouse, types characters, or surfaces
windows, the OS generates application GUI events, such as mouse clicks,
keystrokes, or window paint events. It determines which window and application
should receive each event and places it in the application's event queue.
The underlying structure for any windowed GUI application is an event loop.
Applications initialize and then start a loop which simply reads the GUI events
from the queue and reacts accordingly. Any work that is done while handling one
of these events must happen quickly in order to keep the GUI system responsive
to the user.
Long operations triggered by UI events should be performed in a separate
thread in order to allow the event loop thread to return quickly and fetch the
next event from the application's queue. However, access to the widgets and
platform API from other threads must be controlled with explicit locking and
serialization. An application that fails to follow the rules can cause an OS
call to fail, or worse, lock up the entire GUI system.
SWT UI thread
SWT follows the threading model supported directly by the platforms. The
application program runs the event loop in its main thread and dispatches
events directly from this thread. The UI thread is the thread in which the
Display
was created. All other widgets must be created in the UI thread.
Since all event code is triggered from the application's UI thread,
application code that handles events can freely access the widgets and
make graphics calls without any special techniques. However, the application
is responsible for forking computational threads when performing long
operations in response to an event.
Note: SWT will trigger an
SWTException
for any calls made from a non-UI thread that must be made from the UI
thread.
The main thread, including the event loop, for an SWT application has
the following structure:
public static void main (String [] args) {
Display display = new Display ();
Shell shell = new Shell (display);
shell.open ();
// start the event loop. We stop when the user has done
// something to dispose our window.
while (!shell.isDisposed ()) {
if (!display.readAndDispatch ())
display.sleep ();
}
display.dispose ();
}
Once the widgets are created and the shell is opened, the application
reads and dispatches events from the OS queue until the shell window is
disposed. If there are no events available for us in the queue, we tell the
display to sleep to give other applications a chance to run.
SWT provides special access methods for calling widget and graphics code
from a background thread.
Applications that wish to call UI code from a non-UI thread must provide a
Runnable that calls the UI code. The methods
syncExec(Runnable) and asyncExec(Runnable)
in the
Display
class are used to execute these runnables in the UI thread during the event
loop.
-
syncExec(Runnable) should be used when the application
code in the non-UI thread depends on the return value from the UI code
or otherwise needs to ensure that the runnable is run to completion
before returning to the thread. SWT will block the calling thread until
the runnable has been run from the application's UI thread. For example,
a background thread that is computing something based on a window's
current size would want to synchronously run the code to get the window's
size and then continue with its computations.
-
asyncExec(Runnable) should be used when the application
needs to perform some UI operations, but is not dependent upon the
operations being completed before continuing. For example, a background
thread that updates a progress indicator or redraws a window could
request the update asynchronously and continue with its processing. In
this case, there is no guaranteed relationship between the timing of the
background thread and the execution of the runnable.
The following code snippet demonstrates the pattern for using these methods:
// do time-intensive computations
...
// now update the UI. We don't depend on the result,
// so use async.
display.asyncExec (new Runnable () {
public void run () {
if (!myWindow.isDisposed())
myWindow.redraw ();
}
});
// now do more computations
...
It is good practice to check if your widget is disposed from within the runnable
when using asyncExec. Since other things can happen in the UI thread between
the call to asyncExec
and the execution of your runnable,
you can never be sure what state your widgets are in by the time your runnable
executes.
The threading rules are very clear when you are implementing an SWT
application from the ground up since you control the creation of the event
loop and the decision to fork computational threads in your application.
Things get a bit more complicated when you are contributing plug-in code
to the workbench. The following rules can be considered "rules of engagement"
when using platform UI classes, although from release to release there may be
exceptions to these rules:
- In general, any workbench UI extensions you add to the platform will be
executing in the workbench's UI thread, unless they are specifically related
to threads or background jobs (such as background job progress indication).
- If you receive an event from the workbench, it is not guaranteed that it is
executing in the UI thread of the workbench. Consult the javadoc for the particular
class that defines the listener or event. If there is no specific documentation
discussing threading, and the class is clearly a UI-related class, you may expect that
the event arrives in the UI thread of the workbench.
- Likewise, a platform UI library should not be considered thread-safe unless it is
specifically documented as such. Note that most platform UI classes dispatch listeners
from the calling thread that triggered the event. Workbench and JFace API calls do not
check that the caller is executing in the UI thread.This means that your plug-in may
introduce a problem if you call a method that triggers an event from a non-UI thread.
SWT triggers an
SWTException
for all API calls made from a non-UI thread. In general, avoid calling platform UI code from
another thread unless the javadoc specifically allows it.
- If your plug-in forks a computational thread or uses a workbench Job, it must use the
Display
asyncExec(Runnable) or syncExec(Runnable)
methods when calling any API for the workbench, JFace, or SWT, unless the API specifically
allows call-in from a background thread.
- If your plug-in uses the JFace
IRunnableContext
interface to invoke a progress monitor and run an operation, it supplies
an argument to specify whether a computational thread is forked for
running the operation.