Merge Rizzo's waitfor update (bug #4584)
authorMark Spencer <markster@digium.com>
Mon, 30 Jan 2006 03:13:33 +0000 (03:13 +0000)
committerMark Spencer <markster@digium.com>
Mon, 30 Jan 2006 03:13:33 +0000 (03:13 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@8877 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channel.c
channels/chan_agent.c
channels/chan_features.c
include/asterisk/channel.h

index 501baa8..ee80b7d 100644 (file)
--- a/channel.c
+++ b/channel.c
@@ -604,7 +604,10 @@ struct ast_channel *ast_channel_alloc(int needqueue)
                return NULL;
        }
        
-       for (x=0; x<AST_MAX_FDS - 1; x++)
+       /* Don't bother initializing the last two FD here, because they
+          will *always* be set just a few lines down (AST_TIMING_FD,
+          AST_ALERT_FD). */
+       for (x=0; x<AST_MAX_FDS - 2; x++)
                tmp->fds[x] = -1;
 
 #ifdef ZAPTEL_OPTIMIZATIONS
@@ -636,9 +639,9 @@ struct ast_channel *ast_channel_alloc(int needqueue)
                tmp->alertpipe[0] = tmp->alertpipe[1] = -1;
 
        /* Always watch the alertpipe */
-       tmp->fds[AST_MAX_FDS-1] = tmp->alertpipe[0];
+       tmp->fds[AST_ALERT_FD] = tmp->alertpipe[0];
        /* And timing pipe */
-       tmp->fds[AST_MAX_FDS-2] = tmp->timingfd;
+       tmp->fds[AST_TIMING_FD] = tmp->timingfd;
        strcpy(tmp->name, "**Unknown**");
        /* Initial state */
        tmp->_state = AST_STATE_DOWN;
@@ -1414,6 +1417,7 @@ void ast_deactivate_generator(struct ast_channel *chan)
                        chan->generator->release(chan, chan->generatordata);
                chan->generatordata = NULL;
                chan->generator = NULL;
+               chan->fds[AST_GENERATOR_FD] = -1;
                ast_clear_flag(chan, AST_FLAG_WRITE_INT);
                ast_settimeout(chan, 0, NULL, NULL);
        }
@@ -1470,56 +1474,8 @@ int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen,
 /*! \brief Wait for x amount of time on a file descriptor to have input.  */
 int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception)
 {
-       struct timeval start = { 0 , 0 };
-       int res;
-       int x, y;
        int winner = -1;
-       int spoint;
-       struct pollfd *pfds;
-       
-       pfds = alloca(sizeof(struct pollfd) * n);
-       if (!pfds) {
-               ast_log(LOG_ERROR, "Out of memory\n");
-               return -1;
-       }
-       if (*ms > 0)
-               start = ast_tvnow();
-       y = 0;
-       for (x=0; x < n; x++) {
-               if (fds[x] > -1) {
-                       pfds[y].fd = fds[x];
-                       pfds[y].events = POLLIN | POLLPRI;
-                       y++;
-               }
-       }
-       res = poll(pfds, y, *ms);
-       if (res < 0) {
-               /* Simulate a timeout if we were interrupted */
-               if (errno != EINTR)
-                       *ms = -1;
-               else
-                       *ms = 0;
-               return -1;
-       }
-       spoint = 0;
-       for (x=0; x < n; x++) {
-               if (fds[x] > -1) {
-                       if ((res = ast_fdisset(pfds, fds[x], y, &spoint))) {
-                               winner = fds[x];
-                               if (exception) {
-                                       if (res & POLLPRI)
-                                               *exception = -1;
-                                       else
-                                               *exception = 0;
-                               }
-                       }
-               }
-       }
-       if (*ms > 0) {
-               *ms -= ast_tvdiff_ms(ast_tvnow(), start);
-               if (*ms < 0)
-                       *ms = 0;
-       }
+       ast_waitfor_nandfds(NULL, 0, fds, n, exception, &winner, ms);
        return winner;
 }
 
@@ -1532,13 +1488,19 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds,
        int res;
        long rms;
        int x, y, max;
-       int spoint;
+       int sz;
        time_t now = 0;
-       long whentohangup = 0, havewhen = 0, diff;
+       long whentohangup = 0, diff;
        struct ast_channel *winner = NULL;
-
-       pfds = alloca(sizeof(struct pollfd) * (n * AST_MAX_FDS + nfds));
-       if (!pfds) {
+       struct fdmap {
+               int chan;
+               int fdno;
+       } *fdmap;
+
+       sz = n * AST_MAX_FDS + nfds;
+       pfds = alloca(sizeof(struct pollfd) * sz);
+       fdmap = alloca(sizeof(struct fdmap) * sz);
+       if (!pfds || !fdmap) {
                ast_log(LOG_ERROR, "Out of memory\n");
                *outfd = -1;
                return NULL;
@@ -1552,15 +1514,6 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds,
        /* Perform any pending masquerades */
        for (x=0; x < n; x++) {
                ast_mutex_lock(&c[x]->lock);
-               if (c[x]->whentohangup) {
-                       if (!havewhen)
-                               time(&now);
-                       diff = c[x]->whentohangup - now;
-                       if (!havewhen || (diff < whentohangup)) {
-                               havewhen++;
-                               whentohangup = diff;
-                       }
-               }
                if (c[x]->masq) {
                        if (ast_do_masquerade(c[x])) {
                                ast_log(LOG_WARNING, "Masquerade failed\n");
@@ -1569,40 +1522,52 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds,
                                return NULL;
                        }
                }
+               if (c[x]->whentohangup) {
+                       if (!whentohangup)
+                               time(&now);
+                       diff = c[x]->whentohangup - now;
+                       if (diff < 1) {
+                               /* Should already be hungup */
+                               c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
+                               ast_mutex_unlock(&c[x]->lock);
+                               return c[x];
+                       }
+                       if (!whentohangup || (diff < whentohangup))
+                               whentohangup = diff;
+               }
                ast_mutex_unlock(&c[x]->lock);
        }
-
+       /* Wait full interval */
        rms = *ms;
-       
-       if (havewhen) {
-               if ((*ms < 0) || (whentohangup * 1000 < *ms)) {
-                       rms =  whentohangup * 1000;
-               }
+       if (whentohangup) {
+               rms = (whentohangup - now) * 1000;      /* timeout in milliseconds */
+               if (*ms >= 0 && *ms < rms)              /* original *ms still smaller */
+                       rms =  *ms;
        }
+       /*
+        * Build the pollfd array, putting the channels' fds first,
+        * followed by individual fds. Order is important because
+        * individual fd's must have priority over channel fds.
+        */
        max = 0;
-       for (x=0; x < n; x++) {
-               for (y=0; y< AST_MAX_FDS; y++) {
-                       if (c[x]->fds[y] > -1) {
-                               pfds[max].fd = c[x]->fds[y];
-                               pfds[max].events = POLLIN | POLLPRI;
-                               pfds[max].revents = 0;
-                               max++;
-                       }
+       for (x=0; x<n; x++) {
+               for (y=0; y<AST_MAX_FDS; y++) {
+                       fdmap[max].fdno = y;  /* fd y is linked to this pfds */
+                       fdmap[max].chan = x;  /* channel x is linked to this pfds */
+                       max += ast_add_fd(&pfds[max], c[x]->fds[y]);
                }
                CHECK_BLOCKING(c[x]);
        }
-       for (x=0; x < nfds; x++) {
-               if (fds[x] > -1) {
-                       pfds[max].fd = fds[x];
-                       pfds[max].events = POLLIN | POLLPRI;
-                       pfds[max].revents = 0;
-                       max++;
-               }
+       /* Add the individual fds */
+       for (x=0; x<nfds; x++) {
+               fdmap[max].chan = -1;
+               max += ast_add_fd(&pfds[max], fds[x]);
        }
+
        if (*ms > 0) 
                start = ast_tvnow();
        
-       if (sizeof(int) == 4) {
+       if (sizeof(int) == 4) { /* XXX fix timeout > 600000 on linux x86-32 */
                do {
                        int kbrms = rms;
                        if (kbrms > 600000)
@@ -1614,65 +1579,49 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds,
        } else {
                res = poll(pfds, max, rms);
        }
-       
-       if (res < 0) {
-               for (x=0; x < n; x++) 
-                       ast_clear_flag(c[x], AST_FLAG_BLOCKING);
-               /* Simulate a timeout if we were interrupted */
-               if (errno != EINTR)
-                       *ms = -1;
-               else {
-                       /* Just an interrupt */
-#if 0
-                       *ms = 0;
-#endif                 
-               }
+       for (x=0; x<n; x++)
+               ast_clear_flag(c[x], AST_FLAG_BLOCKING);
+       if (res < 0) { /* Simulate a timeout if we were interrupted */
+               *ms = (errno != EINTR) ? -1 : 0;
                return NULL;
-        } else {
-               /* If no fds signalled, then timeout. So set ms = 0
-                  since we may not have an exact timeout.
-               */
-               if (res == 0)
-                       *ms = 0;
        }
-
-       if (havewhen)
+       if (whentohangup) {   /* if we have a timeout, check who expired */
                time(&now);
-       spoint = 0;
-       for (x=0; x < n; x++) {
-               ast_clear_flag(c[x], AST_FLAG_BLOCKING);
-               if (havewhen && c[x]->whentohangup && (now > c[x]->whentohangup)) {
-                       c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
-                       if (!winner)
-                               winner = c[x];
-               }
-               for (y=0; y < AST_MAX_FDS; y++) {
-                       if (c[x]->fds[y] > -1) {
-                               if ((res = ast_fdisset(pfds, c[x]->fds[y], max, &spoint))) {
-                                       if (res & POLLPRI)
-                                               ast_set_flag(c[x], AST_FLAG_EXCEPTION);
-                                       else
-                                               ast_clear_flag(c[x], AST_FLAG_EXCEPTION);
-                                       c[x]->fdno = y;
+               for (x=0; x<n; x++) {
+                       if (c[x]->whentohangup && now >= c[x]->whentohangup) {
+                               c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
+                               if (winner == NULL)
                                        winner = c[x];
-                               }
                        }
                }
        }
-       for (x=0; x < nfds; x++) {
-               if (fds[x] > -1) {
-                       if ((res = ast_fdisset(pfds, fds[x], max, &spoint))) {
-                               if (outfd)
-                                       *outfd = fds[x];
-                               if (exception) {        
-                                       if (res & POLLPRI) 
-                                               *exception = -1;
-                                       else
-                                               *exception = 0;
-                               }
-                               winner = NULL;
-                       }
-               }       
+       if (res == 0) { /* no fd ready, reset timeout and done */
+               *ms = 0;        /* XXX use 0 since we may not have an exact timeout. */
+               return winner;
+       }
+       /*
+        * Then check if any channel or fd has a pending event.
+        * Remember to check channels first and fds last, as they
+        * must have priority on setting 'winner'
+        */
+       for (x = 0; x < max; x++) {
+               res = pfds[x].revents;
+               if (res == 0)
+                       continue;
+               if (fdmap[x].chan >= 0) {       /* this is a channel */
+                       winner = c[fdmap[x].chan];      /* override previous winners */
+                       if (res & POLLPRI)
+                               ast_set_flag(winner, AST_FLAG_EXCEPTION);
+                       else
+                               ast_clear_flag(winner, AST_FLAG_EXCEPTION);
+                       winner->fdno = fdmap[x].fdno;
+               } else {                        /* this is an fd */
+                       if (outfd)
+                               *outfd = pfds[x].fd;
+                       if (exception)
+                               *exception = (res & POLLPRI) ? -1 : 0;
+                       winner = NULL;
+               }
        }
        if (*ms > 0) {
                *ms -= ast_tvdiff_ms(ast_tvnow(), start);
@@ -1689,16 +1638,11 @@ struct ast_channel *ast_waitfor_n(struct ast_channel **c, int n, int *ms)
 
 int ast_waitfor(struct ast_channel *c, int ms)
 {
-       struct ast_channel *chan;
        int oldms = ms;
 
-       chan = ast_waitfor_n(&c, 1, &ms);
-       if (ms < 0) {
-               if (oldms < 0)
-                       return 0;
-               else
-                       return -1;
-       }
+       ast_waitfor_nandfds(&c, 1, NULL, 0, NULL, NULL, &ms);
+       if ((ms < 0) && (oldms < 0))
+               ms = 0;
        return ms;
 }
 
@@ -1856,7 +1800,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                read(chan->alertpipe[0], &blah, sizeof(blah));
        }
 #ifdef ZAPTEL_OPTIMIZATIONS
-       if ((chan->timingfd > -1) && (chan->fdno == AST_MAX_FDS - 2) && ast_test_flag(chan, AST_FLAG_EXCEPTION)) {
+       if (chan->timingfd > -1 && chan->fdno == AST_TIMING_FD && ast_test_flag(chan, AST_FLAG_EXCEPTION)) {
                ast_clear_flag(chan, AST_FLAG_EXCEPTION);
                blah = -1;
                /* IF we can't get event, assume it's an expired as-per the old interface */
@@ -1898,8 +1842,19 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                        return f;
                } else
                        ast_log(LOG_NOTICE, "No/unknown event '%d' on timer for '%s'?\n", blah, chan->name);
-       }
+       } else
 #endif
+       /* Check for AST_GENERATOR_FD if not null.  If so, call generator with -1
+          arguments now so it can do whatever it needs to. */
+       if (chan->fds[AST_GENERATOR_FD] > -1 && chan->fdno == AST_GENERATOR_FD) {
+               void *tmp = chan->generatordata;
+               chan->generatordata = NULL;     /* reset to let ast_write get through */
+               chan->generator->generate(chan, tmp, -1, -1);
+               chan->generatordata = tmp;
+               f = &null_frame;
+               return f;
+       }
+
        /* Check for pending read queue */
        if (chan->readq) {
                f = chan->readq;
@@ -3088,9 +3043,10 @@ int ast_do_masquerade(struct ast_channel *original)
        
        /* Keep the same language.  */
        ast_copy_string(original->language, clone->language, sizeof(original->language));
-       /* Copy the FD's */
+       /* Copy the FD's other than the generator fd */
        for (x = 0; x < AST_MAX_FDS; x++) {
-               original->fds[x] = clone->fds[x];
+               if (x != AST_GENERATOR_FD)
+                       original->fds[x] = clone->fds[x];
        }
        clone_variables(original, clone);
        AST_LIST_HEAD_INIT_NOLOCK(&clone->varshead);
@@ -3114,7 +3070,7 @@ int ast_do_masquerade(struct ast_channel *original)
        clone->cid = tmpcid;
        
        /* Restore original timing file descriptor */
-       original->fds[AST_MAX_FDS - 2] = original->timingfd;
+       original->fds[AST_TIMING_FD] = original->timingfd;
        
        /* Our native formats are different now */
        original->nativeformats = clone->nativeformats;
index 89e21ef..3299aa0 100644 (file)
@@ -233,10 +233,10 @@ static AST_LIST_HEAD_STATIC(agents, agent_pvt);   /**< Holds the list of agents (l
        int x; \
        if (p->chan) { \
                for (x=0;x<AST_MAX_FDS;x++) {\
-                       if (x != AST_MAX_FDS - 2) \
+                       if (x != AST_TIMING_FD) \
                                ast->fds[x] = p->chan->fds[x]; \
                } \
-               ast->fds[AST_MAX_FDS - 3] = p->chan->fds[AST_MAX_FDS - 2]; \
+               ast->fds[AST_AGENT_FD] = p->chan->fds[AST_TIMING_FD]; \
        } \
 } while(0)
 
@@ -445,10 +445,7 @@ static struct ast_frame *agent_read(struct ast_channel *ast)
        CHECK_FORMATS(ast, p);
        if (p->chan) {
                ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION);
-               if (ast->fdno == AST_MAX_FDS - 3)
-                       p->chan->fdno = AST_MAX_FDS - 2;
-               else
-                       p->chan->fdno = ast->fdno;
+               p->chan->fdno = (ast->fdno == AST_AGENT_FD) ? AST_TIMING_FD : ast->fdno;
                f = ast_read(p->chan);
        } else
                f = &null_frame;
index dc2b70a..7eef2e9 100644 (file)
@@ -170,8 +170,8 @@ static void restore_channel(struct feature_pvt *p, int index)
        p->subs[index].owner->timingfd = p->subs[index].timingfdbackup;
        p->subs[index].owner->alertpipe[0] = p->subs[index].alertpipebackup[0];
        p->subs[index].owner->alertpipe[1] = p->subs[index].alertpipebackup[1];
-       p->subs[index].owner->fds[AST_MAX_FDS-1] = p->subs[index].alertpipebackup[0];
-       p->subs[index].owner->fds[AST_MAX_FDS-2] = p->subs[index].timingfdbackup;
+       p->subs[index].owner->fds[AST_ALERT_FD] = p->subs[index].alertpipebackup[0];
+       p->subs[index].owner->fds[AST_TIMING_FD] = p->subs[index].timingfdbackup;
 }
 
 static void update_features(struct feature_pvt *p, int index)
index 4b120d8..f07265f 100644 (file)
@@ -119,6 +119,14 @@ extern "C" {
 #define MAX_MUSICCLASS         20
 
 #define AST_MAX_FDS            8
+/*
+ * We have AST_MAX_FDS file descriptors in a channel.
+ * Some of them have a fixed use:
+ */
+#define AST_ALERT_FD   (AST_MAX_FDS-1)         /* used for alertpipe */
+#define AST_TIMING_FD  (AST_MAX_FDS-2)         /* used for timingfd */
+#define AST_AGENT_FD   (AST_MAX_FDS-3) /* used by agents for pass thru */
+#define AST_GENERATOR_FD       (AST_MAX_FDS-4) /* used by generator */
 
 enum ast_bridge_result {
        AST_BRIDGE_COMPLETE = 0,
@@ -1124,16 +1132,31 @@ void ast_channel_stop_silence_generator(struct ast_channel *chan, struct ast_sil
 
 /* Misc. functions below */
 
+/* if fd is a valid descriptor, set *pfd with the descriptor
+ * Return 1 (not -1!) if added, 0 otherwise (so we can add the
+ * return value to the index into the array)
+ */
+static inline int ast_add_fd(struct pollfd *pfd, int fd)
+{
+       pfd->fd = fd;
+       pfd->events = POLLIN | POLLPRI;
+       return fd >= 0;
+}
+
 /* Helper function for migrating select to poll */
 static inline int ast_fdisset(struct pollfd *pfds, int fd, int max, int *start)
 {
        int x;
-       for (x=start ? *start : 0;x<max;x++)
+       int dummy=0;
+
+       if (fd < 0)
+               return 0;
+       if (!start)
+               start = &dummy;
+       for (x = *start; x<max; x++)
                if (pfds[x].fd == fd) {
-                       if (start) {
-                               if (x==*start)
-                                       (*start)++;
-                       }
+                       if (x == *start)
+                               (*start)++;
                        return pfds[x].revents;
                }
        return 0;