]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Make some deadlock related fixes. These bugs were discovered and reported
authorRussell Bryant <russell@russellbryant.com>
Mon, 28 Jan 2008 17:15:41 +0000 (17:15 +0000)
committerRussell Bryant <russell@russellbryant.com>
Mon, 28 Jan 2008 17:15:41 +0000 (17:15 +0000)
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/branches/1.4@100581 65c4cc65-6c06-0410-ace0-fbb531ad65f3

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

index cbf7f7f48584f5c61919162d17f37358114e636c..3795be9c6bab8913692438383920e2662038432d 100644 (file)
@@ -154,8 +154,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;
 
@@ -173,27 +171,28 @@ retrylock:
                ast_clear_flag(p, LOCAL_GLARE_DETECT);
                return 0;
        }
-       if (ast_mutex_trylock(&other->lock)) {
-               /* Failed to lock.  Release main lock and try again */
-               ast_mutex_unlock(&p->lock);
-               if (us) {
-                       if (ast_mutex_unlock(&us->lock)) {
-                               ast_log(LOG_WARNING, "%s wasn't locked while sending %d/%d\n",
-                                       us->name, f->frametype, f->subclass);
-                               us = NULL;
-                       }
+
+       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);
                }
-               /* Wait just a bit */
-               usleep(1);
-               /* Only we can destroy ourselves, so we can't disappear here */
-               if (us)
-                       ast_mutex_lock(&us->lock);
-               ast_mutex_lock(&p->lock);
-               goto retrylock;
+       } else {
+               ast_channel_lock(other);
        }
+
        ast_queue_frame(other, f);
-       ast_mutex_unlock(&other->lock);
+
+       ast_channel_unlock(other);
+
+       ast_mutex_lock(&p->lock);
+
        ast_clear_flag(p, LOCAL_GLARE_DETECT);
+
        return 0;
 }
 
index 23f1409f7b87d1ee918b28fd30a54512f4c02030..429308ee067358699777246cfa6798da2b7fefba 100644 (file)
@@ -142,7 +142,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);
 };
 
index 40aa67f7ba51c1cbac36174e149a4673f2861240..d5af9cb2ee874d10dd881064d081fb54ffe16544 100644 (file)
@@ -1556,16 +1556,23 @@ 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) {
                if (option_debug)
                        ast_log(LOG_DEBUG, "Auto-deactivating generator\n");
                ast_deactivate_generator(chan);
        }
+
        return 0;
 }
 
@@ -1975,13 +1982,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