Last active
January 11, 2017 09:37
-
-
Save rherrmann/7324823630a089217f46 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*************************************************************************************************** | |
* Copyright (c) 2014 Rüdiger Herrmann | |
* All rights reserved. This program and the accompanying materials are made available under the | |
* terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/epl-v10.html | |
* | |
* Contributors: | |
* Rüdiger Herrmann - initial API and implementation | |
**************************************************************************************************/ | |
package com.codeaffine.ui.util; | |
import org.eclipse.swt.SWT; | |
import org.eclipse.swt.SWTException; | |
import org.eclipse.swt.widgets.Display; | |
import org.eclipse.swt.widgets.Widget; | |
public class UIThreadSynchronizer { | |
public void asyncExec( Widget contextWidget, Runnable runnable ) { | |
new UIThreadRunner( contextWidget ).asyncExec( runnable ); | |
} | |
public void syncExec( Widget contextWidget, Runnable runnable ) { | |
new UIThreadRunner( contextWidget ).syncExec( runnable ); | |
} | |
private static class UIThreadRunner { | |
private final Widget contextWidget; | |
UIThreadRunner( Widget contextWidget ) { | |
this.contextWidget = contextWidget; | |
} | |
void asyncExec( Runnable runnable ) { | |
Display display = getDisplay(); | |
if( display != null ) { | |
display.asyncExec( new GuardedRunnable( contextWidget, runnable ) ); | |
} | |
} | |
void syncExec( Runnable runnable ) { | |
Display display = getDisplay(); | |
if( display != null ) { | |
display.syncExec( new GuardedRunnable( contextWidget, runnable ) ); | |
} | |
} | |
private Display getDisplay() { | |
Display result = null; | |
if( !contextWidget.isDisposed() ) { | |
result = safeGetDisplay(); | |
} | |
return result; | |
} | |
private Display safeGetDisplay() { | |
Display result = null; | |
try { | |
result = contextWidget.getDisplay(); | |
} catch( SWTException exception ) { | |
handleSWTException( exception ); | |
} | |
return result; | |
} | |
private static void handleSWTException( SWTException exception ) { | |
if( exception.code != SWT.ERROR_WIDGET_DISPOSED ) { | |
throw exception; | |
} | |
} | |
} | |
private static class GuardedRunnable implements Runnable { | |
private final Widget contextWidget; | |
private final Runnable runnable; | |
GuardedRunnable( Widget contextWidget, Runnable runnable ) { | |
this.contextWidget = contextWidget; | |
this.runnable = runnable; | |
} | |
@Override | |
public void run() { | |
if( !contextWidget.isDisposed() ) { | |
runnable.run(); | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*************************************************************************************************** | |
* Copyright (c) 2014 Rüdiger Herrmann | |
* All rights reserved. This program and the accompanying materials are made available under the | |
* terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at | |
* http://www.eclipse.org/legal/epl-v10.html | |
* | |
* Contributors: | |
* Rüdiger Herrmann - initial API and implementation | |
**************************************************************************************************/ | |
package com.codeaffine.ui.util; | |
import static org.mockito.Mockito.doThrow; | |
import static org.mockito.Mockito.mock; | |
import static org.mockito.Mockito.never; | |
import static org.mockito.Mockito.verify; | |
import org.eclipse.swt.SWT; | |
import org.eclipse.swt.SWTException; | |
import org.eclipse.swt.widgets.Display; | |
import org.eclipse.swt.widgets.Shell; | |
import org.eclipse.swt.widgets.Widget; | |
import org.junit.After; | |
import org.junit.Before; | |
import org.junit.Test; | |
public class UIThreadSynchronizerTest { | |
private Display display; | |
private Widget widget; | |
private Runnable runnable; | |
private UIThreadSynchronizer synchronizer; | |
@Test | |
public void testSyncExecRunsCode() { | |
synchronizer.syncExec( widget, runnable ); | |
verify( runnable ).run(); | |
} | |
@Test | |
public void testSyncExecAfterWidgetIsDisposedDoesNotRunCode() throws InterruptedException { | |
widget.dispose(); | |
runInThread( new Runnable() { | |
@Override | |
public void run() { | |
synchronizer.syncExec( widget, runnable ); | |
} | |
} ); | |
verify( runnable, never() ).run(); | |
} | |
@Test | |
public void testSyncExecWithDisposedDisplayDoesNotRunCode() { | |
display.dispose(); | |
synchronizer.asyncExec( widget, runnable ); | |
verify( runnable, never() ).run(); | |
} | |
@Test | |
public void testAsyncExecRunsCode() { | |
synchronizer.asyncExec( widget, runnable ); | |
flushPendingEvents(); | |
verify( runnable ).run(); | |
} | |
@Test | |
public void testAsyncExecRunsCodeFromBackgroundThread() throws InterruptedException { | |
runInThread( new Runnable() { | |
@Override | |
public void run() { | |
synchronizer.asyncExec( widget, runnable ); | |
} | |
} ); | |
flushPendingEvents(); | |
verify( runnable ).run(); | |
} | |
@Test | |
public void testAsyncExecAfterWidgetIsDisposedDoesNotRunCode() throws InterruptedException { | |
runInThread( new Runnable() { | |
@Override | |
public void run() { | |
synchronizer.asyncExec( widget, runnable ); | |
} | |
} ); | |
widget.dispose(); | |
flushPendingEvents(); | |
verify( runnable, never() ).run(); | |
} | |
@Test | |
public void testAsyncExecWithDisposedDisplayDoesNotRunCode() { | |
display.dispose(); | |
synchronizer.asyncExec( widget, runnable ); | |
flushPendingEvents(); | |
verify( runnable, never() ).run(); | |
} | |
@Test(expected=SWTException.class) | |
public void testAsyncExecPropagatesWidgetDisposedExceptionInRunnable() { | |
doThrow( new SWTException( SWT.ERROR_WIDGET_DISPOSED ) ).when( runnable ).run(); | |
synchronizer.asyncExec( widget, runnable ); | |
flushPendingEvents(); | |
} | |
@Before | |
public void setUp() { | |
display = new Display(); | |
widget = new Shell( display ); | |
runnable = mock( Runnable.class ); | |
synchronizer = new UIThreadSynchronizer(); | |
} | |
@After | |
public void tearDown() { | |
display.dispose(); | |
} | |
private void flushPendingEvents() { | |
while( !display.isDisposed() && display.readAndDispatch() ) {} | |
} | |
private static void runInThread( final Runnable runnable ) throws InterruptedException { | |
ExceptionGuard exceptionGuard = new ExceptionGuard( runnable ); | |
Thread thread = new Thread( exceptionGuard ); | |
thread.setDaemon( true ); | |
thread.start(); | |
thread.join(); | |
exceptionGuard.handleException(); | |
} | |
private static class ExceptionGuard implements Runnable { | |
private final Runnable runnable; | |
private volatile Throwable caughtException; | |
ExceptionGuard( Runnable runnable ) { | |
this.runnable = runnable; | |
} | |
@Override | |
public void run() { | |
try { | |
runnable.run(); | |
} catch( Throwable thr ) { | |
caughtException = thr; | |
} | |
} | |
void handleException() { | |
if( caughtException != null ) { | |
throw new RuntimeException( "Caught exception in thread", caughtException ); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment