001: /**
002: *******************************************************************************
003: * Copyright (C) 2001-2006, International Business Machines Corporation and *
004: * others. All Rights Reserved. *
005: *******************************************************************************
006: */package com.ibm.icu.impl;
007:
008: // See Allan Holub's 1999 column in JavaWorld, and Doug Lea's code for RWLocks with writer preference.
009:
010: /**
011: * <p>A simple Reader/Writer lock. This assumes that there will
012: * be little writing contention. It also doesn't allow
013: * active readers to acquire and release a write lock, or
014: * deal with priority inversion issues.</p>
015: *
016: * <p>Access to the lock should be enclosed in a try/finally block
017: * in order to ensure that the lock is always released in case of
018: * exceptions:<br><pre>
019: * try {
020: * lock.acquireRead();
021: * // use service protected by the lock
022: * }
023: * finally {
024: * lock.releaseRead();
025: * }
026: * </pre></p>
027: *
028: * <p>The lock provides utility methods getStats and clearStats
029: * to return statistics on the use of the lock.</p>
030: */
031: public class ICURWLock {
032: private Object writeLock = new Object();
033: private Object readLock = new Object();
034: private int wwc; // waiting writers
035: private int rc; // active readers, -1 if there's an active writer
036: private int wrc; // waiting readers
037:
038: private Stats stats = new Stats(); // maybe don't init to start...
039:
040: /**
041: * Internal class used to gather statistics on the RWLock.
042: */
043: public final static class Stats {
044: /**
045: * Number of times read access granted (read count).
046: */
047: public int _rc;
048:
049: /**
050: * Number of times concurrent read access granted (multiple read count).
051: */
052: public int _mrc;
053:
054: /**
055: * Number of times blocked for read (waiting reader count).
056: */
057: public int _wrc; // wait for read
058:
059: /**
060: * Number of times write access granted (writer count).
061: */
062: public int _wc;
063:
064: /**
065: * Number of times blocked for write (waiting writer count).
066: */
067: public int _wwc;
068:
069: private Stats() {
070: }
071:
072: private Stats(int rc, int mrc, int wrc, int wc, int wwc) {
073: this ._rc = rc;
074: this ._mrc = mrc;
075: this ._wrc = wrc;
076: this ._wc = wc;
077: this ._wwc = wwc;
078: }
079:
080: private Stats(Stats rhs) {
081: this (rhs._rc, rhs._mrc, rhs._wrc, rhs._wc, rhs._wwc);
082: }
083:
084: /**
085: * Return a string listing all the stats.
086: */
087: public String toString() {
088: return " rc: " + _rc + " mrc: " + _mrc + " wrc: " + _wrc
089: + " wc: " + _wc + " wwc: " + _wwc;
090: }
091: }
092:
093: /**
094: * Reset the stats. Returns existing stats, if any.
095: */
096: public synchronized Stats resetStats() {
097: Stats result = stats;
098: stats = new Stats();
099: return result;
100: }
101:
102: /**
103: * Clear the stats (stop collecting stats). Returns existing stats, if any.
104: */
105: public synchronized Stats clearStats() {
106: Stats result = stats;
107: stats = null;
108: return result;
109: }
110:
111: /**
112: * Return a snapshot of the current stats. This does not reset the stats.
113: */
114: public synchronized Stats getStats() {
115: return stats == null ? null : new Stats(stats);
116: }
117:
118: // utilities
119:
120: private synchronized boolean gotRead() {
121: ++rc;
122: if (stats != null) {
123: ++stats._rc;
124: if (rc > 1)
125: ++stats._mrc;
126: }
127: return true;
128: }
129:
130: private synchronized boolean getRead() {
131: if (rc >= 0 && wwc == 0) {
132: return gotRead();
133: }
134: ++wrc;
135: return false;
136: }
137:
138: private synchronized boolean retryRead() {
139: if (stats != null)
140: ++stats._wrc;
141: if (rc >= 0 && wwc == 0) {
142: --wrc;
143: return gotRead();
144: }
145: return false;
146: }
147:
148: private synchronized boolean finishRead() {
149: if (rc > 0) {
150: return (0 == --rc && wwc > 0);
151: }
152: throw new IllegalStateException("no current reader to release");
153: }
154:
155: private synchronized boolean gotWrite() {
156: rc = -1;
157: if (stats != null) {
158: ++stats._wc;
159: }
160: return true;
161: }
162:
163: private synchronized boolean getWrite() {
164: if (rc == 0) {
165: return gotWrite();
166: }
167: ++wwc;
168: return false;
169: }
170:
171: private synchronized boolean retryWrite() {
172: if (stats != null)
173: ++stats._wwc;
174: if (rc == 0) {
175: --wwc;
176: return gotWrite();
177: }
178: return false;
179: }
180:
181: private static final int NOTIFY_NONE = 0;
182: private static final int NOTIFY_WRITERS = 1;
183: private static final int NOTIFY_READERS = 2;
184:
185: private synchronized int finishWrite() {
186: if (rc < 0) {
187: rc = 0;
188: if (wwc > 0) {
189: return NOTIFY_WRITERS;
190: } else if (wrc > 0) {
191: return NOTIFY_READERS;
192: } else {
193: return NOTIFY_NONE;
194: }
195: }
196: throw new IllegalStateException("no current writer to release");
197: }
198:
199: /**
200: * <p>Acquire a read lock, blocking until a read lock is
201: * available. Multiple readers can concurrently hold the read
202: * lock.</p>
203: *
204: * <p>If there's a writer, or a waiting writer, increment the
205: * waiting reader count and block on this. Otherwise
206: * increment the active reader count and return. Caller must call
207: * releaseRead when done (for example, in a finally block).</p>
208: */
209: public void acquireRead() {
210: if (!getRead()) {
211: for (;;) {
212: try {
213: synchronized (readLock) {
214: readLock.wait();
215: }
216: if (retryRead()) {
217: return;
218: }
219: } catch (InterruptedException e) {
220: }
221: }
222: }
223: }
224:
225: /**
226: * <p>Release a read lock and return. An error will be thrown
227: * if a read lock is not currently held.</p>
228: *
229: * <p>If this is the last active reader, notify the oldest
230: * waiting writer. Call when finished with work
231: * controlled by acquireRead.</p>
232: */
233: public void releaseRead() {
234: if (finishRead()) {
235: synchronized (writeLock) {
236: writeLock.notify();
237: }
238: }
239: }
240:
241: /**
242: * <p>Acquire the write lock, blocking until the write lock is
243: * available. Only one writer can acquire the write lock, and
244: * when held, no readers can acquire the read lock.</p>
245: *
246: * <p>If there are no readers and no waiting writers, mark as
247: * having an active writer and return. Otherwise, add a lock to the
248: * end of the waiting writer list, and block on it. Caller
249: * must call releaseWrite when done (for example, in a finally
250: * block).<p>
251: */
252: public void acquireWrite() {
253: if (!getWrite()) {
254: for (;;) {
255: try {
256: synchronized (writeLock) {
257: writeLock.wait();
258: }
259: if (retryWrite()) {
260: return;
261: }
262: } catch (InterruptedException e) {
263: }
264: }
265: }
266: }
267:
268: /**
269: * <p>Release the write lock and return. An error will be thrown
270: * if the write lock is not currently held.</p>
271: *
272: * <p>If there are waiting readers, make them all active and
273: * notify all of them. Otherwise, notify the oldest waiting
274: * writer, if any. Call when finished with work controlled by
275: * acquireWrite.</p>
276: */
277: public void releaseWrite() {
278: switch (finishWrite()) {
279: case NOTIFY_WRITERS:
280: synchronized (writeLock) {
281: writeLock.notify();
282: }
283: break;
284: case NOTIFY_READERS:
285: synchronized (readLock) {
286: readLock.notifyAll();
287: }
288: break;
289: case NOTIFY_NONE:
290: break;
291: }
292: }
293: }
|