001: /*
002: * The contents of this file are subject to the Sapient Public License
003: * Version 1.0 (the "License"); you may not use this file except in compliance
004: * with the License. You may obtain a copy of the License at
005: * http://carbon.sf.net/License.html.
006: *
007: * Software distributed under the License is distributed on an "AS IS" basis,
008: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
009: * the specific language governing rights and limitations under the License.
010: *
011: * The Original Code is The Carbon Component Framework.
012: *
013: * The Initial Developer of the Original Code is Sapient Corporation
014: *
015: * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
016: */
017:
018: package org.sape.carbon.core.util.thread;
019:
020: /*
021: Originally written by Doug Lea and released into the public domain.
022: This may be used for any purposes whatsoever without acknowledgment.
023: Thanks for the assistance and support of Sun Microsystems Labs,
024: and everyone contributing, testing, and using this code.
025:
026: History:
027: Date Who What
028: 26 aug1998 dl Create public version
029: 7 sep2000 dl Readers are now also reentrant
030: 19 jan2001 dl Allow read->write upgrades if the only reader
031: 10 dec2002 dl Throw IllegalStateException on extra release
032: */
033:
034: import java.util.HashMap;
035:
036: /**
037: * A writer-preference ReadWriteLock that allows both readers and
038: * writers to reacquire
039: * read or write locks in the style of a ReentrantLock.
040: * Readers are not allowed until all write locks held by
041: * the writing thread have been released.
042: * Among other applications, reentrancy can be useful when
043: * write locks are held during calls or callbacks to methods that perform
044: * reads under read locks.
045: * <p>
046: * <b>Sample usage</b>. Here is a code sketch showing how to exploit
047: * reentrancy to perform lock downgrading after updating a cache:
048: * <pre>
049: * class CachedData {
050: * Object data;
051: * volatile boolean cacheValid;
052: * ReentrantWriterPreferenceReadWriteLock rwl = ...
053: *
054: * void processCachedData() {
055: * rwl.readLock().acquire();
056: * if (!cacheValid) {
057: *
058: * // upgrade lock:
059: * rwl.readLock().release(); // must release first to obtain writelock
060: * rwl.writeLock().acquire();
061: * if (!cacheValid) { // recheck
062: * data = ...
063: * cacheValid = true;
064: * }
065: * // downgrade lock
066: * rwl.readLock().acquire(); // reacquire read without giving up lock
067: * rwl.writeLock().release(); // release write, still hold read
068: * }
069: *
070: * use(data);
071: * rwl.readLock().release();
072: * }
073: * }
074: * </pre>
075: *
076: *
077: * <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>]
078: *
079: * @since carbon 2.0
080: * @author Doug Lea
081: * @version $Revision: 1.3 $($Author: dvoet $ / $Date: 2003/05/05 21:21:24 $)
082: **/
083: public class ReentrantWriterPreferenceReadWriteLock extends
084: WriterPreferenceReadWriteLock {
085:
086: /** Number of acquires on write lock by activeWriter_ thread **/
087: protected long writeHolds_ = 0;
088:
089: /** Number of acquires on read lock by any reader thread **/
090: protected HashMap readers_ = new HashMap();
091:
092: /** cache/reuse the special Integer value one to speed up readlocks **/
093: protected static final Integer IONE = new Integer(1);
094:
095: protected boolean allowReader() {
096: return (activeWriter_ == null && waitingWriters_ == 0)
097: || activeWriter_ == Thread.currentThread();
098: }
099:
100: protected synchronized boolean startRead() {
101: Thread t = Thread.currentThread();
102: Object c = readers_.get(t);
103: if (c != null) { // already held -- just increment hold count
104: readers_
105: .put(t, new Integer(((Integer) (c)).intValue() + 1));
106: ++activeReaders_;
107: return true;
108: } else if (allowReader()) {
109: readers_.put(t, IONE);
110: ++activeReaders_;
111: return true;
112: } else
113: return false;
114: }
115:
116: protected synchronized boolean startWrite() {
117: if (activeWriter_ == Thread.currentThread()) { // already held; re-acquire
118: ++writeHolds_;
119: return true;
120: } else if (writeHolds_ == 0) {
121: if (activeReaders_ == 0
122: || (readers_.size() == 1 && readers_.get(Thread
123: .currentThread()) != null)) {
124: activeWriter_ = Thread.currentThread();
125: writeHolds_ = 1;
126: return true;
127: } else
128: return false;
129: } else
130: return false;
131: }
132:
133: protected synchronized Signaller endRead() {
134: Thread t = Thread.currentThread();
135: Object c = readers_.get(t);
136: if (c == null)
137: throw new IllegalStateException();
138: --activeReaders_;
139: if (c != IONE) { // more than one hold; decrement count
140: int h = ((Integer) (c)).intValue() - 1;
141: Integer ih = (h == 1) ? IONE : new Integer(h);
142: readers_.put(t, ih);
143: return null;
144: } else {
145: readers_.remove(t);
146:
147: if (writeHolds_ > 0) // a write lock is still held by current thread
148: return null;
149: else if (activeReaders_ == 0 && waitingWriters_ > 0)
150: return writerLock_;
151: else
152: return null;
153: }
154: }
155:
156: protected synchronized Signaller endWrite() {
157: --writeHolds_;
158: if (writeHolds_ > 0) // still being held
159: return null;
160: else {
161: activeWriter_ = null;
162: if (waitingReaders_ > 0 && allowReader())
163: return readerLock_;
164: else if (waitingWriters_ > 0)
165: return writerLock_;
166: else
167: return null;
168: }
169: }
170:
171: }
|