The problem was that the under Windows message queues are per-thread. So, MsgWaitForMultipleObjects has to run in the thread that created the window (i.e. the VM thread). Since that is not possible, gst has to resort to polling. For 3.2 I'm always going to poll, while in 3.3 I'll poll while the VM is busy and not while it's idle.

This costs something, but the polling is only taking 2% of CPU on a Core2 running at 800 MHz (I was in energy-saving mode :-)) under Wine, so it's probably unnoticeable. I didn't see noticeable delays in GUI action, either. Of course, there's no polling under X11 and Mac OS X.

I haven't tested it under native Windows, but I don't see why it shouldn't work.

I committed this patch and also the binary package generator for Windows. It requires Fedora _and_ Wine, and it's unlikely to work on any machine but mine, but I'll gladly provide help if anyone wants to take a look. Or more likely, I'll gladly provide a pre-built package if anyone wants to test it.

Paolo
diff --git a/packages/gtk/gst-gtk.c b/packages/gtk/gst-gtk.c
index 46b2cf7..91ba8f5 100644
--- a/packages/gtk/gst-gtk.c
+++ b/packages/gtk/gst-gtk.c
@@ -53,7 +53,7 @@
 #include "gstpub.h"
 
 #include <stdio.h>
-#include <ctype.h>
+#include <assert.h>
 #include <errno.h>
 #include <gdk/gdk.h>
 #include <gtk/gtk.h>
@@ -62,6 +62,10 @@
 
 #include <gobject/gvaluecollector.h>
 
+#ifdef G_WIN32_MSG_HANDLE
+#include <windows.h>
+#endif
+
 #ifdef STDC_HEADERS
 #include <stdlib.h>
 #include <string.h>
@@ -719,46 +723,175 @@ static GCond *cond;
 static GCond *cond_dispatch;
 static volatile gboolean queued;
 
+#ifdef G_WIN32_MSG_HANDLE
+static gint
+gst_gtk_poll (GPollFD *fds,
+             guint    nfds,
+             gint     timeout)
+{
+  HANDLE handles[MAXIMUM_WAIT_OBJECTS];
+  gint win32_timeout;
+  gint poll_msgs = -1;
+  GPollFD *f;
+  DWORD ready;
+  gint nhandles = 0;
+
+  for (f = fds; f < &fds[nfds]; ++f)
+      {
+        HANDLE h;
+        assert (f->fd >= 0);
+        if (f->fd == G_WIN32_MSG_HANDLE)
+          {
+            assert (poll_msgs == -1 && nhandles == f - fds);
+            poll_msgs = nhandles;
+#if 1
+            continue;
+#else
+           /* Once the VM will host the event loop, it will be possible to
+              have a MsgWaitForMultipleObjects call in the VM thread and use
+              the result to wake up this side of the loop.  For now resort
+              to polling; messages are checked by the VM thread every 20ms
+              (in the GTK check function, called by g_main_context_check).  */
+            h = hWokenUpEvent;
+#endif
+          }
+        else
+          h = (HANDLE) f->fd;
+        if (nhandles == MAXIMUM_WAIT_OBJECTS)
+          {
+            g_warning (G_STRLOC ": Too many handles to wait for!\n");
+            break;
+          }
+        handles[nhandles++] = (HANDLE) f->fd;
+      }
+
+  if (nhandles == 0)
+    {
+      /* Wait for nothing (huh?) */
+      return 0;
+    }
+
+  /* If the VM were idling, it could in principle use MsgWaitForMultipleObjects
+     and tell us when it gets a message on its queue.  This would remove the
+     need for polling.  However, we cannot implement this until the main
+     loop is moved inside the VM.  */
+  if (poll_msgs != -1 /* && !idle */ )
+    win32_timeout = (timeout == -1 || timeout > 20) ? 20 : timeout;
+  else
+    win32_timeout = (timeout == -1) ? INFINITE : timeout;
+
+  ready = WaitForMultipleObjects (nhandles, handles, FALSE, win32_timeout);
+  if (ready == WAIT_FAILED)
+    {
+      gchar *emsg = g_win32_error_message (GetLastError ());
+      g_warning (G_STRLOC ": WaitForMultipleObjects() failed: %s", emsg);
+      g_free (emsg);
+    }
+
+  for (f = fds; f < &fds[nfds]; ++f)
+    f->revents = 0;
+
+#if 1
+  if (poll_msgs != -1)
+    {
+      if (ready >= WAIT_OBJECT_0 + poll_msgs
+         && ready <= WAIT_OBJECT_0 + nhandles)
+        ready++;
+
+      else if (ready == WAIT_TIMEOUT
+              && win32_timeout != INFINITE)
+        ready = WAIT_OBJECT_0 + poll_msgs;
+    }
+#endif
+
+  if (ready == WAIT_FAILED)
+    return -1;
+  if (ready == WAIT_TIMEOUT)
+    return 0;
+
+  f = &fds[ready - WAIT_OBJECT_0];
+  if (f->events & (G_IO_IN | G_IO_OUT))
+    {
+      if (f->events & G_IO_IN)
+        f->revents |= G_IO_IN;
+      else
+        f->revents |= G_IO_OUT;
+    }
+
+  return 1;
+}
+
+#if 0
+/* libgst should have something like this: */
+
+static gint
+_gst_pause ()
+{
+  idle = true;
+  ResetEvent (hWakeUpEvent);
+  MsgWaitForMultipleObjects (1, &hWakeUpEvent, FALSE, INFINITE, QS_ALLEVENTS);
+  SetEvent (hWokenUpEvent);
+}
+
+static gint
+_gst_wakeup ()
+{
+  idle = false;
+  SetEvent (hWakeUpEvent);
+}
+#endif
+#else
+#define gst_gtk_poll g_poll
+#endif
+
+
 static void
 main_context_acquire_wait (GMainContext *context)
 {
-  g_mutex_lock (mutex);
-  g_main_context_wait (context, cond, mutex);
-
-  /* No need to keep the mutex except during g_main_context_acquire_wait
-     and g_main_context_release_signal, i.e. except while we operate on
-     cond.  */
-  g_mutex_unlock (mutex);
+  while (!g_main_context_wait (context, cond, mutex));
 }
 
 static void
-main_context_release_signal (GMainContext *context)
+main_context_signal (GMainContext *context)
 {
-  g_mutex_lock (mutex);
-  g_main_context_release (context);
-
   /* Restart the polling thread.  Note that #iterate is asynchronous, so
      this might execute before the Smalltalk code finishes running!  This
      allows debugging GTK+ signal handlers.  */
+  g_mutex_lock (mutex);
   queued = false;
   g_cond_broadcast (cond_dispatch);
   g_mutex_unlock (mutex);
 }
 
+static GPollFD *fds;
+static int allocated_nfds, nfds;
+static int maxprio;
+
 static void
 main_context_iterate (GMainContext *context)
 {
+  g_mutex_lock (mutex);
+  if (!fds)
+    {
+      g_mutex_unlock (mutex);
+      return;
+    }
+
+  /* No need to keep the mutex except during g_main_context_acquire_wait
+     and g_main_context_release_signal, i.e. except while we operate on
+     cond.  */
   main_context_acquire_wait (context);
+  g_mutex_unlock (mutex);
+  g_main_context_check (context, maxprio, fds, nfds);
   g_main_context_dispatch (context);
-  main_context_release_signal (context);
+  g_main_context_release (context);
+  main_context_signal (context);
 }
 
 static gpointer
 main_loop_thread (gpointer semaphore)
 {
   OOP semaphoreOOP = semaphore;
-  static GPollFD *fds;
-  static int allocated_nfds;
   GMainContext *context = g_main_loop_get_context (loop);
 
   if (!fds)
@@ -774,9 +907,9 @@ main_loop_thread (gpointer semaphore)
   g_mutex_lock (mutex);
   while (g_main_loop_is_running (loop))
     {
-      int nfds, maxprio, timeout;
+      int timeout;
 
-      g_main_context_wait (context, cond, mutex);
+      main_context_acquire_wait (context);
       g_main_context_prepare (context, &maxprio);
       while ((nfds = g_main_context_query (context, maxprio,
                                            &timeout, fds, allocated_nfds))
@@ -788,18 +921,14 @@ main_loop_thread (gpointer semaphore)
         }
 
       /* Release the context so that the other thread can dispatch while
-         this one polls.  */
-      g_main_context_release (context);
+         this one polls.  g_main_context_release unlocks the mutex for us.  */
       g_mutex_unlock (mutex);
-
-      g_poll (fds, nfds, timeout);
-
-      g_mutex_lock (mutex);
-      g_main_context_wait (context, cond, mutex);
-      g_main_context_check (context, maxprio, fds, nfds);
       g_main_context_release (context);
 
+      gst_gtk_poll (fds, nfds, timeout);
+
       /* Dispatch on the other thread and wait for it to rendez-vous.  */
+      g_mutex_lock (mutex);
       queued = true;
       _gst_vm_proxy->asyncSignal (semaphoreOOP);
       
_______________________________________________
help-smalltalk mailing list
help-smalltalk@gnu.org
http://lists.gnu.org/mailman/listinfo/help-smalltalk

Reply via email to