diff options
5 files changed, 686 insertions, 2 deletions
diff --git a/make/scripts/runtest.sh b/make/scripts/runtest.sh index ec4701d..34f3bc6 100755 --- a/make/scripts/runtest.sh +++ b/make/scripts/runtest.sh @@ -55,6 +55,7 @@ function onetest() { #onetest com.jogamp.common.util.TestVersionInfo 2>&1 | tee -a $LOG #onetest com.jogamp.common.util.TestIteratorIndexCORE 2>&1 | tee -a $LOG #onetest com.jogamp.common.util.locks.TestRecursiveLock01 2>&1 | tee -a $LOG +onetest com.jogamp.common.util.locks.TestRecursiveThreadGroupLock01 2>&1 | tee -a $LOG #onetest com.jogamp.common.util.TestArrayHashSet01 2>&1 | tee -a $LOG #onetest com.jogamp.common.util.IntIntHashMapTest 2>&1 | tee -a $LOG #onetest com.jogamp.common.util.IntObjectHashMapTest 2>&1 | tee -a $LOG @@ -64,7 +65,7 @@ function onetest() { #onetest com.jogamp.common.nio.TestPointerBufferEndian 2>&1 | tee -a $LOG #onetest com.jogamp.common.nio.TestStructAccessorEndian 2>&1 | tee -a $LOG #onetest com.jogamp.gluegen.test.junit.generation.Test1p1JavaEmitter 2>&1 | tee -a $LOG -onetest com.jogamp.gluegen.test.junit.generation.Test1p2ProcAddressEmitter 2>&1 | tee -a $LOG +#onetest com.jogamp.gluegen.test.junit.generation.Test1p2ProcAddressEmitter 2>&1 | tee -a $LOG #onetest com.jogamp.common.util.TestPlatform01 2>&1 | tee -a $LOG #onetest com.jogamp.common.util.TestRunnableTask01 2>&1 | tee -a $LOG #onetest com.jogamp.common.util.TestIOUtil01 2>&1 | tee -a $LOG 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()+"]"; + } +} diff --git a/src/junit/com/jogamp/common/util/locks/TestRecursiveThreadGroupLock01.java b/src/junit/com/jogamp/common/util/locks/TestRecursiveThreadGroupLock01.java new file mode 100644 index 0000000..2eecce1 --- /dev/null +++ b/src/junit/com/jogamp/common/util/locks/TestRecursiveThreadGroupLock01.java @@ -0,0 +1,327 @@ +/** + * 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 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; +import com.jogamp.junit.util.JunitTracer; + +public class TestRecursiveThreadGroupLock01 extends JunitTracer { + + public enum YieldMode { + NONE(0), YIELD(1), SLEEP(2); + + public final int id; + + YieldMode(int id){ + this.id = id; + } + } + + static void yield(YieldMode mode) { + switch(mode) { + case YIELD: + Thread.yield(); + break; + case SLEEP: + try { + Thread.sleep(10); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + break; + default: + break; + } + + } + + static class LockedObject { + static final boolean DEBUG = false; + + private RecursiveThreadGroupLock locker; // post + private volatile int slaveCounter; + + public LockedObject() { + locker = LockFactory.createRecursiveThreadGroupLock(); + slaveCounter = 0; + } + + public final void masterAction(String tab, String name, Thread[] slaves, int loops, int mark, final YieldMode yieldMode) { + locker.lock(); + if(DEBUG) { + System.err.println(tab+"<"+name+" c "+slaveCounter); + } + Assert.assertTrue(mark>loops); + Assert.assertTrue(loops*loops>mark); + try { + if(slaveCounter<mark) { + for(int i=0; i<slaves.length; i++) { + locker.addOwner(slaves[i]); + slaves[i].start(); + } + while(slaveCounter<mark) { + yield(yieldMode); + } + } + } finally { + if(DEBUG) { + System.err.println(tab+" "+name+" c "+slaveCounter+">"); + } + // Implicit waits until all slaves got off the lock + locker.unlock(); + } + } + + public final void slaveAction(String tab, String name, int loops, int mark, YieldMode yieldMode) { + if(slaveCounter>=mark) { + if(DEBUG) { + System.err.println(tab+"["+name+" c "+slaveCounter+" - NOP]"); + } + return; + } + locker.lock(); + if(DEBUG) { + System.err.println(tab+"["+name+" c "+slaveCounter); + } + Assert.assertTrue(mark>loops); + Assert.assertTrue(loops*loops>mark); + try { + while(loops>0 && slaveCounter<mark) { + slaveCounter++; + loops--; + } + /** + while(slaveCounter<mark) { + slaveCounter++; + } */ + yield(yieldMode); + } finally { + if(DEBUG) { + System.err.println(tab+" "+name+" c "+slaveCounter+"]"); + } + locker.unlock(); + } + } + + public final boolean isLocked() { + return locker.isLocked(); + } + + } + + interface LockedObjectRunner extends Runnable { + void stop(); + boolean isStarted(); + boolean isStopped(); + void waitUntilStopped(); + } + + class LockedObjectRunner1 implements LockedObjectRunner { + volatile boolean shouldStop; + volatile boolean stopped; + volatile boolean started; + String tab, name; + LockedObject lo; + Thread[] slaves; + int loops; + int mark; + YieldMode yieldMode; + + /** master constructor */ + public LockedObjectRunner1(String tab, String name, LockedObject lo, Thread[] slaves, int loops, int mark, YieldMode yieldMode) { + this.tab = tab; + this.name = name; + this.lo = lo; + this.slaves = slaves; + this.loops = loops; + this.mark = mark; + this.shouldStop = false; + this.stopped = false; + this.yieldMode = yieldMode; + Assert.assertTrue(mark>loops); + Assert.assertTrue(loops*loops>mark); + } + + /** slave constructor */ + public LockedObjectRunner1(String tab, String name, LockedObject lo, int loops, int mark, YieldMode yieldMode) { + this.tab = tab; + this.name = name; + this.lo = lo; + this.slaves = null; // slave + this.loops = loops; + this.mark = mark; + this.shouldStop = false; + this.stopped = false; + this.yieldMode = yieldMode; + Assert.assertTrue(mark>loops); + Assert.assertTrue(loops*loops>mark); + } + + public final void stop() { + shouldStop = true; + } + + public final boolean isStarted() { + return started; + } + public final boolean isStopped() { + return stopped; + } + + public void waitUntilStopped() { + synchronized(this) { + while(!stopped) { + try { + this.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + } + + public void run() { + synchronized(this) { + started = true; + for(int i=0; !shouldStop && i<loops; i++) { + if(null != slaves) { + lo.masterAction(tab, name, slaves, loops, mark, yieldMode); + } else { + lo.slaveAction(tab, name, loops, mark, yieldMode); + } + } + stopped = true; + this.notifyAll(); + } + } + } + + protected long testLockedObjectImpl(LockFactory.ImplType implType, boolean fair, + int slaveThreadNum, int concurrentThreadNum, + int loops, int mark, YieldMode yieldMode) throws InterruptedException { + final long t0 = System.currentTimeMillis(); + LockedObject lo = new LockedObject(); + LockedObjectRunner[] concurrentRunners = new LockedObjectRunner[concurrentThreadNum]; + LockedObjectRunner[] slaveRunners = new LockedObjectRunner[slaveThreadNum]; + Thread[] concurrentThreads = new Thread[concurrentThreadNum]; + Thread[] slaveThreads = new Thread[slaveThreadNum]; + Thread[] noCoOwnerThreads = new Thread[0]; + int i; + + for(i=0; i<slaveThreadNum; i++) { + slaveRunners[i] = new LockedObjectRunner1(" ", "s"+i, lo, loops, mark, yieldMode); + String name = "ActionThread-Slaves-"+i+"_of_"+slaveThreadNum; + slaveThreads[i] = new Thread( slaveRunners[i], name ); + } + for(i=0; i<concurrentThreadNum; i++) { + String name; + if(i==0) { + concurrentRunners[i] = new LockedObjectRunner1("", "M0", lo, slaveThreads, loops, mark, yieldMode); + name = "ActionThread-Master-"+i+"_of_"+concurrentThreadNum; + } else { + concurrentRunners[i] = new LockedObjectRunner1(" ", "O"+i, lo, noCoOwnerThreads, loops, mark, yieldMode); + name = "ActionThread-Others-"+i+"_of_"+concurrentThreadNum; + } + concurrentThreads[i] = new Thread( concurrentRunners[i], name ); + concurrentThreads[i].start(); + if(i==0) { + // master thread w/ slaves shall start first + while(!concurrentRunners[i].isStarted()) { + Thread.sleep(100); + } + } + } + + for( i=0; i<slaveThreadNum; i++ ) { + slaveRunners[i].waitUntilStopped(); + } + for( i=0; i<concurrentThreadNum; i++ ) { + concurrentRunners[i].waitUntilStopped(); + } + Assert.assertEquals(0, lo.locker.getHoldCount()); + Assert.assertEquals(false, lo.locker.isLocked()); + + final long dt = System.currentTimeMillis()-t0; + + 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", + implType, fair_S, concurrentThreadNum, loops, mark, yieldMode, dt); + System.err.println(); + return dt; + } + + @Test + public void testTwoThreadsInGroup() throws InterruptedException { + LockFactory.ImplType t = LockFactory.ImplType.Int02ThreadGroup; + boolean fair=true; + int coOwnerThreadNum=2; + int threadNum=5; + int loops=1000; + int mark=10000; + YieldMode yieldMode=YieldMode.YIELD; + + if( Platform.getCPUFamily() == Platform.CPUFamily.ARM ) { + loops=5; mark=10; + } + + testLockedObjectImpl(t, fair, coOwnerThreadNum, threadNum, loops, mark, yieldMode); + } + + public static void main(String args[]) throws IOException, InterruptedException { + String tstname = TestRecursiveThreadGroupLock01.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(); + */ + } + +} |