1 /*
   2  * Copyright (c) 1996, 2017, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.awt;
  27 
  28 import java.awt.event.*;
  29 
  30 import java.awt.peer.ComponentPeer;
  31 
  32 import java.lang.ref.WeakReference;
  33 import java.lang.reflect.InvocationTargetException;
  34 
  35 import java.security.AccessController;
  36 import java.security.PrivilegedAction;
  37 
  38 import java.util.EmptyStackException;
  39 
  40 import sun.awt.*;
  41 import sun.awt.dnd.SunDropTargetEvent;
  42 import sun.util.logging.PlatformLogger;
  43 
  44 import java.util.concurrent.locks.Condition;
  45 import java.util.concurrent.locks.Lock;
  46 import java.util.concurrent.atomic.AtomicInteger;
  47 
  48 import java.security.AccessControlContext;
  49 
  50 import jdk.internal.misc.SharedSecrets;
  51 import jdk.internal.misc.JavaSecurityAccess;
  52 
  53 /**
  54  * {@code EventQueue} is a platform-independent class
  55  * that queues events, both from the underlying peer classes
  56  * and from trusted application classes.
  57  * <p>
  58  * It encapsulates asynchronous event dispatch machinery which
  59  * extracts events from the queue and dispatches them by calling
  60  * {@link #dispatchEvent(AWTEvent) dispatchEvent(AWTEvent)} method
  61  * on this {@code EventQueue} with the event to be dispatched
  62  * as an argument.  The particular behavior of this machinery is
  63  * implementation-dependent.  The only requirements are that events
  64  * which were actually enqueued to this queue (note that events
  65  * being posted to the {@code EventQueue} can be coalesced)
  66  * are dispatched:
  67  * <dl>
  68  *   <dt> Sequentially.
  69  *   <dd> That is, it is not permitted that several events from
  70  *        this queue are dispatched simultaneously.
  71  *   <dt> In the same order as they are enqueued.
  72  *   <dd> That is, if {@code AWTEvent}&nbsp;A is enqueued
  73  *        to the {@code EventQueue} before
  74  *        {@code AWTEvent}&nbsp;B then event B will not be
  75  *        dispatched before event A.
  76  * </dl>
  77  * <p>
  78  * Some browsers partition applets in different code bases into
  79  * separate contexts, and establish walls between these contexts.
  80  * In such a scenario, there will be one {@code EventQueue}
  81  * per context. Other browsers place all applets into the same
  82  * context, implying that there will be only a single, global
  83  * {@code EventQueue} for all applets. This behavior is
  84  * implementation-dependent.  Consult your browser's documentation
  85  * for more information.
  86  * <p>
  87  * For information on the threading issues of the event dispatch
  88  * machinery, see <a href="doc-files/AWTThreadIssues.html#Autoshutdown"
  89  * >AWT Threading Issues</a>.
  90  *
  91  * @author Thomas Ball
  92  * @author Fred Ecks
  93  * @author David Mendenhall
  94  *
  95  * @since       1.1
  96  */
  97 public class EventQueue {
  98     private static final AtomicInteger threadInitNumber = new AtomicInteger(0);
  99 
 100     private static final int LOW_PRIORITY = 0;
 101     private static final int NORM_PRIORITY = 1;
 102     private static final int HIGH_PRIORITY = 2;
 103     private static final int ULTIMATE_PRIORITY = 3;
 104 
 105     private static final int NUM_PRIORITIES = ULTIMATE_PRIORITY + 1;
 106 
 107     /*
 108      * We maintain one Queue for each priority that the EventQueue supports.
 109      * That is, the EventQueue object is actually implemented as
 110      * NUM_PRIORITIES queues and all Events on a particular internal Queue
 111      * have identical priority. Events are pulled off the EventQueue starting
 112      * with the Queue of highest priority. We progress in decreasing order
 113      * across all Queues.
 114      */
 115     private Queue[] queues = new Queue[NUM_PRIORITIES];
 116 
 117     /*
 118      * The next EventQueue on the stack, or null if this EventQueue is
 119      * on the top of the stack.  If nextQueue is non-null, requests to post
 120      * an event are forwarded to nextQueue.
 121      */
 122     private EventQueue nextQueue;
 123 
 124     /*
 125      * The previous EventQueue on the stack, or null if this is the
 126      * "base" EventQueue.
 127      */
 128     private EventQueue previousQueue;
 129 
 130     /*
 131      * A single lock to synchronize the push()/pop() and related operations with
 132      * all the EventQueues from the AppContext. Synchronization on any particular
 133      * event queue(s) is not enough: we should lock the whole stack.
 134      */
 135     private final Lock pushPopLock;
 136     private final Condition pushPopCond;
 137 
 138     /*
 139      * Dummy runnable to wake up EDT from getNextEvent() after
 140      push/pop is performed
 141      */
 142     private static final Runnable dummyRunnable = new Runnable() {
 143         public void run() {
 144         }
 145     };
 146 
 147     private EventDispatchThread dispatchThread;
 148 
 149     private final ThreadGroup threadGroup =
 150         Thread.currentThread().getThreadGroup();
 151     private final ClassLoader classLoader =
 152         Thread.currentThread().getContextClassLoader();
 153 
 154     /*
 155      * The time stamp of the last dispatched InputEvent or ActionEvent.
 156      */
 157     private long mostRecentEventTime = System.currentTimeMillis();
 158 
 159     /*
 160      * The time stamp of the last KeyEvent .
 161      */
 162     private long mostRecentKeyEventTime = System.currentTimeMillis();
 163 
 164     /**
 165      * The modifiers field of the current event, if the current event is an
 166      * InputEvent or ActionEvent.
 167      */
 168     private WeakReference<AWTEvent> currentEvent;
 169 
 170     /*
 171      * Non-zero if a thread is waiting in getNextEvent(int) for an event of
 172      * a particular ID to be posted to the queue.
 173      */
 174     private volatile int waitForID;
 175 
 176     /*
 177      * AppContext corresponding to the queue.
 178      */
 179     private final AppContext appContext;
 180 
 181     private final String name = "AWT-EventQueue-" + threadInitNumber.getAndIncrement();
 182 
 183     private FwDispatcher fwDispatcher;
 184 
 185     private static volatile PlatformLogger eventLog;
 186 
 187     private static final PlatformLogger getEventLog() {
 188         if(eventLog == null) {
 189             eventLog = PlatformLogger.getLogger("java.awt.event.EventQueue");
 190         }
 191         return eventLog;
 192     }
 193 
 194     static {
 195         AWTAccessor.setEventQueueAccessor(
 196             new AWTAccessor.EventQueueAccessor() {
 197                 public Thread getDispatchThread(EventQueue eventQueue) {
 198                     return eventQueue.getDispatchThread();
 199                 }
 200                 public boolean isDispatchThreadImpl(EventQueue eventQueue) {
 201                     return eventQueue.isDispatchThreadImpl();
 202                 }
 203                 public void removeSourceEvents(EventQueue eventQueue,
 204                                                Object source,
 205                                                boolean removeAllEvents)
 206                 {
 207                     eventQueue.removeSourceEvents(source, removeAllEvents);
 208                 }
 209                 public boolean noEvents(EventQueue eventQueue) {
 210                     return eventQueue.noEvents();
 211                 }
 212                 public void wakeup(EventQueue eventQueue, boolean isShutdown) {
 213                     eventQueue.wakeup(isShutdown);
 214                 }
 215                 public void invokeAndWait(Object source, Runnable r)
 216                     throws InterruptedException, InvocationTargetException
 217                 {
 218                     EventQueue.invokeAndWait(source, r);
 219                 }
 220                 public void setFwDispatcher(EventQueue eventQueue,
 221                                             FwDispatcher dispatcher) {
 222                     eventQueue.setFwDispatcher(dispatcher);
 223                 }
 224 
 225                 @Override
 226                 public long getMostRecentEventTime(EventQueue eventQueue) {
 227                     return eventQueue.getMostRecentEventTimeImpl();
 228                 }
 229             });
 230     }
 231 
 232     /**
 233      * Initializes a new instance of {@code EventQueue}.
 234      */
 235     public EventQueue() {
 236         for (int i = 0; i < NUM_PRIORITIES; i++) {
 237             queues[i] = new Queue();
 238         }
 239         /*
 240          * NOTE: if you ever have to start the associated event dispatch
 241          * thread at this point, be aware of the following problem:
 242          * If this EventQueue instance is created in
 243          * SunToolkit.createNewAppContext() the started dispatch thread
 244          * may call AppContext.getAppContext() before createNewAppContext()
 245          * completes thus causing mess in thread group to appcontext mapping.
 246          */
 247 
 248         appContext = AppContext.getAppContext();
 249         pushPopLock = (Lock)appContext.get(AppContext.EVENT_QUEUE_LOCK_KEY);
 250         pushPopCond = (Condition)appContext.get(AppContext.EVENT_QUEUE_COND_KEY);
 251     }
 252 
 253     /**
 254      * Posts a 1.1-style event to the {@code EventQueue}.
 255      * If there is an existing event on the queue with the same ID
 256      * and event source, the source {@code Component}'s
 257      * {@code coalesceEvents} method will be called.
 258      *
 259      * @param theEvent an instance of {@code java.awt.AWTEvent},
 260      *          or a subclass of it
 261      * @throws NullPointerException if {@code theEvent} is {@code null}
 262      */
 263     public void postEvent(AWTEvent theEvent) {
 264         SunToolkit.flushPendingEvents(appContext);
 265         postEventPrivate(theEvent);
 266     }
 267 
 268     /**
 269      * Posts a 1.1-style event to the {@code EventQueue}.
 270      * If there is an existing event on the queue with the same ID
 271      * and event source, the source {@code Component}'s
 272      * {@code coalesceEvents} method will be called.
 273      *
 274      * @param theEvent an instance of {@code java.awt.AWTEvent},
 275      *          or a subclass of it
 276      */
 277     private final void postEventPrivate(AWTEvent theEvent) {
 278         theEvent.isPosted = true;
 279         pushPopLock.lock();
 280         try {
 281             if (nextQueue != null) {
 282                 // Forward the event to the top of EventQueue stack
 283                 nextQueue.postEventPrivate(theEvent);
 284                 return;
 285             }
 286             if (dispatchThread == null) {
 287                 if (theEvent.getSource() == AWTAutoShutdown.getInstance()) {
 288                     return;
 289                 } else {
 290                     initDispatchThread();
 291                 }
 292             }
 293             postEvent(theEvent, getPriority(theEvent));
 294         } finally {
 295             pushPopLock.unlock();
 296         }
 297     }
 298 
 299     private static int getPriority(AWTEvent theEvent) {
 300         if (theEvent instanceof PeerEvent) {
 301             PeerEvent peerEvent = (PeerEvent)theEvent;
 302             if ((peerEvent.getFlags() & PeerEvent.ULTIMATE_PRIORITY_EVENT) != 0) {
 303                 return ULTIMATE_PRIORITY;
 304             }
 305             if ((peerEvent.getFlags() & PeerEvent.PRIORITY_EVENT) != 0) {
 306                 return HIGH_PRIORITY;
 307             }
 308             if ((peerEvent.getFlags() & PeerEvent.LOW_PRIORITY_EVENT) != 0) {
 309                 return LOW_PRIORITY;
 310             }
 311         }
 312         int id = theEvent.getID();
 313         if ((id >= PaintEvent.PAINT_FIRST) && (id <= PaintEvent.PAINT_LAST)) {
 314             return LOW_PRIORITY;
 315         }
 316         return NORM_PRIORITY;
 317     }
 318 
 319     /**
 320      * Posts the event to the internal Queue of specified priority,
 321      * coalescing as appropriate.
 322      *
 323      * @param theEvent an instance of {@code java.awt.AWTEvent},
 324      *          or a subclass of it
 325      * @param priority  the desired priority of the event
 326      */
 327     private void postEvent(AWTEvent theEvent, int priority) {
 328         if (coalesceEvent(theEvent, priority)) {
 329             return;
 330         }
 331 
 332         EventQueueItem newItem = new EventQueueItem(theEvent);
 333 
 334         cacheEQItem(newItem);
 335 
 336         boolean notifyID = (theEvent.getID() == this.waitForID);
 337 
 338         if (queues[priority].head == null) {
 339             boolean shouldNotify = noEvents();
 340             queues[priority].head = queues[priority].tail = newItem;
 341 
 342             if (shouldNotify) {
 343                 if (theEvent.getSource() != AWTAutoShutdown.getInstance()) {
 344                     AWTAutoShutdown.getInstance().notifyThreadBusy(dispatchThread);
 345                 }
 346                 pushPopCond.signalAll();
 347             } else if (notifyID) {
 348                 pushPopCond.signalAll();
 349             }
 350         } else {
 351             // The event was not coalesced or has non-Component source.
 352             // Insert it at the end of the appropriate Queue.
 353             queues[priority].tail.next = newItem;
 354             queues[priority].tail = newItem;
 355             if (notifyID) {
 356                 pushPopCond.signalAll();
 357             }
 358         }
 359     }
 360 
 361     private boolean coalescePaintEvent(PaintEvent e) {
 362         ComponentPeer sourcePeer = ((Component)e.getSource()).peer;
 363         if (sourcePeer != null) {
 364             sourcePeer.coalescePaintEvent(e);
 365         }
 366         EventQueueItem[] cache = ((Component)e.getSource()).eventCache;
 367         if (cache == null) {
 368             return false;
 369         }
 370         int index = eventToCacheIndex(e);
 371 
 372         if (index != -1 && cache[index] != null) {
 373             PaintEvent merged = mergePaintEvents(e, (PaintEvent)cache[index].event);
 374             if (merged != null) {
 375                 cache[index].event = merged;
 376                 return true;
 377             }
 378         }
 379         return false;
 380     }
 381 
 382     private PaintEvent mergePaintEvents(PaintEvent a, PaintEvent b) {
 383         Rectangle aRect = a.getUpdateRect();
 384         Rectangle bRect = b.getUpdateRect();
 385         if (bRect.contains(aRect)) {
 386             return b;
 387         }
 388         if (aRect.contains(bRect)) {
 389             return a;
 390         }
 391         return null;
 392     }
 393 
 394     private boolean coalesceMouseEvent(MouseEvent e) {
 395         EventQueueItem[] cache = ((Component)e.getSource()).eventCache;
 396         if (cache == null) {
 397             return false;
 398         }
 399         int index = eventToCacheIndex(e);
 400         if (index != -1 && cache[index] != null) {
 401             cache[index].event = e;
 402             return true;
 403         }
 404         return false;
 405     }
 406 
 407     private boolean coalescePeerEvent(PeerEvent e) {
 408         EventQueueItem[] cache = ((Component)e.getSource()).eventCache;
 409         if (cache == null) {
 410             return false;
 411         }
 412         int index = eventToCacheIndex(e);
 413         if (index != -1 && cache[index] != null) {
 414             e = e.coalesceEvents((PeerEvent)cache[index].event);
 415             if (e != null) {
 416                 cache[index].event = e;
 417                 return true;
 418             } else {
 419                 cache[index] = null;
 420             }
 421         }
 422         return false;
 423     }
 424 
 425     /*
 426      * Should avoid of calling this method by any means
 427      * as it's working time is dependent on EQ length.
 428      * In the worst case this method alone can slow down the entire application
 429      * 10 times by stalling the Event processing.
 430      * Only here by backward compatibility reasons.
 431      */
 432     private boolean coalesceOtherEvent(AWTEvent e, int priority) {
 433         int id = e.getID();
 434         Component source = (Component)e.getSource();
 435         for (EventQueueItem entry = queues[priority].head;
 436             entry != null; entry = entry.next)
 437         {
 438             // Give Component.coalesceEvents a chance
 439             if (entry.event.getSource() == source && entry.event.getID() == id) {
 440                 AWTEvent coalescedEvent = source.coalesceEvents(
 441                     entry.event, e);
 442                 if (coalescedEvent != null) {
 443                     entry.event = coalescedEvent;
 444                     return true;
 445                 }
 446             }
 447         }
 448         return false;
 449     }
 450 
 451     private boolean coalesceEvent(AWTEvent e, int priority) {
 452         if (!(e.getSource() instanceof Component)) {
 453             return false;
 454         }
 455         if (e instanceof PeerEvent) {
 456             return coalescePeerEvent((PeerEvent)e);
 457         }
 458         // The worst case
 459         if (((Component)e.getSource()).isCoalescingEnabled()
 460             && coalesceOtherEvent(e, priority))
 461         {
 462             return true;
 463         }
 464         if (e instanceof PaintEvent) {
 465             return coalescePaintEvent((PaintEvent)e);
 466         }
 467         if (e instanceof MouseEvent) {
 468             return coalesceMouseEvent((MouseEvent)e);
 469         }
 470         return false;
 471     }
 472 
 473     private void cacheEQItem(EventQueueItem entry) {
 474         int index = eventToCacheIndex(entry.event);
 475         if (index != -1 && entry.event.getSource() instanceof Component) {
 476             Component source = (Component)entry.event.getSource();
 477             if (source.eventCache == null) {
 478                 source.eventCache = new EventQueueItem[CACHE_LENGTH];
 479             }
 480             source.eventCache[index] = entry;
 481         }
 482     }
 483 
 484     private void uncacheEQItem(EventQueueItem entry) {
 485         int index = eventToCacheIndex(entry.event);
 486         if (index != -1 && entry.event.getSource() instanceof Component) {
 487             Component source = (Component)entry.event.getSource();
 488             if (source.eventCache == null) {
 489                 return;
 490             }
 491             source.eventCache[index] = null;
 492         }
 493     }
 494 
 495     private static final int PAINT = 0;
 496     private static final int UPDATE = 1;
 497     private static final int MOVE = 2;
 498     private static final int DRAG = 3;
 499     private static final int PEER = 4;
 500     private static final int CACHE_LENGTH = 5;
 501 
 502     private static int eventToCacheIndex(AWTEvent e) {
 503         switch(e.getID()) {
 504         case PaintEvent.PAINT:
 505             return PAINT;
 506         case PaintEvent.UPDATE:
 507             return UPDATE;
 508         case MouseEvent.MOUSE_MOVED:
 509             return MOVE;
 510         case MouseEvent.MOUSE_DRAGGED:
 511             // Return -1 for SunDropTargetEvent since they are usually synchronous
 512             // and we don't want to skip them by coalescing with MouseEvent or other drag events
 513             return e instanceof SunDropTargetEvent ? -1 : DRAG;
 514         default:
 515             return e instanceof PeerEvent ? PEER : -1;
 516         }
 517     }
 518 
 519     /**
 520      * Returns whether an event is pending on any of the separate
 521      * Queues.
 522      * @return whether an event is pending on any of the separate Queues
 523      */
 524     private boolean noEvents() {
 525         for (int i = 0; i < NUM_PRIORITIES; i++) {
 526             if (queues[i].head != null) {
 527                 return false;
 528             }
 529         }
 530 
 531         return true;
 532     }
 533 
 534     /**
 535      * Removes an event from the {@code EventQueue} and
 536      * returns it.  This method will block until an event has
 537      * been posted by another thread.
 538      * @return the next {@code AWTEvent}
 539      * @exception InterruptedException
 540      *            if any thread has interrupted this thread
 541      */
 542     public AWTEvent getNextEvent() throws InterruptedException {
 543         do {
 544             /*
 545              * SunToolkit.flushPendingEvents must be called outside
 546              * of the synchronized block to avoid deadlock when
 547              * event queues are nested with push()/pop().
 548              */
 549             SunToolkit.flushPendingEvents(appContext);
 550             pushPopLock.lock();
 551             try {
 552                 AWTEvent event = getNextEventPrivate();
 553                 if (event != null) {
 554                     return event;
 555                 }
 556                 AWTAutoShutdown.getInstance().notifyThreadFree(dispatchThread);
 557                 pushPopCond.await();
 558             } finally {
 559                 pushPopLock.unlock();
 560             }
 561         } while(true);
 562     }
 563 
 564     /*
 565      * Must be called under the lock. Doesn't call flushPendingEvents()
 566      */
 567     AWTEvent getNextEventPrivate() throws InterruptedException {
 568         for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
 569             if (queues[i].head != null) {
 570                 EventQueueItem entry = queues[i].head;
 571                 queues[i].head = entry.next;
 572                 if (entry.next == null) {
 573                     queues[i].tail = null;
 574                 }
 575                 uncacheEQItem(entry);
 576                 return entry.event;
 577             }
 578         }
 579         return null;
 580     }
 581 
 582     AWTEvent getNextEvent(int id) throws InterruptedException {
 583         do {
 584             /*
 585              * SunToolkit.flushPendingEvents must be called outside
 586              * of the synchronized block to avoid deadlock when
 587              * event queues are nested with push()/pop().
 588              */
 589             SunToolkit.flushPendingEvents(appContext);
 590             pushPopLock.lock();
 591             try {
 592                 for (int i = 0; i < NUM_PRIORITIES; i++) {
 593                     for (EventQueueItem entry = queues[i].head, prev = null;
 594                          entry != null; prev = entry, entry = entry.next)
 595                     {
 596                         if (entry.event.getID() == id) {
 597                             if (prev == null) {
 598                                 queues[i].head = entry.next;
 599                             } else {
 600                                 prev.next = entry.next;
 601                             }
 602                             if (queues[i].tail == entry) {
 603                                 queues[i].tail = prev;
 604                             }
 605                             uncacheEQItem(entry);
 606                             return entry.event;
 607                         }
 608                     }
 609                 }
 610                 waitForID = id;
 611                 pushPopCond.await();
 612                 waitForID = 0;
 613             } finally {
 614                 pushPopLock.unlock();
 615             }
 616         } while(true);
 617     }
 618 
 619     /**
 620      * Returns the first event on the {@code EventQueue}
 621      * without removing it.
 622      * @return the first event
 623      */
 624     public AWTEvent peekEvent() {
 625         pushPopLock.lock();
 626         try {
 627             for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
 628                 if (queues[i].head != null) {
 629                     return queues[i].head.event;
 630                 }
 631             }
 632         } finally {
 633             pushPopLock.unlock();
 634         }
 635 
 636         return null;
 637     }
 638 
 639     /**
 640      * Returns the first event with the specified id, if any.
 641      * @param id the id of the type of event desired
 642      * @return the first event of the specified id or {@code null}
 643      *    if there is no such event
 644      */
 645     public AWTEvent peekEvent(int id) {
 646         pushPopLock.lock();
 647         try {
 648             for (int i = NUM_PRIORITIES - 1; i >= 0; i--) {
 649                 EventQueueItem q = queues[i].head;
 650                 for (; q != null; q = q.next) {
 651                     if (q.event.getID() == id) {
 652                         return q.event;
 653                     }
 654                 }
 655             }
 656         } finally {
 657             pushPopLock.unlock();
 658         }
 659 
 660         return null;
 661     }
 662 
 663     private static final JavaSecurityAccess javaSecurityAccess =
 664         SharedSecrets.getJavaSecurityAccess();
 665 
 666     /**
 667      * Dispatches an event. The manner in which the event is
 668      * dispatched depends upon the type of the event and the
 669      * type of the event's source object:
 670      *
 671      * <table class="striped">
 672      * <caption>Event types, source types, and dispatch methods</caption>
 673      * <thead>
 674      * <tr>
 675      *     <th>Event Type</th>
 676      *     <th>Source Type</th>
 677      *     <th>Dispatched To</th>
 678      * </tr>
 679      * </thead>
 680      * <tbody>
 681      * <tr>
 682      *     <td>ActiveEvent</td>
 683      *     <td>Any</td>
 684      *     <td>event.dispatch()</td>
 685      * </tr>
 686      * <tr>
 687      *     <td>Other</td>
 688      *     <td>Component</td>
 689      *     <td>source.dispatchEvent(AWTEvent)</td>
 690      * </tr>
 691      * <tr>
 692      *     <td>Other</td>
 693      *     <td>MenuComponent</td>
 694      *     <td>source.dispatchEvent(AWTEvent)</td>
 695      * </tr>
 696      * <tr>
 697      *     <td>Other</td>
 698      *     <td>Other</td>
 699      *     <td>No action (ignored)</td>
 700      * </tr>
 701      * </tbody>
 702      * </table>
 703      *
 704      * @param event an instance of {@code java.awt.AWTEvent},
 705      *          or a subclass of it
 706      * @throws NullPointerException if {@code event} is {@code null}
 707      * @since           1.2
 708      */
 709     protected void dispatchEvent(final AWTEvent event) {
 710         final Object src = event.getSource();
 711         final PrivilegedAction<Void> action = new PrivilegedAction<Void>() {
 712             public Void run() {
 713                 // In case fwDispatcher is installed and we're already on the
 714                 // dispatch thread (e.g. performing DefaultKeyboardFocusManager.sendMessage),
 715                 // dispatch the event straight away.
 716                 if (fwDispatcher == null || isDispatchThreadImpl()) {
 717                     dispatchEventImpl(event, src);
 718                 } else {
 719                     fwDispatcher.scheduleDispatch(new Runnable() {
 720                         @Override
 721                         public void run() {
 722                             if (dispatchThread.filterAndCheckEvent(event)) {
 723                                 dispatchEventImpl(event, src);
 724                             }
 725                         }
 726                     });
 727                 }
 728                 return null;
 729             }
 730         };
 731 
 732         final AccessControlContext stack = AccessController.getContext();
 733         final AccessControlContext srcAcc = getAccessControlContextFrom(src);
 734         final AccessControlContext eventAcc = event.getAccessControlContext();
 735         if (srcAcc == null) {
 736             javaSecurityAccess.doIntersectionPrivilege(action, stack, eventAcc);
 737         } else {
 738             javaSecurityAccess.doIntersectionPrivilege(
 739                 new PrivilegedAction<Void>() {
 740                     public Void run() {
 741                         javaSecurityAccess.doIntersectionPrivilege(action, eventAcc);
 742                         return null;
 743                     }
 744                 }, stack, srcAcc);
 745         }
 746     }
 747 
 748     private static AccessControlContext getAccessControlContextFrom(Object src) {
 749         return src instanceof Component ?
 750             ((Component)src).getAccessControlContext() :
 751             src instanceof MenuComponent ?
 752                 ((MenuComponent)src).getAccessControlContext() :
 753                 src instanceof TrayIcon ?
 754                     ((TrayIcon)src).getAccessControlContext() :
 755                     null;
 756     }
 757 
 758     /**
 759      * Called from dispatchEvent() under a correct AccessControlContext
 760      */
 761     private void dispatchEventImpl(final AWTEvent event, final Object src) {
 762         event.isPosted = true;
 763         if (event instanceof ActiveEvent) {
 764             // This could become the sole method of dispatching in time.
 765             setCurrentEventAndMostRecentTimeImpl(event);
 766             ((ActiveEvent)event).dispatch();
 767         } else if (src instanceof Component) {
 768             ((Component)src).dispatchEvent(event);
 769             event.dispatched();
 770         } else if (src instanceof MenuComponent) {
 771             ((MenuComponent)src).dispatchEvent(event);
 772         } else if (src instanceof TrayIcon) {
 773             ((TrayIcon)src).dispatchEvent(event);
 774         } else if (src instanceof AWTAutoShutdown) {
 775             if (noEvents()) {
 776                 dispatchThread.stopDispatching();
 777             }
 778         } else {
 779             if (getEventLog().isLoggable(PlatformLogger.Level.FINE)) {
 780                 getEventLog().fine("Unable to dispatch event: " + event);
 781             }
 782         }
 783     }
 784 
 785     /**
 786      * Returns the timestamp of the most recent event that had a timestamp, and
 787      * that was dispatched from the {@code EventQueue} associated with the
 788      * calling thread. If an event with a timestamp is currently being
 789      * dispatched, its timestamp will be returned. If no events have yet
 790      * been dispatched, the EventQueue's initialization time will be
 791      * returned instead.In the current version of
 792      * the JDK, only {@code InputEvent}s,
 793      * {@code ActionEvent}s, and {@code InvocationEvent}s have
 794      * timestamps; however, future versions of the JDK may add timestamps to
 795      * additional event types. Note that this method should only be invoked
 796      * from an application's {@link #isDispatchThread event dispatching thread}.
 797      * If this method is
 798      * invoked from another thread, the current system time (as reported by
 799      * {@code System.currentTimeMillis()}) will be returned instead.
 800      *
 801      * @return the timestamp of the last {@code InputEvent},
 802      *         {@code ActionEvent}, or {@code InvocationEvent} to be
 803      *         dispatched, or {@code System.currentTimeMillis()} if this
 804      *         method is invoked on a thread other than an event dispatching
 805      *         thread
 806      * @see java.awt.event.InputEvent#getWhen
 807      * @see java.awt.event.ActionEvent#getWhen
 808      * @see java.awt.event.InvocationEvent#getWhen
 809      * @see #isDispatchThread
 810      *
 811      * @since 1.4
 812      */
 813     public static long getMostRecentEventTime() {
 814         return Toolkit.getEventQueue().getMostRecentEventTimeImpl();
 815     }
 816     private long getMostRecentEventTimeImpl() {
 817         pushPopLock.lock();
 818         try {
 819             return (Thread.currentThread() == dispatchThread)
 820                 ? mostRecentEventTime
 821                 : System.currentTimeMillis();
 822         } finally {
 823             pushPopLock.unlock();
 824         }
 825     }
 826 
 827     /**
 828      * @return most recent event time on all threads.
 829      */
 830     long getMostRecentEventTimeEx() {
 831         pushPopLock.lock();
 832         try {
 833             return mostRecentEventTime;
 834         } finally {
 835             pushPopLock.unlock();
 836         }
 837     }
 838 
 839     /**
 840      * Returns the event currently being dispatched by the
 841      * {@code EventQueue} associated with the calling thread. This is
 842      * useful if a method needs access to the event, but was not designed to
 843      * receive a reference to it as an argument. Note that this method should
 844      * only be invoked from an application's event dispatching thread. If this
 845      * method is invoked from another thread, null will be returned.
 846      *
 847      * @return the event currently being dispatched, or null if this method is
 848      *         invoked on a thread other than an event dispatching thread
 849      * @since 1.4
 850      */
 851     public static AWTEvent getCurrentEvent() {
 852         return Toolkit.getEventQueue().getCurrentEventImpl();
 853     }
 854     private AWTEvent getCurrentEventImpl() {
 855         pushPopLock.lock();
 856         try {
 857                 return (Thread.currentThread() == dispatchThread)
 858                 ? currentEvent.get()
 859                 : null;
 860         } finally {
 861             pushPopLock.unlock();
 862         }
 863     }
 864 
 865     /**
 866      * Replaces the existing {@code EventQueue} with the specified one.
 867      * Any pending events are transferred to the new {@code EventQueue}
 868      * for processing by it.
 869      *
 870      * @param newEventQueue an {@code EventQueue}
 871      *          (or subclass thereof) instance to be use
 872      * @see      java.awt.EventQueue#pop
 873      * @throws NullPointerException if {@code newEventQueue} is {@code null}
 874      * @since           1.2
 875      */
 876     public void push(EventQueue newEventQueue) {
 877         if (getEventLog().isLoggable(PlatformLogger.Level.FINE)) {
 878             getEventLog().fine("EventQueue.push(" + newEventQueue + ")");
 879         }
 880 
 881         pushPopLock.lock();
 882         try {
 883             EventQueue topQueue = this;
 884             while (topQueue.nextQueue != null) {
 885                 topQueue = topQueue.nextQueue;
 886             }
 887             if (topQueue.fwDispatcher != null) {
 888                 throw new RuntimeException("push() to queue with fwDispatcher");
 889             }
 890             if ((topQueue.dispatchThread != null) &&
 891                 (topQueue.dispatchThread.getEventQueue() == this))
 892             {
 893                 newEventQueue.dispatchThread = topQueue.dispatchThread;
 894                 topQueue.dispatchThread.setEventQueue(newEventQueue);
 895             }
 896 
 897             // Transfer all events forward to new EventQueue.
 898             while (topQueue.peekEvent() != null) {
 899                 try {
 900                     // Use getNextEventPrivate() as it doesn't call flushPendingEvents()
 901                     newEventQueue.postEventPrivate(topQueue.getNextEventPrivate());
 902                 } catch (InterruptedException ie) {
 903                     if (getEventLog().isLoggable(PlatformLogger.Level.FINE)) {
 904                         getEventLog().fine("Interrupted push", ie);
 905                     }
 906                 }
 907             }
 908 
 909             if (topQueue.dispatchThread != null) {
 910                 // Wake up EDT waiting in getNextEvent(), so it can
 911                 // pick up a new EventQueue. Post the waking event before
 912                 // topQueue.nextQueue is assigned, otherwise the event would
 913                 // go newEventQueue
 914                 topQueue.postEventPrivate(new InvocationEvent(topQueue, dummyRunnable));
 915             }
 916 
 917             newEventQueue.previousQueue = topQueue;
 918             topQueue.nextQueue = newEventQueue;
 919 
 920             if (appContext.get(AppContext.EVENT_QUEUE_KEY) == topQueue) {
 921                 appContext.put(AppContext.EVENT_QUEUE_KEY, newEventQueue);
 922             }
 923 
 924             pushPopCond.signalAll();
 925         } finally {
 926             pushPopLock.unlock();
 927         }
 928     }
 929 
 930     /**
 931      * Stops dispatching events using this {@code EventQueue}.
 932      * Any pending events are transferred to the previous
 933      * {@code EventQueue} for processing.
 934      * <p>
 935      * Warning: To avoid deadlock, do not declare this method
 936      * synchronized in a subclass.
 937      *
 938      * @exception EmptyStackException if no previous push was made
 939      *  on this {@code EventQueue}
 940      * @see      java.awt.EventQueue#push
 941      * @since           1.2
 942      */
 943     protected void pop() throws EmptyStackException {
 944         if (getEventLog().isLoggable(PlatformLogger.Level.FINE)) {
 945             getEventLog().fine("EventQueue.pop(" + this + ")");
 946         }
 947 
 948         pushPopLock.lock();
 949         try {
 950             EventQueue topQueue = this;
 951             while (topQueue.nextQueue != null) {
 952                 topQueue = topQueue.nextQueue;
 953             }
 954             EventQueue prevQueue = topQueue.previousQueue;
 955             if (prevQueue == null) {
 956                 throw new EmptyStackException();
 957             }
 958 
 959             topQueue.previousQueue = null;
 960             prevQueue.nextQueue = null;
 961 
 962             // Transfer all events back to previous EventQueue.
 963             while (topQueue.peekEvent() != null) {
 964                 try {
 965                     prevQueue.postEventPrivate(topQueue.getNextEventPrivate());
 966                 } catch (InterruptedException ie) {
 967                     if (getEventLog().isLoggable(PlatformLogger.Level.FINE)) {
 968                         getEventLog().fine("Interrupted pop", ie);
 969                     }
 970                 }
 971             }
 972 
 973             if ((topQueue.dispatchThread != null) &&
 974                 (topQueue.dispatchThread.getEventQueue() == this))
 975             {
 976                 prevQueue.dispatchThread = topQueue.dispatchThread;
 977                 topQueue.dispatchThread.setEventQueue(prevQueue);
 978             }
 979 
 980             if (appContext.get(AppContext.EVENT_QUEUE_KEY) == this) {
 981                 appContext.put(AppContext.EVENT_QUEUE_KEY, prevQueue);
 982             }
 983 
 984             // Wake up EDT waiting in getNextEvent(), so it can
 985             // pick up a new EventQueue
 986             topQueue.postEventPrivate(new InvocationEvent(topQueue, dummyRunnable));
 987 
 988             pushPopCond.signalAll();
 989         } finally {
 990             pushPopLock.unlock();
 991         }
 992     }
 993 
 994     /**
 995      * Creates a new {@code secondary loop} associated with this
 996      * event queue. Use the {@link SecondaryLoop#enter} and
 997      * {@link SecondaryLoop#exit} methods to start and stop the
 998      * event loop and dispatch the events from this queue.
 999      *
1000      * @return secondaryLoop A new secondary loop object, which can
1001      *                       be used to launch a new nested event
1002      *                       loop and dispatch events from this queue
1003      *
1004      * @see SecondaryLoop#enter
1005      * @see SecondaryLoop#exit
1006      *
1007      * @since 1.7
1008      */
1009     public SecondaryLoop createSecondaryLoop() {
1010         return createSecondaryLoop(null, null, 0);
1011     }
1012 
1013     private class FwSecondaryLoopWrapper implements SecondaryLoop {
1014         final private SecondaryLoop loop;
1015         final private EventFilter filter;
1016 
1017         public FwSecondaryLoopWrapper(SecondaryLoop loop, EventFilter filter) {
1018             this.loop = loop;
1019             this.filter = filter;
1020         }
1021 
1022         @Override
1023         public boolean enter() {
1024             if (filter != null) {
1025                 dispatchThread.addEventFilter(filter);
1026             }
1027             return loop.enter();
1028         }
1029 
1030         @Override
1031         public boolean exit() {
1032             if (filter != null) {
1033                 dispatchThread.removeEventFilter(filter);
1034             }
1035             return loop.exit();
1036         }
1037     }
1038 
1039     SecondaryLoop createSecondaryLoop(Conditional cond, EventFilter filter, long interval) {
1040         pushPopLock.lock();
1041         try {
1042             if (nextQueue != null) {
1043                 // Forward the request to the top of EventQueue stack
1044                 return nextQueue.createSecondaryLoop(cond, filter, interval);
1045             }
1046             if (fwDispatcher != null) {
1047                 return new FwSecondaryLoopWrapper(fwDispatcher.createSecondaryLoop(), filter);
1048             }
1049             if (dispatchThread == null) {
1050                 initDispatchThread();
1051             }
1052             return new WaitDispatchSupport(dispatchThread, cond, filter, interval);
1053         } finally {
1054             pushPopLock.unlock();
1055         }
1056     }
1057 
1058     /**
1059      * Returns true if the calling thread is
1060      * {@link Toolkit#getSystemEventQueue the current AWT EventQueue}'s
1061      * dispatch thread. Use this method to ensure that a particular
1062      * task is being executed (or not being) there.
1063      * <p>
1064      * Note: use the {@link #invokeLater} or {@link #invokeAndWait}
1065      * methods to execute a task in
1066      * {@link Toolkit#getSystemEventQueue the current AWT EventQueue}'s
1067      * dispatch thread.
1068      *
1069      * @return true if running in
1070      * {@link Toolkit#getSystemEventQueue the current AWT EventQueue}'s
1071      * dispatch thread
1072      * @see             #invokeLater
1073      * @see             #invokeAndWait
1074      * @see             Toolkit#getSystemEventQueue
1075      * @since           1.2
1076      */
1077     public static boolean isDispatchThread() {
1078         EventQueue eq = Toolkit.getEventQueue();
1079         return eq.isDispatchThreadImpl();
1080     }
1081 
1082     final boolean isDispatchThreadImpl() {
1083         EventQueue eq = this;
1084         pushPopLock.lock();
1085         try {
1086             EventQueue next = eq.nextQueue;
1087             while (next != null) {
1088                 eq = next;
1089                 next = eq.nextQueue;
1090             }
1091             if (eq.fwDispatcher != null) {
1092                 return eq.fwDispatcher.isDispatchThread();
1093             }
1094             return (Thread.currentThread() == eq.dispatchThread);
1095         } finally {
1096             pushPopLock.unlock();
1097         }
1098     }
1099 
1100     final void initDispatchThread() {
1101         pushPopLock.lock();
1102         try {
1103             if (dispatchThread == null && !threadGroup.isDestroyed() && !appContext.isDisposed()) {
1104                 dispatchThread = AccessController.doPrivileged(
1105                     new PrivilegedAction<EventDispatchThread>() {
1106                         public EventDispatchThread run() {
1107                             EventDispatchThread t =
1108                                 new EventDispatchThread(threadGroup,
1109                                                         name,
1110                                                         EventQueue.this);
1111                             t.setContextClassLoader(classLoader);
1112                             t.setPriority(Thread.NORM_PRIORITY + 1);
1113                             t.setDaemon(false);
1114                             AWTAutoShutdown.getInstance().notifyThreadBusy(t);
1115                             return t;
1116                         }
1117                     }
1118                 );
1119                 dispatchThread.start();
1120             }
1121         } finally {
1122             pushPopLock.unlock();
1123         }
1124     }
1125 
1126     final void detachDispatchThread(EventDispatchThread edt) {
1127         /*
1128          * Minimize discard possibility for non-posted events
1129          */
1130         SunToolkit.flushPendingEvents(appContext);
1131         /*
1132          * This synchronized block is to secure that the event dispatch
1133          * thread won't die in the middle of posting a new event to the
1134          * associated event queue. It is important because we notify
1135          * that the event dispatch thread is busy after posting a new event
1136          * to its queue, so the EventQueue.dispatchThread reference must
1137          * be valid at that point.
1138          */
1139         pushPopLock.lock();
1140         try {
1141             if (edt == dispatchThread) {
1142                 dispatchThread = null;
1143             }
1144             AWTAutoShutdown.getInstance().notifyThreadFree(edt);
1145             /*
1146              * Event was posted after EDT events pumping had stopped, so start
1147              * another EDT to handle this event
1148              */
1149             if (peekEvent() != null) {
1150                 initDispatchThread();
1151             }
1152         } finally {
1153             pushPopLock.unlock();
1154         }
1155     }
1156 
1157     /*
1158      * Gets the {@code EventDispatchThread} for this
1159      * {@code EventQueue}.
1160      * @return the event dispatch thread associated with this event queue
1161      *         or {@code null} if this event queue doesn't have a
1162      *         working thread associated with it
1163      * @see    java.awt.EventQueue#initDispatchThread
1164      * @see    java.awt.EventQueue#detachDispatchThread
1165      */
1166     final EventDispatchThread getDispatchThread() {
1167         pushPopLock.lock();
1168         try {
1169             return dispatchThread;
1170         } finally {
1171             pushPopLock.unlock();
1172         }
1173     }
1174 
1175     /*
1176      * Removes any pending events for the specified source object.
1177      * If removeAllEvents parameter is {@code true} then all
1178      * events for the specified source object are removed, if it
1179      * is {@code false} then {@code SequencedEvent}, {@code SentEvent},
1180      * {@code FocusEvent}, {@code WindowEvent}, {@code KeyEvent},
1181      * and {@code InputMethodEvent} are kept in the queue, but all other
1182      * events are removed.
1183      *
1184      * This method is normally called by the source's
1185      * {@code removeNotify} method.
1186      */
1187     final void removeSourceEvents(Object source, boolean removeAllEvents) {
1188         SunToolkit.flushPendingEvents(appContext);
1189         pushPopLock.lock();
1190         try {
1191             for (int i = 0; i < NUM_PRIORITIES; i++) {
1192                 EventQueueItem entry = queues[i].head;
1193                 EventQueueItem prev = null;
1194                 while (entry != null) {
1195                     if ((entry.event.getSource() == source)
1196                         && (removeAllEvents
1197                             || ! (entry.event instanceof SequencedEvent
1198                                   || entry.event instanceof SentEvent
1199                                   || entry.event instanceof FocusEvent
1200                                   || entry.event instanceof WindowEvent
1201                                   || entry.event instanceof KeyEvent
1202                                   || entry.event instanceof InputMethodEvent)))
1203                     {
1204                         if (entry.event instanceof SequencedEvent) {
1205                             ((SequencedEvent)entry.event).dispose();
1206                         }
1207                         if (entry.event instanceof SentEvent) {
1208                             ((SentEvent)entry.event).dispose();
1209                         }
1210                         if (entry.event instanceof InvocationEvent) {
1211                             AWTAccessor.getInvocationEventAccessor()
1212                                     .dispose((InvocationEvent)entry.event);
1213                         }
1214                         if (entry.event instanceof SunDropTargetEvent) {
1215                             ((SunDropTargetEvent)entry.event).dispose();
1216                         }
1217                         if (prev == null) {
1218                             queues[i].head = entry.next;
1219                         } else {
1220                             prev.next = entry.next;
1221                         }
1222                         uncacheEQItem(entry);
1223                     } else {
1224                         prev = entry;
1225                     }
1226                     entry = entry.next;
1227                 }
1228                 queues[i].tail = prev;
1229             }
1230         } finally {
1231             pushPopLock.unlock();
1232         }
1233     }
1234 
1235     synchronized long getMostRecentKeyEventTime() {
1236         pushPopLock.lock();
1237         try {
1238             return mostRecentKeyEventTime;
1239         } finally {
1240             pushPopLock.unlock();
1241         }
1242     }
1243 
1244     static void setCurrentEventAndMostRecentTime(AWTEvent e) {
1245         Toolkit.getEventQueue().setCurrentEventAndMostRecentTimeImpl(e);
1246     }
1247     private void setCurrentEventAndMostRecentTimeImpl(AWTEvent e) {
1248         pushPopLock.lock();
1249         try {
1250             if (Thread.currentThread() != dispatchThread) {
1251                 return;
1252             }
1253 
1254             currentEvent = new WeakReference<>(e);
1255 
1256             // This series of 'instanceof' checks should be replaced with a
1257             // polymorphic type (for example, an interface which declares a
1258             // getWhen() method). However, this would require us to make such
1259             // a type public, or to place it in sun.awt. Both of these approaches
1260             // have been frowned upon. So for now, we hack.
1261             //
1262             // In tiger, we will probably give timestamps to all events, so this
1263             // will no longer be an issue.
1264             long mostRecentEventTime2 = Long.MIN_VALUE;
1265             if (e instanceof InputEvent) {
1266                 InputEvent ie = (InputEvent)e;
1267                 mostRecentEventTime2 = ie.getWhen();
1268                 if (e instanceof KeyEvent) {
1269                     mostRecentKeyEventTime = ie.getWhen();
1270                 }
1271             } else if (e instanceof InputMethodEvent) {
1272                 InputMethodEvent ime = (InputMethodEvent)e;
1273                 mostRecentEventTime2 = ime.getWhen();
1274             } else if (e instanceof ActionEvent) {
1275                 ActionEvent ae = (ActionEvent)e;
1276                 mostRecentEventTime2 = ae.getWhen();
1277             } else if (e instanceof InvocationEvent) {
1278                 InvocationEvent ie = (InvocationEvent)e;
1279                 mostRecentEventTime2 = ie.getWhen();
1280             }
1281             mostRecentEventTime = Math.max(mostRecentEventTime, mostRecentEventTime2);
1282         } finally {
1283             pushPopLock.unlock();
1284         }
1285     }
1286 
1287     /**
1288      * Causes {@code runnable} to have its {@code run}
1289      * method called in the {@link #isDispatchThread dispatch thread} of
1290      * {@link Toolkit#getSystemEventQueue the system EventQueue}.
1291      * This will happen after all pending events are processed.
1292      *
1293      * @param runnable  the {@code Runnable} whose {@code run}
1294      *                  method should be executed
1295      *                  asynchronously in the
1296      *                  {@link #isDispatchThread event dispatch thread}
1297      *                  of {@link Toolkit#getSystemEventQueue the system EventQueue}
1298      * @see             #invokeAndWait
1299      * @see             Toolkit#getSystemEventQueue
1300      * @see             #isDispatchThread
1301      * @since           1.2
1302      */
1303     public static void invokeLater(Runnable runnable) {
1304         Toolkit.getEventQueue().postEvent(
1305             new InvocationEvent(Toolkit.getDefaultToolkit(), runnable));
1306     }
1307 
1308     /**
1309      * Causes {@code runnable} to have its {@code run}
1310      * method called in the {@link #isDispatchThread dispatch thread} of
1311      * {@link Toolkit#getSystemEventQueue the system EventQueue}.
1312      * This will happen after all pending events are processed.
1313      * The call blocks until this has happened.  This method
1314      * will throw an Error if called from the
1315      * {@link #isDispatchThread event dispatcher thread}.
1316      *
1317      * @param runnable  the {@code Runnable} whose {@code run}
1318      *                  method should be executed
1319      *                  synchronously in the
1320      *                  {@link #isDispatchThread event dispatch thread}
1321      *                  of {@link Toolkit#getSystemEventQueue the system EventQueue}
1322      * @exception       InterruptedException  if any thread has
1323      *                  interrupted this thread
1324      * @exception       InvocationTargetException  if an throwable is thrown
1325      *                  when running {@code runnable}
1326      * @see             #invokeLater
1327      * @see             Toolkit#getSystemEventQueue
1328      * @see             #isDispatchThread
1329      * @since           1.2
1330      */
1331     public static void invokeAndWait(Runnable runnable)
1332         throws InterruptedException, InvocationTargetException
1333     {
1334         invokeAndWait(Toolkit.getDefaultToolkit(), runnable);
1335     }
1336 
1337     static void invokeAndWait(Object source, Runnable runnable)
1338         throws InterruptedException, InvocationTargetException
1339     {
1340         if (EventQueue.isDispatchThread()) {
1341             throw new Error("Cannot call invokeAndWait from the event dispatcher thread");
1342         }
1343 
1344         class AWTInvocationLock {}
1345         Object lock = new AWTInvocationLock();
1346 
1347         InvocationEvent event =
1348             new InvocationEvent(source, runnable, lock, true);
1349 
1350         synchronized (lock) {
1351             Toolkit.getEventQueue().postEvent(event);
1352             while (!event.isDispatched()) {
1353                 lock.wait();
1354             }
1355         }
1356 
1357         Throwable eventThrowable = event.getThrowable();
1358         if (eventThrowable != null) {
1359             throw new InvocationTargetException(eventThrowable);
1360         }
1361     }
1362 
1363     /*
1364      * Called from PostEventQueue.postEvent to notify that a new event
1365      * appeared. First it proceeds to the EventQueue on the top of the
1366      * stack, then notifies the associated dispatch thread if it exists
1367      * or starts a new one otherwise.
1368      */
1369     private void wakeup(boolean isShutdown) {
1370         pushPopLock.lock();
1371         try {
1372             if (nextQueue != null) {
1373                 // Forward call to the top of EventQueue stack.
1374                 nextQueue.wakeup(isShutdown);
1375             } else if (dispatchThread != null) {
1376                 pushPopCond.signalAll();
1377             } else if (!isShutdown) {
1378                 initDispatchThread();
1379             }
1380         } finally {
1381             pushPopLock.unlock();
1382         }
1383     }
1384 
1385     // The method is used by AWTAccessor for javafx/AWT single threaded mode.
1386     private void setFwDispatcher(FwDispatcher dispatcher) {
1387         if (nextQueue != null) {
1388             nextQueue.setFwDispatcher(dispatcher);
1389         } else {
1390             fwDispatcher = dispatcher;
1391         }
1392     }
1393 }
1394 
1395 /**
1396  * The Queue object holds pointers to the beginning and end of one internal
1397  * queue. An EventQueue object is composed of multiple internal Queues, one
1398  * for each priority supported by the EventQueue. All Events on a particular
1399  * internal Queue have identical priority.
1400  */
1401 class Queue {
1402     EventQueueItem head;
1403     EventQueueItem tail;
1404 }