diff options
Diffstat (limited to 'src')
5 files changed, 413 insertions, 86 deletions
diff --git a/src/junit/com/jogamp/test/junit/nativewindow/TestRecursiveToolkitLockCORE.java b/src/junit/com/jogamp/test/junit/nativewindow/TestRecursiveToolkitLockCORE.java new file mode 100644 index 000000000..7da448363 --- /dev/null +++ b/src/junit/com/jogamp/test/junit/nativewindow/TestRecursiveToolkitLockCORE.java @@ -0,0 +1,281 @@ +/** + * 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/util/UITestCase.java b/src/junit/com/jogamp/test/junit/util/UITestCase.java index 200a96fe9..5bf76234d 100644 --- a/src/junit/com/jogamp/test/junit/util/UITestCase.java +++ b/src/junit/com/jogamp/test/junit/util/UITestCase.java @@ -28,7 +28,9 @@ package com.jogamp.test.junit.util; +import org.junit.Before; import org.junit.BeforeClass; +import org.junit.After; import org.junit.AfterClass; import java.lang.reflect.InvocationTargetException; @@ -55,5 +57,16 @@ public abstract class UITestCase { // one-time cleanup code singletonInstance.unlock(); } + + @Before + public void setUp() { + System.err.println("++++ UITestCase.setUp: "+getClass().getName()); + } + + @After + public void tearDown() { + System.err.println("++++ UITestCase.tearDown: "+getClass().getName()); + } + } diff --git a/src/nativewindow/classes/com/jogamp/nativewindow/impl/RecursiveToolkitLock.java b/src/nativewindow/classes/com/jogamp/nativewindow/impl/RecursiveToolkitLock.java index a894c616a..bfa3d32a5 100644 --- a/src/nativewindow/classes/com/jogamp/nativewindow/impl/RecursiveToolkitLock.java +++ b/src/nativewindow/classes/com/jogamp/nativewindow/impl/RecursiveToolkitLock.java @@ -34,110 +34,144 @@ import javax.media.nativewindow.*; // Reentrance locking toolkit // public class RecursiveToolkitLock { - private Thread owner = null; - private int recursionCount = 0; - private Exception lockedStack = null; - private static final long timeout = 5000; // maximum wait 5s - // private static final long timeout = 300000; // maximum wait 300s + static class SyncData { + Thread owner = null; + int recursionCount = 0; + Exception lockedStack = null; + } + 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 = 10000; // default maximum wait 10s + // private static final long defaultTimeout = 300000; // default maximum wait 300s / 5min private static final boolean TRACE_LOCK = Debug.debug("TraceLock"); - public Exception getLockedStack() { - return lockedStack; + public RecursiveToolkitLock() { + this.timeout = defaultTimeout; } - public Thread getOwner() { - return owner; + public RecursiveToolkitLock(long timeout) { + this.timeout = timeout; } - public boolean isOwner() { - return isOwner(Thread.currentThread()); + public final Exception getLockedStack() { + synchronized(sdata) { + return sdata.lockedStack; + } } - public synchronized boolean isOwner(Thread thread) { - return owner == thread ; + public final Thread getOwner() { + synchronized(sdata) { + return sdata.owner; + } } - public synchronized boolean isLocked() { - return null != owner; + public final boolean isOwner() { + return isOwner(Thread.currentThread()); } - public synchronized boolean isLockedByOtherThread() { - return null != owner && Thread.currentThread() != owner ; + public final boolean isOwner(Thread thread) { + synchronized(sdata) { + return sdata.owner == thread ; + } } - public synchronized int getRecursionCount() { - return recursionCount; + public final boolean isLocked() { + synchronized(sdata) { + return null != sdata.owner; + } } - public synchronized void validateLocked() { - if ( !isLocked() ) { - throw new RuntimeException(Thread.currentThread()+": Not locked"); + public final boolean isLockedByOtherThread() { + synchronized(sdata) { + return null != sdata.owner && Thread.currentThread() != sdata.owner ; } - if ( !isOwner() ) { - getLockedStack().printStackTrace(); - throw new RuntimeException(Thread.currentThread()+": Not owner, owner is "+owner); + } + + public final int getRecursionCount() { + synchronized(sdata) { + return sdata.recursionCount; } } - /** Recursive and blocking lockSurface() implementation */ - public synchronized void lock() { - Thread cur = Thread.currentThread(); - if(TRACE_LOCK) { - System.out.println("... LOCK 0 ["+this+"], recursions "+recursionCount+", "+cur); + 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); + } } - if (owner == cur) { - ++recursionCount; + } + + /** Recursive and blocking lockSurface() implementation */ + public final void lock() { + synchronized(sdata) { + Thread cur = Thread.currentThread(); if(TRACE_LOCK) { - System.out.println("+++ LOCK 1 ["+this+"], recursions "+recursionCount+", "+cur); + System.out.println("... LOCK 0 ["+this+"], recursions "+sdata.recursionCount+", "+cur); + } + if (sdata.owner == cur) { + ++sdata.recursionCount; + if(TRACE_LOCK) { + System.out.println("+++ LOCK 1 ["+this+"], recursions "+sdata.recursionCount+", "+cur); + } + return; } - return; - } - long ts = System.currentTimeMillis(); - while (owner != null && (System.currentTimeMillis()-ts) < timeout) { - try { - wait(timeout); - } catch (InterruptedException e) { - throw new RuntimeException(e); + long ts = System.currentTimeMillis(); + while (sdata.owner != null && (System.currentTimeMillis()-ts) < timeout) { + try { + sdata.wait(timeout); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } } + if(sdata.owner != null) { + sdata.lockedStack.printStackTrace(); + throw new RuntimeException("Waited "+timeout+"ms for: "+sdata.owner+" - "+cur+", with recursionCount "+sdata.recursionCount+", lock: "+this); + } + if(TRACE_LOCK) { + System.out.println("+++ LOCK X ["+this+"], recursions "+sdata.recursionCount+", "+cur); + } + sdata.owner = cur; + sdata.lockedStack = new Exception("Previously locked by "+sdata.owner+", lock: "+this); } - if(owner != null) { - lockedStack.printStackTrace(); - throw new RuntimeException("Waited "+timeout+"ms for: "+owner+" - "+cur+", with recursionCount "+recursionCount+", lock: "+this); - } - if(TRACE_LOCK) { - System.out.println("+++ LOCK X ["+this+"], recursions "+recursionCount+", "+cur); - } - owner = cur; - lockedStack = new Exception("Previously locked by "+owner+", lock: "+this); } /** Recursive and unblocking unlockSurface() implementation */ - public synchronized void unlock() { + public final void unlock() { unlock(null); } /** Recursive and unblocking unlockSurface() implementation */ - public synchronized void unlock(Runnable taskAfterUnlockBeforeNotify) { - validateLocked(); - - if (recursionCount > 0) { - --recursionCount; + public final void unlock(Runnable taskAfterUnlockBeforeNotify) { + synchronized(sdata) { + validateLocked(); + + if (sdata.recursionCount > 0) { + --sdata.recursionCount; + if(TRACE_LOCK) { + System.out.println("--- LOCK 1 ["+this+"], recursions "+sdata.recursionCount+", "+Thread.currentThread()); + } + return; + } + sdata.owner = null; + sdata.lockedStack = null; + if(null!=taskAfterUnlockBeforeNotify) { + taskAfterUnlockBeforeNotify.run(); + } if(TRACE_LOCK) { - System.out.println("--- LOCK 1 ["+this+"], recursions "+recursionCount+", "+Thread.currentThread()); + System.out.println("--- LOCK X ["+this+"], recursions "+sdata.recursionCount+", "+Thread.currentThread()); } - return; - } - owner = null; - lockedStack = null; - if(null!=taskAfterUnlockBeforeNotify) { - taskAfterUnlockBeforeNotify.run(); - } - if(TRACE_LOCK) { - System.out.println("--- LOCK X ["+this+"], recursions "+recursionCount+", "+Thread.currentThread()); + // 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(); } - notifyAll(); } } diff --git a/src/newt/classes/com/jogamp/newt/impl/WindowImpl.java b/src/newt/classes/com/jogamp/newt/impl/WindowImpl.java index 0e9114145..72c33f596 100644 --- a/src/newt/classes/com/jogamp/newt/impl/WindowImpl.java +++ b/src/newt/classes/com/jogamp/newt/impl/WindowImpl.java @@ -202,7 +202,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer System.err.println("Window.createNative() START ("+getThreadName()+", "+this+")"); } if( null != parentWindow && - NativeWindow.LOCK_SURFACE_NOT_READY >= parentWindow.lockSurface() ) { + NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindow.lockSurface() ) { throw new NativeWindowException("Parent surface lock: not ready: "+parentWindow); } try { @@ -236,26 +236,24 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer private static long getNativeWindowHandle(NativeWindow nativeWindow) { long handle = 0; if(null!=nativeWindow) { - boolean locked=false; - try { - if( NativeWindow.LOCK_SURFACE_NOT_READY < nativeWindow.lockSurface() ) { - locked=true; + boolean wasLocked = false; + if( NativeSurface.LOCK_SURFACE_NOT_READY < nativeWindow.lockSurface() ) { + wasLocked = true; + try { handle = nativeWindow.getWindowHandle(); if(0==handle) { throw new NativeWindowException("Parent native window handle is NULL, after succesful locking: "+nativeWindow); } - } - } catch (NativeWindowException nwe) { - if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.getNativeWindowHandle: not successful yet: "+nwe); - } - } finally { - if(locked) { + } catch (NativeWindowException nwe) { + if(DEBUG_IMPLEMENTATION) { + System.err.println("Window.getNativeWindowHandle: not successful yet: "+nwe); + } + } finally { nativeWindow.unlockSurface(); } } if(DEBUG_IMPLEMENTATION) { - System.err.println("Window.getNativeWindowHandle: locked "+locked+", "+nativeWindow); + System.err.println("Window.getNativeWindowHandle: locked "+wasLocked+", "+nativeWindow); } } return handle; @@ -809,7 +807,7 @@ public abstract class WindowImpl implements Window, NEWTEventConsumer NativeWindow parentWindowLocked = null; if( null != parentWindow ) { parentWindowLocked = parentWindow; - if(NativeWindow.LOCK_SURFACE_NOT_READY >= parentWindowLocked.lockSurface() ) { + if(NativeSurface.LOCK_SURFACE_NOT_READY >= parentWindowLocked.lockSurface() ) { throw new NativeWindowException("Parent surface lock: not ready: "+parentWindow); } } diff --git a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java index a7104bf1a..d39e0e29b 100644 --- a/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java +++ b/src/newt/classes/com/jogamp/newt/opengl/GLWindow.java @@ -456,11 +456,12 @@ public class GLWindow implements GLAutoDrawable, Window { if(forceReshape) { sendReshape = true; } - lockSurface(); - try{ - helper.invokeGL(drawable, context, displayAction, initAction); - } finally { - unlockSurface(); + if( NativeSurface.LOCK_SURFACE_NOT_READY < lockSurface() ) { + try{ + helper.invokeGL(drawable, context, displayAction, initAction); + } finally { + unlockSurface(); + } } } } |