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.awt.EventQueue;
045: import java.awt.Toolkit;
046: import java.awt.event.InvocationEvent;
047: import java.awt.event.PaintEvent;
048: import java.lang.reflect.InvocationTargetException;
049: import java.util.ArrayList;
050: import java.util.List;
051: import org.openide.util.Utilities;
052:
053: // XXX could track read vs. write state
054:
055: /**
056: * Lock impl that works in the event thread.
057: * @author Jesse Glick
058: */
059: final class EventLock implements RWLock {
060:
061: public final static RWLock DEFAULT = new EventLock();
062:
063: private EventLock() {
064: }
065:
066: public <T> T read(final LockAction<T> action) {
067: if (isDispatchThread()) {
068: return action.run();
069: } else {
070: final List<T> result = new ArrayList<T>(1);
071: try {
072: invokeAndWaitLowPriority(this , new Runnable() {
073: public void run() {
074: result.add(action.run());
075: }
076: });
077: } catch (InterruptedException e) {
078: throw new IllegalStateException(e.toString());
079: } catch (InvocationTargetException e) {
080: Throwable t = e.getTargetException();
081: if (t instanceof RuntimeException) {
082: throw (RuntimeException) t;
083: } else if (t instanceof Error) {
084: throw (Error) t;
085: } else {
086: throw new IllegalStateException(t.toString());
087: }
088: }
089: return result.get(0);
090: }
091: }
092:
093: public <T, E extends Exception> T read(
094: final LockExceptionAction<T, E> action) throws E {
095: if (isDispatchThread()) {
096: return action.run();
097: } else {
098: final Throwable[] exc = new Throwable[1];
099: final List<T> result = new ArrayList<T>(1);
100: try {
101: invokeAndWaitLowPriority(this , new Runnable() {
102: public void run() {
103: try {
104: result.add(action.run());
105: } catch (Throwable t) {
106: exc[0] = t;
107: }
108: }
109: });
110: } catch (InterruptedException e) {
111: throw new IllegalStateException(e.toString());
112: } catch (InvocationTargetException e) {
113: // Should not happen since we caught Exception above already:
114: throw new IllegalStateException(e.getTargetException()
115: .toString());
116: }
117: if (exc[0] instanceof RuntimeException) {
118: throw (RuntimeException) exc[0];
119: } else if (exc[0] instanceof Error) {
120: throw (Error) exc[0];
121: } else if (exc[0] != null) {
122: @SuppressWarnings("unchecked")
123: E e = (E) exc[0];
124: throw e;
125: } else {
126: return result.get(0);
127: }
128: }
129: }
130:
131: public void readLater(final Runnable action) {
132: invokeLaterLowPriority(this , action);
133: }
134:
135: public <T> T write(LockAction<T> action) {
136: return read(action);
137: }
138:
139: public <T, E extends Exception> T write(
140: LockExceptionAction<T, E> action) throws E {
141: return read(action);
142: }
143:
144: public void writeLater(Runnable action) {
145: readLater(action);
146: }
147:
148: public void read(Runnable action) {
149: if (isDispatchThread()) {
150: action.run();
151: } else {
152: try {
153: invokeAndWaitLowPriority(this , action);
154: } catch (InterruptedException e) {
155: throw new IllegalStateException(e.toString());
156: } catch (InvocationTargetException e) {
157: Throwable t = e.getTargetException();
158: if (t instanceof RuntimeException) {
159: throw (RuntimeException) t;
160: } else if (t instanceof Error) {
161: throw (Error) t;
162: } else {
163: throw new IllegalStateException(t.toString());
164: }
165: }
166: }
167: }
168:
169: public void write(Runnable action) {
170: read(action);
171: }
172:
173: public boolean canRead() {
174: return isDispatchThread();
175: }
176:
177: public boolean canWrite() {
178: return isDispatchThread();
179: }
180:
181: public String toString() {
182: return "Locks.eventLock"; // NOI18N
183: }
184:
185: /** @return true iff current thread is EventDispatchThread */
186: static boolean isDispatchThread() {
187: boolean dispatch = EventQueue.isDispatchThread();
188: if (!dispatch
189: && Utilities.getOperatingSystem() == Utilities.OS_SOLARIS) {
190: // on solaris the event queue is not always recognized correctly
191: // => try to guess by name
192: dispatch = (Thread.currentThread().getClass().getName()
193: .indexOf("EventDispatchThread") >= 0); // NOI18N
194: }
195: return dispatch;
196: }
197:
198: /**
199: * Similar to {@link EventQueue#invokeLater} but posts the event at the same
200: * priority as paint requests, to avoid bad visual artifacts.
201: */
202: static void invokeLaterLowPriority(RWLock m, Runnable r) {
203: Toolkit t = Toolkit.getDefaultToolkit();
204: EventQueue q = t.getSystemEventQueue();
205: q.postEvent(new PaintPriorityEvent(m, t, r, null, false));
206: }
207:
208: /**
209: * Similar to {@link EventQueue#invokeAndWait} but posts the event at the same
210: * priority as paint requests, to avoid bad visual artifacts.
211: */
212: static void invokeAndWaitLowPriority(RWLock m, Runnable r)
213: throws InterruptedException, InvocationTargetException {
214: Toolkit t = Toolkit.getDefaultToolkit();
215: EventQueue q = t.getSystemEventQueue();
216: Object lock = new PaintPriorityEventLock();
217: InvocationEvent ev = new PaintPriorityEvent(m, t, r, lock, true);
218: synchronized (lock) {
219: q.postEvent(ev);
220: lock.wait();
221: }
222: Exception e = ev.getException();
223: if (e != null) {
224: throw new InvocationTargetException(e);
225: }
226: }
227:
228: private static final class PaintPriorityEvent extends
229: InvocationEvent {
230: private final RWLock m;
231:
232: public PaintPriorityEvent(RWLock m, Toolkit source,
233: Runnable runnable, Object notifier,
234: boolean catchExceptions) {
235: super (source, PaintEvent.PAINT, runnable, notifier,
236: catchExceptions);
237: this .m = m;
238: }
239:
240: public String paramString() {
241: return super .paramString() + ",lock=" + m; // NOI18N
242: }
243: }
244:
245: private static final class PaintPriorityEventLock {
246: }
247:
248: }
|