summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorGeorge Joseph <gjoseph@digium.com>2017-09-01 04:17:02 -0600
committerRichard Mudgett <rmudgett@digium.com>2017-09-06 13:00:49 -0500
commit94091c7b960aa45923036cd4d79929dcb3178cdc (patch)
tree4c045660fa8987fe3cf28a3d1bf075d0bc23c3e7 /main
parent2857a3334aa430b81baac31bdfe0efb3871f37d4 (diff)
stasis/control: Fix possible deadlock with swap channel
If an error occurs during a bridge impart it's possible that the "bridge_after" callback might try to run before control_swap_channel_in_bridge has been signalled to continue. Since control_swap_channel_in_bridge is holding the control lock and the callback needs it, a deadlock will occur. * control_swap_channel_in_bridge now only holds the control lock while it's actually modifying the control structure and releases it while the bridge impart is running. * bridge_after_cb is now tolerant of impart failures. Change-Id: Ifd239aa93955b3eb475521f61e284fcb0da2c3b3
Diffstat (limited to 'main')
-rw-r--r--main/bridge.c6
-rw-r--r--main/bridge_after.c30
2 files changed, 19 insertions, 17 deletions
diff --git a/main/bridge.c b/main/bridge.c
index b732d5fc5..ab12ecf64 100644
--- a/main/bridge.c
+++ b/main/bridge.c
@@ -1758,12 +1758,13 @@ join_exit:;
static void *bridge_channel_depart_thread(void *data)
{
struct ast_bridge_channel *bridge_channel = data;
+ int res = 0;
if (bridge_channel->callid) {
ast_callid_threadassoc_add(bridge_channel->callid);
}
- bridge_channel_internal_join(bridge_channel);
+ res = bridge_channel_internal_join(bridge_channel);
/*
* cleanup
@@ -1775,7 +1776,8 @@ static void *bridge_channel_depart_thread(void *data)
ast_bridge_features_destroy(bridge_channel->features);
bridge_channel->features = NULL;
- ast_bridge_discard_after_callback(bridge_channel->chan, AST_BRIDGE_AFTER_CB_REASON_DEPART);
+ ast_bridge_discard_after_callback(bridge_channel->chan,
+ res ? AST_BRIDGE_AFTER_CB_REASON_IMPART_FAILED : AST_BRIDGE_AFTER_CB_REASON_DEPART);
/* If join failed there will be impart threads waiting. */
bridge_channel_impart_signal(bridge_channel->chan);
ast_bridge_discard_after_goto(bridge_channel->chan);
diff --git a/main/bridge_after.c b/main/bridge_after.c
index d4aec75d0..69c8f989b 100644
--- a/main/bridge_after.c
+++ b/main/bridge_after.c
@@ -293,23 +293,23 @@ int ast_bridge_set_after_callback(struct ast_channel *chan, ast_bridge_after_cb
return 0;
}
-const char *reason_strings[] = {
- [AST_BRIDGE_AFTER_CB_REASON_DESTROY] = "Channel destroyed (hungup)",
- [AST_BRIDGE_AFTER_CB_REASON_REPLACED] = "Callback was replaced",
- [AST_BRIDGE_AFTER_CB_REASON_MASQUERADE] = "Channel masqueraded",
- [AST_BRIDGE_AFTER_CB_REASON_DEPART] = "Channel was departed from bridge",
- [AST_BRIDGE_AFTER_CB_REASON_REMOVED] = "Callback was removed",
-};
-
const char *ast_bridge_after_cb_reason_string(enum ast_bridge_after_cb_reason reason)
{
- if (reason < AST_BRIDGE_AFTER_CB_REASON_DESTROY
- || AST_BRIDGE_AFTER_CB_REASON_REMOVED < reason
- || !reason_strings[reason]) {
- return "Unknown";
- }
-
- return reason_strings[reason];
+ switch (reason) {
+ case AST_BRIDGE_AFTER_CB_REASON_DESTROY:
+ return "Channel destroyed (hungup)";
+ case AST_BRIDGE_AFTER_CB_REASON_REPLACED:
+ return "Callback was replaced";
+ case AST_BRIDGE_AFTER_CB_REASON_MASQUERADE:
+ return "Channel masqueraded";
+ case AST_BRIDGE_AFTER_CB_REASON_DEPART:
+ return "Channel was departed from bridge";
+ case AST_BRIDGE_AFTER_CB_REASON_REMOVED:
+ return "Callback was removed";
+ case AST_BRIDGE_AFTER_CB_REASON_IMPART_FAILED:
+ return "Channel failed joining the bridge";
+ }
+ return "Unknown";
}
struct after_bridge_goto_ds {