/** * 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.IOException; import org.junit.Assert; import org.junit.Test; import com.jogamp.common.os.Platform; import com.jogamp.common.util.InterruptSource; import com.jogamp.junit.util.SingletonJunitCase; import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TestRecursiveThreadGroupLock01 extends SingletonJunitCase { public enum YieldMode { NONE(0), YIELD(1), SLEEP(2); public final int id; YieldMode(final int id){ this.id = id; } } static void yield(final YieldMode mode) { switch(mode) { case YIELD: Thread.yield(); break; case SLEEP: try { Thread.sleep(10); } catch (final InterruptedException ie) { ie.printStackTrace(); } break; default: break; } } static class LockedObject { static final boolean DEBUG = false; private final RecursiveThreadGroupLock locker; // post private volatile int slaveCounter; public LockedObject() { locker = LockFactory.createRecursiveThreadGroupLock(); slaveCounter = 0; } public final void masterAction(final String tab, final String name, final Thread[] slaves, final int loops, final 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(final String tab, final String name, int loops, final int mark, final 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(final String tab, final String name, final LockedObject lo, final Thread[] slaves, final int loops, final int mark, final 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(final String tab, final String name, final LockedObject lo, final int loops, final int mark, final 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 (final 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(final LockFactory.ImplType implType, final boolean fair, final int slaveThreadNum, final int concurrentThreadNum, final int loops, final int mark, final YieldMode yieldMode) throws InterruptedException { final long t0 = System.currentTimeMillis(); final LockedObject lo = new LockedObject(); final LockedObjectRunner[] concurrentRunners = new LockedObjectRunner[concurrentThreadNum]; final LockedObjectRunner[] slaveRunners = new LockedObjectRunner[slaveThreadNum]; final InterruptSource.Thread[] concurrentThreads = new InterruptSource.Thread[concurrentThreadNum]; final InterruptSource.Thread[] slaveThreads = new InterruptSource.Thread[slaveThreadNum]; final InterruptSource.Thread[] noCoOwnerThreads = new InterruptSource.Thread[0]; int i; for(i=0; i<slaveThreadNum; i++) { slaveRunners[i] = new LockedObjectRunner1(" ", "s"+i, lo, loops, mark, yieldMode); final String name = "ActionThread-Slaves-"+i+"_of_"+slaveThreadNum; slaveThreads[i] = new InterruptSource.Thread( null, 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 InterruptSource.Thread( null, 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 { final LockFactory.ImplType t = LockFactory.ImplType.Int02ThreadGroup; final boolean fair=true; final int coOwnerThreadNum=2; final int threadNum=5; int loops=1000; int mark=10000; final 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(final String args[]) throws IOException, InterruptedException { final 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(); */ } }