1 /*
   2  * Copyright (c) 2019, 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  * @modules java.desktop/sun.awt
  38  * @run main/othervm/timeout=30 MultipleContextsUnitTest
  39  */
  40 public final class MultipleContextsUnitTest {
  41 
  42     private static final int COUNT = 20;
  43     private static final AppContext[] apps = new AppContext[COUNT];
  44     private static final CountDownLatch go = new CountDownLatch(1);
  45     private static final CountDownLatch end = new CountDownLatch(COUNT);
  46 
  47     private static volatile int createSENumber = 0;
  48     private static volatile int dispatchSENumber = 0;
  49 
  50     public static void main(final String[] args) throws Exception {
  51         for (int i = 0; i < COUNT; i++) {
  52             Thread t = testThread(i);
  53             t.start();
  54             t.join();
  55         }
  56 
  57         for (AppContext app : apps) {
  58             SunToolkit.postEvent(app, new InvocationEvent(new Object(), () -> {
  59                 try {
  60                     go.await();
  61                 } catch (InterruptedException e) {
  62                     throw new RuntimeException(e);
  63                 }
  64             }));
  65         }
  66 
  67         // eventOne - created first, but posted last
  68         AWTEvent eventOne = getSequencedEvent();
  69         {
  70             // eventTwo and eventThree - posted in the reverse order
  71             AppContext app = apps[1];
  72             AWTEvent eventTwo = getSequencedEvent();
  73             AWTEvent eventThree = getSequencedEvent();
  74             SunToolkit.postEvent(app, eventThree);
  75             SunToolkit.postEvent(app, eventTwo);
  76             SunToolkit.postEvent(app, new InvocationEvent(new Object(), () -> {
  77                 System.err.println(AppContext.getAppContext());
  78                 end.countDown();
  79             }));
  80         }
  81 
  82         for (int i = 2; i < apps.length; i++) {
  83             // eventTwo and eventThree - posted in the correct order
  84             AppContext app = apps[i];
  85 
  86             AWTEvent eventTwo = getSequencedEvent();
  87             SunToolkit.postEvent(app, eventTwo);
  88 
  89             AtomicReference<Boolean> called1 = new AtomicReference(false);
  90             AtomicReference<Boolean> called2 = new AtomicReference(false);
  91             int num1 = createSENumber;
  92             SunToolkit.postEvent(app, new InvocationEvent(new Object(), () -> {
  93                 if (dispatchSENumber < num1) {
  94                     throw new RuntimeException("Dispatched too early");
  95                 }
  96                 called1.set(true);
  97                 if (called2.get()) {
  98                     throw new RuntimeException("Second event is called before first");
  99                 }
 100             }));
 101             AWTEvent eventThree = getSequencedEvent();
 102             SunToolkit.postEvent(app, eventThree);
 103             int num2 = createSENumber;
 104             SunToolkit.postEvent(app, new InvocationEvent(new Object(), () -> {
 105                 if (dispatchSENumber < num2) {
 106                     throw new RuntimeException("Dispatched too early");
 107                 }
 108                 called2.set(true);
 109                 if (!called1.get()) {
 110                     throw new RuntimeException("First event is not called before second");
 111                 }
 112                 System.err.println(AppContext.getAppContext());
 113                 end.countDown();
 114             }));
 115         }
 116 
 117 
 118 
 119         // eventOne should flush all EDT
 120         SunToolkit.postEvent(apps[0], eventOne);
 121         SunToolkit.postEvent(apps[0], new InvocationEvent(new Object(), () -> {
 122             System.err.println(AppContext.getAppContext());
 123             end.countDown();
 124         }));
 125 
 126         go.countDown();
 127 
 128         System.err.println("Start to wait");
 129         end.await();
 130         System.err.println("End to wait");
 131     }
 132 
 133     private static Thread testThread(int index) {
 134         final ThreadGroup group = new ThreadGroup("TG " + index);
 135         return new Thread(group, () -> {
 136             apps[index] = SunToolkit.createNewAppContext();
 137         });
 138     }
 139 
 140     private static AWTEvent getSequencedEvent()
 141     {
 142         int num = createSENumber++;
 143 
 144         InvocationEvent wrapMe = new InvocationEvent(new Object(), () -> {
 145             if (num != dispatchSENumber++) {
 146                 System.err.println("num: " + num);
 147                 System.err.println("dispatchSENumber: " + dispatchSENumber);
 148                 throw new RuntimeException("Wrong order");
 149             }
 150         });
 151 
 152         try {
 153             /*
 154              * SequencedEvent is a package private class, which cannot be instantiated
 155              * by importing. So use reflection to create an instance.
 156              */
 157             Class<? extends AWTEvent> seqClass = (Class<? extends AWTEvent>) Class.forName("java.awt.SequencedEvent");
 158             Constructor<? extends AWTEvent>
 159                     seqConst = seqClass.getConstructor(AWTEvent.class);
 160             seqConst.setAccessible(true);
 161             return seqConst.newInstance(wrapMe);
 162         } catch (Throwable err) {
 163             throw new RuntimeException("Unable to instantiate SequencedEvent",err);
 164         }
 165     }
 166 }