Skip to content

Instantly share code, notes, and snippets.

@rherrmann
Last active January 11, 2017 09:37
Show Gist options
  • Save rherrmann/7324823630a089217f46 to your computer and use it in GitHub Desktop.
Save rherrmann/7324823630a089217f46 to your computer and use it in GitHub Desktop.
/***************************************************************************************************
* 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();
}
}
}
}
/***************************************************************************************************
* 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