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()