From: Peter Krall Date: Fri, 17 Apr 2026 12:35:31 +0000 (+0200) Subject: res_stasis/resource_bridges: Split bridge playback control and wrapper cleanup X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=176ecdad3cc97437d552d180936faf54c8f2b432;p=thirdparty%2Fasterisk.git res_stasis/resource_bridges: Split bridge playback control and wrapper cleanup 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 --- diff --git a/include/asterisk/stasis_app.h b/include/asterisk/stasis_app.h index 8a41f555d0..44f811cf7f 100644 --- a/include/asterisk/stasis_app.h +++ b/include/asterisk/stasis_app.h @@ -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. * diff --git a/res/ari/resource_bridges.c b/res/ari/resource_bridges.c index 0baaa44b1f..712f6a5ac1 100644 --- a/res/ari/resource_bridges.c +++ b/res/ari/resource_bridges.c @@ -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; } diff --git a/res/res_stasis.c b/res/res_stasis.c index cc17a21b4f..6912f3a9c5 100644 --- a/res/res_stasis.c +++ b/res/res_stasis.c @@ -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); } }