package com.sleepycat.je.latch; import java.util.ArrayList; 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.*; /** * This implementation is used in non-Java5 JVMs. In Java5 JVMs, the * Java5LockWrapperImpl class is used. The switch hitting is performed in * LatchSupport. * Simple thread-based non-transactional exclusive non-nestable latch. * <p> * Latches provide simple exclusive 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. * <p> * A latch can be acquire in wait or no-wait modes. In the former, the caller * will wait for any conflicting holders to release the latch. In the latter, * if the latch is not available, control returns to the caller immediately. */ public class LatchImpl implements Latch { private static final String DEFAULT_LATCH_NAME="LatchImpl"; private String name=null; private List waiters=null; private EnvironmentImpl env=null; private Thread owner=null; /** * Create a latch. */ public LatchImpl( String name, EnvironmentImpl env){ this.name=name; this.env=env; } /** * Create a latch with no name, more optimal for shortlived latches. */ public LatchImpl( EnvironmentImpl env){ this.env=env; this.name=DEFAULT_LATCH_NAME; } /** * Set the latch name, used for latches in objects instantiated from * the log. */ synchronized public void setName( String name){ this.name=name; } /** * Acquire a latch for exclusive/write access. * <p>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.</p> * @throws LatchException if the latch is already held by the calling * thread. * @throws RunRecoveryException if an InterruptedException exception * occurs. */ public void acquire() throws DatabaseException { try { Thread thread=Thread.currentThread(); LatchWaiter waitTarget=null; synchronized (this) { if (thread == owner) { this.hook422(); throw new LatchException(getNameString() + " already held"); } if (owner == null) { this.hook423(); owner=thread; } else { if (waiters == null) { waiters=new ArrayList(); } waitTarget=new LatchWaiter(thread); waiters.add(waitTarget); this.hook424(); } } if (waitTarget != null) { synchronized (waitTarget) { while (true) { if (waitTarget.active) { if (thread == owner) { break; } else { throw new DatabaseException("waitTarget.active but not owner"); } } else { waitTarget.wait(); if (thread == owner) { break; } else { continue; } } } } } assert noteLatch(); } catch ( InterruptedException e) { throw new RunRecoveryException(env,e); } finally { assert EnvironmentImpl.maybeForceYield(); } } /** * Acquire a latch for exclusive/write access, but do not block if it's not * available. * @return true if the latch was acquired, false if it is not available. * @throws LatchException if the latch is already held by the calling * thread. */ public synchronized boolean acquireNoWait() throws LatchException { try { Thread thread=Thread.currentThread(); if (thread == owner) { this.hook425(); throw new LatchException(getNameString() + " already held"); } if (owner == null) { owner=thread; this.hook426(); assert noteLatch(); return true; } else { this.hook427(); return false; } } finally { assert EnvironmentImpl.maybeForceYield(); } } /** * Release the latch. If there are other thread(s) waiting for the latch, * one is woken up and granted the latch. If the latch was not owned by * the caller, just return; */ public void releaseIfOwner(){ doRelease(false); } /** * Release the latch. If there are other thread(s) waiting for the latch, * they are woken up and granted the latch. * @throws LatchNotHeldException if the latch is not currently held. */ public void release() throws LatchNotHeldException { if (doRelease(true)) { throw new LatchNotHeldException(getNameString() + " not held"); } } /** * Do the work of releasing the latch. Wake up any waiters. * @returns true if this latch was not owned by the caller. */ private boolean doRelease( boolean checkHeld){ LatchWaiter newOwner=null; try { synchronized (this) { Thread thread=Thread.currentThread(); if (thread != owner) { return true; } if (waiters != null && waiters.size() > 0) { newOwner=(LatchWaiter)waiters.remove(0); owner=(Thread)newOwner.thread; } else { owner=null; } this.hook428(); assert unNoteLatch(checkHeld); } } finally { assert EnvironmentImpl.maybeForceYield(); } if (newOwner != null) { synchronized (newOwner) { newOwner.active=true; newOwner.notifyAll(); } } return false; } /** * Return true if the current thread holds this latch. * @return true if we hold this latch. False otherwise. */ public boolean isOwner(){ return Thread.currentThread() == owner; } /** * Used only for unit tests. * @return the thread that currently holds the latch for exclusive access. */ public Thread owner(){ return owner; } /** * Return the number of threads waiting. * @return the number of threads waiting for the latch. */ public synchronized int nWaiters(){ return (waiters != null) ? waiters.size() : 0; } /** * Formats a latch owner and waiters. */ public synchronized String toString(){ return LatchSupport.latchTable.toString(name,owner,waiters,0); } /** * For concocting exception messages */ 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( boolean checkHeld){ if (checkHeld) { return LatchSupport.latchTable.unNoteLatch(this,name); } else { LatchSupport.latchTable.unNoteLatch(this,name); return true; } } /** * Simple class that encapsulates a Thread to be 'notify()ed'. */ static private class LatchWaiter { boolean active; Thread thread; LatchWaiter( Thread thread){ this.thread=thread; active=false; } public String toString(){ return "<LatchWaiter: " + thread + ">"; } } protected void hook422() throws DatabaseException, InterruptedException { } protected void hook423() throws DatabaseException, InterruptedException { } protected void hook424() throws DatabaseException, InterruptedException { } protected void hook425() throws LatchException { } protected void hook426() throws LatchException { } protected void hook427() throws LatchException { } protected void hook428(){ } }