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
This commit is contained in:
George Joseph
2017-09-01 04:17:02 -06:00
committed by Richard Mudgett
parent 2857a3334a
commit 94091c7b96
4 changed files with 96 additions and 64 deletions

View File

@@ -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);