1 /*
2  * Copyright (c) 2018, 2020, 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/java.awt:open 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 
main(final String[] args)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 
testThread(int index)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 
getSequencedEvent()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 }
168