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 }