Package com.sleepycat.je.latch

Source Code of com.sleepycat.je.latch.SharedLatch

/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002-2005
*      Sleepycat Software.  All rights reserved.
*
* $Id: SharedLatch.java,v 1.9 2005/08/11 19:28:26 cwl Exp $
*/

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;

/**
* 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.
*/
public class SharedLatch {

    /* Used for debugging */
    private static LatchTable latchTable = new LatchTable("SharedLatch");

    private String name = null;
    private List waiters = new ArrayList();
    private LatchStats stats = new LatchStats();
    /* The object that the latch protects. */
    private Object subject = null;
    private EnvironmentImpl env = null;

    /**
     * Create a latch with a backpointer to its subject (the object that is
     * protected by the latch along with a name (both for debugging purposes).
     */
    public SharedLatch(Object subject, String name, EnvironmentImpl env) {
  this.subject = subject;
  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;
    }

    /* Return the subject of this latch. */
    synchronized public Object getSubject() {
  return subject;
    }

    /**
     * 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 {
                owner = (Owner) waiters.get(index);
                if (owner.type == Owner.SHARED) {
                    stats.nAcquiresUpgrade++;
                    throw new LatchException(getNameString() +
                                             " upgrade not allowed");
                }
                assert owner.type == Owner.EXCLUSIVE;
            }
            if (waiters.size() == 1) {
                stats.nAcquiresNoWaiters++;
            } else {
                stats.nAcquiresWithContention++;
                while (waiters.get(0) != owner) {
                    wait();
                }
            }
            owner.nAcquires += 1;
            assert noteLatch(); // intentional side effect;
  } catch (InterruptedException e) {
      throw new RunRecoveryException(env, e);
  } 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;
            stats.nAcquireSharedSuccessful++;
            assert noteLatch(); // intentional side effect;
  } 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.
     *
     * @throws LatchNotHeldException if the latch is not currently held.
     */
    public synchronized void release()
  throws LatchNotHeldException {

        try {
            Thread thread = Thread.currentThread();
            int index = indexOf(thread);
            if (index < 0 || index > firstWriter()) {
                throw new LatchNotHeldException(getNameString() +
                                                " not held");
            }
            Owner owner = (Owner) waiters.get(index);
            owner.nAcquires -= 1;
            if (owner.nAcquires == 0) {
                waiters.remove(index);
                assert unNoteLatch(); // intentional side effect.
                notifyAll();
            }
            stats.nReleases++;
  } 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;
        }
    }

    /**
     * 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 synchronized Thread owner() {

  return (waiters.size() > 0) ? ((Owner) waiters.get(0)).thread : null;
    }

    /**
     * Return the number of threads waiting.
     *
     * @return the number of threads waiting for the latch.
     */
    synchronized int nWaiters() {

        int n = waiters.size();
        return (n > 0) ? (n - 1) : 0;
    }

    /**
     * Used for debugging latches.
     *
     * @return a LatchStats object with information about this latch.
     */
    LatchStats getLatchStats() {

  return stats;
    }

    /**
     * Formats a latch owner and waiters.
     */
    public synchronized String toString() {

        return latchTable.toString(name, owner(), waiters, 1);
    }

    /*
     * For concocting exception messages
     */
    private String getNameString() {

        return latchTable.getNameString(name);
    }

    /**
     * Only call under the assert system. This records latching by thread.
     */
    private boolean noteLatch()
  throws LatchException {

        return latchTable.noteLatch(this);
    }

    /**
     * Only call under the assert system. This records latching by thread.
     */
    private boolean unNoteLatch()
  throws LatchNotHeldException {

        return latchTable.unNoteLatch(this, name);
    }
   
    /**
     * Only call under the assert system. This records counts held latches.
     */
    static public int countLatchesHeld() {

        return latchTable.countLatchesHeld();
    }

    static public void dumpLatchesHeld() {

        System.out.println(latchesHeldToString());
    }

    static public String latchesHeldToString() {

        return latchTable.latchesHeldToString();
    }

    static public void clearNotes() {

        latchTable.clearNotes();
    }
}
TOP

Related Classes of com.sleepycat.je.latch.SharedLatch

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.