1/** 2 ******************************************************************************* 3 * Copyright (C) 2001-2013, International Business Machines Corporation and * 4 * others. All Rights Reserved. * 5 ******************************************************************************* 6 */ 7package com.ibm.icu.impl; 8 9import java.util.concurrent.locks.ReentrantReadWriteLock; 10 11 12/** 13 * <p>A Reader/Writer lock originally written for ICU service 14 * implementation. The internal implementation was replaced 15 * with the JDK's stock read write lock (ReentrantReadWriteLock) 16 * for ICU 52.</p> 17 * 18 * <p>This assumes that there will be little writing contention. 19 * It also doesn't allow active readers to acquire and release 20 * a write lock, or deal with priority inversion issues.</p> 21 * 22 * <p>Access to the lock should be enclosed in a try/finally block 23 * in order to ensure that the lock is always released in case of 24 * exceptions:<br><pre> 25 * try { 26 * lock.acquireRead(); 27 * // use service protected by the lock 28 * } 29 * finally { 30 * lock.releaseRead(); 31 * } 32 * </pre></p> 33 * 34 * <p>The lock provides utility methods getStats and clearStats 35 * to return statistics on the use of the lock.</p> 36 */ 37public class ICURWLock { 38 private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); 39 40 private Stats stats = null; 41 42 /** 43 * Internal class used to gather statistics on the RWLock. 44 */ 45 public final static class Stats { 46 /** 47 * Number of times read access granted (read count). 48 */ 49 public int _rc; 50 51 /** 52 * Number of times concurrent read access granted (multiple read count). 53 */ 54 public int _mrc; 55 56 /** 57 * Number of times blocked for read (waiting reader count). 58 */ 59 public int _wrc; // wait for read 60 61 /** 62 * Number of times write access granted (writer count). 63 */ 64 public int _wc; 65 66 /** 67 * Number of times blocked for write (waiting writer count). 68 */ 69 public int _wwc; 70 71 private Stats() { 72 } 73 74 private Stats(int rc, int mrc, int wrc, int wc, int wwc) { 75 this._rc = rc; 76 this._mrc = mrc; 77 this._wrc = wrc; 78 this._wc = wc; 79 this._wwc = wwc; 80 } 81 82 private Stats(Stats rhs) { 83 this(rhs._rc, rhs._mrc, rhs._wrc, rhs._wc, rhs._wwc); 84 } 85 86 /** 87 * Return a string listing all the stats. 88 */ 89 public String toString() { 90 return " rc: " + _rc + 91 " mrc: " + _mrc + 92 " wrc: " + _wrc + 93 " wc: " + _wc + 94 " wwc: " + _wwc; 95 } 96 } 97 98 /** 99 * Reset the stats. Returns existing stats, if any. 100 */ 101 public synchronized Stats resetStats() { 102 Stats result = stats; 103 stats = new Stats(); 104 return result; 105 } 106 107 /** 108 * Clear the stats (stop collecting stats). Returns existing stats, if any. 109 */ 110 public synchronized Stats clearStats() { 111 Stats result = stats; 112 stats = null; 113 return result; 114 } 115 116 /** 117 * Return a snapshot of the current stats. This does not reset the stats. 118 */ 119 public synchronized Stats getStats() { 120 return stats == null ? null : new Stats(stats); 121 } 122 123 /** 124 * <p>Acquire a read lock, blocking until a read lock is 125 * available. Multiple readers can concurrently hold the read 126 * lock.</p> 127 * 128 * <p>If there's a writer, or a waiting writer, increment the 129 * waiting reader count and block on this. Otherwise 130 * increment the active reader count and return. Caller must call 131 * releaseRead when done (for example, in a finally block).</p> 132 */ 133 public void acquireRead() { 134 if (stats != null) { // stats is null by default 135 synchronized (this) { 136 stats._rc++; 137 if (rwl.getReadLockCount() > 0) { 138 stats._mrc++; 139 } 140 if (rwl.isWriteLocked()) { 141 stats._wrc++; 142 } 143 } 144 } 145 rwl.readLock().lock(); 146 } 147 148 /** 149 * <p>Release a read lock and return. An error will be thrown 150 * if a read lock is not currently held.</p> 151 * 152 * <p>If this is the last active reader, notify the oldest 153 * waiting writer. Call when finished with work 154 * controlled by acquireRead.</p> 155 */ 156 public void releaseRead() { 157 rwl.readLock().unlock(); 158 } 159 160 /** 161 * <p>Acquire the write lock, blocking until the write lock is 162 * available. Only one writer can acquire the write lock, and 163 * when held, no readers can acquire the read lock.</p> 164 * 165 * <p>If there are no readers and no waiting writers, mark as 166 * having an active writer and return. Otherwise, add a lock to the 167 * end of the waiting writer list, and block on it. Caller 168 * must call releaseWrite when done (for example, in a finally 169 * block).<p> 170 */ 171 public void acquireWrite() { 172 if (stats != null) { // stats is null by default 173 synchronized (this) { 174 stats._wc++; 175 if (rwl.getReadLockCount() > 0 || rwl.isWriteLocked()) { 176 stats._wwc++; 177 } 178 } 179 } 180 rwl.writeLock().lock(); 181 } 182 183 /** 184 * <p>Release the write lock and return. An error will be thrown 185 * if the write lock is not currently held.</p> 186 * 187 * <p>If there are waiting readers, make them all active and 188 * notify all of them. Otherwise, notify the oldest waiting 189 * writer, if any. Call when finished with work controlled by 190 * acquireWrite.</p> 191 */ 192 public void releaseWrite() { 193 rwl.writeLock().unlock(); 194 } 195} 196