From 78f52a7073e8c9ca40811b2e9206e3ea023608c8 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Mon, 26 Jul 2021 11:39:48 +0200 Subject: [PATCH] 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 --- spa/plugins/support/loop.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/spa/plugins/support/loop.c b/spa/plugins/support/loop.c index 00db8f847..88e29dfab 100644 --- a/spa/plugins/support/loop.c +++ b/spa/plugins/support/loop.c @@ -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);