2695N/A/*
2695N/A * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2695N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
2695N/A *
2695N/A * This code is free software; you can redistribute it and/or modify it
2695N/A * under the terms of the GNU General Public License version 2 only, as
2695N/A * published by the Free Software Foundation. Oracle designates this
2695N/A * particular file as subject to the "Classpath" exception as provided
2695N/A * by Oracle in the LICENSE file that accompanied this code.
2695N/A *
2695N/A * This code is distributed in the hope that it will be useful, but WITHOUT
2695N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
2695N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
2695N/A * version 2 for more details (a copy is included in the LICENSE file that
2695N/A * accompanied this code).
2695N/A *
2695N/A * You should have received a copy of the GNU General Public License version
2695N/A * 2 along with this work; if not, write to the Free Software Foundation,
2695N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2695N/A *
2695N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2695N/A * or visit www.oracle.com if you need additional information or have any
2695N/A * questions.
2695N/A */
2695N/A
2695N/Apackage java.awt;
2695N/A
2695N/Aimport java.util.Timer;
2695N/Aimport java.util.TimerTask;
2695N/Aimport java.util.concurrent.atomic.AtomicBoolean;
2695N/A
2695N/Aimport java.security.PrivilegedAction;
2695N/Aimport java.security.AccessController;
2695N/A
2695N/Aimport sun.awt.PeerEvent;
2695N/A
2695N/Aimport sun.util.logging.PlatformLogger;
2695N/A
2695N/A/**
2695N/A * This utility class is used to suspend execution on a thread
2695N/A * while still allowing {@code EventDispatchThread} to dispatch events.
2695N/A * The API methods of the class are thread-safe.
2695N/A *
2695N/A * @author Anton Tarasov, Artem Ananiev
2695N/A *
2695N/A * @since 1.7
2695N/A */
2695N/Aclass WaitDispatchSupport implements SecondaryLoop {
2695N/A
2695N/A private final static PlatformLogger log =
2695N/A PlatformLogger.getLogger("java.awt.event.WaitDispatchSupport");
2695N/A
2695N/A private EventDispatchThread dispatchThread;
2695N/A private EventFilter filter;
2695N/A
2695N/A private volatile Conditional extCondition;
2695N/A private volatile Conditional condition;
2695N/A
2695N/A private long interval;
2695N/A // Use a shared daemon timer to serve all the WaitDispatchSupports
2695N/A private static Timer timer;
2695N/A // When this WDS expires, we cancel the timer task leaving the
2695N/A // shared timer up and running
2695N/A private TimerTask timerTask;
2695N/A
2695N/A private AtomicBoolean keepBlockingEDT = new AtomicBoolean(false);
2695N/A private AtomicBoolean keepBlockingCT = new AtomicBoolean(false);
2695N/A
2695N/A private static synchronized void initializeTimer() {
2695N/A if (timer == null) {
2695N/A timer = new Timer("AWT-WaitDispatchSupport-Timer", true);
2695N/A }
2695N/A }
2695N/A
2695N/A /**
2695N/A * Creates a {@code WaitDispatchSupport} instance to
2695N/A * serve the given event dispatch thread.
2695N/A *
2695N/A * @param dispatchThread An event dispatch thread that
2695N/A * should not stop dispatching events while waiting
2695N/A *
2695N/A * @since 1.7
2695N/A */
2695N/A public WaitDispatchSupport(EventDispatchThread dispatchThread) {
2695N/A this(dispatchThread, null);
2695N/A }
2695N/A
2695N/A /**
2695N/A * Creates a {@code WaitDispatchSupport} instance to
2695N/A * serve the given event dispatch thread.
2695N/A *
2695N/A * @param dispatchThread An event dispatch thread that
2695N/A * should not stop dispatching events while waiting
2695N/A * @param extCondition A conditional object used to determine
2695N/A * if the loop should be terminated
2695N/A *
2695N/A * @since 1.7
2695N/A */
2695N/A public WaitDispatchSupport(EventDispatchThread dispatchThread,
2695N/A Conditional extCond)
2695N/A {
2695N/A if (dispatchThread == null) {
2695N/A throw new IllegalArgumentException("The dispatchThread can not be null");
2695N/A }
2695N/A
2695N/A this.dispatchThread = dispatchThread;
2695N/A this.extCondition = extCond;
2695N/A this.condition = new Conditional() {
2695N/A @Override
2695N/A public boolean evaluate() {
2695N/A if (log.isLoggable(PlatformLogger.FINEST)) {
2695N/A log.finest("evaluate(): blockingEDT=" + keepBlockingEDT.get() +
2695N/A ", blockingCT=" + keepBlockingCT.get());
2695N/A }
2695N/A boolean extEvaluate =
2695N/A (extCondition != null) ? extCondition.evaluate() : true;
2695N/A if (!keepBlockingEDT.get() || !extEvaluate) {
2695N/A if (timerTask != null) {
2695N/A timerTask.cancel();
2695N/A timerTask = null;
2695N/A }
2695N/A return false;
2695N/A }
2695N/A return true;
2695N/A }
2695N/A };
2695N/A }
2695N/A
2695N/A /**
2695N/A * Creates a {@code WaitDispatchSupport} instance to
2695N/A * serve the given event dispatch thread.
2695N/A * <p>
2695N/A * The {@link EventFilter} is set on the {@code dispatchThread}
2695N/A * while waiting. The filter is removed on completion of the
2695N/A * waiting process.
2695N/A * <p>
2695N/A *
2695N/A *
2695N/A * @param dispatchThread An event dispatch thread that
2695N/A * should not stop dispatching events while waiting
2695N/A * @param filter {@code EventFilter} to be set
2695N/A * @param interval A time interval to wait for. Note that
2695N/A * when the waiting process takes place on EDT
2695N/A * there is no guarantee to stop it in the given time
2695N/A *
2695N/A * @since 1.7
2695N/A */
2695N/A public WaitDispatchSupport(EventDispatchThread dispatchThread,
2695N/A Conditional extCondition,
2695N/A EventFilter filter, long interval)
2695N/A {
2695N/A this(dispatchThread, extCondition);
2695N/A this.filter = filter;
2695N/A if (interval < 0) {
2695N/A throw new IllegalArgumentException("The interval value must be >= 0");
2695N/A }
2695N/A this.interval = interval;
2695N/A if (interval != 0) {
2695N/A initializeTimer();
2695N/A }
2695N/A }
2695N/A
2695N/A /**
2695N/A * @inheritDoc
2695N/A */
2695N/A @Override
2695N/A public boolean enter() {
2695N/A log.fine("enter(): blockingEDT=" + keepBlockingEDT.get() +
2695N/A ", blockingCT=" + keepBlockingCT.get());
2695N/A
2695N/A if (!keepBlockingEDT.compareAndSet(false, true)) {
2695N/A log.fine("The secondary loop is already running, aborting");
2695N/A return false;
2695N/A }
2695N/A
2695N/A final Runnable run = new Runnable() {
2695N/A public void run() {
2695N/A log.fine("Starting a new event pump");
2695N/A if (filter == null) {
2695N/A dispatchThread.pumpEvents(condition);
2695N/A } else {
2695N/A dispatchThread.pumpEventsForFilter(condition, filter);
2695N/A }
2695N/A }
2695N/A };
2695N/A
2695N/A // We have two mechanisms for blocking: if we're on the
2695N/A // dispatch thread, start a new event pump; if we're
2695N/A // on any other thread, call wait() on the treelock
2695N/A
2695N/A Thread currentThread = Thread.currentThread();
2695N/A if (currentThread == dispatchThread) {
2695N/A log.finest("On dispatch thread: " + dispatchThread);
2695N/A if (interval != 0) {
2695N/A log.finest("scheduling the timer for " + interval + " ms");
2695N/A timer.schedule(timerTask = new TimerTask() {
2695N/A @Override
2695N/A public void run() {
2695N/A if (keepBlockingEDT.compareAndSet(true, false)) {
2695N/A wakeupEDT();
2695N/A }
2695N/A }
2695N/A }, interval);
2695N/A }
2695N/A // Dispose SequencedEvent we are dispatching on the the current
2695N/A // AppContext, to prevent us from hang - see 4531693 for details
2695N/A SequencedEvent currentSE = KeyboardFocusManager.
2695N/A getCurrentKeyboardFocusManager().getCurrentSequencedEvent();
2695N/A if (currentSE != null) {
2695N/A log.fine("Dispose current SequencedEvent: " + currentSE);
2695N/A currentSE.dispose();
2695N/A }
2695N/A // In case the exit() method is called before starting
2695N/A // new event pump it will post the waking event to EDT.
2695N/A // The event will be handled after the the new event pump
2695N/A // starts. Thus, the enter() method will not hang.
2695N/A //
2695N/A // Event pump should be privileged. See 6300270.
2695N/A AccessController.doPrivileged(new PrivilegedAction() {
2695N/A public Object run() {
2695N/A run.run();
2695N/A return null;
2695N/A }
2695N/A });
2695N/A } else {
2695N/A log.finest("On non-dispatch thread: " + currentThread);
2695N/A synchronized (getTreeLock()) {
2695N/A if (filter != null) {
2695N/A dispatchThread.addEventFilter(filter);
2695N/A }
2695N/A try {
2695N/A EventQueue eq = dispatchThread.getEventQueue();
2695N/A eq.postEvent(new PeerEvent(this, run, PeerEvent.PRIORITY_EVENT));
2695N/A keepBlockingCT.set(true);
2695N/A if (interval > 0) {
2695N/A long currTime = System.currentTimeMillis();
2695N/A while (keepBlockingCT.get() &&
2695N/A ((extCondition != null) ? extCondition.evaluate() : true) &&
2695N/A (currTime + interval > System.currentTimeMillis()))
2695N/A {
2695N/A getTreeLock().wait(interval);
2695N/A }
2695N/A } else {
2695N/A while (keepBlockingCT.get() &&
2695N/A ((extCondition != null) ? extCondition.evaluate() : true))
2695N/A {
2695N/A getTreeLock().wait();
2695N/A }
2695N/A }
2695N/A log.fine("waitDone " + keepBlockingEDT.get() + " " + keepBlockingCT.get());
2695N/A } catch (InterruptedException e) {
2695N/A log.fine("Exception caught while waiting: " + e);
2695N/A } finally {
2695N/A if (filter != null) {
2695N/A dispatchThread.removeEventFilter(filter);
2695N/A }
2695N/A }
2695N/A // If the waiting process has been stopped because of the
2695N/A // time interval passed or an exception occurred, the state
2695N/A // should be changed
2695N/A keepBlockingEDT.set(false);
2695N/A keepBlockingCT.set(false);
2695N/A }
2695N/A }
2695N/A
2695N/A return true;
2695N/A }
2695N/A
2695N/A /**
2695N/A * @inheritDoc
2695N/A */
2695N/A public boolean exit() {
2695N/A log.fine("exit(): blockingEDT=" + keepBlockingEDT.get() +
2695N/A ", blockingCT=" + keepBlockingCT.get());
2695N/A if (keepBlockingEDT.compareAndSet(true, false)) {
2695N/A wakeupEDT();
2695N/A return true;
2695N/A }
2695N/A return false;
2695N/A }
2695N/A
2695N/A private final static Object getTreeLock() {
2695N/A return Component.LOCK;
2695N/A }
2695N/A
2695N/A private final Runnable wakingRunnable = new Runnable() {
2695N/A public void run() {
2695N/A log.fine("Wake up EDT");
2695N/A synchronized (getTreeLock()) {
2695N/A keepBlockingCT.set(false);
2695N/A getTreeLock().notifyAll();
2695N/A }
2695N/A log.fine("Wake up EDT done");
2695N/A }
2695N/A };
2695N/A
2695N/A private void wakeupEDT() {
2695N/A log.finest("wakeupEDT(): EDT == " + dispatchThread);
2695N/A EventQueue eq = dispatchThread.getEventQueue();
2695N/A eq.postEvent(new PeerEvent(this, wakingRunnable, PeerEvent.PRIORITY_EVENT));
2695N/A }
2695N/A}