egl/wayland: Avoid race conditions when on non-main thread
authorJonas Ådahl <jadahl@gmail.com>
Fri, 13 Jan 2017 15:05:10 +0000 (23:05 +0800)
committerEmil Velikov <emil.l.velikov@gmail.com>
Fri, 13 Jan 2017 15:50:37 +0000 (15:50 +0000)
When EGL is used on some other thread than the thread that drives the
main wl_display queue, the Wayland EGL dri2 implementation is
vulnerable to a race condition related to display round trips and global
object advertisements.

The race that may happen is that after after a proxy is created, but
before the queue is set, events meant to be emitted via the yet to be
set queue may already have been queued on the wrong queue.

In order to make it possible to avoid this race, wayland 1.11
introduced new API that allows creating a proxy wrapper that may be used
as the factory proxy when creating new proxies via Wayland requests. The
queue of a proxy wrapper can be changed without effecting what queue
events emitted by the actual proxy will be queued on, while still
effecting what default queue proxies created from it will have.

By introducing a wl_display proxy wrapper and using this when performing
round trips (via wl_display_sync()) and retrieving the global objects (via
wl_display_get_registry()), the mentioned race condition is avoided.

Signed-off-by: Jonas Ådahl <jadahl@gmail.com>
Cc: mesa-stable@lists.freedesktop.org
Reviewed-by: Daniel Stone <daniels@collabora.com>
configure.ac
src/egl/drivers/dri2/egl_dri2.c
src/egl/drivers/dri2/egl_dri2.h
src/egl/drivers/dri2/platform_wayland.c

index bc92fb5535c1285c04de8c5ec74546c360d690ff..459f3e8b0a5745f922732b602f4e3e20cc12fa32 100644 (file)
@@ -84,7 +84,7 @@ GLPROTO_REQUIRED=1.4.14
 LIBOMXIL_BELLAGIO_REQUIRED=0.0
 LIBVA_REQUIRED=0.38.0
 VDPAU_REQUIRED=1.1
-WAYLAND_REQUIRED=1.2.0
+WAYLAND_REQUIRED=1.11
 XCB_REQUIRED=1.9.3
 XCBDRI2_REQUIRED=1.8
 XCBGLX_REQUIRED=1.8.1
index 52fbdff0b128d1ad3e52e792f4f3b4e72d1b38bf..cf72a7e44cc3624c13e685a59d38f4b543614811 100644 (file)
@@ -914,6 +914,7 @@ dri2_display_release(_EGLDisplay *disp)
           wl_shm_destroy(dri2_dpy->wl_shm);
       wl_registry_destroy(dri2_dpy->wl_registry);
       wl_event_queue_destroy(dri2_dpy->wl_queue);
+      wl_proxy_wrapper_destroy(dri2_dpy->wl_dpy_wrapper);
       if (dri2_dpy->own_device) {
          wl_display_disconnect(dri2_dpy->wl_dpy);
       }
index eac58f3cbf35ba7efdafdc165eb461b004482cbf..f3437e2b5a223764f8d2d77ee942a348f75545ae 100644 (file)
@@ -208,6 +208,7 @@ struct dri2_egl_display
 
 #ifdef HAVE_WAYLAND_PLATFORM
    struct wl_display        *wl_dpy;
+   struct wl_display        *wl_dpy_wrapper;
    struct wl_registry       *wl_registry;
    struct wl_drm            *wl_server_drm;
    struct wl_drm            *wl_drm;
index 2b50d8ec949e78fbf0586cd8e00ab9478d0ce0a1..17ff68479fd76902c8b3132d423bf5faf0fcdfc4 100644 (file)
@@ -74,9 +74,8 @@ roundtrip(struct dri2_egl_display *dri2_dpy)
    struct wl_callback *callback;
    int done = 0, ret = 0;
 
-   callback = wl_display_sync(dri2_dpy->wl_dpy);
+   callback = wl_display_sync(dri2_dpy->wl_dpy_wrapper);
    wl_callback_add_listener(callback, &sync_listener, &done);
-   wl_proxy_set_queue((struct wl_proxy *) callback, dri2_dpy->wl_queue);
    while (ret != -1 && !done)
       ret = wl_display_dispatch_queue(dri2_dpy->wl_dpy, dri2_dpy->wl_queue);
 
@@ -780,11 +779,9 @@ dri2_wl_swap_buffers_with_damage(_EGLDriver *drv,
     * handle the commit and send a release event before checking for a free
     * buffer */
    if (dri2_surf->throttle_callback == NULL) {
-      dri2_surf->throttle_callback = wl_display_sync(dri2_dpy->wl_dpy);
+      dri2_surf->throttle_callback = wl_display_sync(dri2_dpy->wl_dpy_wrapper);
       wl_callback_add_listener(dri2_surf->throttle_callback,
                                &throttle_listener, dri2_surf);
-      wl_proxy_set_queue((struct wl_proxy *) dri2_surf->throttle_callback,
-                         dri2_dpy->wl_queue);
    }
 
    wl_display_flush(dri2_dpy->wl_dpy);
@@ -1159,12 +1156,17 @@ dri2_initialize_wayland_drm(_EGLDriver *drv, _EGLDisplay *disp)
 
    dri2_dpy->wl_queue = wl_display_create_queue(dri2_dpy->wl_dpy);
 
+   dri2_dpy->wl_dpy_wrapper = wl_proxy_create_wrapper(dri2_dpy->wl_dpy);
+   if (dri2_dpy->wl_dpy_wrapper == NULL)
+      goto cleanup_dpy_wrapper;
+
+   wl_proxy_set_queue((struct wl_proxy *) dri2_dpy->wl_dpy_wrapper,
+                      dri2_dpy->wl_queue);
+
    if (dri2_dpy->own_device)
       wl_display_dispatch_pending(dri2_dpy->wl_dpy);
 
-   dri2_dpy->wl_registry = wl_display_get_registry(dri2_dpy->wl_dpy);
-   wl_proxy_set_queue((struct wl_proxy *) dri2_dpy->wl_registry,
-                      dri2_dpy->wl_queue);
+   dri2_dpy->wl_registry = wl_display_get_registry(dri2_dpy->wl_dpy_wrapper);
    wl_registry_add_listener(dri2_dpy->wl_registry,
                             &registry_listener_drm, dri2_dpy);
    if (roundtrip(dri2_dpy) < 0 || dri2_dpy->wl_drm == NULL)
@@ -1282,6 +1284,8 @@ dri2_initialize_wayland_drm(_EGLDriver *drv, _EGLDisplay *disp)
    wl_drm_destroy(dri2_dpy->wl_drm);
  cleanup_registry:
    wl_registry_destroy(dri2_dpy->wl_registry);
+   wl_proxy_wrapper_destroy(dri2_dpy->wl_dpy_wrapper);
+ cleanup_dpy_wrapper:
    wl_event_queue_destroy(dri2_dpy->wl_queue);
    if (disp->PlatformDisplay == NULL)
       wl_display_disconnect(dri2_dpy->wl_dpy);
@@ -1595,11 +1599,9 @@ dri2_wl_swrast_commit_backbuffer(struct dri2_egl_surface *dri2_surf)
     * handle the commit and send a release event before checking for a free
     * buffer */
    if (dri2_surf->throttle_callback == NULL) {
-      dri2_surf->throttle_callback = wl_display_sync(dri2_dpy->wl_dpy);
+      dri2_surf->throttle_callback = wl_display_sync(dri2_dpy->wl_dpy_wrapper);
       wl_callback_add_listener(dri2_surf->throttle_callback,
                                &throttle_listener, dri2_surf);
-      wl_proxy_set_queue((struct wl_proxy *) dri2_surf->throttle_callback,
-                         dri2_dpy->wl_queue);
    }
 
    wl_display_flush(dri2_dpy->wl_dpy);
@@ -1875,12 +1877,17 @@ dri2_initialize_wayland_swrast(_EGLDriver *drv, _EGLDisplay *disp)
 
    dri2_dpy->wl_queue = wl_display_create_queue(dri2_dpy->wl_dpy);
 
+   dri2_dpy->wl_dpy_wrapper = wl_proxy_create_wrapper(dri2_dpy->wl_dpy);
+   if (dri2_dpy->wl_dpy_wrapper == NULL)
+      goto cleanup_dpy_wrapper;
+
+   wl_proxy_set_queue((struct wl_proxy *) dri2_dpy->wl_dpy_wrapper,
+                      dri2_dpy->wl_queue);
+
    if (dri2_dpy->own_device)
       wl_display_dispatch_pending(dri2_dpy->wl_dpy);
 
-   dri2_dpy->wl_registry = wl_display_get_registry(dri2_dpy->wl_dpy);
-   wl_proxy_set_queue((struct wl_proxy *) dri2_dpy->wl_registry,
-                      dri2_dpy->wl_queue);
+   dri2_dpy->wl_registry = wl_display_get_registry(dri2_dpy->wl_dpy_wrapper);
    wl_registry_add_listener(dri2_dpy->wl_registry,
                             &registry_listener_swrast, dri2_dpy);
 
@@ -1922,6 +1929,8 @@ dri2_initialize_wayland_swrast(_EGLDriver *drv, _EGLDisplay *disp)
    wl_shm_destroy(dri2_dpy->wl_shm);
  cleanup_registry:
    wl_registry_destroy(dri2_dpy->wl_registry);
+   wl_proxy_wrapper_destroy(dri2_dpy->wl_dpy_wrapper);
+ cleanup_dpy_wrapper:
    wl_event_queue_destroy(dri2_dpy->wl_queue);
    if (disp->PlatformDisplay == NULL)
       wl_display_disconnect(dri2_dpy->wl_dpy);