Merged revisions 292868 via svnmerge from
authorDavid Vossel <dvossel@digium.com>
Mon, 25 Oct 2010 19:11:42 +0000 (19:11 +0000)
committerDavid Vossel <dvossel@digium.com>
Mon, 25 Oct 2010 19:11:42 +0000 (19:11 +0000)
https://origsvn.digium.com/svn/asterisk/branches/1.8

................
  r292868 | dvossel | 2010-10-25 14:07:50 -0500 (Mon, 25 Oct 2010) | 39 lines

  Merged revisions 292867 via svnmerge from
  https://origsvn.digium.com/svn/asterisk/branches/1.6.2

  ................
    r292867 | dvossel | 2010-10-25 14:06:21 -0500 (Mon, 25 Oct 2010) | 32 lines

    Merged revisions 292866 via svnmerge from
    https://origsvn.digium.com/svn/asterisk/branches/1.4

    ........
      r292866 | dvossel | 2010-10-25 14:05:07 -0500 (Mon, 25 Oct 2010) | 27 lines

      This patch turns chan_local pvts into astobj2 objects.

      chan_local does some dangerous things involving deadlock avoidance.
      tech_pvt functions like hangup and queue_frame are provided with a
      locked channel upon entry.  Those functions are completely safe as
      long as you don't attempt to give up that channel lock, but that is
      impossible to guarantee due to the required deadlock avoidance necessary
      to lock both the tech_pvt and both channels involved.

      In the past, we have tried to account for this by doing things like
      setting a "glare" flag that indicates what function should destroy the
      pvt.  This was used in local_hangup and local_queue_frame to decided
      who should destroy the pvt if they collided in separate threads.  I
      have removed the need to do this by converting all chan_local tech_pvts
      to astobj2.  This means we can ref a pvt before deadlock avoidance
      and not have to worry about that pvt possibly getting destroyed under
      us.  It also cleans up where we destroy the tech_pvt.  The only unlink
      from the tech_pvt container occurs in local_hangup now, which is where
      it should occur.

      Since there still may be thread collisions on some functions like
      local_hangup after deadlock avoidance, I have added some checks to detect
      those collisions and exit appropriately.  I think this patch is going to
      solve quite a bit of weirdness we have had with local channels in the past.
    ........
  ................
................

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@292869 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_local.c

index 6cee48f..9c90d0f 100644 (file)
@@ -48,6 +48,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/manager.h"
 #include "asterisk/stringfields.h"
 #include "asterisk/devicestate.h"
+#include "asterisk/astobj2.h"
 
 /*** DOCUMENTATION
        <manager name="LocalOptimizeAway" language="en_US">
@@ -72,6 +73,13 @@ 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,
        .max_size = -1,
@@ -130,7 +138,6 @@ static const struct ast_channel_tech local_tech = {
 
 */
 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 */
@@ -140,18 +147,13 @@ struct local_pvt {
        struct ast_channel *chan;               /*!< Outbound channel - PBX is run here */
        struct ast_module_user *u_owner;        /*!< reference to keep the module loaded while in use */
        struct ast_module_user *u_chan;         /*!< reference to keep the module loaded while in use */
-       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_BRIDGE          (1 << 5) /*!< Report back the "true" channel as being bridged to */
-#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 */
 
 static int local_setoption(struct ast_channel *chan, int option, void * data, int datalen)
 {
@@ -182,7 +184,7 @@ startover:
                return -1;
        }
 
-       while (ast_mutex_trylock(&p->lock)) {
+       while (ao2_trylock(p)) {
                ast_channel_unlock(chan);
                sched_yield();
                ast_channel_lock(chan);
@@ -197,14 +199,14 @@ startover:
        otherchan = (write_info->chan == p->owner) ? p->chan : p->owner;
 
        if (!otherchan || otherchan == write_info->chan) {
-               ast_mutex_unlock(&p->lock);
+               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)) {
-               ast_mutex_unlock(&p->lock);
+               ao2_unlock(p);
                ast_channel_unlock(chan);
                goto startover;
        }
@@ -212,7 +214,7 @@ startover:
        res = write_info->write_fn(otherchan, write_info->function, write_info->data, write_info->value);
 
        ast_channel_unlock(otherchan);
-       ast_mutex_unlock(&p->lock);
+       ao2_unlock(p);
        ast_channel_unlock(chan);
 
        return res;
@@ -225,6 +227,7 @@ static int local_devicestate(void *data)
        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);
@@ -244,28 +247,21 @@ static int local_devicestate(void *data)
                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;
                }
+               ao2_ref(lp, -1);
        }
-       AST_LIST_UNLOCK(&locals);
+       ao2_iterator_destroy(&it);
 
        return res;
 }
 
-/*!
- * \note Assumes the pvt is no longer in the pvts list
- */
-static struct local_pvt *local_pvt_destroy(struct local_pvt *pvt)
-{
-       ast_mutex_destroy(&pvt->lock);
-       ast_free(pvt);
-       return NULL;
-}
-
 /*! \brief Return the bridged channel of a Local channel */
 static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
 {
@@ -278,7 +274,7 @@ static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct
                return NULL;
        }
 
-       ast_mutex_lock(&p->lock);
+       ao2_lock(p);
 
        if (ast_test_flag(p, LOCAL_BRIDGE)) {
                /* Find the opposite channel */
@@ -292,7 +288,7 @@ static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct
                }
        }
 
-       ast_mutex_unlock(&p->lock);
+       ao2_unlock(p);
 
        return bridged;
 }
@@ -312,17 +308,19 @@ static int local_queryoption(struct ast_channel *ast, int option, void *data, in
                return -1;
        }
 
-       ast_mutex_lock(&p->lock);
+       ao2_lock(p);
        chan = IS_OUTBOUND(ast, p) ? p->owner : p->chan;
 
 try_again:
        if (!chan) {
-               ast_mutex_unlock(&p->lock);
+               ao2_unlock(p);
                return -1;
        }
 
        if (ast_channel_trylock(chan)) {
-               DEADLOCK_AVOIDANCE(&p->lock);
+               ao2_unlock(p);
+               sched_yield();
+               ao2_lock(p);
                chan = IS_OUTBOUND(ast, p) ? p->owner : p->chan;
                goto try_again;
        }
@@ -330,25 +328,35 @@ try_again:
        bridged = ast_bridged_channel(chan);
        if (!bridged) {
                /* can't query channel unless we are bridged */
-               ast_mutex_unlock(&p->lock);
+               ao2_unlock(p);
                ast_channel_unlock(chan);
                return -1;
        }
 
        if (ast_channel_trylock(bridged)) {
                ast_channel_unlock(chan);
-               DEADLOCK_AVOIDANCE(&p->lock);
+               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);
-       ast_mutex_unlock(&p->lock);
+       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 int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_frame *f, 
        struct ast_channel *us, int us_locked)
 {
@@ -366,41 +374,24 @@ static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_fra
                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)) {
                int res;
-               if ((res = ast_mutex_unlock(&p->lock))) {
+               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) {
                if (f->frametype == AST_FRAME_CONTROL && f->subclass.integer == AST_CONTROL_RINGING) {
                        ast_setstate(other, AST_STATE_RINGING);
@@ -409,8 +400,6 @@ static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_fra
                ast_channel_unlock(other);
        }
 
-       ast_clear_flag(p, LOCAL_GLARE_DETECT);
-
        return 0;
 }
 
@@ -423,16 +412,18 @@ static int local_answer(struct ast_channel *ast)
        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;
 }
 
@@ -535,7 +526,8 @@ static int local_write(struct ast_channel *ast, struct ast_frame *f)
                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);
@@ -545,8 +537,9 @@ static int local_write(struct ast_channel *ast, struct ast_frame *f)
                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;
 }
 
@@ -557,11 +550,11 @@ static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
        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)
@@ -572,12 +565,12 @@ static int local_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
        /* 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");
-               ast_mutex_unlock(&p->lock);
+               ao2_unlock(p);
                ast_queue_hangup(newchan);
                return -1;
        }
 
-       ast_mutex_unlock(&p->lock);
+       ao2_unlock(p);
        return 0;
 }
 
@@ -591,6 +584,8 @@ static int local_indicate(struct ast_channel *ast, int condition, const void *da
        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) {
                ast_moh_start(ast, data, NULL);
@@ -606,7 +601,7 @@ static int local_indicate(struct ast_channel *ast, int condition, const void *da
                 * we need to transmit the collected connected line information instead of whatever
                 * happens to be in this control frame. The same applies for redirecting information, which
                 * is why it is handled here as well.*/
-               ast_mutex_lock(&p->lock);
+               ao2_lock(p);
                isoutbound = IS_OUTBOUND(ast, p);
                if (isoutbound) {
                        this_channel = p->chan;
@@ -627,23 +622,21 @@ static int local_indicate(struct ast_channel *ast, int condition, const void *da
                        }
                        f.subclass.integer = condition;
                        f.data.ptr = frame_data;
-                       if (!(res = local_queue_frame(p, isoutbound, &f, ast, 1))) {
-                               ast_mutex_unlock(&p->lock);
-                       }
-               } else {
-                       ast_mutex_unlock(&p->lock);
+                       res = local_queue_frame(p, isoutbound, &f, ast, 1);
                }
+               ao2_unlock(p);
        } 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.integer = condition;
                f.data.ptr = (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;
 }
 
@@ -657,11 +650,13 @@ static int local_digit_begin(struct ast_channel *ast, char digit)
        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.integer = 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;
 }
@@ -676,12 +671,14 @@ static int local_digit_end(struct ast_channel *ast, char digit, unsigned int dur
        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.integer = digit;
        f.len = duration;
-       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;
 }
@@ -696,12 +693,14 @@ static int local_sendtext(struct ast_channel *ast, const char *text)
        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.ptr = (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;
 }
 
@@ -714,14 +713,17 @@ static int local_sendhtml(struct ast_channel *ast, int subclass, const char *dat
 
        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.integer = subclass;
        f.data.ptr = (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;
 }
 
@@ -736,25 +738,40 @@ static int local_call(struct ast_channel *ast, char *dest, int timeout)
        char *reduced_dest = ast_strdupa(dest);
        char *slash;
 
-       if (!p)
+       if (!p || p->owner != ast) {
                return -1;
+       }
+
+       /* since we are letting go of channel locks that were locked coming into
+        * this function, then we need to give the tech pvt a ref */
+       ao2_ref(p, 1);
 
-       /* If you value your sanity, please don't look at this code */
-start_over:
-       while (ast_channel_trylock(p->chan)) {
-               ast_channel_unlock(p->owner);
-               usleep(1);
-               ast_channel_lock(p->owner);
+       while (ao2_trylock(p)) {
+               ast_channel_unlock(ast);
+               sched_yield();
+               ast_channel_lock(ast);
+       }
+       while ((p->chan && p->owner) && ast_channel_trylock(p->chan)) {
+               ao2_unlock(p);
+               if (p->owner) {
+                       ast_channel_unlock(p->owner);
+               }
+               sched_yield();
+               if (p->owner) {
+                       ast_channel_lock(p->owner);
+               }
+               ao2_lock(p);
        }
 
-       /* p->owner and p->chan are locked now. Let's get p locked */
-       if (ast_mutex_trylock(&p->lock)) {
-               /* @#$&$@ */
-               ast_channel_unlock(p->chan);
-               ast_channel_unlock(p->owner);
-               usleep(1);
-               ast_channel_lock(p->owner);
-               goto start_over;
+       if (!p->owner || !p->chan) {
+               /* someone went away during the locking madness.
+                * time to bail. */
+               if (p->chan) {
+                       ast_channel_unlock(p->chan);
+               }
+               ao2_unlock(p);
+               ao2_ref(p,-1);
+               return -1;
        }
 
        /*
@@ -781,8 +798,9 @@ start_over:
        if (!ast_exists_extension(NULL, p->chan->context, p->chan->exten, 1,
                S_COR(p->owner->caller.id.number.valid, p->owner->caller.id.number.str, NULL))) {
                ast_log(LOG_NOTICE, "No such extension/context %s@%s while calling Local channel\n", p->chan->exten, p->chan->context);
-               ast_mutex_unlock(&p->lock);
+               ao2_unlock(p);
                ast_channel_unlock(p->chan);
+               ao2_ref(p, -1);
                return -1;
        }
 
@@ -816,8 +834,9 @@ start_over:
        if (!(res = ast_pbx_start(p->chan)))
                ast_set_flag(p, LOCAL_LAUNCHED_PBX);
 
-       ast_mutex_unlock(&p->lock);
+       ao2_unlock(p);
        ast_channel_unlock(p->chan);
+       ao2_ref(p, -1);
        return res;
 }
 
@@ -828,12 +847,15 @@ static int local_hangup(struct ast_channel *ast)
        int isoutbound;
        struct ast_frame f = { AST_FRAME_CONTROL, { AST_CONTROL_HANGUP }, .data.uint32 = ast->hangupcause };
        struct ast_channel *ochan = NULL;
-       int glaredetect = 0, res = 0;
 
        if (!p)
                return -1;
 
-       ast_mutex_lock(&p->lock);
+       /* 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);
 
@@ -847,74 +869,88 @@ static int local_hangup(struct ast_channel *ast)
                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) {
                                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);
                ast_module_user_remove(p->u_chan);
        } 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);
+                               ao2_lock(p);
                }
-
-               p->owner = NULL;
                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;
 }
 
@@ -924,11 +960,11 @@ static struct local_pvt *local_alloc(const char *data, int format)
        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));
@@ -972,14 +1008,11 @@ static struct local_pvt *local_alloc(const char *data, int format)
        } else {
 #endif
                /* Add to list */
-               AST_LIST_LOCK(&locals);
-               AST_LIST_INSERT_HEAD(&locals, tmp, list);
-               AST_LIST_UNLOCK(&locals);
+               ao2_link(locals, tmp);
 #if 0
        }
 #endif
-       
-       return tmp;
+       return tmp; /* this is returned with a ref */
 }
 
 /*! \brief Start new local channel */
@@ -1054,15 +1087,13 @@ static struct ast_channel *local_request(const char *type, format_t format, cons
        /* Allocate a new private structure and then Asterisk channel */
        if ((p = local_alloc(data, format))) {
                if (!(chan = local_new(p, AST_STATE_DOWN, requestor ? requestor->linkedid : NULL))) {
-                       AST_LIST_LOCK(&locals);
-                       AST_LIST_REMOVE(&locals, p, list);
-                       AST_LIST_UNLOCK(&locals);
-                       p = local_pvt_destroy(p);
+                       ao2_unlink(locals, p);
                }
                if (chan && ast_channel_cc_params_init(chan, requestor ? ast_channel_get_cc_config_params((struct ast_channel *)requestor) : NULL)) {
                        chan = ast_channel_release(chan);
-                       p = local_pvt_destroy(p);
+                       ao2_unlink(locals, p);
                }
+               ao2_ref(p, -1); /* kill the ref from the alloc */
        }
 
        return chan;
@@ -1072,6 +1103,7 @@ static struct ast_channel *local_request(const char *type, format_t format, cons
 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:
@@ -1087,16 +1119,19 @@ static char *locals_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *
        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;
 }
@@ -1111,6 +1146,7 @@ static int manager_optimize_away(struct mansession *s, const struct message *m)
        struct local_pvt *p, *tmp = NULL;
        struct ast_channel *c;
        int found = 0;
+       struct ao2_iterator it;
 
        channel = astman_get_header(m, "Channel");
 
@@ -1129,22 +1165,19 @@ static int manager_optimize_away(struct mansession *s, const struct message *m)
        ast_channel_unref(c);
        c = NULL;
 
-       if (AST_LIST_LOCK(&locals)) {
-               astman_send_error(s, m, "Unable to lock the monitor");
-               return 0;
-       }
-
-
-       AST_LIST_TRAVERSE(&locals, tmp, list) {
+       it = ao2_iterator_init(locals, 0);
+       while ((tmp = ao2_iterator_next(&it))) {
                if (tmp == p) {
-                       ast_mutex_lock(&tmp->lock);
+                       ao2_lock(tmp);
                        found = 1;
                        ast_clear_flag(tmp, LOCAL_NO_OPTIMIZATION);
-                       ast_mutex_unlock(&tmp->lock);
+                       ao2_unlock(tmp);
+                       ao2_ref(tmp, -1);
                        break;
                }
+               ao2_ref(tmp, -1);
        }
-       AST_LIST_UNLOCK(&locals);
+       ao2_iterator_destroy(&it);
 
        if (found) {
                astman_send_ack(s, m, "Queued channel to be optimized away");
@@ -1156,12 +1189,22 @@ static int manager_optimize_away(struct mansession *s, const struct message *m)
 }
 
 
+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));
@@ -1174,22 +1217,23 @@ static int load_module(void)
 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_manager_unregister("LocalOptimizeAway");
        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);
+
+       it = ao2_iterator_init(locals, 0);
+       while ((p = ao2_iterator_next(&it))) {
+               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;
-       }               
+               ao2_ref(p, -1);
+       }
+       ao2_iterator_destroy(&it);
+       ao2_ref(locals, -1);
+
        return 0;
 }