jogamp.common.util.locks.RecursiveLockImpl01Unfairish Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gluegen-rt-android Show documentation
Show all versions of gluegen-rt-android Show documentation
JNI binding generator (Android runtime)
/**
* 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.List;
import java.util.concurrent.locks.AbstractOwnableSynchronizer;
import com.jogamp.common.util.locks.RecursiveLock;
/**
* Reentrance locking toolkit, impl a non-complete fair FIFO scheduler.
*
* 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).
*
*
* Sync object extends {@link AbstractOwnableSynchronizer}, hence monitoring is possible.
*/
public class RecursiveLockImpl01Unfairish implements RecursiveLock {
/* package */ static interface Sync {
Thread getOwner();
boolean isOwner(Thread t);
void setOwner(Thread t);
Throwable getLockedStack();
void setLockedStack(Throwable s);
int getHoldCount();
void incrHoldCount(Thread t);
void decrHoldCount(Thread t);
int getQSz();
void incrQSz();
void decrQSz();
}
@SuppressWarnings("serial")
/* package */ static class SingleThreadSync extends AbstractOwnableSynchronizer implements Sync {
/* package */ SingleThreadSync() {
super();
}
@Override
public final Thread getOwner() {
return getExclusiveOwnerThread();
}
@Override
public boolean isOwner(Thread t) {
return getExclusiveOwnerThread()==t;
}
@Override
public final void setOwner(Thread t) {
setExclusiveOwnerThread(t);
}
@Override
public final Throwable getLockedStack() {
return lockedStack;
}
@Override
public final void setLockedStack(Throwable s) {
List ls = LockDebugUtil.getRecursiveLockTrace();
if(s==null) {
ls.remove(lockedStack);
} else {
ls.add(s);
}
lockedStack = s;
}
@Override
public final int getHoldCount() { return holdCount; }
@Override
public void incrHoldCount(Thread t) { holdCount++; }
@Override
public void decrHoldCount(Thread t) { holdCount--; }
@Override
public final int getQSz() { return qsz; }
@Override
public final void incrQSz() { qsz++; }
@Override
public final void decrQSz() { qsz--; }
/** lock count by same thread */
private int holdCount = 0;
/** queue size of waiting threads */
private int qsz = 0;
/** stack trace of the lock, only used if DEBUG */
private Throwable lockedStack = null;
}
protected final Sync sync;
public RecursiveLockImpl01Unfairish(Sync sync) {
this.sync = sync;
}
public RecursiveLockImpl01Unfairish() {
this(new SingleThreadSync());
}
/**
* 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 null
.
* @see com.jogamp.common.util.locks.Lock#DEBUG
*/
public final Throwable getLockedStack() {
synchronized(sync) {
return sync.getLockedStack();
}
}
@Override
public final Thread getOwner() {
synchronized(sync) {
return sync.getOwner();
}
}
@Override
public final boolean isOwner(Thread thread) {
synchronized(sync) {
return sync.isOwner(thread);
}
}
@Override
public final boolean isLocked() {
synchronized(sync) {
return null != sync.getOwner();
}
}
@Override
public final boolean isLockedByOtherThread() {
synchronized(sync) {
final Thread o = sync.getOwner();
return null != o && Thread.currentThread() != o ;
}
}
@Override
public final int getHoldCount() {
synchronized(sync) {
return sync.getHoldCount();
}
}
@Override
public final void validateLocked() throws RuntimeException {
synchronized(sync) {
if ( !sync.isOwner(Thread.currentThread()) ) {
if ( null == sync.getOwner() ) {
throw new RuntimeException(threadName(Thread.currentThread())+": Not locked: "+toString());
}
if(null!=sync.getLockedStack()) {
sync.getLockedStack().printStackTrace();
}
throw new RuntimeException(Thread.currentThread()+": Not owner: "+toString());
}
}
}
@Override
public final void lock() {
synchronized(sync) {
try {
if(!tryLock(TIMEOUT)) {
if(null!=sync.getLockedStack()) {
sync.getLockedStack().printStackTrace();
}
throw new RuntimeException("Waited "+TIMEOUT+"ms for: "+toString()+" - "+threadName(Thread.currentThread()));
}
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted", e);
}
}
}
@Override
public final boolean tryLock(long timeout) throws InterruptedException {
synchronized(sync) {
final Thread cur = Thread.currentThread();
if(TRACE_LOCK) {
System.err.println("+++ LOCK 0 "+toString()+", timeout "+timeout+" ms, cur "+threadName(cur));
}
if (sync.isOwner(cur)) {
sync.incrHoldCount(cur);
if(TRACE_LOCK) {
System.err.println("+++ LOCK XR "+toString()+", cur "+threadName(cur));
}
return true;
}
if ( sync.getOwner() != null || ( 0= timeout ) {
// locked by other thread and no waiting requested
if(TRACE_LOCK) {
System.err.println("+++ LOCK XY "+toString()+", cur "+threadName(cur)+", left "+timeout+" ms");
}
return false;
}
sync.incrQSz();
do {
final long t0 = System.currentTimeMillis();
sync.wait(timeout);
timeout -= System.currentTimeMillis() - t0;
} while (null != sync.getOwner() && 0 < timeout) ;
sync.decrQSz();
if( 0 >= timeout && sync.getOwner() != null ) {
// timed out
if(TRACE_LOCK) {
System.err.println("+++ LOCK XX "+toString()+", cur "+threadName(cur)+", left "+timeout+" ms");
}
return false;
}
if(TRACE_LOCK) {
System.err.println("+++ LOCK X1 "+toString()+", cur "+threadName(cur)+", left "+timeout+" ms");
}
} else if(TRACE_LOCK) {
System.err.println("+++ LOCK X0 "+toString()+", cur "+threadName(cur));
}
sync.setOwner(cur);
sync.incrHoldCount(cur);
if(DEBUG) {
sync.setLockedStack(new Throwable("Previously locked by "+toString()));
}
return true;
}
}
@Override
public final void unlock() {
synchronized(sync) {
unlock(null);
}
}
@Override
public void unlock(Runnable taskAfterUnlockBeforeNotify) {
synchronized(sync) {
validateLocked();
final Thread cur = Thread.currentThread();
sync.decrHoldCount(cur);
if (sync.getHoldCount() > 0) {
if(TRACE_LOCK) {
System.err.println("--- LOCK XR "+toString()+", cur "+threadName(cur));
}
return;
}
sync.setOwner(null);
if(DEBUG) {
sync.setLockedStack(null);
}
if(null!=taskAfterUnlockBeforeNotify) {
taskAfterUnlockBeforeNotify.run();
}
if(TRACE_LOCK) {
System.err.println("--- LOCK X0 "+toString()+", cur "+threadName(cur)+", signal any");
}
sync.notify();
}
}
@Override
public final int getQueueLength() {
synchronized(sync) {
return sync.getQSz();
}
}
@Override
public String toString() {
return syncName()+"[count "+sync.getHoldCount()+
", qsz "+sync.getQSz()+", owner "+threadName(sync.getOwner())+"]";
}
/* package */ final String syncName() {
return "<"+Integer.toHexString(this.hashCode())+", "+Integer.toHexString(sync.hashCode())+">";
}
/* package */ final String threadName(Thread t) { return null!=t ? "<"+t.getName()+">" : "" ; }
}