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