Audio_event_iface_listen blocks indefinitely after audio_pipeline_set_listener is called during dynamic pipeline reconfiguration (AUD-7020)
ESP-ADF Issue Report: audio_event_iface_listen blocks indefinitely after audio_pipeline_set_listener is called during dynamic pipeline reconfiguration
Environment
- ESP-ADF version: [Your version, e.g., v2.6]
- ESP-IDF version: v5.4
- Development Kit: [Your board, e.g., ESP32-S3 custom board]
- Operating System: Linux
Problem Description
When dynamically reconfiguring an audio pipeline (unlink → relink → set_listener), a task blocked on audio_event_iface_listen() with portMAX_DELAY will never receive events again, even after audio_pipeline_set_listener() is called.
Expected Behavior
After calling audio_pipeline_set_listener(), a task waiting on audio_event_iface_listen() should be able to receive new events from the reconfigured pipeline.
Actual Behavior
The task remains blocked indefinitely and never receives any events, including events sent via audio_event_iface_sendout().
Root Cause Analysis
After examining the source code in audio_event_iface.c, I found the following issue:
1. audio_event_iface_listen() waits on a queue_set
esp_err_t audio_event_iface_read(audio_event_iface_handle_t evt, audio_event_iface_msg_t *msg, TickType_t wait_time)
{
if (evt->queue_set) {
QueueSetMemberHandle_t active_queue;
active_queue = xQueueSelectFromSet(evt->queue_set, wait_time); // Blocks here
// ...
}
return ESP_FAIL;
}
2. audio_event_iface_update_listener() destroys and recreates queue_set
This function is called internally by audio_event_iface_set_listener():
static esp_err_t audio_event_iface_update_listener(audio_event_iface_handle_t listen)
{
// ...
if (listen->queue_set) {
vQueueDelete(listen->queue_set); // Old queue_set is deleted!
listen->queue_set = NULL;
}
// ...
listen->queue_set = xQueueCreateSet(queue_size); // New queue_set is created
// ...
}
3. The Problem
When audio_pipeline_set_listener() is called:
- The task is blocked on
xQueueSelectFromSet(OLD_queue_set, portMAX_DELAY) audio_event_iface_update_listener()deletes the OLDqueue_setand creates a NEW one- The task is still waiting on the deleted
queue_set - The task will never wake up because the
queue_setit's waiting on no longer exists
Reproduction Steps
// Task waiting for events
static void event_task(void *pvParameters) {
audio_event_iface_handle_t evt = (audio_event_iface_handle_t)pvParameters;
while (1) {
audio_event_iface_msg_t msg;
// This will block indefinitely after pipeline reconfiguration
esp_err_t ret = audio_event_iface_listen(evt, &msg, portMAX_DELAY);
if (ret == ESP_OK) {
// Process event
}
}
}
// In another task or function:
void reconfigure_pipeline() {
// Step 1: Unlink pipeline (this calls audio_pipeline_remove_listener internally)
audio_pipeline_unlink(pipeline);
// Step 2: Relink with different elements
audio_pipeline_link(pipeline, new_link_tags, 4);
// Step 3: Set listener again
audio_pipeline_set_listener(pipeline, evt); // This triggers queue_set recreation
// Step 4: Send an event
audio_event_iface_sendout(evt, &msg); // This event will NEVER be received!
// Step 5: Run the pipeline
audio_pipeline_run(pipeline); // Pipeline runs, but events are not received
}
Workaround
Use a finite timeout instead of portMAX_DELAY:
// This works because the task will timeout and retry with the new queue_set
esp_err_t ret = audio_event_iface_listen(evt, &msg, pdMS_TO_TICKS(500));
Suggested Fix
Option 1: Before deleting the old queue_set, send a wake-up signal to any waiting tasks.
Option 2: Add a mechanism to notify listeners before reconfiguration, allowing them to safely exit the wait.
Option 3: Document this limitation clearly in the API documentation, recommending users to use finite timeouts when dynamic pipeline reconfiguration is expected.
Additional Context
This issue was discovered when implementing a music player that dynamically switches between different audio sources (e.g., embedded flash tones and SD card music). The pipeline needs to be unlinked and relinked to change the source element, which triggers this bug.
Labels suggestion: bug, audio_pipeline, audio_event_iface