/* * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ package java.awt; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.atomic.AtomicBoolean; import java.security.PrivilegedAction; import java.security.AccessController; import sun.awt.PeerEvent; import sun.util.logging.PlatformLogger; /** * This utility class is used to suspend execution on a thread * while still allowing {@code EventDispatchThread} to dispatch events. * The API methods of the class are thread-safe. * * @author Anton Tarasov, Artem Ananiev * * @since 1.7 */ class WaitDispatchSupport implements SecondaryLoop { private final static PlatformLogger log = PlatformLogger.getLogger("java.awt.event.WaitDispatchSupport"); private EventDispatchThread dispatchThread; private EventFilter filter; private volatile Conditional extCondition; private volatile Conditional condition; private long interval; // Use a shared daemon timer to serve all the WaitDispatchSupports private static Timer timer; // When this WDS expires, we cancel the timer task leaving the // shared timer up and running private TimerTask timerTask; private AtomicBoolean keepBlockingEDT = new AtomicBoolean(false); private AtomicBoolean keepBlockingCT = new AtomicBoolean(false); private AtomicBoolean afterExit = new AtomicBoolean(false); private static synchronized void initializeTimer() { if (timer == null) { timer = new Timer("AWT-WaitDispatchSupport-Timer", true); } } /** * Creates a {@code WaitDispatchSupport} instance to * serve the given event dispatch thread. * * @param dispatchThread An event dispatch thread that * should not stop dispatching events while waiting * * @since 1.7 */ public WaitDispatchSupport(EventDispatchThread dispatchThread) { this(dispatchThread, null); } /** * Creates a {@code WaitDispatchSupport} instance to * serve the given event dispatch thread. * * @param dispatchThread An event dispatch thread that * should not stop dispatching events while waiting * @param extCond A conditional object used to determine * if the loop should be terminated * * @since 1.7 */ public WaitDispatchSupport(EventDispatchThread dispatchThread, Conditional extCond) { if (dispatchThread == null) { throw new IllegalArgumentException("The dispatchThread can not be null"); } this.dispatchThread = dispatchThread; this.extCondition = extCond; this.condition = new Conditional() { @Override public boolean evaluate() { if (log.isLoggable(PlatformLogger.Level.FINEST)) { log.finest("evaluate(): blockingEDT=" + keepBlockingEDT.get() + ", blockingCT=" + keepBlockingCT.get()); } boolean extEvaluate = (extCondition != null) ? extCondition.evaluate() : true; if (!keepBlockingEDT.get() || !extEvaluate) { if (timerTask != null) { timerTask.cancel(); timerTask = null; } return false; } return true; } }; } /** * Creates a {@code WaitDispatchSupport} instance to * serve the given event dispatch thread. *
* The {@link EventFilter} is set on the {@code dispatchThread} * while waiting. The filter is removed on completion of the * waiting process. *
*
*
* @param dispatchThread An event dispatch thread that
* should not stop dispatching events while waiting
* @param filter {@code EventFilter} to be set
* @param interval A time interval to wait for. Note that
* when the waiting process takes place on EDT
* there is no guarantee to stop it in the given time
*
* @since 1.7
*/
public WaitDispatchSupport(EventDispatchThread dispatchThread,
Conditional extCondition,
EventFilter filter, long interval)
{
this(dispatchThread, extCondition);
this.filter = filter;
if (interval < 0) {
throw new IllegalArgumentException("The interval value must be >= 0");
}
this.interval = interval;
if (interval != 0) {
initializeTimer();
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean enter() {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("enter(): blockingEDT=" + keepBlockingEDT.get() +
", blockingCT=" + keepBlockingCT.get());
}
if (!keepBlockingEDT.compareAndSet(false, true)) {
log.fine("The secondary loop is already running, aborting");
return false;
}
try {
if (afterExit.get()) {
log.fine("Exit was called already, aborting");
return false;
}
final Runnable run = new Runnable() {
public void run() {
log.fine("Starting a new event pump");
if (filter == null) {
dispatchThread.pumpEvents(condition);
} else {
dispatchThread.pumpEventsForFilter(condition, filter);
}
}
};
// We have two mechanisms for blocking: if we're on the
// dispatch thread, start a new event pump; if we're
// on any other thread, call wait() on the treelock
Thread currentThread = Thread.currentThread();
if (currentThread == dispatchThread) {
if (log.isLoggable(PlatformLogger.Level.FINEST)) {
log.finest("On dispatch thread: " + dispatchThread);
}
if (interval != 0) {
if (log.isLoggable(PlatformLogger.Level.FINEST)) {
log.finest("scheduling the timer for " + interval + " ms");
}
timer.schedule(timerTask = new TimerTask() {
@Override
public void run() {
if (keepBlockingEDT.compareAndSet(true, false)) {
wakeupEDT();
}
}
}, interval);
}
// Dispose SequencedEvent we are dispatching on the current
// AppContext, to prevent us from hang - see 4531693 for details
SequencedEvent currentSE = KeyboardFocusManager.
getCurrentKeyboardFocusManager().getCurrentSequencedEvent();
if (currentSE != null) {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Dispose current SequencedEvent: " + currentSE);
}
currentSE.dispose();
}
// In case the exit() method is called before starting
// new event pump it will post the waking event to EDT.
// The event will be handled after the new event pump
// starts. Thus, the enter() method will not hang.
//
// Event pump should be privileged. See 6300270.
AccessController.doPrivileged(new PrivilegedAction