libweston: mitigate race when destroying outputs

With some displays connect, disconnect, connect events can happen is a very
short amount of time. When this happens, the output global may already be
destroyed when a client tries to bind it. As a result, the client is
disconnected with a protocol error. See [1] for more details on the general
problem.

To mitigate this problem call wl_global_remove() first and call
wl_global_destroy() several seconds later. This is inspired by the
implementation for the same problem in wlroots.

[1] https://gitlab.freedesktop.org/wayland/wayland/-/issues/10

Signed-off-by: Michael Olbrich <m.olbrich@pengutronix.de>
This commit is contained in:
Michael Olbrich 2023-02-20 18:30:34 +01:00 committed by Marius Vlad
parent 3c6866088d
commit 638dee44ec
1 changed files with 65 additions and 2 deletions

View File

@ -5366,11 +5366,16 @@ bind_output(struct wl_client *client,
return; return;
} }
if (!output) {
wl_resource_set_implementation(resource, &output_interface,
NULL, NULL);
return;
}
wl_list_insert(&head->resource_list, wl_resource_get_link(resource)); wl_list_insert(&head->resource_list, wl_resource_get_link(resource));
wl_resource_set_implementation(resource, &output_interface, head, wl_resource_set_implementation(resource, &output_interface, head,
unbind_resource); unbind_resource);
assert(output);
wl_output_send_geometry(resource, wl_output_send_geometry(resource,
output->x, output->x,
output->y, output->y,
@ -5409,6 +5414,64 @@ weston_head_add_global(struct weston_head *head)
head, bind_output); head, bind_output);
} }
struct weston_destroy_global_data {
struct wl_global *global;
struct wl_event_source *event_source;
struct wl_listener destroy_listener;
};
static void
weston_destroy_global(struct weston_destroy_global_data *data)
{
wl_list_remove(&data->destroy_listener.link);
wl_global_destroy(data->global);
wl_event_source_remove(data->event_source);
free(data);
}
static void
global_compositor_destroy_handler(struct wl_listener *listener, void *_data)
{
struct weston_destroy_global_data *data =
wl_container_of(listener, data, destroy_listener);
weston_destroy_global(data);
}
static int
weston_global_handle_timer_event(void *data)
{
weston_destroy_global(data);
return 0;
}
static void
weston_global_destroy_save(struct weston_compositor *compositor,
struct wl_global *global)
{
struct weston_destroy_global_data *data;
struct wl_event_loop *loop;
if (compositor->state == WESTON_COMPOSITOR_OFFSCREEN) {
wl_global_destroy(global);
return;
}
wl_global_remove(global);
data = xzalloc(sizeof *data);
data->global = global;
loop = wl_display_get_event_loop(compositor->wl_display);
data->event_source =
wl_event_loop_add_timer(loop, weston_global_handle_timer_event,
data);
wl_event_source_timer_update(data->event_source, 5000);
data->destroy_listener.notify = global_compositor_destroy_handler;
wl_signal_add(&compositor->destroy_signal, &data->destroy_listener);
}
/** Remove the global wl_output protocol object /** Remove the global wl_output protocol object
* *
* \param head The head whose global to remove. * \param head The head whose global to remove.
@ -5421,7 +5484,7 @@ weston_head_remove_global(struct weston_head *head)
struct wl_resource *resource, *tmp; struct wl_resource *resource, *tmp;
if (head->global) if (head->global)
wl_global_destroy(head->global); weston_global_destroy_save(head->compositor, head->global);
head->global = NULL; head->global = NULL;
wl_resource_for_each_safe(resource, tmp, &head->resource_list) { wl_resource_for_each_safe(resource, tmp, &head->resource_list) {