aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2012-11-23 08:29:52 +0100
committerSven Gothel <[email protected]>2012-11-23 08:29:52 +0100
commita349db5086a7be7dd80fc2ad29a8a4b55f343e01 (patch)
tree281dd3d64d5e9d6c51e162dc8d56e38602e7abf9 /src
parent68eba2d4f7f4ea88b2b8392b35db8581538c600f (diff)
Bug628: Adding unit test reproducing the deadlock realiable withing first rounds of multi-threaded display.
If a NEWT Window locking operation is performed from within NEWT's EDT (native event dispatch) and a mutable SWT operation performed via NEWT's display invoke - a deadlock occurs.
Diffstat (limited to 'src')
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/swt/TestNewtCanvasSWTBug628ResizeDeadlock.java327
-rw-r--r--src/test/com/jogamp/opengl/test/junit/jogl/swt/TestNewtCanvasSWTGLn.java11
-rw-r--r--src/test/com/jogamp/opengl/test/junit/util/AWTRobotUtil.java41
3 files changed, 370 insertions, 9 deletions
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
new file mode 100644
index 000000000..3b5c4267e
--- /dev/null
+++ b/src/test/com/jogamp/opengl/test/junit/jogl/swt/TestNewtCanvasSWTBug628ResizeDeadlock.java
@@ -0,0 +1,327 @@
+/**
+ * Copyright 2012 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ */
+
+package com.jogamp.opengl.test.junit.jogl.swt;
+
+import java.awt.AWTException;
+import java.awt.Robot;
+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.Test;
+
+import javax.media.opengl.GL ;
+import javax.media.opengl.GL2 ;
+import javax.media.opengl.GLAutoDrawable ;
+import javax.media.opengl.GLCapabilities ;
+import javax.media.opengl.GLEventListener ;
+import javax.media.opengl.GLProfile;
+
+import junit.framework.Assert;
+
+import com.jogamp.newt.event.KeyAdapter;
+import com.jogamp.newt.event.KeyEvent;
+import com.jogamp.newt.opengl.GLWindow ;
+import com.jogamp.newt.swt.NewtCanvasSWT ;
+import com.jogamp.opengl.test.junit.util.AWTRobotUtil;
+import com.jogamp.opengl.test.junit.util.MiscUtils;
+import com.jogamp.opengl.test.junit.util.UITestCase;
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+public class TestNewtCanvasSWTBug628ResizeDeadlock extends UITestCase {
+
+ static int duration = 1000;
+
+ static class BigFlashingX implements GLEventListener
+ {
+ float r = 0f, g = 0f, b = 0f;
+
+ public void init( GLAutoDrawable drawable )
+ {
+ GL2 gl = drawable.getGL().getGL2() ;
+
+ gl.glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ) ;
+
+ gl.glEnable( GL.GL_LINE_SMOOTH ) ;
+ gl.glEnable( GL.GL_BLEND ) ;
+ gl.glBlendFunc( GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA ) ;
+ }
+
+ public void reshape( GLAutoDrawable drawable, int x, int y, int width, int height )
+ {
+ // System.err.println( ">>>>>>>> reshape " + x + ", " + y + ", " + width + ", " +height ) ;
+ GL2 gl = drawable.getGL().getGL2() ;
+
+ gl.glViewport( 0, 0, width, height ) ;
+
+ gl.glMatrixMode( GL2.GL_PROJECTION ) ;
+ gl.glLoadIdentity() ;
+ gl.glOrtho( -1.0, 1.0, -1.0, 1.0, -1.0, 1.0 ) ;
+
+ gl.glMatrixMode( GL2.GL_MODELVIEW ) ;
+ gl.glLoadIdentity() ;
+ }
+
+ public void display( GLAutoDrawable drawable )
+ {
+ // System.err.println( ">>>> display" ) ;
+ GL2 gl = drawable.getGL().getGL2() ;
+
+ // Sven: I could have been seeing things, but it seemed that if this
+ // glClear is in here twice it seems aggravates the problem. Not
+ // sure why other than it just takes longer, but this is pretty
+ // fast operation.
+ gl.glClear( GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT ) ;
+ gl.glClear( GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT ) ;
+
+ gl.glColor4f( r, g, b, 1.0f ) ;
+
+ gl.glBegin( GL.GL_LINES ) ;
+ {
+ gl.glVertex2f( -1.0f, 1.0f ) ;
+ gl.glVertex2f( 1.0f, -1.0f ) ;
+
+ gl.glVertex2f( -1.0f, -1.0f ) ;
+ gl.glVertex2f( 1.0f, 1.0f ) ;
+ }
+ gl.glEnd() ;
+
+ if(r<1f) {
+ r+=0.1f;
+ } else if(g<1f) {
+ g+=0.1f;
+ } else if(b<1f) {
+ b+=0.1f;
+ } else {
+ r = 0f;
+ g = 0f;
+ b = 0f;
+ }
+ }
+
+ public void dispose( GLAutoDrawable drawable )
+ {
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ static class ResizeThread extends Thread {
+ boolean shallStop = false;
+ private Shell _shell ;
+ private int _n ;
+
+ public ResizeThread( Shell shell )
+ {
+ super();
+ _shell = shell ;
+ }
+
+ public void run()
+ {
+ // The problem was originally observed by grabbing the lower right
+ // corner of the window and moving the mouse around rapidly e.g. in
+ // a circle. Eventually the UI will hang with something similar to
+ // the backtrace noted in the bug report.
+ //
+ // This loop simulates rapid resizing by the user by toggling
+ // the shell back-and-forth between two sizes.
+
+ while( !shallStop )
+ {
+ 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 ;
+ }
+ } ) ;
+
+ Thread.sleep( 50L ) ;
+ } catch( InterruptedException e ) {
+ break ;
+ }
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ static class KeyfireThread extends Thread
+ {
+ boolean shallStop = false;
+ Robot _robot;
+ int _n = 0;
+
+ public KeyfireThread(Robot robot)
+ {
+ _robot = robot;
+ }
+
+ public void run()
+ {
+ while( !shallStop )
+ {
+ try {
+ AWTRobotUtil.keyPress(_n, _robot, true, KeyEvent.VK_0, 10);
+ AWTRobotUtil.keyPress(_n, _robot, false, KeyEvent.VK_0, 0);
+ Thread.sleep( 40L ) ;
+ } catch( InterruptedException e ) {
+ break ;
+ }
+ }
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+
+ @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 GLWindow glWindow;
+ {
+ final GLProfile gl2Profile = GLProfile.get( GLProfile.GL2 ) ;
+ GLCapabilities caps = new GLCapabilities( gl2Profile ) ;
+ glWindow = GLWindow.create( caps ) ;
+ glWindow.addGLEventListener( new BigFlashingX() ) ;
+ glWindow.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyTyped(com.jogamp.newt.event.KeyEvent e) {
+ System.err.print(".");
+ glWindow.display();
+ }
+ });
+ NewtCanvasSWT canvas = NewtCanvasSWT.create( composite, SWT.NO_BACKGROUND, glWindow ) ;
+ canvas.setLayoutData( new GridData( SWT.FILL, SWT.FILL, true, true, columnCount, 1 ) ) ;
+ }
+
+ shell.setText( "NewtCanvasSWT Resize Bug Demo" ) ;
+ shell.setSize( 400, 450 ) ;
+ shell.open() ;
+
+ AWTRobotUtil.requestFocus(robot, glWindow, false);
+ AWTRobotUtil.setMouseToClientLocation(robot, glWindow, 50, 50);
+
+ final ResizeThread resizer;
+ {
+ resizer = new ResizeThread( shell ) ;
+ resizer.start() ;
+ }
+
+ final KeyfireThread keyfire;
+ {
+ keyfire = new KeyfireThread( robot ) ;
+ keyfire.start() ;
+ }
+
+ {
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(duration);
+ } catch (InterruptedException e) {}
+ resizer.shallStop = true;
+ keyfire.shallStop = true;
+ try
+ {
+ resizer.join();
+ } catch( InterruptedException e ) { }
+ try
+ {
+ keyfire.join();
+ } catch( InterruptedException e ) { }
+ display.syncExec( new Runnable() {
+ @Override
+ public void run() {
+ shell.dispose();
+ } } );
+ } } ).start();
+ }
+
+ try {
+ while( !shell.isDisposed() ) {
+ if( !display.readAndDispatch() ) {
+ display.sleep();
+ }
+ }
+ } catch (Exception e0) {
+ e0.printStackTrace();
+ Assert.assertTrue("Deadlock @ dispatch: "+e0, false);
+ }
+
+ display.dispose() ;
+ }
+
+ public static void main( String[] args ) {
+ for(int i=0; i<args.length; i++) {
+ if(args[i].equals("-time")) {
+ duration = MiscUtils.atoi(args[++i], duration);
+ }
+ }
+ System.out.println("durationPerTest: "+duration);
+ org.junit.runner.JUnitCore.main(TestNewtCanvasSWTBug628ResizeDeadlock.class.getName());
+ }
+
+}
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 fe40cec28..61caa5ef9 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
@@ -52,6 +52,7 @@ import com.jogamp.newt.opengl.GLWindow;
import com.jogamp.newt.swt.NewtCanvasSWT;
import com.jogamp.opengl.test.junit.jogl.demos.es2.GearsES2;
import com.jogamp.opengl.test.junit.jogl.demos.es2.MultisampleDemoES2;
+import com.jogamp.opengl.test.junit.util.MiscUtils;
import com.jogamp.opengl.test.junit.util.UITestCase;
import com.jogamp.opengl.util.Animator;
import com.jogamp.opengl.util.GLReadBufferUtil;
@@ -213,18 +214,10 @@ public class TestNewtCanvasSWTGLn extends UITestCase {
runTestAGL( caps, new MultisampleDemoES2(true), false /* postAttach */, false /* animator */);
}
- static int atoi(String a) {
- int i=0;
- try {
- i = Integer.parseInt(a);
- } catch (Exception ex) { ex.printStackTrace(); }
- return i;
- }
-
public static void main(String args[]) {
for(int i=0; i<args.length; i++) {
if(args[i].equals("-time")) {
- duration = atoi(args[++i]);
+ duration = MiscUtils.atoi(args[++i], duration);
}
}
System.out.println("durationPerTest: "+duration);
diff --git a/src/test/com/jogamp/opengl/test/junit/util/AWTRobotUtil.java b/src/test/com/jogamp/opengl/test/junit/util/AWTRobotUtil.java
index e64b3208e..b5c96d10f 100644
--- a/src/test/com/jogamp/opengl/test/junit/util/AWTRobotUtil.java
+++ b/src/test/com/jogamp/opengl/test/junit/util/AWTRobotUtil.java
@@ -109,6 +109,33 @@ public class AWTRobotUtil {
return new java.awt.Point(x0, y0);
}
+ public static java.awt.Point getClientLocation(Object obj, int x, int y)
+ throws InterruptedException, InvocationTargetException {
+ Component comp = null;
+ com.jogamp.newt.Window win = null;
+
+ if(obj instanceof com.jogamp.newt.Window) {
+ win = (com.jogamp.newt.Window) obj;
+ } else if(obj instanceof Component) {
+ comp = (Component) obj;
+ } else {
+ throw new RuntimeException("Neither AWT nor NEWT: "+obj);
+ }
+
+ int x0, y0;
+ if(null!=comp) {
+ java.awt.Point p0 = comp.getLocationOnScreen();
+ x0 = (int) p0.getX() + x;
+ y0 = (int) p0.getY() + y;
+ } else {
+ javax.media.nativewindow.util.Point p0 = win.getLocationOnScreen(null);
+ x0 = p0.getX() + x;
+ y0 = p0.getY() + y;
+ }
+
+ return new java.awt.Point(x0, y0);
+ }
+
/**
* toFront, call setVisible(true) and toFront(),
* after positioning the mouse in the middle of the window via robot.
@@ -177,6 +204,20 @@ public class AWTRobotUtil {
robot.delay(ROBOT_DELAY);
}
+ public static void setMouseToClientLocation(Robot robot, Object obj, int x, int y)
+ throws AWTException, InterruptedException, InvocationTargetException {
+
+ if(null == robot) {
+ robot = new Robot();
+ robot.setAutoWaitForIdle(true);
+ }
+
+ java.awt.Point p0 = getClientLocation(obj, x, y);
+
+ robot.mouseMove( (int) p0.getX(), (int) p0.getY() );
+ robot.delay(ROBOT_DELAY);
+ }
+
public static int getClickTimeout(Object obj) {
if(obj instanceof com.jogamp.newt.Window) {
return com.jogamp.newt.event.MouseEvent.getClickTimeout();