ProgressMonitorDialog
Open
Short operations that pop up a ProgressMonitorDialog
do
not keep the dialog open long enough for it to be useful. Further, the
rapid open/close of the dialog is distracting for the user.
We want to show progress in a less invasive way for short running
operations, while keeping the operation as is for long running
operations. Possible solutions are to have
ProgressMonitorDialog
use a delay before it opens, or
to use ApplicationWindow
to run the
operation and report the progress on the status line.
fork
Parameter
Currently, a parameter is passed to
ProgressMonitorDialog.run
to indicate whether or not to
fork a background thread on which to run the
operation. As a rule of thumb run
should be called with
fork = true
if it is being called from the UI thread, and
fork = false
otherwise. However, this is not always
true. Editor saves are saved on the UI thread with fork = false
due to a hack put in late in the 1.0 release cycle. Editors keep the
UI responsive by spinning the SWT event loop manually.
In many solutions, calling run
with fork =
false
on the UI thread will cause the
ProgressMonitorDialog
to *never* open, since the UI is
unresponsive during that time. This behaviour is acceptable, provided
that there is a workaround for the editor save case, since we want to
discourage blocking the UI thread whenever possible.
Eclipse is not currently written to allow progress to be reported to the UI asynchronously. It is easy to cause Eclipse to deadlock if the user is allowed to work while there is an operation running in the background, due to the way that the workspace lock is allocated/freed.
The simplest deadlock to explain occurs when, while in the process of
saving a file, a file is deleted. The save is called with fork =
false
, but is a special case since it spins its own event
loop. Save acquires the workspace lock when it starts, and doesn't
release it until it is done. The event loop is run while the UI thread
holds the workspace lock. During this time, the delete gets
processed. Delete is run with fork = true
so it starts a
new ModalContextThread
to run the operation, and then
spins the event loop. This time, the event loop is spinning tightly,
and *not* working on the save operation. The
ModalContextThread
that is created almost immediately
requests the workspace lock, which is held by the UI thread stuck in
the event loop. The delete cannot start until the save is done, but
the save cannot finish until the UI thread breaks out of the event
loop.
It is not possible to cancel the delete operation since the
ModalContextThread
is blocked and cannot check
isCancelled
until it is unblocked. Eclipse is deadlocked,
but the event loop is dunning, so the UI appears responsive, with a
modal dialog preventing the user from working.
Two possible solutions were considered. The call to
ProgressMonitorDialog.open
could be put on a timer and run
after a specified delay. Otherwise, we could avoid spinning the event
loop until the delay is over.
To prevent the user from interacting with the workbench before the
window opens, the workbench needs to be disabled manually. The
modality of the ProgressMonitorDialog
is only in effect
while the dialog is open, so it must be simulated. Opening the dialog
off-screen and then moving it is not supported on all platforms.
Given the current API, it is not possible to disable the entire
workbench from within ProgessMonitorDialog
. At that
level, there is no notion of the shortcut bar, nor is there a
reference to the coolbar. It is possible to work around this problem
by adding API to enable/disable the entire window.
Currently, enabling and disabling the various toolbars is done with a
call to setEnabled
. However on Windows, for a
control with keyboard focus, setEnabled(false)
followed
by setEnabled(true)
will lose the keyboard focus. It does
not look like SWT supports querying the focus status of a widget to
programmatically restore state.
Some operations pop up a dialog to prompt the user for input after the
operation has been started. When the
ProgressMonitorDialog
is opening on a timer, it can open
in front of the dialog that is asking for input. Windows gives focus
to the ProgressMonitorDialog
in this case, which is
modal, so the user cannot interact with the other window. It is not
always possible to cancel the operation at this point, since most
implementations do not check isCancelled
while waiting
for user input to a modal dialog.
The UI is unresponsive when the event loop is not spinning. This can cause problems if left too long, so the delay here is bounded above in practice.
ProgressMonitorDialog.open
must be called from the UI
thread, so it must occur during a
asyncExec
/syncExec
, which are only
processed after all queued events have been dispatched. It is important
to process the open
before any other dialog gets opened,
to avoid the problems encountered in the first solution.
Unfortunately, any accelerators that are pressed while the event loop
is not running are queued until it starts up again. Accelerators are
processed before syncExec
and asyncExec
so it is possible to start another operation while still
running the first. The second operation is not queued, and will run in
the first operation's event loop. The second operation can pop up a
modal dialog, making the ProgressMonitorDialog
inaccessible, or could even deadlock Eclipse.
To avoid this case, it would be necessary to disable all accelerators while the event loop is stopped. Since mouse clicks are queued as well, it is necessary to disable the toolbar, menubar, and shortcut bar as well. In other words, it is still necessary to disable the entire workbench, even though the event loop is not running.
ApplicationWindow.run
It is not possible to start running using
ApplicationWindow.run
and pop up a dialog after a
specified delay for longer running operations. This is the case when
the save of a small file invokes a complete rebuild for a project. In
this case, all progress is reported to the same progress monitor,
which would end up in the status bar only.
This solution also has to disable the workbench manually. It shares
the setEnabled
and keyboard focus problems mentioned above.