/**
* 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 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.common.util.InterruptSource;
import com.jogamp.junit.util.SingletonJunitCase;
import org.junit.FixMethodOrder;
import org.junit.runners.MethodSorters;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestRecursiveLock01 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;
static class ThreadStat {
ThreadStat() {
total = 0;
counter = 0;
}
long total; // ns
int counter;
}
private final RecursiveLock locker; // post
private int deferredThreadCount = 0; // synced
private final 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(final LockFactory.ImplType implType, final boolean fair) {
locker = LockFactory.createRecursiveLock(implType, fair);
}
private synchronized void incrDeferredThreadCount() {
deferredThreadCount++;
}
private synchronized void decrDeferredThreadCount() {
deferredThreadCount--;
}
public synchronized int getDeferredThreadCount() {
return deferredThreadCount;
}
public final void action1Direct(int l, final YieldMode yieldMode) {
if(DEBUG) {
System.err.print("<a1");
}
lock();
try {
if(DEBUG) {
System.err.print("+");
}
while(l>0) l--;
yield(yieldMode);
} finally {
if(DEBUG) {
System.err.print("-");
}
unlock();
if(DEBUG) {
System.err.println(">");
}
}
}
class Action2 implements Runnable {
int l;
YieldMode yieldMode;
Action2(final int l, final YieldMode yieldMode) {
this.l=l;
this.yieldMode=yieldMode;
incrDeferredThreadCount();
}
public void run() {
if(DEBUG) {
System.err.print("[a2");
}
lock();
try {
if(DEBUG) {
System.err.print("+");
}
while(l>0) l--;
yield(yieldMode);
} finally {
if(DEBUG) {
System.err.print("-");
}
unlock();
if(DEBUG) {
System.err.println("]");
}
}
decrDeferredThreadCount();
final int dc = getDeferredThreadCount();
if(0>dc) {
throw new InternalError("deferredThreads: "+dc);
}
}
}
public final void action2Deferred(final int l, final YieldMode yieldMode) {
final Action2 action2 = new Action2(l, yieldMode);
new InterruptSource.Thread(null, 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() {
locker.unlock();
}
public final boolean isLocked() {
return locker.isLocked();
}
public void stats(final boolean dump) {
long timeAllLocks=0;
int numAllLocks=0;
for(final Iterator<String> i = threadWaitMap.keySet().iterator(); i.hasNext(); ) {
final String name = i.next();
final 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(final Iterator<String> i = threadWaitMap.keySet().iterator(); i.hasNext(); numAllLocks++) {
final 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();
}
}
}
interface LockedObjectRunner extends Runnable {
void stop();
boolean isStopped();
void waitUntilStopped();
}
class LockedObjectRunner1 implements LockedObjectRunner {
volatile boolean shouldStop;
volatile boolean stopped;
LockedObject lo;
int loops;
int iloops;
YieldMode yieldMode;
public LockedObjectRunner1(final LockedObject lo, final int loops, final int iloops, final YieldMode yieldMode) {
this.lo = lo;
this.loops = loops;
this.iloops = iloops;
this.shouldStop = false;
this.stopped = false;
this.yieldMode = yieldMode;
}
public final void stop() {
shouldStop = true;
}
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) {
while(!shouldStop && loops>0) {
lo.action1Direct(iloops, yieldMode);
lo.action2Deferred(iloops, yieldMode);
loops--;
}
stopped = true;
this.notifyAll();
}
}
}
protected long testLockedObjectImpl(final LockFactory.ImplType implType, final boolean fair,
final int threadNum, final int loops, final int iloops, final YieldMode yieldMode) throws InterruptedException {
final long t0 = System.currentTimeMillis();
final LockedObject lo = new LockedObject(implType, fair);
final LockedObjectRunner[] runners = new LockedObjectRunner[threadNum];
final InterruptSource.Thread[] threads = new InterruptSource.Thread[threadNum];
int i;
for(i=0; i<threadNum; i++) {
runners[i] = new LockedObjectRunner1(lo, loops, iloops, yieldMode);
// String name = Thread.currentThread().getName()+"-ActionThread-"+i+"_of_"+threadNum;
final String name = "ActionThread-"+i+"_of_"+threadNum;
threads[i] = new InterruptSource.Thread( null, runners[i], name );
threads[i].start();
}
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 testLockedObjectThreading5x1000x10000N_Int01_Fair() throws InterruptedException {
final LockFactory.ImplType t = LockFactory.ImplType.Int01;
final boolean fair=true;
int threadNum=5;
int loops=1000;
int iloops=10000;
final 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_Java5_Fair() throws InterruptedException {
final LockFactory.ImplType t = LockFactory.ImplType.Java5;
final boolean fair=true;
int threadNum=5;
int loops=1000;
int iloops=10000;
final 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 {
final LockFactory.ImplType t = LockFactory.ImplType.Int01;
final boolean fair=false;
int threadNum=5;
int loops=1000;
int iloops=10000;
final 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_Java5_Unfair() throws InterruptedException {
final LockFactory.ImplType t = LockFactory.ImplType.Java5;
final boolean fair=false;
int threadNum=5;
int loops=1000;
int iloops=10000;
final 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 {
final LockFactory.ImplType t = LockFactory.ImplType.Int01;
final boolean fair=true;
int threadNum=25;
int loops=100;
int iloops=100;
final 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 {
final LockFactory.ImplType t = LockFactory.ImplType.Java5;
final boolean fair=true;
int threadNum=25;
int loops=100;
int iloops=100;
final 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 {
final LockFactory.ImplType t = LockFactory.ImplType.Int01;
final boolean fair=false;
int threadNum=25;
int loops=100;
int iloops=100;
final 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 {
final LockFactory.ImplType t = LockFactory.ImplType.Java5;
final boolean fair=false;
int threadNum=25;
int loops=100;
int iloops=100;
final 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 {
final LockFactory.ImplType t = LockFactory.ImplType.Int01;
final boolean fair=true;
int threadNum=25;
int loops=100;
int iloops=100;
final 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 testLockedObjectThreading25x100x100S_Java5() throws InterruptedException {
final LockFactory.ImplType t = LockFactory.ImplType.Java5;
final boolean fair=true;
int threadNum=25;
int loops=100;
int iloops=100;
final 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 {
final LockFactory.ImplType t = LockFactory.ImplType.Int01;
final boolean fair=true;
int threadNum=25;
int loops=100;
int iloops=100;
final 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_Fair() throws InterruptedException {
final LockFactory.ImplType t = LockFactory.ImplType.Java5;
final boolean fair=true;
int threadNum=25;
int loops=100;
int iloops=100;
final 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_Int01_Unfair() throws InterruptedException {
final LockFactory.ImplType t = LockFactory.ImplType.Int01;
final boolean fair=false;
int threadNum=25;
int loops=100;
int iloops=100;
final 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 {
final LockFactory.ImplType t = LockFactory.ImplType.Java5;
final boolean fair=false;
int threadNum=25;
int loops=100;
int iloops=100;
final 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(final String a) {
int i=0;
try {
i = Integer.parseInt(a);
} catch (final Exception ex) { ex.printStackTrace(); }
return i;
}
public static void main(final String args[]) throws IOException, InterruptedException {
final 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();
*/
}
}