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