6949936: Provide API for running nested events loops, similar to what modal dialogs do
Reviewed-by: ant, anthony
--- a/jdk/src/share/classes/java/awt/Dialog.java Wed Jul 05 17:19:35 2017 +0200
+++ b/jdk/src/share/classes/java/awt/Dialog.java Tue Aug 24 12:54:46 2010 +0400
@@ -277,10 +277,8 @@
*/
String title;
- private transient volatile boolean keepBlockingEDT = false;
- private transient volatile boolean keepBlockingCT = false;
-
private transient ModalEventFilter modalFilter;
+ private transient volatile SecondaryLoop secondaryLoop;
/*
* Indicates that this dialog is being hidden. This flag is set to true at
@@ -1005,12 +1003,6 @@
super.setVisible(b);
}
- /**
- * Stores the app context on which event dispatch thread the dialog
- * is being shown. Initialized in show(), used in hideAndDisposeHandler()
- */
- transient private AppContext showAppContext;
-
/**
* Makes the {@code Dialog} visible. If the dialog and/or its owner
* are not yet displayable, both are made displayable. The
@@ -1037,39 +1029,18 @@
if (!isModal()) {
conditionalShow(null, null);
} else {
- // Set this variable before calling conditionalShow(). That
- // way, if the Dialog is hidden right after being shown, we
- // won't mistakenly block this thread.
- keepBlockingEDT = true;
- keepBlockingCT = true;
-
- // Store the app context on which this dialog is being shown.
- // Event dispatch thread of this app context will be sleeping until
- // we wake it by any event from hideAndDisposeHandler().
- showAppContext = AppContext.getAppContext();
+ AppContext showAppContext = AppContext.getAppContext();
AtomicLong time = new AtomicLong();
Component predictedFocusOwner = null;
try {
predictedFocusOwner = getMostRecentFocusOwner();
if (conditionalShow(predictedFocusOwner, time)) {
- // We have two mechanisms for blocking: 1. If we're on the
- // EventDispatchThread, start a new event pump. 2. If we're
- // on any other thread, call wait() on the treelock.
-
modalFilter = ModalEventFilter.createFilterForDialog(this);
-
- final Runnable pumpEventsForFilter = new Runnable() {
- public void run() {
- EventDispatchThread dispatchThread =
- (EventDispatchThread)Thread.currentThread();
- dispatchThread.pumpEventsForFilter(new Conditional() {
- public boolean evaluate() {
- synchronized (getTreeLock()) {
- return keepBlockingEDT && windowClosingException == null;
- }
- }
- }, modalFilter);
+ Conditional cond = new Conditional() {
+ @Override
+ public boolean evaluate() {
+ return windowClosingException == null;
}
};
@@ -1096,44 +1067,10 @@
modalityPushed();
try {
- if (EventQueue.isDispatchThread()) {
- /*
- * dispose SequencedEvent we are dispatching on current
- * AppContext, to prevent us from hang.
- *
- */
- // BugId 4531693 (son@sparc.spb.su)
- SequencedEvent currentSequencedEvent = KeyboardFocusManager.
- getCurrentKeyboardFocusManager().getCurrentSequencedEvent();
- if (currentSequencedEvent != null) {
- currentSequencedEvent.dispose();
- }
-
- /*
- * Event processing is done inside doPrivileged block so that
- * it wouldn't matter even if user code is on the stack
- * Fix for BugId 6300270
- */
-
- AccessController.doPrivileged(new PrivilegedAction() {
- public Object run() {
- pumpEventsForFilter.run();
- return null;
- }
- });
- } else {
- synchronized (getTreeLock()) {
- Toolkit.getEventQueue().postEvent(new PeerEvent(this,
- pumpEventsForFilter,
- PeerEvent.PRIORITY_EVENT));
- while (keepBlockingCT && windowClosingException == null) {
- try {
- getTreeLock().wait();
- } catch (InterruptedException e) {
- break;
- }
- }
- }
+ EventQueue eventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
+ secondaryLoop = eventQueue.createSecondaryLoop(cond, modalFilter, 5000);
+ if (!secondaryLoop.enter()) {
+ secondaryLoop = null;
}
} finally {
modalityPopped();
@@ -1194,18 +1131,11 @@
windowClosingException = null;
}
}
- final class WakingRunnable implements Runnable {
- public void run() {
- synchronized (getTreeLock()) {
- keepBlockingCT = false;
- getTreeLock().notifyAll();
- }
- }
- }
+
private void hideAndDisposePreHandler() {
isInHide = true;
synchronized (getTreeLock()) {
- if (keepBlockingEDT) {
+ if (secondaryLoop != null) {
modalHide();
// dialog can be shown and then disposed before its
// modal filter is created
@@ -1217,20 +1147,9 @@
}
}
private void hideAndDisposeHandler() {
- synchronized (getTreeLock()) {
- if (keepBlockingEDT) {
- keepBlockingEDT = false;
- PeerEvent wakingEvent = new PeerEvent(getToolkit(), new WakingRunnable(), PeerEvent.PRIORITY_EVENT);
- AppContext curAppContext = AppContext.getAppContext();
- if (showAppContext != curAppContext) {
- // Wake up event dispatch thread on which the dialog was
- // initially shown
- SunToolkit.postEvent(showAppContext, wakingEvent);
- showAppContext = null;
- } else {
- Toolkit.getEventQueue().postEvent(wakingEvent);
- }
- }
+ if (secondaryLoop != null) {
+ secondaryLoop.exit();
+ secondaryLoop = null;
}
isInHide = false;
}
--- a/jdk/src/share/classes/java/awt/EventDispatchThread.java Wed Jul 05 17:19:35 2017 +0200
+++ b/jdk/src/share/classes/java/awt/EventDispatchThread.java Tue Aug 24 12:54:46 2010 +0400
@@ -113,8 +113,7 @@
pumpEventsForHierarchy(id, cond, null);
}
- void pumpEventsForHierarchy(int id, Conditional cond, Component modalComponent)
- {
+ void pumpEventsForHierarchy(int id, Conditional cond, Component modalComponent) {
pumpEventsForFilter(id, cond, new HierarchyEventFilter(modalComponent));
}
@@ -124,6 +123,7 @@
void pumpEventsForFilter(int id, Conditional cond, EventFilter filter) {
addEventFilter(filter);
+ doDispatch = true;
while (doDispatch && cond.evaluate()) {
if (isInterrupted() || !pumpOneEventForFilters(id)) {
doDispatch = false;
@@ -133,6 +133,7 @@
}
void addEventFilter(EventFilter filter) {
+ eventLog.finest("adding the event filter: " + filter);
synchronized (eventFilters) {
if (!eventFilters.contains(filter)) {
if (filter instanceof ModalEventFilter) {
@@ -156,6 +157,7 @@
}
void removeEventFilter(EventFilter filter) {
+ eventLog.finest("removing the event filter: " + filter);
synchronized (eventFilters) {
eventFilters.remove(filter);
}
--- a/jdk/src/share/classes/java/awt/EventQueue.java Wed Jul 05 17:19:35 2017 +0200
+++ b/jdk/src/share/classes/java/awt/EventQueue.java Tue Aug 24 12:54:46 2010 +0400
@@ -884,6 +884,41 @@
}
/**
+ * Creates a new {@code secondary loop} associated with this
+ * event queue. Use the {@link SecondaryLoop#enter} and
+ * {@link SecondaryLoop#exit} methods to start and stop the
+ * event loop and dispatch the events from this queue.
+ *
+ * @return secondaryLoop A new secondary loop object, which can
+ * be used to launch a new nested event
+ * loop and dispatch events from this queue
+ *
+ * @see SecondaryLoop#enter
+ * @see SecondaryLoop#exit
+ *
+ * @since 1.7
+ */
+ public SecondaryLoop createSecondaryLoop() {
+ return createSecondaryLoop(null, null, 0);
+ }
+
+ SecondaryLoop createSecondaryLoop(Conditional cond, EventFilter filter, long interval) {
+ pushPopLock.lock();
+ try {
+ if (nextQueue != null) {
+ // Forward the request to the top of EventQueue stack
+ return nextQueue.createSecondaryLoop(cond, filter, interval);
+ }
+ if (dispatchThread == null) {
+ initDispatchThread();
+ }
+ return new WaitDispatchSupport(dispatchThread, cond, filter, interval);
+ } finally {
+ pushPopLock.unlock();
+ }
+ }
+
+ /**
* Returns true if the calling thread is
* {@link Toolkit#getSystemEventQueue the current AWT EventQueue}'s
* dispatch thread. Use this method to ensure that a particular
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/awt/SecondaryLoop.java Tue Aug 24 12:54:46 2010 +0400
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.awt;
+
+/**
+ * A helper interface to run the nested event loop.
+ * <p>
+ * Objects that implement this interface are created with the
+ * {@link EventQueue#createSecondaryLoop} method. The interface
+ * provides two methods, {@link enter} and {@link exit},
+ * which can be used to start and stop the event loop.
+ * <p>
+ * When the {@link enter} method is called, the current
+ * thread is blocked until the loop is terminated by the
+ * {@link exit} method. Also, a new event loop is started
+ * on the event dispatch thread, which may or may not be
+ * the current thread. The loop can be terminated on any
+ * thread by calling its {@link exit} method. After the
+ * loop is terminated, the {@code SecondaryLoop} object can
+ * be reused to run a new nested event loop.
+ * <p>
+ * A typical use case of applying this interface is AWT
+ * and Swing modal dialogs. When a modal dialog is shown on
+ * the event dispatch thread, it enters a new secondary loop.
+ * Later, when the dialog is hidden or disposed, it exits
+ * the loop, and the thread continues its execution.
+ * <p>
+ * The following example illustrates a simple use case of
+ * secondary loops:
+ *
+ * <pre>
+ * SecondaryLoop loop;
+ *
+ * JButton jButton = new JButton("Button");
+ * jButton.addActionListener(new ActionListener() {
+ * {@code @Override}
+ * public void actionPerformed(ActionEvent e) {
+ * Toolkit tk = Toolkit.getDefaultToolkit();
+ * EventQueue eq = tk.getSystemEventQueue();
+ * loop = eq.createSecondaryLoop();
+ *
+ * // Spawn a new thread to do the work
+ * Thread worker = new WorkerThread();
+ * worker.start();
+ *
+ * // Enter the loop to block the current event
+ * // handler, but leave UI responsive
+ * if (!loop.enter()) {
+ * // Report an error
+ * }
+ * }
+ * });
+ *
+ * class WorkerThread extends Thread {
+ * {@code @Override}
+ * public void run() {
+ * // Perform calculations
+ * doSomethingUseful();
+ *
+ * // Exit the loop
+ * loop.exit();
+ * }
+ * }
+ * </pre>
+ *
+ * @see Dialog#show
+ * @see EventQueue#createSecondaryLoop
+ * @see Toolkit#getSystemEventQueue
+ *
+ * @author Anton Tarasov, Artem Ananiev
+ *
+ * @since 1.7
+ */
+public interface SecondaryLoop {
+
+ /**
+ * Blocks the execution of the current thread and enters a new
+ * secondary event loop on the event dispatch thread.
+ * <p>
+ * This method can be called by any thread including the event
+ * dispatch thread. This thread will be blocked until the {@link
+ * exit} method is called or the loop is terminated. A new
+ * secondary loop will be created on the event dispatch thread
+ * for dispatching events in either case.
+ * <p>
+ * This method can only start one new event loop at a time per
+ * object. If a secondary event loop has already been started
+ * by this object and is currently still running, this method
+ * returns {@code false} to indicate that it was not successful
+ * in starting a new event loop. Otherwise, this method blocks
+ * the calling thread and later returns {@code true} when the
+ * new event loop is terminated. At such time, this object can
+ * again be used to start another new event loop.
+ *
+ * @return {@code true} after termination of the secondary loop,
+ * if the secondary loop was started by this call,
+ * {@code false} otherwise
+ */
+ public boolean enter();
+
+ /**
+ * Unblocks the execution of the thread blocked by the {@link
+ * enter} method and exits the secondary loop.
+ * <p>
+ * This method resumes the thread that called the {@link enter}
+ * method and exits the secondary loop that was created when
+ * the {@link enter} method was invoked.
+ * <p>
+ * Note that if any other secondary loop is started while this
+ * loop is running, the blocked thread will not resume execution
+ * until the nested loop is terminated.
+ * <p>
+ * If this secondary loop has not been started with the {@link
+ * enter} method, or this secondary loop has already finished
+ * with the {@link exit} method, this method returns {@code
+ * false}, otherwise {@code true} is returned.
+ *
+ * @return {@code true} if this loop was previously started and
+ * has not yet been finished with the {@link exit} method,
+ * {@code false} otherwise
+ */
+ public boolean exit();
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/awt/WaitDispatchSupport.java Tue Aug 24 12:54:46 2010 +0400
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+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 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 extCondition 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.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() {
+ 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;
+ }
+
+ 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) {
+ log.finest("On dispatch thread: " + dispatchThread);
+ if (interval != 0) {
+ 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 the current
+ // AppContext, to prevent us from hang - see 4531693 for details
+ SequencedEvent currentSE = KeyboardFocusManager.
+ getCurrentKeyboardFocusManager().getCurrentSequencedEvent();
+ if (currentSE != null) {
+ 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 the new event pump
+ // starts. Thus, the enter() method will not hang.
+ //
+ // Event pump should be privileged. See 6300270.
+ AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ run.run();
+ return null;
+ }
+ });
+ } else {
+ log.finest("On non-dispatch thread: " + currentThread);
+ synchronized (getTreeLock()) {
+ if (filter != null) {
+ dispatchThread.addEventFilter(filter);
+ }
+ try {
+ EventQueue eq = dispatchThread.getEventQueue();
+ eq.postEvent(new PeerEvent(this, run, PeerEvent.PRIORITY_EVENT));
+ keepBlockingCT.set(true);
+ 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();
+ }
+ }
+ log.fine("waitDone " + keepBlockingEDT.get() + " " + keepBlockingCT.get());
+ } catch (InterruptedException e) {
+ log.fine("Exception caught while waiting: " + e);
+ } finally {
+ if (filter != null) {
+ dispatchThread.removeEventFilter(filter);
+ }
+ }
+ // If the waiting process has been stopped because of the
+ // time interval passed or an exception occurred, the state
+ // should be changed
+ keepBlockingEDT.set(false);
+ keepBlockingCT.set(false);
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public boolean exit() {
+ log.fine("exit(): blockingEDT=" + keepBlockingEDT.get() +
+ ", blockingCT=" + keepBlockingCT.get());
+ if (keepBlockingEDT.compareAndSet(true, 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() {
+ log.finest("wakeupEDT(): EDT == " + dispatchThread);
+ EventQueue eq = dispatchThread.getEventQueue();
+ eq.postEvent(new PeerEvent(this, wakingRunnable, PeerEvent.PRIORITY_EVENT));
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/EventQueue/SecondaryLoopTest/SecondaryLoopTest.java Tue Aug 24 12:54:46 2010 +0400
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ @test
+ @bug 6949936
+ @author Artem Ananiev: area=eventqueue
+ @run main/timeout=30 SecondaryLoopTest
+*/
+
+import java.awt.*;
+
+/**
+ * Unit test for java.awt.SecondaryLoop implementation
+ */
+public class SecondaryLoopTest {
+
+ private static volatile boolean loopStarted;
+ private static volatile boolean doubleEntered;
+ private static volatile boolean loopActive;
+ private static volatile boolean eventDispatched;
+
+ public static void main(String[] args) throws Exception {
+ test(true, true);
+ test(true, false);
+ test(false, true);
+ test(false, false);
+ }
+
+ private static void test(final boolean enterEDT, final boolean exitEDT) throws Exception {
+ System.out.println("Running test(" + enterEDT + ", " + exitEDT + ")");
+ System.err.flush();
+ loopStarted = true;
+ Runnable enterRun = new Runnable() {
+ @Override
+ public void run() {
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ EventQueue eq = tk.getSystemEventQueue();
+ final SecondaryLoop loop = eq.createSecondaryLoop();
+ doubleEntered = false;
+ eventDispatched = false;
+ Runnable eventRun = new Runnable() {
+ @Override
+ public void run() {
+ // Let the loop enter
+ sleep(1000);
+ if (loop.enter()) {
+ doubleEntered = true;
+ }
+ eventDispatched = true;
+ }
+ };
+ EventQueue.invokeLater(eventRun);
+ Runnable exitRun = new Runnable() {
+ @Override
+ public void run() {
+ // Let the loop enter and eventRun finish
+ sleep(2000);
+ if (doubleEntered) {
+ // Hopefully, we get here if the loop is entered twice
+ loop.exit();
+ }
+ loop.exit();
+ }
+ };
+ if (exitEDT) {
+ EventQueue.invokeLater(exitRun);
+ } else {
+ new Thread(exitRun).start();
+ }
+ if (!loop.enter()) {
+ loopStarted = false;
+ }
+ loopActive = eventDispatched;
+ }
+ };
+ if (enterEDT) {
+ EventQueue.invokeAndWait(enterRun);
+ } else {
+ enterRun.run();
+ }
+ // Print all the flags before we fail with exception
+ System.out.println(" loopStarted = " + loopStarted);
+ System.out.println(" doubleEntered = " + doubleEntered);
+ System.out.println(" loopActive = " + loopActive);
+ System.out.flush();
+ if (!loopStarted) {
+ throw new RuntimeException("Test FAILED: the secondary loop is not started");
+ }
+ if (doubleEntered) {
+ throw new RuntimeException("Test FAILED: the secondary loop is started twice");
+ }
+ if (!loopActive) {
+ throw new RuntimeException("Test FAILED: the secondary loop exited immediately");
+ }
+ }
+
+ private static void sleep(long t) {
+ try {
+ Thread.sleep(t);
+ } catch (InterruptedException e) {
+ e.printStackTrace(System.err);
+ }
+ }
+
+}