[gtk+/gtk-3-20] gdk/wayland: use the multi-thread safe wayland API



commit 246385a8e7a435acd0c854aa43912352eb104acb
Author: Matthew Waters <matthew centricular com>
Date:   Fri Mar 18 17:17:23 2016 +1100

    gdk/wayland: use the multi-thread safe wayland API
    
    This is required for proper integration with any other library/application that
    may perform wayland API calls and poll() the wayland fd from multiple threads.
    Using wl_display_dispatch{_queue}() is thread-safe if not mixed with custom
    poll() usage, which GSource/GMainContext does.
    
    Essentially, the problem is that multiple threads polling and reading
    the same fd is extremely racy.  Use the wayland provided API for allowing
    concurrent access to the wayland display fd.
    
    See the wayland man pages for wl_display_prepare_read(),
    wl_display_cancel_read() and wl_display_read_events() for more details.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=763852

 gdk/wayland/gdkeventsource.c |   56 +++++++++++++++++++++++++++++++++---------
 1 files changed, 44 insertions(+), 12 deletions(-)
---
diff --git a/gdk/wayland/gdkeventsource.c b/gdk/wayland/gdkeventsource.c
index f04281c..a239cab 100644
--- a/gdk/wayland/gdkeventsource.c
+++ b/gdk/wayland/gdkeventsource.c
@@ -28,6 +28,7 @@ typedef struct _GdkWaylandEventSource {
   GPollFD pfd;
   uint32_t mask;
   GdkDisplay *display;
+  gboolean reading;
 } GdkWaylandEventSource;
 
 static gboolean
@@ -50,12 +51,20 @@ gdk_event_source_prepare (GSource *base,
   if (_gdk_event_queue_find_first (source->display) != NULL)
     return TRUE;
 
+  /* wl_display_prepare_read() needs to be balanced with either
+   * wl_display_read_events() or wl_display_cancel_read()
+   * (in gdk_event_source_check() */
+  if (source->reading)
+    return FALSE;
+
+  /* if prepare_read() returns non-zero, there are events to be dispatched */
+  if (wl_display_prepare_read (display->wl_display) != 0)
+    return TRUE;
+  source->reading = TRUE;
+
   if (wl_display_flush (display->wl_display) < 0)
     g_error ("Error flushing display: %s", g_strerror (errno));
 
-  if (wl_display_dispatch_pending (display->wl_display) < 0)
-    g_error ("Error dispatching display: %s", g_strerror (errno));
-
   return FALSE;
 }
 
@@ -63,9 +72,29 @@ static gboolean
 gdk_event_source_check (GSource *base)
 {
   GdkWaylandEventSource *source = (GdkWaylandEventSource *) base;
+  GdkWaylandDisplay *display_wayland = (GdkWaylandDisplay *) source->display;
 
   if (source->display->event_pause_count > 0)
-    return _gdk_event_queue_find_first (source->display) != NULL;
+    {
+      if (source->reading)
+        wl_display_cancel_read (display_wayland->wl_display);
+      source->reading = FALSE;
+
+      return _gdk_event_queue_find_first (source->display) != NULL;
+    }
+
+  /* read the events from the wayland fd into their respective queues if we have data */
+  if (source->reading)
+    {
+      if (source->pfd.revents & G_IO_IN)
+        {
+          if (wl_display_read_events (display_wayland->wl_display) < 0)
+            g_error ("Error reading events from display: %s", g_strerror (errno));
+        }
+      else
+        wl_display_cancel_read (display_wayland->wl_display);
+      source->reading = FALSE;
+    }
 
   return _gdk_event_queue_find_first (source->display) != NULL ||
     source->pfd.revents;
@@ -97,8 +126,14 @@ gdk_event_source_dispatch (GSource     *base,
 }
 
 static void
-gdk_event_source_finalize (GSource *source)
+gdk_event_source_finalize (GSource *base)
 {
+  GdkWaylandEventSource *source = (GdkWaylandEventSource *) base;
+  GdkWaylandDisplay *display = (GdkWaylandDisplay *) source->display;
+
+  if (source->reading)
+    wl_display_cancel_read (display->wl_display);
+  source->reading = FALSE;
 }
 
 static GSourceFuncs wl_glib_source_funcs = {
@@ -157,14 +192,11 @@ _gdk_wayland_display_queue_events (GdkDisplay *display)
   display_wayland = GDK_WAYLAND_DISPLAY (display);
   source = (GdkWaylandEventSource *) display_wayland->event_source;
 
-  if (source->pfd.revents & G_IO_IN)
+  if (wl_display_dispatch_pending (display_wayland->wl_display) < 0)
     {
-      if (wl_display_dispatch (display_wayland->wl_display) < 0)
-        {
-          g_warning ("Error %d (%s) dispatching to Wayland display.",
-                     errno, g_strerror (errno));
-          exit (1);
-        }
+      g_warning ("Error %d (%s) dispatching to Wayland display.",
+                 errno, g_strerror (errno));
+      exit (1);
     }
 
   if (source->pfd.revents & (G_IO_ERR | G_IO_HUP))


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]