pulse-server: pending-sample: handle client disconnection correctly

Previously, a client disconnecting while a sample was playing could
lead to issues. For example, if a client disconnected before the
"ready" signal of the sample-play arrives, `operation_new_cb()`
would be called and that would try to use the client's pw_manager,
however, that has previously been destroyed in `client_disconnect()`.

If the client disconnected after the "ready" signal but before the reply
has been sent, then `sample_play_ready_reply()` would never be called
since operations are completed via the client's pw_manager which
would already be destroyed at that point.

Fix this by installing a listener on the client, and properly
cancelling the operation and making sure that the pending_sample
is correctly destroyed.
This commit is contained in:
Barnabás Pőcze 2023-05-07 16:19:06 +02:00 committed by Wim Taymans
parent bd510bf602
commit 2ba9881b4d
2 changed files with 18 additions and 0 deletions

View file

@ -88,6 +88,21 @@ static const struct sample_play_events sample_play_events = {
.done = on_sample_play_done, .done = on_sample_play_done,
}; };
static void on_client_disconnect(void *data)
{
struct pending_sample *ps = data;
ps->replied = true;
operation_free_by_tag(ps->client, ps->tag);
schedule_maybe_finish(ps);
}
static const struct client_events client_events = {
VERSION_CLIENT_EVENTS,
.disconnect = on_client_disconnect,
};
int pending_sample_new(struct client *client, struct sample *sample, struct pw_properties *props, uint32_t tag) int pending_sample_new(struct client *client, struct sample *sample, struct pw_properties *props, uint32_t tag)
{ {
struct pending_sample *ps; struct pending_sample *ps;
@ -100,6 +115,7 @@ int pending_sample_new(struct client *client, struct sample *sample, struct pw_p
ps->play = p; ps->play = p;
ps->tag = tag; ps->tag = tag;
sample_play_add_listener(p, &ps->listener, &sample_play_events, ps); sample_play_add_listener(p, &ps->listener, &sample_play_events, ps);
client_add_listener(client, &ps->client_listener, &client_events, ps);
spa_list_append(&client->pending_samples, &ps->link); spa_list_append(&client->pending_samples, &ps->link);
client->ref++; client->ref++;
@ -113,6 +129,7 @@ void pending_sample_free(struct pending_sample *ps)
spa_list_remove(&ps->link); spa_list_remove(&ps->link);
spa_hook_remove(&ps->listener); spa_hook_remove(&ps->listener);
spa_hook_remove(&ps->client_listener);
pw_work_queue_cancel(impl->work_queue, ps, SPA_ID_INVALID); pw_work_queue_cancel(impl->work_queue, ps, SPA_ID_INVALID);
operation_free_by_tag(client, ps->tag); operation_free_by_tag(client, ps->tag);

View file

@ -20,6 +20,7 @@ struct pending_sample {
struct client *client; struct client *client;
struct sample_play *play; struct sample_play *play;
struct spa_hook listener; struct spa_hook listener;
struct spa_hook client_listener;
uint32_t tag; uint32_t tag;
unsigned replied:1; unsigned replied:1;
unsigned done:1; unsigned done:1;