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    
039    package org.jfree.threads;
040    
041    import java.util.ArrayList;
042    import 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     */
049    public 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    }