    001/* ========================================================================
    002 * JCommon : a free general purpose class library for the Java(tm) platform
    003 * ========================================================================
    004 *
    005 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
    006 *
    007 * Project Info:  http://www.jfree.org/jcommon/index.html
    008 *
    009 * This library is free software; you can redistribute it and/or modify it
    010 * under the terms of the GNU Lesser General Public License as published by
    011 * the Free Software Foundation; either version 2.1 of the License, or
    012 * (at your option) any later version.
    013 *
    014 * This library is distributed in the hope that it will be useful, but
    015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017 * License for more details.
    018 *
    019 * You should have received a copy of the GNU Lesser General Public
    020 * License along with this library; if not, write to the Free Software
    021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022 * USA.
    023 *
    024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
    025 * in the United States and other countries.]
    026 *
    027 * ---------------------
    028 * ReaderWriterLock.java
    029 * ---------------------
    030 *
    031 * $Id: ReaderWriterLock.java,v 1.3 2005/10/18 13:18:34 mungady Exp $
    032 *
    033 * Changes
    034 * -------
    035 * 29-Jan-2003 : Added standard header (DG);
    036 *
    037 */
    038
    039package org.jfree.threads;
    040
    041import java.util.ArrayList;
    042import java.util.Iterator;
    043
    044/**
    045 * A reader-writer lock from "Java Threads" by Scott Oak and Henry Wong.
    046 *
    047 * @author Scott Oak and Henry Wong
    048 */
    049public class ReaderWriterLock {
    050
    051    /**
    052     * A node for the waiting list.
    053     *
    054     * @author Scott Oak and Henry Wong
    055     */
    056    private static class ReaderWriterNode {
    057
    058        /** A reader. */
    059        protected static final int READER = 0;
    060
    061        /** A writer. */
    062        protected static final int WRITER = 1;
    063
    064        /** The thread. */
    065        protected Thread t;
    066
    067        /** The state. */
    068        protected int state;
    069
    070        /** The number of acquires.*/
    071        protected int nAcquires;
    072
    073        /**
    074         * Creates a new node.
    075         *
    076         * @param t  the thread.
    077         * @param state  the state.
    078         */
    079        private ReaderWriterNode(final Thread t, final int state) {
    080            this.t = t;
    081            this.state = state;
    082            this.nAcquires = 0;
    083        }
    084
    085    }
    086
    087    /** The waiting threads. */
    088    private ArrayList waiters;
    089
    090    /**
    091     * Default constructor.
    092     */
    093    public ReaderWriterLock() {
    094        this.waiters = new ArrayList();
    095    }
    096
    097    /**
    098     * Grab the read lock.
    099     */
    100    public synchronized void lockRead() {
    101        final ReaderWriterNode node;
    102        final Thread me = Thread.currentThread();
    103        final int index = getIndex(me);
    104        if (index == -1) {
    105            node = new ReaderWriterNode(me, ReaderWriterNode.READER);
    106            this.waiters.add(node);
    107        }
    108        else {
    109            node = (ReaderWriterNode) this.waiters.get(index);
    110        }
    111        while (getIndex(me) > firstWriter()) {
    112            try {
    113                wait();
    114            }
    115            catch (Exception e) {
    116                System.err.println("ReaderWriterLock.lockRead(): exception.");
    117                System.err.print(e.getMessage());
    118            }
    119        }
    120        node.nAcquires++;
    121    }
    122
    123    /**
    124     * Grab the write lock.
    125     */
    126    public synchronized void lockWrite() {
    127        final ReaderWriterNode node;
    128        final Thread me = Thread.currentThread();
    129        final int index = getIndex(me);
    130        if (index == -1) {
    131            node = new ReaderWriterNode(me, ReaderWriterNode.WRITER);
    132            this.waiters.add(node);
    133        }
    134        else {
    135            node = (ReaderWriterNode) this.waiters.get(index);
    136            if (node.state == ReaderWriterNode.READER) {
    137                throw new IllegalArgumentException("Upgrade lock");
    138            }
    139            node.state = ReaderWriterNode.WRITER;
    140        }
    141        while (getIndex(me) != 0) {
    142            try {
    143                wait();
    144            }
    145            catch (Exception e) {
    146                System.err.println("ReaderWriterLock.lockWrite(): exception.");
    147                System.err.print(e.getMessage());
    148            }
    149        }
    150        node.nAcquires++;
    151    }
    152
    153    /**
    154     * Unlock.
    155     */
    156    public synchronized void unlock() {
    157
    158        final ReaderWriterNode node;
    159        final Thread me = Thread.currentThread();
    160        final int index = getIndex(me);
    161        if (index > firstWriter()) {
    162            throw new IllegalArgumentException("Lock not held");
    163        }
    164        node = (ReaderWriterNode) this.waiters.get(index);
    165        node.nAcquires--;
    166        if (node.nAcquires == 0) {
    167            this.waiters.remove(index);
    168        }
    169        notifyAll();
    170    }
    171
    172    /**
    173     * Returns the index of the first waiting writer.
    174     *
    175     * @return The index.
    176     */
    177    private int firstWriter() {
    178        final Iterator e = this.waiters.iterator();
    179        int index = 0;
    180        while (e.hasNext()) {
    181            final ReaderWriterNode node = (ReaderWriterNode) e.next();
    182            if (node.state == ReaderWriterNode.WRITER) {
    183                return index;
    184            }
    185            index += 1;
    186        }
    187        return Integer.MAX_VALUE;
    188    }
    189
    190    /**
    191     * Returns the index of a thread.
    192     *
    193     * @param t  the thread.
    194     *
    195     * @return The index.
    196     */
    197    private int getIndex(final Thread t) {
    198        final Iterator e = this.waiters.iterator();
    199        int index = 0;
    200        while (e.hasNext()) {
    201            final ReaderWriterNode node = (ReaderWriterNode) e.next();
    202            if (node.t == t) {
    203                return index;
    204            }
    205            index += 1;
    206        }
    207        return -1;
    208    }
    209
    210}