comparison src/EDU/oswego/cs/dl/util/concurrent/SyncCollection.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: SyncCollection.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 1Aug1998 dl Create public version
12 */
13
14 package EDU.oswego.cs.dl.util.concurrent;
15 import java.util.*;
16
17 /**
18 * SyncCollections wrap Sync-based control around java.util.Collections.
19 * They are similar in operation to those provided
20 * by java.util.Collection.synchronizedCollection, but have
21 * several extended capabilities.
22 * <p>
23 * The Collection interface is conceptually broken into two
24 * parts for purposes of synchronization control. The purely inspective
25 * reader operations are:
26 * <ul>
27 * <li> size
28 * <li> isEmpty
29 * <li> toArray
30 * <li> contains
31 * <li> containsAll
32 * <li> iterator
33 * </ul>
34 * The possibly mutative writer operations (which are also
35 * the set of operations that are allowed to throw
36 * UnsupportedOperationException) are:
37 * <ul>
38 * <li> add
39 * <li> addAll
40 * <li> remove
41 * <li> clear
42 * <li> removeAll
43 * <li> retainAll
44 * </ul>
45 *
46 * <p>
47 * SyncCollections can be used with either Syncs or ReadWriteLocks.
48 * When used with
49 * single Syncs, the same lock is used as both the reader and writer lock.
50 * The SyncCollection class cannot itself guarantee that using
51 * a pair of read/write locks will always correctly protect objects, since
52 * Collection implementations are not precluded from internally
53 * performing hidden unprotected state changes within conceptually read-only
54 * operations. However, they do work with current java.util implementations.
55 * (Hopefully, implementations that do not provide this natural
56 * guarantee will be clearly documentented as such.)
57 * <p>
58 * This class provides a straight implementation of Collections interface.
59 * In order to conform to this interface, sync failures
60 * due to interruption do NOT result in InterruptedExceptions.
61 * Instead, upon detection of interruption,
62 * <ul>
63 * <li> All mutative operations convert the interruption to
64 * an UnsupportedOperationException, while also propagating
65 * the interrupt status of the thread. Thus, unlike normal
66 * java.util.Collections, SyncCollections can <em>transiently</em>
67 * behave as if mutative operations are not supported.
68 * <li> All read-only operations
69 * attempt to return a result even upon interruption. In some contexts,
70 * such results will be meaningless due to interference, but
71 * provide best-effort status indications that can be useful during
72 * recovery. The cumulative number of synchronization failures encountered
73 * during such operations is accessible using method
74 * <code>synchronizationFailures()</code>.
75 * Non-zero values may indicate serious program errors.
76 * </ul>
77 * <p>
78 * The iterator() method returns a SyncCollectionIterator with
79 * properties and methods that are analogous to those of SyncCollection
80 * itself: hasNext and next are read-only, and remove is mutative.
81 * These methods allow fine-grained controlled access, but do <em>NOT</em>
82 * preclude concurrent modifications from being interleaved with traversals,
83 * which may lead to ConcurrentModificationExceptions.
84 * However, the class also supports method <code>unprotectedIterator</code>
85 * that can be used in conjunction with the <code>readerSync</code> or
86 * <code>writerSync</code> methods to perform locked traversals. For example,
87 * to protect a block of reads:
88 * <pre>
89 * Sync lock = coll.readerSync();
90 * try {
91 * lock.acquire();
92 * try {
93 * Iterator it = coll.unprotectedIterator();
94 * while (it.hasNext())
95 * System.out.println(it.next());
96 * }
97 * finally {
98 * lock.release();
99 * }
100 * }
101 * catch (InterruptedException ex) { ... }
102 * </pre>
103 * If you need to protect blocks of writes, you must use some
104 * form of <em>reentrant</em> lock (for example <code>ReentrantLock</code>
105 * or <code>ReentrantWriterPreferenceReadWriteLock</code>) as the Sync
106 * for the collection in order to allow mutative methods to proceed
107 * while the current thread holds the lock. For example, you might
108 * need to hold a write lock during an initialization sequence:
109 * <pre>
110 * Collection c = new SyncCollection(new ArrayList(),
111 * new ReentrantWriterPreferenceReadWriteLock());
112 * // ...
113 * c.writeLock().acquire();
114 * try {
115 * for (...) {
116 * Object x = someStream.readObject();
117 * c.add(x); // would block if writeLock not reentrant
118 * }
119 * }
120 * catch (IOException iox) {
121 * ...
122 * }
123 * finally {
124 * c.writeLock().release();
125 * }
126 * catch (InterruptedException ex) { ... }
127 * </pre>
128 * <p>
129 * (It would normally be better practice here to not make the
130 * collection accessible until initialization is complete.)
131 * <p>
132 * This class does not specifically support use of
133 * timed synchronization through the attempt method. However,
134 * you can obtain this effect via
135 * the TimeoutSync class. For example:
136 * <pre>
137 * Mutex lock = new Mutex();
138 * TimeoutSync timedLock = new TimeoutSync(lock, 1000); // 1 sec timeouts
139 * Collection c = new SyncCollection(new HashSet(), timedlock);
140 * </pre>
141 * <p>
142 * The same can be done with read-write locks:
143 * <pre>
144 * ReadWriteLock rwl = new WriterPreferenceReadWriteLock();
145 * Sync rlock = new TimeoutSync(rwl.readLock(), 100);
146 * Sync wlock = new TimeoutSync(rwl.writeLock(), 100);
147 * Collection c = new SyncCollection(new HashSet(), rlock, wlock);
148 * </pre>
149 * <p>
150 * In addition to synchronization control, SyncCollections
151 * may be useful in any context requiring before/after methods
152 * surrounding collections. For example, you can use ObservableSync
153 * to arrange notifications on method calls to collections, as in:
154 * <pre>
155 * class X {
156 * Collection c;
157 *
158 * static class CollectionObserver implements ObservableSync.SyncObserver {
159 * public void onAcquire(Object arg) {
160 * Collection coll = (Collection) arg;
161 * System.out.println("Starting operation on" + coll);
162 * // Other plausible responses include performing integrity
163 * // checks on the collection, updating displays, etc
164 * }
165 * public void onRelease(Object arg) {
166 * Collection coll = (Collection) arg;
167 * System.out.println("Finished operation on" + coll);
168 * }
169 * }
170 *
171 * X() {
172 * ObservableSync s = new ObservableSync();
173 * c = new SyncCollection(new HashSet(), s);
174 * s.setNotificationArgument(c);
175 * CollectionObserver obs = new CollectionObserver();
176 * s.attach(obs);
177 * }
178 * ...
179 * }
180 * </pre>
181 *
182 * <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>]
183 * @see LayeredSync
184 * @see TimeoutSync
185 **/
186
187
188 public class SyncCollection implements Collection {
189 protected final Collection c_; // Backing Collection
190 protected final Sync rd_; // sync for read-only methods
191 protected final Sync wr_; // sync for mutative methods
192
193 protected final SynchronizedLong syncFailures_ = new SynchronizedLong(0);
194
195 /**
196 * Create a new SyncCollection protecting the given collection,
197 * and using the given sync to control both reader and writer methods.
198 * Common, reasonable choices for the sync argument include
199 * Mutex, ReentrantLock, and Semaphores initialized to 1.
200 * <p>
201 * <b>Sample Usage</b>
202 * <pre>
203 * Collection c = new SyncCollection(new ArrayList(), new Mutex());
204 * </pre>
205 **/
206 public SyncCollection(Collection collection, Sync sync) {
207 this (collection, sync, sync);
208 }
209
210
211 /**
212 * Create a new SyncCollection protecting the given collection,
213 * and using the given ReadWriteLock to control reader and writer methods.
214 * <p>
215 * <b>Sample Usage</b>
216 * <pre>
217 * Collection c = new SyncCollection(new HashSet(),
218 * new WriterPreferenceReadWriteLock());
219 * </pre>
220 **/
221 public SyncCollection(Collection collection, ReadWriteLock rwl) {
222 this (collection, rwl.readLock(), rwl.writeLock());
223 }
224
225 /**
226 * Create a new SyncCollection protecting the given collection,
227 * and using the given pair of locks to control reader and writer methods.
228 **/
229 public SyncCollection(Collection collection, Sync readLock, Sync writeLock) {
230 c_ = collection;
231 rd_ = readLock;
232 wr_ = writeLock;
233 }
234
235 /**
236 * Return the Sync object managing read-only operations
237 **/
238
239 public Sync readerSync() {
240 return rd_;
241 }
242
243 /**
244 * Return the Sync object managing mutative operations
245 **/
246
247 public Sync writerSync() {
248 return wr_;
249 }
250
251 /**
252 * Return the number of synchronization failures for read-only operations
253 **/
254 public long syncFailures() {
255 return syncFailures_.get();
256 }
257
258
259 /** Try to acquire sync before a reader operation; record failure **/
260 protected boolean beforeRead() {
261 try {
262 rd_.acquire();
263 return false;
264 }
265 catch (InterruptedException ex) {
266 syncFailures_.increment();
267 return true;
268 }
269 }
270
271 /** Clean up after a reader operation **/
272 protected void afterRead(boolean wasInterrupted) {
273 if (wasInterrupted) {
274 Thread.currentThread().interrupt();
275 }
276 else
277 rd_.release();
278 }
279
280
281
282 public int size() {
283 boolean wasInterrupted = beforeRead();
284 try {
285 return c_.size();
286 }
287 finally {
288 afterRead(wasInterrupted);
289 }
290 }
291
292 public boolean isEmpty() {
293 boolean wasInterrupted = beforeRead();
294 try {
295 return c_.isEmpty();
296 }
297 finally {
298 afterRead(wasInterrupted);
299 }
300 }
301
302 public boolean contains(Object o) {
303 boolean wasInterrupted = beforeRead();
304 try {
305 return c_.contains(o);
306 }
307 finally {
308 afterRead(wasInterrupted);
309 }
310 }
311
312 public Object[] toArray() {
313 boolean wasInterrupted = beforeRead();
314 try {
315 return c_.toArray();
316 }
317 finally {
318 afterRead(wasInterrupted);
319 }
320 }
321
322 public Object[] toArray(Object[] a) {
323 boolean wasInterrupted = beforeRead();
324 try {
325 return c_.toArray(a);
326 }
327 finally {
328 afterRead(wasInterrupted);
329 }
330 }
331
332 public boolean containsAll(Collection coll) {
333 boolean wasInterrupted = beforeRead();
334 try {
335 return c_.containsAll(coll);
336 }
337 finally {
338 afterRead(wasInterrupted);
339 }
340 }
341
342
343 public boolean add(Object o) {
344 try {
345 wr_.acquire();
346 try {
347 return c_.add(o);
348 }
349 finally {
350 wr_.release();
351 }
352 }
353 catch (InterruptedException ex) {
354 Thread.currentThread().interrupt();
355 throw new UnsupportedOperationException();
356 }
357 }
358
359 public boolean remove(Object o) {
360 try {
361 wr_.acquire();
362 try {
363 return c_.remove(o);
364 }
365 finally {
366 wr_.release();
367 }
368 }
369 catch (InterruptedException ex) {
370 Thread.currentThread().interrupt();
371 throw new UnsupportedOperationException();
372 }
373 }
374
375 public boolean addAll(Collection coll) {
376 try {
377 wr_.acquire();
378 try {
379 return c_.addAll(coll);
380 }
381 finally {
382 wr_.release();
383 }
384 }
385 catch (InterruptedException ex) {
386 Thread.currentThread().interrupt();
387 throw new UnsupportedOperationException();
388 }
389 }
390
391 public boolean removeAll(Collection coll) {
392 try {
393 wr_.acquire();
394 try {
395 return c_.removeAll(coll);
396 }
397 finally {
398 wr_.release();
399 }
400 }
401 catch (InterruptedException ex) {
402 Thread.currentThread().interrupt();
403 throw new UnsupportedOperationException();
404 }
405 }
406
407
408 public boolean retainAll(Collection coll) {
409 try {
410 wr_.acquire();
411 try {
412 return c_.retainAll(coll);
413 }
414 finally {
415 wr_.release();
416 }
417 }
418 catch (InterruptedException ex) {
419 Thread.currentThread().interrupt();
420 throw new UnsupportedOperationException();
421 }
422 }
423
424
425 public void clear() {
426 try {
427 wr_.acquire();
428 try {
429 c_.clear();
430 }
431 finally {
432 wr_.release();
433 }
434 }
435 catch (InterruptedException ex) {
436 Thread.currentThread().interrupt();
437 throw new UnsupportedOperationException();
438 }
439 }
440
441
442 /** Return the base iterator of the underlying collection **/
443 public Iterator unprotectedIterator() {
444 boolean wasInterrupted = beforeRead();
445 try {
446 return c_.iterator();
447 }
448 finally {
449 afterRead(wasInterrupted);
450 }
451 }
452
453 public Iterator iterator() {
454 boolean wasInterrupted = beforeRead();
455 try {
456 return new SyncCollectionIterator(c_.iterator());
457 }
458 finally {
459 afterRead(wasInterrupted);
460 }
461 }
462
463 public class SyncCollectionIterator implements Iterator {
464 protected final Iterator baseIterator_;
465
466 SyncCollectionIterator(Iterator baseIterator) {
467 baseIterator_ = baseIterator;
468 }
469
470 public boolean hasNext() {
471 boolean wasInterrupted = beforeRead();
472 try {
473 return baseIterator_.hasNext();
474 }
475 finally {
476 afterRead(wasInterrupted);
477 }
478 }
479
480 public Object next() {
481 boolean wasInterrupted = beforeRead();
482 try {
483 return baseIterator_.next();
484 }
485 finally {
486 afterRead(wasInterrupted);
487 }
488 }
489
490 public void remove() {
491 try {
492 wr_.acquire();
493 try {
494 baseIterator_.remove();
495 }
496 finally {
497 wr_.release();
498 }
499 }
500 catch (InterruptedException ex) {
501 Thread.currentThread().interrupt();
502 throw new UnsupportedOperationException();
503 }
504 }
505
506 }
507 }
508
509