/** * 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.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 threadWaitMap = Collections.synchronizedMap(new HashMap()); // 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("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 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() { locker.unlock(); } public final boolean isLocked() { return locker.isLocked(); } public void stats(final boolean dump) { long timeAllLocks=0; int numAllLocks=0; for(final Iterator 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 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 Thread[] threads = new Thread[threadNum]; int i; for(i=0; i