summaryrefslogtreecommitdiffstats
path: root/src/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java')
-rw-r--r--src/java/com/jogamp/common/util/locks/LockFactory.java10
-rw-r--r--src/java/com/jogamp/common/util/locks/RecursiveThreadGroupLock.java137
-rw-r--r--src/java/jogamp/common/util/locks/RecursiveThreadGroupLockImpl01Unfairish.java211
3 files changed, 357 insertions, 1 deletions
diff --git a/src/java/com/jogamp/common/util/locks/LockFactory.java b/src/java/com/jogamp/common/util/locks/LockFactory.java
index c374c2d..bb2d5f4 100644
--- a/src/java/com/jogamp/common/util/locks/LockFactory.java
+++ b/src/java/com/jogamp/common/util/locks/LockFactory.java
@@ -30,11 +30,12 @@ package com.jogamp.common.util.locks;
import jogamp.common.util.locks.RecursiveLockImpl01CompleteFair;
import jogamp.common.util.locks.RecursiveLockImpl01Unfairish;
import jogamp.common.util.locks.RecursiveLockImplJava5;
+import jogamp.common.util.locks.RecursiveThreadGroupLockImpl01Unfairish;
public class LockFactory {
public enum ImplType {
- Int01(0), Java5(1);
+ Int01(0), Java5(1), Int02ThreadGroup(2);
public final int id;
@@ -48,12 +49,19 @@ public class LockFactory {
return new RecursiveLockImpl01Unfairish();
}
+ /** default is ImplType.Int02ThreadGroup, unfair'ish (fastest w/ least deviation) */
+ public static RecursiveThreadGroupLock createRecursiveThreadGroupLock() {
+ return new RecursiveThreadGroupLockImpl01Unfairish();
+ }
+
public static RecursiveLock createRecursiveLock(ImplType t, boolean fair) {
switch(t) {
case Int01:
return fair ? new RecursiveLockImpl01CompleteFair() : new RecursiveLockImpl01Unfairish();
case Java5:
return new RecursiveLockImplJava5(fair);
+ case Int02ThreadGroup:
+ return new RecursiveThreadGroupLockImpl01Unfairish();
}
throw new InternalError("XXX");
}
diff --git a/src/java/com/jogamp/common/util/locks/RecursiveThreadGroupLock.java b/src/java/com/jogamp/common/util/locks/RecursiveThreadGroupLock.java
new file mode 100644
index 0000000..5e11f29
--- /dev/null
+++ b/src/java/com/jogamp/common/util/locks/RecursiveThreadGroupLock.java
@@ -0,0 +1,137 @@
+/**
+ * Copyright 2011 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;
+
+/**
+ * Reentrance capable locking toolkit, supporting multiple threads as owner.
+ * <p>
+ * See use case description at {@link #addOwner(Thread)}.
+ * </p>
+ */
+public interface RecursiveThreadGroupLock extends RecursiveLock {
+ /**
+ * Returns true if the current thread is the original lock owner, ie.
+ * successfully claimed this lock the first time, ie. {@link #getHoldCount()} == 1.
+ */
+ boolean isOriginalOwner();
+
+ /**
+ * Returns true if the passed thread is the original lock owner, ie.
+ * successfully claimed this lock the first time, ie. {@link #getHoldCount()} == 1.
+ */
+ boolean isOriginalOwner(Thread thread);
+
+ /**
+ * Add a thread to the list of additional lock owners, which enables them to recursively claim this lock.
+ * <p>
+ * The caller must hold this lock and be the original lock owner, see {@link #isOriginalOwner()}.
+ * </p>
+ * <p>
+ * If the original owner releases this lock via {@link #unlock()}
+ * all additional lock owners are released as well.
+ * This ensures consistency of spawn off additional lock owner threads and it's release.
+ * </p>
+ * Use case:
+ * <pre>
+ * Thread2 thread2 = new Thread2();
+ *
+ * Thread1 {
+ *
+ * // Claim this lock and become the original lock owner.
+ * lock.lock();
+ *
+ * try {
+ *
+ * // Allow Thread2 to claim the lock, ie. make thread2 an additional lock owner
+ * addOwner(thread2);
+ *
+ * // Start thread2
+ * thread2.start();
+ *
+ * // Wait until thread2 has finished requiring this lock, but keep thread2 running
+ * while(!thread2.waitForResult()) sleep();
+ *
+ * // Optional: Only if sure that this thread doesn't hold the lock anymore,
+ * // otherwise just release the lock via unlock().
+ * removeOwner(thread2);
+ *
+ * } finally {
+ *
+ * // Release this lock and remove all additional lock owners.
+ * // Implicit wait until thread2 gets off the lock.
+ * lock.unlock();
+ *
+ * }
+ *
+ * }.start();
+ * </pre>
+ *
+ * @param t the thread to be added to the list of additional owning threads
+ * @throws RuntimeException if the current thread does not hold the lock.
+ * @throws IllegalArgumentException if the passed thread is the lock owner or already added.
+ *
+ * @see #removeOwner(Thread)
+ * @see #unlock()
+ * @see #lock()
+ */
+ void addOwner(Thread t) throws RuntimeException, IllegalArgumentException;
+
+ /**
+ * Remove a thread from the list of additional lock owner threads.
+ * <p>
+ * The caller must hold this lock and be the original lock owner, see {@link #isOriginalOwner()}.
+ * </p>
+ * <p>
+ * Only use this method if sure that the thread doesn't hold the lock anymore.
+ * </p>
+ *
+ * @param t the thread to be removed from the list of additional owning threads
+ * @throws RuntimeException if the current thread does not hold the lock.
+ * @throws IllegalArgumentException if the passed thread is not added by {@link #addOwner(Thread)}
+ */
+ void removeOwner(Thread t) throws RuntimeException, IllegalArgumentException;
+
+ /**
+ * <p>
+ * Wait's until all additional owners released this lock before releasing it.
+ * </p>
+ *
+ * {@inheritDoc}
+ */
+ void unlock() throws RuntimeException;
+
+ /**
+ * <p>
+ * Wait's until all additional owners released this lock before releasing it.
+ * </p>
+ *
+ * {@inheritDoc}
+ */
+ void unlock(Runnable taskAfterUnlockBeforeNotify);
+
+}
diff --git a/src/java/jogamp/common/util/locks/RecursiveThreadGroupLockImpl01Unfairish.java b/src/java/jogamp/common/util/locks/RecursiveThreadGroupLockImpl01Unfairish.java
new file mode 100644
index 0000000..561ac61
--- /dev/null
+++ b/src/java/jogamp/common/util/locks/RecursiveThreadGroupLockImpl01Unfairish.java
@@ -0,0 +1,211 @@
+/**
+ * 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 jogamp.common.util.locks;
+
+import java.util.Arrays;
+
+import com.jogamp.common.util.locks.RecursiveThreadGroupLock;
+
+public class RecursiveThreadGroupLockImpl01Unfairish
+ extends RecursiveLockImpl01Unfairish
+ implements RecursiveThreadGroupLock
+{
+ /* package */ @SuppressWarnings("serial")
+ static class ThreadGroupSync extends SingleThreadSync {
+ /* package */ ThreadGroupSync() {
+ super();
+ threadNum = 0;
+ threads = null;
+ holdCountAdditionOwner = 0;
+ }
+ public final void incrHoldCount(Thread t) {
+ super.incrHoldCount(t);
+ if(!isOriginalOwner(t)) {
+ holdCountAdditionOwner++;
+ }
+ }
+ public final void decrHoldCount(Thread t) {
+ super.decrHoldCount(t);
+ if(!isOriginalOwner(t)) {
+ holdCountAdditionOwner--;
+ }
+ }
+ public final int getAdditionalOwnerHoldCount() {
+ return holdCountAdditionOwner;
+ }
+
+ public final boolean isOriginalOwner(Thread t) {
+ return super.isOwner(t);
+ }
+ public final boolean isOwner(Thread t) {
+ if(getExclusiveOwnerThread()==t) {
+ return true;
+ }
+ for(int i=threadNum-1; 0<=i; i--) {
+ if(threads[i]==t) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public final int getAddOwnerCount() {
+ return threadNum;
+ }
+ public final void addOwner(Thread t) throws IllegalArgumentException {
+ if(null == threads) {
+ if(threadNum>0) {
+ throw new InternalError("XXX");
+ }
+ threads = new Thread[4];
+ }
+ for(int i=threadNum-1; 0<=i; i--) {
+ if(threads[i]==t) {
+ throw new IllegalArgumentException("Thread already added: "+t);
+ }
+ }
+ if (threadNum == threads.length) {
+ threads = Arrays.copyOf(threads, threadNum * 2);
+ }
+ threads[threadNum] = t;
+ threadNum++;
+ }
+
+ public final void removeAllOwners() {
+ for(int i=threadNum-1; 0<=i; i--) {
+ threads[i]=null;
+ }
+ threadNum=0;
+ }
+
+ public final void removeOwner(Thread t) throws IllegalArgumentException {
+ for (int i = 0 ; i < threadNum ; i++) {
+ if (threads[i] == t) {
+ threadNum--;
+ System.arraycopy(threads, i + 1, threads, i, threadNum - i);
+ threads[threadNum] = null; // cleanup 'dead' [or duplicate] reference for GC
+ return;
+ }
+ }
+ throw new IllegalArgumentException("Not an owner: "+t);
+ }
+
+ String addOwnerToString() {
+ StringBuffer sb = new StringBuffer();
+ for(int i=0; i<threadNum; i++) {
+ if(i>0) {
+ sb.append(", ");
+ }
+ sb.append(threads[i].getName());
+ }
+ return sb.toString();
+ }
+
+ // lock count by addition owner threads
+ private int holdCountAdditionOwner;
+ private Thread[] threads;
+ private int threadNum;
+ }
+
+ public RecursiveThreadGroupLockImpl01Unfairish() {
+ super(new ThreadGroupSync());
+ }
+
+ public final boolean isOriginalOwner() {
+ return isOriginalOwner(Thread.currentThread());
+ }
+
+ public final boolean isOriginalOwner(Thread thread) {
+ synchronized(sync) {
+ return ((ThreadGroupSync)sync).isOriginalOwner(thread) ;
+ }
+ }
+
+ public final void addOwner(Thread t) throws RuntimeException, IllegalArgumentException {
+ validateLocked();
+ final Thread cur = Thread.currentThread();
+ final ThreadGroupSync tgSync = (ThreadGroupSync)sync;
+ if(!tgSync.isOriginalOwner(cur)) {
+ throw new IllegalArgumentException("Current thread is not the original owner: orig-owner: "+tgSync.getOwner()+", current "+cur);
+ }
+ if(tgSync.isOriginalOwner(t)) {
+ throw new IllegalArgumentException("Passed thread is original owner: "+t);
+ }
+ tgSync.addOwner(t);
+ }
+
+ public final void unlock(Runnable taskAfterUnlockBeforeNotify) {
+ synchronized(sync) {
+ final Thread cur = Thread.currentThread();
+ final ThreadGroupSync tgSync = (ThreadGroupSync)sync;
+
+ if( tgSync.getAddOwnerCount()>0 ) {
+ if(DEBUG) {
+ System.err.println("++ unlock(0): currentThread "+cur.getName()+", lock: "+this.toString());
+ }
+ if( tgSync.isOriginalOwner(cur) ) {
+ // original locking owner thread
+ if( tgSync.getHoldCount() - tgSync.getAdditionalOwnerHoldCount() == 1 ) {
+ // release orig. lock
+ while ( tgSync.getAdditionalOwnerHoldCount() > 0 ) {
+ try {
+ sync.wait();
+ } catch (InterruptedException e) {
+ // regular wake up!
+ }
+ }
+ tgSync.removeAllOwners();
+ }
+ } else if( tgSync.getAdditionalOwnerHoldCount() == 1 ) {
+ // last additional owner thread wakes up original owner
+ final Thread originalOwner = tgSync.getOwner();
+ if(originalOwner.getState() == Thread.State.WAITING) {
+ originalOwner.interrupt();
+ }
+ }
+ }
+ if(DEBUG) {
+ System.err.println("++ unlock(X): currentThread "+cur.getName()+", lock: "+this.toString());
+ }
+ super.unlock(taskAfterUnlockBeforeNotify);
+ }
+ }
+
+ public final void removeOwner(Thread t) throws RuntimeException, IllegalArgumentException {
+ validateLocked();
+ ((ThreadGroupSync)sync).removeOwner(t);
+ }
+
+ public String toString() {
+ final ThreadGroupSync tgSync = (ThreadGroupSync)sync;
+ final int hc = sync.getHoldCount();
+ final int addHC = tgSync.getAdditionalOwnerHoldCount();
+ return syncName()+"[count "+hc+" [ add. "+addHC+", orig "+(hc-addHC)+
+ "], qsz "+sync.getQSz()+", owner "+threadName(sync.getOwner())+", add.owner "+tgSync.addOwnerToString()+"]";
+ }
+}