From 1c7f8f8dafe0252afbb0e2701687210814b56793 Mon Sep 17 00:00:00 2001 From: Sven Gothel Date: Thu, 14 Oct 2010 21:30:14 +0200 Subject: Moved locking to: com.jogamp.common.util.locks ; Better abstraction ; Misc changes --- make/build.xml | 2 + make/scripts/runtest.sh | 14 +- src/java/com/jogamp/common/impl/Debug.java | 25 +- src/java/com/jogamp/common/os/Platform.java | 48 +++- .../jogamp/common/util/RecursiveToolkitLock.java | 215 ---------------- .../com/jogamp/common/util/ReflectionUtil.java | 49 ++-- src/java/com/jogamp/common/util/locks/Lock.java | 77 ++++++ src/java/com/jogamp/common/util/locks/LockExt.java | 50 ++++ .../jogamp/common/util/locks/RecursiveLock.java | 237 +++++++++++++++++ .../common/util/TestRecursiveToolkitLock.java | 279 -------------------- .../common/util/locks/TestRecursiveLock01.java | 280 +++++++++++++++++++++ 11 files changed, 756 insertions(+), 520 deletions(-) delete mode 100644 src/java/com/jogamp/common/util/RecursiveToolkitLock.java create mode 100644 src/java/com/jogamp/common/util/locks/Lock.java create mode 100644 src/java/com/jogamp/common/util/locks/LockExt.java create mode 100644 src/java/com/jogamp/common/util/locks/RecursiveLock.java delete mode 100644 src/junit/com/jogamp/common/util/TestRecursiveToolkitLock.java create mode 100644 src/junit/com/jogamp/common/util/locks/TestRecursiveLock01.java diff --git a/make/build.xml b/make/build.xml index 0ed82e9..4babd6b 100755 --- a/make/build.xml +++ b/make/build.xml @@ -634,6 +634,7 @@ + @@ -682,6 +683,7 @@ + diff --git a/make/scripts/runtest.sh b/make/scripts/runtest.sh index c58b69a..9484eb4 100644 --- a/make/scripts/runtest.sh +++ b/make/scripts/runtest.sh @@ -8,14 +8,20 @@ if [ -z "$builddir" ] ; then exit 1 fi +LOG=runtest.log +rm -f $LOG + +#D_ARGS="-Djogamp.debug.ProcAddressHelper=true -Djogamp.debug.NativeLibrary=true" +D_ARGS="-Djogamp.debug.TraceLock" + function onetest() { clazz=$1 shift echo $clazz - java -Djava.library.path=$builddir/obj:$builddir/test/build/natives -classpath lib/junit.jar:$builddir/classes:$builddir/test/build/classes $clazz + java $D_ARGS -Djava.library.path=$builddir/obj:$builddir/test/build/natives -classpath lib/junit.jar:$builddir/classes:$builddir/test/build/classes $clazz echo } -onetest com.jogamp.common.util.TestRecursiveToolkitLock -#onetest com.jogamp.gluegen.test.TestPointerBufferEndian -#onetest com.jogamp.gluegen.test.TestStructAccessorEndian +onetest com.jogamp.common.util.TestRecursiveToolkitLock 2>&1 | tee -a $LOG +#onetest com.jogamp.gluegen.test.TestPointerBufferEndian 2>&1 | tee -a $LOG +#onetest com.jogamp.gluegen.test.TestStructAccessorEndian 2>&1 | tee -a $LOG diff --git a/src/java/com/jogamp/common/impl/Debug.java b/src/java/com/jogamp/common/impl/Debug.java index 06a5fea..d8412ae 100644 --- a/src/java/com/jogamp/common/impl/Debug.java +++ b/src/java/com/jogamp/common/impl/Debug.java @@ -56,18 +56,33 @@ public class Debug { } static int getIntProperty(final String property, final boolean jnlpAlias) { - return getIntProperty(property, jnlpAlias, localACC); + return getIntProperty(property, jnlpAlias, localACC, 0); } - public static int getIntProperty(final String property, final boolean jnlpAlias, final AccessControlContext acc) { - int i=0; + public static int getIntProperty(final String property, final boolean jnlpAlias, final AccessControlContext acc, int defaultValue) { + int i=defaultValue; try { - Integer iv = Integer.valueOf(Debug.getProperty(property, jnlpAlias, acc)); - i = iv.intValue(); + String sv = Debug.getProperty(property, jnlpAlias, acc); + if(null!=sv) { + Integer iv = Integer.valueOf(sv); + i = iv.intValue(); + } } catch (NumberFormatException nfe) {} return i; } + public static long getLongProperty(final String property, final boolean jnlpAlias, final AccessControlContext acc, long defaultValue) { + long l=defaultValue; + try { + String sv = Debug.getProperty(property, jnlpAlias, acc); + if(null!=sv) { + Long lv = Long.valueOf(sv); + l = lv.longValue(); + } + } catch (NumberFormatException nfe) {} + return l; + } + static boolean getBooleanProperty(final String property, final boolean jnlpAlias) { return getBooleanProperty(property, jnlpAlias, localACC); } diff --git a/src/java/com/jogamp/common/os/Platform.java b/src/java/com/jogamp/common/os/Platform.java index 2b59d64..aac72af 100644 --- a/src/java/com/jogamp/common/os/Platform.java +++ b/src/java/com/jogamp/common/os/Platform.java @@ -48,7 +48,12 @@ public class Platform { public static final boolean JAVA_SE; public static final boolean LITTLE_ENDIAN; public static final String OS; + public static final String OS_VERSION; public static final String ARCH; + public static final String JAVA_VENDOR; + public static final String JAVA_VENDOR_URL; + public static final String JAVA_VERSION; + public static final String NEWLINE; private static final boolean is32Bit; private static final int pointerSizeInBits; @@ -60,7 +65,12 @@ public class Platform { // here as these system properties are visible even to unsigned // applets OS = System.getProperty("os.name"); + OS_VERSION = System.getProperty("os.version"); ARCH = System.getProperty("os.arch"); + JAVA_VENDOR = System.getProperty("java.vendor"); + JAVA_VENDOR_URL = System.getProperty("java.vendor.url"); + JAVA_VERSION = System.getProperty("java.version"); + NEWLINE = System.getProperty("line.separator"); pointerSizeInBits = getPointerSizeInBitsImpl(); is32Bit = initArch(); @@ -119,7 +129,7 @@ public class Platform { } // probe for classes we need on a SE environment - try{ + try { Class.forName("java.nio.LongBuffer"); Class.forName("java.nio.DoubleBuffer"); return true; @@ -162,6 +172,14 @@ public class Platform { return OS; } + /** + * Returns the OS version. + */ + public static String getOSVersion() { + return OS_VERSION; + } + + /** * Returns the CPU architecture String. */ @@ -169,6 +187,34 @@ public class Platform { return ARCH; } + /** + * Returns the JAVA vendor + */ + public static String getJavaVendor() { + return JAVA_VENDOR; + } + + /** + * Returns the JAVA vendor url + */ + public static String getJavaVendorURL() { + return JAVA_VENDOR_URL; + } + + /** + * Returns the JAVA vendor + */ + public static String getJavaVersion() { + return JAVA_VERSION; + } + + /** + * Returns the JAVA vendor + */ + public static String getNewline() { + return NEWLINE; + } + /** * Returns true if this JVM is a 32bit JVM. */ diff --git a/src/java/com/jogamp/common/util/RecursiveToolkitLock.java b/src/java/com/jogamp/common/util/RecursiveToolkitLock.java deleted file mode 100644 index e02ff94..0000000 --- a/src/java/com/jogamp/common/util/RecursiveToolkitLock.java +++ /dev/null @@ -1,215 +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.common.util; - -import com.jogamp.common.impl.Debug; - -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/java/com/jogamp/common/util/ReflectionUtil.java b/src/java/com/jogamp/common/util/ReflectionUtil.java index a04ec73..bf4624f 100644 --- a/src/java/com/jogamp/common/util/ReflectionUtil.java +++ b/src/java/com/jogamp/common/util/ReflectionUtil.java @@ -42,7 +42,9 @@ import com.jogamp.common.impl.Debug; public final class ReflectionUtil { - public static final boolean DEBUG = Debug.debug("ReflectionUtil"); + public static final boolean DEBUG = Debug.debug("ReflectionUtil"); + + private static final Class[] zeroTypes = new Class[0]; /** * Returns true only if the class could be loaded. @@ -87,12 +89,14 @@ public final class ReflectionUtil { static final String asString(Class[] argTypes) { StringBuffer args = new StringBuffer(); boolean coma = false; - for (int i = 0; i < argTypes.length; i++) { - if(coma) { - args.append(", "); + if(null != argTypes) { + for (int i = 0; i < argTypes.length; i++) { + if(coma) { + args.append(", "); + } + args.append(argTypes[i].getName()); + coma = true; } - args.append(argTypes[i].getName()); - coma = true; } return args.toString(); } @@ -103,6 +107,9 @@ public final class ReflectionUtil { public static final Constructor getConstructor(Class clazz, Class[] cstrArgTypes) throws JogampRuntimeException { try { + if(null == cstrArgTypes) { + cstrArgTypes = zeroTypes; + } return clazz.getDeclaredConstructor(cstrArgTypes); } catch (NoSuchMethodException ex) { throw new JogampRuntimeException("Constructor: '" + clazz + "(" + asString(cstrArgTypes) + ")' not found", ex); @@ -111,7 +118,7 @@ public final class ReflectionUtil { public static final Constructor getConstructor(String clazzName, ClassLoader cl) throws JogampRuntimeException { - return getConstructor(clazzName, new Class[0], cl); + return getConstructor(clazzName, null, cl); } /** @@ -140,9 +147,12 @@ public final class ReflectionUtil { public static final Object createInstance(Class clazz, Object[] cstrArgs) throws JogampRuntimeException, RuntimeException { - Class[] cstrArgTypes = new Class[cstrArgs.length]; - for(int i=0; ijogamp.debug.Lock */ + public static final boolean DEBUG = Debug.debug("Lock"); + + /** Defines the default {@link #TIMEOUT} value */ + public static final long DEFAULT_TIMEOUT = 5000; // 5s default timeout + + /** + * Defines the TIMEOUT for {@link #lock()} in ms, + * and defaults to {@link #DEFAULT_TIMEOUT}.
+ * It can be overriden via the system property jogamp.common.utils.locks.Lock.timeout. + */ + public static final long TIMEOUT = Debug.getLongProperty("jogamp.common.utils.locks.Lock.timeout", true, AccessController.getContext(), DEFAULT_TIMEOUT); + + /** + * Blocking until the lock is acquired by this Thread or {@link #TIMEOUT} is reached. + * + * @throws RuntimeException in case of {@link #TIMEOUT} + */ + void lock() throws RuntimeException; + + /** + * Blocking until the lock is acquired by this Thread or maxwait in ms is reached. + * + * @param maxwait Maximum time in ms to wait to acquire the lock. If this value is zero, + * the call returns immediately either without being able + * to acquire the lock, or with acquiring the lock directly while ignoring any scheduling order. + * @return true if the lock has been acquired within maxwait, otherwise false + * + * @throws RuntimeException in case of {@link #TIMEOUT} + */ + boolean tryLock(long maxwait) throws RuntimeException; + + /** + * Unblocking. + * + * @throws RuntimeException in case the lock is not acquired by this thread. + */ + void unlock() throws RuntimeException; +} diff --git a/src/java/com/jogamp/common/util/locks/LockExt.java b/src/java/com/jogamp/common/util/locks/LockExt.java new file mode 100644 index 0000000..42a01c6 --- /dev/null +++ b/src/java/com/jogamp/common/util/locks/LockExt.java @@ -0,0 +1,50 @@ +/** + * 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.common.util.locks; + +/** + * Extending the {@link Lock} features with convenient functionality. + */ +public interface LockExt extends Lock { + + /** + * @return the Thread owning this lock if locked, otherwise null + */ + Thread getOwner(); + + boolean isLocked(); + + boolean isLockedByOtherThread(); + + boolean isOwner(); + + boolean isOwner(Thread thread); + + void validateLocked(); +} diff --git a/src/java/com/jogamp/common/util/locks/RecursiveLock.java b/src/java/com/jogamp/common/util/locks/RecursiveLock.java new file mode 100644 index 0000000..010467b --- /dev/null +++ b/src/java/com/jogamp/common/util/locks/RecursiveLock.java @@ -0,0 +1,237 @@ +/** + * 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.common.util.locks; + +import com.jogamp.common.impl.Debug; +import java.security.AccessController; + +import java.util.LinkedList; + +/** + * Reentrance locking toolkit, impl a complete fair FIFO scheduler + */ +public class RecursiveLock implements LockExt { + + static class SyncData { + // owner of the lock + Thread owner = null; + // lock recursion + int recursionCount = 0; + // stack trace of the lock, only used if DEBUG + Throwable 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 static final boolean TRACE_LOCK = Debug.isPropertyDefined("jogamp.debug.Lock.TraceLock", true, AccessController.getContext()); + + public RecursiveLock() { + } + + /** + * Returns the Throwable instance generated when this lock was taken the 1st time + * and if {@link com.jogamp.common.util.locks.Lock#DEBUG} is turned on, otherwise it returns always null. + * @see com.jogamp.common.util.locks.Lock#DEBUG + */ + public final Throwable 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 ) { + if(null!=sdata.lockedStack) { + sdata.lockedStack.printStackTrace(); + } + throw new RuntimeException(Thread.currentThread()+": Not owner, owner is "+sdata.owner); + } + } + } + + public final void lock() { + synchronized(sdata) { + if(!tryLock(TIMEOUT)) { + if(null!=sdata.lockedStack) { + sdata.lockedStack.printStackTrace(); + } + throw new RuntimeException("Waited "+TIMEOUT+"ms for: "+sdata.owner+" - "+Thread.currentThread()+", with recursionCount "+sdata.recursionCount+", lock: "+this+", qsz "+sdata.threadQueue.size()); + } + } + } + + public boolean tryLock(long maxwait) { + synchronized(sdata) { + Thread cur = Thread.currentThread(); + if(TRACE_LOCK) { + Throwable tt = new Throwable("LOCK 0 ["+this+"], recursions "+sdata.recursionCount+", cur "+cur+", owner "+sdata.owner); + tt.printStackTrace(); + } + if (sdata.owner == cur) { + ++sdata.recursionCount; + if(TRACE_LOCK) { + System.err.println("+++ LOCK 2 ["+this+"], recursions "+sdata.recursionCount+", "+cur); + } + return true; + } + + if ( sdata.owner != null || + 0 < maxwait && ( sdata.signaled || sdata.threadQueue.size() > 0 ) ) { + + if ( 0 >= maxwait ) { + // implies 'sdata.owner != null': locked by other thread + // no waiting requested, bail out right away + return false; + } + + boolean timedOut = false; + do { + sdata.threadQueue.addFirst(cur); // should only happen once + try { + sdata.wait(maxwait); + 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) { + return false; + } + + 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; + if(DEBUG) { + sdata.lockedStack = new Throwable("Previously locked by "+sdata.owner+", lock: "+this); + } + return true; + } + } + + + public final void unlock() { + unlock(null); + } + + 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/junit/com/jogamp/common/util/TestRecursiveToolkitLock.java b/src/junit/com/jogamp/common/util/TestRecursiveToolkitLock.java deleted file mode 100644 index ea2e925..0000000 --- a/src/junit/com/jogamp/common/util/TestRecursiveToolkitLock.java +++ /dev/null @@ -1,279 +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.common.util; - -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; - -public class TestRecursiveToolkitLock { - - 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("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; i0) 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(); + } + + RecursiveLock 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