Mercurial > hg > openjdk > jdk8u > jdk
changeset 13453:5188a1b80628
Merge
author | phh |
---|---|
date | Mon, 25 Feb 2019 22:13:59 +0000 |
parents | 111b0d11b261 (current diff) f30cf8f6a97d (diff) |
children | 09670d5fb69d |
files | |
diffstat | 11 files changed, 636 insertions(+), 42 deletions(-) [+] |
line wrap: on
line diff
--- a/src/share/classes/java/awt/DefaultKeyboardFocusManager.java Mon Feb 25 22:13:39 2019 +0000 +++ b/src/share/classes/java/awt/DefaultKeyboardFocusManager.java Mon Feb 25 22:13:59 2019 +0000 @@ -30,18 +30,17 @@ import java.awt.peer.ComponentPeer; import java.awt.peer.LightweightPeer; import java.lang.ref.WeakReference; +import java.util.Iterator; import java.util.LinkedList; -import java.util.Iterator; import java.util.ListIterator; import java.util.Set; -import sun.util.logging.PlatformLogger; - +import sun.awt.AWTAccessor; import sun.awt.AppContext; +import sun.awt.CausedFocusEvent; import sun.awt.SunToolkit; -import sun.awt.AWTAccessor; -import sun.awt.CausedFocusEvent; import sun.awt.TimedWindowEvent; +import sun.util.logging.PlatformLogger; /** * The default KeyboardFocusManager for AWT applications. Focus traversal is @@ -777,7 +776,7 @@ : NULL_WINDOW_WR; typeAheadAssertions(currentFocusedWindow, we); - if (oppositeWindow == null) { + if (oppositeWindow == null && activeWindow != null) { // Then we need to deactive the active Window as well. // No need to synthesize in other cases, because // WINDOW_ACTIVATED will handle it if necessary.
--- a/src/share/classes/java/awt/SequencedEvent.java Mon Feb 25 22:13:39 2019 +0000 +++ b/src/share/classes/java/awt/SequencedEvent.java Mon Feb 25 22:13:59 2019 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, 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 @@ -25,6 +25,7 @@ package java.awt; +import java.util.Iterator; import java.util.LinkedList; import sun.awt.AWTAccessor; import sun.awt.AppContext; @@ -54,6 +55,7 @@ private final AWTEvent nested; private AppContext appContext; private boolean disposed; + private final LinkedList<AWTEvent> pendingEvents = new LinkedList<>(); static { AWTAccessor.setSequencedEventAccessor(new AWTAccessor.SequencedEventAccessor() { @@ -66,6 +68,35 @@ }); } + private static final class SequencedEventsFilter implements EventFilter { + private final SequencedEvent currentSequencedEvent; + private SequencedEventsFilter(SequencedEvent currentSequencedEvent) { + this.currentSequencedEvent = currentSequencedEvent; + } + @Override + public FilterAction acceptEvent(AWTEvent ev) { + if (ev.getID() == ID) { + // Move forward dispatching only if the event is previous + // in SequencedEvent.list. Otherwise, hold it for reposting later. + synchronized (SequencedEvent.class) { + Iterator<SequencedEvent> it = list.iterator(); + while (it.hasNext()) { + SequencedEvent iev = it.next(); + if (iev.equals(currentSequencedEvent)) { + break; + } else if (iev.equals(ev)) { + return FilterAction.ACCEPT; + } + } + } + } else if (ev.getID() == SentEvent.ID) { + return FilterAction.ACCEPT; + } + currentSequencedEvent.pendingEvents.add(ev); + return FilterAction.REJECT; + } + } + /** * Constructs a new SequencedEvent which will dispatch the specified * nested event. @@ -104,11 +135,8 @@ if (EventQueue.isDispatchThread()) { EventDispatchThread edt = (EventDispatchThread) Thread.currentThread(); - edt.pumpEvents(SentEvent.ID, new Conditional() { - public boolean evaluate() { - return !SequencedEvent.this.isFirstOrDisposed(); - } - }); + edt.pumpEventsForFilter(() -> !SequencedEvent.this.isFirstOrDisposed(), + new SequencedEventsFilter(this)); } else { while(!isFirstOrDisposed()) { synchronized (SequencedEvent.class) { @@ -197,10 +225,6 @@ } disposed = true; } - // Wake myself up - if (appContext != null) { - SunToolkit.postEvent(appContext, new SentEvent()); - } SequencedEvent next = null; @@ -221,5 +245,9 @@ if (next != null && next.appContext != null) { SunToolkit.postEvent(next.appContext, new SentEvent()); } + + for(AWTEvent e : pendingEvents) { + SunToolkit.postEvent(appContext, e); + } } }
--- a/src/share/classes/javax/swing/text/DefaultEditorKit.java Mon Feb 25 22:13:39 2019 +0000 +++ b/src/share/classes/javax/swing/text/DefaultEditorKit.java Mon Feb 25 22:13:59 2019 +0000 @@ -878,11 +878,10 @@ isPrintableMask = ((SunToolkit)tk).isPrintableCharacterModifiersMask(mod); } - if (isPrintableMask) { - char c = content.charAt(0); - if ((c >= 0x20) && (c != 0x7F)) { - target.replaceSelection(content); - } + char c = content.charAt(0); + if ((isPrintableMask && (c >= 0x20) && (c != 0x7F)) || + (!isPrintableMask && (c >= 0x200C) && (c <= 0x200D))) { + target.replaceSelection(content); } } }
--- a/src/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java Mon Feb 25 22:13:39 2019 +0000 +++ b/src/share/classes/sun/security/ssl/ExtendedMasterSecretExtension.java Mon Feb 25 22:13:59 2019 +0000 @@ -1,11 +1,12 @@ /* - * Copyright (c) 2017, Red Hat, Inc. and/or its affiliates. - * + * Copyright (c) 2017, Red Hat, Inc. * 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. + * 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
--- a/src/windows/classes/sun/awt/windows/WToolkit.java Mon Feb 25 22:13:39 2019 +0000 +++ b/src/windows/classes/sun/awt/windows/WToolkit.java Mon Feb 25 22:13:59 2019 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2019, 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 @@ -45,6 +45,7 @@ import sun.awt.AppContext; import sun.awt.AWTAccessor; import sun.awt.AWTAutoShutdown; +import sun.awt.DisplayChangedListener; import sun.awt.LightweightFrame; import sun.awt.SunToolkit; import sun.misc.ThreadGroupUtils; @@ -68,6 +69,8 @@ import java.util.Locale; import java.util.Map; import java.util.Properties; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import sun.font.FontManager; import sun.font.FontManagerFactory; @@ -828,20 +831,34 @@ .paletteChanged(); } + private static ExecutorService displayChangeExecutor; + /* * Called from Toolkit native code when a WM_DISPLAYCHANGE occurs. * Have Win32GraphicsEnvironment execute the display change code on the * Event thread. */ static public void displayChanged() { - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - ((Win32GraphicsEnvironment)GraphicsEnvironment - .getLocalGraphicsEnvironment()) - .displayChanged(); + final Runnable runnable = () -> { + Object lge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + if (lge instanceof DisplayChangedListener) { + ((DisplayChangedListener) lge).displayChanged(); } - }); + }; + if (AppContext.getAppContext() != null) { + // Common case, standalone application + EventQueue.invokeLater(runnable); + } else { + if (displayChangeExecutor == null) { + // No synchronization, called on the Toolkit thread only + displayChangeExecutor = Executors.newFixedThreadPool(1, r -> { + Thread t = Executors.defaultThreadFactory().newThread(r); + t.setDaemon(true); + return t; + }); + } + displayChangeExecutor.submit(runnable); + } } /**
--- a/src/windows/classes/sun/awt/windows/WWindowPeer.java Mon Feb 25 22:13:39 2019 +0000 +++ b/src/windows/classes/sun/awt/windows/WWindowPeer.java Mon Feb 25 22:13:59 2019 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2019, 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 @@ -474,13 +474,7 @@ * Called from native code when we have been dragged onto another screen. */ void draggedToNewScreen() { - SunToolkit.executeOnEventHandlerThread((Component)target,new Runnable() - { - @Override - public void run() { - displayChanged(); - } - }); + displayChanged(); } public void updateGC() { @@ -539,7 +533,7 @@ */ @Override public void displayChanged() { - updateGC(); + SunToolkit.executeOnEventHandlerThread(target, this::updateGC); } /**
--- a/test/TEST.ROOT Mon Feb 25 22:13:39 2019 +0000 +++ b/test/TEST.ROOT Mon Feb 25 22:13:59 2019 +0000 @@ -8,8 +8,11 @@ # would not count as "randomness" by this definition.) Extra care # should be taken to handle test failures of intermittent or # randomness tests. +# +# A "headful" test requires a graphical environment to meaningfully +# run. Tests that are not headful are "headless." -keys=2d dnd i18n intermittent randomness +keys=2d dnd i18n intermittent randomness headful # Tests that must run in othervm mode othervm.dirs=java/awt java/beans java/rmi javax/accessibility javax/imageio javax/sound javax/print javax/management com/sun/awt sun/awt sun/java2d sun/pisces sun/rmi
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/awt/Focus/NullActiveWindowOnFocusLost/NullActiveWindowOnFocusLost.java Mon Feb 25 22:13:59 2019 +0000 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018, 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. + */ + +import java.awt.Button; +import java.awt.Frame; +import java.util.concurrent.TimeUnit; + +import sun.awt.SunToolkit; + +/** + * @test + * @bug 8211435 + * @modules java.desktop/sun.awt + */ +public final class NullActiveWindowOnFocusLost { + + private static volatile long endtime; + private static Throwable failed; + + public static void main(final String[] args) throws Exception { + // Will run the test no more than 30 seconds + endtime = System.nanoTime() + TimeUnit.SECONDS.toNanos(30); + Thread.setDefaultUncaughtExceptionHandler((t, e) -> failed = e); + + final Thread[] threads = new Thread[20]; + for (int i = 0; i < threads.length; i++) { + threads[i] = testThread(i); + } + for (final Thread thread : threads) { + thread.start(); + } + for (final Thread thread : threads) { + thread.join(); + } + if (failed != null) { + failed.printStackTrace(); + throw new RuntimeException(failed); + } + } + + private static Thread testThread(int index) { + return new Thread(new ThreadGroup("TG " + index), () -> { + SunToolkit.createNewAppContext(); + while (!isComplete()) { + final Frame frame = new Frame(); + frame.setSize(300, 300); + frame.add(new Button("Button")); + frame.setLocationRelativeTo(null); + frame.setVisible(true); + try { + Thread.sleep(index); // increase probability of the failure + } catch (InterruptedException ignored) { + } + frame.dispose(); + } + }); + } + + private static boolean isComplete() { + return endtime - System.nanoTime() < 0 || failed != null; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/awt/Toolkit/DisplayChangesException/DisplayChangesException.java Mon Feb 25 22:13:59 2019 +0000 @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2018, 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. + */ + +import java.awt.EventQueue; +import java.awt.GraphicsEnvironment; +import java.awt.Toolkit; +import java.lang.reflect.Method; +import java.util.concurrent.CountDownLatch; + +import javax.swing.JButton; +import javax.swing.JFrame; + +import sun.awt.DisplayChangedListener; +import sun.awt.SunToolkit; + +/** + * @test + * @key headful + * @bug 8207070 + * @modules java.desktop/sun.java2d + * java.desktop/sun.awt + */ +public final class DisplayChangesException { + + private static boolean fail; + private static CountDownLatch go = new CountDownLatch(1); + + static final class TestThread extends Thread { + + private JFrame frame; + + private TestThread(ThreadGroup tg, String threadName) { + super(tg, threadName); + } + + public void run() { + try { + test(); + } catch (final Exception e) { + throw new RuntimeException(e); + } + } + + private void test() throws Exception { + SunToolkit.createNewAppContext(); + EventQueue.invokeAndWait(() -> { + frame = new JFrame(); + final JButton b = new JButton(); + b.addPropertyChangeListener(evt -> { + if (!SunToolkit.isDispatchThreadForAppContext(b)) { + System.err.println("Wrong thread:" + currentThread()); + fail = true; + } + }); + frame.add(b); + frame.setSize(100, 100); + frame.setLocationRelativeTo(null); + frame.pack(); + }); + go.await(); + EventQueue.invokeAndWait(() -> { + frame.dispose(); + }); + } + } + + public static void main(final String[] args) throws Exception { + ThreadGroup tg0 = new ThreadGroup("ThreadGroup0"); + ThreadGroup tg1 = new ThreadGroup("ThreadGroup1"); + + TestThread t0 = new TestThread(tg0, "TestThread 0"); + TestThread t1 = new TestThread(tg1, "TestThread 1"); + + t0.start(); + t1.start(); + Thread.sleep(1500); // Cannot use Robot.waitForIdle + testToolkit(); + Thread.sleep(1500); + testGE(); + Thread.sleep(1500); + go.countDown(); + + if (fail) { + throw new RuntimeException(); + } + } + + private static void testGE() { + Object ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + if (!(ge instanceof DisplayChangedListener)) { + return; + } + ((DisplayChangedListener) ge).displayChanged(); + } + + private static void testToolkit() { + final Class toolkit; + try { + toolkit = Class.forName("sun.awt.windows.WToolkit"); + } catch (final ClassNotFoundException ignored) { + return; + } + try { + final Method displayChanged = toolkit.getMethod("displayChanged"); + displayChanged.invoke(Toolkit.getDefaultToolkit()); + } catch (final Exception e) { + e.printStackTrace(); + fail = true; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/awt/event/SequencedEvent/MultipleContextsFunctionalTest.java Mon Feb 25 22:13:59 2019 +0000 @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2019, 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 8204142 + * @summary Deadlock when queueing SequencedEvent of different AppContexts + * @author Laurent Bourges + * @modules java.desktop/sun.awt + * @run main/othervm/timeout=30 MultipleContextsFunctionalTest + */ + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.SwingUtilities; +import javax.swing.Timer; + +public final class MultipleContextsFunctionalTest { + + private static final long serialVersionUID = 1L; + + private static final int NUM_WINDOW = 2; + private static final int INTERVAL = 50; + private static final int MAX_TIME = 10000; // 10s + private static final int TOLERANCE = 10000;// 10s + private static final int CHECK_LAPSE = 100; + private static final int MAX_COUNT = MAX_TIME / INTERVAL; + private static final int EXPECTED = MAX_COUNT * NUM_WINDOW; + private static final List<TestWindow> WINDOWS = new ArrayList<TestWindow>(); + + public static void main(String[] args) { + for (int i = 0; i < NUM_WINDOW; i++) { + createWin(i); + } + + int total = 0; + int waitingTime = MAX_TIME + TOLERANCE; + while (waitingTime > 0 && total != EXPECTED) { + try { + Thread.sleep(CHECK_LAPSE); + } catch (InterruptedException e) { + e.printStackTrace(); + } + waitingTime -= CHECK_LAPSE; + + total = 0; + for (TestWindow window : WINDOWS) { + total += window.getCounter(); + } + } + + // Failure if AWT hanging: assert + System.out.println("Total [" + total + "] - Expected [" + EXPECTED + "]"); + if (total == EXPECTED) { + System.out.println("Test PASSED"); + return; + } + System.out.println("Test FAILED"); + Runtime.getRuntime().halt(-1); + } + + private static void createWin(int tgNum) { + new Thread(new ThreadGroup("TG " + tgNum), + new Runnable() { + @Override + public void run() { + sun.awt.SunToolkit.createNewAppContext(); + + final AtomicReference<TestWindow> ref = + new AtomicReference<TestWindow>(); + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + final TestWindow window = new TestWindow(tgNum); + window.setVisible(true); + ref.set(window); + WINDOWS.add(window); + } + }); + + // Wait for window to show + TestWindow window = ref.get(); + while (window == null) { + try { + Thread.sleep(100); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + window = ref.get(); + } + window.enableTimer(true); + } + }).start(); + } + + private static final class TestWindow extends JFrame implements ActionListener { + + private final JButton btn; + private int counter = 0; + private final Timer t; + + TestWindow(final int num) { + super("Test Window [" + num + "]"); + setMinimumSize(new Dimension(300, 200)); + setLocation(100 + 400 * (num - 1), 100); + + setLayout(new BorderLayout()); + JLabel textBlock = new JLabel("Lorem ipsum dolor sit amet..."); + add(textBlock); + + btn = new JButton("TEST"); + add(btn, BorderLayout.SOUTH); + + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + pack(); + + t = new Timer(INTERVAL, this); + t.setRepeats(false); + } + + @Override + public void actionPerformed(ActionEvent e) { + this.toFront(); + btn.setText("TEST " + (++counter)); + this.toBack(); + if (counter < MAX_COUNT) { + enableTimer(true); + } else { + dispose(); + } + } + + void enableTimer(boolean enable) { + if (enable) { + t.start(); + } else { + t.stop(); + } + } + + int getCounter() { + return counter; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/awt/event/SequencedEvent/MultipleContextsUnitTest.java Mon Feb 25 22:13:59 2019 +0000 @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2019, 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. + */ + +import java.awt.AWTEvent; +import java.awt.event.InvocationEvent; +import java.lang.reflect.Constructor; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicReference; + +import sun.awt.AppContext; +import sun.awt.SunToolkit; + +/** + * @test + * @bug 8204142 + * @author Sergey Bylokhov + * @modules java.desktop/sun.awt + * @run main/othervm/timeout=30 MultipleContextsUnitTest + */ +public final class MultipleContextsUnitTest { + + private static final int COUNT = 20; + private static final AppContext[] apps = new AppContext[COUNT]; + private static final CountDownLatch go = new CountDownLatch(1); + private static final CountDownLatch end = new CountDownLatch(COUNT); + + private static volatile int createSENumber = 0; + private static volatile int dispatchSENumber = 0; + + public static void main(final String[] args) throws Exception { + for (int i = 0; i < COUNT; i++) { + Thread t = testThread(i); + t.start(); + t.join(); + } + + for (AppContext app : apps) { + SunToolkit.postEvent(app, new InvocationEvent(new Object(), () -> { + try { + go.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + })); + } + + // eventOne - created first, but posted last + AWTEvent eventOne = getSequencedEvent(); + { + // eventTwo and eventThree - posted in the reverse order + AppContext app = apps[1]; + AWTEvent eventTwo = getSequencedEvent(); + AWTEvent eventThree = getSequencedEvent(); + SunToolkit.postEvent(app, eventThree); + SunToolkit.postEvent(app, eventTwo); + SunToolkit.postEvent(app, new InvocationEvent(new Object(), () -> { + System.err.println(AppContext.getAppContext()); + end.countDown(); + })); + } + + for (int i = 2; i < apps.length; i++) { + // eventTwo and eventThree - posted in the correct order + AppContext app = apps[i]; + + AWTEvent eventTwo = getSequencedEvent(); + SunToolkit.postEvent(app, eventTwo); + + AtomicReference<Boolean> called1 = new AtomicReference(false); + AtomicReference<Boolean> called2 = new AtomicReference(false); + int num1 = createSENumber; + SunToolkit.postEvent(app, new InvocationEvent(new Object(), () -> { + if (dispatchSENumber < num1) { + throw new RuntimeException("Dispatched too early"); + } + called1.set(true); + if (called2.get()) { + throw new RuntimeException("Second event is called before first"); + } + })); + AWTEvent eventThree = getSequencedEvent(); + SunToolkit.postEvent(app, eventThree); + int num2 = createSENumber; + SunToolkit.postEvent(app, new InvocationEvent(new Object(), () -> { + if (dispatchSENumber < num2) { + throw new RuntimeException("Dispatched too early"); + } + called2.set(true); + if (!called1.get()) { + throw new RuntimeException("First event is not called before second"); + } + System.err.println(AppContext.getAppContext()); + end.countDown(); + })); + } + + + + // eventOne should flush all EDT + SunToolkit.postEvent(apps[0], eventOne); + SunToolkit.postEvent(apps[0], new InvocationEvent(new Object(), () -> { + System.err.println(AppContext.getAppContext()); + end.countDown(); + })); + + go.countDown(); + + System.err.println("Start to wait"); + end.await(); + System.err.println("End to wait"); + } + + private static Thread testThread(int index) { + final ThreadGroup group = new ThreadGroup("TG " + index); + return new Thread(group, () -> { + apps[index] = SunToolkit.createNewAppContext(); + }); + } + + private static AWTEvent getSequencedEvent() + { + int num = createSENumber++; + + InvocationEvent wrapMe = new InvocationEvent(new Object(), () -> { + if (num != dispatchSENumber++) { + System.err.println("num: " + num); + System.err.println("dispatchSENumber: " + dispatchSENumber); + throw new RuntimeException("Wrong order"); + } + }); + + try { + /* + * SequencedEvent is a package private class, which cannot be instantiated + * by importing. So use reflection to create an instance. + */ + Class<? extends AWTEvent> seqClass = (Class<? extends AWTEvent>) Class.forName("java.awt.SequencedEvent"); + Constructor<? extends AWTEvent> + seqConst = seqClass.getConstructor(AWTEvent.class); + seqConst.setAccessible(true); + return seqConst.newInstance(wrapMe); + } catch (Throwable err) { + throw new RuntimeException("Unable to instantiate SequencedEvent",err); + } + } +}