From 1c7f8f8dafe0252afbb0e2701687210814b56793 Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Thu, 14 Oct 2010 21:30:14 +0200
Subject: Moved locking to: com.jogamp.common.util.locks ; Better abstraction ;
 Misc changes

---
 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 +++++++++++++++++++++
 7 files changed, 464 insertions(+), 237 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

(limited to 'src/java/com')

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; i<cstrArgs.length; i++) {
-        cstrArgTypes[i] = cstrArgs[i].getClass();
+    Class[] cstrArgTypes = null;
+    if(null!=cstrArgs) {
+        cstrArgTypes = new Class[cstrArgs.length];
+        for(int i=0; i<cstrArgs.length; i++) {
+            cstrArgTypes[i] = cstrArgs[i].getClass();
+        }
     }
     return createInstance(clazz, cstrArgTypes, cstrArgs);
   }
@@ -160,9 +170,12 @@ public final class ReflectionUtil {
   public static final Object createInstance(String clazzName, Object[] cstrArgs, ClassLoader cl) 
       throws JogampRuntimeException, RuntimeException
   {
-    Class[] cstrArgTypes = new Class[cstrArgs.length];
-    for(int i=0; i<cstrArgs.length; i++) {
-        cstrArgTypes[i] = cstrArgs[i].getClass();
+    Class[] cstrArgTypes = null;
+    if(null!=cstrArgs) {
+        cstrArgTypes = new Class[cstrArgs.length];
+        for(int i=0; i<cstrArgs.length; i++) {
+            cstrArgTypes[i] = cstrArgs[i].getClass();
+        }
     }
     return createInstance(clazzName, cstrArgTypes, cstrArgs, cl);
   }
@@ -170,7 +183,7 @@ public final class ReflectionUtil {
   public static final Object createInstance(String clazzName, ClassLoader cl)
       throws JogampRuntimeException, RuntimeException
   {
-    return createInstance(clazzName, new Class[0], null, cl);
+    return createInstance(clazzName, null, null, cl);
   }
 
   public static final boolean instanceOf(Object obj, String clazzName) {
@@ -239,8 +252,12 @@ public final class ReflectionUtil {
   }
 
   /**
-   * @throws JogampRuntimeException if the call fails
-   * @throws RuntimeException if the call fails
+   * @param instance may be null in case of a static method
+   * @param method the method to be called
+   * @param args the method arguments
+   * @return the methods result, maybe null if void
+   * @throws JogampRuntimeException if call fails
+   * @throws RuntimeException if call fails
    */
   public static final Object callMethod(Object instance, Method method, Object[] args)
       throws JogampRuntimeException, RuntimeException
diff --git a/src/java/com/jogamp/common/util/locks/Lock.java b/src/java/com/jogamp/common/util/locks/Lock.java
new file mode 100644
index 0000000..7065ff8
--- /dev/null
+++ b/src/java/com/jogamp/common/util/locks/Lock.java
@@ -0,0 +1,77 @@
+/**
+ * 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;
+
+/**
+ * Specifying a thread blocking lock implementation
+ */
+public interface Lock {
+
+    /** Enable via the property <code>jogamp.debug.Lock</code> */
+    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 <code>TIMEOUT</code> for {@link #lock()} in ms,
+     * and defaults to {@link #DEFAULT_TIMEOUT}.<br>
+     * It can be overriden via the system property <code>jogamp.common.utils.locks.Lock.timeout</code>.
+     */
+    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 <code>maxwait</code> 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 <code>maxwait</code>, 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 <code>null</code>.
+     * @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());
+            }
+        }
+    }
+}
+
-- 
cgit v1.2.3