]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
res_stasis/resource_bridges: Split bridge playback control and wrapper cleanup
authorPeter Krall <pekkaar@gmail.com>
Fri, 17 Apr 2026 12:35:31 +0000 (14:35 +0200)
committergithub-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Thu, 7 May 2026 18:55:33 +0000 (18:55 +0000)
Modified the bridge playback teardown so the worker thread removes only the
playback control, while the after-bridge callback removes the playback
wrapper once the announcer has actually left the bridge.

This avoids a stale window where a new playback request could create a
replacement announcer before the old announcer had fully exited the holding
bridge.

Also replaced the flexible trailing bridge_id storage in the shared worker
thread data with an optional bridge_id pointer, since recording paths use the
same structure without a bridge id.

Fixes: #1861
include/asterisk/stasis_app.h
res/ari/resource_bridges.c
res/res_stasis.c

index 8a41f555d0631f6b69c0e81c7eaa5d6033a919d6..44f811cf7f9c8363c92339e5c8c5491baf7c3abf 100644 (file)
@@ -795,6 +795,15 @@ int stasis_app_bridge_playback_channel_add(struct ast_bridge *bridge,
        struct ast_channel *chan,
        struct stasis_app_control *control);
 
+/*!
+ * \brief Remove a bridge playback channel's control from the app controls list.
+ *
+ * \param bridge_id The unique ID of the bridge the playback channel is in.
+ * \param control The app control structure for the playback channel
+ */
+void stasis_app_bridge_playback_channel_control_remove(const char *bridge_id,
+       struct stasis_app_control *control);
+
 /*!
  * \brief remove channel from list of ARI playback channels for bridges.
  *
index 0baaa44b1fbee309d789870049159117fe725c32..712f6a5ac1f74c28d8c5f4421d7dac0564e9b590 100644 (file)
@@ -282,7 +282,7 @@ struct bridge_channel_control_thread_data {
        struct ast_channel *bridge_channel;
        struct stasis_app_control *control;
        struct stasis_forward *forward;
-       char bridge_id[0];
+       char *bridge_id;
 };
 
 static void *bridge_channel_control_thread(void *data)
@@ -292,7 +292,7 @@ static void *bridge_channel_control_thread(void *data)
        struct stasis_app_control *control = thread_data->control;
        struct stasis_forward *forward = thread_data->forward;
        ast_callid callid = ast_channel_callid(bridge_channel);
-       char *bridge_id = ast_strdupa(thread_data->bridge_id);
+       char *bridge_id = thread_data->bridge_id;
 
        if (callid) {
                ast_callid_threadassoc_add(callid);
@@ -304,7 +304,10 @@ static void *bridge_channel_control_thread(void *data)
        stasis_app_control_execute_until_exhausted(bridge_channel, control);
        stasis_app_control_flush_queue(control);
 
-       stasis_app_bridge_playback_channel_remove(bridge_id, control);
+       if (bridge_id) {
+               stasis_app_bridge_playback_channel_control_remove(bridge_id, control);
+               ast_free(bridge_id);
+       }
        stasis_forward_cancel(forward);
        ao2_cleanup(control);
        ast_hangup(bridge_channel);
@@ -532,7 +535,7 @@ static void ari_bridges_play_new(const char **args_media,
        ast_bridge_queue_everyone_else(bridge, NULL, &prog);
 
        /* Give play_channel and control reference to the thread data */
-       thread_data = ast_malloc(sizeof(*thread_data) + strlen(bridge->uniqueid) + 1);
+       thread_data = ast_calloc(1, sizeof(*thread_data));
        if (!thread_data) {
                stasis_app_bridge_playback_channel_remove((char *)bridge->uniqueid, control);
                ast_ari_response_alloc_failed(response);
@@ -542,11 +545,17 @@ static void ari_bridges_play_new(const char **args_media,
        thread_data->bridge_channel = play_channel;
        thread_data->control = control;
        thread_data->forward = channel_forward;
-       /* Safe */
-       strcpy(thread_data->bridge_id, bridge->uniqueid);
+       thread_data->bridge_id = ast_strdup(bridge->uniqueid);
+       if (!thread_data->bridge_id) {
+               stasis_app_bridge_playback_channel_remove((char *)bridge->uniqueid, control);
+               ast_ari_response_alloc_failed(response);
+               ast_free(thread_data);
+               return;
+       }
 
        if (ast_pthread_create_detached(&threadid, NULL, bridge_channel_control_thread, thread_data)) {
                stasis_app_bridge_playback_channel_remove((char *)bridge->uniqueid, control);
+               ast_free(thread_data->bridge_id);
                ast_ari_response_alloc_failed(response);
                ast_free(thread_data);
                return;
@@ -653,7 +662,7 @@ static void ari_bridges_handle_play(
                 * in which case we'll revert to ari_bridges_play_new.
                 */
                if (ari_bridges_play_found(args_media, args_media_count, args_lang,
-                               args_offset_ms, args_skipms, args_playback_id, response,bridge,
+                               args_offset_ms, args_skipms, args_playback_id, response, bridge,
                                play_channel) == PLAY_FOUND_CHANNEL_UNAVAILABLE) {
                        continue;
                }
index cc17a21b4fcb53477a19ebf2324a69529626b98e..6912f3a9c5ea449764b27395947c83511076a772 100644 (file)
@@ -714,6 +714,13 @@ static void remove_bridge_playback(char *bridge_id)
        ast_free(bridge_id);
 }
 
+void stasis_app_bridge_playback_channel_control_remove(const char *bridge_id,
+       struct stasis_app_control *control)
+{
+       ast_assert(!ast_strlen_zero(bridge_id));
+       ao2_unlink(app_controls, control);
+}
+
 static void playback_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data)
 {
        char *bridge_id = data;
@@ -777,7 +784,7 @@ void stasis_app_bridge_playback_channel_remove(char *bridge_id,
                 * called or is in progress. No need to unlink the control here since that has
                 * been done or is about to be done in the after bridge callback
                 */
-               ao2_unlink(app_controls, control);
+               stasis_app_bridge_playback_channel_control_remove(bridge_id, control);
                ao2_ref(wrapper, -1);
        }
 }