]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_4_2-src/main/classes/core/src/com/ibm/icu/impl/ICURWLock.java
go
[Dictionary.git] / jars / icu4j-4_4_2-src / main / classes / core / src / com / ibm / icu / impl / ICURWLock.java
1 /**\r
2  *******************************************************************************\r
3  * Copyright (C) 2001-2006, International Business Machines Corporation and    *\r
4  * others. All Rights Reserved.                                                *\r
5  *******************************************************************************\r
6  */\r
7 package com.ibm.icu.impl;\r
8 \r
9 // See Allan Holub's 1999 column in JavaWorld, and Doug Lea's code for RWLocks with writer preference.\r
10 \r
11 \r
12 /**\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
17  *\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
21  * try {\r
22  *     lock.acquireRead();\r
23  *     // use service protected by the lock\r
24  * }\r
25  * finally {\r
26  *     lock.releaseRead();\r
27  * }\r
28  * </pre></p>\r
29  *\r
30  * <p>The lock provides utility methods getStats and clearStats\r
31  * to return statistics on the use of the lock.</p>\r
32  */\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
39 \r
40     private Stats stats = new Stats(); // maybe don't init to start...\r
41 \r
42     /**\r
43      * Internal class used to gather statistics on the RWLock.\r
44      */\r
45     public final static class Stats {\r
46         /**\r
47          * Number of times read access granted (read count).\r
48          */\r
49         public int _rc;\r
50 \r
51         /**\r
52          * Number of times concurrent read access granted (multiple read count).\r
53          */\r
54         public int _mrc;\r
55 \r
56         /**\r
57          * Number of times blocked for read (waiting reader count).\r
58          */\r
59         public int _wrc; // wait for read\r
60 \r
61         /**\r
62          * Number of times write access granted (writer count).\r
63          */\r
64         public int _wc;\r
65 \r
66         /**\r
67          * Number of times blocked for write (waiting writer count).\r
68          */\r
69         public int _wwc;\r
70 \r
71         private Stats() {\r
72         }\r
73 \r
74         private Stats(int rc, int mrc, int wrc, int wc, int wwc) {\r
75             this._rc = rc;\r
76             this._mrc = mrc;\r
77             this._wrc = wrc;\r
78             this._wc = wc;\r
79             this._wwc = wwc;\r
80         }\r
81 \r
82         private Stats(Stats rhs) {\r
83             this(rhs._rc, rhs._mrc, rhs._wrc, rhs._wc, rhs._wwc);\r
84         }\r
85 \r
86         /**\r
87          * Return a string listing all the stats.\r
88          */\r
89         public String toString() {\r
90             return " rc: " + _rc +\r
91                 " mrc: " + _mrc + \r
92                 " wrc: " + _wrc +\r
93                 " wc: " + _wc +\r
94                 " wwc: " + _wwc;\r
95         }\r
96     }\r
97 \r
98     /**\r
99      * Reset the stats.  Returns existing stats, if any.\r
100      */\r
101     public synchronized Stats resetStats() {\r
102         Stats result = stats;\r
103         stats = new Stats();\r
104         return result;\r
105     }\r
106 \r
107     /**\r
108      * Clear the stats (stop collecting stats).  Returns existing stats, if any.\r
109      */\r
110     public synchronized Stats clearStats() {\r
111         Stats result = stats;\r
112         stats = null;\r
113         return result;\r
114     }\r
115     \r
116     /**\r
117      * Return a snapshot of the current stats.  This does not reset the stats.\r
118      */\r
119     public synchronized Stats getStats() {\r
120         return stats == null ? null : new Stats(stats);\r
121     }\r
122 \r
123     // utilities\r
124 \r
125     private synchronized boolean gotRead() {\r
126         ++rc;\r
127         if (stats != null) {\r
128             ++stats._rc;\r
129             if (rc > 1) ++stats._mrc;\r
130         }\r
131         return true;\r
132     }\r
133 \r
134     private synchronized boolean getRead() {\r
135         if (rc >= 0 && wwc == 0) {\r
136             return gotRead();\r
137         }\r
138         ++wrc;\r
139         return false;\r
140     }\r
141 \r
142     private synchronized boolean retryRead() {\r
143         if (stats != null) ++stats._wrc;\r
144         if (rc >= 0 && wwc == 0) {\r
145             --wrc;\r
146             return gotRead();\r
147         }\r
148         return false;\r
149     }\r
150 \r
151     private synchronized boolean finishRead() {\r
152         if (rc > 0) {\r
153             return (0 == --rc && wwc > 0);\r
154         }\r
155         throw new IllegalStateException("no current reader to release");\r
156     }\r
157     \r
158     private synchronized boolean gotWrite() {\r
159         rc = -1;\r
160         if (stats != null) {\r
161             ++stats._wc;\r
162         }\r
163         return true;\r
164     }\r
165 \r
166     private synchronized boolean getWrite() {\r
167         if (rc == 0) {\r
168             return gotWrite();\r
169         }\r
170         ++wwc;\r
171         return false;\r
172     }\r
173 \r
174     private synchronized boolean retryWrite() {\r
175         if (stats != null) ++stats._wwc;\r
176         if (rc == 0) {\r
177             --wwc;\r
178             return gotWrite();\r
179         }\r
180         return false;\r
181     }\r
182 \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
186 \r
187     private synchronized int finishWrite() {\r
188         if (rc < 0) {\r
189             rc = 0;\r
190             if (wwc > 0) {\r
191                 return NOTIFY_WRITERS;\r
192             } else if (wrc > 0) {\r
193                 return NOTIFY_READERS;\r
194             } else {\r
195                 return NOTIFY_NONE;\r
196             }\r
197         }\r
198         throw new IllegalStateException("no current writer to release");\r
199     }\r
200     \r
201     /**\r
202      * <p>Acquire a read lock, blocking until a read lock is\r
203      * available.  Multiple readers can concurrently hold the read\r
204      * lock.</p>\r
205      *\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
210      */\r
211     public void acquireRead() {\r
212         if (!getRead()) {\r
213             for (;;) {\r
214                 try {\r
215                     synchronized (readLock) {\r
216                         readLock.wait();\r
217                     }\r
218                     if (retryRead()) {\r
219                         return;\r
220                     }\r
221                 }\r
222                 catch (InterruptedException e) {\r
223                 }\r
224             }\r
225         }\r
226     }\r
227 \r
228     /**\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
231      *\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
235      */\r
236     public void releaseRead() {\r
237         if (finishRead()) {\r
238             synchronized (writeLock) {\r
239                 writeLock.notify();\r
240             }\r
241         }\r
242     }\r
243 \r
244     /**\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
248      *\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
253      * block).<p> \r
254      */\r
255     public void acquireWrite() {\r
256         if (!getWrite()) {\r
257             for (;;) {\r
258                 try {\r
259                     synchronized (writeLock) {\r
260                         writeLock.wait();\r
261                     }\r
262                     if (retryWrite()) {\r
263                         return;\r
264                     }\r
265                 }\r
266                 catch (InterruptedException e) {\r
267                 }\r
268             }\r
269         }\r
270     }\r
271 \r
272     /**\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
275      *\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
280      */\r
281     public void releaseWrite() {\r
282         switch (finishWrite()) {\r
283         case NOTIFY_WRITERS:\r
284             synchronized (writeLock) {\r
285                 writeLock.notify();\r
286             }\r
287             break;\r
288         case NOTIFY_READERS:\r
289             synchronized (readLock) {\r
290                 readLock.notifyAll();\r
291             }\r
292             break;\r
293         case NOTIFY_NONE:\r
294             break;\r
295         }\r
296     }\r
297 }\r