diff options
author | Sven Gothel <[email protected]> | 2012-11-27 01:55:10 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2012-11-27 01:55:10 +0100 |
commit | 17dd761d7c2b224f0505a399bf4ecb18634e9250 (patch) | |
tree | d7cf306da5cf1f27e0f43a3f4de868ad9d7afc65 /src | |
parent | 8cf694c1424277e6358039a964ecd75c54cf9af9 (diff) |
SWTEDTUtil/AWTEDTUtil: Fix deadlock situations ; Cleanup TestNewtCanvasSWTBug628ResizeDeadlock
- Fix deadlock situation in waitUntilStopped/Idle(), skip if on AWT/SWT EDT
- Use RunnableTask for sync task invocation, don't block AWT/SWT EDT.
- Cleanup TestNewtCanvasSWTBug628ResizeDeadlock (works on OSX as well)
Diffstat (limited to 'src')
5 files changed, 240 insertions, 118 deletions
diff --git a/src/newt/classes/com/jogamp/newt/swt/SWTEDTUtil.java b/src/newt/classes/com/jogamp/newt/swt/SWTEDTUtil.java index d08fefa29..42e1c9be5 100644 --- a/src/newt/classes/com/jogamp/newt/swt/SWTEDTUtil.java +++ b/src/newt/classes/com/jogamp/newt/swt/SWTEDTUtil.java @@ -29,8 +29,11 @@ package com.jogamp.newt.swt; import java.awt.EventQueue; +import javax.media.nativewindow.NativeWindowException; + import jogamp.newt.Debug; +import com.jogamp.common.util.RunnableTask; import com.jogamp.newt.util.EDTUtil; /** @@ -136,38 +139,58 @@ public class SWTEDTUtil implements EDTUtil { if(task == null) { throw new RuntimeException("Null Runnable"); } - synchronized(edtLock) { // lock the EDT status - if( nedt.shouldStop ) { - // drop task .. - if(DEBUG) { - System.err.println("Warning: EDT about (1) to stop, won't enqueue new task: "+nedt); - Thread.dumpStack(); + Throwable throwable = null; + RunnableTask rTask = null; + Object rTaskLock = new Object(); + synchronized(rTaskLock) { // lock the optional task execution + synchronized(edtLock) { // lock the EDT status + if( nedt.shouldStop ) { + // drop task .. + if(DEBUG) { + System.err.println("Warning: EDT about (1) to stop, won't enqueue new task: "+nedt); + Thread.dumpStack(); + } + return; } - return; - } - // System.err.println(Thread.currentThread()+" XXX stop: "+stop+", tasks: "+edt.tasks.size()+", task: "+task); - // Thread.dumpStack(); - if(stop) { - nedt.shouldStop = true; - if(DEBUG) { - System.err.println(Thread.currentThread()+": EDT signal STOP (on edt: "+isCurrentThreadEDT()+") - "+nedt); - // Thread.dumpStack(); + // System.err.println(Thread.currentThread()+" XXX stop: "+stop+", tasks: "+edt.tasks.size()+", task: "+task); + // Thread.dumpStack(); + if(stop) { + nedt.shouldStop = true; + if(DEBUG) { + System.err.println(Thread.currentThread()+": EDT signal STOP (on edt: "+isCurrentThreadEDT()+") - "+nedt); + // Thread.dumpStack(); + } + } + if( isCurrentThreadEDT() ) { + task.run(); + wait = false; // running in same thread (EDT) -> no wait + } else if( swtDisplay.isDisposed() ) { + wait = false; // drop task, SWT disposed + } else { + // start if should not stop && not started yet + if( !stop && !nedt.isRunning() ) { + startImpl(); + } + rTask = new RunnableTask(task, + wait ? rTaskLock : null, + true /* always catch and report Exceptions, don't disturb EDT */); + swtDisplay.asyncExec(rTask); } } - if( isCurrentThreadEDT() ) { - task.run(); - wait = false; // running in same thread (EDT) -> no wait - } else if( swtDisplay.isDisposed() ) { - wait = false; // drop task, SWT disposed - } else { - // start if should not stop && not started yet - if( !stop && !nedt.isRunning() ) { - startImpl(); + if( wait ) { + try { + rTaskLock.wait(); // free lock, allow execution of rTask + } catch (InterruptedException ie) { + throwable = ie; } - if(wait) { - swtDisplay.syncExec(task); - } else { - swtDisplay.asyncExec(task); + if(null==throwable) { + throwable = rTask.getThrowable(); + } + if(null!=throwable) { + if(throwable instanceof NativeWindowException) { + throw (NativeWindowException)throwable; + } + throw new RuntimeException(throwable); } } } @@ -175,11 +198,12 @@ public class SWTEDTUtil implements EDTUtil { @Override final public void waitUntilIdle() { - final NewtEventDispatchThread _edt; + final NewtEventDispatchThread _nedt; synchronized(edtLock) { - _edt = nedt; + _nedt = nedt; } - if(!_edt.isRunning() || EventQueue.isDispatchThread() || _edt == Thread.currentThread()) { + final Thread ct = Thread.currentThread(); + if(!_nedt.isRunning() || _nedt == ct || swtDisplay.getThread() == ct) { return; } try { @@ -192,7 +216,8 @@ public class SWTEDTUtil implements EDTUtil { @Override final public void waitUntilStopped() { synchronized(edtLock) { - if(nedt.isRunning() && nedt != Thread.currentThread() ) { + final Thread ct = Thread.currentThread(); + if(nedt.isRunning() && nedt != ct && swtDisplay.getThread() != ct) { while(nedt.isRunning()) { try { edtLock.wait(); diff --git a/src/newt/classes/jogamp/newt/DisplayImpl.java b/src/newt/classes/jogamp/newt/DisplayImpl.java index fbccc5767..317535805 100644 --- a/src/newt/classes/jogamp/newt/DisplayImpl.java +++ b/src/newt/classes/jogamp/newt/DisplayImpl.java @@ -366,12 +366,10 @@ public abstract class DisplayImpl extends Display { private ArrayList<NEWTEventTask> events = new ArrayList<NEWTEventTask>(); private volatile boolean haveEvents = false; - class DispatchMessagesRunnable implements Runnable { + final protected Runnable dispatchMessagesRunnable = new Runnable() { public void run() { DisplayImpl.this.dispatchMessages(); - } - } - protected DispatchMessagesRunnable dispatchMessagesRunnable = new DispatchMessagesRunnable(); + } }; final void dispatchMessage(final NEWTEventTask eventTask) { final NEWTEvent event = eventTask.get(); diff --git a/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java b/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java index 01b5ad8a4..2175f2190 100644 --- a/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java +++ b/src/newt/classes/jogamp/newt/driver/awt/AWTEDTUtil.java @@ -30,6 +30,9 @@ package jogamp.newt.driver.awt; import java.awt.EventQueue; +import javax.media.nativewindow.NativeWindowException; + +import com.jogamp.common.util.RunnableTask; import com.jogamp.newt.util.EDTUtil; import jogamp.common.awt.AWTEDTExecutor; @@ -124,33 +127,57 @@ public class AWTEDTUtil implements EDTUtil { if(task == null) { throw new RuntimeException("Null Runnable"); } - synchronized(edtLock) { // lock the EDT status - if( nedt.shouldStop ) { - // drop task .. - if(DEBUG) { - System.err.println("Warning: EDT about (1) to stop, won't enqueue new task: "+nedt); - Thread.dumpStack(); + Throwable throwable = null; + RunnableTask rTask = null; + Object rTaskLock = new Object(); + synchronized(rTaskLock) { // lock the optional task execution + synchronized(edtLock) { // lock the EDT status + if( nedt.shouldStop ) { + // drop task .. + if(DEBUG) { + System.err.println("Warning: EDT about (1) to stop, won't enqueue new task: "+nedt); + Thread.dumpStack(); + } + return; } - return; - } - // System.err.println(Thread.currentThread()+" XXX stop: "+stop+", tasks: "+edt.tasks.size()+", task: "+task); - // Thread.dumpStack(); - if(stop) { - nedt.shouldStop = true; - if(DEBUG) { - System.err.println(Thread.currentThread()+": EDT signal STOP (on edt: "+isCurrentThreadEDT()+") - "+nedt); - // Thread.dumpStack(); + // System.err.println(Thread.currentThread()+" XXX stop: "+stop+", tasks: "+edt.tasks.size()+", task: "+task); + // Thread.dumpStack(); + if(stop) { + nedt.shouldStop = true; + if(DEBUG) { + System.err.println(Thread.currentThread()+": EDT signal STOP (on edt: "+isCurrentThreadEDT()+") - "+nedt); + // Thread.dumpStack(); + } + } + if( isCurrentThreadEDT() ) { + task.run(); + wait = false; // running in same thread (EDT) -> no wait + } else { + // start if should not stop && not started yet + if( !stop && !nedt.isRunning() ) { + startImpl(); + } + rTask = new RunnableTask(task, + wait ? rTaskLock : null, + true /* always catch and report Exceptions, don't disturb EDT */); + AWTEDTExecutor.singleton.invoke(false, rTask); } } - if( isCurrentThreadEDT() ) { - task.run(); - wait = false; // running in same thread (EDT) -> no wait - } else { - // start if should not stop && not started yet - if( !stop && !nedt.isRunning() ) { - startImpl(); + if( wait ) { + try { + rTaskLock.wait(); // free lock, allow execution of rTask + } catch (InterruptedException ie) { + throwable = ie; + } + if(null==throwable) { + throwable = rTask.getThrowable(); + } + if(null!=throwable) { + if(throwable instanceof NativeWindowException) { + throw (NativeWindowException)throwable; + } + throw new RuntimeException(throwable); } - AWTEDTExecutor.singleton.invoke(wait, task); } } } @@ -161,7 +188,7 @@ public class AWTEDTUtil implements EDTUtil { synchronized(edtLock) { _edt = nedt; } - if(!_edt.isRunning() || EventQueue.isDispatchThread() || _edt == Thread.currentThread()) { + if(!_edt.isRunning() || _edt == Thread.currentThread() || EventQueue.isDispatchThread()) { return; } try { @@ -174,7 +201,7 @@ public class AWTEDTUtil implements EDTUtil { @Override final public void waitUntilStopped() { synchronized(edtLock) { - if(nedt.isRunning() && nedt != Thread.currentThread() ) { + if(nedt.isRunning() && nedt != Thread.currentThread() && !EventQueue.isDispatchThread()) { while(nedt.isRunning()) { try { edtLock.wait(); diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestNewtCanvasSWTBug628ResizeDeadlock.java b/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestNewtCanvasSWTBug628ResizeDeadlock.java index 3b5c4267e..9c0762c12 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestNewtCanvasSWTBug628ResizeDeadlock.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestNewtCanvasSWTBug628ResizeDeadlock.java @@ -35,12 +35,11 @@ import java.lang.reflect.InvocationTargetException; import org.eclipse.swt.SWT ; import org.eclipse.swt.layout.FillLayout ; -import org.eclipse.swt.layout.GridData ; -import org.eclipse.swt.layout.GridLayout ; import org.eclipse.swt.widgets.Composite ; import org.eclipse.swt.widgets.Display ; import org.eclipse.swt.widgets.Shell ; +import org.junit.Assume; import org.junit.Test; import javax.media.opengl.GL ; @@ -52,6 +51,7 @@ import javax.media.opengl.GLProfile; import junit.framework.Assert; +import com.jogamp.nativewindow.swt.SWTAccessor; import com.jogamp.newt.event.KeyAdapter; import com.jogamp.newt.event.KeyEvent; import com.jogamp.newt.opengl.GLWindow ; @@ -142,7 +142,7 @@ public class TestNewtCanvasSWTBug628ResizeDeadlock extends UITestCase { //////////////////////////////////////////////////////////////////////////////// static class ResizeThread extends Thread { - boolean shallStop = false; + volatile boolean shallStop = false; private Shell _shell ; private int _n ; @@ -152,6 +152,26 @@ public class TestNewtCanvasSWTBug628ResizeDeadlock extends UITestCase { _shell = shell ; } + final Runnable resizeAction = new Runnable() { + public void run() + { + System.err.println("[R-i shallStop "+shallStop+", disposed "+_shell.isDisposed()+"]"); + if( shallStop || _shell.isDisposed() ) { + return; + } + try { + if( _n % 2 == 0 ) { + _shell.setSize( 200, 200 ) ; + } else { + _shell.setSize( 400, 450 ) ; + } + } catch (Exception e0) { + e0.printStackTrace(); + Assert.assertTrue("Deadlock @ setSize: "+e0, false); + } + ++_n ; + } }; + public void run() { // The problem was originally observed by grabbing the lower right @@ -162,33 +182,24 @@ public class TestNewtCanvasSWTBug628ResizeDeadlock extends UITestCase { // This loop simulates rapid resizing by the user by toggling // the shell back-and-forth between two sizes. - while( !shallStop ) + System.err.println("[R-0 shallStop "+shallStop+", disposed "+_shell.isDisposed()+"]"); + + final Display display = _shell.getDisplay(); + + while( !shallStop && !_shell.isDisposed() ) { try { - _shell.getDisplay().asyncExec( new Runnable() - { - public void run() - { - try { - if( _n % 2 == 0 ) { - _shell.setSize( 200, 200 ) ; - } else { - _shell.setSize( 400, 450 ) ; - } - } catch (Exception e0) { - e0.printStackTrace(); - Assert.assertTrue("Deadlock @ setSize: "+e0, false); - } - ++_n ; - } - } ) ; + System.err.println("[R-n shallStop "+shallStop+", disposed "+_shell.isDisposed()+"]"); + display.asyncExec( resizeAction ); + display.wake(); - Thread.sleep( 50L ) ; + Thread.sleep( 50L ) ; } catch( InterruptedException e ) { break ; } } + System.err.println("*R-Exit* shallStop "+shallStop+", disposed "+_shell.isDisposed()); } } @@ -196,50 +207,101 @@ public class TestNewtCanvasSWTBug628ResizeDeadlock extends UITestCase { static class KeyfireThread extends Thread { - boolean shallStop = false; + volatile boolean shallStop = false; + Display _display; Robot _robot; int _n = 0; - public KeyfireThread(Robot robot) + public KeyfireThread(Robot robot, Display display) { _robot = robot; + _display = display; } public void run() { + System.err.println("[K-0]"); + while( !shallStop ) { try { + System.err.println("[K-"+_n+"]"); AWTRobotUtil.keyPress(_n, _robot, true, KeyEvent.VK_0, 10); AWTRobotUtil.keyPress(_n, _robot, false, KeyEvent.VK_0, 0); Thread.sleep( 40L ) ; + _n++; + if(!_display.isDisposed()) { + _display.wake(); + } } catch( InterruptedException e ) { break ; } } + System.err.println("*K-Exit*"); } } //////////////////////////////////////////////////////////////////////////////// + private volatile boolean shallStop = false; + + static class SWT_DSC { + Display display; + Shell shell; + Composite composite; + + public void init() { + SWTAccessor.invoke(true, new Runnable() { + public void run() { + display = new Display(); + Assert.assertNotNull( display ); + }}); + + display.syncExec(new Runnable() { + public void run() { + shell = new Shell( display ); + Assert.assertNotNull( shell ); + shell.setLayout( new FillLayout() ); + composite = new Composite( shell, SWT.NONE ); + composite.setLayout( new FillLayout() ); + Assert.assertNotNull( composite ); + }}); + } + + public void dispose() { + Assert.assertNotNull( display ); + Assert.assertNotNull( shell ); + Assert.assertNotNull( composite ); + try { + display.syncExec(new Runnable() { + public void run() { + composite.dispose(); + shell.dispose(); + }}); + SWTAccessor.invoke(true, new Runnable() { + public void run() { + display.dispose(); + }}); + } + catch( Throwable throwable ) { + throwable.printStackTrace(); + Assume.assumeNoException( throwable ); + } + display = null; + shell = null; + composite = null; + } + } + @Test public void test() throws InterruptedException, AWTException, InvocationTargetException { - final int columnCount = 1; - final Display display = new Display() ; - - final Shell shell = new Shell( display ) ; - shell.setLayout( new FillLayout() ) ; - final Robot robot = new Robot(); - Composite composite = new Composite( shell, SWT.NONE ) ; - { - GridLayout layout = new GridLayout() ; - layout.numColumns = columnCount ; - composite.setLayout( layout ) ; - } - + final SWT_DSC dsc = new SWT_DSC(); + dsc.init(); + final GLWindow glWindow; + final NewtCanvasSWT canvas; { final GLProfile gl2Profile = GLProfile.get( GLProfile.GL2 ) ; GLCapabilities caps = new GLCapabilities( gl2Profile ) ; @@ -252,26 +314,30 @@ public class TestNewtCanvasSWTBug628ResizeDeadlock extends UITestCase { glWindow.display(); } }); - NewtCanvasSWT canvas = NewtCanvasSWT.create( composite, SWT.NO_BACKGROUND, glWindow ) ; - canvas.setLayoutData( new GridData( SWT.FILL, SWT.FILL, true, true, columnCount, 1 ) ) ; + canvas = NewtCanvasSWT.create( dsc.composite, 0, glWindow ) ; } - shell.setText( "NewtCanvasSWT Resize Bug Demo" ) ; - shell.setSize( 400, 450 ) ; - shell.open() ; - + dsc.display.syncExec( new Runnable() { + public void run() { + dsc.shell.setText( "NewtCanvasSWT Resize Bug Demo" ) ; + dsc.shell.setSize( 400, 450 ) ; + dsc.shell.open() ; + } } ); + AWTRobotUtil.requestFocus(robot, glWindow, false); AWTRobotUtil.setMouseToClientLocation(robot, glWindow, 50, 50); + shallStop = false; + final ResizeThread resizer; { - resizer = new ResizeThread( shell ) ; + resizer = new ResizeThread( dsc.shell ) ; resizer.start() ; } final KeyfireThread keyfire; { - keyfire = new KeyfireThread( robot ) ; + keyfire = new KeyfireThread( robot, dsc.display ) ; keyfire.start() ; } @@ -292,18 +358,15 @@ public class TestNewtCanvasSWTBug628ResizeDeadlock extends UITestCase { { keyfire.join(); } catch( InterruptedException e ) { } - display.syncExec( new Runnable() { - @Override - public void run() { - shell.dispose(); - } } ); + shallStop = true; + dsc.display.wake(); } } ).start(); } - + try { - while( !shell.isDisposed() ) { - if( !display.readAndDispatch() ) { - display.sleep(); + while( !shallStop && !dsc.display.isDisposed() ) { + if( !dsc.display.readAndDispatch() ) { + dsc.display.sleep(); } } } catch (Exception e0) { @@ -311,7 +374,10 @@ public class TestNewtCanvasSWTBug628ResizeDeadlock extends UITestCase { Assert.assertTrue("Deadlock @ dispatch: "+e0, false); } - display.dispose() ; + System.err.println("NewtCanvasAWT Dispose"); + canvas.dispose(); + + dsc.dispose(); } public static void main( String[] args ) { diff --git a/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestNewtCanvasSWTGLn.java b/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestNewtCanvasSWTGLn.java index 61caa5ef9..396d219b4 100644 --- a/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestNewtCanvasSWTGLn.java +++ b/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestNewtCanvasSWTGLn.java @@ -93,6 +93,9 @@ public class TestNewtCanvasSWTGLn extends UITestCase { public void run() { display = new Display(); Assert.assertNotNull( display ); + }}); + display.syncExec(new Runnable() { + public void run() { shell = new Shell( display ); Assert.assertNotNull( shell ); shell.setLayout( new FillLayout() ); @@ -108,10 +111,13 @@ public class TestNewtCanvasSWTGLn extends UITestCase { Assert.assertNotNull( shell ); Assert.assertNotNull( composite ); try { - SWTAccessor.invoke(true, new Runnable() { + display.syncExec(new Runnable() { public void run() { composite.dispose(); shell.dispose(); + }}); + SWTAccessor.invoke(true, new Runnable() { + public void run() { display.dispose(); }}); } @@ -149,7 +155,7 @@ public class TestNewtCanvasSWTGLn extends UITestCase { final NewtCanvasSWT canvas1 = NewtCanvasSWT.create( composite, 0, postAttach ? null : glWindow1 ); Assert.assertNotNull( canvas1 ); - SWTAccessor.invoke(true, new Runnable() { + display.syncExec( new Runnable() { public void run() { shell.setText( getSimpleTestName(".") ); shell.setSize( 640, 480 ); |