001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package threaddemo.locking;
043:
044: import java.lang.reflect.InvocationTargetException;
045: import java.util.ArrayList;
046: import java.util.HashSet;
047: import java.util.List;
048: import java.util.Set;
049:
050: // XXX give preference to waiting writers: first to enter after last reader leaves; no new readers
051: // XXX handle interactions with other locks
052: // XXX forbid R -> W in AWT if have explicitly entered R
053:
054: /**
055: * Special "event hybrid" lock.
056: * If in AWT, automatically canRead, and read access is a no-op.
057: * Write access from AWT is OK.
058: * Write access otherwise calls invokeAndWait.
059: * @author Jesse Glick
060: */
061: final class EventHybridLock implements RWLock {
062:
063: public static final RWLock DEFAULT = new EventHybridLock();
064:
065: private EventHybridLock() {
066: }
067:
068: public <T> T read(LockAction<T> action) {
069: if (EventLock.isDispatchThread()) {
070: // Fine, go ahead.
071: if (semaphore == -1) {
072: semaphore = -2;
073: try {
074: return action.run();
075: } finally {
076: semaphore = -1;
077: }
078: } else {
079: boolean oldReadingInAwt = readingInAwt;
080: readingInAwt = true;
081: try {
082: return action.run();
083: } finally {
084: readingInAwt = oldReadingInAwt;
085: }
086: }
087: } else {
088: // Need to make sure no writers are running.
089: Thread reader;
090: synchronized (this ) {
091: while (semaphore < 0) {
092: try {
093: wait();
094: } catch (InterruptedException e) {
095: throw new IllegalStateException(e.toString());
096: }
097: }
098: semaphore++;
099: Thread curr = Thread.currentThread();
100: if (readers.add(curr)) {
101: reader = curr;
102: } else {
103: reader = null;
104: }
105: }
106: try {
107: return action.run();
108: } finally {
109: synchronized (this ) {
110: semaphore--;
111: if (reader != null) {
112: readers.remove(reader);
113: }
114: notifyAll();
115: }
116: }
117: }
118: }
119:
120: private static final class Holder<T, E extends Exception> {
121: public final T object;
122: public final E exception;
123:
124: public Holder(T object) {
125: this .object = object;
126: this .exception = null;
127: }
128:
129: public Holder(E exception) {
130: this .object = null;
131: this .exception = exception;
132: }
133: }
134:
135: @SuppressWarnings("unchecked")
136: private static <T, E extends Exception> LockAction<Holder<T, E>> convertExceptionAction(
137: final LockExceptionAction<T, E> action) {
138: return new LockAction<Holder<T, E>>() {
139: public Holder<T, E> run() {
140: try {
141: return new Holder<T, E>(action.run());
142: } catch (RuntimeException e) {
143: throw e;
144: } catch (Exception e) {
145: return new Holder<T, E>((E) e);
146: }
147: }
148: };
149: }
150:
151: private static <T, E extends Exception> T finishExceptionAction(
152: Holder<T, E> result) throws E {
153: if (result.exception != null) {
154: throw result.exception;
155: } else {
156: return result.object;
157: }
158: }
159:
160: public <T, E extends Exception> T read(
161: LockExceptionAction<T, E> action) throws E {
162: return finishExceptionAction(read(convertExceptionAction(action)));
163: }
164:
165: public void read(final Runnable action) {
166: read(convertRunnable(action));
167: }
168:
169: public <T> T write(final LockAction<T> action) {
170: if (!EventLock.isDispatchThread()) {
171: // Try again in AWT.
172: if (canRead()) {
173: throw new IllegalStateException("Cannot go R -> W"); // NOI18N
174: }
175: try {
176: final List<T> o = new ArrayList<T>(1);
177: final Error[] err = new Error[1];
178: EventLock.invokeAndWaitLowPriority(this ,
179: new Runnable() {
180: public void run() {
181: try {
182: o.add(write(action));
183: } catch (Error e) {
184: err[0] = e;
185: }
186: }
187: });
188: if (err[0] != null) {
189: throw err[0];
190: }
191: return o.get(0);
192: } catch (InterruptedException e) {
193: throw new IllegalStateException(e.toString());
194: } catch (InvocationTargetException e) {
195: Throwable x = e.getTargetException();
196: if (x instanceof RuntimeException) {
197: throw (RuntimeException) x;
198: } else if (x instanceof Error) {
199: throw (Error) x;
200: } else {
201: throw new IllegalStateException(x.toString());
202: }
203: }
204: }
205: // We are in AWT.
206: int oldSemaphore;
207: synchronized (this ) {
208: while (semaphore > 0) {
209: // Wait for readers to finish first.
210: try {
211: wait();
212: } catch (InterruptedException e) {
213: throw new IllegalStateException(e.toString());
214: }
215: }
216: oldSemaphore = semaphore;
217: if (semaphore == 0) {
218: if (readingInAwt) {
219: throw new IllegalStateException("Cannot go R -> W"); // NOI18N
220: } else {
221: semaphore = -1;
222: }
223: } else if (semaphore == -1) {
224: // OK.
225: } else if (semaphore == -2) {
226: throw new IllegalStateException("Cannot go R -> W"); // NOI18N
227: }
228: }
229: try {
230: return action.run();
231: } finally {
232: if (oldSemaphore == 0) {
233: // Exiting outermost write; permit readers to enter.
234: synchronized (this ) {
235: semaphore = 0;
236: notifyAll();
237: }
238: }
239: }
240: }
241:
242: public <T, E extends Exception> T write(
243: LockExceptionAction<T, E> action) throws E {
244: return finishExceptionAction(write(convertExceptionAction(action)));
245: }
246:
247: public void write(final Runnable action) {
248: write(convertRunnable(action));
249: }
250:
251: private static <T> LockAction<T> convertRunnable(
252: final Runnable action) {
253: return new LockAction<T>() {
254: public T run() {
255: action.run();
256: return null;
257: }
258: };
259: }
260:
261: public void readLater(final Runnable action) {
262: EventLock.invokeLaterLowPriority(this , new Runnable() {
263: public void run() {
264: read(action);
265: }
266: });
267: }
268:
269: public void writeLater(final Runnable action) {
270: EventLock.invokeLaterLowPriority(this , new Runnable() {
271: public void run() {
272: write(action);
273: }
274: });
275: }
276:
277: public boolean canRead() {
278: if (EventLock.isDispatchThread()) {
279: return true;
280: } else {
281: return readers.contains(Thread.currentThread());
282: }
283: }
284:
285: public boolean canWrite() {
286: if (EventLock.isDispatchThread()) {
287: return semaphore == -1;
288: } else {
289: return false;
290: }
291: }
292:
293: public String toString() {
294: // XXX include state info here
295: return "Locks.eventHybridLock"; // NOI18N
296: }
297:
298: /**
299: * Count of active outside readers, or write lock state.
300: * When positive, one or more readers outside AWT are holding the read lock.
301: * When zero, the lock is uncontended (available in AWT).
302: * When -1, the write lock is held.
303: * When -2, the read lock is held inside the write lock.
304: * In AWT, you can recursively enter either the read or write lock as much
305: * as desired; semaphore tracks the current status only.
306: */
307: private int semaphore = 0;
308:
309: /**
310: * If true, we are explicitly reading in AWT at the moment.
311: */
312: private boolean readingInAwt = false;
313:
314: /**
315: * Set of readers which are running.
316: */
317: private final Set<Thread> readers = new HashSet<Thread>();
318:
319: }
|