Hi,

I committed this patch. It fixes: a race condition that was causing some invokeAndWait calls to wait indefinitely, a FIXME about checking that the native event queue is empty, and a potential ConcurrentModificationException in Frame.

Tom

2006-10-23  Thomas Fitzsimmons  <[EMAIL PROTECTED]>

        * gnu/java/awt/peer/NativeEventLoopRunningEvent.java: New file.
        * gnu/java/awt/peer/gtk/GtkMainThread.java: Post
        NativeEventLoopRunningEvent after GTK main loop start and stop.
        * java/awt/EventQueue.java (isShutdown): Check nativeLoopRunning.
        (getNextEvent): Set dispatchThread to null.
        (postEventImpl): Set nativeLoopRunning.
        (pop): Interrupt event dispatch thread.
        * java/awt/Frame.java (noteFrame): Synchronize on weakFrames.
Index: gnu/java/awt/peer/NativeEventLoopRunningEvent.java
===================================================================
RCS file: gnu/java/awt/peer/NativeEventLoopRunningEvent.java
diff -N gnu/java/awt/peer/NativeEventLoopRunningEvent.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnu/java/awt/peer/NativeEventLoopRunningEvent.java	23 Oct 2006 18:57:11 -0000
@@ -0,0 +1,58 @@
+/* NativeEventLoopRunningEvent.java -- communicates to EventQueue the
+   state of the native event loop
+   Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+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 gnu.java.awt.peer;
+
+import java.awt.AWTEvent;
+
+public class NativeEventLoopRunningEvent
+  extends AWTEvent
+{
+  private boolean running;
+
+  public NativeEventLoopRunningEvent(Object source)
+  {
+    super(source, 2999);
+    running = ((Boolean) source).booleanValue();
+  }
+
+  public boolean isRunning()
+  {
+    return running;
+  }
+}
Index: gnu/java/awt/peer/gtk/GtkMainThread.java
===================================================================
RCS file: /sources/classpath/classpath/gnu/java/awt/peer/gtk/GtkMainThread.java,v
retrieving revision 1.16
diff -u -r1.16 GtkMainThread.java
--- gnu/java/awt/peer/gtk/GtkMainThread.java	17 Oct 2006 23:22:56 -0000	1.16
+++ gnu/java/awt/peer/gtk/GtkMainThread.java	23 Oct 2006 18:57:11 -0000
@@ -38,13 +38,11 @@
 
 package gnu.java.awt.peer.gtk;
 
+import gnu.java.awt.peer.NativeEventLoopRunningEvent;
+
 import java.awt.AWTEvent;
 
 /**
- * This class implements the AWT exit conditions listed here:
- *
- * http://java.sun.com/j2se/1.5.0/docs/api/java/awt/doc-files/AWTThreadIssues.html
- *
  * The Java thread representing the native GTK main loop, that is,
  * GtkMainThread.mainThread, terminates when GtkToolkit.gtkMain()
  * returns.  That happens in response to the last window peer being
@@ -73,17 +71,10 @@
  * GtkMainThread.mainThread is started when the window count goes from
  * zero to one.
  *
- * The three exit conditions in the above document are satisfied by
- * GtkMainThread alone: 1) no displayable AWT or Swing components: if
- * no window peers exist then no AWT or Swing component is
- * displayable; 2) no native events in the native event queue: when
- * the last window is disposed of, the native GTK main loop is
- * terminated, which means that there is no native event queue let
- * alone native events therein; 3) no AWT events in the AWT event
- * queue: endMainThread posts a dummy event to the AWT event queue,
- * which if the other exit conditions are satisfied, becomes the last
- * event posted to the AWT event queue before the AWT event dispatch
- * thread terminates.
+ * GtkMainThread keeps the AWT event queue informed of its status by
+ * posting NativeEventLoopRunningEvents.  The AWT event queue uses
+ * this status to determine whether or not the AWT exit conditions
+ * have been met (see EventQueue.isShutdown).
  */
 public class GtkMainThread extends Thread
 {
@@ -144,6 +135,8 @@
                                         + " for GTK main loop to start");
                   }
               }
+            GtkGenericPeer.q()
+              .postEvent(new NativeEventLoopRunningEvent(new Boolean(true)));
           }
       }
   }
@@ -169,18 +162,9 @@
                                         + " for GTK main loop to stop");
                   }
               }
-            // Post a dummy event to wake up the AWT event dispatch
-            // thread.  EventQueue.getNextEvent first checks the
-            // shutdown condition, then waits indefinitely for the
-            // next event to be posted.  There is a possibility that
-            // the native GTK main loop will end between the
-            // isShutdown check and the wait call.  Posting a dummy
-            // event here causes the AWT event dispatch thread to wake
-            // up, giving it a chance to check the shutdown condition
-            // again and terminate cleanly.
             GtkGenericPeer.q()
-              .postEvent(new WakeupEventDispatchThreadEvent (mainThread));
-          }
+              .postEvent(new NativeEventLoopRunningEvent(new Boolean(false)));
+            }
       }
   }
 
@@ -204,12 +188,3 @@
       }
   }
 }
-
-class WakeupEventDispatchThreadEvent
-  extends AWTEvent
-{
-  WakeupEventDispatchThreadEvent(Object o)
-  {
-    super (o, 2000);
-  }
-}
Index: java/awt/EventQueue.java
===================================================================
RCS file: /sources/classpath/classpath/java/awt/EventQueue.java,v
retrieving revision 1.30
diff -u -r1.30 EventQueue.java
--- java/awt/EventQueue.java	27 Sep 2006 20:17:27 -0000	1.30
+++ java/awt/EventQueue.java	23 Oct 2006 18:57:12 -0000
@@ -39,6 +39,7 @@
 package java.awt;
 
 import gnu.java.awt.LowPriorityEvent;
+import gnu.java.awt.peer.NativeEventLoopRunningEvent;
 
 import java.awt.event.ActionEvent;
 import java.awt.event.InputEvent;
@@ -114,31 +115,25 @@
   private long lastWhen = System.currentTimeMillis();
 
   private EventDispatchThread dispatchThread = new EventDispatchThread(this);
-  private boolean shutdown = false;
+  private boolean nativeLoopRunning = false;
 
-  synchronized private void setShutdown (boolean b) 
+  private boolean isShutdown ()
   {
-    shutdown = b;
-  }
-
-  synchronized boolean isShutdown ()
-  {
-    if (shutdown)
-      return true;
-
     // This is the exact self-shutdown condition specified in J2SE:
     // http://java.sun.com/j2se/1.4.2/docs/api/java/awt/doc-files/AWTThreadIssues.html
-    
-    // FIXME: check somewhere that the native queue is empty
-    if (peekEvent() == null)
-      {
-        Frame[] frames = Frame.getFrames();
-        for (int i = 0; i < frames.length; ++i)
-          if (frames[i].isDisplayable())
-            return false;
-        return true;
-      }
-    return false;
+
+    if (nativeLoopRunning)
+      return false;
+
+    if (peekEvent() != null)
+      return false;
+
+    Frame[] frames = Frame.getFrames();
+    for (int i = 0; i < frames.length; ++i)
+      if (frames[i].isDisplayable())
+        return false;
+
+    return true;
   }
 
   /**
@@ -167,19 +162,23 @@
       return next.getNextEvent();
 
     AWTEvent res = getNextEventImpl(true);
+
     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();
+          {
+            // Explicitly set dispathThread to null.  If we don't do
+            // this, there is a race condition where dispatchThread
+            // can be != null even after the event dispatch thread has
+            // stopped running.  If that happens, then the
+            // dispatchThread == null check in postEventImpl will
+            // fail, and a new event dispatch thread will not be
+            // created, leaving invokeAndWaits waiting indefinitely.
+            dispatchThread = null;
+
+            // Interrupt the event dispatch thread.
+            throw new InterruptedException();
+          }
 
         wait();
         res = getNextEventImpl(true);
@@ -296,6 +295,12 @@
       priority = LOW_PRIORITY;
     // TODO: Maybe let Swing RepaintManager events also be processed with
     // low priority.
+    if (evt instanceof NativeEventLoopRunningEvent)
+      {
+        nativeLoopRunning = ((NativeEventLoopRunningEvent) evt).isRunning();
+        notify();
+        return;
+      }
     postEventImpl(evt, priority);
   }
 
@@ -576,11 +581,11 @@
                         ex.printStackTrace();
                       }
                   }
-
                 prev = null;
-                setShutdown(true);
+                // Tell our EventDispatchThread that it can end
+                // execution.
+                dispatchThread.interrupt();
                 dispatchThread = null;
-                this.notifyAll();
               }
           }
       }
Index: java/awt/Frame.java
===================================================================
RCS file: /sources/classpath/classpath/java/awt/Frame.java,v
retrieving revision 1.40
diff -u -r1.40 Frame.java
--- java/awt/Frame.java	18 Oct 2006 22:09:16 -0000	1.40
+++ java/awt/Frame.java	23 Oct 2006 18:57:12 -0000
@@ -488,7 +488,10 @@
 
   private static void noteFrame(Frame f)
   {
-    weakFrames.add(new WeakReference(f));
+    synchronized (weakFrames)
+      {
+        weakFrames.add(new WeakReference(f));
+      }
   }
 
   public static Frame[] getFrames()

Reply via email to