You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
329 lines
11 KiB
329 lines
11 KiB
/*
|
|
* 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.
|
|
* <p>
|
|
* The {@link EventFilter} is set on the {@code dispatchThread}
|
|
* while waiting. The filter is removed on completion of the
|
|
* waiting process.
|
|
* <p>
|
|
*
|
|
*
|
|
* @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<Void>() {
|
|
public Void run() {
|
|
run.run();
|
|
return null;
|
|
}
|
|
});
|
|
} else {
|
|
if (log.isLoggable(PlatformLogger.Level.FINEST)) {
|
|
log.finest("On non-dispatch thread: " + currentThread);
|
|
}
|
|
keepBlockingCT.set(true);
|
|
synchronized (getTreeLock()) {
|
|
if (afterExit.get()) return false;
|
|
if (filter != null) {
|
|
dispatchThread.addEventFilter(filter);
|
|
}
|
|
try {
|
|
EventQueue eq = dispatchThread.getEventQueue();
|
|
eq.postEvent(new PeerEvent(this, run, PeerEvent.PRIORITY_EVENT));
|
|
if (interval > 0) {
|
|
long currTime = System.currentTimeMillis();
|
|
while (keepBlockingCT.get() &&
|
|
((extCondition != null) ? extCondition.evaluate() : true) &&
|
|
(currTime + interval > System.currentTimeMillis()))
|
|
{
|
|
getTreeLock().wait(interval);
|
|
}
|
|
} else {
|
|
while (keepBlockingCT.get() &&
|
|
((extCondition != null) ? extCondition.evaluate() : true))
|
|
{
|
|
getTreeLock().wait();
|
|
}
|
|
}
|
|
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
|
log.fine("waitDone " + keepBlockingEDT.get() + " " + keepBlockingCT.get());
|
|
}
|
|
} catch (InterruptedException e) {
|
|
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
|
log.fine("Exception caught while waiting: " + e);
|
|
}
|
|
} finally {
|
|
if (filter != null) {
|
|
dispatchThread.removeEventFilter(filter);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
finally {
|
|
keepBlockingEDT.set(false);
|
|
keepBlockingCT.set(false);
|
|
afterExit.set(false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public boolean exit() {
|
|
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
|
log.fine("exit(): blockingEDT=" + keepBlockingEDT.get() +
|
|
", blockingCT=" + keepBlockingCT.get());
|
|
}
|
|
afterExit.set(true);
|
|
if (keepBlockingEDT.getAndSet(false)) {
|
|
wakeupEDT();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private final static Object getTreeLock() {
|
|
return Component.LOCK;
|
|
}
|
|
|
|
private final Runnable wakingRunnable = new Runnable() {
|
|
public void run() {
|
|
log.fine("Wake up EDT");
|
|
synchronized (getTreeLock()) {
|
|
keepBlockingCT.set(false);
|
|
getTreeLock().notifyAll();
|
|
}
|
|
log.fine("Wake up EDT done");
|
|
}
|
|
};
|
|
|
|
private void wakeupEDT() {
|
|
if (log.isLoggable(PlatformLogger.Level.FINEST)) {
|
|
log.finest("wakeupEDT(): EDT == " + dispatchThread);
|
|
}
|
|
EventQueue eq = dispatchThread.getEventQueue();
|
|
eq.postEvent(new PeerEvent(this, wakingRunnable, PeerEvent.PRIORITY_EVENT));
|
|
}
|
|
}
|