loop: avoid corruption of ringbuffer

The ringbuffer can't be written to from multiple threads.

When both the main loop and data thread do _invoke, they both write to
the ringbuffer and cause it to be corrupted because the ringbuffer is
not multi-writer safe.

Doing invoke from the thread itself is usually done to flush things out
so we really only need to flush the ringbuffer and call the callback.

See #1451
This commit is contained in:
Wim Taymans 2021-07-26 11:39:48 +02:00
parent d1905716e4
commit 78f52a7073

View file

@ -154,6 +154,20 @@ static void flush_items(struct impl *impl)
impl->flushing = false;
}
static int
loop_invoke_inthread(struct impl *impl,
spa_invoke_func_t func,
uint32_t seq,
const void *data,
size_t size,
bool block,
void *user_data)
{
if (!impl->flushing)
flush_items(impl);
return func ? func(&impl->loop, true, seq, data, size, user_data) : 0;
}
static int
loop_invoke(void *object,
spa_invoke_func_t func,
@ -164,12 +178,14 @@ loop_invoke(void *object,
void *user_data)
{
struct impl *impl = object;
bool in_thread = pthread_equal(impl->thread, pthread_self());
struct invoke_item *item;
int res;
int32_t filled;
uint32_t avail, idx, offset, l0;
if (pthread_equal(impl->thread, pthread_self()))
return loop_invoke_inthread(impl, func, seq, data, size, block, user_data);
filled = spa_ringbuffer_get_write_index(&impl->buffer, &idx);
if (filled < 0 || filled > DATAS_SIZE) {
spa_log_warn(impl->log, NAME " %p: queue xrun %d", impl, filled);
@ -190,7 +206,7 @@ loop_invoke(void *object,
item->func = func;
item->seq = seq;
item->size = size;
item->block = block && !in_thread;
item->block = block;
item->user_data = user_data;
item->item_size = SPA_ROUND_UP_N(sizeof(struct invoke_item) + size, 8);
@ -220,14 +236,9 @@ loop_invoke(void *object,
spa_ringbuffer_write_update(&impl->buffer, idx + item->item_size);
if (in_thread) {
if (!impl->flushing)
flush_items(impl);
} else {
loop_signal_event(impl, impl->wakeup);
}
loop_signal_event(impl, impl->wakeup);
if (block && !in_thread) {
if (block) {
uint64_t count = 1;
spa_loop_control_hook_before(&impl->hooks_list);