From 39852f1146941fa93daf767b4ecfb41b2d11aaef Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 20 Jul 2019 23:36:29 +0300 Subject: server: add wl_global_remove This commit adds a new wl_global_remove function that just sends a global remove event without destroying it. See [1] for details. Removing a global is racy, because clients have no way to acknowledge they received the removal event. It's possible to mitigate the issue by sending the removal event, waiting a little and then destructing the global for real. The "wait a little" part is compositor policy. [1]: https://gitlab.freedesktop.org/wayland/wayland/issues/10 Signed-off-by: Simon Ser --- src/wayland-server-core.h | 3 +++ src/wayland-server.c | 42 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/wayland-server-core.h b/src/wayland-server-core.h index 68d7ddb..fd4fc3e 100644 --- a/src/wayland-server-core.h +++ b/src/wayland-server-core.h @@ -246,6 +246,9 @@ wl_global_create(struct wl_display *display, int version, void *data, wl_global_bind_func_t bind); +void +wl_global_remove(struct wl_global *global); + void wl_global_destroy(struct wl_global *global); diff --git a/src/wayland-server.c b/src/wayland-server.c index 8c537bb..c4c52ed 100644 --- a/src/wayland-server.c +++ b/src/wayland-server.c @@ -25,6 +25,7 @@ #define _GNU_SOURCE +#include #include #include #include @@ -112,6 +113,7 @@ struct wl_global { void *data; wl_global_bind_func_t bind; struct wl_list link; + bool removed; }; struct wl_resource { @@ -992,7 +994,7 @@ display_get_registry(struct wl_client *client, ®istry_resource->link); wl_list_for_each(global, &display->global_list, link) - if (wl_global_is_visible(client, global)) + if (wl_global_is_visible(client, global) && !global->removed) wl_resource_post_event(registry_resource, WL_REGISTRY_GLOBAL, global->name, @@ -1208,6 +1210,7 @@ wl_global_create(struct wl_display *display, global->version = version; global->data = data; global->bind = bind; + global->removed = false; wl_list_insert(display->global_list.prev, &global->link); wl_list_for_each(resource, &display->registry_resource_list, link) @@ -1220,15 +1223,50 @@ wl_global_create(struct wl_display *display, return global; } +/** Remove the global + * + * \param global The Wayland global. + * + * Broadcast a global remove event to all clients without destroying the + * global. This function can only be called once per global. + * + * wl_global_destroy() removes the global and immediately destroys it. On + * the other end, this function only removes the global, allowing clients + * that have not yet received the global remove event to continue to bind to + * it. + * + * This can be used by compositors to mitigate clients being disconnected + * because a global has been added and removed too quickly. Compositors can call + * wl_global_remove(), then wait an implementation-defined amount of time, then + * call wl_global_destroy(). Note that the destruction of a global is still + * racy, since clients have no way to acknowledge that they received the remove + * event. + * + * \since 1.17.90 + */ WL_EXPORT void -wl_global_destroy(struct wl_global *global) +wl_global_remove(struct wl_global *global) { struct wl_display *display = global->display; struct wl_resource *resource; + if (global->removed) + wl_abort("wl_global_remove: called twice on the same " + "global '%s@%"PRIu32"'", global->interface->name, + global->name); + wl_list_for_each(resource, &display->registry_resource_list, link) wl_resource_post_event(resource, WL_REGISTRY_GLOBAL_REMOVE, global->name); + + global->removed = true; +} + +WL_EXPORT void +wl_global_destroy(struct wl_global *global) +{ + if (!global->removed) + wl_global_remove(global); wl_list_remove(&global->link); free(global); } -- cgit v1.2.3-70-g09d2