diff --git a/include/asterisk/core_local.h b/include/asterisk/core_local.h index 8557072c69bfd81cb1fc909b7514b90f7c03377f..7d66983369a2feb9e1405f31ff491741af7a4c12 100644 --- a/include/asterisk/core_local.h +++ b/include/asterisk/core_local.h @@ -45,6 +45,8 @@ struct stasis_message_type; * \brief Lock the "chan" and "owner" channels (and return them) on the base * private structure as well as the base private structure itself. * + * \deprecated - *DO NOT USE* Please use ast_local_lock_all2 instead. + * * \note This also adds references to each of the above mentioned elements and * also the underlying private local structure. * \note None of these locks should be held prior to calling this function. @@ -59,10 +61,29 @@ struct stasis_message_type; void ast_local_lock_all(struct ast_channel *chan, struct ast_channel **outchan, struct ast_channel **outowner); +/*! + * \brief Add a reference to the local channel's private tech, lock the local channel's + * private base, and add references and lock both sides of the local channel. + * + * \note None of these locks should be held prior to calling this function. + * \note To undo this process call ast_local_unlock_all2. + * + * \since 13.17.0, 14.6.0 + * + * \param chan Must be a local channel + * \param tech_pvt [out] channel's private tech (ref and lock added) + * \param base_chan [out] One side of the local channel (ref and lock added) + * \param base_owner [out] Other side of the local channel (ref and lock added) + */ +void ast_local_lock_all2(struct ast_channel *chan, void **tech_pvt, + struct ast_channel **base_chan, struct ast_channel **base_owner); + /*! * \brief Unlock the "chan" and "owner" channels on the base private structure * as well as the base private structure itself. * + * \deprecated - *DO NOT USE* Please use ast_local_unlock_all2 instead. + * * \note This also removes references to each of the above mentioned elements and * also the underlying private local structure. * \note This function should be used in conjunction with ast_local_lock_all. @@ -73,6 +94,22 @@ void ast_local_lock_all(struct ast_channel *chan, struct ast_channel **outchan, */ void ast_local_unlock_all(struct ast_channel *chan); +/*! + * \brief Remove a reference to the given local channel's private tech, unlock the given + * local channel's private base, and remove references and unlock both sides of + * given the local channel. + * + * \note This function should be used in conjunction with ast_local_lock_all2. + * + * \since 13.17.0, 14.6.0 + * + * \param tech_pvt channel's private tech (ref and lock removed) + * \param base_chan One side of the local channel (ref and lock removed) + * \param base_owner Other side of the local channel (ref and lock removed) + */ +void ast_local_unlock_all2(void *tech_pvt, struct ast_channel *base_chan, + struct ast_channel *base_owner); + /*! * \brief Get the other local channel in the pair. * \since 12.0.0 diff --git a/main/bridge.c b/main/bridge.c index b2beb86377ff09d3c875a5fba82f9965005c773c..b812ac89f938db9f70795730ec0e4d5086739a88 100644 --- a/main/bridge.c +++ b/main/bridge.c @@ -4241,14 +4241,15 @@ static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *cha BRIDGE_LOCK_ONE_OR_BOTH(bridge1, bridge2); if (bridge2) { + void *tech; struct ast_channel *locals[2]; /* Have to lock everything just in case a hangup comes in early */ - ast_local_lock_all(local_chan, &locals[0], &locals[1]); + ast_local_lock_all2(local_chan, &tech, &locals[0], &locals[1]); if (!locals[0] || !locals[1]) { ast_log(LOG_ERROR, "Transfer failed probably due to an early hangup - " "missing other half of '%s'\n", ast_channel_name(local_chan)); - ast_local_unlock_all(local_chan); + ast_local_unlock_all2(tech, locals[0], locals[1]); ao2_cleanup(local_chan); return AST_BRIDGE_TRANSFER_FAIL; } @@ -4259,7 +4260,7 @@ static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *cha } ast_attended_transfer_message_add_link(transfer_msg, locals); - ast_local_unlock_all(local_chan); + ast_local_unlock_all2(tech, locals[0], locals[1]); } else { ast_attended_transfer_message_add_app(transfer_msg, app, local_chan); } diff --git a/main/core_local.c b/main/core_local.c index 1b8ebf6f1ea8082353ff160e3c8a98b691cadc8b..a5918f525ca0f111b2648569300053aad463ddb3 100644 --- a/main/core_local.c +++ b/main/core_local.c @@ -235,17 +235,45 @@ struct local_pvt { char exten[AST_MAX_EXTENSION]; }; -void ast_local_lock_all(struct ast_channel *chan, struct ast_channel **outchan, - struct ast_channel **outowner) +void ast_local_lock_all2(struct ast_channel *chan, void **tech_pvt, + struct ast_channel **base_chan, struct ast_channel **base_owner) { struct local_pvt *p = ast_channel_tech_pvt(chan); - *outchan = NULL; - *outowner = NULL; + *tech_pvt = NULL; + *base_chan = NULL; + *base_owner = NULL; if (p) { - ao2_ref(p, 1); - ast_unreal_lock_all(&p->base, outchan, outowner); + *tech_pvt = ao2_bump(p); + ast_unreal_lock_all(&p->base, base_chan, base_owner); + } +} + +void ast_local_lock_all(struct ast_channel *chan, struct ast_channel **outchan, + struct ast_channel **outowner) +{ + void *tech_pvt; + ast_local_lock_all2(chan, &tech_pvt, outchan, outowner); +} + +void ast_local_unlock_all2(void *tech_pvt, struct ast_channel *base_chan, + struct ast_channel *base_owner) +{ + if (base_chan) { + ast_channel_unlock(base_chan); + ast_channel_unref(base_chan); + } + + if (base_owner) { + ast_channel_unlock(base_owner); + ast_channel_unref(base_owner); + } + + if (tech_pvt) { + struct local_pvt *p = tech_pvt; + ao2_unlock(&p->base); + ao2_ref(tech_pvt, -1); } } @@ -259,19 +287,7 @@ void ast_local_unlock_all(struct ast_channel *chan) } base = &p->base; - - if (base->owner) { - ast_channel_unlock(base->owner); - ast_channel_unref(base->owner); - } - - if (base->chan) { - ast_channel_unlock(base->chan); - ast_channel_unref(base->chan); - } - - ao2_unlock(base); - ao2_ref(p, -1); + ast_local_unlock_all2(p, base->chan, base->owner); } struct ast_channel *ast_local_get_peer(struct ast_channel *ast)