[PATCH] wayland: Don't race when releasing named buffers
Jonas Ådahl
jadahl at gmail.com
Wed Oct 2 08:06:47 PDT 2013
This patch fixes a race when a client releases a named buffer before the
server had the time to handle 'wl_drm_create_buffer'. When triggered,
the server would fail to create the buffer since the client already
having destroyed it, throwing out the client in the process with a
protocol error.
To solve this, use a lazy non-blocking roundtrip when creating the
buffer that will only potentially block when releasing if the roundtrip
callback was not already invoked.
Signed-off-by: Jonas Ådahl <jadahl at gmail.com>
---
src/egl/drivers/dri2/egl_dri2.h | 1 +
src/egl/drivers/dri2/platform_wayland.c | 74 ++++++++++++++++++++++++++++++++-
2 files changed, 73 insertions(+), 2 deletions(-)
diff --git a/src/egl/drivers/dri2/egl_dri2.h b/src/egl/drivers/dri2/egl_dri2.h
index fba5f81..6508612 100644
--- a/src/egl/drivers/dri2/egl_dri2.h
+++ b/src/egl/drivers/dri2/egl_dri2.h
@@ -189,6 +189,7 @@ struct dri2_egl_surface
struct wl_buffer *wl_buffer;
__DRIimage *dri_image;
int pitch, name;
+ struct wl_callback *created_callback;
#endif
#ifdef HAVE_DRM_PLATFORM
struct gbm_bo *bo;
diff --git a/src/egl/drivers/dri2/platform_wayland.c b/src/egl/drivers/dri2/platform_wayland.c
index 1d417bb..3a873f7 100644
--- a/src/egl/drivers/dri2/platform_wayland.c
+++ b/src/egl/drivers/dri2/platform_wayland.c
@@ -184,6 +184,55 @@ dri2_create_window_surface(_EGLDriver *drv, _EGLDisplay *disp,
window, attrib_list);
}
+static void
+buffer_create_sync_callback(void *data,
+ struct wl_callback *callback,
+ uint32_t serial)
+{
+ struct wl_callback **created_callback = data;
+
+ *created_callback = NULL;
+ wl_callback_destroy(callback);
+}
+
+static const struct wl_callback_listener buffer_create_sync_listener = {
+ buffer_create_sync_callback
+};
+
+static void
+lazy_create_buffer_roundtrip(struct dri2_egl_display *dri2_dpy,
+ struct dri2_egl_surface *dri2_surf)
+{
+ struct wl_callback *callback;
+
+ callback = wl_display_sync(dri2_dpy->wl_dpy);
+ dri2_surf->current->created_callback = callback;
+
+ wl_callback_add_listener(callback,
+ &buffer_create_sync_listener,
+ &dri2_surf->current->created_callback);
+ wl_proxy_set_queue((struct wl_proxy *) callback, dri2_dpy->wl_queue);
+}
+
+static int
+synchronize_create_buffer(struct dri2_egl_display *dri2_dpy,
+ struct dri2_egl_surface *dri2_surf,
+ int i)
+{
+ int ret = 0;
+
+ while (ret != -1 && dri2_surf->color_buffers[i].created_callback)
+ ret = wl_display_dispatch_queue(dri2_dpy->wl_dpy, dri2_dpy->wl_queue);
+
+ if (dri2_surf->color_buffers[i].created_callback) {
+ wl_callback_destroy(dri2_surf->color_buffers[i].created_callback);
+ dri2_surf->color_buffers[i].created_callback = NULL;
+ return -1;
+ }
+
+ return 0;
+}
+
/**
* Called via eglDestroySurface(), drv->API.DestroySurface().
*/
@@ -206,6 +255,8 @@ dri2_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf)
wl_buffer_destroy(dri2_surf->color_buffers[i].wl_buffer);
if (dri2_surf->color_buffers[i].dri_image)
dri2_dpy->image->destroyImage(dri2_surf->color_buffers[i].dri_image);
+ if (dri2_surf->color_buffers[i].created_callback)
+ wl_callback_destroy(dri2_surf->color_buffers[i].created_callback);
}
for (i = 0; i < __DRI_BUFFER_COUNT; i++)
@@ -227,7 +278,7 @@ dri2_destroy_surface(_EGLDriver *drv, _EGLDisplay *disp, _EGLSurface *surf)
return EGL_TRUE;
}
-static void
+static int
dri2_release_buffers(struct dri2_egl_surface *dri2_surf)
{
struct dri2_egl_display *dri2_dpy =
@@ -235,6 +286,11 @@ dri2_release_buffers(struct dri2_egl_surface *dri2_surf)
int i;
for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
+ if (dri2_surf->color_buffers[i].created_callback) {
+ if (synchronize_create_buffer(dri2_dpy, dri2_surf, i) != 0)
+ return -1;
+ }
+
if (dri2_surf->color_buffers[i].wl_buffer &&
!dri2_surf->color_buffers[i].locked)
wl_buffer_destroy(dri2_surf->color_buffers[i].wl_buffer);
@@ -251,6 +307,8 @@ dri2_release_buffers(struct dri2_egl_surface *dri2_surf)
dri2_surf->dri_buffers[i]->attachment != __DRI_BUFFER_BACK_LEFT)
dri2_dpy->dri2->releaseBuffer(dri2_dpy->dri_screen,
dri2_surf->dri_buffers[i]);
+
+ return 0;
}
static int
@@ -349,7 +407,10 @@ dri2_get_buffers_with_format(__DRIdrawable * driDrawable,
(dri2_surf->base.Width != dri2_surf->wl_win->width ||
dri2_surf->base.Height != dri2_surf->wl_win->height)) {
- dri2_release_buffers(dri2_surf);
+ if (dri2_release_buffers(dri2_surf) != 0) {
+ _eglError(EGL_BAD_DISPLAY, "failed to release color buffers");
+ return NULL;
+ }
dri2_surf->base.Width = dri2_surf->wl_win->width;
dri2_surf->base.Height = dri2_surf->wl_win->height;
@@ -381,6 +442,13 @@ dri2_get_buffers_with_format(__DRIdrawable * driDrawable,
for (i = 0; i < ARRAY_SIZE(dri2_surf->color_buffers); i++) {
if (!dri2_surf->color_buffers[i].locked &&
dri2_surf->color_buffers[i].wl_buffer) {
+ if (dri2_surf->color_buffers[i].created_callback) {
+ if (synchronize_create_buffer(dri2_dpy, dri2_surf, i) != 0) {
+ _eglError(EGL_BAD_DISPLAY, "failed to release color buffer");
+ return NULL;
+ }
+ }
+
wl_buffer_destroy(dri2_surf->color_buffers[i].wl_buffer);
dri2_dpy->image->destroyImage(dri2_surf->color_buffers[i].dri_image);
dri2_surf->color_buffers[i].wl_buffer = NULL;
@@ -483,6 +551,8 @@ create_wl_buffer(struct dri2_egl_surface *dri2_surf)
dri2_surf->base.Height,
dri2_surf->current->pitch,
dri2_surf->format);
+
+ lazy_create_buffer_roundtrip(dri2_dpy, dri2_surf);
}
wl_proxy_set_queue((struct wl_proxy *) dri2_surf->current->wl_buffer,
--
1.8.1.2
More information about the wayland-devel
mailing list