]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
Fix race condition that can cause important control frames (such as a hangup) to...
authorMark Michelson <mmichelson@digium.com>
Fri, 2 Mar 2012 00:59:18 +0000 (00:59 +0000)
committerMark Michelson <mmichelson@digium.com>
Fri, 2 Mar 2012 00:59:18 +0000 (00:59 +0000)
This takes two actions.

1. Move the reading of the alertpipe in __ast_read() to immediately before the
removal of frames from the readq. This means we won't do something silly like
read from the alertpipe, then ignore the fact that there's a frame to get from
the readq since channel's fdno is the AST_TIMING_FD.

2. When ast_settimeout() sets the rate to 0 and the timingfunc to NULL, if the
channel's fdno is the AST_TIMING_FD, then set the fdno to -1. This is because
if the rate is 0 and the timingfunc is NULL, it means that the channel's timing
fd is being invalidated, so any pending reads should not occur.

This may actually solve more issues than the referenced one below, but it's not
known at this time for sure.

(closes issue ASTERISK-19223)
reported by Frank-Michael Wittig

Review: https://reviewboard.asterisk.org/r/1779

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

main/channel.c

index 273f2c9b347ad4ef9e793f41602361d3e42bfaf3..173dfc642329f65022afb7445ca7e81dc382e25f 100644 (file)
@@ -3507,6 +3507,16 @@ int ast_settimeout(struct ast_channel *c, unsigned int rate, int (*func)(const v
        c->timingfunc = func;
        c->timingdata = data;
 
+       if (func == NULL && rate == 0 && c->fdno == AST_TIMING_FD) {
+               /* Clearing the timing func and setting the rate to 0
+                * means that we don't want to be reading from the timingfd
+                * any more. Setting c->fdno to -1 means we won't have any
+                * errant reads from the timingfd, meaning we won't potentially
+                * miss any important frames.
+                */
+               c->fdno = -1;
+       }
+
        ast_channel_unlock(c);
 
        return res;
@@ -3779,27 +3789,6 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
        }
 
        prestate = chan->_state;
-
-       /* Read and ignore anything on the alertpipe, but read only
-          one sizeof(blah) per frame that we send from it */
-       if (chan->alertpipe[0] > -1) {
-               int flags = fcntl(chan->alertpipe[0], F_GETFL);
-               /* For some odd reason, the alertpipe occasionally loses nonblocking status,
-                * which immediately causes a deadlock scenario.  Detect and prevent this. */
-               if ((flags & O_NONBLOCK) == 0) {
-                       ast_log(LOG_ERROR, "Alertpipe on channel %s lost O_NONBLOCK?!!\n", chan->name);
-                       if (fcntl(chan->alertpipe[0], F_SETFL, flags | O_NONBLOCK) < 0) {
-                               ast_log(LOG_WARNING, "Unable to set alertpipe nonblocking! (%d: %s)\n", errno, strerror(errno));
-                               f = &ast_null_frame;
-                               goto done;
-                       }
-               }
-               if (read(chan->alertpipe[0], &blah, sizeof(blah)) < 0) {
-                       if (errno != EINTR && errno != EAGAIN)
-                               ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
-               }
-       }
-
        if (chan->timingfd > -1 && chan->fdno == AST_TIMING_FD) {
                enum ast_timer_event res;
 
@@ -3848,6 +3837,27 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                goto done;
        }
 
+       /* Read and ignore anything on the alertpipe, but read only
+          one sizeof(blah) per frame that we send from it */
+       if (chan->alertpipe[0] > -1) {
+               int flags = fcntl(chan->alertpipe[0], F_GETFL);
+               /* For some odd reason, the alertpipe occasionally loses nonblocking status,
+                * which immediately causes a deadlock scenario.  Detect and prevent this. */
+               if ((flags & O_NONBLOCK) == 0) {
+                       ast_log(LOG_ERROR, "Alertpipe on channel %s lost O_NONBLOCK?!!\n", chan->name);
+                       if (fcntl(chan->alertpipe[0], F_SETFL, flags | O_NONBLOCK) < 0) {
+                               ast_log(LOG_WARNING, "Unable to set alertpipe nonblocking! (%d: %s)\n", errno, strerror(errno));
+                               f = &ast_null_frame;
+                               goto done;
+                       }
+               }
+               if (read(chan->alertpipe[0], &blah, sizeof(blah)) < 0) {
+                       if (errno != EINTR && errno != EAGAIN)
+                               ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
+               }
+       }
+
+
        /* Check for pending read queue */
        if (!AST_LIST_EMPTY(&chan->readq)) {
                int skip_dtmf = should_skip_dtmf(chan);