[PATCH wayland] client: Check that the proxy object exists before invoking closure

Kristian Høgsberg hoegsberg at gmail.com
Mon Oct 29 13:09:26 PDT 2012

On Sun, Oct 28, 2012 at 12:45:40AM +0200, Jonas Ådahl wrote:
> Even when a closure got queued in the queue of a proxy, the proxy object
> can be destroyed during a closure invoke earlier in the queue. Therefore
> check that the proxy object still exists before invoking a closure.
> Signed-off-by: Jonas Ådahl <jadahl at gmail.com>
> ---
> Hi,
> I've been seeing crashes in dispatch_event() when destroying the closure,
> and it seems to be because of how frame callbacks were handled in mesa in
> combination with surfaces being destroyed. The cause seems to be a race
> condition occurring when eglSwapBuffers() is called soon after
> eglSurfaceDestroy(), for example when a surface is attached after another
> was destroyed.
> This race condition could be fixed by destroying the callback proxy when
> destroying the surface, but doing this triggers another bug in the event
> queue handling code which is fixed in this patch. What happened was that
> when a proxy object was destroyed as explained above, it could
> potentially happen after a closure associated with the proxy object was
> queued, but before it was invoked.

This happens in the case where we get a wl_callback.done, then
wl_display.delete_id (or the wl_callback) ID, and queue the done event
in the EGL queue and the delete_id in the main queue.  Then we
dispatch the main queue and as part of that calls wl_callback delete,
which, together with the wl_display.delete_id destroys the proxy and
inserts a NULL in the map.

The problem with just checking for proxy == NULL is that if we set the
proxy to NULL, we've seen the delete_id event, but that also means
that we could have reallocated that ID for something else.

The core issue is that we're deleting a proxy associated with one
event queue (EGL) in a callback from another (main).  If you delete
the object from within a callback from its own queue, the order of
events will be fine. A workaround would be to always dispatch all EGL
events before deleting the callback, if we're not running out of an
EGL event callback.

That's not very nice though, and I think that we need to make
wl_proxy::id_deleted into a uint32_t flags field and make id_deleted a
flag and add a WL_PROXY_QUEUED flag.  Then when we queue an event, we
mark the proxy with the QUEUED flag and clear the flag before
dispatching the event.  Then if we get a delete_id and hit
wl_proxy_destroy for a queue proxy, we can insert
WL_QUEUE_ZOMBIE_OBJECT (that'll be (void *) 3) and then check for that
in dispatch_event, don't call out if we get that and call
wl_map_remove() for the ID if we do.  Something like that...


> Jonas
>  src/wayland-client.c |    2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> diff --git a/src/wayland-client.c b/src/wayland-client.c
> index 7e50b40..155a5b7 100644
> --- a/src/wayland-client.c
> +++ b/src/wayland-client.c
> @@ -740,7 +740,7 @@ dispatch_event(struct wl_display *display, struct wl_event_queue *queue)
>  	pthread_mutex_unlock(&display->mutex);
> -	if (proxy != WL_ZOMBIE_OBJECT &&
> +	if (proxy && proxy != WL_ZOMBIE_OBJECT &&
>  	    proxy->object.implementation && ret == 0) {
>  		if (wl_debug)
>  			wl_closure_print(closure, &proxy->object, false);
> -- 
> _______________________________________________
> wayland-devel mailing list
> wayland-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/wayland-devel

More information about the wayland-devel mailing list