Package com.lightcrafts.media.jai.util

Source Code of com.lightcrafts.media.jai.util.RWLock$UpgradeNotAllowed

/*
* $RCSfile: RWLock.java,v $
*
* Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
*
* Use is subject to license terms.
*
* $Revision: 1.1 $
* $Date: 2005/02/11 04:57:01 $
* $State: Exp $
*/
package com.lightcrafts.media.jai.util;

import java.util.LinkedList;
import java.util.ListIterator;

/**
* A class that provides a classic reader/writer lock functionality.
* This implementation is based on the JavaWorld article by Tark Modi
* titled "Lock on to an alternate synchronized mechanism" with some
* minor modifications.
*
* <p>This lock provides the following functionality : <ul>
*
* <li> Allows multiple threads read access while only a single
*  thread can have a write access.</li>
*
* <li> Allows a thread owning a read lock to "upgrade" to a write
*  lock.</li>
*
* <li> Allows a thread owning a write lock to "downgrade" to a read
*  lock.</li>
*
* <li> Allows the option to either block or specify a waitTime on
*  certain requests.
*
* </ul>
*/
public final class RWLock {

    // Should a read lock be allowed to upgrade to a write lock if the 
    // thread requesting the write lock already owns the read lock.    
    private boolean allowUpgrades;

    // Lock Types
    private static int READ  = 1;
    private static int WRITE = 2;

    private static int NOT_FOUND = -1;

    // A list of threads that are waiting to acquire a lock, waiting to
    // upgrade their lock or have been granted locks.
    private WaitingList waitingList = new WaitingList();

    private class WaitingList extends LinkedList {

  // Return index of the first writer in the list.
  int indexOfFirstWriter() {

      ListIterator iter = listIterator(0);
      int index = 0;

      while (iter.hasNext()) {
    if (((ReaderWriter)iter.next()).lockType == WRITE)
        return index;
    index++;
      }                                        

      return NOT_FOUND;
  }

  // Return the index of the last thread granted a lock
  int indexOfLastGranted() {

      ListIterator iter = listIterator(size());
      int index = size()-1;

      while (iter.hasPrevious()) {
    if (((ReaderWriter)iter.previous()).granted == true)
        return index;
    index--;
      }

      return NOT_FOUND;
  }

  // Return index of the element for this thread
  int findMe() {
      return indexOf(new ReaderWriter());
  }
    }

    // Element stored in the waiting list.
    private class ReaderWriter {

  Thread  key;    // set equal to the Thread ID
  int     lockType;  // READ or WRITE 
  int     lockCount;  // # of times lock was taken
  boolean granted;  // Has this thread been granted the lock

  ReaderWriter() {
      this(0);
  }

  ReaderWriter(int type) {

      key        = Thread.currentThread();
      lockType  = type;
      lockCount =  0;      
      granted   = false;
  }

        /**
         * Two elements are equal if their keys are equal. Note that
         * this does not make any instanceof checks since it assumes
         * that this implementation uses the this list a certain way.
         */
  public boolean equals(Object o) {
      return key == ((ReaderWriter)o).key;
  }
    }

    /**
     * Constructor.
     *
     * @param should upgrades of read locks to write locks be
     *         allowed ?
     */
    public RWLock(boolean allowUpgrades) {

  this.allowUpgrades = allowUpgrades;
    }

    /**
     * Constructor. Equivalent to <code>RWLock(true)</code>
     * which creates an upgradable reader-writer lock.
     */
    public RWLock() {
  this(true);
    }

    /**
     * Tries to obtain a read lock within the specified time.
     *
     * @param waitTime the time to wait in milliseconds while trying
     *      to obtain the read lock. A negative value indicates a
     *      blocking wait.
     *
     * @return true if the read lock was obtained without timing out.
     */
    public synchronized boolean forReading(int waitTime) {

  ReaderWriter element = null;

  // Is there a node for this thread already in the list?
  // If not, create a new one.
  int index = waitingList.findMe();

  if (index != NOT_FOUND) {
      element = (ReaderWriter)waitingList.get(index);

  } else {
      element = new ReaderWriter(READ);
      waitingList.add(element);
  }

  // If a lock has already been granted once just increment the
  // count and return. It does not matter whether the lock granted
  // initially was a READ or WRITE lock.
  if (element.lockCount > 0) {
      element.lockCount++;      
      return true;
  }

  long startTime = System.currentTimeMillis();
  long   endTime = waitTime + startTime;

  do {
      int nextWriter = waitingList.indexOfFirstWriter();      

      index = waitingList.findMe();

      // If there is no writer in front of me I get a
      // read lock. Otherwise, I wait...
      if ((nextWriter == NOT_FOUND) || (nextWriter > index)) {
    element.lockCount++;
    element.granted = true;
    return true;
      }

      // Non-blocking version, just return.  Do not need notifyAll
      // here since we added and removed the new element within the
      // same synchronized block and so no other thread ever saw
      // it.
      if (waitTime == 0) {
    waitingList.remove(element);                              
    return false;
      }

      // Now wait for the lock.
      try {
    // Negative wait time indicates a blocking wait.
    if (waitTime < 0) {
        wait();

    } else {
        long delta = endTime - System.currentTimeMillis();

        if (delta > 0) wait(delta);
    }
      } catch (InterruptedException e) {
    // Should never be here.
    // These messages are for debugging purposes only
    System.err.println(element.key.getName() +
      " : interrupted while waiting for a READ lock!");
      }

  } while ((waitTime < 0) || (endTime > System.currentTimeMillis()));

  // Could not get the lock and timed out.
  waitingList.remove(element);            

  // Important to notify all threads of our removal.
  notifyAll();

  // Failed to get lock.
  return false;      
    }

    /**
     * Tries to obtain a read lock. Equivalent to <code>forReading(-1)
     * </code> which will go into a blocking wait attempting to get a  
     * read lock.
     *
     * @return true, always.
     */
    public synchronized boolean forReading() {
  return forReading(-1);
    }

    /**
     * Tries to obtain a write lock withing the specified time. If the 
     * current thread owns a read lock, then it is upgraded to a write 
     * lock if upgrades are allowed, else, throws an UpgradeNotAllowed 
     * exception. If the lock is not owned by the current thread then  
     * it waits for a write lock for the specified time.               
     *
     * @param waitTime the time to wait in milliseconds while trying
     *      to obtain the write lock. A negative value indicates a
     *      blocking wait.
     *
     * @return true if the write lock was obtained without timing out.
     *
     * @throws UpgradeNotAllowed if current thread owns a read lock
     *      and upgrades are not allowed.
     */
    public synchronized boolean forWriting(int waitTime)
      throws UpgradeNotAllowed {

  ReaderWriter element = null;

  // Is there a node for this thread already in the list?
  // If not, create a new one.
  int index = waitingList.findMe();

  if (index != NOT_FOUND) {
      element = (ReaderWriter)waitingList.get(index);

  } else {
      element = new ReaderWriter(WRITE);
      waitingList.add(element);
  }

  // If the thread has a READ lock, we need to upgrade
  if ((element.granted == true) && (element.lockType == READ)) {

      try {
    if (!upgrade(waitTime))
        return false;
      }
      catch (LockNotHeld e) {
        return false;
      }
  }

  // If a lock has already been granted once just increment the
  // count and return.  At this point the thread either had a WRITE
  // lock or was upgraded to have one.
  if (element.lockCount > 0) {
      element.lockCount++;      
      return true;
  }

  long startTime = System.currentTimeMillis();
  long   endTime = waitTime + startTime;

  do {
      // If there are any readers in front of me
      // I have to wait...
      index = waitingList.findMe();

      // If I am the first one in the list I get the lock.
      if (index == 0) {
    element.lockCount++;
    element.granted = true;
    return true;
      }

      // Non-blocking version, just return.  Do not need notifyAll
      // here since we added and removed the new element within the
      // same synchronized block and so no other thread ever saw
      // it.
      if (waitTime == 0) {
    waitingList.remove(element);                              
    return false;
      }

      // Now wait for the lock.
      try {
    // Negative wait time indicates a blocking wait.
    if (waitTime < 0) {
        wait();

    } else {
        long delta = endTime - System.currentTimeMillis();

        if (delta > 0) wait(delta);
    }
      } catch (InterruptedException e) {
    // Should never be here.
    // These messages are for debugging purposes only
    System.err.println(element.key.getName() +
      " : interrupted while waiting for a WRITE lock!");
      }

  } while ((waitTime < 0) || (endTime > System.currentTimeMillis()));

  // Could not get the lock and timed out.
  waitingList.remove(element);                  

  // Notify all threads of our removal.
  notifyAll();

  // Failed to get the lock.
  return false;
    }

    /**
     * Tries to obtain a write lock. Equivalent to <code>forWriting(-1)
     * </code> which will go into a blocking wait attempting to get a  
     * write lock.
     *
     * @return true, always.
     *
     * @throws UpgradeNotAllowed if current thread owns a read lock
     *      and upgrades are not allowed.
     */
    public synchronized boolean forWriting()
      throws UpgradeNotAllowed {
  return forWriting(-1);
    }

    /**
     * Try to upgrade a write lock within the specified amount of time.
     * If the current thread does not own the lock or if upgrades      
     * are not allowed an exception is thrown. If the current thread   
     * already owns a write lock, nothing happens. Otherwise, threads  
     * already owning a read lock are given a higher priority in       
     * receiving the write lock than those that own no lock.           
     *
     * @param waitTime the time to wait in milliseconds while trying
     *      to upgrade to a write lock. A negative value indicates a
     *      blocking wait.
     *
     * @return true if the upgrade was performed without timing out.
     *
     * @throws LockNotHeld if the current thread does not own the lock
     * @throws UpgradeNotAllowed if the lock is not currently
     *      held by the owner.
     */
    public synchronized boolean upgrade(int waitTime)
      throws UpgradeNotAllowed, LockNotHeld {

  if (!allowUpgrades)
      throw new UpgradeNotAllowed();

  // We should already be in the list. If not, it is an error.
  int index = waitingList.findMe();

  if (index == NOT_FOUND)
      throw new LockNotHeld();

  // Get the actual element. If the lock type is already WRITE,
  // just return.
  ReaderWriter element = (ReaderWriter)waitingList.get(index);

  if (element.lockType == WRITE)
      return true;

  // What is the index of the last granted lock?      
  int lastGranted = waitingList.indexOfLastGranted();

  // lastGranted can not be NOT_FOUND, after all we are
  // granted a READ lock!
  if (lastGranted == NOT_FOUND)
      throw new LockNotHeld();

        // If we are not the last granted lock, then we will position
        // ourselves as such.
  if (index != lastGranted) {
      waitingList.remove(index);
      ListIterator iter = waitingList.listIterator(lastGranted);
      iter.add(element);
  }

  // We want new readers to think this is a write lock     
  // This is important so that they block i.e. do not get granted.
  // Since we are now waiting for a write lock it is
  // important that we were after all granted read locks.
  element.lockType = WRITE;

  long startTime = System.currentTimeMillis();
  long   endTime = waitTime + startTime;

  do {
      index = waitingList.findMe();

      if (index == 0) {
    return true;
      }

      // Non-blocking version. Do not need notifyAll here since
      // we changed the lock type back and forth within the same
      // synchronized block and so no other thread ever saw it.
      if (waitTime == 0) {

    // Back to READ type           
    element.lockType = READ;           

    // No need to readjust position since it does not matter
    // for already granted locks.
    return false;
      }

      // Now wait for the lock.
      try {
    // Negative wait time indicates a blocking wait.
    if (waitTime < 0) {
        wait();
    } else {
        long delta = endTime - System.currentTimeMillis();

        if (delta > 0) wait(delta);
    }
      } catch (InterruptedException e) {
    // Should never be here.
    // These messages are for debugging purposes only
    System.err.println(element.key.getName() +
      " : interrupted while waiting to UPGRADE lock!");
      }

  } while ((waitTime < 0) || (endTime > System.currentTimeMillis()));

  // We failed to upgrade. Go back to original lock type
  element.lockType = READ;

  // Important to notify all threads that we are back
  // to being a READ lock.
  notifyAll();      

  // Failed to upgrade.
  return false;      
    }

    /**
     * Tries to upgrade to a write lock. Equivalent to                 
     * <code>upgrade(-1)</code> which will go into a blocking wait    
     * attempting to get a upgrade to a write lock.
     *
     * @return true, always.
     *
     * @throws LockNotHeld if some other thread owns the lock
     * @throws UpgradeNotAllowed if the lock is not currently
     *      held by the owner.
     */
    public synchronized boolean upgrade()
      throws UpgradeNotAllowed, LockNotHeld {
  return upgrade(-1);
    }

    /**
     * Tries to downgrade a write lock to a read lock. If the current  
     * thread does not hold the lock an exception is thrown. If the    
     * current thread already owns a read lock, nothing happens. If it 
     * owns a write lock, then it is downgraded to a read lock. All    
     * threads waiting for a read lock can now get it only if there are
     * no other threads waiting for a write lock ahead of them.
     *
     * @return true if the downgrade was performed successfully.
     *
     * @throws LockNotHeld if the current thread does not own the lock.
     */
    public synchronized boolean downgrade() throws LockNotHeld {

  // We should already be in the list. If not, it is an error.      
  int index = waitingList.findMe();

  if (index == NOT_FOUND)
      throw new LockNotHeld();

  // Get the element for this thread
  ReaderWriter e = (ReaderWriter)waitingList.get(index);

  // Downgrade the WRITE lock and notify all threads of the change.
  if (e.lockType == WRITE) {
      e.lockType = READ;
      notifyAll();      
  }

  return true;
    }

    /**
     * Tries to relinquish the ownership of a lock. If the             
     * current thread does not hold the lock an exception is           
     * thrown. Note that every call to <code>forReading</code>         
     * and <code>forWriting</code> must have a corresponding           
     * <code>release()</code> call. However, <code>upgrade()</code>    
     * and <code>downgrade()</code> do not have corresponding          
     * <code>release()</code> calls.                                   
     *
     * @throws LockNotHeld if the current thread does not own the lock.
     */
    public synchronized void release() throws LockNotHeld {

  // We should already be in the list. If not, it is an error.      
  int index = waitingList.findMe();

  if (index == NOT_FOUND)
      throw new LockNotHeld();

  // Get the element for this thread
  ReaderWriter e = (ReaderWriter)waitingList.get(index);

  // If the lock count goes down to zero,
  // remove the lock and notify all threads of the change.
  if ((--e.lockCount) == 0) {
      waitingList.remove(index);           
      notifyAll();     
  }
    }

    /**
     * The exception thrown when trying to upgrade a lock when
     * the lock does not allow upgrades.
     */
    public class UpgradeNotAllowed extends RuntimeException {
    }

    /**
     * The exception thrown when trying to upgrade a lock when
     * the lock is not held by the current thread.
     */
    public class LockNotHeld extends RuntimeException {
    }
}
TOP

Related Classes of com.lightcrafts.media.jai.util.RWLock$UpgradeNotAllowed

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.