1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import java.awt.AWTEvent; 25 import java.awt.event.InvocationEvent; 26 import java.lang.reflect.Constructor; 27 import java.util.concurrent.CountDownLatch; 28 import java.util.concurrent.atomic.AtomicReference; 29 30 import sun.awt.AppContext; 31 import sun.awt.SunToolkit; 32 33 /** 34 * @test 35 * @bug 8204142 36 * @author Sergey Bylokhov 37 * @key headful 38 * @modules java.desktop/sun.awt 39 * @run main/othervm/timeout=30 MultipleContextsUnitTest 40 */ 41 public final class MultipleContextsUnitTest { 42 43 private static final int COUNT = 20; 44 private static final AppContext[] apps = new AppContext[COUNT]; 45 private static final CountDownLatch go = new CountDownLatch(1); 46 private static final CountDownLatch end = new CountDownLatch(COUNT); 47 48 private static volatile int createSENumber = 0; 49 private static volatile int dispatchSENumber = 0; 50 51 public static void main(final String[] args) throws Exception { 52 for (int i = 0; i < COUNT; i++) { 53 Thread t = testThread(i); 54 t.start(); 55 t.join(); 56 } 57 58 for (AppContext app : apps) { 59 SunToolkit.postEvent(app, new InvocationEvent(new Object(), () -> { 60 try { 61 go.await(); 62 } catch (InterruptedException e) { 63 throw new RuntimeException(e); 64 } 65 })); 66 } 67 68 // eventOne - created first, but posted last 69 AWTEvent eventOne = getSequencedEvent(); 70 { 71 // eventTwo and eventThree - posted in the reverse order 72 AppContext app = apps[1]; 73 AWTEvent eventTwo = getSequencedEvent(); 74 AWTEvent eventThree = getSequencedEvent(); 75 SunToolkit.postEvent(app, eventThree); 76 SunToolkit.postEvent(app, eventTwo); 77 SunToolkit.postEvent(app, new InvocationEvent(new Object(), () -> { 78 System.err.println(AppContext.getAppContext()); 79 end.countDown(); 80 })); 81 } 82 83 for (int i = 2; i < apps.length; i++) { 84 // eventTwo and eventThree - posted in the correct order 85 AppContext app = apps[i]; 86 87 AWTEvent eventTwo = getSequencedEvent(); 88 SunToolkit.postEvent(app, eventTwo); 89 90 AtomicReference<Boolean> called1 = new AtomicReference(false); 91 AtomicReference<Boolean> called2 = new AtomicReference(false); 92 int num1 = createSENumber; 93 SunToolkit.postEvent(app, new InvocationEvent(new Object(), () -> { 94 if (dispatchSENumber < num1) { 95 throw new RuntimeException("Dispatched too early"); 96 } 97 called1.set(true); 98 if (called2.get()) { 99 throw new RuntimeException("Second event is called before first"); 100 } 101 })); 102 AWTEvent eventThree = getSequencedEvent(); 103 SunToolkit.postEvent(app, eventThree); 104 int num2 = createSENumber; 105 SunToolkit.postEvent(app, new InvocationEvent(new Object(), () -> { 106 if (dispatchSENumber < num2) { 107 throw new RuntimeException("Dispatched too early"); 108 } 109 called2.set(true); 110 if (!called1.get()) { 111 throw new RuntimeException("First event is not called before second"); 112 } 113 System.err.println(AppContext.getAppContext()); 114 end.countDown(); 115 })); 116 } 117 118 119 120 // eventOne should flush all EDT 121 SunToolkit.postEvent(apps[0], eventOne); 122 SunToolkit.postEvent(apps[0], new InvocationEvent(new Object(), () -> { 123 System.err.println(AppContext.getAppContext()); 124 end.countDown(); 125 })); 126 127 go.countDown(); 128 129 System.err.println("Start to wait"); 130 end.await(); 131 System.err.println("End to wait"); 132 } 133 134 private static Thread testThread(int index) { 135 final ThreadGroup group = new ThreadGroup("TG " + index); 136 return new Thread(group, () -> { 137 apps[index] = SunToolkit.createNewAppContext(); 138 }); 139 } 140 141 private static AWTEvent getSequencedEvent() 142 { 143 int num = createSENumber++; 144 145 InvocationEvent wrapMe = new InvocationEvent(new Object(), () -> { 146 if (num != dispatchSENumber++) { 147 System.err.println("num: " + num); 148 System.err.println("dispatchSENumber: " + dispatchSENumber); 149 throw new RuntimeException("Wrong order"); 150 } 151 }); 152 153 try { 154 /* 155 * SequencedEvent is a package private class, which cannot be instantiated 156 * by importing. So use reflection to create an instance. 157 */ 158 Class<? extends AWTEvent> seqClass = (Class<? extends AWTEvent>) Class.forName("java.awt.SequencedEvent"); 159 Constructor<? extends AWTEvent> 160 seqConst = seqClass.getConstructor(AWTEvent.class); 161 seqConst.setAccessible(true); 162 return seqConst.newInstance(wrapMe); 163 } catch (Throwable err) { 164 throw new RuntimeException("Unable to instantiate SequencedEvent",err); 165 } 166 } 167 }