fix: defer port unregistration to avoid race condition in channel removal
Co-authored-by: aider (deepseek/deepseek-reasoner) <aider@aider.chat>
This commit is contained in:
@@ -19,7 +19,7 @@ void channel_add(jack_client_t *client, int idx)
|
|||||||
if (!channels[idx].audio_in || !channels[idx].audio_out) {
|
if (!channels[idx].audio_in || !channels[idx].audio_out) {
|
||||||
fprintf(stderr, "Failed to register ports for channel %d\n", next_channel_id);
|
fprintf(stderr, "Failed to register ports for channel %d\n", next_channel_id);
|
||||||
/* Do NOT mark channel active – process loop will skip it */
|
/* Do NOT mark channel active – process loop will skip it */
|
||||||
channels[idx].active = 0;
|
atomic_store(&channels[idx].active, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,8 +36,7 @@ void channel_add(jack_client_t *client, int idx)
|
|||||||
|
|
||||||
void channel_remove(jack_client_t *client, int idx)
|
void channel_remove(jack_client_t *client, int idx)
|
||||||
{
|
{
|
||||||
jack_port_unregister(client, channels[idx].audio_in);
|
(void)client;
|
||||||
jack_port_unregister(client, channels[idx].audio_out);
|
|
||||||
atomic_store(&channels[idx].active, 0);
|
atomic_store(&channels[idx].active, 0);
|
||||||
channel_count--;
|
channel_count--;
|
||||||
}
|
}
|
||||||
|
|||||||
17
src/looper.c
17
src/looper.c
@@ -19,6 +19,9 @@ jack_port_t *midi_control_port = NULL;
|
|||||||
jack_port_t *midi_clock_port = NULL;
|
jack_port_t *midi_clock_port = NULL;
|
||||||
atomic_int control_key_active = 0;
|
atomic_int control_key_active = 0;
|
||||||
|
|
||||||
|
/* Deferred removal index (1 second grace) */
|
||||||
|
static int pending_unregister_idx = -1;
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
* process callback
|
* process callback
|
||||||
* ---------------------------------------------------------------- */
|
* ---------------------------------------------------------------- */
|
||||||
@@ -198,6 +201,18 @@ int looper_init(jack_client_t *client)
|
|||||||
* ---------------------------------------------------------------- */
|
* ---------------------------------------------------------------- */
|
||||||
void looper_process_commands(jack_client_t *client)
|
void looper_process_commands(jack_client_t *client)
|
||||||
{
|
{
|
||||||
|
/* Unregister any ports that were marked for deferred removal.
|
||||||
|
By now the real‑time thread has had at least one full cycle
|
||||||
|
to see the `active = 0` store. */
|
||||||
|
if (pending_unregister_idx != -1) {
|
||||||
|
int idx = pending_unregister_idx;
|
||||||
|
if (channels[idx].audio_in)
|
||||||
|
jack_port_unregister(client, channels[idx].audio_in);
|
||||||
|
if (channels[idx].audio_out)
|
||||||
|
jack_port_unregister(client, channels[idx].audio_out);
|
||||||
|
pending_unregister_idx = -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (atomic_exchange(&cmd_add, 0)) {
|
if (atomic_exchange(&cmd_add, 0)) {
|
||||||
int idx;
|
int idx;
|
||||||
for (idx = 0; idx < MAX_CHANNELS; idx++)
|
for (idx = 0; idx < MAX_CHANNELS; idx++)
|
||||||
@@ -212,7 +227,9 @@ void looper_process_commands(jack_client_t *client)
|
|||||||
for (int idx = 1; idx < MAX_CHANNELS; idx++)
|
for (int idx = 1; idx < MAX_CHANNELS; idx++)
|
||||||
if (channels[idx].active) remove_idx = idx;
|
if (channels[idx].active) remove_idx = idx;
|
||||||
if (remove_idx != -1) {
|
if (remove_idx != -1) {
|
||||||
|
/* Mark inactive now; ports will be unregistered next round */
|
||||||
channel_remove(client, remove_idx);
|
channel_remove(client, remove_idx);
|
||||||
|
pending_unregister_idx = remove_idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user