diff --git a/include/asterisk/bridge_channel_internal.h b/include/asterisk/bridge_channel_internal.h index 7f7d5a88b14583204331175cf690e3dcaccf5c8c..fb8e781e8fe376a6e2517d7cae2b37f349cb3f42 100644 --- a/include/asterisk/bridge_channel_internal.h +++ b/include/asterisk/bridge_channel_internal.h @@ -151,47 +151,20 @@ int bridge_channel_internal_push_full(struct ast_bridge_channel *bridge_channel, void bridge_channel_internal_pull(struct ast_bridge_channel *bridge_channel); /*! - * \brief Internal bridge channel wait condition and associated result. - */ -struct bridge_channel_internal_cond { - /*! Lock for the data structure */ - ast_mutex_t lock; - /*! Wait condition */ - ast_cond_t cond; - /*! Wait until done */ - int done; - /*! The bridge channel */ - struct ast_bridge_channel *bridge_channel; -}; - -/*! - * \internal - * \brief Wait for the expected signal. - * \since 13.5.0 - * - * \param cond the wait object - * - * \return Nothing - */ -void bridge_channel_internal_wait(struct bridge_channel_internal_cond *cond); - -/*! - * \internal - * \brief Signal the condition wait. - * \since 13.5.0 + * \brief Signal imparting threads to wake up. + * \since 13.9.0 * - * \param cond the wait object + * \param chan Channel imparted that we need to signal. * * \return Nothing */ -void bridge_channel_internal_signal(struct bridge_channel_internal_cond *cond); +void bridge_channel_impart_signal(struct ast_channel *chan); /*! * \internal * \brief Join the bridge_channel to the bridge (blocking) * * \param bridge_channel The Channel in the bridge - * \param cond data used for signaling * * \note The bridge_channel->swap holds a channel reference for the swap * channel going into the bridging system. The ref ensures that the swap @@ -206,8 +179,7 @@ void bridge_channel_internal_signal(struct bridge_channel_internal_cond *cond); * \retval 0 bridge channel successfully joined the bridge * \retval -1 bridge channel failed to join the bridge */ -int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel, - struct bridge_channel_internal_cond *cond); +int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel); /*! * \internal diff --git a/main/bridge.c b/main/bridge.c index b9a436e86a5efcebe2d5a22a2fceb7ff39b24149..7e72052743b4ae7dfdb1e53594c254df0c8c37fa 100644 --- a/main/bridge.c +++ b/main/bridge.c @@ -1481,6 +1481,150 @@ void ast_bridge_notify_masquerade(struct ast_channel *chan) ao2_ref(bridge_channel, -1); } +/*! + * \brief Internal bridge impart wait condition and associated conditional. + */ +struct bridge_channel_impart_cond { + AST_LIST_ENTRY(bridge_channel_impart_cond) node; + /*! Lock for the data structure */ + ast_mutex_t lock; + /*! Wait condition */ + ast_cond_t cond; + /*! Wait until done */ + int done; +}; + +AST_LIST_HEAD_NOLOCK(bridge_channel_impart_ds_head, bridge_channel_impart_cond); + +/*! + * \internal + * \brief Signal imparting threads to wake up. + * \since 13.9.0 + * + * \param ds_head List of imparting threads to wake up. + * + * \return Nothing + */ +static void bridge_channel_impart_ds_head_signal(struct bridge_channel_impart_ds_head *ds_head) +{ + if (ds_head) { + struct bridge_channel_impart_cond *cond; + + while ((cond = AST_LIST_REMOVE_HEAD(ds_head, node))) { + ast_mutex_lock(&cond->lock); + cond->done = 1; + ast_cond_signal(&cond->cond); + ast_mutex_unlock(&cond->lock); + } + } +} + +static void bridge_channel_impart_ds_head_dtor(void *doomed) +{ + bridge_channel_impart_ds_head_signal(doomed); + ast_free(doomed); +} + +/*! + * \internal + * \brief Fixup the bridge impart datastore. + * \since 13.9.0 + * + * \param data Bridge impart datastore data to fixup from old_chan. + * \param old_chan The datastore is moving from this channel. + * \param new_chan The datastore is moving to this channel. + * + * \return Nothing + */ +static void bridge_channel_impart_ds_head_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) +{ + /* + * Signal any waiting impart threads. The masquerade is going to kill + * old_chan and we don't need to be waiting on new_chan. + */ + bridge_channel_impart_ds_head_signal(data); +} + +static const struct ast_datastore_info bridge_channel_impart_ds_info = { + .type = "bridge-impart-ds", + .destroy = bridge_channel_impart_ds_head_dtor, + .chan_fixup = bridge_channel_impart_ds_head_fixup, +}; + +/*! + * \internal + * \brief Add impart wait datastore conditional to channel. + * \since 13.9.0 + * + * \param chan Channel to add the impart wait conditional. + * \param cond Imparting conditional to add. + * + * \retval 0 on success. + * \retval -1 on error. + */ +static int bridge_channel_impart_add(struct ast_channel *chan, struct bridge_channel_impart_cond *cond) +{ + struct ast_datastore *datastore; + struct bridge_channel_impart_ds_head *ds_head; + + ast_channel_lock(chan); + + datastore = ast_channel_datastore_find(chan, &bridge_channel_impart_ds_info, NULL); + if (!datastore) { + datastore = ast_datastore_alloc(&bridge_channel_impart_ds_info, NULL); + if (!datastore) { + ast_channel_unlock(chan); + return -1; + } + ds_head = ast_calloc(1, sizeof(*ds_head)); + if (!ds_head) { + ast_channel_unlock(chan); + ast_datastore_free(datastore); + return -1; + } + datastore->data = ds_head; + ast_channel_datastore_add(chan, datastore); + } else { + ds_head = datastore->data; + ast_assert(ds_head != NULL); + } + + AST_LIST_INSERT_TAIL(ds_head, cond, node); + + ast_channel_unlock(chan); + return 0; +} + +void bridge_channel_impart_signal(struct ast_channel *chan) +{ + struct ast_datastore *datastore; + + ast_channel_lock(chan); + datastore = ast_channel_datastore_find(chan, &bridge_channel_impart_ds_info, NULL); + if (datastore) { + bridge_channel_impart_ds_head_signal(datastore->data); + } + ast_channel_unlock(chan); +} + +/*! + * \internal + * \brief Block imparting channel thread until signaled. + * \since 13.9.0 + * + * \param cond Imparting conditional to wait for. + * + * \return Nothing + */ +static void bridge_channel_impart_wait(struct bridge_channel_impart_cond *cond) +{ + ast_mutex_lock(&cond->lock); + while (!cond->done) { + ast_cond_wait(&cond->cond, &cond->lock); + } + ast_mutex_unlock(&cond->lock); +} + /* * XXX ASTERISK-21271 make ast_bridge_join() require features to be allocated just like ast_bridge_impart() and not expect the struct back. * @@ -1549,7 +1693,7 @@ int ast_bridge_join(struct ast_bridge *bridge, } if (!res) { - res = bridge_channel_internal_join(bridge_channel, NULL); + res = bridge_channel_internal_join(bridge_channel); } /* Cleanup all the data in the bridge channel after it leaves the bridge. */ @@ -1566,6 +1710,7 @@ int ast_bridge_join(struct ast_bridge *bridge, join_exit:; ast_bridge_run_after_callback(chan); + bridge_channel_impart_signal(chan); if (!(ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) && !ast_bridge_setup_after_goto(chan)) { /* Claim the after bridge goto is an async goto destination. */ @@ -1579,14 +1724,13 @@ join_exit:; /*! \brief Thread responsible for imparted bridged channels to be departed */ static void *bridge_channel_depart_thread(void *data) { - struct bridge_channel_internal_cond *cond = data; - struct ast_bridge_channel *bridge_channel = cond->bridge_channel; + struct ast_bridge_channel *bridge_channel = data; if (bridge_channel->callid) { ast_callid_threadassoc_add(bridge_channel->callid); } - bridge_channel_internal_join(bridge_channel, cond); + bridge_channel_internal_join(bridge_channel); /* * cleanup @@ -1599,6 +1743,8 @@ static void *bridge_channel_depart_thread(void *data) bridge_channel->features = NULL; ast_bridge_discard_after_callback(bridge_channel->chan, 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); return NULL; @@ -1607,15 +1753,14 @@ static void *bridge_channel_depart_thread(void *data) /*! \brief Thread responsible for independent imparted bridged channels */ static void *bridge_channel_ind_thread(void *data) { - struct bridge_channel_internal_cond *cond = data; - struct ast_bridge_channel *bridge_channel = cond->bridge_channel; + struct ast_bridge_channel *bridge_channel = data; struct ast_channel *chan; if (bridge_channel->callid) { ast_callid_threadassoc_add(bridge_channel->callid); } - bridge_channel_internal_join(bridge_channel, cond); + bridge_channel_internal_join(bridge_channel); chan = bridge_channel->chan; /* cleanup */ @@ -1632,15 +1777,18 @@ static void *bridge_channel_ind_thread(void *data) ao2_ref(bridge_channel, -1); ast_bridge_run_after_callback(chan); + /* If join failed there will be impart threads waiting. */ + bridge_channel_impart_signal(chan); ast_bridge_run_after_goto(chan); return NULL; } -int ast_bridge_impart(struct ast_bridge *bridge, +static int bridge_impart_internal(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, - enum ast_bridge_impart_flags flags) + enum ast_bridge_impart_flags flags, + struct bridge_channel_impart_cond *cond) { int res = 0; struct ast_bridge_channel *bridge_channel; @@ -1699,27 +1847,20 @@ int ast_bridge_impart(struct ast_bridge *bridge, /* Actually create the thread that will handle the channel */ if (!res) { - struct bridge_channel_internal_cond cond = { - .done = 0, - .bridge_channel = bridge_channel - }; - ast_mutex_init(&cond.lock); - ast_cond_init(&cond.cond, NULL); - + res = bridge_channel_impart_add(chan, cond); + } + if (!res) { if ((flags & AST_BRIDGE_IMPART_CHAN_MASK) == AST_BRIDGE_IMPART_CHAN_INDEPENDENT) { res = ast_pthread_create_detached(&bridge_channel->thread, NULL, - bridge_channel_ind_thread, &cond); + bridge_channel_ind_thread, bridge_channel); } else { res = ast_pthread_create(&bridge_channel->thread, NULL, - bridge_channel_depart_thread, &cond); + bridge_channel_depart_thread, bridge_channel); } if (!res) { - bridge_channel_internal_wait(&cond); + bridge_channel_impart_wait(cond); } - - ast_cond_destroy(&cond.cond); - ast_mutex_destroy(&cond.lock); } if (res) { @@ -1740,6 +1881,32 @@ int ast_bridge_impart(struct ast_bridge *bridge, return 0; } +int ast_bridge_impart(struct ast_bridge *bridge, + struct ast_channel *chan, + struct ast_channel *swap, + struct ast_bridge_features *features, + enum ast_bridge_impart_flags flags) +{ + struct bridge_channel_impart_cond cond = { + .done = 0, + }; + int res; + + ast_mutex_init(&cond.lock); + ast_cond_init(&cond.cond, NULL); + + res = bridge_impart_internal(bridge, chan, swap, features, flags, &cond); + if (res) { + /* Impart failed. Signal any other waiting impart threads */ + bridge_channel_impart_signal(chan); + } + + ast_cond_destroy(&cond.cond); + ast_mutex_destroy(&cond.lock); + + return res; +} + int ast_bridge_depart(struct ast_channel *chan) { struct ast_bridge_channel *bridge_channel; diff --git a/main/bridge_channel.c b/main/bridge_channel.c index 66f26eefeda153ab8b9a6b7d264b5a12bdf147e2..db4ecfe57cd10cf497892425a34f5212a9e76e92 100644 --- a/main/bridge_channel.c +++ b/main/bridge_channel.c @@ -2637,27 +2637,7 @@ static void bridge_channel_event_join_leave(struct ast_bridge_channel *bridge_ch ao2_iterator_destroy(&iter); } -void bridge_channel_internal_wait(struct bridge_channel_internal_cond *cond) -{ - ast_mutex_lock(&cond->lock); - while (!cond->done) { - ast_cond_wait(&cond->cond, &cond->lock); - } - ast_mutex_unlock(&cond->lock); -} - -void bridge_channel_internal_signal(struct bridge_channel_internal_cond *cond) -{ - if (cond) { - ast_mutex_lock(&cond->lock); - cond->done = 1; - ast_cond_signal(&cond->cond); - ast_mutex_unlock(&cond->lock); - } -} - -int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel, - struct bridge_channel_internal_cond *cond) +int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel) { int res = 0; struct ast_bridge_features *channel_features; @@ -2687,7 +2667,6 @@ int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel, bridge_channel->bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan)); - bridge_channel_internal_signal(cond); return -1; } ast_channel_internal_bridge_set(bridge_channel->chan, bridge_channel->bridge); @@ -2722,8 +2701,6 @@ int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel, } bridge_reconfigured(bridge_channel->bridge, !bridge_channel->inhibit_colp); - bridge_channel_internal_signal(cond); - if (bridge_channel->state == BRIDGE_CHANNEL_STATE_WAIT) { /* * Indicate a source change since this channel is entering the @@ -2735,6 +2712,7 @@ int bridge_channel_internal_join(struct ast_bridge_channel *bridge_channel, ast_indicate(bridge_channel->chan, AST_CONTROL_SRCCHANGE); } + bridge_channel_impart_signal(bridge_channel->chan); ast_bridge_unlock(bridge_channel->bridge); /* Must release any swap ref after unlocking the bridge. */