From 05ae8a24def08081a00089084160bf0545c1b52f Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 15 Sep 2020 20:13:32 +0200 Subject: [PATCH] loop: make safe version of befor and after signal Use a safer version of the before and after hooks. First call all before hooks and save them in reverse order in a save list. Then call the after event for the ones remaining in the save list and move them back to the hook list. This makes it possible to remove the hooks from one the callbacks or even from other threads with the right locks. Found as a solution to the following problem as observed in vlc: main thread thread_loop pw_thread_loop_lock() before hook: lock suspend thread pw_context_destroy() - removes before hook to flush clients pw_thread_loop_unlock() before hook: lock acquired, resume before hook: flush client hook executed *crash* pw_thread_loop_stop() pw_thread_loop_destroy() Any of the safer cursor methods (like spa_hook_list_call()) would also work but are more expensive and don't reverse the before/after order. --- spa/plugins/support/loop.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/spa/plugins/support/loop.c b/spa/plugins/support/loop.c index f9c166531..02c5675a6 100644 --- a/spa/plugins/support/loop.c +++ b/spa/plugins/support/loop.c @@ -278,13 +278,24 @@ static int loop_iterate(void *object, int timeout) struct impl *impl = object; struct spa_loop *loop = &impl->loop; struct spa_poll_event ep[32]; + struct spa_list save; + struct spa_hook *hook; int i, nfds; - spa_loop_control_hook_before(&impl->hooks_list); + spa_list_init(&save); + spa_list_consume(hook, &impl->hooks_list.list, link) { + spa_list_remove(&hook->link); + spa_list_prepend(&save, &hook->link); + spa_callbacks_call(&hook->cb, struct spa_loop_control_hooks, before, 0); + } nfds = spa_system_pollfd_wait(impl->system, impl->poll_fd, ep, SPA_N_ELEMENTS(ep), timeout); - spa_loop_control_hook_after(&impl->hooks_list); + spa_list_consume(hook, &save, link) { + spa_list_remove(&hook->link); + spa_list_append(&impl->hooks_list.list, &hook->link); + spa_callbacks_call(&hook->cb, struct spa_loop_control_hooks, after, 0); + } if (SPA_UNLIKELY(nfds < 0)) return nfds;