diff options
6 files changed, 1059 insertions, 199 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>" ; } +} diff --git a/src/junit/com/jogamp/common/util/locks/TestRecursiveLock01.java b/src/junit/com/jogamp/common/util/locks/TestRecursiveLock01.java index 96282f4..3654428 100644 --- a/src/junit/com/jogamp/common/util/locks/TestRecursiveLock01.java +++ b/src/junit/com/jogamp/common/util/locks/TestRecursiveLock01.java @@ -28,26 +28,39 @@ package com.jogamp.common.util.locks; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import org.junit.Assert; import org.junit.Test; import com.jogamp.common.os.Platform; public class TestRecursiveLock01 { - static final int YIELD_NONE = 0; - static final int YIELD_YIELD = 1; - static final int YIELD_SLEEP = 2; + public enum YieldMode { + NONE(0), YIELD(1), SLEEP(2); + + public final int id; - static void yield(int mode) { + YieldMode(int id){ + this.id = id; + } + } + + static void yield(YieldMode mode) { switch(mode) { - case YIELD_YIELD: + case YIELD: Thread.yield(); break; - case YIELD_SLEEP: + case SLEEP: try { - Thread.sleep(20); + Thread.sleep(10); } catch (InterruptedException ie) { ie.printStackTrace(); } @@ -60,13 +73,39 @@ public class TestRecursiveLock01 { static class LockedObject { static final boolean DEBUG = false; + + static class ThreadStat { + ThreadStat() { + total = 0; + counter = 0; + } + long total; // ns + int counter; + } + + private RecursiveLock locker; // post + private int deferredThreadCount = 0; // synced + private Map<String, ThreadStat> threadWaitMap = Collections.synchronizedMap(new HashMap<String, ThreadStat>()); // locked + + long avrg; // ns, post + long max_deviation; // ns, post + long min_deviation; // ns, post - public LockedObject() { - locker = new RecursiveLock(); - actionCounter = 0; + public LockedObject(LockFactory.ImplType implType, boolean fair) { + locker = LockFactory.createRecursiveLock(implType, fair); } - public final void action1Direct(int l, int yieldMode) { + private synchronized void incrDeferredThreadCount() { + deferredThreadCount++; + } + private synchronized void decrDeferredThreadCount() { + deferredThreadCount--; + } + public synchronized int getDeferredThreadCount() { + return deferredThreadCount; + } + + public final void action1Direct(int l, YieldMode yieldMode) { if(DEBUG) { System.err.print("<a1"); } @@ -76,7 +115,6 @@ public class TestRecursiveLock01 { System.err.print("+"); } while(l>0) l--; - actionCounter++; yield(yieldMode); } finally { if(DEBUG) { @@ -90,11 +128,15 @@ public class TestRecursiveLock01 { } class Action2 implements Runnable { - int l, yieldMode; - Action2(int l, int yieldMode) { + int l; + YieldMode yieldMode; + + Action2(int l, YieldMode yieldMode) { this.l=l; this.yieldMode=yieldMode; + incrDeferredThreadCount(); } + public void run() { if(DEBUG) { System.err.print("[a2"); @@ -105,8 +147,7 @@ public class TestRecursiveLock01 { System.err.print("+"); } while(l>0) l--; - actionCounter++; - yield(yieldMode); + yield(yieldMode); } finally { if(DEBUG) { System.err.print("-"); @@ -116,16 +157,32 @@ public class TestRecursiveLock01 { System.err.println("]"); } } - } + decrDeferredThreadCount(); + final int dc = getDeferredThreadCount(); + if(0>dc) { + throw new InternalError("deferredThreads: "+dc); + } + } } - public final void action2Deferred(int l, int yieldMode) { - Thread thread = new Thread(new Action2(l, yieldMode), Thread.currentThread()+"-action2Deferred"); - thread.start(); + public final void action2Deferred(int l, YieldMode yieldMode) { + Action2 action2 = new Action2(l, yieldMode); + new Thread(action2, Thread.currentThread().getName()+"-deferred").start(); } public final void lock() { + long td = System.nanoTime(); locker.lock(); + td = System.nanoTime() - td; + + final String cur = Thread.currentThread().getName(); + ThreadStat ts = threadWaitMap.get(cur); + if(null == ts) { + ts = new ThreadStat(); + } + ts.total += td; + ts.counter++; + threadWaitMap.put(cur, ts); } public final void unlock() { @@ -135,26 +192,60 @@ public class TestRecursiveLock01 { public final boolean isLocked() { return locker.isLocked(); } + + public void stats(boolean dump) { + long timeAllLocks=0; + int numAllLocks=0; + for(Iterator<String> i = threadWaitMap.keySet().iterator(); i.hasNext(); ) { + String name = i.next(); + ThreadStat ts = threadWaitMap.get(name); + timeAllLocks += ts.total; + numAllLocks += ts.counter; + } + max_deviation = Long.MIN_VALUE; + min_deviation = Long.MAX_VALUE; + avrg = timeAllLocks/numAllLocks; + if(dump) { + System.err.printf("Average: %6d ms / %6d times = %8d ns", + timeAllLocks/1000000, numAllLocks, avrg); + System.err.println(); + } + for(Iterator<String> i = threadWaitMap.keySet().iterator(); i.hasNext(); numAllLocks++) { + String name = i.next(); + final ThreadStat ts = threadWaitMap.get(name); + final long a = ts.total/ts.counter; + final long d = a - avrg; + max_deviation = Math.max(max_deviation, d); + min_deviation = Math.min(min_deviation, d); + if(dump) { + System.err.printf("%-35s %12d ns / %6d times, a %8d ns, d %8d ns", + name, ts.total, ts.counter, a, d); + System.err.println(); + } + } + if(dump) { + System.err.printf("Deviation (min/max): [%8d ns - %8d ns]", min_deviation, max_deviation); + System.err.println(); + } + } - RecursiveLock locker; - int actionCounter; } - interface LockedObjectIf extends Runnable { + interface LockedObjectRunner extends Runnable { void stop(); boolean isStopped(); - int remaining(); + void waitUntilStopped(); } - class LockedObjectAction1 implements LockedObjectIf { + class LockedObjectRunner1 implements LockedObjectRunner { volatile boolean shouldStop; volatile boolean stopped; LockedObject lo; - volatile int loops; + int loops; int iloops; - int yieldMode; + YieldMode yieldMode; - public LockedObjectAction1(LockedObject lo, int loops, int iloops, int yieldMode) { + public LockedObjectRunner1(LockedObject lo, int loops, int iloops, YieldMode yieldMode) { this.lo = lo; this.loops = loops; this.iloops = iloops; @@ -170,88 +261,292 @@ public class TestRecursiveLock01 { public final boolean isStopped() { return stopped; } - - public final int remaining() { - return loops; + + public void waitUntilStopped() { + synchronized(this) { + while(!stopped) { + try { + this.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } public void run() { - while(!shouldStop && loops>0) { - lo.action1Direct(iloops, yieldMode); - lo.action2Deferred(iloops, yieldMode); - loops--; + synchronized(this) { + while(!shouldStop && loops>0) { + lo.action1Direct(iloops, yieldMode); + lo.action2Deferred(iloops, yieldMode); + loops--; + } + stopped = true; + this.notifyAll(); } - stopped = true; } } - protected void testLockedObjectImpl(int threadNum, int loops, int iloops, int yieldMode) throws InterruptedException { - LockedObject lo = new LockedObject(); - LockedObjectIf[] runners = new LockedObjectIf[threadNum]; + protected long testLockedObjectImpl(LockFactory.ImplType implType, boolean fair, + int threadNum, int loops, int iloops, YieldMode yieldMode) throws InterruptedException { + final long t0 = System.currentTimeMillis(); + LockedObject lo = new LockedObject(implType, fair); + LockedObjectRunner[] runners = new LockedObjectRunner[threadNum]; Thread[] threads = new Thread[threadNum]; int i; for(i=0; i<threadNum; i++) { - runners[i] = new LockedObjectAction1(lo, loops, iloops, yieldMode); - threads[i] = new Thread( runners[i], Thread.currentThread()+"-ActionThread-"+i+"/"+threadNum); + runners[i] = new LockedObjectRunner1(lo, loops, iloops, yieldMode); + // String name = Thread.currentThread().getName()+"-ActionThread-"+i+"_of_"+threadNum; + String name = "ActionThread-"+i+"_of_"+threadNum; + threads[i] = new Thread( runners[i], name ); threads[i].start(); } - int active; - do { - active = threadNum; - for(i=0; i<threadNum; i++) { - if(runners[i].isStopped()) { - active--; - } - } - yield(yieldMode); - } while(0<active); + for( i=0; i<threadNum; i++ ) { + runners[i].waitUntilStopped(); + } + while( 0 < lo.getDeferredThreadCount() ) { + Thread.sleep(100); + } + Assert.assertEquals(0, lo.locker.getHoldCount()); + Assert.assertEquals(false, lo.locker.isLocked()); + Assert.assertEquals(0, lo.getDeferredThreadCount()); + + final long dt = System.currentTimeMillis()-t0; + lo.stats(false); + + System.err.println(); + final String fair_S = fair ? "fair " : "unfair" ; + System.err.printf("---- TestRecursiveLock01.testLockedObjectThreading: i %5s, %s, threads %2d, loops-outter %6d, loops-inner %6d, yield %5s - dt %6d ms, avrg %8d ns, deviation [ %8d .. %8d ] ns", + implType, fair_S, threadNum, loops, iloops, yieldMode, dt, lo.avrg, lo.min_deviation, lo.max_deviation); + System.err.println(); + return dt; } - // @Test - public void testLockedObjectThreading2x10000() throws InterruptedException { - System.err.println("++++ TestRecursiveLock01.testLockedObjectThreading2x10000"); + @Test + public void testLockedObjectThreading5x1000x10000N_Int01_Fair() throws InterruptedException { + LockFactory.ImplType t = LockFactory.ImplType.Int01; + boolean fair=true; + int threadNum=5; + int loops=1000; + int iloops=10000; + YieldMode yieldMode=YieldMode.NONE; + if( Platform.getCPUFamily() == Platform.CPUFamily.ARM ) { - testLockedObjectImpl(2, 5, 10, YIELD_NONE); - } else { - testLockedObjectImpl(2, 10000, 10000, YIELD_NONE); + threadNum=5; loops=5; iloops=10; } - System.err.println("---- TestRecursiveLock01.testLockedObjectThreading2x10000"); + + testLockedObjectImpl(t, fair, threadNum, loops, iloops, yieldMode); } @Test - public void testLockedObjectThreading25x25Yield() throws InterruptedException { - System.err.println("++++ TestRecursiveLock01.testLockedObjectThreading25x25-Yield"); + public void testLockedObjectThreading5x1000x10000N_Java5_Fair() throws InterruptedException { + LockFactory.ImplType t = LockFactory.ImplType.Java5; + boolean fair=true; + int threadNum=5; + int loops=1000; + int iloops=10000; + YieldMode yieldMode=YieldMode.NONE; + + if( Platform.getCPUFamily() == Platform.CPUFamily.ARM ) { + threadNum=5; loops=5; iloops=10; + } + + testLockedObjectImpl(t, fair, threadNum, loops, iloops, yieldMode); + } + + @Test + public void testLockedObjectThreading5x1000x10000N_Int01_Unfair() throws InterruptedException { + LockFactory.ImplType t = LockFactory.ImplType.Int01; + boolean fair=false; + int threadNum=5; + int loops=1000; + int iloops=10000; + YieldMode yieldMode=YieldMode.NONE; + if( Platform.getCPUFamily() == Platform.CPUFamily.ARM ) { - testLockedObjectImpl(2, 5, 10, YIELD_YIELD); - } else { - testLockedObjectImpl(25, 25, 100, YIELD_YIELD); + threadNum=5; loops=5; iloops=10; } - System.err.println("---- TestRecursiveLock01.testLockedObjectThreading25x25-Yield"); + + testLockedObjectImpl(t, fair, threadNum, loops, iloops, yieldMode); + } + + @Test + public void testLockedObjectThreading5x1000x10000N_Java5_Unfair() throws InterruptedException { + LockFactory.ImplType t = LockFactory.ImplType.Java5; + boolean fair=false; + int threadNum=5; + int loops=1000; + int iloops=10000; + YieldMode yieldMode=YieldMode.NONE; + + if( Platform.getCPUFamily() == Platform.CPUFamily.ARM ) { + threadNum=5; loops=5; iloops=10; + } + + testLockedObjectImpl(t, fair, threadNum, loops, iloops, yieldMode); + } + + @Test + public void testLockedObjectThreading25x100x100Y_Int01_Fair() throws InterruptedException { + LockFactory.ImplType t = LockFactory.ImplType.Int01; + boolean fair=true; + int threadNum=25; + int loops=100; + int iloops=100; + YieldMode yieldMode=YieldMode.YIELD; + + if( Platform.getCPUFamily() == Platform.CPUFamily.ARM ) { + threadNum=5; loops=5; iloops=10; + } + + testLockedObjectImpl(t, fair, threadNum, loops, iloops, yieldMode); + } + + @Test + public void testLockedObjectThreading25x100x100Y_Java5_Fair() throws InterruptedException { + LockFactory.ImplType t = LockFactory.ImplType.Java5; + boolean fair=true; + int threadNum=25; + int loops=100; + int iloops=100; + YieldMode yieldMode=YieldMode.YIELD; + + if( Platform.getCPUFamily() == Platform.CPUFamily.ARM ) { + threadNum=5; loops=5; iloops=10; + } + + testLockedObjectImpl(t, fair, threadNum, loops, iloops, yieldMode); + } + + @Test + public void testLockedObjectThreading25x100x100Y_Int01_Unair() throws InterruptedException { + LockFactory.ImplType t = LockFactory.ImplType.Int01; + boolean fair=false; + int threadNum=25; + int loops=100; + int iloops=100; + YieldMode yieldMode=YieldMode.YIELD; + + if( Platform.getCPUFamily() == Platform.CPUFamily.ARM ) { + threadNum=5; loops=5; iloops=10; + } + + testLockedObjectImpl(t, fair, threadNum, loops, iloops, yieldMode); + } + + @Test + public void testLockedObjectThreading25x100x100Y_Java5_Unfair() throws InterruptedException { + LockFactory.ImplType t = LockFactory.ImplType.Java5; + boolean fair=false; + int threadNum=25; + int loops=100; + int iloops=100; + YieldMode yieldMode=YieldMode.YIELD; + + if( Platform.getCPUFamily() == Platform.CPUFamily.ARM ) { + threadNum=5; loops=5; iloops=10; + } + + testLockedObjectImpl(t, fair, threadNum, loops, iloops, yieldMode); + } + + // @Test + public void testLockedObjectThreading25x100x100S_Int01_Fair() throws InterruptedException { + LockFactory.ImplType t = LockFactory.ImplType.Int01; + boolean fair=true; + int threadNum=25; + int loops=100; + int iloops=100; + YieldMode yieldMode=YieldMode.SLEEP; + + if( Platform.getCPUFamily() == Platform.CPUFamily.ARM ) { + threadNum=5; loops=5; iloops=10; + } + + testLockedObjectImpl(t, fair, threadNum, loops, iloops, yieldMode); } // @Test - public void testLockedObjectThreading25x25Sleep() throws InterruptedException { - System.err.println("++++ TestRecursiveLock01.testLockedObjectThreading25x25-Sleep"); + public void testLockedObjectThreading25x100x100S_Java5() throws InterruptedException { + LockFactory.ImplType t = LockFactory.ImplType.Java5; + boolean fair=true; + int threadNum=25; + int loops=100; + int iloops=100; + YieldMode yieldMode=YieldMode.SLEEP; + + if( Platform.getCPUFamily() == Platform.CPUFamily.ARM ) { + threadNum=5; loops=5; iloops=10; + } + + testLockedObjectImpl(t, fair, threadNum, loops, iloops, yieldMode); + } + + @Test + public void testLockedObjectThreading25x100x100N_Int01_Fair() throws InterruptedException { + LockFactory.ImplType t = LockFactory.ImplType.Int01; + boolean fair=true; + int threadNum=25; + int loops=100; + int iloops=100; + YieldMode yieldMode=YieldMode.NONE; + if( Platform.getCPUFamily() == Platform.CPUFamily.ARM ) { - testLockedObjectImpl(2, 5, 10, YIELD_SLEEP); - } else { - testLockedObjectImpl(25, 25, 100, YIELD_SLEEP); + threadNum=5; loops=5; iloops=10; } - System.err.println("---- TestRecursiveLock01.testLockedObjectThreading25x25-Sleep"); + + testLockedObjectImpl(t, fair, threadNum, loops, iloops, yieldMode); } @Test - public void testLockedObjectThreading25x25None() throws InterruptedException { - System.err.println("++++ TestRecursiveLock01.testLockedObjectThreading25x25-None"); + public void testLockedObjectThreading25x100x100N_Java5_Fair() throws InterruptedException { + LockFactory.ImplType t = LockFactory.ImplType.Java5; + boolean fair=true; + int threadNum=25; + int loops=100; + int iloops=100; + YieldMode yieldMode=YieldMode.NONE; + if( Platform.getCPUFamily() == Platform.CPUFamily.ARM ) { - testLockedObjectImpl(2, 5, 10, YIELD_NONE); - } else { - testLockedObjectImpl(25, 25, 100, YIELD_NONE); + threadNum=5; loops=5; iloops=10; } - System.err.println("---- TestRecursiveLock01.testLockedObjectThreading25x25-None"); + testLockedObjectImpl(t, fair, threadNum, loops, iloops, yieldMode); + } + + @Test + public void testLockedObjectThreading25x100x100N_Int01_Unfair() throws InterruptedException { + LockFactory.ImplType t = LockFactory.ImplType.Int01; + boolean fair=false; + int threadNum=25; + int loops=100; + int iloops=100; + YieldMode yieldMode=YieldMode.NONE; + + if( Platform.getCPUFamily() == Platform.CPUFamily.ARM ) { + threadNum=5; loops=5; iloops=10; + } + + testLockedObjectImpl(t, fair, threadNum, loops, iloops, yieldMode); + } + + @Test + public void testLockedObjectThreading25x100x100N_Java5_Unfair() throws InterruptedException { + LockFactory.ImplType t = LockFactory.ImplType.Java5; + boolean fair=false; + int threadNum=25; + int loops=100; + int iloops=100; + YieldMode yieldMode=YieldMode.NONE; + + if( Platform.getCPUFamily() == Platform.CPUFamily.ARM ) { + threadNum=5; loops=5; iloops=10; + } + + testLockedObjectImpl(t, fair, threadNum, loops, iloops, yieldMode); } static int atoi(String a) { @@ -262,9 +557,22 @@ public class TestRecursiveLock01 { return i; } - public static void main(String args[]) throws IOException { + public static void main(String args[]) throws IOException, InterruptedException { String tstname = TestRecursiveLock01.class.getName(); org.junit.runner.JUnitCore.main(tstname); + + /** + BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in)); + System.err.println("Press enter to continue"); + System.err.println(stdin.readLine()); + TestRecursiveLock01 t = new TestRecursiveLock01(); + t.testLockedObjectThreading5x1000x10000N_Int01_Unfair(); + + t.testLockedObjectThreading5x1000x10000N_Int01_Fair(); + t.testLockedObjectThreading5x1000x10000N_Java5_Fair(); + t.testLockedObjectThreading5x1000x10000N_Int01_Unfair(); + t.testLockedObjectThreading5x1000x10000N_Java5_Unfair(); + */ } } |