src/share/classes/java/awt/EventQueue.java

Print this page




  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.dnd.SunDropTargetEvent;
  41 import sun.util.logging.PlatformLogger;
  42 
  43 import sun.awt.AppContext;
  44 import sun.awt.AWTAutoShutdown;
  45 import sun.awt.PeerEvent;
  46 import sun.awt.SunToolkit;
  47 import sun.awt.EventQueueItem;
  48 import sun.awt.AWTAccessor;
  49 
  50 import java.util.concurrent.locks.Condition;
  51 import java.util.concurrent.locks.Lock;
  52 import java.util.concurrent.atomic.AtomicInteger;
  53 
  54 import java.security.AccessControlContext;
  55 
  56 import sun.misc.SharedSecrets;
  57 import sun.misc.JavaSecurityAccess;
  58 
  59 /**
  60  * <code>EventQueue</code> is a platform-independent class
  61  * that queues events, both from the underlying peer classes
  62  * and from trusted application classes.
  63  * <p>
  64  * It encapsulates asynchronous event dispatch machinery which
  65  * extracts events from the queue and dispatches them by calling
  66  * {@link #dispatchEvent(AWTEvent) dispatchEvent(AWTEvent)} method
  67  * on this <code>EventQueue</code> with the event to be dispatched
  68  * as an argument.  The particular behavior of this machinery is
  69  * implementation-dependent.  The only requirements are that events


 164 
 165     /*
 166      * The time stamp of the last KeyEvent .
 167      */
 168     private long mostRecentKeyEventTime = System.currentTimeMillis();
 169 
 170     /**
 171      * The modifiers field of the current event, if the current event is an
 172      * InputEvent or ActionEvent.
 173      */
 174     private WeakReference<AWTEvent> currentEvent;
 175 
 176     /*
 177      * Non-zero if a thread is waiting in getNextEvent(int) for an event of
 178      * a particular ID to be posted to the queue.
 179      */
 180     private volatile int waitForID;
 181 
 182     private final String name = "AWT-EventQueue-" + threadInitNumber.getAndIncrement();
 183 


 184     private static final PlatformLogger eventLog = PlatformLogger.getLogger("java.awt.event.EventQueue");
 185 
 186     static {
 187         AWTAccessor.setEventQueueAccessor(
 188             new AWTAccessor.EventQueueAccessor() {
 189                 public Thread getDispatchThread(EventQueue eventQueue) {
 190                     return eventQueue.getDispatchThread();
 191                 }
 192                 public boolean isDispatchThreadImpl(EventQueue eventQueue) {
 193                     return eventQueue.isDispatchThreadImpl();
 194                 }
 195                 public void removeSourceEvents(EventQueue eventQueue,
 196                                                Object source,
 197                                                boolean removeAllEvents)
 198                 {
 199                     eventQueue.removeSourceEvents(source, removeAllEvents);
 200                 }
 201                 public boolean noEvents(EventQueue eventQueue) {
 202                     return eventQueue.noEvents();
 203                 }
 204                 public void wakeup(EventQueue eventQueue, boolean isShutdown) {
 205                     eventQueue.wakeup(isShutdown);
 206                 }
 207                 public void invokeAndWait(Object source, Runnable r)
 208                     throws InterruptedException, InvocationTargetException
 209                 {
 210                     EventQueue.invokeAndWait(source, r);
 211                 }




 212             });
 213     }
 214 
 215     public EventQueue() {
 216         for (int i = 0; i < NUM_PRIORITIES; i++) {
 217             queues[i] = new Queue();
 218         }
 219         /*
 220          * NOTE: if you ever have to start the associated event dispatch
 221          * thread at this point, be aware of the following problem:
 222          * If this EventQueue instance is created in
 223          * SunToolkit.createNewAppContext() the started dispatch thread
 224          * may call AppContext.getAppContext() before createNewAppContext()
 225          * completes thus causing mess in thread group to appcontext mapping.
 226          */
 227 
 228         pushPopLock = (Lock)AppContext.getAppContext().get(AppContext.EVENT_QUEUE_LOCK_KEY);
 229         pushPopCond = (Condition)AppContext.getAppContext().get(AppContext.EVENT_QUEUE_COND_KEY);
 230     }
 231 


 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 


 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


 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>


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                             t.setContextClassLoader(classLoader);
1040                             t.setPriority(Thread.NORM_PRIORITY + 1);


1286      * Called from PostEventQueue.postEvent to notify that a new event
1287      * appeared. First it proceeds to the EventQueue on the top of the
1288      * stack, then notifies the associated dispatch thread if it exists
1289      * or starts a new one otherwise.
1290      */
1291     private void wakeup(boolean isShutdown) {
1292         pushPopLock.lock();
1293         try {
1294             if (nextQueue != null) {
1295                 // Forward call to the top of EventQueue stack.
1296                 nextQueue.wakeup(isShutdown);
1297             } else if (dispatchThread != null) {
1298                 pushPopCond.signalAll();
1299             } else if (!isShutdown) {
1300                 initDispatchThread();
1301             }
1302         } finally {
1303             pushPopLock.unlock();
1304         }
1305     }









1306 }
1307 
1308 /**
1309  * The Queue object holds pointers to the beginning and end of one internal
1310  * queue. An EventQueue object is composed of multiple internal Queues, one
1311  * for each priority supported by the EventQueue. All Events on a particular
1312  * internal Queue have identical priority.
1313  */
1314 class Queue {
1315     EventQueueItem head;
1316     EventQueueItem tail;
1317 }


  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 sun.misc.SharedSecrets;
  51 import sun.misc.JavaSecurityAccess;
  52 
  53 /**
  54  * <code>EventQueue</code> 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</code> 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


 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     private final String name = "AWT-EventQueue-" + threadInitNumber.getAndIncrement();
 177 
 178     private FwDispatcher fwDispatcher;
 179 
 180     private static final PlatformLogger eventLog = PlatformLogger.getLogger("java.awt.event.EventQueue");
 181 
 182     static {
 183         AWTAccessor.setEventQueueAccessor(
 184             new AWTAccessor.EventQueueAccessor() {
 185                 public Thread getDispatchThread(EventQueue eventQueue) {
 186                     return eventQueue.getDispatchThread();
 187                 }
 188                 public boolean isDispatchThreadImpl(EventQueue eventQueue) {
 189                     return eventQueue.isDispatchThreadImpl();
 190                 }
 191                 public void removeSourceEvents(EventQueue eventQueue,
 192                                                Object source,
 193                                                boolean removeAllEvents)
 194                 {
 195                     eventQueue.removeSourceEvents(source, removeAllEvents);
 196                 }
 197                 public boolean noEvents(EventQueue eventQueue) {
 198                     return eventQueue.noEvents();
 199                 }
 200                 public void wakeup(EventQueue eventQueue, boolean isShutdown) {
 201                     eventQueue.wakeup(isShutdown);
 202                 }
 203                 public void invokeAndWait(Object source, Runnable r)
 204                     throws InterruptedException, InvocationTargetException
 205                 {
 206                     EventQueue.invokeAndWait(source, r);
 207                 }
 208                 public void setFwDispatcher(EventQueue eventQueue,
 209                                             FwDispatcher dispatcher) {
 210                     eventQueue.setFwDispatcher(dispatcher);
 211                 }
 212             });
 213     }
 214 
 215     public EventQueue() {
 216         for (int i = 0; i < NUM_PRIORITIES; i++) {
 217             queues[i] = new Queue();
 218         }
 219         /*
 220          * NOTE: if you ever have to start the associated event dispatch
 221          * thread at this point, be aware of the following problem:
 222          * If this EventQueue instance is created in
 223          * SunToolkit.createNewAppContext() the started dispatch thread
 224          * may call AppContext.getAppContext() before createNewAppContext()
 225          * completes thus causing mess in thread group to appcontext mapping.
 226          */
 227 
 228         pushPopLock = (Lock)AppContext.getAppContext().get(AppContext.EVENT_QUEUE_LOCK_KEY);
 229         pushPopCond = (Condition)AppContext.getAppContext().get(AppContext.EVENT_QUEUE_COND_KEY);
 230     }
 231 


 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                 if (fwDispatcher == null) {
 688                     dispatchEventImpl(event, src);
 689                 } else {
 690                     fwDispatcher.scheduleDispatch(new Runnable() {
 691                         @Override
 692                         public void run() {
 693                             dispatchEventImpl(event, src);
 694                         }
 695                     });
 696                 }
 697                 return null;
 698             }
 699         };
 700 
 701         final AccessControlContext stack = AccessController.getContext();
 702         final AccessControlContext srcAcc = getAccessControlContextFrom(src);
 703         final AccessControlContext eventAcc = event.getAccessControlContext();
 704         if (srcAcc == null) {
 705             javaSecurityAccess.doIntersectionPrivilege(action, stack, eventAcc);
 706         } else {
 707             javaSecurityAccess.doIntersectionPrivilege(
 708                 new PrivilegedAction<Void>() {
 709                     public Void run() {
 710                         javaSecurityAccess.doIntersectionPrivilege(action, eventAcc);
 711                         return null;
 712                     }
 713                 }, stack, srcAcc);
 714         }
 715     }
 716 


 836      * Any pending events are transferred to the new <code>EventQueue</code>
 837      * for processing by it.
 838      *
 839      * @param newEventQueue an <code>EventQueue</code>
 840      *          (or subclass thereof) instance to be use
 841      * @see      java.awt.EventQueue#pop
 842      * @throws NullPointerException if <code>newEventQueue</code> is <code>null</code>
 843      * @since           1.2
 844      */
 845     public void push(EventQueue newEventQueue) {
 846         if (eventLog.isLoggable(PlatformLogger.FINE)) {
 847             eventLog.fine("EventQueue.push(" + newEventQueue + ")");
 848         }
 849 
 850         pushPopLock.lock();
 851         try {
 852             EventQueue topQueue = this;
 853             while (topQueue.nextQueue != null) {
 854                 topQueue = topQueue.nextQueue;
 855             }
 856             if (topQueue.fwDispatcher != null) {
 857                 throw new RuntimeException("push() to queue with fwDispatcher");
 858             }
 859             if ((topQueue.dispatchThread != null) &&
 860                 (topQueue.dispatchThread.getEventQueue() == this))
 861             {
 862                 newEventQueue.dispatchThread = topQueue.dispatchThread;
 863                 topQueue.dispatchThread.setEventQueue(newEventQueue);
 864             }
 865 
 866             // Transfer all events forward to new EventQueue.
 867             while (topQueue.peekEvent() != null) {
 868                 try {
 869                     // Use getNextEventPrivate() as it doesn't call flushPendingEvents()
 870                     newEventQueue.postEventPrivate(topQueue.getNextEventPrivate());
 871                 } catch (InterruptedException ie) {
 872                     if (eventLog.isLoggable(PlatformLogger.FINE)) {
 873                         eventLog.fine("Interrupted push", ie);
 874                     }
 875                 }
 876             }
 877 
 878             // Wake up EDT waiting in getNextEvent(), so it can


 969      * @return secondaryLoop A new secondary loop object, which can
 970      *                       be used to launch a new nested event
 971      *                       loop and dispatch events from this queue
 972      *
 973      * @see SecondaryLoop#enter
 974      * @see SecondaryLoop#exit
 975      *
 976      * @since 1.7
 977      */
 978     public SecondaryLoop createSecondaryLoop() {
 979         return createSecondaryLoop(null, null, 0);
 980     }
 981 
 982     SecondaryLoop createSecondaryLoop(Conditional cond, EventFilter filter, long interval) {
 983         pushPopLock.lock();
 984         try {
 985             if (nextQueue != null) {
 986                 // Forward the request to the top of EventQueue stack
 987                 return nextQueue.createSecondaryLoop(cond, filter, interval);
 988             }
 989             if (fwDispatcher != null) {
 990                 return fwDispatcher.createSecondaryLoop();
 991             }
 992             if (dispatchThread == null) {
 993                 initDispatchThread();
 994             }
 995             return new WaitDispatchSupport(dispatchThread, cond, filter, interval);
 996         } finally {
 997             pushPopLock.unlock();
 998         }
 999     }
1000 
1001     /**
1002      * Returns true if the calling thread is
1003      * {@link Toolkit#getSystemEventQueue the current AWT EventQueue}'s
1004      * dispatch thread. Use this method to ensure that a particular
1005      * task is being executed (or not being) there.
1006      * <p>
1007      * Note: use the {@link #invokeLater} or {@link #invokeAndWait}
1008      * methods to execute a task in
1009      * {@link Toolkit#getSystemEventQueue the current AWT EventQueue}'s
1010      * dispatch thread.
1011      * <p>


1015      * dispatch thread
1016      * @see             #invokeLater
1017      * @see             #invokeAndWait
1018      * @see             Toolkit#getSystemEventQueue
1019      * @since           1.2
1020      */
1021     public static boolean isDispatchThread() {
1022         EventQueue eq = Toolkit.getEventQueue();
1023         return eq.isDispatchThreadImpl();
1024     }
1025 
1026     final boolean isDispatchThreadImpl() {
1027         EventQueue eq = this;
1028         pushPopLock.lock();
1029         try {
1030             EventQueue next = eq.nextQueue;
1031             while (next != null) {
1032                 eq = next;
1033                 next = eq.nextQueue;
1034             }
1035             if (eq.fwDispatcher != null) {
1036                 return eq.fwDispatcher.isDispatchThread();
1037             }
1038             return (Thread.currentThread() == eq.dispatchThread);
1039         } finally {
1040             pushPopLock.unlock();
1041         }
1042     }
1043 
1044     final void initDispatchThread() {
1045         pushPopLock.lock();
1046         try {
1047             AppContext appContext = AppContext.getAppContext();
1048             if (dispatchThread == null && !threadGroup.isDestroyed() && !appContext.isDisposed()) {
1049                 dispatchThread = AccessController.doPrivileged(
1050                     new PrivilegedAction<EventDispatchThread>() {
1051                         public EventDispatchThread run() {
1052                             EventDispatchThread t =
1053                                 new EventDispatchThread(threadGroup,
1054                                                         name,
1055                                                         EventQueue.this);
1056                             t.setContextClassLoader(classLoader);
1057                             t.setPriority(Thread.NORM_PRIORITY + 1);


1303      * Called from PostEventQueue.postEvent to notify that a new event
1304      * appeared. First it proceeds to the EventQueue on the top of the
1305      * stack, then notifies the associated dispatch thread if it exists
1306      * or starts a new one otherwise.
1307      */
1308     private void wakeup(boolean isShutdown) {
1309         pushPopLock.lock();
1310         try {
1311             if (nextQueue != null) {
1312                 // Forward call to the top of EventQueue stack.
1313                 nextQueue.wakeup(isShutdown);
1314             } else if (dispatchThread != null) {
1315                 pushPopCond.signalAll();
1316             } else if (!isShutdown) {
1317                 initDispatchThread();
1318             }
1319         } finally {
1320             pushPopLock.unlock();
1321         }
1322     }
1323 
1324     // The method is used by AWTAccessor for javafx/AWT single threaded mode.
1325     private void setFwDispatcher(FwDispatcher dispatcher) {
1326         if (nextQueue != null) {
1327             nextQueue.setFwDispatcher(dispatcher);
1328         } else {
1329             fwDispatcher = dispatcher;
1330         }
1331     }
1332 }
1333 
1334 /**
1335  * The Queue object holds pointers to the beginning and end of one internal
1336  * queue. An EventQueue object is composed of multiple internal Queues, one
1337  * for each priority supported by the EventQueue. All Events on a particular
1338  * internal Queue have identical priority.
1339  */
1340 class Queue {
1341     EventQueueItem head;
1342     EventQueueItem tail;
1343 }