Merged revisions 100581 via svnmerge from
authorRussell Bryant <russell@russellbryant.com>
Mon, 28 Jan 2008 17:21:24 +0000 (17:21 +0000)
committerRussell Bryant <russell@russellbryant.com>
Mon, 28 Jan 2008 17:21:24 +0000 (17:21 +0000)
https://origsvn.digium.com/svn/asterisk/branches/1.4

........
r100581 | russell | 2008-01-28 11:15:41 -0600 (Mon, 28 Jan 2008) | 9 lines

Make some deadlock related fixes.  These bugs were discovered and reported
internally at Digium by Steve Pitts.
 - Fix up chan_local to ensure that the channel lock is held before the local
   pvt lock.
 - Don't hold the channel lock when executing the timing function, as it can
   cause a deadlock when using chan_local.  This actually changes the code back
   to be how it was before the change for issue #10765.  But, I added some other
   locking that I think will prevent the problem reported there, as well.

........

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

channels/chan_local.c
include/asterisk/channel.h
main/channel.c

index 130068e..ae55aaf 100644 (file)
@@ -161,8 +161,6 @@ static int local_queue_frame(struct local_pvt *p, int isoutbound, struct ast_fra
 {
        struct ast_channel *other = NULL;
 
-retrylock:             
-
        /* Recalculate outbound channel */
        other = isoutbound ? p->owner : p->chan;
 
@@ -180,27 +178,28 @@ retrylock:
                ast_clear_flag(p, LOCAL_GLARE_DETECT);
                return 0;
        }
-       if (ast_channel_trylock(other)) {
-               /* Failed to lock.  Release main lock and try again */
-               ast_mutex_unlock(&p->lock);
-               if (us) {
-                       if (ast_channel_unlock(us)) {
-                               ast_log(LOG_WARNING, "%s wasn't locked while sending %d/%d\n",
-                                       us->name, f->frametype, f->subclass);
-                               us = NULL;
-                       }
-               }
-               /* Wait just a bit */
-               usleep(1);
-               /* Only we can destroy ourselves, so we can't disappear here */
-               if (us)
+
+       ast_mutex_unlock(&p->lock);
+
+       /* Ensure that we have both channels locked */
+       if (us) {
+               while (ast_channel_trylock(other)) {
+                       ast_channel_unlock(us);
+                       usleep(1);
                        ast_channel_lock(us);
-               ast_mutex_lock(&p->lock);
-               goto retrylock;
+               }
+       } else {
+               ast_channel_lock(other);
        }
+
        ast_queue_frame(other, f);
+
        ast_channel_unlock(other);
+
+       ast_mutex_lock(&p->lock);
+
        ast_clear_flag(p, LOCAL_GLARE_DETECT);
+
        return 0;
 }
 
index 58fec7a..c1c9cc2 100644 (file)
@@ -177,7 +177,10 @@ typedef unsigned long long ast_group_t;
 struct ast_generator {
        void *(*alloc)(struct ast_channel *chan, void *params);
        void (*release)(struct ast_channel *chan, void *data);
-       /*! This function gets called with the channel locked */
+       /*! This function gets called with the channel unlocked, but is called in
+        *  the context of the channel thread so we know the channel is not going
+        *  to disappear.  This callback is responsible for locking the channel as
+        *  necessary. */
        int (*generate)(struct ast_channel *chan, void *data, int len, int samples);
        /*! This gets called when DTMF_END frames are read from the channel */
        void (*digit)(struct ast_channel *chan, char digit);
index 3553a3f..446c0d4 100644 (file)
@@ -1564,15 +1564,22 @@ static int generator_force(const void *data)
        int res;
        int (*generate)(struct ast_channel *chan, void *tmp, int datalen, int samples);
        struct ast_channel *chan = (struct ast_channel *)data;
+
+       ast_channel_lock(chan);
        tmp = chan->generatordata;
        chan->generatordata = NULL;
        generate = chan->generator->generate;
+       ast_channel_unlock(chan);
+
        res = generate(chan, tmp, 0, 160);
+
        chan->generatordata = tmp;
+
        if (res) {
                ast_debug(1, "Auto-deactivating generator\n");
                ast_deactivate_generator(chan);
        }
+
        return 0;
 }
 
@@ -2200,13 +2207,17 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                } else if (blah == ZT_EVENT_TIMER_EXPIRED) {
                        ioctl(chan->timingfd, ZT_TIMERACK, &blah);
                        if (chan->timingfunc) {
-                               chan->timingfunc(chan->timingdata);
+                               /* save a copy of func/data before unlocking the channel */
+                               int (*func)(const void *) = chan->timingfunc;
+                               void *data = chan->timingdata;
+                               ast_channel_unlock(chan);
+                               func(data);
                        } else {
                                blah = 0;
                                ioctl(chan->timingfd, ZT_TIMERCONFIG, &blah);
                                chan->timingdata = NULL;
+                               ast_channel_unlock(chan);
                        }
-                       ast_channel_unlock(chan);
                        /* cannot 'goto done' because the channel is already unlocked */
                        return &ast_null_frame;
                } else