mirror of
https://github.com/openjdk/jdk.git
synced 2026-04-17 10:20:33 +00:00
6980209: Make tracking SecondaryLoop.enter/exit methods easier
Reviewed-by: serb, ant
This commit is contained in:
parent
bcbb909487
commit
8c26397da5
@ -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<Void>() {
|
||||
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<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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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;
|
||||
}
|
||||
|
||||
241
jdk/test/java/awt/EventQueue/6980209/bug6980209.java
Normal file
241
jdk/test/java/awt/EventQueue/6980209/bug6980209.java
Normal file
@ -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---");
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user