diff options
14 files changed, 314 insertions, 569 deletions
diff --git a/make/scripts/tests.sh b/make/scripts/tests.sh index 4ac3ecdd5..941d18ecb 100755 --- a/make/scripts/tests.sh +++ b/make/scripts/tests.sh @@ -1,5 +1,7 @@ #! /bin/bash +rm -f java-run.log + spath=`dirname $0` #com.jogamp.test.junit.jogl.acore.TestGLProfile01CORE @@ -25,12 +27,11 @@ spath=`dirname $0` # $spath/java-run-all.sh ../build-x86_64 com.jogamp.test.junit.newt.parenting.TestParenting01cAWT $* # $spath/java-run-all.sh ../build-x86_64 com.jogamp.test.junit.newt.parenting.TestParenting01cSwingAWT $* # $spath/java-run-all.sh ../build-x86_64 com.jogamp.test.junit.newt.parenting.TestParenting02AWT $* +$spath/java-run-all.sh ../build-x86_64 com.jogamp.test.junit.newt.parenting.TestParenting03AWT $* # $spath/java-run-all.sh ../build-x86_64 com.jogamp.test.junit.jogl.awt.TestSwingAWTRobotUsageBeforeJOGLInitBug411 $* # $spath/java-run-all.sh ../build-x86_64 com.jogamp.test.junit.newt.TestFocus01SwingAWTRobot $* -$spath/java-run-all.sh ../build-x86_64 com.jogamp.test.junit.newt.TestFocus02SwingAWTRobot $* - -# $spath/java-run-all.sh ../build-x86_64 com.jogamp.test.junit.nativewindow.TestRecursiveToolkitLockCORE $* +# $spath/java-run-all.sh ../build-x86_64 com.jogamp.test.junit.newt.TestFocus02SwingAWTRobot $* $spath/count-edt-start.sh java-run.log diff --git a/src/jogl/classes/com/jogamp/opengl/impl/GLContextLock.java b/src/jogl/classes/com/jogamp/opengl/impl/GLContextLock.java index 6d0c5d984..565ec967e 100644 --- a/src/jogl/classes/com/jogamp/opengl/impl/GLContextLock.java +++ b/src/jogl/classes/com/jogamp/opengl/impl/GLContextLock.java @@ -51,9 +51,10 @@ import javax.media.opengl.*; public class GLContextLock { static class SyncData { - Thread owner = null; boolean failFastMode = true; + Thread owner = null; int waiters = 0; + Exception lockedStack = null; } private SyncData sdata = new SyncData(); // synchronized (flow/mem) mutable access @@ -65,9 +66,11 @@ public class GLContextLock { Thread current = Thread.currentThread(); if (sdata.owner == null) { sdata.owner = current; + sdata.lockedStack = new Exception("Previously made current (1) by "+sdata.owner+", lock: "+this); } else if (sdata.owner != current) { while (sdata.owner != null) { if (sdata.failFastMode) { + sdata.lockedStack.printStackTrace(); throw new GLException("Attempt to make context current on thread " + current + " which is already current on thread " + sdata.owner); } else { @@ -82,6 +85,7 @@ public class GLContextLock { } } sdata.owner = current; + sdata.lockedStack = new Exception("Previously made current (2) by "+sdata.owner+", lock: "+this); } else { throw new GLException("Attempt to make the same context current twice on thread " + current); } @@ -94,6 +98,7 @@ public class GLContextLock { Thread current = Thread.currentThread(); if (sdata.owner == current) { sdata.owner = null; + sdata.lockedStack = null; // Assuming notify() implementation weaks up the longest waiting thread, to avoid starvation. // Otherwise we would need to have a Thread queue implemented, using sleep(timeout) and interrupt. sdata.notify(); @@ -112,8 +117,7 @@ public class GLContextLock { /** Indicates whether this lock is held by the current thread. */ public final boolean isHeld() { synchronized(sdata) { - Thread current = Thread.currentThread(); - return (sdata.owner == current); + return (Thread.currentThread() == sdata.owner); } } @@ -131,7 +135,14 @@ public class GLContextLock { public final boolean hasWaiters() { synchronized(sdata) { - return (sdata.waiters != 0); + return (0 != sdata.waiters); + } + } + + public final Exception getLockedStack() { + synchronized(sdata) { + return sdata.lockedStack; } } + } diff --git a/src/jogl/classes/com/jogamp/opengl/impl/GLPbufferImpl.java b/src/jogl/classes/com/jogamp/opengl/impl/GLPbufferImpl.java index 9f113f337..e2c217ac0 100644 --- a/src/jogl/classes/com/jogamp/opengl/impl/GLPbufferImpl.java +++ b/src/jogl/classes/com/jogamp/opengl/impl/GLPbufferImpl.java @@ -49,7 +49,7 @@ import java.beans.PropertyChangeListener; import javax.media.nativewindow.*; import javax.media.opengl.*; -import com.jogamp.nativewindow.impl.RecursiveToolkitLock; +import com.jogamp.common.util.RecursiveToolkitLock; /** Platform-independent class exposing pbuffer functionality to applications. This class is not exposed in the public API as it diff --git a/src/junit/com/jogamp/test/junit/nativewindow/TestRecursiveToolkitLockCORE.java b/src/junit/com/jogamp/test/junit/nativewindow/TestRecursiveToolkitLockCORE.java deleted file mode 100644 index 7da448363..000000000 --- a/src/junit/com/jogamp/test/junit/nativewindow/TestRecursiveToolkitLockCORE.java +++ /dev/null @@ -1,281 +0,0 @@ -/** - * Copyright 2010 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.test.junit.nativewindow; - -import java.lang.reflect.*; -import java.io.IOException; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Test; - -import com.jogamp.nativewindow.impl.RecursiveToolkitLock; - -public class TestRecursiveToolkitLockCORE { - - static final int YIELD_NONE = 0; - static final int YIELD_YIELD = 1; - static final int YIELD_SLEEP = 2; - - static void yield(int mode) { - switch(mode) { - case YIELD_YIELD: - Thread.yield(); - break; - case YIELD_SLEEP: - try { - Thread.sleep(20); - } catch (InterruptedException ie) { - ie.printStackTrace(); - } - break; - default: - break; - } - - } - - static class LockedObject { - static final boolean DEBUG = false; - - public LockedObject() { - locker = new RecursiveToolkitLock(); - actionCounter = 0; - } - - public final void action1Direct(int l, int yieldMode) { - if(DEBUG) { - System.err.print("<a1"); - } - lock(); - try { - if(DEBUG) { - System.err.print("+"); - } - while(l>0) l--; - actionCounter++; - yield(yieldMode); - } finally { - if(DEBUG) { - System.err.print("-"); - } - unlock(); - if(DEBUG) { - System.err.println(">"); - } - } - } - - class Action2 implements Runnable { - int l, yieldMode; - Action2(int l, int yieldMode) { - this.l=l; - this.yieldMode=yieldMode; - } - public void run() { - if(DEBUG) { - System.err.print("[a2"); - } - lock(); - try { - if(DEBUG) { - System.err.print("+"); - } - while(l>0) l--; - actionCounter++; - yield(yieldMode); - } finally { - if(DEBUG) { - System.err.print("-"); - } - unlock(); - if(DEBUG) { - System.err.println("]"); - } - } - } - } - - public final void action2Deferred(int l, int yieldMode) { - Thread thread = new Thread(new Action2(l, yieldMode), Thread.currentThread()+"-action2Deferred"); - thread.start(); - } - - public final void lock() { - locker.lock(); - } - - public final void unlock() { - locker.unlock(); - } - - public final boolean isLocked() { - return locker.isLocked(); - } - - RecursiveToolkitLock locker; - int actionCounter; - } - - interface LockedObjectIf extends Runnable { - void stop(); - boolean isStopped(); - int remaining(); - } - - class LockedObjectAction1 implements LockedObjectIf { - boolean shouldStop; - boolean stopped; - LockedObject lo; - volatile int loops; - int iloops; - int yieldMode; - - public LockedObjectAction1(LockedObject lo, int loops, int iloops, int yieldMode) { - this.lo = lo; - this.loops = loops; - this.iloops = iloops; - this.shouldStop = false; - this.stopped = false; - this.yieldMode = yieldMode; - } - - public final synchronized void stop() { - shouldStop = true; - } - - public final synchronized boolean isStopped() { - return stopped; - } - - public final int remaining() { - return loops; - } - - public void run() { - while(!shouldStop && loops>0) { - lo.action1Direct(iloops, yieldMode); - lo.action2Deferred(iloops, yieldMode); - loops--; - } - synchronized(this) { - stopped = true; - notifyAll(); - } - } - } - - protected void testLockedObjectImpl(int threadNum, int loops, int iloops, int yieldMode) throws InterruptedException { - LockedObject lo = new LockedObject(); - LockedObjectIf[] runners = new LockedObjectIf[threadNum]; - Thread[] threads = new Thread[threadNum]; - int i; - - for(i=0; i<threadNum; i++) { - runners[i] = new LockedObjectAction1(lo, loops, iloops, yieldMode); - threads[i] = new Thread( runners[i], Thread.currentThread()+"-ActionThread-"+i+"/"+threadNum); - threads[i].start(); - } - - int active; - do { - active = threadNum; - for(i=0; i<threadNum; i++) { - if(runners[i].isStopped()) { - active--; - } - } - yield(yieldMode); - } while(0<active); - } - - // @Test - public void testLockedObjectThreading1x100() throws InterruptedException { - System.err.println("++++ TestRecursiveToolkitLockCORE.testLockedObjectThreading1x100"); - testLockedObjectImpl(1, 100, 100, YIELD_NONE); - System.err.println("---- TestRecursiveToolkitLockCORE.testLockedObjectThreading1x100"); - } - - @Test - public void testLockedObjectThreading200x200Yield() throws InterruptedException { - System.err.println("++++ TestRecursiveToolkitLockCORE.testLockedObjectThreading200x200-Yield"); - testLockedObjectImpl(200, 200, 100, YIELD_YIELD); - System.err.println("---- TestRecursiveToolkitLockCORE.testLockedObjectThreading200x200-Yield"); - } - - // @Test - public void testLockedObjectThreading200x200Sleep() throws InterruptedException { - System.err.println("++++ TestRecursiveToolkitLockCORE.testLockedObjectThreading200x200-Sleep"); - testLockedObjectImpl(200, 200, 100, YIELD_SLEEP); - System.err.println("---- TestRecursiveToolkitLockCORE.testLockedObjectThreading200x200-Sleep"); - } - - @Test - public void testLockedObjectThreading200x200None() throws InterruptedException { - System.err.println("++++ TestRecursiveToolkitLockCORE.testLockedObjectThreading200x200-None"); - testLockedObjectImpl(200, 200, 100, YIELD_NONE); - System.err.println("---- TestRecursiveToolkitLockCORE.testLockedObjectThreading200x200-None"); - } - - 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[]) throws IOException { - /*for(int i=0; i<args.length; i++) { - if(args[i].equals("-time")) { - durationPerTest = atoi(args[++i]); - } - } - System.out.println("durationPerTest: "+durationPerTest); - */ - String tstname = TestRecursiveToolkitLockCORE.class.getName(); - /* - org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.main(new String[] { - tstname, - "filtertrace=true", - "haltOnError=false", - "haltOnFailure=false", - "showoutput=true", - "outputtoformatters=true", - "logfailedtests=true", - "logtestlistenerevents=true", - "formatter=org.apache.tools.ant.taskdefs.optional.junit.PlainJUnitResultFormatter", - "formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,TEST-"+tstname+".xml" } ); */ - org.junit.runner.JUnitCore.main(tstname); - } - -} diff --git a/src/junit/com/jogamp/test/junit/newt/parenting/TestParenting03AWT.java b/src/junit/com/jogamp/test/junit/newt/parenting/TestParenting03AWT.java new file mode 100644 index 000000000..86900a41e --- /dev/null +++ b/src/junit/com/jogamp/test/junit/newt/parenting/TestParenting03AWT.java @@ -0,0 +1,221 @@ +/** + * Copyright 2010 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.test.junit.newt.parenting; + +import java.lang.reflect.*; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Test; + +import java.awt.Button; +import java.awt.BorderLayout; +import java.awt.Canvas; +import java.awt.Frame; +import java.awt.Dimension; +import java.awt.Label; + +import javax.media.opengl.*; +import javax.media.nativewindow.*; + +import com.jogamp.opengl.util.Animator; +import com.jogamp.opengl.util.FPSAnimator; +import com.jogamp.newt.*; +import com.jogamp.newt.event.*; +import com.jogamp.newt.opengl.*; +import com.jogamp.newt.awt.NewtCanvasAWT; + +import java.io.IOException; + +import com.jogamp.test.junit.util.*; +import com.jogamp.test.junit.jogl.demos.gl2.gears.Gears; + +public class TestParenting03AWT extends UITestCase { + static { + GLProfile.initSingleton(); + } + + static int width, height; + static long durationPerTest = 800; + static long waitReparent = 0; + static GLCapabilities glCaps; + + @BeforeClass + public static void initClass() { + width = 800; + height = 400; + glCaps = new GLCapabilities(null); + } + + @Test + public void testWindowParenting1AWT2NewtChilds01() throws InterruptedException { + testWindowParenting1AWT2NewtChilds(); + } + + public void testWindowParenting1AWT2NewtChilds() throws InterruptedException { + int x = 0; + int y = 0; + + NEWTEventFiFo eventFifo = new NEWTEventFiFo(); + + GLWindow glWindow1 = GLWindow.create(glCaps); + glWindow1.setUndecorated(true); + GLEventListener demo1 = new Gears(); + setDemoFields(demo1, glWindow1, false); + glWindow1.addGLEventListener(demo1); + final GLWindow f_glWindow1 = glWindow1; + glWindow1.addKeyListener(new KeyAdapter() { + public void keyTyped(KeyEvent e) { + if(e.getKeyChar()=='f') { + f_glWindow1.invoke(false, new GLRunnable() { + public void run(GLAutoDrawable drawable) { + GLWindow win = (GLWindow)drawable; + win.setFullscreen(!win.isFullscreen()); + } }); + } + } + }); + GLAnimatorControl animator1 = new Animator(glWindow1); + animator1.start(); + + GLWindow glWindow2 = GLWindow.create(glCaps); + glWindow2.setUndecorated(true); + GLEventListener demo2 = new Gears(); + setDemoFields(demo2, glWindow2, false); + glWindow2.addGLEventListener(demo2); + final GLWindow f_glWindow2 = glWindow2; + glWindow2.addKeyListener(new KeyAdapter() { + public void keyTyped(KeyEvent e) { + if(e.getKeyChar()=='f') { + f_glWindow2.invoke(false, new GLRunnable() { + public void run(GLAutoDrawable drawable) { + GLWindow win = (GLWindow)drawable; + win.setFullscreen(!win.isFullscreen()); + } }); + } + } + }); + GLAnimatorControl animator2 = new Animator(glWindow2); + animator2.start(); + + NewtCanvasAWT newtCanvasAWT1 = new NewtCanvasAWT(glWindow1); + NewtCanvasAWT newtCanvasAWT2 = new NewtCanvasAWT(glWindow2); + + Frame frame1 = new Frame("AWT Parent Frame"); + frame1.setLayout(new BorderLayout()); + frame1.add(newtCanvasAWT1, BorderLayout.EAST); + frame1.add(new Label("center"), BorderLayout.CENTER); + frame1.add(newtCanvasAWT2, BorderLayout.WEST); + frame1.setLocation(0, 0); + frame1.setSize(width/2, height/2); + System.err.println("1: "+frame1); + frame1.pack(); + frame1.setVisible(true); + System.err.println("2: "+frame1); + + Assert.assertEquals(newtCanvasAWT1.getNativeWindow(),glWindow1.getParent()); + Assert.assertEquals(newtCanvasAWT2.getNativeWindow(),glWindow2.getParent()); + + Assert.assertEquals(true, animator1.isAnimating()); + Assert.assertEquals(false, animator1.isPaused()); + Assert.assertNotNull(animator1.getThread()); + + Assert.assertEquals(true, animator2.isAnimating()); + Assert.assertEquals(false, animator2.isPaused()); + Assert.assertNotNull(animator2.getThread()); + + Thread.sleep(durationPerTest); + + animator1.stop(); + Assert.assertEquals(false, animator1.isAnimating()); + Assert.assertEquals(false, animator1.isPaused()); + Assert.assertEquals(null, animator1.getThread()); + + animator2.stop(); + Assert.assertEquals(false, animator2.isAnimating()); + Assert.assertEquals(false, animator2.isPaused()); + Assert.assertEquals(null, animator2.getThread()); + + frame1.dispose(); + glWindow1.destroy(true); + glWindow2.destroy(true); + } + + public static void setDemoFields(GLEventListener demo, GLWindow glWindow, boolean debug) { + Assert.assertNotNull(demo); + Assert.assertNotNull(glWindow); + Window window = glWindow.getWindow(); + if(debug) { + MiscUtils.setFieldIfExists(demo, "glDebug", true); + MiscUtils.setFieldIfExists(demo, "glTrace", true); + } + if(!MiscUtils.setFieldIfExists(demo, "window", window)) { + MiscUtils.setFieldIfExists(demo, "glWindow", glWindow); + } + } + + 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[]) throws IOException { + for(int i=0; i<args.length; i++) { + if(args[i].equals("-time")) { + durationPerTest = atoi(args[++i]); + } else if(args[i].equals("-wait")) { + waitReparent = atoi(args[++i]); + } + } + String tstname = TestParenting03AWT.class.getName(); + /* + org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.main(new String[] { + tstname, + "filtertrace=true", + "haltOnError=false", + "haltOnFailure=false", + "showoutput=true", + "outputtoformatters=true", + "logfailedtests=true", + "logtestlistenerevents=true", + "formatter=org.apache.tools.ant.taskdefs.optional.junit.PlainJUnitResultFormatter", + "formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,TEST-"+tstname+".xml" } ); */ + org.junit.runner.JUnitCore.main(tstname); + } + +} diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/impl/ProxySurface.java b/src/nativewindow/classes/com/jogamp/nativewindow/impl/ProxySurface.java index 5e6487ae8..9d9f360cd 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/impl/ProxySurface.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/impl/ProxySurface.java @@ -43,6 +43,8 @@ import javax.media.nativewindow.NativeWindow; import javax.media.nativewindow.NativeWindowException; import javax.media.nativewindow.SurfaceChangeable; +import com.jogamp.common.util.RecursiveToolkitLock; + public class ProxySurface implements NativeSurface, SurfaceChangeable { private RecursiveToolkitLock recurLock = new RecursiveToolkitLock(); protected int width, height, scrnIndex; diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/impl/RecursiveToolkitLock.java b/src/nativewindow/classes/com/jogamp/nativewindow/impl/RecursiveToolkitLock.java deleted file mode 100644 index d85df359e..000000000 --- a/src/nativewindow/classes/com/jogamp/nativewindow/impl/RecursiveToolkitLock.java +++ /dev/null @@ -1,214 +0,0 @@ -/** - * Copyright 2010 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.nativewindow.impl; - -import javax.media.nativewindow.*; -import java.util.LinkedList; - -/** - * Reentrance locking toolkit, impl a complete fair FIFO scheduler - */ -public class RecursiveToolkitLock { - static class SyncData { - // owner of the lock - Thread owner = null; - // lock recursion - int recursionCount = 0; - // stack trace of the lock - Exception lockedStack = null; - // waiting thread queue - LinkedList threadQueue = new LinkedList(); - // flag signaling unlock has woken up a waiting thread - boolean signaled = false; - } - private SyncData sdata = new SyncData(); // synchronized (flow/mem) mutable access - - private long timeout; - private static final long defaultTimeout = 5000; // default maximum wait 5s - // private static final long defaultTimeout = 300000; // default maximum wait 300s / 5min - private static final boolean TRACE_LOCK = Debug.debug("TraceLock"); - - public RecursiveToolkitLock() { - this.timeout = defaultTimeout; - } - - public RecursiveToolkitLock(long timeout) { - this.timeout = timeout; - } - - public final Exception getLockedStack() { - synchronized(sdata) { - return sdata.lockedStack; - } - } - - public final Thread getOwner() { - synchronized(sdata) { - return sdata.owner; - } - } - - public final boolean isOwner() { - return isOwner(Thread.currentThread()); - } - - public final boolean isOwner(Thread thread) { - synchronized(sdata) { - return sdata.owner == thread ; - } - } - - public final boolean isLocked() { - synchronized(sdata) { - return null != sdata.owner; - } - } - - public final boolean isLockedByOtherThread() { - synchronized(sdata) { - return null != sdata.owner && Thread.currentThread() != sdata.owner ; - } - } - - public final int getRecursionCount() { - synchronized(sdata) { - return sdata.recursionCount; - } - } - - public final void validateLocked() { - synchronized(sdata) { - if ( null == sdata.owner ) { - throw new RuntimeException(Thread.currentThread()+": Not locked"); - } - if ( Thread.currentThread() != sdata.owner ) { - getLockedStack().printStackTrace(); - throw new RuntimeException(Thread.currentThread()+": Not owner, owner is "+sdata.owner); - } - } - } - - /** Recursive and blocking lockSurface() implementation */ - public final void lock() { - synchronized(sdata) { - Thread cur = Thread.currentThread(); - if (sdata.owner == cur) { - ++sdata.recursionCount; - if(TRACE_LOCK) { - System.err.println("+++ LOCK 2 ["+this+"], recursions "+sdata.recursionCount+", "+cur); - } - return; - } - - if (sdata.owner != null || sdata.signaled || sdata.threadQueue.size() > 0) { - // enqueue due to locked resource or already waiting or signaled threads (be fair) - boolean timedOut = false; - do { - sdata.threadQueue.addFirst(cur); // should only happen once - try { - sdata.wait(timeout); - timedOut = sdata.threadQueue.remove(cur); // timeout if not already removed by unlock - } catch (InterruptedException e) { - if(!sdata.signaled) { - // theoretically we could stay in the loop, - // in case the interrupt wasn't issued by unlock, - // hence the re-enqueue - sdata.threadQueue.remove(cur); - if(TRACE_LOCK) { - System.err.println("XXX LOCK - ["+this+"], recursions "+sdata.recursionCount+", "+cur); - } - } - } - } while (null != sdata.owner && !timedOut) ; - - sdata.signaled = false; - - if(timedOut || null != sdata.owner) { - sdata.lockedStack.printStackTrace(); - throw new RuntimeException("Waited "+timeout+"ms for: "+sdata.owner+" - "+cur+", with recursionCount "+sdata.recursionCount+", lock: "+this+", qsz "+sdata.threadQueue.size()); - } - - if(TRACE_LOCK) { - System.err.println("+++ LOCK 3 ["+this+"], recursions "+sdata.recursionCount+", qsz "+sdata.threadQueue.size()+", "+cur); - } - } else if(TRACE_LOCK) { - System.err.println("+++ LOCK 1 ["+this+"], recursions "+sdata.recursionCount+", qsz "+sdata.threadQueue.size()+", "+cur); - } - - sdata.owner = cur; - sdata.lockedStack = new Exception("Previously locked by "+sdata.owner+", lock: "+this); - } - } - - - /** Recursive and unblocking unlockSurface() implementation */ - public final void unlock() { - unlock(null); - } - - /** Recursive and unblocking unlockSurface() implementation */ - public final void unlock(Runnable taskAfterUnlockBeforeNotify) { - synchronized(sdata) { - validateLocked(); - - if (sdata.recursionCount > 0) { - --sdata.recursionCount; - if(TRACE_LOCK) { - System.err.println("--- LOCK 1 ["+this+"], recursions "+sdata.recursionCount+", "+Thread.currentThread()); - } - return; - } - sdata.owner = null; - sdata.lockedStack = null; - if(null!=taskAfterUnlockBeforeNotify) { - taskAfterUnlockBeforeNotify.run(); - } - - int qsz = sdata.threadQueue.size(); - if(qsz > 0) { - Thread parkedThread = (Thread) sdata.threadQueue.removeLast(); - if(TRACE_LOCK) { - System.err.println("--- LOCK X ["+this+"], recursions "+sdata.recursionCount+ - ", "+Thread.currentThread()+", irq "+(qsz-1)+": "+parkedThread); - } - sdata.signaled = true; - if(qsz==1) { - // fast path, just one waiting thread - sdata.notify(); - } else { - // signal the oldest one .. - parkedThread.interrupt(); // Propagate SecurityException if it happens - } - } else if(TRACE_LOCK) { - System.err.println("--- LOCK X ["+this+"], recursions "+sdata.recursionCount+", "+Thread.currentThread()); - } - } - } -} - diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/impl/jawt/JAWTUtil.java b/src/nativewindow/classes/com/jogamp/nativewindow/impl/jawt/JAWTUtil.java index d7c690fc6..7127bacf9 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/impl/jawt/JAWTUtil.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/impl/jawt/JAWTUtil.java @@ -40,6 +40,8 @@ import com.jogamp.nativewindow.impl.*; import javax.media.nativewindow.*; +import com.jogamp.common.util.RecursiveToolkitLock; + import java.awt.GraphicsEnvironment; import java.lang.reflect.*; import java.security.*; diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/impl/jawt/JAWTWindow.java b/src/nativewindow/classes/com/jogamp/nativewindow/impl/jawt/JAWTWindow.java index 0cb861453..ad0b6104b 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/impl/jawt/JAWTWindow.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/impl/jawt/JAWTWindow.java @@ -37,12 +37,13 @@ package com.jogamp.nativewindow.impl.jawt; +import com.jogamp.nativewindow.impl.*; +import com.jogamp.common.util.RecursiveToolkitLock; import java.awt.Component; import java.awt.Window; import java.awt.GraphicsEnvironment; import javax.media.nativewindow.*; -import com.jogamp.nativewindow.impl.*; import javax.media.nativewindow.util.Point; import javax.media.nativewindow.util.Rectangle; diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/impl/x11/X11Util.java b/src/nativewindow/classes/com/jogamp/nativewindow/impl/x11/X11Util.java index a0936de15..6871cd5f2 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/impl/x11/X11Util.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/impl/x11/X11Util.java @@ -38,6 +38,7 @@ import java.util.Collection; import java.util.ArrayList; import java.util.Iterator; import com.jogamp.common.util.LongObjectHashMap; +import com.jogamp.common.util.RecursiveToolkitLock; import javax.media.nativewindow.*; diff --git a/src/nativewindow/classes/javax/media/nativewindow/NativeSurface.java b/src/nativewindow/classes/javax/media/nativewindow/NativeSurface.java index 3648a9a32..ef45a79d3 100644 --- a/src/nativewindow/classes/javax/media/nativewindow/NativeSurface.java +++ b/src/nativewindow/classes/javax/media/nativewindow/NativeSurface.java @@ -63,14 +63,14 @@ public interface NativeSurface extends SurfaceUpdatedListener { * This call allows recursion from the same thread.<P> * * The implementation may want to aquire the - * application level {@link com.jogamp.nativewindow.impl.RecursiveToolkitLock} + * application level {@link com.jogamp.common.util.RecursiveToolkitLock} * first before proceeding with a native surface lock. <P> * * @return {@link #LOCK_SUCCESS}, {@link #LOCK_SURFACE_CHANGED} or {@link #LOCK_SURFACE_NOT_READY}. * * @throws RuntimeException after timeout when waiting for the surface lock * - * @see com.jogamp.nativewindow.impl.RecursiveToolkitLock + * @see com.jogamp.common.util.RecursiveToolkitLock */ public int lockSurface(); @@ -82,7 +82,7 @@ public interface NativeSurface extends SurfaceUpdatedListener { * @throws RuntimeException if surface is not locked * * @see #lockSurface - * @see com.jogamp.nativewindow.impl.RecursiveToolkitLock + * @see com.jogamp.common.util.RecursiveToolkitLock */ public void unlockSurface() throws NativeWindowException ; diff --git a/src/newt/classes/com/jogamp/newt/event/awt/AWTParentWindowAdapter.java b/src/newt/classes/com/jogamp/newt/event/awt/AWTParentWindowAdapter.java index ba5581def..44ad3586e 100644 --- a/src/newt/classes/com/jogamp/newt/event/awt/AWTParentWindowAdapter.java +++ b/src/newt/classes/com/jogamp/newt/event/awt/AWTParentWindowAdapter.java @@ -71,15 +71,23 @@ public class AWTParentWindowAdapter if(DEBUG_IMPLEMENTATION) { System.err.println("AWT: componentResized: "+comp); } - newtWindow.runOnEDTIfAvail(false, new Runnable() { - public void run() { - if( 0 < comp.getWidth() * comp.getHeight() ) { - newtWindow.setSize(comp.getWidth(), comp.getHeight()); - newtWindow.setVisible(comp.isVisible()); - } else { - newtWindow.setVisible(false); - } - }}); + if(newtWindow.isValid()) { + newtWindow.runOnEDTIfAvail(false, new Runnable() { + public void run() { + int cw = comp.getWidth(); + int ch = comp.getHeight(); + if( 0 < cw * ch ) { + if( newtWindow.getWidth() != cw || newtWindow.getHeight() != ch ) { + newtWindow.setSize(cw, ch); + if(comp.isVisible() != newtWindow.isVisible()) { + newtWindow.setVisible(comp.isVisible()); + } + } + } else if(newtWindow.isVisible()) { + newtWindow.setVisible(false); + } + }}); + } } public void componentMoved(java.awt.event.ComponentEvent e) { @@ -106,14 +114,15 @@ public class AWTParentWindowAdapter if(newtWindow.isValid()) { newtWindow.runOnEDTIfAvail(false, new Runnable() { public void run() { - newtWindow.setVisible(showing); - } - }); + if(newtWindow.isVisible() != showing) { + newtWindow.setVisible(showing); + } + }}); } } - if( 0 != ( java.awt.event.HierarchyEvent.DISPLAYABILITY_CHANGED & bits ) ) { - final boolean displayability = changed.isDisplayable(); - if(DEBUG_IMPLEMENTATION) { + if(DEBUG_IMPLEMENTATION) { + if( 0 != ( java.awt.event.HierarchyEvent.DISPLAYABILITY_CHANGED & bits ) ) { + final boolean displayability = changed.isDisplayable(); System.err.println("AWT: hierarchyChanged DISPLAYABILITY_CHANGED: displayability "+displayability+", "+changed); } } diff --git a/src/newt/classes/com/jogamp/newt/impl/WindowImpl.java b/src/newt/classes/com/jogamp/newt/impl/WindowImpl.java index 72c33f596..7802251e5 100644 --- a/src/newt/classes/com/jogamp/newt/impl/WindowImpl.java +++ b/src/newt/classes/com/jogamp/newt/impl/WindowImpl.java @@ -42,7 +42,7 @@ import com.jogamp.newt.event.*; import com.jogamp.common.util.*; import javax.media.nativewindow.*; -import com.jogamp.nativewindow.impl.RecursiveToolkitLock; +import com.jogamp.common.util.RecursiveToolkitLock; import java.util.ArrayList; import java.util.Iterator; @@ -399,12 +399,6 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } public void setVisible(boolean visible) { - if(DEBUG_IMPLEMENTATION) { - String msg = new String("Window setVisible: START ("+getThreadName()+") "+x+"/"+y+" "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible: "+this.visible+" -> "+visible+", parentWindowHandle "+toHexString(this.parentWindowHandle)+", parentWindow "+(null!=this.parentWindow)/*+", "+this*/); - System.err.println(msg); - //Exception ee = new Exception(msg); - //ee.printStackTrace(); - } if(isValid()) { VisibleAction va = new VisibleAction(visible); runOnEDTIfAvail(true, va); @@ -434,6 +428,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer windowLock.lock(); try { if( isValid() ) { + if(DEBUG_IMPLEMENTATION) { + String msg = new String("Window setVisible: START ("+getThreadName()+") "+x+"/"+y+" "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible: "+WindowImpl.this.visible+" -> "+visible+", parentWindowHandle "+toHexString(WindowImpl.this.parentWindowHandle)+", parentWindow "+(null!=WindowImpl.this.parentWindow)/*+", "+this*/); + System.err.println(msg); + } if(!visible && childWindows.size()>0) { synchronized(childWindowsLock) { for(Iterator i = childWindows.iterator(); i.hasNext(); ) { @@ -471,12 +469,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } } } + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window setVisible: END ("+getThreadName()+") "+x+"/"+y+" "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible: "+WindowImpl.this.visible+", nativeWindowCreated: "+nativeWindowCreated+", madeVisible: "+madeVisible); + } } - - if(DEBUG_IMPLEMENTATION) { - System.err.println("Window setVisible: END ("+getThreadName()+") "+x+"/"+y+" "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible: "+WindowImpl.this.visible+", nativeWindowCreated: "+nativeWindowCreated+", madeVisible: "+madeVisible); - } - } finally { windowLock.unlock(); } @@ -488,35 +484,31 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer int visibleAction = 0; // 1 invisible, 2 visible windowLock.lock(); try{ - if(DEBUG_IMPLEMENTATION) { - String msg = new String("Window setSize: START "+this.width+"x"+this.height+" -> "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible "+visible); - System.err.println(msg); - // Exception e = new Exception(msg); - // e.printStackTrace(); - } - if (width != this.width || this.height != height) { - if(!fullscreen) { - nfs_width=width; - nfs_height=height; - if ( 0 != windowHandle && 0>=width*height && visible ) { - visibleAction=1; // invisible - this.width = 0; - this.height = 0; - } else if ( 0 == windowHandle && 0<width*height && visible ) { - visibleAction = 2; // visible - this.width = width; - this.height = height; - } else if ( 0 != windowHandle ) { - // this width/height will be set by windowChanged, called by the native implementation - setSizeImpl(width, height); - } else { - this.width = width; - this.height = height; - } + if ( !fullscreen && ( width != this.width || this.height != height ) ) { + if(DEBUG_IMPLEMENTATION) { + String msg = new String("Window setSize: START "+this.width+"x"+this.height+" -> "+width+"x"+height+", fs "+fullscreen+", windowHandle "+toHexString(windowHandle)+", visible "+visible); + System.err.println(msg); + } + nfs_width=width; + nfs_height=height; + if ( 0 != windowHandle && 0>=width*height && visible ) { + visibleAction=1; // invisible + this.width = 0; + this.height = 0; + } else if ( 0 == windowHandle && 0<width*height && visible ) { + visibleAction = 2; // visible + this.width = width; + this.height = height; + } else if ( 0 != windowHandle ) { + // this width/height will be set by windowChanged, called by the native implementation + setSizeImpl(width, height); + } else { + this.width = width; + this.height = height; + } + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window setSize: END "+this.width+"x"+this.height+", visibleAction "+visibleAction); } - } - if(DEBUG_IMPLEMENTATION) { - System.err.println("Window setSize: END "+this.width+"x"+this.height+", visibleAction "+visibleAction); } } finally { windowLock.unlock(); @@ -1661,10 +1653,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } protected void sizeChanged(int newWidth, int newHeight) { - if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.sizeChanged: ("+getThreadName()+"): "+width+"x"+height+" -> "+newWidth+"x"+newHeight+" - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); - } if(width != newWidth || height != newHeight) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.sizeChanged: ("+getThreadName()+"): "+width+"x"+height+" -> "+newWidth+"x"+newHeight+" - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); + } width = newWidth; height = newHeight; if(!fullscreen) { @@ -1678,10 +1670,10 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer } protected void positionChanged(int newX, int newY) { - if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.positionChanged: ("+getThreadName()+"): "+x+"/"+y+" -> "+newX+"/"+newY+" - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); - } if( 0==parentWindowHandle && ( x != newX || y != newY ) ) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.positionChanged: ("+getThreadName()+"): "+x+"/"+y+" -> "+newX+"/"+newY+" - windowHandle "+toHexString(windowHandle)+" parentWindowHandle "+toHexString(parentWindowHandle)); + } x = newX; y = newY; if(!fullscreen) { diff --git a/src/newt/classes/com/jogamp/newt/impl/macosx/MacWindow.java b/src/newt/classes/com/jogamp/newt/impl/macosx/MacWindow.java index a114417f0..a2b7cd335 100644 --- a/src/newt/classes/com/jogamp/newt/impl/macosx/MacWindow.java +++ b/src/newt/classes/com/jogamp/newt/impl/macosx/MacWindow.java @@ -35,7 +35,7 @@ package com.jogamp.newt.impl.macosx; import javax.media.nativewindow.*; -import com.jogamp.nativewindow.impl.RecursiveToolkitLock; +import com.jogamp.common.util.RecursiveToolkitLock; import com.jogamp.newt.event.*; import com.jogamp.newt.impl.*; |