| // © 2016 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html#License |
| /** |
| ******************************************************************************* |
| * Copyright (C) 2001-2013, International Business Machines Corporation and * |
| * others. All Rights Reserved. * |
| ******************************************************************************* |
| */ |
| package com.ibm.icu.impl; |
| |
| import java.util.concurrent.locks.ReentrantReadWriteLock; |
| |
| |
| /** |
| * <p>A Reader/Writer lock originally written for ICU service |
| * implementation. The internal implementation was replaced |
| * with the JDK's stock read write lock (ReentrantReadWriteLock) |
| * for ICU 52.</p> |
| * |
| * <p>This assumes that there will be little writing contention. |
| * It also doesn't allow active readers to acquire and release |
| * a write lock, or deal with priority inversion issues.</p> |
| * |
| * <p>Access to the lock should be enclosed in a try/finally block |
| * in order to ensure that the lock is always released in case of |
| * exceptions:<br><pre> |
| * try { |
| * lock.acquireRead(); |
| * // use service protected by the lock |
| * } |
| * finally { |
| * lock.releaseRead(); |
| * } |
| * </pre></p> |
| * |
| * <p>The lock provides utility methods getStats and clearStats |
| * to return statistics on the use of the lock.</p> |
| */ |
| public class ICURWLock { |
| private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); |
| |
| private Stats stats = null; |
| |
| /** |
| * Internal class used to gather statistics on the RWLock. |
| */ |
| public final static class Stats { |
| /** |
| * Number of times read access granted (read count). |
| */ |
| public int _rc; |
| |
| /** |
| * Number of times concurrent read access granted (multiple read count). |
| */ |
| public int _mrc; |
| |
| /** |
| * Number of times blocked for read (waiting reader count). |
| */ |
| public int _wrc; // wait for read |
| |
| /** |
| * Number of times write access granted (writer count). |
| */ |
| public int _wc; |
| |
| /** |
| * Number of times blocked for write (waiting writer count). |
| */ |
| public int _wwc; |
| |
| private Stats() { |
| } |
| |
| private Stats(int rc, int mrc, int wrc, int wc, int wwc) { |
| this._rc = rc; |
| this._mrc = mrc; |
| this._wrc = wrc; |
| this._wc = wc; |
| this._wwc = wwc; |
| } |
| |
| private Stats(Stats rhs) { |
| this(rhs._rc, rhs._mrc, rhs._wrc, rhs._wc, rhs._wwc); |
| } |
| |
| /** |
| * Return a string listing all the stats. |
| */ |
| @Override |
| public String toString() { |
| return " rc: " + _rc + |
| " mrc: " + _mrc + |
| " wrc: " + _wrc + |
| " wc: " + _wc + |
| " wwc: " + _wwc; |
| } |
| } |
| |
| /** |
| * Reset the stats. Returns existing stats, if any. |
| */ |
| public synchronized Stats resetStats() { |
| Stats result = stats; |
| stats = new Stats(); |
| return result; |
| } |
| |
| /** |
| * Clear the stats (stop collecting stats). Returns existing stats, if any. |
| */ |
| public synchronized Stats clearStats() { |
| Stats result = stats; |
| stats = null; |
| return result; |
| } |
| |
| /** |
| * Return a snapshot of the current stats. This does not reset the stats. |
| */ |
| public synchronized Stats getStats() { |
| return stats == null ? null : new Stats(stats); |
| } |
| |
| /** |
| * <p>Acquire a read lock, blocking until a read lock is |
| * available. Multiple readers can concurrently hold the read |
| * lock.</p> |
| * |
| * <p>If there's a writer, or a waiting writer, increment the |
| * waiting reader count and block on this. Otherwise |
| * increment the active reader count and return. Caller must call |
| * releaseRead when done (for example, in a finally block).</p> |
| */ |
| public void acquireRead() { |
| if (stats != null) { // stats is null by default |
| synchronized (this) { |
| stats._rc++; |
| if (rwl.getReadLockCount() > 0) { |
| stats._mrc++; |
| } |
| if (rwl.isWriteLocked()) { |
| stats._wrc++; |
| } |
| } |
| } |
| rwl.readLock().lock(); |
| } |
| |
| /** |
| * <p>Release a read lock and return. An error will be thrown |
| * if a read lock is not currently held.</p> |
| * |
| * <p>If this is the last active reader, notify the oldest |
| * waiting writer. Call when finished with work |
| * controlled by acquireRead.</p> |
| */ |
| public void releaseRead() { |
| rwl.readLock().unlock(); |
| } |
| |
| /** |
| * <p>Acquire the write lock, blocking until the write lock is |
| * available. Only one writer can acquire the write lock, and |
| * when held, no readers can acquire the read lock.</p> |
| * |
| * <p>If there are no readers and no waiting writers, mark as |
| * having an active writer and return. Otherwise, add a lock to the |
| * end of the waiting writer list, and block on it. Caller |
| * must call releaseWrite when done (for example, in a finally |
| * block).<p> |
| */ |
| public void acquireWrite() { |
| if (stats != null) { // stats is null by default |
| synchronized (this) { |
| stats._wc++; |
| if (rwl.getReadLockCount() > 0 || rwl.isWriteLocked()) { |
| stats._wwc++; |
| } |
| } |
| } |
| rwl.writeLock().lock(); |
| } |
| |
| /** |
| * <p>Release the write lock and return. An error will be thrown |
| * if the write lock is not currently held.</p> |
| * |
| * <p>If there are waiting readers, make them all active and |
| * notify all of them. Otherwise, notify the oldest waiting |
| * writer, if any. Call when finished with work controlled by |
| * acquireWrite.</p> |
| */ |
| public void releaseWrite() { |
| rwl.writeLock().unlock(); |
| } |
| } |