[svn-commits] dvossel: branch dvossel/masq_locking_order_trunk r221479 - /team/dvossel/masq...
SVN commits to the Digium repositories
svn-commits at lists.digium.com
Wed Sep 30 17:45:05 CDT 2009
Author: dvossel
Date: Wed Sep 30 17:45:01 2009
New Revision: 221479
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=221479
Log:
fixes chan_sip's local_attended_transfer() function to work
with ast_do_masquerade() now requiring no channel locks.
Modified:
team/dvossel/masq_locking_order_trunk/channels/chan_sip.c
Modified: team/dvossel/masq_locking_order_trunk/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/team/dvossel/masq_locking_order_trunk/channels/chan_sip.c?view=diff&rev=221479&r1=221478&r2=221479
==============================================================================
--- team/dvossel/masq_locking_order_trunk/channels/chan_sip.c (original)
+++ team/dvossel/masq_locking_order_trunk/channels/chan_sip.c Wed Sep 30 17:45:01 2009
@@ -2690,7 +2690,7 @@
static int handle_request_options(struct sip_pvt *p, struct sip_request *req);
static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin, int *nounlock);
static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, const char *e);
-static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, int seqno);
+static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, int seqno, int *nounlock);
/*------Response handling functions */
static void handle_response_invite(struct sip_pvt *p, int resp, const char *rest, struct sip_request *req, int seqno);
@@ -20885,8 +20885,20 @@
}
/*! \brief Find all call legs and bridge transferee with target
- * called from handle_request_refer */
-static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, int seqno)
+ * called from handle_request_refer
+ *
+ * \note this function assumes two locks to begin with, sip_pvt transferer and current.chan1 (the pvt's owner)...
+ * 2 additional locks are held at the beginning of the function, targetcall_pvt, and targetcall_pvt's owner
+ * channel (which is stored in target.chan1). These 2 locks _MUST_ be let go by the end of the function. Do
+ * not be confused into thinking a pvt's owner is the same thing as the channels locked at the beginning of
+ * this function, after the masquerade this may not be true. Be consistent and unlock only the exact same
+ * pointers that were locked to begin with.
+ *
+ * If this function is successful, only the transferer pvt lock will remain on return. Setting nounlock indicates
+ * to socket_read that the pvt's owner it locked does not require an unlock... This is very important to take care of
+ * before a masqurade as the owner channel socket_read locks will probably not be the one it trys to unlock.
+ */
+static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, int seqno, int *nounlock)
{
struct sip_dual target; /* Chan 1: Call from tranferer to Asterisk */
/* Chan 2: Call from Asterisk to target */
@@ -20906,7 +20918,7 @@
to follow the standard */
transmit_notify_with_sipfrag(transferer, seqno, "481 Call leg/transaction does not exist", TRUE);
append_history(transferer, "Xfer", "Refer failed");
- ast_clear_flag(&transferer->flags[0], SIP_GOTREFER);
+ ast_clear_flag(&transferer->flags[0], SIP_GOTREFER);
transferer->refer->status = REFER_FAILED;
return -1;
}
@@ -20972,21 +20984,17 @@
ast_party_connected_line_copy(&connected_to_target, &target.chan1->connected);
connected_to_target.source = connected_to_transferee.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
res = attempt_transfer(current, &target);
- sip_pvt_unlock(targetcall_pvt);
if (res) {
/* Failed transfer */
transmit_notify_with_sipfrag(transferer, seqno, "486 Busy Here", TRUE);
append_history(transferer, "Xfer", "Refer failed");
- if (targetcall_pvt->owner)
- ast_channel_unlock(targetcall_pvt->owner);
ast_clear_flag(&transferer->flags[0], SIP_DEFER_BYE_ON_TRANSFER);
} else {
/* Transfer succeeded! */
const char *xfersound = pbx_builtin_getvar_helper(target.chan1, "ATTENDED_TRANSFER_COMPLETE_SOUND");
- /* target.chan1 was locked in get_sip_pvt_byid_locked */
+ /* target.chan1 was locked in get_sip_pvt_byid_locked, do not unlock target.chan1 before this */
ast_cel_report_event(target.chan1, AST_CEL_ATTENDEDTRANSFER, NULL, transferer_linkedid, target.chan2);
- ast_channel_unlock(target.chan1);
/* Tell transferer that we're done. */
transmit_notify_with_sipfrag(transferer, seqno, "200 OK", TRUE);
@@ -20995,20 +21003,19 @@
if (target.chan2 && !ast_strlen_zero(xfersound) && ast_streamfile(target.chan2, xfersound, target.chan2->language) >= 0) {
ast_waitstream(target.chan2, "");
}
-
+
/* By forcing the masquerade, we know that target.chan1 and target.chan2 are bridged. We then
* can queue connected line updates where they need to go.
- *
- * No need to lock target.chan1 here since it was previously locked in get_sip_pvt_byid_locked
*/
if (target.chan1->masq) {
+ /* before a masquerade, all channel locks must be unlocked. Any recursive
+ * channel locks held before this function invalidates channel container locking order */
+ ast_channel_unlock(target.chan1);
+ ast_channel_unlock(current->chan1); /* current.chan1 is p->owner before the masq, it was locked by socket_read() */
+ *nounlock = 1; /* we just unlocked the dialog's channel and have no plans of locking it again. */
/* If the channel thread already did the masquerade, then we don't need to do anything */
ast_do_masquerade(target.chan1);
- }
-
- if (targetcall_pvt->owner) {
- ast_debug(1, "SIP attended transfer: Unlocking channel %s\n", targetcall_pvt->owner->name);
- ast_channel_unlock(targetcall_pvt->owner);
+ ast_channel_lock(target.chan1);
}
if (target.chan2) {
@@ -21023,6 +21030,9 @@
ast_channel_update_connected_line(target.chan1, &connected_to_target);
}
}
+ /* remove locks from targetcall_pvt and target.chan1 (targetcall_pvt's owner at the beginning of the function) */
+ sip_pvt_unlock(targetcall_pvt);
+ ast_channel_unlock(target.chan1);
ast_party_connected_line_free(&connected_to_target);
ast_party_connected_line_free(&connected_to_transferee);
if (targetcall_pvt)
@@ -21248,7 +21258,7 @@
/* Attended transfer: Find all call legs and bridge transferee with target*/
if (p->refer->attendedtransfer) {
- if ((res = local_attended_transfer(p, ¤t, req, seqno)))
+ if ((res = local_attended_transfer(p, ¤t, req, seqno, nounlock)))
return res; /* We're done with the transfer */
/* Fall through for remote transfers that we did not find locally */
if (sipdebug)
More information about the svn-commits
mailing list