This is the mail archive of the java-patches@gcc.gnu.org mailing list for the Java project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[gui][patch] improvements to single threaded event dispatch


hi,

this patch, which I've committed to java-gui-branch, improves the event dispatch performance of the recent "single threading" patch I put into the AWT peers. there are two major changes here:

 - if the java side is full for "sufficiently long", it checks the
   native side in a non-blocking manner to see if there are pending
   native events. this prevents java-generated events (painting, timers)
   from starving the peer event queue.

 - if the peer side is full for "sufficiently long", it transfers
   control back up to the java side for a moment, to give it a chance to
   drain the java queue.

the latter behavior almost existed in the initial patch, but it used an ad-hoc "event batch count" to decide when to transfer control back to java. now both behaviors are controlled by time: if 100ms worth of processing has elapsed on either side (this is the human perceptual latency threshold), the transition occurs.

note that this is *not* the same as saying that the two halves of the event loop are polling or "busy waiting". the thread still blocks in the native side when there is no event traffic. it simply means that time measurements govern the transitions between behavior modes *during* the time when there is activity. you could think of it somewhat like a timeslice in a multi-threaded program in which the threads might do blocking i/o.

(indeed, the need for this patch might be a sensible argument that having two threads was a better state of affairs; but I still think I prefer this organization, since we have more explicit control over scheduling).

-graydon

2005-01-17 Graydon Hoare <graydon@redhat.com>

	* gnu/awt/xlib/XEventLoop.java: Add non-blocking event mode.
	* gnu/awt/xlib/XToolkit.java: Likewise.
	* gnu/gcj/xlib/XAnyEvent.java: Likewise.
	* gnu/gcj/xlib/natXAnyEvent.cc: Likewise.
	* gnu/java/awt/ClasspathToolkit.java: Likewise.
	* gnu/java/awt/peer/gtk/GtkToolkit.java: Likewise.
	* java/awt/EventQueue.java (getNextEvent):
	Adjust event loop to switch to native mode after 100ms.
	* javax/swing/Timer.java (drainEvents): Reuse Runnable.
	* jni/gtk-peer/gnu_java_awt_peer_gtk_GtkGenericPeer.c
	(Java_gnu_java_awt_peer_gtk_GtkGenericPeer_dispose):
	Wake up event thread.
	* jni/gtk-peer/gnu_java_awt_peer_gtk_GtkToolkit.c
	(Java_gnu_java_awt_peer_gtk_GtkToolkit_iterateNativeQueue):
	Adjust event loop to switch to java mode after 100ms.
--- gnu/awt/xlib/XEventLoop.java	4 Jan 2005 18:45:19 -0000	1.3.14.2
+++ gnu/awt/xlib/XEventLoop.java	18 Jan 2005 03:28:58 -0000
@@ -42,23 +42,23 @@
     anyEvent.interrupt();
   }
 
-  void postNextEvent()
+  void postNextEvent(boolean block)
   {
-    AWTEvent evt = getNextEvent();
+    AWTEvent evt = getNextEvent(block);
     if (evt != null)
       queue.postEvent(evt);
   }
     
   /** get next event. Will block until events become available. */
  
-  public AWTEvent getNextEvent()
+  public AWTEvent getNextEvent(boolean block)
   {
     // ASSERT:
     if (isIdle())
       throw new Error("should not be idle");
     
     AWTEvent event = null;
-    if (loadNextEvent())
+    if (loadNextEvent(block))
       {
         event = createEvent();        
         event = lightweightRedirector.redirect(event);
@@ -66,7 +66,7 @@
     return event;
   }
 
-  boolean loadNextEvent()
+  boolean loadNextEvent(boolean block)
   {
     boolean gotEvent = false;
     try
@@ -95,7 +95,7 @@
 	   of events. */
 	
 	//display.flush(); // implicit?
-	gotEvent = anyEvent.loadNext();
+	gotEvent = anyEvent.loadNext(block);
       }
     catch (RuntimeException re)
       {
--- gnu/awt/xlib/XToolkit.java	4 Jan 2005 18:45:20 -0000	1.7.2.3
+++ gnu/awt/xlib/XToolkit.java	18 Jan 2005 03:28:59 -0000
@@ -457,10 +457,10 @@
     eventLoop.interrupt();
   }
 
-  public void iterateNativeQueue(java.awt.EventQueue locked) 
+  public void iterateNativeQueue(java.awt.EventQueue locked, boolean block) 
   {
     interrupted = false;
     while (!interrupted)
-      eventLoop.postNextEvent();
+      eventLoop.postNextEvent(block);
   }; 
 }
--- gnu/gcj/xlib/XAnyEvent.java	4 Jan 2005 18:45:20 -0000	1.3.10.1
+++ gnu/gcj/xlib/XAnyEvent.java	18 Jan 2005 03:28:59 -0000
@@ -70,7 +70,7 @@
   /**
    * Load next event into the event structure.
    */
-  public native boolean loadNext();
+  public native boolean loadNext(boolean block);
   public native void interrupt();
 
   public native int getType();
--- gnu/gcj/xlib/natXAnyEvent.cc	4 Jan 2005 18:45:20 -0000	1.1.92.1
+++ gnu/gcj/xlib/natXAnyEvent.cc	18 Jan 2005 03:28:59 -0000
@@ -51,7 +51,7 @@
   structure = 0;
 }
 
-jboolean gnu::gcj::xlib::XAnyEvent::loadNext()
+jboolean gnu::gcj::xlib::XAnyEvent::loadNext(jboolean block)
 {
   ::Display* dpy = (::Display*) display->display;
   ::XEvent* evt = (::XEvent*) structure;
@@ -62,6 +62,9 @@
       return true;  
     }
 
+  if (!block)
+    return false;
+
   int *pipe = reinterpret_cast<int *>(pipefds);
   int xfd = XConnectionNumber(dpy);
   int pipefd = pipe[0];
--- gnu/java/awt/ClasspathToolkit.java	4 Jan 2005 20:50:38 -0000	1.1.18.7
+++ gnu/java/awt/ClasspathToolkit.java	18 Jan 2005 03:28:59 -0000
@@ -367,5 +367,5 @@
 
   public abstract boolean nativeQueueEmpty();
   public abstract void wakeNativeQueue();  
-  public abstract void iterateNativeQueue(EventQueue locked);
+  public abstract void iterateNativeQueue(EventQueue locked, boolean block);
 }
--- gnu/java/awt/peer/gtk/GtkToolkit.java	4 Jan 2005 20:50:39 -0000	1.8.2.17
+++ gnu/java/awt/peer/gtk/GtkToolkit.java	18 Jan 2005 03:28:59 -0000
@@ -656,6 +656,6 @@
 
   public native boolean nativeQueueEmpty();
   public native void wakeNativeQueue();  
-  public native void iterateNativeQueue(EventQueue locked);
+  public native void iterateNativeQueue(EventQueue locked, boolean block);
 
 } // class GtkToolkit
--- java/awt/EventQueue.java	4 Jan 2005 18:45:22 -0000	1.17.4.3
+++ java/awt/EventQueue.java	18 Jan 2005 03:28:59 -0000
@@ -76,6 +76,9 @@
   private EventDispatchThread dispatchThread = new EventDispatchThread(this);
   private boolean shutdown = false;
 
+  private long lastNativeQueueAccess = 0;
+  private long humanLatencyThreshold = 100;
+
   synchronized void setShutdown (boolean b) 
   {
     shutdown = b;
@@ -122,6 +125,16 @@
   {
     if (next != null)
       return next.getNextEvent();
+    
+    ClasspathToolkit tk = ((ClasspathToolkit) Toolkit.getDefaultToolkit());
+    long curr = System.currentTimeMillis();
+
+    if (! tk.nativeQueueEmpty() &&
+        (curr - lastNativeQueueAccess > humanLatencyThreshold))
+      {
+        tk.iterateNativeQueue(this, false);
+        lastNativeQueueAccess = curr;
+      }
 
     while (next_in == next_out)
       {
@@ -143,7 +156,8 @@
             if (isShutdown())
               throw new InterruptedException();
 
-            ((ClasspathToolkit) Toolkit.getDefaultToolkit()).iterateNativeQueue(this);
+            tk.iterateNativeQueue(this, true);
+            lastNativeQueueAccess = System.currentTimeMillis();
           }
         else
           {
--- javax/swing/Timer.java	22 Oct 2004 17:43:52 -0000	1.3.18.12
+++ javax/swing/Timer.java	18 Jan 2005 03:28:59 -0000
@@ -72,6 +72,14 @@
   /** DOCUMENT ME! */
   private Waker waker;
 
+  private Runnable drainer = new Runnable() 
+    {
+      public void run()
+      {
+        drainEvents();
+      }
+    };
+
   /**
    * DOCUMENT ME!
    */
@@ -81,14 +89,7 @@
       {
 	queue++;
 	if (queue == 1)
-	  SwingUtilities.invokeLater(new Runnable()
-	      {
-		public void run()
-		{
-		  drainEvents();
-		}
-	      });
-
+	  SwingUtilities.invokeLater(drainer);
       }
   }
 
--- jni/gtk-peer/gnu_java_awt_peer_gtk_GtkGenericPeer.c	7 Nov 2004 00:04:27 -0000	1.1.16.3
+++ jni/gtk-peer/gnu_java_awt_peer_gtk_GtkGenericPeer.c	18 Jan 2005 03:28:59 -0000
@@ -56,6 +56,13 @@
   gtk_widget_destroy (GTK_WIDGET (ptr));
 
   gdk_threads_leave ();
+
+  /* 
+   * Wake up the main thread, to make sure it re-checks the window
+   * destruction condition. 
+   */
+
+  g_main_context_wakeup (NULL);
 }
 
 JNIEXPORT void JNICALL
--- jni/gtk-peer/gnu_java_awt_peer_gtk_GtkToolkit.c	4 Jan 2005 18:45:23 -0000	1.2.16.2
+++ jni/gtk-peer/gnu_java_awt_peer_gtk_GtkToolkit.c	18 Jan 2005 03:28:59 -0000
@@ -40,6 +40,8 @@
 #include "gnu_java_awt_peer_gtk_GtkToolkit.h"
 #include "gthread-jni.h"
 
+#include <sys/time.h>
+
 #ifdef JVM_SUN
   struct state_table *native_state_table;
   struct state_table *native_global_ref_table;
@@ -298,12 +300,27 @@
     dpi_conversion_factor = PANGO_SCALE * 72.0 / (int_dpi / PANGO_SCALE);
 }
 
+static int
+within_human_latency_tolerance(struct timeval *init)
+{
+  struct timeval curr;
+  unsigned long milliseconds_elapsed;
+
+  gettimeofday(&curr, NULL);
+  
+  milliseconds_elapsed = (((curr.tv_sec * 1000) + (curr.tv_usec / 1000))
+			  - ((init->tv_sec * 1000) + (init->tv_usec / 1000)));
+  
+  return milliseconds_elapsed < 100;
+}
+
 
 JNIEXPORT void JNICALL 
 Java_gnu_java_awt_peer_gtk_GtkToolkit_iterateNativeQueue
 (JNIEnv *env, 
  jobject self __attribute__((unused)),
- jobject lockedQueue)
+ jobject lockedQueue,
+ jboolean block)
 {
   /* We're holding an EventQueue lock, and we're about to acquire the GDK
    * lock before dropping the EventQueue lock. This can deadlock if someone
@@ -313,21 +330,30 @@
    * acquiring the GDK lock and calling back into
    * EventQueue.getNextEvent().
    */
+
+  struct timeval init;
+  gettimeofday(&init, NULL);
+
   gdk_threads_enter ();
   (*env)->MonitorExit (env, lockedQueue);
 
-  /* It is quite important that this be a do .. while loop. The first pass
-   * should do an iteration w/o a test so that it sleeps when there really
-   * aren't any events; and the loop should continue for as many events as
-   * there are to avoid pointless thrashing up and down through JNI (it
-   * runs very slowly when this is not a loop).
-   */
-  do 
+  if (block)
     {
-      gtk_main_iteration();
+      
+      /* If we're blocking-when-empty, we want a do .. while loop. */
+      do 
+	gtk_main_iteration ();
+      while (within_human_latency_tolerance (&init) 
+	     && gtk_events_pending ());
     }
-  while (gtk_events_pending());
-
+  else
+    {
+      /* If we're not blocking-when-empty, we want a while loop. */
+      while (within_human_latency_tolerance (&init) 
+	     && gtk_events_pending ())
+	gtk_main_iteration ();      
+    }
+  
   (*env)->MonitorEnter (env, lockedQueue);
   gdk_threads_leave ();
 }

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]