From 9da44813000351f4000ed34774adc2bc8e479af0 Mon Sep 17 00:00:00 2001 From: Loic Coenen Date: Sun, 10 May 2026 10:50:03 +0000 Subject: [PATCH] fix: defer freeing old channel array until RT thread sees new pointer Co-authored-by: aider (deepseek/deepseek-reasoner) --- src/looper.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/looper.c b/src/looper.c index 4ba4f58..1f0e226 100644 --- a/src/looper.c +++ b/src/looper.c @@ -30,6 +30,10 @@ spsc_queue_t cmd_queue; static int pending_unregister_idx = -1; static int pending_unregister_cycle = 0; +/* Deferred free of old channel array (must not free while RT thread may hold pointer) */ +static struct channel_t *pending_old = NULL; +static int pending_old_cycle = 0; + /* Helper: grow the channel array so that index idx is valid */ static int ensure_capacity(jack_client_t *client, int idx) { (void)client; @@ -45,10 +49,12 @@ static int ensure_capacity(jack_client_t *client, int idx) { /* copy existing channels */ if (cur_cap > 0) memcpy(new_arr, atomic_load(&channels), cur_cap * sizeof(struct channel_t)); - /* atomically publish new array */ + /* atomically publish new array, defer free of old */ struct channel_t *old = atomic_exchange(&channels, new_arr); atomic_store(&channel_capacity, new_cap); - free(old); + /* schedule old pointer for later deallocation (after RT cycle) */ + pending_old = old; + pending_old_cycle = atomic_load(&global_rt_cycles); return 0; } @@ -397,4 +403,13 @@ void looper_process_commands(jack_client_t *client) { pending_unregister_idx = -1; } } + + /* Deferred free of old channel array – wait until RT thread has seen new pointer */ + if (pending_old != NULL) { + int current_cycle = atomic_load(&global_rt_cycles); + if (current_cycle - pending_old_cycle >= 1) { + free(pending_old); + pending_old = NULL; + } + } }