summaryrefslogtreecommitdiff
path: root/main/bridge_basic.c
diff options
context:
space:
mode:
authorMark Michelson <mmichelson@digium.com>2014-11-14 15:24:48 +0000
committerMark Michelson <mmichelson@digium.com>2014-11-14 15:24:48 +0000
commit1536b0ecb6fd99bc4eaceebba59563d98e19e65b (patch)
tree3325d451e1428e2c4459ffc258cb2d207cda3038 /main/bridge_basic.c
parent9067e6faa4f3ff762fb47a43a091b3b93fbe29fb (diff)
Fix race condition that could result in ARI transfer messages not being sent.
From reviewboard: "During blind transfer testing, it was noticed that tests were failing occasionally because the ARI blind transfer event was not being sent. After investigating, I detected a race condition in the blind transfer code. When blind transferring a single channel, the actual transfer operation (i.e. removing the transferee from the bridge and directing them to the proper dialplan location) is queued onto the transferee bridge channel. After queuing the transfer operation, the blind transfer Stasis message is published. At the time of publication, snapshots of the channels and bridge involved are created. The ARI subscriber to the blind transfer Stasis message then attempts to determine if the bridge or any of the involved channels are subscribed to by ARI applications. If so, then the blind transfer message is sent to the applications. The way that the ARI blind transfer message handler works is to first see if the transferer channel is subscribed to. If not, then iterate over all the channel IDs in the bridge snapshot and determine if any of those are subscribed to. In the test we were running, the lone transferee channel was subscribed to, so an ARI event should have been sent to our application. Occasionally, though, the bridge snapshot did not have any channels IDs on it at all. Why? The problem is that since the blind transfer operation is handled by a separate thread, it is possible that the transfer will have completed and the channels removed from the bridge before we publish the blind transfer Stasis message. Since the blind transfer has completed, the bridge on which the transfer occurred no longer has any channels on it, so the resulting bridge snapshot has no channels on it. Through investigation of the code, I found that attended transfers can have this issue too for the case where a transferee is transferred to an application." The fix employed here is to decouple the creation of snapshots for the transfer messages from the publication of the transfer messages. This way, snapshots can be created to reflect what they are at the time of the transfer operation. Review: https://reviewboard.asterisk.org/r/4135 ........ Merged revisions 427848 from http://svn.asterisk.org/svn/asterisk/branches/12 git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/13@427870 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main/bridge_basic.c')
-rw-r--r--main/bridge_basic.c118
1 files changed, 37 insertions, 81 deletions
diff --git a/main/bridge_basic.c b/main/bridge_basic.c
index a69750d15..97892e395 100644
--- a/main/bridge_basic.c
+++ b/main/bridge_basic.c
@@ -1606,33 +1606,21 @@ static void get_transfer_parties(struct ast_channel *transferer, struct ast_brid
static void publish_transfer_success(struct attended_transfer_properties *props,
struct ast_channel *transferee_channel, struct ast_channel *target_channel)
{
- struct ast_bridge_channel_pair transferee = {
- .channel = props->transferer,
- .bridge = props->transferee_bridge,
- };
- struct ast_bridge_channel_pair transfer_target = {
- .channel = props->transferer,
- .bridge = props->target_bridge,
- };
-
- if (transferee.bridge && transfer_target.bridge) {
- ast_bridge_lock_both(transferee.bridge, transfer_target.bridge);
- } else if (transferee.bridge) {
- ast_bridge_lock(transferee.bridge);
- } else if (transfer_target.bridge) {
- ast_bridge_lock(transfer_target.bridge);
- }
+ struct ast_attended_transfer_message *transfer_msg;
- ast_bridge_publish_attended_transfer_bridge_merge(0, AST_BRIDGE_TRANSFER_SUCCESS,
- &transferee, &transfer_target, props->transferee_bridge, transferee_channel,
- target_channel);
+ transfer_msg = ast_attended_transfer_message_create(0, props->transferer,
+ props->transferee_bridge, props->transferer, props->target_bridge,
+ transferee_channel, target_channel);
- if (transferee.bridge) {
- ast_bridge_unlock(transferee.bridge);
- }
- if (transfer_target.bridge) {
- ast_bridge_unlock(transfer_target.bridge);
+ if (!transfer_msg) {
+ ast_log(LOG_ERROR, "Unable to publish successful attended transfer from %s\n",
+ ast_channel_name(props->transferer));
+ return;
}
+
+ ast_attended_transfer_message_add_merge(transfer_msg, props->transferee_bridge);
+ ast_bridge_publish_attended_transfer(transfer_msg);
+ ao2_cleanup(transfer_msg);
}
/*!
@@ -1641,37 +1629,22 @@ static void publish_transfer_success(struct attended_transfer_properties *props,
static void publish_transfer_threeway(struct attended_transfer_properties *props,
struct ast_channel *transferee_channel, struct ast_channel *target_channel)
{
- struct ast_bridge_channel_pair transferee = {
- .channel = props->transferer,
- .bridge = props->transferee_bridge,
- };
- struct ast_bridge_channel_pair transfer_target = {
- .channel = props->transferer,
- .bridge = props->target_bridge,
- };
- struct ast_bridge_channel_pair threeway = {
- .channel = props->transferer,
- .bridge = props->transferee_bridge,
- };
+ struct ast_attended_transfer_message *transfer_msg;
- if (transferee.bridge && transfer_target.bridge) {
- ast_bridge_lock_both(transferee.bridge, transfer_target.bridge);
- } else if (transferee.bridge) {
- ast_bridge_lock(transferee.bridge);
- } else if (transfer_target.bridge) {
- ast_bridge_lock(transfer_target.bridge);
- }
+ transfer_msg = ast_attended_transfer_message_create(0, props->transferer,
+ props->transferee_bridge, props->transferer, props->target_bridge,
+ transferee_channel, target_channel);
- ast_bridge_publish_attended_transfer_threeway(0, AST_BRIDGE_TRANSFER_SUCCESS,
- &transferee, &transfer_target, &threeway, transferee_channel,
- target_channel);
-
- if (transferee.bridge) {
- ast_bridge_unlock(transferee.bridge);
- }
- if (transfer_target.bridge) {
- ast_bridge_unlock(transfer_target.bridge);
+ if (!transfer_msg) {
+ ast_log(LOG_ERROR, "Unable to publish successful three-way transfer from %s\n",
+ ast_channel_name(props->transferer));
+ return;
}
+
+ ast_attended_transfer_message_add_threeway(transfer_msg, props->transferer,
+ props->transferee_bridge);
+ ast_bridge_publish_attended_transfer(transfer_msg);
+ ao2_cleanup(transfer_msg);
}
/*!
@@ -1679,38 +1652,21 @@ static void publish_transfer_threeway(struct attended_transfer_properties *props
*/
static void publish_transfer_fail(struct attended_transfer_properties *props)
{
- struct ast_bridge_channel_pair transferee = {
- .channel = props->transferer,
- .bridge = props->transferee_bridge,
- };
- struct ast_bridge_channel_pair transfer_target = {
- .channel = props->transferer,
- .bridge = props->target_bridge,
- };
- struct ast_channel *transferee_channel;
- struct ast_channel *target_channel;
-
- if (transferee.bridge && transfer_target.bridge) {
- ast_bridge_lock_both(transferee.bridge, transfer_target.bridge);
- } else if (transferee.bridge) {
- ast_bridge_lock(transferee.bridge);
- } else if (transfer_target.bridge) {
- ast_bridge_lock(transfer_target.bridge);
- }
+ struct ast_attended_transfer_message *transfer_msg;
- get_transfer_parties(props->transferer, props->transferee_bridge, props->target_bridge,
- &transferee_channel, &target_channel);
- ast_bridge_publish_attended_transfer_fail(0, AST_BRIDGE_TRANSFER_FAIL,
- &transferee, &transfer_target, transferee_channel, target_channel);
- ast_channel_cleanup(transferee_channel);
- ast_channel_cleanup(target_channel);
+ transfer_msg = ast_attended_transfer_message_create(0, props->transferer,
+ props->transferee_bridge, props->transferer, props->target_bridge,
+ NULL, NULL);
- if (transferee.bridge) {
- ast_bridge_unlock(transferee.bridge);
- }
- if (transfer_target.bridge) {
- ast_bridge_unlock(transfer_target.bridge);
+ if (!transfer_msg) {
+ ast_log(LOG_ERROR, "Unable to publish failed transfer from %s\n",
+ ast_channel_name(props->transferer));
+ return;
}
+
+ transfer_msg->result = AST_BRIDGE_TRANSFER_FAIL;
+ ast_bridge_publish_attended_transfer(transfer_msg);
+ ao2_cleanup(transfer_msg);
}
/*!