diff options
author | markster <markster@f38db490-d61c-443f-a65b-d21fe96a405b> | 2006-01-30 03:13:33 +0000 |
---|---|---|
committer | markster <markster@f38db490-d61c-443f-a65b-d21fe96a405b> | 2006-01-30 03:13:33 +0000 |
commit | df81c56d1365a5d81d8c46aa4235ae7737d2dcc1 (patch) | |
tree | cca132b8be25dadbe207b7752c6b7b16bc379435 | |
parent | 666c469664803f6b58e7e2e4e785b71119206c74 (diff) |
Merge Rizzo's waitfor update (bug #4584)
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@8877 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r-- | channel.c | 262 | ||||
-rw-r--r-- | channels/chan_agent.c | 9 | ||||
-rw-r--r-- | channels/chan_features.c | 4 | ||||
-rw-r--r-- | include/asterisk/channel.h | 33 |
4 files changed, 142 insertions, 166 deletions
@@ -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; diff --git a/channels/chan_agent.c b/channels/chan_agent.c index 89e21ef43..3299aa02e 100644 --- a/channels/chan_agent.c +++ b/channels/chan_agent.c @@ -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; diff --git a/channels/chan_features.c b/channels/chan_features.c index dc2b70a10..7eef2e990 100644 --- a/channels/chan_features.c +++ b/channels/chan_features.c @@ -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) diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 4b120d8cf..f07265fa4 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -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; |