[PATCH wayland] shm: Allow deferred deletion of shared memory pools

Derek Foreman derekf at osg.samsung.com
Tue Mar 31 11:26:04 PDT 2015


Some compositors perform rendering in a separate thread.  If a client
destroy occurs during rendering a segfault will occur when the client's
shared memory pools are unmapped and the renderer continues to use them.

This adds a way (disabled by default) to queue shared memory pool deletion
instead of performing it immediately, then the compositor can process
the deletion list at a time that won't interfere with rendering.

Signed-off-by: Derek Foreman <derekf at osg.samsung.com>
---
 src/wayland-private.h |  7 +++++++
 src/wayland-server.c  | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 src/wayland-server.h  |  7 +++++++
 src/wayland-shm.c     | 37 +++++++++++++++++++++++++++++++++++++
 4 files changed, 98 insertions(+)

Hopefully this isn't hugely controversial. :)

Enlightenment's wayland compositor does its rendering asynchronously.  This
gives us some trouble when clients exit, since wayland-server destroys all
the client's resources on our behalf.  Shared memory buffers are immediately
destroyed, which leads to the shared memory pools being unmapped.  The
render thread may already be scanning out from these buffers, so a segfault
can occur.


This patch adds a way to defer the unmapping so we can finish our render pass
before cleaning up any deleted shmem pools.

diff --git a/src/wayland-private.h b/src/wayland-private.h
index db76081..3e1a806 100644
--- a/src/wayland-private.h
+++ b/src/wayland-private.h
@@ -25,6 +25,7 @@
 #ifndef WAYLAND_PRIVATE_H
 #define WAYLAND_PRIVATE_H
 
+#include <stdbool.h>
 #include <stdarg.h>
 
 #define WL_HIDE_DEPRECATED 1
@@ -173,4 +174,10 @@ struct wl_display;
 struct wl_array *
 wl_display_get_additional_shm_formats(struct wl_display *display);
 
+bool
+wl_display_get_deferred_shm_delete(struct wl_display *display);
+
+struct wl_list *
+wl_display_get_deferred_shm_delete_list(struct wl_display *display);
+
 #endif
diff --git a/src/wayland-server.c b/src/wayland-server.c
index ecbae68..eba7bb0 100644
--- a/src/wayland-server.c
+++ b/src/wayland-server.c
@@ -95,6 +95,9 @@ struct wl_display {
 	struct wl_signal destroy_signal;
 
 	struct wl_array additional_shm_formats;
+
+	bool deferred_shm_delete;
+	struct wl_list deferred_shm_list;
 };
 
 struct wl_global {
@@ -826,6 +829,9 @@ wl_display_create(void)
 
 	wl_array_init(&display->additional_shm_formats);
 
+	wl_list_init(&display->deferred_shm_list);
+	display->deferred_shm_delete = false;
+
 	return display;
 }
 
@@ -1383,6 +1389,47 @@ wl_display_get_additional_shm_formats(struct wl_display *display)
 	return &display->additional_shm_formats;
 }
 
+/** Enable deferred shm deletion for a display
+ * \param display The display object
+ *
+ * When a client is deleted, all its resources are immediately destroyed,
+ * including any shared memory buffers.  Once all the buffers in a pool
+ * are deleted, the pool is unmapped.
+ *
+ * Some compositors do rendering asynchronously, so they may be dispatching
+ * events which lead to a client destruction while the renderer is using
+ * these buffers.  Unmapping the pools at this time will cause a segmentation
+ * fault.
+ *
+ * Enabling deferred shm deletion will cause deleted pools to be queued
+ * in a list instead of immediately deleted.  This way event dispatch
+ * can still be handled in parallel with a rendering thread without causing
+ * a crash.
+ *
+ * When it is safe to delete the shared memory pools the compositor must
+ * call wl_display_deferred_shm_delete().
+ *
+ * If used, deferred shm deletion should be enabled before
+ * wl_display_init_shm() is called.  Once enabled it can not be disabled.
+ */
+WL_EXPORT void
+wl_display_enable_deferred_shm_delete(struct wl_display *display)
+{
+	display->deferred_shm_delete = true;
+}
+
+bool
+wl_display_get_deferred_shm_delete(struct wl_display *display)
+{
+	return display->deferred_shm_delete;
+}
+
+struct wl_list *
+wl_display_get_deferred_shm_delete_list(struct wl_display *display)
+{
+	return &display->deferred_shm_list;
+}
+
 /** \cond */ /* Deprecated functions below. */
 
 uint32_t
diff --git a/src/wayland-server.h b/src/wayland-server.h
index af2f03d..9b88e4d 100644
--- a/src/wayland-server.h
+++ b/src/wayland-server.h
@@ -450,6 +450,13 @@ wl_shm_buffer_create(struct wl_client *client,
 
 void wl_log_set_handler_server(wl_log_func_t handler);
 
+void
+wl_display_enable_deferred_shm_delete(struct wl_display *display);
+
+void
+wl_display_deferred_shm_delete(struct wl_display *display);
+
+
 #ifdef  __cplusplus
 }
 #endif
diff --git a/src/wayland-shm.c b/src/wayland-shm.c
index b6b31d6..9f223aa 100644
--- a/src/wayland-shm.c
+++ b/src/wayland-shm.c
@@ -49,9 +49,11 @@ static struct sigaction wl_shm_old_sigbus_action;
 
 struct wl_shm_pool {
 	struct wl_resource *resource;
+	struct wl_display *display;
 	int refcount;
 	char *data;
 	int32_t size;
+	struct wl_list link;
 };
 
 struct wl_shm_buffer {
@@ -69,6 +71,31 @@ struct wl_shm_sigbus_data {
 	int fallback_mapping_used;
 };
 
+/** Process any pending shared memory pool deletions for a display
+ *
+ * \param buffer The display object
+ *
+ * If deferred deletion of shared memory pools is enabled this will
+ * process the list of deferred deletions, unmapping and freeing any
+ * pools that should be destroyed.
+ */
+WL_EXPORT void
+wl_display_deferred_shm_delete(struct wl_display *display)
+{
+	struct wl_list *dlist;
+	struct wl_shm_pool *pool, *next;
+
+	assert(wl_display_get_deferred_shm_delete(display));
+
+	dlist = wl_display_get_deferred_shm_delete_list(display);
+
+	wl_list_for_each_safe(pool, next, dlist, link) {
+		munmap(pool->data, pool->size);
+		wl_list_remove(&pool->link);
+		free(pool);
+	}
+}
+
 static void
 shm_pool_unref(struct wl_shm_pool *pool)
 {
@@ -76,6 +103,14 @@ shm_pool_unref(struct wl_shm_pool *pool)
 	if (pool->refcount)
 		return;
 
+	if (wl_display_get_deferred_shm_delete(pool->display)) {
+		struct wl_list *dlist;
+
+		dlist = wl_display_get_deferred_shm_delete_list(pool->display);
+		wl_list_insert(dlist, &pool->link);
+		return;
+	}
+
 	munmap(pool->data, pool->size);
 	free(pool);
 }
@@ -233,6 +268,8 @@ shm_create_pool(struct wl_client *client, struct wl_resource *resource,
 		goto err_close;
 	}
 
+	pool->display = wl_client_get_display(client);
+
 	if (size <= 0) {
 		wl_resource_post_error(resource,
 				       WL_SHM_ERROR_INVALID_STRIDE,
-- 
2.1.4



More information about the wayland-devel mailing list