loop: fix race in shutdown

Make it possible to call loop_queue_destroy() from both the TSS destroy
and impl_clear() without races. We make sure that only one can remove
the queue from the queue list and cleanup. We also store the IN_TSS flag
in the flags so that we can see them before the queue is added to the
queue list. Only free the IN_TSS queue when the TSS destroy is called.

See #4356
This commit is contained in:
Wim Taymans 2024-10-21 16:45:17 +02:00
parent 6549e313ef
commit c4fece74a5

View file

@ -86,13 +86,13 @@ struct queue {
#define QUEUE_FLAG_NONE (0) #define QUEUE_FLAG_NONE (0)
#define QUEUE_FLAG_ACK_FD (1<<0) #define QUEUE_FLAG_ACK_FD (1<<0)
#define QUEUE_FLAG_IN_TSS (1<<1)
uint32_t flags; uint32_t flags;
struct queue *overflow; struct queue *overflow;
int ack_fd; int ack_fd;
struct spa_ratelimit rate_limit; struct spa_ratelimit rate_limit;
bool destroyed; bool destroyed;
bool in_tss;
struct spa_ringbuffer buffer; struct spa_ringbuffer buffer;
uint8_t *buffer_data; uint8_t *buffer_data;
@ -413,9 +413,7 @@ static void loop_queue_destroy(void *data)
struct queue *queue = data; struct queue *queue = data;
struct impl *impl = queue->impl; struct impl *impl = queue->impl;
if (!queue->destroyed) { if (SPA_ATOMIC_CAS(queue->destroyed, false, true)) {
queue->destroyed = true;
pthread_mutex_lock(&impl->queue_lock); pthread_mutex_lock(&impl->queue_lock);
spa_list_remove(&queue->link); spa_list_remove(&queue->link);
pthread_mutex_unlock(&impl->queue_lock); pthread_mutex_unlock(&impl->queue_lock);
@ -426,7 +424,7 @@ static void loop_queue_destroy(void *data)
if (queue->flags & QUEUE_FLAG_ACK_FD) if (queue->flags & QUEUE_FLAG_ACK_FD)
spa_system_close(impl->system, queue->ack_fd); spa_system_close(impl->system, queue->ack_fd);
} }
if (!queue->in_tss) if (!SPA_FLAG_IS_SET(queue->flags, QUEUE_FLAG_IN_TSS))
free(queue); free(queue);
} }
@ -434,8 +432,8 @@ static void loop_queue_destroy_tss(void *data)
{ {
struct queue *queue = data; struct queue *queue = data;
if (queue) { if (queue) {
queue->in_tss = false;
loop_queue_destroy(queue); loop_queue_destroy(queue);
free(queue);
} }
} }
@ -447,10 +445,9 @@ static int loop_invoke(void *object, spa_invoke_func_t func, uint32_t seq,
local_queue = tss_get(impl->queue_tss_id); local_queue = tss_get(impl->queue_tss_id);
if (local_queue == NULL) { if (local_queue == NULL) {
local_queue = loop_create_queue(impl, QUEUE_FLAG_ACK_FD); local_queue = loop_create_queue(impl, QUEUE_FLAG_ACK_FD | QUEUE_FLAG_IN_TSS);
if (local_queue == NULL) if (local_queue == NULL)
return -errno; return -errno;
local_queue->in_tss = true;
tss_set(impl->queue_tss_id, local_queue); tss_set(impl->queue_tss_id, local_queue);
} }
return loop_queue_invoke(local_queue, func, seq, data, size, block, user_data); return loop_queue_invoke(local_queue, func, seq, data, size, block, user_data);