diff options
Diffstat (limited to 'src/java')
5 files changed, 674 insertions, 122 deletions
diff --git a/src/java/com/jogamp/common/util/locks/LockFactory.java b/src/java/com/jogamp/common/util/locks/LockFactory.java new file mode 100644 index 0000000..c374c2d --- /dev/null +++ b/src/java/com/jogamp/common/util/locks/LockFactory.java @@ -0,0 +1,61 @@ +/** + * 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; + +import jogamp.common.util.locks.RecursiveLockImpl01CompleteFair; +import jogamp.common.util.locks.RecursiveLockImpl01Unfairish; +import jogamp.common.util.locks.RecursiveLockImplJava5; + +public class LockFactory { + + public enum ImplType { + Int01(0), Java5(1); + + public final int id; + + ImplType(int id){ + this.id = id; + } + } + + /** default is ImplType.Int01, unfair'ish (fastest w/ least deviation) */ + public static RecursiveLock createRecursiveLock() { + return new RecursiveLockImpl01Unfairish(); + } + + public static RecursiveLock createRecursiveLock(ImplType t, boolean fair) { + switch(t) { + case Int01: + return fair ? new RecursiveLockImpl01CompleteFair() : new RecursiveLockImpl01Unfairish(); + case Java5: + return new RecursiveLockImplJava5(fair); + } + throw new InternalError("XXX"); + } + +} diff --git a/src/java/jogamp/common/util/locks/LockDebugUtil.java b/src/java/jogamp/common/util/locks/LockDebugUtil.java new file mode 100644 index 0000000..480b143 --- /dev/null +++ b/src/java/jogamp/common/util/locks/LockDebugUtil.java @@ -0,0 +1,80 @@ +/** + * 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 jogamp.common.util.locks; + +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import com.jogamp.common.util.locks.Lock; + +/** + * Functionality enabled if {@link Lock#DEBUG} is <code>true</code>. + */ +public class LockDebugUtil { + private static final ThreadLocal<ArrayList<Throwable>> tlsLockedStacks; + private static final List<Throwable> dummy; + static { + if(Lock.DEBUG) { + tlsLockedStacks = new ThreadLocal<ArrayList<Throwable>>(); + dummy = null; + } else { + tlsLockedStacks = null; + dummy = new ArrayList<Throwable>(0); + } + } + + public static List<Throwable> getRecursiveLockTrace() { + if(Lock.DEBUG) { + ArrayList<Throwable> ls = tlsLockedStacks.get(); + if(null == ls) { + ls = new ArrayList<Throwable>(); + tlsLockedStacks.set(ls); + } + return ls; + } else { + return dummy; + } + } + + public static void dumpRecursiveLockTrace(PrintStream out) { + if(Lock.DEBUG) { + List<Throwable> ls = getRecursiveLockTrace(); + if(null!=ls && ls.size()>0) { + int j=0; + out.println("TLSLockedStacks: locks "+ls.size()); + for(Iterator<Throwable> i=ls.iterator(); i.hasNext(); j++) { + out.print(j+": "); + i.next().printStackTrace(out); + } + } + } + } +} diff --git a/src/java/jogamp/common/util/locks/RecursiveLockImpl01CompleteFair.java b/src/java/jogamp/common/util/locks/RecursiveLockImpl01CompleteFair.java new file mode 100644 index 0000000..bbd0a94 --- /dev/null +++ b/src/java/jogamp/common/util/locks/RecursiveLockImpl01CompleteFair.java @@ -0,0 +1,312 @@ +/** + * 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.ArrayList; +import java.util.List; +import java.util.concurrent.locks.AbstractOwnableSynchronizer; + +import com.jogamp.common.util.locks.RecursiveLock; + +/** + * Reentrance locking toolkit, impl a complete fair FIFO scheduler + * + * <p> + * Sync object extends {@link AbstractOwnableSynchronizer}, hence monitoring is possible.</p> + */ +public class RecursiveLockImpl01CompleteFair implements RecursiveLock { + + private static class WaitingThread { + WaitingThread(Thread t) { + thread = t; + signaledByUnlock = false; + } + final Thread thread; + boolean signaledByUnlock; // if true, it's also removed from queue + } + + @SuppressWarnings("serial") + private static class Sync extends AbstractOwnableSynchronizer { + private Sync() { + super(); + } + private final Thread getOwner() { + return getExclusiveOwnerThread(); + } + private final void setOwner(Thread t) { + setExclusiveOwnerThread(t); + } + private final void setLockedStack(Throwable s) { + List<Throwable> ls = LockDebugUtil.getRecursiveLockTrace(); + if(s==null) { + ls.remove(lockedStack); + } else { + ls.add(s); + } + lockedStack = s; + } + // lock count by same thread + private int holdCount = 0; + // stack trace of the lock, only used if DEBUG + private Throwable lockedStack = null; + // waiting thread queue + final ArrayList<WaitingThread> queue = new ArrayList<WaitingThread>(); + } + private Sync sync = new Sync(); + + public RecursiveLockImpl01CompleteFair() { + } + + /** + * 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(sync) { + return sync.lockedStack; + } + } + + public final Thread getOwner() { + synchronized(sync) { + return sync.getOwner(); + } + } + + public final boolean isOwner() { + synchronized(sync) { + return isOwner(Thread.currentThread()); + } + } + + public final boolean isOwner(Thread thread) { + synchronized(sync) { + return sync.getOwner() == thread ; + } + } + + public final boolean isLocked() { + synchronized(sync) { + return null != sync.getOwner(); + } + } + + public final boolean isLockedByOtherThread() { + synchronized(sync) { + return null != sync.getOwner() && Thread.currentThread() != sync.getOwner() ; + } + } + + public final int getHoldCount() { + synchronized(sync) { + return sync.holdCount; + } + } + + public final void validateLocked() { + synchronized(sync) { + if ( Thread.currentThread() != sync.getOwner() ) { + if ( null == sync.getOwner() ) { + throw new RuntimeException(threadName(Thread.currentThread())+": Not locked: "+toString()); + } + if(null!=sync.lockedStack) { + sync.lockedStack.printStackTrace(); + } + throw new RuntimeException(Thread.currentThread()+": Not owner: "+toString()); + } + } + } + + public final void lock() { + synchronized(sync) { + try { + if(!tryLock(TIMEOUT)) { + if(null!=sync.lockedStack) { + sync.lockedStack.printStackTrace(); + } + throw new RuntimeException("Waited "+TIMEOUT+"ms for: "+toString()+" - "+threadName(Thread.currentThread())); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted", e); + } + } + } + + public final boolean tryLock(long timeout) throws InterruptedException { + synchronized(sync) { + final Thread cur = Thread.currentThread(); + if(TRACE_LOCK) { + System.err.println("+++ LOCK 0 "+toString()+", cur "+threadName(cur)); + } + if (sync.getOwner() == cur) { + ++sync.holdCount; + if(TRACE_LOCK) { + System.err.println("+++ LOCK XR "+toString()+", cur "+threadName(cur)); + } + return true; + } + + if ( sync.getOwner() != null || ( 0<timeout && 0<sync.queue.size() ) ) { + + if ( 0 >= timeout ) { + // locked by other thread and no waiting requested + return false; + } + + // enqueue at the start + WaitingThread wCur = new WaitingThread(cur); + sync.queue.add(0, wCur); + do { + final long t0 = System.currentTimeMillis(); + try { + sync.wait(timeout); + timeout -= System.currentTimeMillis() - t0; + } catch (InterruptedException e) { + if( !wCur.signaledByUnlock ) { + sync.queue.remove(wCur); // O(n) + throw e; // propagate interruption not send by unlock + } else if( cur != sync.getOwner() ) { + // Issued by unlock, but still locked by other thread + // + timeout -= System.currentTimeMillis() - t0; + + if(TRACE_LOCK) { + System.err.println("+++ LOCK 1 "+toString()+", cur "+threadName(cur)+", left "+timeout+" ms, signaled: "+wCur.signaledByUnlock); + } + + if(0 < timeout) { + // not timed out, re-enque - lock was 'stolen' + wCur.signaledByUnlock = false; + sync.queue.add(sync.queue.size(), wCur); + } + } // else: Issued by unlock, owning lock .. expected! + } + } while ( cur != sync.getOwner() && 0 < timeout ) ; + + if( 0 >= timeout ) { + // timed out + if(!wCur.signaledByUnlock) { + sync.queue.remove(wCur); // O(n) + } + if(cur == sync.getOwner()) { + sync.setOwner(null); + } + if(TRACE_LOCK || DEBUG) { + System.err.println("+++ LOCK XX "+toString()+", cur "+threadName(cur)+", left "+timeout+" ms"); + } + return false; + } + + ++sync.holdCount; + if(TRACE_LOCK) { + System.err.println("+++ LOCK X1 "+toString()+", cur "+threadName(cur)+", left "+timeout+" ms"); + } + } else { + ++sync.holdCount; + if(TRACE_LOCK) { + System.err.println("+++ LOCK X0 "+toString()+", cur "+threadName(cur)); + } + } + + sync.setOwner(cur); + if(DEBUG) { + sync.setLockedStack(new Throwable("Previously locked by "+toString())); + } + return true; + } + } + + + public final void unlock() { + synchronized(sync) { + unlock(null); + } + } + + public final void unlock(Runnable taskAfterUnlockBeforeNotify) { + synchronized(sync) { + validateLocked(); + final Thread cur = Thread.currentThread(); + + --sync.holdCount; + + if (sync.holdCount > 0) { + if(TRACE_LOCK) { + System.err.println("--- LOCK XR "+toString()+", cur "+threadName(cur)); + } + return; + } + + if(DEBUG) { + sync.setLockedStack(null); + } + if(null!=taskAfterUnlockBeforeNotify) { + taskAfterUnlockBeforeNotify.run(); + } + + if(sync.queue.size() > 0) { + // fair, wakeup the oldest one .. + // final WaitingThread oldest = queue.removeLast(); + final WaitingThread oldest = sync.queue.remove(sync.queue.size()-1); + sync.setOwner(oldest.thread); + + if(TRACE_LOCK) { + System.err.println("--- LOCK X1 "+toString()+", cur "+threadName(cur)+", signal: "+threadName(oldest.thread)); + } + + oldest.signaledByUnlock = true; + oldest.thread.interrupt(); // Propagate SecurityException if it happens + } else { + sync.setOwner(null); + if(TRACE_LOCK) { + System.err.println("--- LOCK X0 "+toString()+", cur "+threadName(cur)+", signal any"); + } + sync.notify(); + } + } + } + + public final int getQueueLength() { + synchronized(sync) { + return sync.queue.size(); + } + } + + public String toString() { + return syncName()+"[count "+sync.holdCount+ + ", qsz "+sync.queue.size()+", owner "+threadName(sync.getOwner())+"]"; + } + + private final String syncName() { + return "<"+Integer.toHexString(this.hashCode())+", "+Integer.toHexString(sync.hashCode())+">"; + } + private final String threadName(Thread t) { return null!=t ? "<"+t.getName()+">" : "<NULL>" ; } +} + diff --git a/src/java/jogamp/common/util/locks/RecursiveLockImpl01Unfairish.java b/src/java/jogamp/common/util/locks/RecursiveLockImpl01Unfairish.java index ad29f78..23e2c90 100644 --- a/src/java/jogamp/common/util/locks/RecursiveLockImpl01Unfairish.java +++ b/src/java/jogamp/common/util/locks/RecursiveLockImpl01Unfairish.java @@ -26,35 +26,54 @@ * or implied, of JogAmp Community. */ -package com.jogamp.common.util.locks; +package jogamp.common.util.locks; -import jogamp.common.Debug; -import java.security.AccessController; +import java.util.List; +import java.util.concurrent.locks.AbstractOwnableSynchronizer; -import java.util.LinkedList; +import com.jogamp.common.util.locks.RecursiveLock; /** - * Reentrance locking toolkit, impl a complete fair FIFO scheduler + * Reentrance locking toolkit, impl a non-complete fair FIFO scheduler. + * <p> + * Fair scheduling is not guaranteed due to the usage of {@link Object#notify()}, + * however new lock-applicants will wait if queue is not empty for {@link #lock()} + * and {@link #tryLock(long) tryLock}(timeout>0).</p> + * + * <p> + * Sync object extends {@link AbstractOwnableSynchronizer}, hence monitoring is possible.</p> */ -public class RecursiveLock implements LockExt { +public class RecursiveLockImpl01Unfairish implements RecursiveLock { - static class SyncData { - // owner of the lock - Thread owner = null; - // lock recursion - int recursionCount = 0; + @SuppressWarnings("serial") + private static class Sync extends AbstractOwnableSynchronizer { + private Sync() { + super(); + } + private final Thread getOwner() { + return getExclusiveOwnerThread(); + } + private final void setOwner(Thread t) { + setExclusiveOwnerThread(t); + } + private final void setLockedStack(Throwable s) { + List<Throwable> ls = LockDebugUtil.getRecursiveLockTrace(); + if(s==null) { + ls.remove(lockedStack); + } else { + ls.add(s); + } + lockedStack = s; + } + // lock count by same thread + private int holdCount = 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 Throwable lockedStack = null; + private int qsz = 0; } - private final 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() { + private Sync sync = new Sync(); + + public RecursiveLockImpl01Unfairish() { } /** @@ -63,131 +82,127 @@ public class RecursiveLock implements LockExt { * @see com.jogamp.common.util.locks.Lock#DEBUG */ public final Throwable getLockedStack() { - synchronized(sdata) { - return sdata.lockedStack; + synchronized(sync) { + return sync.lockedStack; } } public final Thread getOwner() { - synchronized(sdata) { - return sdata.owner; + synchronized(sync) { + return sync.getOwner(); } } public final boolean isOwner() { - return isOwner(Thread.currentThread()); + synchronized(sync) { + return isOwner(Thread.currentThread()); + } } public final boolean isOwner(Thread thread) { - synchronized(sdata) { - return sdata.owner == thread ; + synchronized(sync) { + return sync.getOwner() == thread ; } } public final boolean isLocked() { - synchronized(sdata) { - return null != sdata.owner; + synchronized(sync) { + return null != sync.getOwner(); } } public final boolean isLockedByOtherThread() { - synchronized(sdata) { - return null != sdata.owner && Thread.currentThread() != sdata.owner ; + synchronized(sync) { + return null != sync.getOwner() && Thread.currentThread() != sync.getOwner() ; } } - public final int getRecursionCount() { - synchronized(sdata) { - return sdata.recursionCount; + public final int getHoldCount() { + synchronized(sync) { + return sync.holdCount; } } 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(); + synchronized(sync) { + if ( Thread.currentThread() != sync.getOwner() ) { + if ( null == sync.getOwner() ) { + throw new RuntimeException(threadName(Thread.currentThread())+": Not locked: "+toString()); } - throw new RuntimeException(Thread.currentThread()+": Not owner, owner is "+sdata.owner); + if(null!=sync.lockedStack) { + sync.lockedStack.printStackTrace(); + } + throw new RuntimeException(Thread.currentThread()+": Not owner: "+toString()); } } } public final void lock() { - synchronized(sdata) { - if(!tryLock(TIMEOUT)) { - if(null!=sdata.lockedStack) { - sdata.lockedStack.printStackTrace(); + synchronized(sync) { + try { + if(!tryLock(TIMEOUT)) { + if(null!=sync.lockedStack) { + sync.lockedStack.printStackTrace(); + } + throw new RuntimeException("Waited "+TIMEOUT+"ms for: "+toString()+" - "+threadName(Thread.currentThread())); } - throw new RuntimeException("Waited "+TIMEOUT+"ms for: "+sdata.owner+" - "+Thread.currentThread()+", with recursionCount "+sdata.recursionCount+", lock: "+this+", qsz "+sdata.threadQueue.size()); + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted", e); } } } - public boolean tryLock(long maxwait) { - synchronized(sdata) { - Thread cur = Thread.currentThread(); + public final boolean tryLock(long timeout) throws InterruptedException { + synchronized(sync) { + final Thread cur = Thread.currentThread(); if(TRACE_LOCK) { - String msg = "LOCK 0 ["+this+"], recursions "+sdata.recursionCount+", cur "+cur+", owner "+sdata.owner; - System.err.println(msg); - //Throwable tt = new Throwable(msg); - //tt.printStackTrace(); + System.err.println("+++ LOCK 0 "+toString()+", cur "+threadName(cur)); } - if (sdata.owner == cur) { - ++sdata.recursionCount; + if (sync.getOwner() == cur) { + ++sync.holdCount; if(TRACE_LOCK) { - System.err.println("+++ LOCK 2 ["+this+"], recursions "+sdata.recursionCount+", "+cur); + System.err.println("+++ LOCK XR "+toString()+", cur "+threadName(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 + + if ( sync.getOwner() != null || ( 0<timeout && 0<sync.qsz ) ) { + + if ( 0 >= timeout ) { + // locked by other thread and no waiting requested return false; } - - boolean timedOut = false; + + ++sync.qsz; 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); - } - } + final long t0 = System.currentTimeMillis(); + sync.wait(timeout); + timeout -= System.currentTimeMillis() - t0; + } while (null != sync.getOwner() && 0 < timeout) ; + --sync.qsz; + + if( 0 >= timeout ) { + // timed out + if(TRACE_LOCK || DEBUG) { + System.err.println("+++ LOCK XX "+toString()+", cur "+threadName(cur)+", left "+timeout+" ms"); } - } while (null != sdata.owner && !timedOut) ; - - sdata.signaled = false; - - if(timedOut || null != sdata.owner) { return false; } - + + ++sync.holdCount; + if(TRACE_LOCK) { + System.err.println("+++ LOCK X1 "+toString()+", cur "+threadName(cur)+", left "+timeout+" ms"); + } + } else { + ++sync.holdCount; if(TRACE_LOCK) { - System.err.println("+++ LOCK 3 ["+this+"], recursions "+sdata.recursionCount+", qsz "+sdata.threadQueue.size()+", "+cur); + System.err.println("+++ LOCK X0 "+toString()+", cur "+threadName(cur)); } - } else if(TRACE_LOCK) { - System.err.println("+++ LOCK 1 ["+this+"], recursions "+sdata.recursionCount+", qsz "+sdata.threadQueue.size()+", "+cur); } - - sdata.owner = cur; + + sync.setOwner(cur); if(DEBUG) { - sdata.lockedStack = new Throwable("Previously locked by "+sdata.owner+", lock: "+this); + sync.setLockedStack(new Throwable("Previously locked by "+toString())); } return true; } @@ -195,51 +210,54 @@ public class RecursiveLock implements LockExt { public final void unlock() { - unlock(null); + synchronized(sync) { + unlock(null); + } } public final void unlock(Runnable taskAfterUnlockBeforeNotify) { - synchronized(sdata) { + synchronized(sync) { validateLocked(); - - if (sdata.recursionCount > 0) { - --sdata.recursionCount; + final Thread cur = Thread.currentThread(); + + --sync.holdCount; + + if (sync.holdCount > 0) { if(TRACE_LOCK) { - System.err.println("--- LOCK 1 ["+this+"], recursions "+sdata.recursionCount+", "+Thread.currentThread()); + System.err.println("--- LOCK XR "+toString()+", cur "+threadName(cur)); } return; } - sdata.owner = null; - sdata.lockedStack = null; + + sync.setOwner(null); + if(DEBUG) { + sync.setLockedStack(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()); + + if(TRACE_LOCK) { + System.err.println("--- LOCK X0 "+toString()+", cur "+threadName(cur)+", signal any"); } + sync.notify(); } } - public int getWaitingThreadQueueSize() { - synchronized(sdata) { - return sdata.threadQueue.size(); + public final int getQueueLength() { + synchronized(sync) { + return sync.qsz; } } + + public String toString() { + return syncName()+"[count "+sync.holdCount+ + ", qsz "+sync.qsz+", owner "+threadName(sync.getOwner())+"]"; + } + + private final String syncName() { + return "<"+Integer.toHexString(this.hashCode())+", "+Integer.toHexString(sync.hashCode())+">"; + } + private final String threadName(Thread t) { return null!=t ? "<"+t.getName()+">" : "<NULL>" ; } } diff --git a/src/java/jogamp/common/util/locks/RecursiveLockImplJava5.java b/src/java/jogamp/common/util/locks/RecursiveLockImplJava5.java new file mode 100644 index 0000000..b4e6ce0 --- /dev/null +++ b/src/java/jogamp/common/util/locks/RecursiveLockImplJava5.java @@ -0,0 +1,81 @@ +package jogamp.common.util.locks; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; + +import com.jogamp.common.util.locks.RecursiveLock; + +public class RecursiveLockImplJava5 implements RecursiveLock { + + volatile Thread owner = null; + ReentrantLock lock; + + public RecursiveLockImplJava5(boolean fair) { + lock = new ReentrantLock(fair); + } + + public void lock() { + try { + if(!tryLock(TIMEOUT)) { + throw new RuntimeException("Waited "+TIMEOUT+"ms for: "+threadName(owner)+" - "+threadName(Thread.currentThread())+", with count "+getHoldCount()+", lock: "+this); + } + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted", e); + } + owner = Thread.currentThread(); + } + + public boolean tryLock(long timeout) throws InterruptedException { + if(lock.tryLock(timeout, TimeUnit.MILLISECONDS)) { + owner = Thread.currentThread(); + return true; + } + return false; + } + + public void unlock() throws RuntimeException { + validateLocked(); + owner = null; + lock.unlock(); + } + + public boolean isLocked() { + return lock.isLocked(); + } + + public Thread getOwner() { + return owner; + } + + public boolean isLockedByOtherThread() { + return lock.isLocked() && !lock.isHeldByCurrentThread(); + } + + public boolean isOwner() { + return lock.isHeldByCurrentThread(); + } + + public boolean isOwner(Thread thread) { + return lock.isLocked() && owner == thread; + } + + public void validateLocked() { + if ( !lock.isHeldByCurrentThread() ) { + if ( !lock.isLocked() ) { + throw new RuntimeException(Thread.currentThread()+": Not locked"); + } else { + throw new RuntimeException(Thread.currentThread()+": Not owner, owner is "+owner); + } + } + } + + public int getHoldCount() { + return lock.getHoldCount(); + } + + public int getQueueLength() { + return lock.getQueueLength(); + } + + private String threadName(Thread t) { return null!=t ? "<"+t.getName()+">" : "<NULL>" ; } +} |