wayland-server: Add wl_client_add_destroy_late_listener

A late-destroy listener for a client is called after all the client's
resources have been destroyed and the destroy callbacks emitted. This
lives in parallel to the existing client destroy listener, called
immediately before the client's objects get destroyed.

Signed-off-by: Daniel Stone <daniels@collabora.com>
Fixes: wayland/wayland#207
This commit is contained in:
Daniel Stone 2022-07-21 11:07:04 +01:00
parent e886b456ab
commit 51d788de5b
3 changed files with 74 additions and 0 deletions

View File

@ -330,6 +330,14 @@ struct wl_listener *
wl_client_get_destroy_listener(struct wl_client *client,
wl_notify_func_t notify);
void
wl_client_add_destroy_late_listener(struct wl_client *client,
struct wl_listener *listener);
struct wl_listener *
wl_client_get_destroy_late_listener(struct wl_client *client,
wl_notify_func_t notify);
struct wl_resource *
wl_client_get_object(struct wl_client *client, uint32_t id);

View File

@ -79,6 +79,7 @@ struct wl_client {
struct wl_list link;
struct wl_map objects;
struct wl_priv_signal destroy_signal;
struct wl_priv_signal destroy_late_signal;
pid_t pid;
uid_t uid;
gid_t gid;
@ -547,6 +548,7 @@ wl_client_create(struct wl_display *display, int fd)
goto err_map;
wl_priv_signal_init(&client->destroy_signal);
wl_priv_signal_init(&client->destroy_late_signal);
if (bind_display(client, display) < 0)
goto err_map;
@ -864,6 +866,17 @@ wl_resource_get_class(struct wl_resource *resource)
return resource->object.interface->name;
}
/**
* Add a listener to be called at the beginning of wl_client destruction
*
* The listener provided will be called when wl_client destroy has begun,
* before any of that client's resources have been destroyed.
*
* There is no requirement to remove the link of the wl_listener when the
* signal is emitted.
*
* \memberof wl_client
*/
WL_EXPORT void
wl_client_add_destroy_listener(struct wl_client *client,
struct wl_listener *listener)
@ -878,6 +891,32 @@ wl_client_get_destroy_listener(struct wl_client *client,
return wl_priv_signal_get(&client->destroy_signal, notify);
}
/**
* Add a listener to be called at the end of wl_client destruction
*
* The listener provided will be called when wl_client destroy is nearly
* complete, after all of that client's resources have been destroyed.
*
* There is no requirement to remove the link of the wl_listener when the
* signal is emitted.
*
* \memberof wl_client
* \since 1.22.0
*/
WL_EXPORT void
wl_client_add_destroy_late_listener(struct wl_client *client,
struct wl_listener *listener)
{
wl_priv_signal_add(&client->destroy_late_signal, listener);
}
WL_EXPORT struct wl_listener *
wl_client_get_destroy_late_listener(struct wl_client *client,
wl_notify_func_t notify)
{
return wl_priv_signal_get(&client->destroy_late_signal, notify);
}
WL_EXPORT void
wl_client_destroy(struct wl_client *client)
{
@ -890,6 +929,9 @@ wl_client_destroy(struct wl_client *client)
wl_map_release(&client->objects);
wl_event_source_remove(client->source);
close(wl_connection_destroy(client->connection));
wl_priv_signal_final_emit(&client->destroy_late_signal, client);
wl_list_remove(&client->link);
wl_list_remove(&client->resource_created_signal.listener_list);
free(client);

View File

@ -41,6 +41,8 @@
struct client_destroy_listener {
struct wl_listener listener;
bool done;
struct wl_listener late_listener;
bool late_done;
};
static void
@ -49,9 +51,20 @@ client_destroy_notify(struct wl_listener *l, void *data)
struct client_destroy_listener *listener =
wl_container_of(l, listener, listener);
assert(!listener->late_done);
listener->done = true;
}
static void
client_late_destroy_notify(struct wl_listener *l, void *data)
{
struct client_destroy_listener *listener =
wl_container_of(l, listener, late_listener);
assert(listener->done);
listener->late_done = true;
}
TEST(client_destroy_listener)
{
struct wl_display *display;
@ -67,21 +80,32 @@ TEST(client_destroy_listener)
a.listener.notify = client_destroy_notify;
a.done = false;
a.late_listener.notify = client_late_destroy_notify;
a.late_done = false;
wl_client_add_destroy_listener(client, &a.listener);
wl_client_add_destroy_late_listener(client, &a.late_listener);
assert(wl_client_get_destroy_listener(client, client_destroy_notify) ==
&a.listener);
assert(wl_client_get_destroy_late_listener(client, client_late_destroy_notify) ==
&a.late_listener);
b.listener.notify = client_destroy_notify;
b.done = false;
b.late_listener.notify = client_late_destroy_notify;
b.late_done = false;
wl_client_add_destroy_listener(client, &b.listener);
wl_client_add_destroy_late_listener(client, &b.late_listener);
wl_list_remove(&a.listener.link);
wl_list_remove(&a.late_listener.link);
wl_client_destroy(client);
assert(!a.done);
assert(!a.late_done);
assert(b.done);
assert(b.late_done);
close(s[0]);
close(s[1]);