diff --git a/jdk/src/java.desktop/share/classes/java/awt/WaitDispatchSupport.java b/jdk/src/java.desktop/share/classes/java/awt/WaitDispatchSupport.java index 15129a457ea..d3a692ab5a8 100644 --- a/jdk/src/java.desktop/share/classes/java/awt/WaitDispatchSupport.java +++ b/jdk/src/java.desktop/share/classes/java/awt/WaitDispatchSupport.java @@ -65,6 +65,7 @@ class WaitDispatchSupport implements SecondaryLoop { 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) { @@ -114,7 +115,7 @@ class WaitDispatchSupport implements SecondaryLoop { } boolean extEvaluate = (extCondition != null) ? extCondition.evaluate() : true; - if (!keepBlockingEDT.get() || !extEvaluate) { + if (!keepBlockingEDT.get() || !extEvaluate || afterExit.get()) { if (timerTask != null) { timerTask.cancel(); timerTask = null; @@ -174,110 +175,116 @@ class WaitDispatchSupport implements SecondaryLoop { 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() { - public Void run() { - run.run(); - return null; - } - }); - } else { - if (log.isLoggable(PlatformLogger.Level.FINEST)) { - 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); - } + final Runnable run = new Runnable() { + public void run() { + log.fine("Starting a new event pump"); + if (filter == null) { + dispatchThread.pumpEvents(condition); } 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); + dispatchThread.pumpEventsForFilter(condition, 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; + // 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() { + 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); + } } /** @@ -288,7 +295,8 @@ class WaitDispatchSupport implements SecondaryLoop { log.fine("exit(): blockingEDT=" + keepBlockingEDT.get() + ", blockingCT=" + keepBlockingCT.get()); } - if (keepBlockingEDT.compareAndSet(true, false)) { + afterExit.set(true); + if (keepBlockingEDT.getAndSet(false)) { wakeupEDT(); return true; } diff --git a/jdk/test/java/awt/EventQueue/6980209/bug6980209.java b/jdk/test/java/awt/EventQueue/6980209/bug6980209.java new file mode 100644 index 00000000000..d39235ae44c --- /dev/null +++ b/jdk/test/java/awt/EventQueue/6980209/bug6980209.java @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2015, 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 6980209 + @summary Make tracking SecondaryLoop.enter/exit methods easier + @author Semyon Sadetsky + */ + +import sun.util.logging.PlatformLogger; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + +public class bug6980209 implements ActionListener { + private final static PlatformLogger log = + PlatformLogger.getLogger("java.awt.event.WaitDispatchSupport"); + public static final int ATTEMPTS = 100; + public static final int EVENTS = 5; + + private static boolean runInEDT; + private static JFrame frame; + private static int disorderCounter = 0; + private static Boolean enterReturn; + private static Boolean exitReturn; + private static int dispatchedEvents; + + public static void main(String[] args) throws Exception { + System.out.println( + "PLEASE DO NOT TOUCH KEYBOARD AND MOUSE DURING THE TEST RUN!"); + // log.setLevel(PlatformLogger.Level.FINE); + // log.setLevel(PlatformLogger.Level.FINEST); + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + frame = new JFrame(); + frame.setUndecorated(true); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setup(frame); + } + }); + testExitBeforeEnter(); + System.out.println("Run random test in EDT"); + runInEDT = true; + testRandomly(); + System.out.println("Run random test in another thread"); + runInEDT = false; + testRandomly(); + System.out.println("ok"); + + } finally { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + frame.dispose(); + } + }); + } + } + + private static void testExitBeforeEnter() throws Exception { + final SecondaryLoop loop = + Toolkit.getDefaultToolkit().getSystemEventQueue() + .createSecondaryLoop(); + loop.exit(); + Robot robot = new Robot(); + robot.mouseWheel(1); + robot.waitForIdle(); + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + if(loop.enter()) { + throw new RuntimeException("Wrong enter() return value"); + } + } + }); + } + + private static void testRandomly() throws AWTException { + disorderCounter = 0; + final Robot robot = new Robot(); + for (int i = 0; i < ATTEMPTS; i++) { + enterReturn = null; + exitReturn = null; + dispatchedEvents = 0; + synchronized (bug6980209.class) { + try { + for (int j = 0; j < EVENTS; j++) { + robot.keyPress(KeyEvent.VK_1); + robot.keyRelease(KeyEvent.VK_1); + } + + // trigger the button action that starts secondary loop + robot.keyPress(KeyEvent.VK_SPACE); + robot.keyRelease(KeyEvent.VK_SPACE); + + for (int j = 0; j < EVENTS; j++) { + robot.keyPress(KeyEvent.VK_1); + robot.keyRelease(KeyEvent.VK_1); + } + long time = System.nanoTime(); + // wait for enter() returns + bug6980209.class.wait(1000); + if (enterReturn == null) { + System.out.println("wait time=" + + ((System.nanoTime() - time) / 1E9) + + " seconds"); + throw new RuntimeException( + "It seems the secondary loop will never end"); + } + if (!enterReturn) disorderCounter++; + + robot.waitForIdle(); + if (dispatchedEvents < + 2 * EVENTS) { //check that all events are dispatched + throw new RuntimeException( + "KeyEvent.VK_1 has been lost!"); + } + + } catch (InterruptedException e) { + throw new RuntimeException("Interrupted!"); + } + } + } + if (disorderCounter == 0) { + System.out.println( + "Zero disordered enter/exit caught. It is recommended to run scenario again"); + } else { + System.out.println( + "Disordered calls is " + disorderCounter + " from " + + ATTEMPTS); + } + } + + private static void setup(final JFrame frame) { + JButton jButton = new JButton("Button"); + frame.getContentPane().add(jButton); + jButton.addActionListener(new bug6980209()); + frame.pack(); + frame.setVisible(true); + jButton.setFocusable(true); + jButton.requestFocus(); + jButton.addKeyListener(new KeyListener() { + @Override + public void keyTyped(KeyEvent e) { + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyChar() == '1') dispatchedEvents++; + } + + @Override + public void keyReleased(KeyEvent e) { + if (e.getKeyChar() == '1') dispatchedEvents++; + } + }); + } + + + @Override + public void actionPerformed(ActionEvent e) { + if (runInEDT) { + runSecondaryLoop(); + return; + } + new Thread("Secondary loop run thread") { + @Override + public void run() { + runSecondaryLoop(); + } + }.start(); + } + + private static void runSecondaryLoop() { + log.fine("\n---TEST START---"); + + final SecondaryLoop loop = + Toolkit.getDefaultToolkit().getSystemEventQueue() + .createSecondaryLoop(); + + final Object LOCK = new Object(); //lock to start simultaneously + Thread exitThread = new Thread("Exit thread") { + @Override + public void run() { + synchronized (LOCK) { + LOCK.notify(); + } + Thread.yield(); + exitReturn = loop.exit(); + log.fine("exit() returns " + exitReturn); + } + }; + + synchronized (LOCK) { + try { + exitThread.start(); + LOCK.wait(); + } catch (InterruptedException e1) { + throw new RuntimeException("What?"); + } + } + + enterReturn = loop.enter(); + log.fine("enter() returns " + enterReturn); + + try { + exitThread.join(); + } catch (InterruptedException e) { + throw new RuntimeException("What?"); + } + synchronized (bug6980209.class) { + bug6980209.class.notifyAll(); + } + log.fine("\n---TEST END---"); + } +}