[PATCH weston 1/4] Handle requests using outputs that have been unplugged

Pekka Paalanen ppaalanen at gmail.com
Tue May 27 00:47:44 PDT 2014


Hi,

I see you posted a v2 of this patch, but I think my comments below are
still valid.


On Mon, 19 May 2014 18:23:45 +0100
Neil Roberts <neil at linux.intel.com> wrote:

> Previously when an output was unplugged the clients' resources for it
> were left around and if they were used in a request it could cause
> Weston to access invalid memory. Now when an output is unplugged the
> resources for it are marked as zombies. This is done by setting a
> dummy dispatcher and setting the user data to NULL. The dispatcher
> ignores all requests except for one which is marked as a destructor.
> When the destructor is called the zombie resource will be destroyed.
> The opcode for the destructor request is stashed into one of the
> pointers in the resource's linked-list node so that it can be compared
> against all of the opcodes later. The wl_output interface doesn't
> currently have any requests nor a destructor but the intention is that
> this mechanism could also be used later for more complicated
> interfaces.
> 
> Any requests that take a wl_output as an argument have also been
> patched to take into account that the output can now be a zombie.
> These are handled on a case-by-case basis but in general if the
> argument is allowed to be NULL then zombie resources will be treated
> as such. Otherwise the request is generally completely ignored.
> 
> The screenshooter interface is a special case because that has a
> callback so we can't really just ignore the request. Instead the
> buffer for the screenshot is cleared and the callback is immediately
> invoked.

I think we should be using the term "inert".

"zombie" already means a protocol object id, that is still in the object
map waiting for confirmation that the other side has acknowledged its
destruction, but there is nothing existing for it anymore (the wl_proxy
has already been destroyed).

To me "zombie" refers to protocol objects that are already destroyed on
one side, and waiting for the other side to ack.

"Inert" OTOH means a protocol object that is not destroyed, it still
exists, but what it used to mean or refer to is no longer valid.
Protocol objects created from binding to globals are just one example.
Another example is a wl_sub_surface, whose wl_surface has been
destroyed.

Inert is also a term that is already being used in the protocol
specifications.

> The code for zombifying a resource is based on a snippet by Jason
> Esktrand.
> 
> https://bugs.freedesktop.org/show_bug.cgi?id=78415
> ---
>  desktop-shell/input-panel.c         |  6 +++++
>  desktop-shell/shell.c               | 38 ++++++++++++++++++++++-------
>  fullscreen-shell/fullscreen-shell.c | 12 ++++++++++
>  src/compositor.c                    | 36 ++++++++++++++++++++++++++++
>  src/compositor.h                    |  3 +++
>  src/screenshooter.c                 | 48 +++++++++++++++++++++++++++++++++----
>  6 files changed, 130 insertions(+), 13 deletions(-)
> 
> diff --git a/desktop-shell/input-panel.c b/desktop-shell/input-panel.c
> index 7623f6c..df365fb 100644
> --- a/desktop-shell/input-panel.c
> +++ b/desktop-shell/input-panel.c
> @@ -252,6 +252,12 @@ input_panel_surface_set_toplevel(struct wl_client *client,
>  	struct input_panel_surface *input_panel_surface =
>  		wl_resource_get_user_data(resource);
>  	struct desktop_shell *shell = input_panel_surface->shell;
> +	struct weston_output *output;
> +
> +	output = wl_resource_get_user_data(output_resource);
> +	/* Ignore the request if the output has become a zombie */
> +	if (output == NULL)
> +		return;
>  
>  	wl_list_insert(&shell->input_panel.surfaces,
>  		       &input_panel_surface->link);
> diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c
> index dd0b2f9..97c5d11 100644
> --- a/desktop-shell/shell.c
> +++ b/desktop-shell/shell.c
> @@ -2431,10 +2431,12 @@ shell_surface_set_fullscreen(struct wl_client *client,
>  	struct shell_surface *shsurf = wl_resource_get_user_data(resource);
>  	struct weston_output *output;
>  
> -	if (output_resource)
> +	if (output_resource) {
>  		output = wl_resource_get_user_data(output_resource);
> -	else
> +		/* Output can be NULL here if the resource is a zombie */
> +	} else {
>  		output = NULL;
> +	}

It could be NULL also on purpose, so the comment might be misleading.

>  
>  	shell_surface_set_parent(shsurf, NULL);
>  
> @@ -2566,10 +2568,12 @@ shell_surface_set_maximized(struct wl_client *client,
>  	shsurf->type = SHELL_SURFACE_TOPLEVEL;
>  	shell_surface_set_parent(shsurf, NULL);
>  
> -	if (output_resource)
> +	if (output_resource) {
>  		output = wl_resource_get_user_data(output_resource);
> -	else
> +		/* output can be NULL here if the resource is zombified */
> +	} else {
>  		output = NULL;
> +	}

It could be NULL also on purpose.

>  
>  	shell_surface_set_output(shsurf, output);
>  
> @@ -3487,10 +3491,13 @@ xdg_surface_set_fullscreen(struct wl_client *client,
>  	shsurf->state_requested = true;
>  	shsurf->requested_state.fullscreen = true;
>  
> -	if (output_resource)
> +	if (output_resource) {
>  		output = wl_resource_get_user_data(output_resource);
> -	else
> +		/* output can still be NULL here if the resource has
> +		 * become a zombie */
> +	} else {
>  		output = NULL;
> +	}

It could be NULL also on purpose.

>  
>  	shell_surface_set_output(shsurf, output);
>  	shsurf->fullscreen_output = shsurf->output;
> @@ -3919,6 +3926,10 @@ desktop_shell_set_background(struct wl_client *client,
>  		return;
>  	}
>  
> +	/* Skip the request if the output has become a zombie */
> +	if (wl_resource_get_user_data(output_resource) == NULL)
> +		return;
> +
>  	wl_list_for_each_safe(view, next, &surface->views, surface_link)
>  		weston_view_destroy(view);
>  	view = weston_view_create(surface);
> @@ -3953,6 +3964,7 @@ desktop_shell_set_panel(struct wl_client *client,
>  	struct desktop_shell *shell = wl_resource_get_user_data(resource);
>  	struct weston_surface *surface =
>  		wl_resource_get_user_data(surface_resource);
> +	struct weston_output *output;
>  	struct weston_view *view, *next;
>  
>  	if (surface->configure) {
> @@ -3962,14 +3974,20 @@ desktop_shell_set_panel(struct wl_client *client,
>  		return;
>  	}
>  
> +	output = wl_resource_get_user_data(output_resource);
> +
> +	/* Skip the request if the output has become a zombie */
> +	if (output == NULL)
> +		return;
> +
>  	wl_list_for_each_safe(view, next, &surface->views, surface_link)
>  		weston_view_destroy(view);
>  	view = weston_view_create(surface);
>  
>  	surface->configure = panel_configure;
>  	surface->configure_private = shell;
> -	surface->output = wl_resource_get_user_data(output_resource);
> -	view->output = surface->output;
> +	surface->output = output;
> +	view->output = output;
>  	desktop_shell_send_configure(resource, 0,
>  				     surface_resource,
>  				     surface->output->width,
> @@ -5362,6 +5380,10 @@ screensaver_set_surface(struct wl_client *client,
>  	struct weston_output *output = wl_resource_get_user_data(output_resource);
>  	struct weston_view *view, *next;
>  
> +	/* Skip the request if the output has become a zombie */
> +	if (output == NULL)
> +		return;
> +
>  	/* Make sure we only have one view */
>  	wl_list_for_each_safe(view, next, &surface->views, surface_link)
>  		weston_view_destroy(view);

> diff --git a/src/compositor.c b/src/compositor.c
> index 574db2d..ea0c9b4 100644
> --- a/src/compositor.c
> +++ b/src/compositor.c
> @@ -3107,9 +3107,42 @@ weston_compositor_remove_output(struct weston_compositor *compositor,
>  	}
>  }
>  
> +static int
> +zombie_dispatcher(const void *data, void *resource, uint32_t opcode,
> +		  const struct wl_message *msg, union wl_argument *args)
> +{
> +	struct wl_list *link = wl_resource_get_link(resource);
> +	uint32_t destructor = (uintptr_t) link->next;
> +
> +	if (opcode == destructor)
> +		wl_resource_destroy(resource);
> +
> +	return 0;
> +}
> +
> +WL_EXPORT void
> +weston_resource_zombify(struct wl_resource *res, uint32_t destructor)
> +{
> +	struct wl_list *link;
> +
> +	/* Set a dummy dispatcher so that all requests will be
> +	 * ignored. The NULL user_data is used to mark that this is a
> +	 * zombie */
> +	wl_resource_set_dispatcher(res, zombie_dispatcher,
> +				   NULL /* implementation */,
> +				   NULL /* user data */,
> +				   NULL /* destroy */);
> +	/* Stash the destructor opcode in one of the pointers of the
> +	 * list node so we can compare it later in the dispatcher */
> +	link = wl_resource_get_link(res);
> +	link->next = (struct wl_list *) (uintptr_t) destructor;
> +}

You are overwriting the destroy vfunc, how do we get the user data
destroyed? Is the caller responsible for calling the destroy vfunc
manually first?

I think a better API would be to have wl_resource destroy function that
turns the object inert. It would call the destroy vfunc automatically.
Also documenting it as a "delayed" destructor would make the point,
that as long as the compositor is concerned, the protocol object is
gone, which also means you are free to abuse the list pointers (ugh).

wl_resource_zombify() also has the limitation, that it cannot handle
interfaces which have requests creating new protocol objects. This
should be clearly documented, but it also makes me wonder if this is
really worth it.

> +
>  WL_EXPORT void
>  weston_output_destroy(struct weston_output *output)
>  {
> +	struct wl_resource *resource, *tmp;
> +
>  	output->destroying = 1;
>  
>  	weston_compositor_remove_output(output->compositor, output);
> @@ -3124,6 +3157,9 @@ weston_output_destroy(struct weston_output *output)
>  	output->compositor->output_id_pool &= ~(1 << output->id);
>  
>  	wl_global_destroy(output->global);
> +
> +	wl_resource_for_each_safe(resource, tmp, &output->resource_list)
> +		weston_resource_zombify(resource, ~0);
>  }
>  
>  static void

Thanks,
pq


More information about the wayland-devel mailing list