[PATCH weston v2] Handle requests using outputs that have been unplugged

Neil Roberts neil at linux.intel.com
Tue May 20 04:09:24 PDT 2014


Version 2 of the patch makes the following changes:

• The patch is reordered to be on top of the patch to handle
  wl_output.release so that it doesn't need to have a dummy marker for
  zombifying without a destructor.

• The destructor opcode is stored in the implementation pointer rather
  than using the list node. This requires the Wayland patch to add
  wl_resource_get_implementation.

• The wl_fullscreen_shell.present_surface function now bails out if
  the output is a zombie instead of treating it as if it were NULL.

I've pushed the patches to the following two branches to make it a bit
easier to untangle the thread:

https://github.com/bpeel/wayland/commits/wip/output-release
https://github.com/bpeel/weston/commits/wip/output-release

- Neil

------- >8 --------------- (use git am --scissors to automatically chop here)

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 the
implementation pointer so that it can be compared against all of the
opcodes later.

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.

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 | 13 ++++++++++
 src/compositor.c                    | 33 +++++++++++++++++++++++++
 src/compositor.h                    |  3 +++
 src/screenshooter.c                 | 48 +++++++++++++++++++++++++++++++++----
 6 files changed, 128 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;
+	}
 
 	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;
+	}
 
 	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;
+	}
 
 	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/fullscreen-shell/fullscreen-shell.c b/fullscreen-shell/fullscreen-shell.c
index a8dad8e..81e1b35 100644
--- a/fullscreen-shell/fullscreen-shell.c
+++ b/fullscreen-shell/fullscreen-shell.c
@@ -673,6 +673,14 @@ fullscreen_shell_present_surface(struct wl_client *client,
 
 	if (output_res) {
 		output = wl_resource_get_user_data(output_res);
+		/* Ignore the request if the output is a zombie */
+		if (output == NULL)
+			return;
+	} else {
+		output = NULL;
+	}
+
+	if (output) {
 		fsout = fs_output_for_output(output);
 		fs_output_set_surface(fsout, surface, method, 0, 0);
 	} else {
@@ -712,6 +720,11 @@ fullscreen_shell_present_surface_for_mode(struct wl_client *client,
 	struct fs_output *fsout;
 
 	output = wl_resource_get_user_data(output_res);
+
+	/* Skip the request if the output has become a zombie */
+	if (output == NULL)
+		return;
+
 	fsout = fs_output_for_output(output);
 
 	if (surface_res == NULL) {
diff --git a/src/compositor.c b/src/compositor.c
index d3a52bd..1efc41e 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -3120,9 +3120,37 @@ 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)
+{
+	const void *implementation = wl_resource_get_implementation(resource);
+	uint32_t destructor = (uintptr_t) implementation;
+
+	if (opcode == destructor)
+		wl_resource_destroy(resource);
+
+	return 0;
+}
+
+WL_EXPORT void
+weston_resource_zombify(struct wl_resource *res, uint32_t destructor)
+{
+	/* Set a dummy dispatcher so that all requests will be
+	 * ignored. The NULL user_data is used to mark that this is a
+	 * zombie. The destructor opcode is stashed in the
+	 * implementation pointer */
+	wl_resource_set_dispatcher(res, zombie_dispatcher,
+				   (void *) (uintptr_t) destructor,
+				   NULL /* user data */,
+				   NULL /* destroy */);
+}
+
 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);
@@ -3137,6 +3165,11 @@ 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,
+					WL_REQUEST_OPCODE(wl_output_interface,
+							  release));
 }
 
 static void
diff --git a/src/compositor.h b/src/compositor.h
index 057f8be..6d9800a 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -1416,6 +1416,9 @@ weston_transformed_region(int width, int height,
 void *
 weston_load_module(const char *name, const char *entrypoint);
 
+void
+weston_resource_zombify(struct wl_resource *res, uint32_t destructor);
+
 #ifdef  __cplusplus
 }
 #endif
diff --git a/src/screenshooter.c b/src/screenshooter.c
index 369e920..574fde1 100644
--- a/src/screenshooter.c
+++ b/src/screenshooter.c
@@ -213,6 +213,33 @@ weston_screenshooter_shoot(struct weston_output *output,
 }
 
 static void
+shoot_zombie_output(struct weston_buffer *buffer,
+		    weston_screenshooter_done_func_t done,
+		    void *data)
+{
+	struct wl_shm_buffer *shm_buffer;
+	int32_t height, stride;
+	void *pixels;
+
+	shm_buffer = wl_shm_buffer_get(buffer->resource);
+
+	if (shm_buffer == NULL) {
+		done(data, WESTON_SCREENSHOOTER_BAD_BUFFER);
+		return;
+	}
+
+	height = wl_shm_buffer_get_height(shm_buffer);
+	stride = wl_shm_buffer_get_stride(shm_buffer);
+	pixels = wl_shm_buffer_get_data(shm_buffer);
+
+	wl_shm_buffer_begin_access(shm_buffer);
+	memset(pixels, 0, height * stride);
+	wl_shm_buffer_end_access(shm_buffer);
+
+	done(data, WESTON_SCREENSHOOTER_SUCCESS);
+}
+
+static void
 screenshooter_done(void *data, enum weston_screenshooter_outcome outcome)
 {
 	struct wl_resource *resource = data;
@@ -235,17 +262,28 @@ screenshooter_shoot(struct wl_client *client,
 		    struct wl_resource *output_resource,
 		    struct wl_resource *buffer_resource)
 {
-	struct weston_output *output =
-		wl_resource_get_user_data(output_resource);
-	struct weston_buffer *buffer =
-		weston_buffer_from_resource(buffer_resource);
+	struct weston_output *output;
+	struct weston_buffer *buffer;
+
+	output = wl_resource_get_user_data(output_resource);
+	buffer = weston_buffer_from_resource(buffer_resource);
 
 	if (buffer == NULL) {
 		wl_resource_post_no_memory(resource);
 		return;
 	}
 
-	weston_screenshooter_shoot(output, buffer, screenshooter_done, resource);
+	/* If the output has become a zombie then we obviously can't
+	 * take a screenshot. We don't want to report an error because
+	 * that would likely make the client abort and this is an
+	 * unavoidable occurence. Instead we can just clear the
+	 * buffer. This is probably reasonable because that is what
+	 * the output will actually if it is unplugged */
+	if (output == NULL)
+		shoot_zombie_output(buffer, screenshooter_done, resource);
+	else
+		weston_screenshooter_shoot(output, buffer,
+					   screenshooter_done, resource);
 }
 
 struct screenshooter_interface screenshooter_implementation = {
-- 
1.9.0



More information about the wayland-devel mailing list