2 *******************************************************************************
\r
3 * Copyright (C) 2001-2006, International Business Machines Corporation and *
\r
4 * others. All Rights Reserved. *
\r
5 *******************************************************************************
\r
7 package com.ibm.icu.impl;
\r
9 // See Allan Holub's 1999 column in JavaWorld, and Doug Lea's code for RWLocks with writer preference.
\r
13 * <p>A simple Reader/Writer lock. This assumes that there will
\r
14 * be little writing contention. It also doesn't allow
\r
15 * active readers to acquire and release a write lock, or
\r
16 * deal with priority inversion issues.</p>
\r
18 * <p>Access to the lock should be enclosed in a try/finally block
\r
19 * in order to ensure that the lock is always released in case of
\r
20 * exceptions:<br><pre>
\r
22 * lock.acquireRead();
\r
23 * // use service protected by the lock
\r
26 * lock.releaseRead();
\r
30 * <p>The lock provides utility methods getStats and clearStats
\r
31 * to return statistics on the use of the lock.</p>
\r
33 public class ICURWLock {
\r
34 private Object writeLock = new Object();
\r
35 private Object readLock = new Object();
\r
36 private int wwc; // waiting writers
\r
37 private int rc; // active readers, -1 if there's an active writer
\r
38 private int wrc; // waiting readers
\r
40 private Stats stats = new Stats(); // maybe don't init to start...
\r
43 * Internal class used to gather statistics on the RWLock.
\r
45 public final static class Stats {
\r
47 * Number of times read access granted (read count).
\r
52 * Number of times concurrent read access granted (multiple read count).
\r
57 * Number of times blocked for read (waiting reader count).
\r
59 public int _wrc; // wait for read
\r
62 * Number of times write access granted (writer count).
\r
67 * Number of times blocked for write (waiting writer count).
\r
74 private Stats(int rc, int mrc, int wrc, int wc, int wwc) {
\r
82 private Stats(Stats rhs) {
\r
83 this(rhs._rc, rhs._mrc, rhs._wrc, rhs._wc, rhs._wwc);
\r
87 * Return a string listing all the stats.
\r
89 public String toString() {
\r
90 return " rc: " + _rc +
\r
99 * Reset the stats. Returns existing stats, if any.
\r
101 public synchronized Stats resetStats() {
\r
102 Stats result = stats;
\r
103 stats = new Stats();
\r
108 * Clear the stats (stop collecting stats). Returns existing stats, if any.
\r
110 public synchronized Stats clearStats() {
\r
111 Stats result = stats;
\r
117 * Return a snapshot of the current stats. This does not reset the stats.
\r
119 public synchronized Stats getStats() {
\r
120 return stats == null ? null : new Stats(stats);
\r
125 private synchronized boolean gotRead() {
\r
127 if (stats != null) {
\r
129 if (rc > 1) ++stats._mrc;
\r
134 private synchronized boolean getRead() {
\r
135 if (rc >= 0 && wwc == 0) {
\r
142 private synchronized boolean retryRead() {
\r
143 if (stats != null) ++stats._wrc;
\r
144 if (rc >= 0 && wwc == 0) {
\r
151 private synchronized boolean finishRead() {
\r
153 return (0 == --rc && wwc > 0);
\r
155 throw new IllegalStateException("no current reader to release");
\r
158 private synchronized boolean gotWrite() {
\r
160 if (stats != null) {
\r
166 private synchronized boolean getWrite() {
\r
174 private synchronized boolean retryWrite() {
\r
175 if (stats != null) ++stats._wwc;
\r
183 private static final int NOTIFY_NONE = 0;
\r
184 private static final int NOTIFY_WRITERS = 1;
\r
185 private static final int NOTIFY_READERS = 2;
\r
187 private synchronized int finishWrite() {
\r
191 return NOTIFY_WRITERS;
\r
192 } else if (wrc > 0) {
\r
193 return NOTIFY_READERS;
\r
195 return NOTIFY_NONE;
\r
198 throw new IllegalStateException("no current writer to release");
\r
202 * <p>Acquire a read lock, blocking until a read lock is
\r
203 * available. Multiple readers can concurrently hold the read
\r
206 * <p>If there's a writer, or a waiting writer, increment the
\r
207 * waiting reader count and block on this. Otherwise
\r
208 * increment the active reader count and return. Caller must call
\r
209 * releaseRead when done (for example, in a finally block).</p>
\r
211 public void acquireRead() {
\r
215 synchronized (readLock) {
\r
222 catch (InterruptedException e) {
\r
229 * <p>Release a read lock and return. An error will be thrown
\r
230 * if a read lock is not currently held.</p>
\r
232 * <p>If this is the last active reader, notify the oldest
\r
233 * waiting writer. Call when finished with work
\r
234 * controlled by acquireRead.</p>
\r
236 public void releaseRead() {
\r
237 if (finishRead()) {
\r
238 synchronized (writeLock) {
\r
239 writeLock.notify();
\r
245 * <p>Acquire the write lock, blocking until the write lock is
\r
246 * available. Only one writer can acquire the write lock, and
\r
247 * when held, no readers can acquire the read lock.</p>
\r
249 * <p>If there are no readers and no waiting writers, mark as
\r
250 * having an active writer and return. Otherwise, add a lock to the
\r
251 * end of the waiting writer list, and block on it. Caller
\r
252 * must call releaseWrite when done (for example, in a finally
\r
255 public void acquireWrite() {
\r
259 synchronized (writeLock) {
\r
262 if (retryWrite()) {
\r
266 catch (InterruptedException e) {
\r
273 * <p>Release the write lock and return. An error will be thrown
\r
274 * if the write lock is not currently held.</p>
\r
276 * <p>If there are waiting readers, make them all active and
\r
277 * notify all of them. Otherwise, notify the oldest waiting
\r
278 * writer, if any. Call when finished with work controlled by
\r
279 * acquireWrite.</p>
\r
281 public void releaseWrite() {
\r
282 switch (finishWrite()) {
\r
283 case NOTIFY_WRITERS:
\r
284 synchronized (writeLock) {
\r
285 writeLock.notify();
\r
288 case NOTIFY_READERS:
\r
289 synchronized (readLock) {
\r
290 readLock.notifyAll();
\r