Mercurial > hg > blitz_condensed
comparison src/EDU/oswego/cs/dl/util/concurrent/WriterPreferenceReadWriteLock.java @ 0:3dc0c5604566
Initial checkin of blitz 2.0 fcs - no installer yet.
author | Dan Creswell <dan.creswell@gmail.com> |
---|---|
date | Sat, 21 Mar 2009 11:00:06 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:3dc0c5604566 |
---|---|
1 /* | |
2 File: WriterPreferenceReadWriteLock.java | |
3 | |
4 Originally written by Doug Lea and released into the public domain. | |
5 This may be used for any purposes whatsoever without acknowledgment. | |
6 Thanks for the assistance and support of Sun Microsystems Labs, | |
7 and everyone contributing, testing, and using this code. | |
8 | |
9 History: | |
10 Date Who What | |
11 11Jun1998 dl Create public version | |
12 5Aug1998 dl replaced int counters with longs | |
13 25aug1998 dl record writer thread | |
14 3May1999 dl add notifications on interrupt/timeout | |
15 | |
16 */ | |
17 | |
18 package EDU.oswego.cs.dl.util.concurrent; | |
19 | |
20 /** | |
21 * A ReadWriteLock that prefers waiting writers over | |
22 * waiting readers when there is contention. This class | |
23 * is adapted from the versions described in CPJ, improving | |
24 * on the ones there a bit by segregating reader and writer | |
25 * wait queues, which is typically more efficient. | |
26 * <p> | |
27 * The locks are <em>NOT</em> reentrant. In particular, | |
28 * even though it may appear to usually work OK, | |
29 * a thread holding a read lock should not attempt to | |
30 * re-acquire it. Doing so risks lockouts when there are | |
31 * also waiting writers. | |
32 * <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>] | |
33 **/ | |
34 | |
35 public class WriterPreferenceReadWriteLock implements ReadWriteLock { | |
36 | |
37 protected long activeReaders_ = 0; | |
38 protected Thread activeWriter_ = null; | |
39 protected long waitingReaders_ = 0; | |
40 protected long waitingWriters_ = 0; | |
41 | |
42 | |
43 protected final ReaderLock readerLock_ = new ReaderLock(); | |
44 protected final WriterLock writerLock_ = new WriterLock(); | |
45 | |
46 public Sync writeLock() { return writerLock_; } | |
47 public Sync readLock() { return readerLock_; } | |
48 | |
49 /* | |
50 A bunch of small synchronized methods are needed | |
51 to allow communication from the Lock objects | |
52 back to this object, that serves as controller | |
53 */ | |
54 | |
55 | |
56 protected synchronized void cancelledWaitingReader() { --waitingReaders_; } | |
57 protected synchronized void cancelledWaitingWriter() { --waitingWriters_; } | |
58 | |
59 | |
60 /** Override this method to change to reader preference **/ | |
61 protected boolean allowReader() { | |
62 return activeWriter_ == null && waitingWriters_ == 0; | |
63 } | |
64 | |
65 | |
66 protected synchronized boolean startRead() { | |
67 boolean allowRead = allowReader(); | |
68 if (allowRead) ++activeReaders_; | |
69 return allowRead; | |
70 } | |
71 | |
72 protected synchronized boolean startWrite() { | |
73 | |
74 // The allowWrite expression cannot be modified without | |
75 // also changing startWrite, so is hard-wired | |
76 | |
77 boolean allowWrite = (activeWriter_ == null && activeReaders_ == 0); | |
78 if (allowWrite) activeWriter_ = Thread.currentThread(); | |
79 return allowWrite; | |
80 } | |
81 | |
82 | |
83 /* | |
84 Each of these variants is needed to maintain atomicity | |
85 of wait counts during wait loops. They could be | |
86 made faster by manually inlining each other. We hope that | |
87 compilers do this for us though. | |
88 */ | |
89 | |
90 protected synchronized boolean startReadFromNewReader() { | |
91 boolean pass = startRead(); | |
92 if (!pass) ++waitingReaders_; | |
93 return pass; | |
94 } | |
95 | |
96 protected synchronized boolean startWriteFromNewWriter() { | |
97 boolean pass = startWrite(); | |
98 if (!pass) ++waitingWriters_; | |
99 return pass; | |
100 } | |
101 | |
102 protected synchronized boolean startReadFromWaitingReader() { | |
103 boolean pass = startRead(); | |
104 if (pass) --waitingReaders_; | |
105 return pass; | |
106 } | |
107 | |
108 protected synchronized boolean startWriteFromWaitingWriter() { | |
109 boolean pass = startWrite(); | |
110 if (pass) --waitingWriters_; | |
111 return pass; | |
112 } | |
113 | |
114 /** | |
115 * Called upon termination of a read. | |
116 * Returns the object to signal to wake up a waiter, or null if no such | |
117 **/ | |
118 protected synchronized Signaller endRead() { | |
119 if (--activeReaders_ == 0 && waitingWriters_ > 0) | |
120 return writerLock_; | |
121 else | |
122 return null; | |
123 } | |
124 | |
125 | |
126 /** | |
127 * Called upon termination of a write. | |
128 * Returns the object to signal to wake up a waiter, or null if no such | |
129 **/ | |
130 protected synchronized Signaller endWrite() { | |
131 activeWriter_ = null; | |
132 if (waitingReaders_ > 0 && allowReader()) | |
133 return readerLock_; | |
134 else if (waitingWriters_ > 0) | |
135 return writerLock_; | |
136 else | |
137 return null; | |
138 } | |
139 | |
140 | |
141 /** | |
142 * Reader and Writer requests are maintained in two different | |
143 * wait sets, by two different objects. These objects do not | |
144 * know whether the wait sets need notification since they | |
145 * don't know preference rules. So, each supports a | |
146 * method that can be selected by main controlling object | |
147 * to perform the notifications. This base class simplifies mechanics. | |
148 **/ | |
149 | |
150 protected abstract class Signaller { // base for ReaderLock and WriterLock | |
151 abstract void signalWaiters(); | |
152 } | |
153 | |
154 protected class ReaderLock extends Signaller implements Sync { | |
155 | |
156 public void acquire() throws InterruptedException { | |
157 if (Thread.interrupted()) throw new InterruptedException(); | |
158 InterruptedException ie = null; | |
159 synchronized(this) { | |
160 if (!startReadFromNewReader()) { | |
161 for (;;) { | |
162 try { | |
163 ReaderLock.this.wait(); | |
164 if (startReadFromWaitingReader()) | |
165 return; | |
166 } | |
167 catch(InterruptedException ex){ | |
168 cancelledWaitingReader(); | |
169 ie = ex; | |
170 break; | |
171 } | |
172 } | |
173 } | |
174 } | |
175 if (ie != null) { | |
176 // fall through outside synch on interrupt. | |
177 // This notification is not really needed here, | |
178 // but may be in plausible subclasses | |
179 writerLock_.signalWaiters(); | |
180 throw ie; | |
181 } | |
182 } | |
183 | |
184 | |
185 public void release() { | |
186 Signaller s = endRead(); | |
187 if (s != null) s.signalWaiters(); | |
188 } | |
189 | |
190 | |
191 synchronized void signalWaiters() { ReaderLock.this.notifyAll(); } | |
192 | |
193 public boolean attempt(long msecs) throws InterruptedException { | |
194 if (Thread.interrupted()) throw new InterruptedException(); | |
195 InterruptedException ie = null; | |
196 synchronized(this) { | |
197 if (msecs <= 0) | |
198 return startRead(); | |
199 else if (startReadFromNewReader()) | |
200 return true; | |
201 else { | |
202 long waitTime = msecs; | |
203 long start = System.currentTimeMillis(); | |
204 for (;;) { | |
205 try { ReaderLock.this.wait(waitTime); } | |
206 catch(InterruptedException ex){ | |
207 cancelledWaitingReader(); | |
208 ie = ex; | |
209 break; | |
210 } | |
211 if (startReadFromWaitingReader()) | |
212 return true; | |
213 else { | |
214 waitTime = msecs - (System.currentTimeMillis() - start); | |
215 if (waitTime <= 0) { | |
216 cancelledWaitingReader(); | |
217 break; | |
218 } | |
219 } | |
220 } | |
221 } | |
222 } | |
223 // safeguard on interrupt or timeout: | |
224 writerLock_.signalWaiters(); | |
225 if (ie != null) throw ie; | |
226 else return false; // timed out | |
227 } | |
228 | |
229 } | |
230 | |
231 protected class WriterLock extends Signaller implements Sync { | |
232 | |
233 public void acquire() throws InterruptedException { | |
234 if (Thread.interrupted()) throw new InterruptedException(); | |
235 InterruptedException ie = null; | |
236 synchronized(this) { | |
237 if (!startWriteFromNewWriter()) { | |
238 for (;;) { | |
239 try { | |
240 WriterLock.this.wait(); | |
241 if (startWriteFromWaitingWriter()) | |
242 return; | |
243 } | |
244 catch(InterruptedException ex){ | |
245 cancelledWaitingWriter(); | |
246 WriterLock.this.notify(); | |
247 ie = ex; | |
248 break; | |
249 } | |
250 } | |
251 } | |
252 } | |
253 if (ie != null) { | |
254 // Fall through outside synch on interrupt. | |
255 // On exception, we may need to signal readers. | |
256 // It is not worth checking here whether it is strictly necessary. | |
257 readerLock_.signalWaiters(); | |
258 throw ie; | |
259 } | |
260 } | |
261 | |
262 public void release(){ | |
263 Signaller s = endWrite(); | |
264 if (s != null) s.signalWaiters(); | |
265 } | |
266 | |
267 synchronized void signalWaiters() { WriterLock.this.notify(); } | |
268 | |
269 public boolean attempt(long msecs) throws InterruptedException { | |
270 if (Thread.interrupted()) throw new InterruptedException(); | |
271 InterruptedException ie = null; | |
272 synchronized(this) { | |
273 if (msecs <= 0) | |
274 return startWrite(); | |
275 else if (startWriteFromNewWriter()) | |
276 return true; | |
277 else { | |
278 long waitTime = msecs; | |
279 long start = System.currentTimeMillis(); | |
280 for (;;) { | |
281 try { WriterLock.this.wait(waitTime); } | |
282 catch(InterruptedException ex){ | |
283 cancelledWaitingWriter(); | |
284 WriterLock.this.notify(); | |
285 ie = ex; | |
286 break; | |
287 } | |
288 if (startWriteFromWaitingWriter()) | |
289 return true; | |
290 else { | |
291 waitTime = msecs - (System.currentTimeMillis() - start); | |
292 if (waitTime <= 0) { | |
293 cancelledWaitingWriter(); | |
294 WriterLock.this.notify(); | |
295 break; | |
296 } | |
297 } | |
298 } | |
299 } | |
300 } | |
301 | |
302 readerLock_.signalWaiters(); | |
303 if (ie != null) throw ie; | |
304 else return false; // timed out | |
305 } | |
306 | |
307 } | |
308 | |
309 | |
310 | |
311 } | |
312 |