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 <jad...@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 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/mesa-dev