summaryrefslogtreecommitdiff
path: root/res/stasis
diff options
context:
space:
mode:
authorScott Griepentrog <sgriepentrog@digium.com>2015-01-29 23:03:14 +0000
committerScott Griepentrog <sgriepentrog@digium.com>2015-01-29 23:03:14 +0000
commit388d691f34d7cfcb77130965bf8709eee4b692c1 (patch)
tree1b9064d7cc66b65ea4918d2765845ebbdeb5ba8e /res/stasis
parentf61c80a8f7b30af0551167e6fe59d9e9c005f60d (diff)
stasis transfer: fix stasis bridge push race part two
When swapping a Local channel in place of one already in a bridge (to complete a bridge attended transfer), the channel that was swapped out can actually be hung up before the stasis bridge push callback executes on the independant transfer thread. This results in the stasis app loop dropping out and removing the control that has the the app name which the local replacement channel needs so it can re-enter stasis. To avoid this race condition a new push_peek callback has been added, and called from the ast_bridge_impart thread before it launches the independant thread that will complete the transfer. Now the stasis push_peek callback can copy the stasis app name before the swap channel can hang up. ASTERISK-24649 Review: https://reviewboard.asterisk.org/r/4382/ ........ Merged revisions 431450 from http://svn.asterisk.org/svn/asterisk/branches/13 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@431451 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'res/stasis')
-rw-r--r--res/stasis/stasis_bridge.c72
1 files changed, 51 insertions, 21 deletions
diff --git a/res/stasis/stasis_bridge.c b/res/stasis/stasis_bridge.c
index 657238417..e41088134 100644
--- a/res/stasis/stasis_bridge.c
+++ b/res/stasis/stasis_bridge.c
@@ -99,6 +99,56 @@ static void bridge_stasis_queue_join_action(struct ast_bridge *self,
/*!
* \internal
+ * \brief Peek at channel before it is pushed into bridge
+ * \since 13.2.0
+ *
+ * \param self Bridge to operate upon.
+ * \param bridge_channel Bridge channel to push.
+ * \param swap Bridge channel to swap places with if not NULL.
+ *
+ * \note On entry, self is already locked.
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure. The channel should not be pushed.
+ */
+static int bridge_stasis_push_peek(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
+{
+ struct stasis_app_control *swap_control;
+ struct ast_channel_snapshot *to_be_replaced;
+
+ if (!swap) {
+ goto done;
+ }
+
+ swap_control = stasis_app_control_find_by_channel(swap->chan);
+ if (!swap_control) {
+ ast_log(LOG_ERROR,"Failed to find stasis app control for swapped channel %s\n", ast_channel_name(swap->chan));
+ return -1;
+ }
+ to_be_replaced = ast_channel_snapshot_get_latest(ast_channel_uniqueid(swap->chan));
+
+ ast_debug(3, "Copying stasis app name %s from %s to %s\n", app_name(control_app(swap_control)),
+ ast_channel_name(swap->chan), ast_channel_name(bridge_channel->chan));
+
+ ast_channel_lock(bridge_channel->chan);
+
+ /* copy the app name from the swap channel */
+ app_set_replace_channel_app(bridge_channel->chan, app_name(control_app(swap_control)));
+
+ /* set the replace channel snapshot */
+ app_set_replace_channel_snapshot(bridge_channel->chan, to_be_replaced);
+
+ ast_channel_unlock(bridge_channel->chan);
+
+ ao2_ref(swap_control, -1);
+ ao2_cleanup(to_be_replaced);
+
+done:
+ return ast_bridge_base_v_table.push_peek(self, bridge_channel, swap);
+}
+
+/*!
+ * \internal
* \brief Push this channel into the Stasis bridge.
* \since 12.5.0
*
@@ -115,27 +165,6 @@ static int bridge_stasis_push(struct ast_bridge *self, struct ast_bridge_channel
{
struct stasis_app_control *control = stasis_app_control_find_by_channel(bridge_channel->chan);
- if (swap) {
- struct ast_channel_snapshot *to_be_replaced = ast_channel_snapshot_get_latest(ast_channel_uniqueid(swap->chan));
- struct stasis_app_control *swap_control = stasis_app_control_find_by_channel(swap->chan);
-
- /* set the replace channel snapshot */
- ast_channel_lock(bridge_channel->chan);
- app_set_replace_channel_snapshot(bridge_channel->chan, to_be_replaced);
-
- /* copy the app name from the swap channel */
- if (swap_control) {
- ast_debug(3, "Copying stasis app name %s from %s to %s\n",
- app_name(control_app(swap_control)),
- ast_channel_name(swap->chan),
- ast_channel_name(bridge_channel->chan));
- app_set_replace_channel_app(bridge_channel->chan, app_name(control_app(swap_control)));
- }
- ast_channel_unlock(bridge_channel->chan);
- ao2_cleanup(to_be_replaced);
- ao2_cleanup(swap_control);
- }
-
if (!control && !stasis_app_channel_is_internal(bridge_channel->chan)) {
/* channel not in Stasis(), get it there */
/* Attach after-bridge callback and pass ownership of swap_app to it */
@@ -256,4 +285,5 @@ void bridge_stasis_init(void)
bridge_stasis_v_table.name = "stasis";
bridge_stasis_v_table.push = bridge_stasis_push;
bridge_stasis_v_table.pull = bridge_stasis_pull;
+ bridge_stasis_v_table.push_peek = bridge_stasis_push_peek;
}