- 
                Notifications
    You must be signed in to change notification settings 
- Fork 226
CountDownLatch Synchronization for RCPTestWorkbenchAdvisor #3429
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|  | @@ -13,6 +13,9 @@ | |||||||||||||||||||||||||||||||||||||
| *******************************************************************************/ | ||||||||||||||||||||||||||||||||||||||
| package org.eclipse.ui.tests.harness.util; | ||||||||||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||||||||||
| import java.util.concurrent.CountDownLatch; | ||||||||||||||||||||||||||||||||||||||
| import java.util.concurrent.TimeUnit; | ||||||||||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||||||||||
| import org.eclipse.jface.preference.IPreferenceStore; | ||||||||||||||||||||||||||||||||||||||
| import org.eclipse.swt.SWTException; | ||||||||||||||||||||||||||||||||||||||
| import org.eclipse.swt.widgets.Display; | ||||||||||||||||||||||||||||||||||||||
|  | @@ -44,6 +47,9 @@ public class RCPTestWorkbenchAdvisor extends WorkbenchAdvisor { | |||||||||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||||||||||
| private static boolean started = false; | ||||||||||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||||||||||
| // Use a CountDownLatch to ensure async operations complete before postStartup | ||||||||||||||||||||||||||||||||||||||
| private static final CountDownLatch asyncLatch = new CountDownLatch(4); | ||||||||||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||||||||||
| public static boolean isSTARTED() { | ||||||||||||||||||||||||||||||||||||||
| synchronized (RCPTestWorkbenchAdvisor.class) { | ||||||||||||||||||||||||||||||||||||||
| return started; | ||||||||||||||||||||||||||||||||||||||
|  | @@ -122,12 +128,10 @@ public void eventLoopIdle(final Display display) { | |||||||||||||||||||||||||||||||||||||
| public void preStartup() { | ||||||||||||||||||||||||||||||||||||||
| super.preStartup(); | ||||||||||||||||||||||||||||||||||||||
| final Display display = Display.getCurrent(); | ||||||||||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||||||||||
| if (display != null) { | ||||||||||||||||||||||||||||||||||||||
| display.asyncExec(() -> { | ||||||||||||||||||||||||||||||||||||||
| if (isSTARTED()) | ||||||||||||||||||||||||||||||||||||||
| asyncDuringStartup = Boolean.FALSE; | ||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||
| asyncDuringStartup = Boolean.TRUE; | ||||||||||||||||||||||||||||||||||||||
| asyncDuringStartup = !isSTARTED(); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||||||||||
|  | @@ -160,15 +164,18 @@ public void run() { | |||||||||||||||||||||||||||||||||||||
| display.syncExec(() -> { | ||||||||||||||||||||||||||||||||||||||
| synchronized (RCPTestWorkbenchAdvisor.class) { | ||||||||||||||||||||||||||||||||||||||
| if (callDisplayAccess) | ||||||||||||||||||||||||||||||||||||||
| syncWithDisplayAccess = !isSTARTED() ? Boolean.TRUE : Boolean.FALSE; | ||||||||||||||||||||||||||||||||||||||
| syncWithDisplayAccess = !isSTARTED(); | ||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||
| syncWithoutDisplayAccess = !isSTARTED() ? Boolean.TRUE : Boolean.FALSE; | ||||||||||||||||||||||||||||||||||||||
| syncWithoutDisplayAccess = !isSTARTED(); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| } catch (SWTException e) { | ||||||||||||||||||||||||||||||||||||||
| // this can happen because we shut down the workbench just | ||||||||||||||||||||||||||||||||||||||
| // as soon as we're initialized - ie: when we're trying to | ||||||||||||||||||||||||||||||||||||||
| // run this runnable in the deferred case. | ||||||||||||||||||||||||||||||||||||||
| } finally { | ||||||||||||||||||||||||||||||||||||||
| // Signal that this operation has completed | ||||||||||||||||||||||||||||||||||||||
| asyncLatch.countDown(); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||
|  | @@ -182,14 +189,20 @@ private void setupAsyncDisplayThread(final boolean callDisplayAccess, final Disp | |||||||||||||||||||||||||||||||||||||
| public void run() { | ||||||||||||||||||||||||||||||||||||||
| if (callDisplayAccess) | ||||||||||||||||||||||||||||||||||||||
| DisplayAccess.accessDisplayDuringStartup(); | ||||||||||||||||||||||||||||||||||||||
| display.asyncExec(() -> { | ||||||||||||||||||||||||||||||||||||||
| synchronized (RCPTestWorkbenchAdvisor.class) { | ||||||||||||||||||||||||||||||||||||||
| if (callDisplayAccess) | ||||||||||||||||||||||||||||||||||||||
| asyncWithDisplayAccess = !isSTARTED() ? Boolean.TRUE : Boolean.FALSE; | ||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||
| asyncWithoutDisplayAccess = !isSTARTED() ? Boolean.TRUE : Boolean.FALSE; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||
| display.asyncExec(() -> { | ||||||||||||||||||||||||||||||||||||||
| synchronized (RCPTestWorkbenchAdvisor.class) { | ||||||||||||||||||||||||||||||||||||||
| if (callDisplayAccess) | ||||||||||||||||||||||||||||||||||||||
| asyncWithDisplayAccess = !isSTARTED(); | ||||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||||
| asyncWithoutDisplayAccess = !isSTARTED(); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| } finally { | ||||||||||||||||||||||||||||||||||||||
| // Signal that this operation has been scheduled (not necessarily executed yet) | ||||||||||||||||||||||||||||||||||||||
| // This avoids deadlock since we're not waiting for execution on the UI thread | ||||||||||||||||||||||||||||||||||||||
| asyncLatch.countDown(); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||
| asyncThread.setDaemon(true); | ||||||||||||||||||||||||||||||||||||||
|  | @@ -199,6 +212,38 @@ public void run() { | |||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||
| public void postStartup() { | ||||||||||||||||||||||||||||||||||||||
| super.postStartup(); | ||||||||||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||||||||||
| // Wait for all async/sync operations to be scheduled (not blocking UI thread) | ||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||
| // Wait up to 5 seconds for all operations to be scheduled | ||||||||||||||||||||||||||||||||||||||
| boolean completed = asyncLatch.await(5, TimeUnit.SECONDS); | ||||||||||||||||||||||||||||||||||||||
| if (!completed) { | ||||||||||||||||||||||||||||||||||||||
| throw new AssertionError("Not all async/sync operations were scheduled within timeout"); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } catch (InterruptedException e) { | ||||||||||||||||||||||||||||||||||||||
| Thread.currentThread().interrupt(); | ||||||||||||||||||||||||||||||||||||||
| throw new RuntimeException("Interrupted while waiting for async/sync operations", e); | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||||||||||
| // Pump the event loop to ensure async runnables execute before marking as started | ||||||||||||||||||||||||||||||||||||||
| // This prevents the original race condition where async variables might not be set yet | ||||||||||||||||||||||||||||||||||||||
| Display display = Display.getCurrent(); | ||||||||||||||||||||||||||||||||||||||
| if (display != null) { | ||||||||||||||||||||||||||||||||||||||
| // Process pending async events multiple times to ensure they execute | ||||||||||||||||||||||||||||||||||||||
| for (int i = 0; i < 10; i++) { | ||||||||||||||||||||||||||||||||||||||
| while (display.readAndDispatch()) { | ||||||||||||||||||||||||||||||||||||||
| // Keep processing events | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| // Small yield to allow any final async operations to complete | ||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||
| Thread.sleep(10); | ||||||||||||||||||||||||||||||||||||||
| } catch (InterruptedException e) { | ||||||||||||||||||||||||||||||||||||||
| Thread.currentThread().interrupt(); | ||||||||||||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| 
      Comment on lines
    
      +230
     to 
      +245
    
   There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this quite complex event processing really necessary? All relevant runnables are guaranteed to be scheduled here, so  
        Suggested change
       
 | ||||||||||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||||||||||
| synchronized (RCPTestWorkbenchAdvisor.class) { | ||||||||||||||||||||||||||||||||||||||
| started = true; | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|  | ||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: wouldn't it be cleaner to use something like a
Phaserand make every thread register itself and then deregister/arrive when being executed (like now done withasyncLatch.countDown()), so that the latch's count does not need to magically match the number of thread created?