You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
org.eclipse.ui.progress.IProgressService.run(boolean fork, boolean cancelable, IRunnableWithProgress runnable) does not consistently honor the "Always run in background" preference (Window/Preferences > General). Depending on the fork/cancelable combination passed in, the preference is silently ignored and a modal progress dialog pops up anyway — even though the user explicitly asked for operations to run in the background.
There are two other confusing (but arguably "working as documented") behaviors around thread-affinity and the fork=false contract that make the overall API very easy to misuse. A demo view (ProgressServiceView, see below) makes all of this directly observable/reproducible.
A screen recording demonstrating the bug and the fix using this view is attached to this issue.
Screen recording in 2025-06
Screen recording in 2026-09 I-BUILD I20260722-2300 (likely M2)
Environment
Eclipse Platform UI (org.eclipse.ui.workbench), class org.eclipse.ui.internal.progress.ProgressManager
Reproduced via a new example view: org.eclipse.ui.examples.jobs.views.ProgressServiceView (See linked PR: Fix/progress service run #4203)
Root cause
ProgressManager.run(...) branches on !fork || !cancelable to decide how to run the operation:
When fork == false or cancelable == false, it goes through ProgressMonitorJobsDialog directly, which correctly calls shouldRunInBackground() and suppresses the modal dialog when the preference is enabled.
When fork == true && cancelable == true (the most common call shape — e.g. simply run(true, true, runnable)), it delegates to busyCursorWhile(runnable), which never checks the preference and always schedules the dialog-opening job.
Net effect: with "Always run in background" enabled, run(true, true, runnable) still shows a modal progress dialog. The preference is only honored for some fork/cancelable combinations, not all of them, even though nothing in the IProgressService or IRunnableContext javadoc suggests the preference should only apply to some combinations.
Reproduction steps (via ProgressServiceView)
NOTE: ProgressServiceView is added in the linked PR: #4203
Open ProgressServiceView (Window > Show View > Other... > Progress Examples > Progress Service).
Check "Always run in background" (or set it under Preferences > General — the view's checkbox stays in sync either way).
Check both fork and cancelable, set a duration long enough to exceed the short-operation threshold (e.g. 3000 ms), click Run.
Observed (buggy) behavior: a modal progress dialog appears anyway, blocking interaction with the rest of the workbench, even though the user asked for background execution.
Expected behavior: no dialog should appear; the UI shouldn't freeze, showing the operation ran in the background exactly like the other fork/cancelable combinations already do correctly.
Other confusing (non-bug, but easy-to-misuse) behaviors surfaced by the same view
These are not being changed — they match documented contracts — but they explain why this API is easy to get wrong in practice, and are demonstrated by dedicated buttons in ProgressServiceView:
Calling IProgressService.run(...) from a non-UI thread throws a raw SWT "invalid thread access" exception. This matches IProgressService's class javadoc ("All methods on the progress service must be called from the UI thread"), but the exception itself is generic/SWT-level and gives no hint about why. The "Run from non-UI thread" button in the view reproduces this.
run(false, cancelable, runnable) shows no progress feedback at all and freezes the UI for the duration of the runnable. This matches the documented contract of the underlying ProgressMonitorDialog#run(boolean, boolean, IRunnableWithProgress) implementation (which ProgressManager delegates to): its javadoc explicitly states that when fork == false, "the runnable will run in the UI thread and it is the runnable's responsibility to call Display.readAndDispatch() to ensure UI responsiveness." The class-level javadoc reinforces this: "not forking the process will result in it running in the UI which may starve the UI... It is recommended that fork is set to true in most cases." Note that IRunnableContext#run's own javadoc (the interface) only says the current thread is used when fork is false — it does not itself mention event pumping; the explicit responsibility is documented on the concrete ProgressMonitorDialog implementation. The view has two side-by-side buttons: "fork=false (naive — will freeze)", which freezes the UI heartbeat, and "fork=false (pumping events — correct)", which keeps the UI responsive by pumping events inside the runnable — showing that the "freeze" is a caller responsibility, not something the framework can fix on the callee side.
Community
I understand reporting an issue to this OSS project does not mandate anyone to fix it. Other contributors may consider the issue, or not, at their own convenience. The most efficient way to get it fixed is that I fix it myself and contribute it back as a good quality patch to the project.
I noticed an inconsistent behavior while working on #4086.
Summary
org.eclipse.ui.progress.IProgressService.run(boolean fork, boolean cancelable, IRunnableWithProgress runnable)does not consistently honor the "Always run in background" preference (Window/Preferences > General). Depending on thefork/cancelablecombination passed in, the preference is silently ignored and a modal progress dialog pops up anyway — even though the user explicitly asked for operations to run in the background.There are two other confusing (but arguably "working as documented") behaviors around thread-affinity and the
fork=falsecontract that make the overall API very easy to misuse. A demo view (ProgressServiceView, see below) makes all of this directly observable/reproducible.A screen recording demonstrating the bug and the fix using this view is attached to this issue.
Screen recording in 2025-06
Screen recording in 2026-09 I-BUILD I20260722-2300 (likely M2)
Environment
org.eclipse.ui.workbench), classorg.eclipse.ui.internal.progress.ProgressManagerorg.eclipse.ui.examples.jobs.views.ProgressServiceView(See linked PR: Fix/progress service run #4203)Root cause
ProgressManager.run(...)branches on!fork || !cancelableto decide how to run the operation:fork == falseorcancelable == false, it goes throughProgressMonitorJobsDialogdirectly, which correctly callsshouldRunInBackground()and suppresses the modal dialog when the preference is enabled.fork == true && cancelable == true(the most common call shape — e.g. simplyrun(true, true, runnable)), it delegates tobusyCursorWhile(runnable), which never checks the preference and always schedules the dialog-opening job.Net effect: with "Always run in background" enabled,
run(true, true, runnable)still shows a modal progress dialog. The preference is only honored for some fork/cancelable combinations, not all of them, even though nothing in theIProgressServiceorIRunnableContextjavadoc suggests the preference should only apply to some combinations.Reproduction steps (via
ProgressServiceView)NOTE:
ProgressServiceViewis added in the linked PR: #4203ProgressServiceView(Window > Show View > Other... > Progress Examples > Progress Service).Preferences > General— the view's checkbox stays in sync either way).Other confusing (non-bug, but easy-to-misuse) behaviors surfaced by the same view
These are not being changed — they match documented contracts — but they explain why this API is easy to get wrong in practice, and are demonstrated by dedicated buttons in
ProgressServiceView:Calling
IProgressService.run(...)from a non-UI thread throws a raw SWT "invalid thread access" exception. This matchesIProgressService's class javadoc ("All methods on the progress service must be called from the UI thread"), but the exception itself is generic/SWT-level and gives no hint about why. The "Run from non-UI thread" button in the view reproduces this.run(false, cancelable, runnable)shows no progress feedback at all and freezes the UI for the duration of the runnable. This matches the documented contract of the underlyingProgressMonitorDialog#run(boolean, boolean, IRunnableWithProgress)implementation (whichProgressManagerdelegates to): its javadoc explicitly states that whenfork == false, "the runnable will run in the UI thread and it is the runnable's responsibility to callDisplay.readAndDispatch()to ensure UI responsiveness." The class-level javadoc reinforces this: "not forking the process will result in it running in the UI which may starve the UI... It is recommended that fork is set to true in most cases." Note thatIRunnableContext#run's own javadoc (the interface) only says the current thread is used whenforkisfalse— it does not itself mention event pumping; the explicit responsibility is documented on the concreteProgressMonitorDialogimplementation. The view has two side-by-side buttons: "fork=false (naive — will freeze)", which freezes the UI heartbeat, and "fork=false (pumping events — correct)", which keeps the UI responsive by pumping events inside the runnable — showing that the "freeze" is a caller responsibility, not something the framework can fix on the callee side.Community