package com.sleepycat.je.latch;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.RunRecoveryException;
import com.sleepycat.je.dbi.EnvironmentImpl;
import de.ovgu.cide.jakutil.*;
/**
* Simple thread-based non-transactional reader-writer/shared-exclusive latch.
* Latches provide simple exclusive or shared transient locks on objects.
* Latches are expected to be held for short, defined periods of time. No
* deadlock detection is provided so it is the caller's responsibility to
* sequence latch acquisition in an ordered fashion to avoid deadlocks.
* Nested latches for a single thread are supported, but upgrading a shared
* latch to an exclusive latch is not. This implementation is based on the
* section Reader-Writer Locks in the book Java Threads by Scott Oaks, 2nd
* Edition, Chapter 8.
* This SharedLatch implementation is only used when Java 5
* ReentrantReadWriteLocks are not available.
*/
public class SharedLatchImpl implements SharedLatch {
private String name=null;
private List waiters=new ArrayList();
private EnvironmentImpl env=null;
private boolean noteLatch;
/**
* Create a shared latch.
*/
public SharedLatchImpl( String name, EnvironmentImpl env){
this.name=name;
this.env=env;
}
/**
* Set the latch name, used for latches in objects instantiated from the
* log.
*/
synchronized public void setName( String name){
this.name=name;
}
/**
* If noteLatch is true, then track latch usage in the latchTable.
*/
synchronized public void setNoteLatch( boolean noteLatch){
this.noteLatch=noteLatch;
}
/**
* Acquire a latch for exclusive/write access. Nesting is allowed, that
* is, the latch may be acquired more than once by the same thread for
* exclusive access. However, if the thread already holds the latch for
* shared access, it cannot be upgraded and LatchException will be thrown.
* Wait for the latch if some other thread is holding it. If there are
* threads waiting for access, they will be granted the latch on a FIFO
* basis. When the method returns, the latch is held for exclusive access.
* @throws LatchException if the latch is already held by the current
* thread for shared access.
* @throws RunRecoveryException if an InterruptedException exception
* occurs.
*/
public synchronized void acquireExclusive() throws DatabaseException {
try {
Thread thread=Thread.currentThread();
int index=indexOf(thread);
Owner owner;
if (index < 0) {
owner=new Owner(thread,Owner.EXCLUSIVE);
waiters.add(owner);
}
else {
throw new LatchException(getNameString() + " reentrancy/upgrade not allowed");
}
if (waiters.size() == 1) {
this.hook429();
}
else {
this.hook430();
while (waiters.get(0) != owner) {
wait();
}
}
owner.nAcquires+=1;
assert (noteLatch ? noteLatch() : true);
}
catch ( InterruptedException e) {
throw new RunRecoveryException(env,e);
}
finally {
assert EnvironmentImpl.maybeForceYield();
}
}
public boolean acquireExclusiveNoWait() throws DatabaseException {
try {
Thread thread=Thread.currentThread();
int index=indexOf(thread);
if (index < 0) {
if (waiters.size() == 0) {
Owner owner=new Owner(thread,Owner.EXCLUSIVE);
waiters.add(owner);
owner.nAcquires+=1;
this.hook431();
assert (noteLatch ? noteLatch() : true);
return true;
}
else {
return false;
}
}
else {
throw new LatchException(getNameString() + " reentrancy/upgrade not allowed");
}
}
finally {
assert EnvironmentImpl.maybeForceYield();
}
}
/**
* Acquire a latch for shared/read access. Nesting is allowed, that is,
* the latch may be acquired more than once by the same thread.
* @throws RunRecoveryException if an InterruptedException exception
* occurs.
*/
public synchronized void acquireShared() throws DatabaseException {
try {
Thread thread=Thread.currentThread();
int index=indexOf(thread);
Owner owner;
if (index < 0) {
owner=new Owner(thread,Owner.SHARED);
waiters.add(owner);
}
else {
owner=(Owner)waiters.get(index);
}
while (indexOf(thread) > firstWriter()) {
wait();
}
owner.nAcquires+=1;
this.hook432();
assert (noteLatch ? noteLatch() : true);
}
catch ( InterruptedException e) {
throw new RunRecoveryException(env,e);
}
finally {
assert EnvironmentImpl.maybeForceYield();
}
}
/**
* Release an exclusive or shared latch. If there are other thread(s)
* waiting for the latch, they are woken up and granted the latch.
*/
public synchronized void release() throws LatchNotHeldException {
try {
Thread thread=Thread.currentThread();
int index=indexOf(thread);
if (index < 0 || index > firstWriter()) {
return;
}
Owner owner=(Owner)waiters.get(index);
owner.nAcquires-=1;
if (owner.nAcquires == 0) {
waiters.remove(index);
assert (noteLatch ? unNoteLatch() : true);
notifyAll();
}
this.hook433();
}
finally {
assert EnvironmentImpl.maybeForceYield();
}
}
/**
* Returns the index of the first Owner for the given thread, or -1 if
* none.
*/
private int indexOf( Thread thread){
Iterator i=waiters.iterator();
for (int index=0; i.hasNext(); index+=1) {
Owner owner=(Owner)i.next();
if (owner.thread == thread) {
return index;
}
}
return -1;
}
/**
* Returns the index of the first Owner waiting for a write lock, or
* Integer.MAX_VALUE if none.
*/
private int firstWriter(){
Iterator i=waiters.iterator();
for (int index=0; i.hasNext(); index+=1) {
Owner owner=(Owner)i.next();
if (owner.type == Owner.EXCLUSIVE) {
return index;
}
}
return Integer.MAX_VALUE;
}
/**
* Holds the state of a single owner thread.
*/
private static class Owner {
static final int SHARED=0;
static final int EXCLUSIVE=1;
Thread thread;
int type;
int nAcquires;
Owner( Thread thread, int type){
this.thread=thread;
this.type=type;
}
}
private String getNameString(){
return LatchSupport.latchTable.getNameString(name);
}
/**
* Only call under the assert system. This records latching by thread.
*/
private boolean noteLatch() throws LatchException {
return LatchSupport.latchTable.noteLatch(this);
}
/**
* Only call under the assert system. This records latching by thread.
*/
private boolean unNoteLatch() throws LatchNotHeldException {
return LatchSupport.latchTable.unNoteLatch(this,name);
}
public synchronized boolean isWriteLockedByCurrentThread(){
if (waiters.size() > 0) {
Owner curOwner=(Owner)waiters.get(0);
return (curOwner.thread == Thread.currentThread() && curOwner.type == Owner.EXCLUSIVE);
}
else {
return false;
}
}
protected void hook429() throws DatabaseException, InterruptedException {
}
protected void hook430() throws DatabaseException, InterruptedException {
}
protected void hook431() throws DatabaseException {
}
protected void hook432() throws DatabaseException, InterruptedException {
}
protected void hook433() throws LatchNotHeldException {
}
}