[asterisk-commits] oej: branch oej/pine-pine-pine-pine-everywhere-pine-1.6.0 r312382 - /team/oej...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Apr 1 06:53:14 CDT 2011


Author: oej
Date: Fri Apr  1 06:53:11 2011
New Revision: 312382

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=312382
Log:
Backporting chan_local with objects as it solves a lot of issues

Modified:
    team/oej/pine-pine-pine-pine-everywhere-pine-1.6.0/channels/chan_local.c

Modified: team/oej/pine-pine-pine-pine-everywhere-pine-1.6.0/channels/chan_local.c
URL: http://svnview.digium.com/svn/asterisk/team/oej/pine-pine-pine-pine-everywhere-pine-1.6.0/channels/chan_local.c?view=diff&rev=312382&r1=312381&r2=312382
==============================================================================
--- team/oej/pine-pine-pine-pine-everywhere-pine-1.6.0/channels/chan_local.c (original)
+++ team/oej/pine-pine-pine-pine-everywhere-pine-1.6.0/channels/chan_local.c Fri Apr  1 06:53:11 2011
@@ -49,10 +49,17 @@
 #include "asterisk/manager.h"
 #include "asterisk/stringfields.h"
 #include "asterisk/devicestate.h"
+#include "asterisk/astobj2.h"
 
 static const char tdesc[] = "Local Proxy Channel Driver";
 
 #define IS_OUTBOUND(a,b) (a == b->chan ? 1 : 0)
+/* right now we are treating the locals astobj2 container as a
+ * list.  If there is ever a reason to make this more efficient
+ * increasing the bucket size would help. */
+static const int BUCKET_SIZE = 1;
+
+static struct ao2_container *locals;
 
 static struct ast_jb_conf g_jb_conf = {
 	.flags = 0,
@@ -75,6 +82,11 @@
 static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
 static int local_sendtext(struct ast_channel *ast, const char *text);
 static int local_devicestate(void *data);
+static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
+static int local_queryoption(struct ast_channel *ast, int option, void *data, int *datalen);
+#ifdef OEJ_SETOPT
+static int local_setoption(struct ast_channel *chan, int option, void *data, int datalen);
+#endif
 
 /* PBX interface structure for channel registration */
 static const struct ast_channel_tech local_tech = {
@@ -96,10 +108,14 @@
 	.send_html = local_sendhtml,
 	.send_text = local_sendtext,
 	.devicestate = local_devicestate,
+	.bridged_channel = local_bridgedchannel,
+	.queryoption = local_queryoption,
+#ifdef OEJ_SETOPT
+	.setoption = local_setoption,
+#endif
 };
 
 struct local_pvt {
-	ast_mutex_t lock;			/* Channel private lock */
 	unsigned int flags;                     /* Private flags */
 	char context[AST_MAX_CONTEXT];		/* Context to call */
 	char exten[AST_MAX_EXTENSION];		/* Extension to call */
@@ -112,14 +128,78 @@
 	AST_LIST_ENTRY(local_pvt) list;		/* Next entity */
 };
 
-#define LOCAL_GLARE_DETECT    (1 << 0) /*!< Detect glare on hangup */
-#define LOCAL_CANCEL_QUEUE    (1 << 1) /*!< Cancel queue */
-#define LOCAL_ALREADY_MASQED  (1 << 2) /*!< Already masqueraded */
-#define LOCAL_LAUNCHED_PBX    (1 << 3) /*!< PBX was launched */
-#define LOCAL_NO_OPTIMIZATION (1 << 4) /*!< Do not optimize using masquerading */
-#define LOCAL_MOH_PASSTHRU    (1 << 6) /*!< Pass through music on hold start/stop frames */
-
-static AST_LIST_HEAD_STATIC(locals, local_pvt);
+#define LOCAL_ALREADY_MASQED  (1 << 0) /*!< Already masqueraded */
+#define LOCAL_LAUNCHED_PBX    (1 << 1) /*!< PBX was launched */
+#define LOCAL_NO_OPTIMIZATION (1 << 2) /*!< Do not optimize using masquerading */
+#define LOCAL_BRIDGE          (1 << 3) /*!< Report back the "true" channel as being bridged to */
+#define LOCAL_MOH_PASSTHRU    (1 << 4) /*!< Pass through music on hold start/stop frames */
+
+#ifdef OEJ_SETOPT
+static int local_setoption(struct ast_channel *chan, int option, void * data, int datalen)
+{
+	int res;
+	struct local_pvt *p;
+	struct ast_channel *otherchan;
+	ast_chan_write_info_t *write_info;
+
+	if (option != AST_OPTION_CHANNEL_WRITE) {
+		return -1;
+	}
+
+	write_info = data;
+
+	if (write_info->version != AST_CHAN_WRITE_INFO_T_VERSION) {
+		ast_log(LOG_ERROR, "The chan_write_info_t type has changed, and this channel hasn't been updated!\n");
+		return -1;
+	}
+
+
+startover:
+	ast_channel_lock(chan);
+
+	p = chan->tech_pvt;
+	if (!p) {
+		ast_channel_unlock(chan);
+		ast_log(LOG_WARNING, "Could not update other side of %s, local_pvt went away.\n", chan->name);
+		return -1;
+	}
+
+	while (ao2_trylock(p)) {
+		ast_channel_unlock(chan);
+		sched_yield();
+		ast_channel_lock(chan);
+		p = chan->tech_pvt;
+		if (!p) {
+			ast_channel_unlock(chan);
+			ast_log(LOG_WARNING, "Could not update other side of %s, local_pvt went away.\n", chan->name);
+			return -1;
+		}
+	}
+
+	otherchan = (write_info->chan == p->owner) ? p->chan : p->owner;
+
+	if (!otherchan || otherchan == write_info->chan) {
+		ao2_unlock(p);
+		ast_channel_unlock(chan);
+		ast_log(LOG_WARNING, "Could not update other side of %s, other side went away.\n", chan->name);
+		return 0;
+	}
+
+	if (ast_channel_trylock(otherchan)) {
+		ao2_unlock(p);
+		ast_channel_unlock(chan);
+		goto startover;
+	}
+
+	res = write_info->write_fn(otherchan, write_info->function, write_info->data, write_info->value);
+
+	ast_channel_unlock(otherchan);
+	ao2_unlock(p);
+	ast_channel_unlock(chan);
+
+	return res;
+}
+#endif
 
 /*! \brief Adds devicestate to local channels */
 static int local_devicestate(void *data)
@@ -128,6 +208,7 @@
 	char *context = NULL, *opts = NULL;
 	int res;
 	struct local_pvt *lp;
+	struct ao2_iterator it;
 
 	if (!(context = strchr(exten, '@'))) {
 		ast_log(LOG_WARNING, "Someone used Local/%s somewhere without a @context. This is bad.\n", exten);
@@ -147,28 +228,116 @@
 		return AST_DEVICE_INVALID;
 	
 	res = AST_DEVICE_NOT_INUSE;
-	AST_LIST_LOCK(&locals);
-	AST_LIST_TRAVERSE(&locals, lp, list) {
+
+	it = ao2_iterator_init(locals, 0);
+	while ((lp = ao2_iterator_next(&it))) {
 		if (!strcmp(exten, lp->exten) && !strcmp(context, lp->context) && lp->owner) {
 			res = AST_DEVICE_INUSE;
+			ao2_ref(lp, -1);
 			break;
 		}
-	}
-	AST_LIST_UNLOCK(&locals);
+		ao2_ref(lp, -1);
+	}
+	ao2_iterator_destroy(&it);
 
 	return res;
 }
 
-/*!
- * \note Assumes the pvt is no longer in the pvts list
+/*! \brief Return the bridged channel of a Local channel */
+static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
+{
+	struct local_pvt *p = bridge->tech_pvt;
+	struct ast_channel *bridged = bridge;
+
+	if (!p) {
+		ast_debug(1, "Asked for bridged channel on '%s'/'%s', returning <none>\n",
+			chan->name, bridge->name);
+		return NULL;
+	}
+
+	ao2_lock(p);
+
+	if (ast_test_flag(p, LOCAL_BRIDGE)) {
+		/* Find the opposite channel */
+		bridged = (bridge == p->owner ? p->chan : p->owner);
+		
+		/* Now see if the opposite channel is bridged to anything */
+		if (!bridged) {
+			bridged = bridge;
+		} else if (bridged->_bridge) {
+			bridged = bridged->_bridge;
+		}
+	}
+
+	ao2_unlock(p);
+
+	return bridged;
+}
+
+static int local_queryoption(struct ast_channel *ast, int option, void *data, int *datalen)
+{
+	struct local_pvt *p = ast->tech_pvt;
+	struct ast_channel *chan, *bridged;
+	int res;
+
+	if (!p) {
+		return -1;
+	}
+
+	if (option != AST_OPTION_T38_STATE) {
+		/* AST_OPTION_T38_STATE is the only supported option at this time */
+		return -1;
+	}
+
+	ao2_lock(p);
+	chan = IS_OUTBOUND(ast, p) ? p->owner : p->chan;
+
+try_again:
+	if (!chan) {
+		ao2_unlock(p);
+		return -1;
+	}
+
+	if (ast_channel_trylock(chan)) {
+		ao2_unlock(p);
+		sched_yield();
+		ao2_lock(p);
+		chan = IS_OUTBOUND(ast, p) ? p->owner : p->chan;
+		goto try_again;
+	}
+
+	bridged = ast_bridged_channel(chan);
+	if (!bridged) {
+		/* can't query channel unless we are bridged */
+		ao2_unlock(p);
+		ast_channel_unlock(chan);
+		return -1;
+	}
+
+	if (ast_channel_trylock(bridged)) {
+		ast_channel_unlock(chan);
+		ao2_unlock(p);
+		sched_yield();
+		ao2_lock(p);
+		chan = IS_OUTBOUND(ast, p) ? p->owner : p->chan;
+		goto try_again;
+	}
+
+	res = ast_channel_queryoption(bridged, option, data, datalen, 0);
+	ao2_unlock(p);
+	ast_channel_unlock(chan);
+	ast_channel_unlock(bridged);
+	return res;
+}
+
+/*! \brief queue a frame on a to either the p->owner or p->chan
+ *
+ * \note the local_pvt MUST have it's ref count bumped before entering this function and
+ * decremented after this function is called.  This is a side effect of the deadlock
+ * avoidance that is necessary to lock 2 channels and a tech_pvt.  Without a ref counted
+ * local_pvt, it is impossible to guarantee it will not be destroyed by another thread
+ * during deadlock avoidance.
  */
-static struct local_pvt *local_pvt_destroy(struct local_pvt *pvt)
-{
-	ast_mutex_destroy(&pvt->lock);
-	ast_free(pvt);
-	return NULL;
-}
-
 static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_frame *f, 
 	struct ast_channel *us, int us_locked)
 {
@@ -186,35 +355,22 @@
 		return 0;
 	}
 
-	/* Set glare detection */
-	ast_set_flag(p, LOCAL_GLARE_DETECT);
-
 	/* Ensure that we have both channels locked */
 	while (other && ast_channel_trylock(other)) {
-		ast_mutex_unlock(&p->lock);
+		int res;
+		if ((res = ao2_unlock(p))) {
+			ast_log(LOG_ERROR, "chan_local bug! '&p->lock' was not locked when entering local_queue_frame! (%s)\n", strerror(res));
+			return -1;
+		}
 		if (us && us_locked) {
 			do {
 				CHANNEL_DEADLOCK_AVOIDANCE(us);
-			} while (ast_mutex_trylock(&p->lock));
+			} while (ao2_trylock(p));
 		} else {
 			usleep(1);
-			ast_mutex_lock(&p->lock);
+			ao2_lock(p);
 		}
 		other = isoutbound ? p->owner : p->chan;
-	}
-
-	/* Since glare detection only occurs within this function, and because
-	 * a pvt flag cannot be set without having the pvt lock, this is the only
-	 * location where we could detect a cancelling of the queue. */
-	if (ast_test_flag(p, LOCAL_CANCEL_QUEUE)) {
-		/* We had a glare on the hangup.  Forget all this business,
-		return and destroy p.  */
-		ast_mutex_unlock(&p->lock);
-		p = local_pvt_destroy(p);
-		if (other) {
-			ast_channel_unlock(other);
-		}
-		return -1;
 	}
 
 	if (other) {
@@ -225,8 +381,6 @@
 		ast_channel_unlock(other);
 	}
 
-	ast_clear_flag(p, LOCAL_GLARE_DETECT);
-
 	return 0;
 }
 
@@ -239,16 +393,18 @@
 	if (!p)
 		return -1;
 
-	ast_mutex_lock(&p->lock);
+	ao2_lock(p);
+	ao2_ref(p, 1);
 	isoutbound = IS_OUTBOUND(ast, p);
 	if (isoutbound) {
 		/* Pass along answer since somebody answered us */
 		struct ast_frame answer = { AST_FRAME_CONTROL, AST_CONTROL_ANSWER };
 		res = local_queue_frame(p, isoutbound, &answer, ast, 1);
-	} else
+	} else {
 		ast_log(LOG_WARNING, "Huh?  Local is being asked to answer?\n");
-	if (!res)
-		ast_mutex_unlock(&p->lock);
+	}
+	ao2_unlock(p);
+	ao2_ref(p, -1);
 	return res;
 }
 
@@ -292,6 +448,26 @@
 							p->chan->audiohooks = p->owner->audiohooks;
 							p->owner->audiohooks = audiohooks_swapper;
 						}
+
+						/* If any Caller ID was set, preserve it after masquerade like above. We must check
+						 * to see if Caller ID was set because otherwise we'll mistakingly copy info not
+						 * set from the dialplan and will overwrite the real channel Caller ID. The reason
+						 * for this whole preswapping action is because the Caller ID is set on the channel
+						 * thread (which is the to be masqueraded away local channel) before both local
+						 * channels are optimized away.
+						 */
+						if (p->owner->cid.cid_dnid || p->owner->cid.cid_num ||
+							p->owner->cid.cid_name || p->owner->cid.cid_ani ||
+							p->owner->cid.cid_rdnis || p->owner->cid.cid_pres ||  
+							p->owner->cid.cid_ani2 || p->owner->cid.cid_ton ||  
+							p->owner->cid.cid_tns) {
+
+							struct ast_callerid tmpcid;
+							tmpcid = p->owner->cid;
+							p->owner->cid = p->chan->_bridge->cid;
+							p->chan->_bridge->cid = tmpcid;
+						}
+
 						ast_app_group_update(p->chan, p->owner);
 						ast_channel_masquerade(p->owner, p->chan->_bridge);
 						ast_set_flag(p, LOCAL_ALREADY_MASQED);
@@ -319,7 +495,8 @@
 		return -1;
 
 	/* Just queue for delivery to the other side */
-	ast_mutex_lock(&p->lock);
+	ao2_lock(p);
+	ao2_ref(p, 1); /* ref for local_queue_frame */
 	isoutbound = IS_OUTBOUND(ast, p);
 	if (isoutbound && f && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO))
 		check_bridge(p);
@@ -329,8 +506,9 @@
 		ast_debug(1, "Not posting to queue since already masked on '%s'\n", ast->name);
 		res = 0;
 	}
-	if (!res)
-		ast_mutex_unlock(&p->lock);
+	ao2_unlock(p);
+	ao2_ref(p, -1);
+
 	return res;
 }
 
@@ -341,18 +519,27 @@
 	if (!p)
 		return -1;
 
-	ast_mutex_lock(&p->lock);
+	ao2_lock(p);
 
 	if ((p->owner != oldchan) && (p->chan != oldchan)) {
 		ast_log(LOG_WARNING, "Old channel wasn't %p but was %p/%p\n", oldchan, p->owner, p->chan);
-		ast_mutex_unlock(&p->lock);
+		ao2_unlock(p);
 		return -1;
 	}
 	if (p->owner == oldchan)
 		p->owner = newchan;
 	else
 		p->chan = newchan;
-	ast_mutex_unlock(&p->lock);
+
+	/* Do not let a masquerade cause a Local channel to be bridged to itself! */
+	if (!ast_check_hangup(newchan) && (p->owner->_bridge == p->chan || p->chan->_bridge == p->owner)) {
+		ast_log(LOG_WARNING, "You can not bridge a Local channel to itself!\n");
+		ao2_unlock(p);
+		ast_queue_hangup(newchan);
+		return -1;
+	}
+
+	ao2_unlock(p);
 	return 0;
 }
 
@@ -365,6 +552,8 @@
 
 	if (!p)
 		return -1;
+
+	ao2_ref(p, 1); /* ref for local_queue_frame */
 
 	/* If this is an MOH hold or unhold, do it on the Local channel versus real channel */
 	if (!ast_test_flag(p, LOCAL_MOH_PASSTHRU) && condition == AST_CONTROL_HOLD) {
@@ -373,15 +562,16 @@
 		ast_moh_stop(ast);
 	} else {
 		/* Queue up a frame representing the indication as a control frame */
-		ast_mutex_lock(&p->lock);
+		ao2_lock(p);
 		isoutbound = IS_OUTBOUND(ast, p);
 		f.subclass = condition;
 		f.data = (void*)data;
 		f.datalen = datalen;
-		if (!(res = local_queue_frame(p, isoutbound, &f, ast, 1)))
-			ast_mutex_unlock(&p->lock);
-	}
-
+		res = local_queue_frame(p, isoutbound, &f, ast, 1);
+		ao2_unlock(p);
+	}
+
+	ao2_ref(p, -1);
 	return res;
 }
 
@@ -395,11 +585,13 @@
 	if (!p)
 		return -1;
 
-	ast_mutex_lock(&p->lock);
+	ao2_ref(p, 1); /* ref for local_queue_frame */
+	ao2_lock(p);
 	isoutbound = IS_OUTBOUND(ast, p);
 	f.subclass = digit;
-	if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
-		ast_mutex_unlock(&p->lock);
+	res = local_queue_frame(p, isoutbound, &f, ast, 0);
+	ao2_unlock(p);
+	ao2_ref(p, -1);
 
 	return res;
 }
@@ -411,17 +603,20 @@
 	struct ast_frame f = { AST_FRAME_DTMF_END, };
 	int isoutbound;
 
-	if (!p)
-		return -1;
-
-	ast_mutex_lock(&p->lock);
+	if (!p) {
+		return -1;
+	}
+
+	ao2_lock(p);
+	ao2_ref(p, 1); /* ref for local_queue_frame */
 	isoutbound = IS_OUTBOUND(ast, p);
 	f.subclass = digit;
 	f.len = duration;
-	if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
-		ast_mutex_unlock(&p->lock);
-
-	return res;
+	res = local_queue_frame(p, isoutbound, &f, ast, 0);
+	ao2_unlock(p);
+	ao2_ref(p, -1);
+	return 0;
+
 }
 
 static int local_sendtext(struct ast_channel *ast, const char *text)
@@ -434,12 +629,14 @@
 	if (!p)
 		return -1;
 
-	ast_mutex_lock(&p->lock);
+	ao2_lock(p);
+	ao2_ref(p, 1); /* ref for local_queue_frame */
 	isoutbound = IS_OUTBOUND(ast, p);
 	f.data = (char *) text;
 	f.datalen = strlen(text) + 1;
-	if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
-		ast_mutex_unlock(&p->lock);
+	res = local_queue_frame(p, isoutbound, &f, ast, 0);
+	ao2_unlock(p);
+	ao2_ref(p, -1);
 	return res;
 }
 
@@ -453,13 +650,15 @@
 	if (!p)
 		return -1;
 	
-	ast_mutex_lock(&p->lock);
+	ao2_lock(p);
+	ao2_ref(p, 1); /* ref for local_queue_frame */
 	isoutbound = IS_OUTBOUND(ast, p);
 	f.subclass = subclass;
 	f.data = (char *)data;
 	f.datalen = datalen;
-	if (!(res = local_queue_frame(p, isoutbound, &f, ast, 0)))
-		ast_mutex_unlock(&p->lock);
+	res = local_queue_frame(p, isoutbound, &f, ast, 0);
+	ao2_unlock(p);
+	ao2_ref(p, -1);
 	return res;
 }
 
@@ -468,14 +667,14 @@
 static int local_call(struct ast_channel *ast, char *dest, int timeout)
 {
 	struct local_pvt *p = ast->tech_pvt;
-	int res;
+	int res = 0;
 	struct ast_var_t *varptr = NULL, *new;
 	size_t len, namelen;
 
 	if (!p)
 		return -1;
-	
-	ast_mutex_lock(&p->lock);
+
+	ao2_lock(p);
 
 	/*
 	 * Note that cid_num and cid_name aren't passed in the ast_channel_alloc
@@ -496,6 +695,11 @@
 	ast_cdr_update(p->chan);
 	p->chan->cdrflags = p->owner->cdrflags;
 
+	/* Make sure we inherit the ANSWERED_ELSEWHERE flag if it's set on the queue/dial call request in the dialplan */
+	if (ast_test_flag(ast, AST_FLAG_ANSWERED_ELSEWHERE)) {
+		ast_set_flag(p->chan, AST_FLAG_ANSWERED_ELSEWHERE);
+	}
+
 	/* copy the channel variables from the incoming channel to the outgoing channel */
 	/* Note that due to certain assumptions, they MUST be in the same order */
 	AST_LIST_TRAVERSE(&p->owner->varshead, varptr, entries) {
@@ -509,11 +713,17 @@
 	}
 	ast_channel_datastore_inherit(p->owner, p->chan);
 
+	if (!ast_exists_extension(p->chan, p->chan->context, p->chan->exten, 1, p->owner->cid.cid_num)) {
+		ast_log(LOG_NOTICE, "No such extension/context %s@%s while calling Local channel\n", p->chan->exten, p->chan->context);
+		ao2_unlock(p);
+		return -1;
+	}
+
 	/* Start switch on sub channel */
 	if (!(res = ast_pbx_start(p->chan)))
 		ast_set_flag(p, LOCAL_LAUNCHED_PBX);
 
-	ast_mutex_unlock(&p->lock);
+	ao2_unlock(p);
 	return res;
 }
 
@@ -522,37 +732,58 @@
 {
 	struct local_pvt *p = ast->tech_pvt;
 	int isoutbound;
-	struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
+	struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP};
+	/*
+	struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP, .data = ast->hangupcause };
+	*/
 	struct ast_channel *ochan = NULL;
-	int glaredetect = 0, res = 0;
 
 	if (!p)
 		return -1;
 
-	ast_mutex_lock(&p->lock);
-
-	if (p->chan && ast_test_flag(ast, AST_FLAG_ANSWERED_ELSEWHERE)) 
+	/* we MUST give the tech_pvt a ref here since we are unlocking the
+	 * channel during deadlock avoidance. */
+	ao2_ref(p, 1);
+
+	ao2_lock(p);
+
+	isoutbound = IS_OUTBOUND(ast, p);
+
+	if (p->chan && ast_test_flag(ast, AST_FLAG_ANSWERED_ELSEWHERE)) {
 		ast_set_flag(p->chan, AST_FLAG_ANSWERED_ELSEWHERE);
-	isoutbound = IS_OUTBOUND(ast, p);
+		ast_debug(2, "This local call has the ANSWERED_ELSEWHERE flag set.\n");
+	}
+
 	if (isoutbound) {
 		const char *status = pbx_builtin_getvar_helper(p->chan, "DIALSTATUS");
 		if ((status) && (p->owner)) {
 			/* Deadlock avoidance */
 			while (p->owner && ast_channel_trylock(p->owner)) {
-				ast_mutex_unlock(&p->lock);
+				ao2_unlock(p);
 				if (p->chan) {
 					ast_channel_unlock(p->chan);
 				}
-				usleep(1);
+				sched_yield();
 				if (p->chan) {
 					ast_channel_lock(p->chan);
 				}
-				ast_mutex_lock(&p->lock);
+				ao2_lock(p);
 			}
 			if (p->owner) {
+				p->owner->hangupcause = p->chan->hangupcause;
 				pbx_builtin_setvar_helper(p->owner, "CHANLOCALSTATUS", status);
 				ast_channel_unlock(p->owner);
 			}
+		}
+		if (!p->chan) {
+			/* chan was == to ast and was !NULL before deadlock avoidance started, if chan
+			 * is NULL now, then we should bail because that channel
+			 * hungup already. This is possible because we let go of the
+			 * lock given to the ast channel passed to this function during
+			 * deadlock avoidance. */
+			ao2_unlock(p);
+			ao2_ref(p, -1);
+			return 0;
 		}
 		p->chan = NULL;
 		ast_clear_flag(p, LOCAL_LAUNCHED_PBX);
@@ -560,53 +791,57 @@
 	} else {
 		ast_module_user_remove(p->u_owner);
 		while (p->chan && ast_channel_trylock(p->chan)) {
-				ast_mutex_unlock(&p->lock);
+				ao2_unlock(p);
 				if (p->owner) {
 					ast_channel_unlock(p->owner);
 				}
-				usleep(1);
+				sched_yield();
 				if (p->owner) {
 					ast_channel_lock(p->owner);
 				}
-				ast_mutex_lock(&p->lock);
-		}
-
-		p->owner = NULL;
+				ao2_lock(p);
+		}
 		if (p->chan) {
 			ast_queue_hangup(p->chan);
 			ast_channel_unlock(p->chan);
 		}
-	}
-	
+
+		if (!p->owner) {
+			/* owner was == to ast and was !NULL before deadlock avoidance started, if
+			 * owner is NULL now, then we should bail because that channel
+			 * hungup already. This is possible because we let go of the
+			 * lock given to the ast channel passed to this function during
+			 * deadlock avoidance. */
+			ao2_unlock(p);
+			ao2_ref(p, -1);
+			return 0;
+		}
+		p->owner = NULL;
+	}
+
 	ast->tech_pvt = NULL;
-	
+
 	if (!p->owner && !p->chan) {
-		/* Okay, done with the private part now, too. */
-		glaredetect = ast_test_flag(p, LOCAL_GLARE_DETECT);
-		/* If we have a queue holding, don't actually destroy p yet, but
-		   let local_queue do it. */
-		if (glaredetect)
-			ast_set_flag(p, LOCAL_CANCEL_QUEUE);
+		ao2_unlock(p);
+
 		/* Remove from list */
-		AST_LIST_LOCK(&locals);
-		AST_LIST_REMOVE(&locals, p, list);
-		AST_LIST_UNLOCK(&locals);
-		ast_mutex_unlock(&p->lock);
-		/* And destroy */
-		if (!glaredetect) {
-			p = local_pvt_destroy(p);
-		}
+		ao2_unlink(locals, p);
+		ao2_ref(p, -1);
 		return 0;
 	}
-	if (p->chan && !ast_test_flag(p, LOCAL_LAUNCHED_PBX))
+	if (p->chan && !ast_test_flag(p, LOCAL_LAUNCHED_PBX)) {
 		/* Need to actually hangup since there is no PBX */
 		ochan = p->chan;
-	else
-		res = local_queue_frame(p, isoutbound, &f, NULL, 1);
-	if (!res)
-		ast_mutex_unlock(&p->lock);
-	if (ochan)
+	} else {
+		local_queue_frame(p, isoutbound, &f, NULL, 1);
+	}
+
+	ao2_unlock(p);
+	if (ochan) {
 		ast_hangup(ochan);
+	}
+
+	ao2_ref(p, -1);
 	return 0;
 }
 
@@ -616,11 +851,11 @@
 	struct local_pvt *tmp = NULL;
 	char *c = NULL, *opts = NULL;
 
-	if (!(tmp = ast_calloc(1, sizeof(*tmp))))
+	if (!(tmp = ao2_alloc(sizeof(*tmp), NULL))) {
 		return NULL;
+	}
 
 	/* Initialize private structure information */
-	ast_mutex_init(&tmp->lock);
 	ast_copy_string(tmp->exten, data, sizeof(tmp->exten));
 
 	memcpy(&tmp->jb_conf, &g_jb_conf, sizeof(tmp->jb_conf));
@@ -638,6 +873,9 @@
 					"to use the 'j' option to enable the jitterbuffer\n");
 			}
 		}
+		if (strchr(opts, 'b')) {
+			ast_set_flag(tmp, LOCAL_BRIDGE);
+		}
 		if (strchr(opts, 'm')) {
 			ast_set_flag(tmp, LOCAL_MOH_PASSTHRU);
 		}
@@ -651,17 +889,21 @@
 
 	tmp->reqformat = format;
 
+#if 0
+	/* We can't do this check here, because we don't know the CallerID yet, and
+	 * the CallerID could potentially affect what step is actually taken (or
+	 * even if that step exists). */
 	if (!ast_exists_extension(NULL, tmp->context, tmp->exten, 1, NULL)) {
 		ast_log(LOG_NOTICE, "No such extension/context %s@%s creating local channel\n", tmp->exten, tmp->context);
 		tmp = local_pvt_destroy(tmp);
 	} else {
+#endif
 		/* Add to list */
-		AST_LIST_LOCK(&locals);
-		AST_LIST_INSERT_HEAD(&locals, tmp, list);
-		AST_LIST_UNLOCK(&locals);
-	}
-	
-	return tmp;
+		ao2_link(locals, tmp);
+#if 0
+	}
+#endif
+	return tmp; /* this is returned with a ref */
 }
 
 /*! \brief Start new local channel */
@@ -737,11 +979,9 @@
 	/* Allocate a new private structure and then Asterisk channel */
 	if ((p = local_alloc(data, format))) {
 		if (!(chan = local_new(p, AST_STATE_DOWN))) {
-			AST_LIST_LOCK(&locals);
-			AST_LIST_REMOVE(&locals, p, list);
-			AST_LIST_UNLOCK(&locals);
-			p = local_pvt_destroy(p);
-		}
+			ao2_unlink(locals, p);
+		}
+		ao2_ref(p, -1); /* kill the ref from the alloc */
 	}
 
 	return chan;
@@ -751,6 +991,7 @@
 static char *locals_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	struct local_pvt *p = NULL;
+	struct ao2_iterator it;
 
 	switch (cmd) {
 	case CLI_INIT:
@@ -766,16 +1007,19 @@
 	if (a->argc != 3)
 		return CLI_SHOWUSAGE;
 
-	AST_LIST_LOCK(&locals);
-	if (!AST_LIST_EMPTY(&locals)) {
-		AST_LIST_TRAVERSE(&locals, p, list) {
-			ast_mutex_lock(&p->lock);
-			ast_cli(a->fd, "%s -- %s@%s\n", p->owner ? p->owner->name : "<unowned>", p->exten, p->context);
-			ast_mutex_unlock(&p->lock);
-		}
-	} else
+	if (ao2_container_count(locals) == 0) {
 		ast_cli(a->fd, "No local channels in use\n");
-	AST_LIST_UNLOCK(&locals);
+		return RESULT_SUCCESS;
+	}
+
+	it = ao2_iterator_init(locals, 0);
+	while ((p = ao2_iterator_next(&it))) {
+		ao2_lock(p);
+		ast_cli(a->fd, "%s -- %s@%s\n", p->owner ? p->owner->name : "<unowned>", p->exten, p->context);
+		ao2_unlock(p);
+		ao2_ref(p, -1);
+	}
+	ao2_iterator_destroy(&it);
 
 	return CLI_SUCCESS;
 }
@@ -784,12 +1028,22 @@
 	AST_CLI_DEFINE(locals_show, "List status of local channels"),
 };
 
+static int locals_cmp_cb(void *obj, void *arg, int flags)
+{
+	return (obj == arg) ? CMP_MATCH : 0;
+}
+
 /*! \brief Load module into PBX, register channel */
 static int load_module(void)
 {
+	if (!(locals = ao2_container_alloc(BUCKET_SIZE, NULL, locals_cmp_cb))) {
+		return AST_MODULE_LOAD_FAILURE;
+	}
+
 	/* Make sure we can register our channel type */
 	if (ast_channel_register(&local_tech)) {
 		ast_log(LOG_ERROR, "Unable to register channel class 'Local'\n");
+		ao2_ref(locals, -1);
 		return AST_MODULE_LOAD_FAILURE;
 	}
 	ast_cli_register_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
@@ -800,22 +1054,23 @@
 static int unload_module(void)
 {
 	struct local_pvt *p = NULL;
+	struct ao2_iterator it;
 
 	/* First, take us out of the channel loop */
 	ast_cli_unregister_multiple(cli_local, sizeof(cli_local) / sizeof(struct ast_cli_entry));
 	ast_channel_unregister(&local_tech);
-	if (!AST_LIST_LOCK(&locals)) {
-		/* Hangup all interfaces if they have an owner */
-		AST_LIST_TRAVERSE(&locals, p, list) {
-			if (p->owner)
-				ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
-		}
-		AST_LIST_UNLOCK(&locals);
-	} else {
-		ast_log(LOG_WARNING, "Unable to lock the monitor\n");
-		return -1;
-	}		
+
+	it = ao2_iterator_init(locals, 0);
+	while ((p = ao2_iterator_next(&it))) {
+		if (p->owner) {
+			ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
+		}
+		ao2_ref(p, -1);
+	}
+	ao2_iterator_destroy(&it);
+	ao2_ref(locals, -1);
 	return 0;
 }
 
 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Local Proxy Channel (Note: used internally by other modules)");
+




More information about the asterisk-commits mailing list