This fixes a small issue in EventQueue.invokeAndWait() (use custom sync object rather then the current thread) and makes the implementation of EventQueue a little more elegant by replacing the array-based queue with a single-linked-list-based queue.

2006-09-18  Roman Kennke  <[EMAIL PROTECTED]>

        * java/awt/EventQueue.java
        (INITIAL_QUEUE_DEPTH): Removed obsolete field.
        (next_in): Removed obsolete field.
        (next_out): Removed obsolete field.
        (queueHead): New field. Markes the head of the queue.
        (queueTail): New field. Markes the tail of the queue.
        (queue): Removed obsolete field.
        (EventQueue): Documented empty block.
        (getNextEvent): Changed array based implementation to single-linked
        list based implementation.
        (invokeAndWait): Use an Object as synchronization object rather
        than the current thread.
        (peekEvent(int)): Changed array based implementation to single-linked
        list based implementation.
        (peekEvent()): Changed array based implementation to single-linked
        list based implementation.
        (pop()): Changed array based implementation to single-linked
        list based implementation.
        (postEvent): Foward to postEventImpl.
        (postEventImpl): Changed array based implementation to single-linked
        list based implementation.
        (push): Changed array based implementation to single-linked
        list based implementation.
        * java/awt/AWTEvent.java
        (queueNext): New field. Implements a single-linked list for
        the EventQueue.

/Roman
Index: java/awt/EventQueue.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/awt/EventQueue.java,v
retrieving revision 1.28
diff -u -1 -2 -r1.28 EventQueue.java
--- java/awt/EventQueue.java	28 Feb 2006 11:27:15 -0000	1.28
+++ java/awt/EventQueue.java	18 Sep 2006 10:51:01 -0000
@@ -33,48 +33,55 @@
 or based on this library.  If you modify this library, you may extend
 this exception to your version of the library, but you are not
 obligated to do so.  If you do not wish to do so, delete this
 exception statement from your version. */
 
 
 package java.awt;
 
 import java.awt.event.ActionEvent;
 import java.awt.event.InputEvent;
 import java.awt.event.InputMethodEvent;
 import java.awt.event.InvocationEvent;
+import java.awt.event.PaintEvent;
+import java.awt.peer.ComponentPeer;
+import java.awt.peer.LightweightPeer;
 import java.lang.reflect.InvocationTargetException;
 import java.util.EmptyStackException;
 
 /* Written using on-line Java 2 Platform Standard Edition v1.3 API 
  * Specification, as well as "The Java Class Libraries", 2nd edition 
  * (Addison-Wesley, 1998).
  * Status:  Believed complete, but untested.
  */
 
 /**
  * This class manages a queue of <code>AWTEvent</code> objects that
  * are posted to it.  The AWT system uses only one event queue for all
  * events.
  *
  * @author Bryce McKinlay
  * @author Aaron M. Renn ([EMAIL PROTECTED])
  */
 public class EventQueue
 {
-  private static final int INITIAL_QUEUE_DEPTH = 8;
-  private AWTEvent[] queue = new AWTEvent[INITIAL_QUEUE_DEPTH];
+  /**
+   * The first item in the queue. This is where events are popped from.
+   */
+  private AWTEvent queueHead;
 
-  private int next_in = 0; // Index where next event will be added to queue
-  private int next_out = 0; // Index of next event to be removed from queue
+  /**
+   * The last item. This is where events are posted to.
+   */
+  private AWTEvent queueTail;
 
   private EventQueue next;
   private EventQueue prev;
   private AWTEvent currentEvent;
   private long lastWhen = System.currentTimeMillis();
 
   private EventDispatchThread dispatchThread = new EventDispatchThread(this);
   private boolean shutdown = false;
 
   synchronized private void setShutdown (boolean b) 
   {
     shutdown = b;
@@ -96,177 +103,204 @@
           if (frames[i].isDisplayable())
             return false;
         return true;
       }
     return false;
   }
 
   /**
    * Initializes a new instance of <code>EventQueue</code>.
    */
   public EventQueue()
   {
+    // Nothing to do here.
   }
 
   /**
    * Returns the next event in the queue.  This method will block until
    * an event is available or until the thread is interrupted.
    *
    * @return The next event in the queue.
    *
    * @exception InterruptedException If this thread is interrupted while
    * waiting for an event to be posted to the queue.
    */
   public synchronized AWTEvent getNextEvent()
     throws InterruptedException
   {
     if (next != null)
       return next.getNextEvent();
 
-    while (next_in == next_out)
+    AWTEvent res = queueHead;
+    while (res == null)
       {
         // We are not allowed to return null from this method, yet it
         // is possible that we actually have run out of native events
         // in the enclosing while() loop, and none of the native events
         // happened to cause AWT events. We therefore ought to check
         // the isShutdown() condition here, before risking a "native
         // wait". If we check it before entering this function we may
         // wait forever for events after the shutdown condition has
         // arisen.
 
         if (isShutdown())
           throw new InterruptedException();
 
         wait();
+        res = queueHead;
       }
 
-    AWTEvent res = queue[next_out];
-
-    if (++next_out == queue.length)
-      next_out = 0;
+    // Unlink event from the queue.
+    queueHead = res.queueNext;
+    if (queueHead == null)
+      queueTail = null;
+    res.queueNext = null;
     return res;
   }
 
   /**
    * Returns the next event in the queue without removing it from the queue.
    * This method will block until an event is available or until the thread
    * is interrupted.
    *
    * @return The next event in the queue.
    * @specnote Does not block. Returns null if there are no events on the 
    *            queue. 
    */ 
   public synchronized AWTEvent peekEvent()
   {
     if (next != null)
       return next.peekEvent();
 
-    if (next_in != next_out)
-      return queue[next_out];
-    else
-      return null;
+    return queueHead;
   }
 
   /**
    * Returns the next event in the queue that has the specified id
    * without removing it from the queue.
    * This method will block until an event is available or until the thread
    * is interrupted.
    *
    * @param id The event id to return.
    *
    * @return The next event in the queue.
    *
    * @specnote Does not block. Returns null if there are no matching events 
    *            on the queue. 
    */ 
   public synchronized AWTEvent peekEvent(int id)
   {
     if (next != null)
       return next.peekEvent(id);
 
-    int i = next_out;
-    while (i != next_in)
+    AWTEvent evt = queueHead;
+    while (evt != null && evt.id != id)
       {
-        AWTEvent qevt = queue[i];
-        if (qevt.id == id)
-          return qevt;
+        evt = evt.queueNext;
       }
-    return null;
+    return evt;
   }
 
   /**
    * Posts a new event to the queue.
    *
    * @param evt The event to post to the queue.
    *
    * @exception NullPointerException If event is null.
    */
-  public synchronized void postEvent(AWTEvent evt)
+  public void postEvent(AWTEvent evt)
+  {
+    postEventImpl(evt);
+  }
+
+  /**
+   * Actually performs the event posting. This is needed because the
+   * RI doesn't use the public postEvent() method when transferring events
+   * between event queues in push() and pop().
+   * 
+   * @param evt the event to post
+   */
+  private synchronized final void postEventImpl(AWTEvent evt)
   {
     if (evt == null)
       throw new NullPointerException();
 
     if (next != null)
       {
         next.postEvent(evt);
         return;
       }
 
-    /* Check for any events already on the queue with the same source 
-       and ID. */	
-    int i = next_out;
-    while (i != next_in)
-      {
-        AWTEvent qevt = queue[i];
-        Object src;
-        if (qevt.id == evt.id
-            && (src = qevt.getSource()) == evt.getSource()
-            && src instanceof Component)
+    Object source = evt.getSource();
+
+    if (source instanceof Component)
+      {
+        // For PaintEvents, ask the ComponentPeer to coalesce the event
+        // when the component is heavyweight.
+        Component comp = (Component) source;
+        ComponentPeer peer = comp.peer;
+        if (peer != null && evt instanceof PaintEvent
+            && ! (peer instanceof LightweightPeer))
+          peer.coalescePaintEvent((PaintEvent) evt);
+
+        // Check for any events already on the queue with the same source
+        // and ID.
+        AWTEvent previous = null;
+        for (AWTEvent qevt = queueHead; qevt != null; qevt = qevt.queueNext)
           {
-            /* If there are, call coalesceEvents on the source component 
-               to see if they can be combined. */
-            Component srccmp = (Component) src;
-            AWTEvent coalesced_evt = srccmp.coalesceEvents(qevt, evt);
-            if (coalesced_evt != null)
+            Object src = qevt.getSource();
+            if (qevt.id == evt.id && src == comp)
               {
-                /* Yes. Replace the existing event with the combined event. */
-                queue[i] = coalesced_evt;
-                return;
+                // If there are, call coalesceEvents on the source component 
+                // to see if they can be combined.
+                Component srccmp = (Component) src;
+                AWTEvent coalescedEvt = srccmp.coalesceEvents(qevt, evt);
+                if (false && coalescedEvt != null)
+                  {
+                    // Yes. Replace the existing event with the combined event.
+                    if (qevt != coalescedEvt)
+                      {
+                        if (previous != null)
+                          {
+                            assert previous.queueNext == qevt;
+                            previous.queueNext = coalescedEvt;
+                          }
+                        else
+                          {
+                            assert queueHead == qevt;
+                            queueHead = coalescedEvt;
+                          }
+                        coalescedEvt.queueNext = qevt.queueNext;
+                        qevt.queueNext = null;
+                      }
+                    return;
+                  }
               }
-            break;
+            previous = qevt;
           }
-        if (++i == queue.length)
-          i = 0;
       }
 
-    queue[next_in] = evt;    
-    if (++next_in == queue.length)
-      next_in = 0;
-
-    if (next_in == next_out)
+    if (queueHead == null)
       {
-        /* Queue is full. Extend it. */
-        AWTEvent[] oldQueue = queue;
-        queue = new AWTEvent[queue.length * 2];
-
-        int len = oldQueue.length - next_out;
-        System.arraycopy(oldQueue, next_out, queue, 0, len);
-        if (next_out != 0)
-          System.arraycopy(oldQueue, 0, queue, len, next_out);
-
-        next_out = 0;
-        next_in = oldQueue.length;
+        // We have an empty queue. Set this event both as head and as tail.
+        queueHead = evt;
+        queueTail = evt;
       }
-    
+    else
+      {
+        // Note: queueTail should not be null here.
+        queueTail.queueNext = evt;
+        queueTail = evt;
+      }
+
     if (dispatchThread == null || !dispatchThread.isAlive())
       {
         dispatchThread = new EventDispatchThread(this);
         dispatchThread.start();
       }
 
     notify();
   }
 
   /**
    * Causes runnable to have its run method called in the dispatch thread of the
    * EventQueue. This will happen after all pending events are processed. The
@@ -278,33 +312,33 @@
    * @exception InvocationTargetException If an exception is thrown when running
    * runnable.
    *
    * @since 1.2
    */
   public static void invokeAndWait(Runnable runnable)
     throws InterruptedException, InvocationTargetException
   {
     if (isDispatchThread ())
       throw new Error("Can't call invokeAndWait from event dispatch thread");
 
     EventQueue eq = Toolkit.getDefaultToolkit().getSystemEventQueue(); 
-    Thread current = Thread.currentThread();
+    Object notifyObject = new Object();
 
-    InvocationEvent ie = 
-      new InvocationEvent(eq, runnable, current, true);
+    InvocationEvent ie =
+      new InvocationEvent(eq, runnable, notifyObject, true);
 
-    synchronized (current)
+    synchronized (notifyObject)
       {
         eq.postEvent(ie);
-        current.wait();
+        notifyObject.wait();
       }
 
     Exception exception;
 
     if ((exception = ie.getException()) != null)
       throw new InvocationTargetException(exception);
   }
 
   /**
    * This arranges for runnable to have its run method called in the
    * dispatch thread of the EventQueue.  This will happen after all
    * pending events are processed.
@@ -378,75 +412,94 @@
        only get a reference to the one at the bottom using
        Toolkit.getDefaultToolkit().getSystemEventQueue() */
     if (next != null)
       {
         next.push (newEventQueue);
         return;
       }
 
     /* Make sure we have a live dispatch thread to drive the queue */
     if (dispatchThread == null)
       dispatchThread = new EventDispatchThread(this);
 
-    int i = next_out;
-    while (i != next_in)
+    synchronized (newEventQueue)
       {
-        newEventQueue.postEvent(queue[i]);
-        next_out = i;
-        if (++i == queue.length)
-          i = 0;
+        // The RI transfers the events without calling the new eventqueue's
+        // push(), but using getNextEvent().
+        while (peekEvent() != null)
+          {
+            try
+              {
+                newEventQueue.postEventImpl(getNextEvent());
+              }
+            catch (InterruptedException ex)
+              {
+                // What should we do with this?
+                ex.printStackTrace();
+              }
+          }
+        newEventQueue.prev = this;
       }
 
     next = newEventQueue;
-    newEventQueue.prev = this;    
   }
 
   /** Transfer any pending events from this queue back to the parent queue that
     * was previously push()ed. Event dispatch from this queue is suspended.
     *
     * @exception EmptyStackException If no previous push was made on this
     * EventQueue.
     */
   protected void pop() throws EmptyStackException
   {
-    if (prev == null)
-      throw new EmptyStackException();
-
     /* The order is important here, we must get the prev lock first,
        or deadlock could occur as callers usually get here following
        prev's next pointer, and thus obtain prev's lock before trying
        to get this lock. */
-    synchronized (prev)
+    EventQueue previous = prev;
+    if (previous == null)
+      throw new EmptyStackException();
+    synchronized (previous)
       {
-        prev.next = next;
-        if (next != null)
-          next.prev = prev;
-
         synchronized (this)
           {
-            int i = next_out;
-            while (i != next_in)
+            EventQueue nextQueue = next;
+            if (nextQueue != null)
+              {
+                nextQueue.pop();
+              }
+            else
               {
-                prev.postEvent(queue[i]);
-                next_out = i;
-                if (++i == queue.length)
-                  i = 0;
+                previous.next = null;
+
+                // The RI transfers the events without calling the new eventqueue's
+                // push(), so this should be OK and most effective.
+                while (peekEvent() != null)
+                  {
+                    try
+                      {
+                        previous.postEventImpl(getNextEvent());
+                      }
+                    catch (InterruptedException ex)
+                      {
+                        // What should we do with this?
+                        ex.printStackTrace();
+                      }
+                  }
+
+                prev = null;
+                setShutdown(true);
+                dispatchThread = null;
+                this.notifyAll();
               }
-	    // Empty the queue so it can be reused
-	    next_in = 0;
-	    next_out = 0;
-
-            setShutdown(true);
-	    dispatchThread = null;
-            this.notifyAll();
           }
       }
   }
 
   /**
    * Dispatches an event. The manner in which the event is dispatched depends
    * upon the type of the event and the type of the event's source object.
    *
    * @exception NullPointerException If event is null.
    */
   protected void dispatchEvent(AWTEvent evt)
   {
Index: java/awt/AWTEvent.java
===================================================================
RCS file: /cvsroot/classpath/classpath/java/awt/AWTEvent.java,v
retrieving revision 1.18
diff -u -1 -2 -r1.18 AWTEvent.java
--- java/awt/AWTEvent.java	26 Jul 2006 19:09:50 -0000	1.18
+++ java/awt/AWTEvent.java	18 Sep 2006 10:51:01 -0000
@@ -88,24 +88,29 @@
   /**
    * Indicates if the event has been consumed. False mean it is passed to
    * the peer, true means it has already been processed. Semantic events
    * generated by low-level events always have the value true.
    *
    * @see #consume()
    * @see #isConsumed()
    * @serial whether the event has been consumed
    */
   protected boolean consumed;
 
   /**
+   * Used for implementing a simple linked list in EventQueue.
+   */
+  transient AWTEvent queueNext;
+
+  /**
    * Who knows? It's in the serial version.
    *
    * @serial No idea what this is for.
    */
   byte[] bdata;
 
   /**
    * Indicates if this event is dispatched by the KeyboardFocusManager.
    */
   boolean isFocusManagerEvent = false;
 
   /** Mask for selecting component events. */

Reply via email to