mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-06-29 13:14:14 -04:00
jack: fix race in get_buffer_output()
When a single output port is linked to multiple input ports of the same client and the client uses multiple threads to process the input ports, get_buffer_output() is called from multiple threads concurrently and causes a race. Multiple threads will try to dequeue a buffer concurrently and set the HAVE_DATA io status, which causes the port to run out of buffers quickly and the io are to become corrupted. Use CAS to make sure only one thread dequeues and sets the io status. The other concurrent threads will spin until there is a buffer. The fast path will be that the buffer is already dequeued and then it is simply reused. Fixes #5324
This commit is contained in:
parent
d97a9bf44b
commit
2a4222a538
1 changed files with 15 additions and 3 deletions
|
|
@ -248,6 +248,7 @@ struct mix {
|
|||
struct port *peer_port;
|
||||
|
||||
struct spa_io_buffers *io[2];
|
||||
uint32_t io_busy;
|
||||
|
||||
struct spa_list queue;
|
||||
struct buffer buffers[MAX_BUFFERS];
|
||||
|
|
@ -652,6 +653,7 @@ static void init_mix(struct mix *mix, uint32_t mix_id, struct port *port, uint32
|
|||
mix->io[0] = mix->io[1] = NULL;
|
||||
mix->n_buffers = 0;
|
||||
spa_list_init(&mix->queue);
|
||||
SPA_ATOMIC_STORE(mix->io_busy, 0);
|
||||
if (mix_id == SPA_ID_INVALID) {
|
||||
port->global_mix = mix;
|
||||
if (port->n_mix > 0)
|
||||
|
|
@ -733,6 +735,7 @@ static int clear_buffers(struct client *c, struct mix *mix)
|
|||
}
|
||||
mix->n_buffers = 0;
|
||||
spa_list_init(&mix->queue);
|
||||
SPA_ATOMIC_STORE(mix->io_busy, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1724,11 +1727,11 @@ static inline void *get_buffer_output(struct port *p, uint32_t frames, uint32_t
|
|||
if (SPA_UNLIKELY((io = mix->io[cycle]) == NULL || mix->n_buffers == 0))
|
||||
return NULL;
|
||||
|
||||
if (io->status == SPA_STATUS_HAVE_DATA &&
|
||||
if (SPA_ATOMIC_LOAD(io->status) == SPA_STATUS_HAVE_DATA &&
|
||||
io->buffer_id < mix->n_buffers) {
|
||||
b = &mix->buffers[io->buffer_id];
|
||||
d = &b->datas[0];
|
||||
} else {
|
||||
} else if (SPA_ATOMIC_CAS(mix->io_busy, 0, 1)) {
|
||||
if (mix->n_buffers == 1) {
|
||||
b = &mix->buffers[0];
|
||||
} else {
|
||||
|
|
@ -1739,6 +1742,7 @@ static inline void *get_buffer_output(struct port *p, uint32_t frames, uint32_t
|
|||
if (SPA_UNLIKELY(b == NULL)) {
|
||||
pw_log_warn("port %p: out of buffers %d", p, mix->n_buffers);
|
||||
io->buffer_id = SPA_ID_INVALID;
|
||||
SPA_ATOMIC_STORE(mix->io_busy, 0);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
|
@ -1748,7 +1752,15 @@ static inline void *get_buffer_output(struct port *p, uint32_t frames, uint32_t
|
|||
d->chunk->stride = stride;
|
||||
|
||||
io->buffer_id = b->id;
|
||||
io->status = SPA_STATUS_HAVE_DATA;
|
||||
SPA_ATOMIC_STORE(io->status, SPA_STATUS_HAVE_DATA);
|
||||
SPA_ATOMIC_STORE(mix->io_busy, 0);
|
||||
} else {
|
||||
while (SPA_ATOMIC_LOAD(io->status) != SPA_STATUS_HAVE_DATA)
|
||||
;
|
||||
if (SPA_UNLIKELY(io->buffer_id >= mix->n_buffers))
|
||||
return NULL;
|
||||
b = &mix->buffers[io->buffer_id];
|
||||
d = &b->datas[0];
|
||||
}
|
||||
ptr = d->data;
|
||||
if (buf)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue