Merge Rizzo's waitfor update (bug #4584)

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@8877 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Mark Spencer
2006-01-30 03:13:33 +00:00
parent f8b6a4db98
commit 37815b2cce
4 changed files with 143 additions and 167 deletions

264
channel.c
View File

@@ -604,7 +604,10 @@ struct ast_channel *ast_channel_alloc(int needqueue)
return NULL; 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; tmp->fds[x] = -1;
#ifdef ZAPTEL_OPTIMIZATIONS #ifdef ZAPTEL_OPTIMIZATIONS
@@ -636,9 +639,9 @@ struct ast_channel *ast_channel_alloc(int needqueue)
tmp->alertpipe[0] = tmp->alertpipe[1] = -1; tmp->alertpipe[0] = tmp->alertpipe[1] = -1;
/* Always watch the alertpipe */ /* Always watch the alertpipe */
tmp->fds[AST_MAX_FDS-1] = tmp->alertpipe[0]; tmp->fds[AST_ALERT_FD] = tmp->alertpipe[0];
/* And timing pipe */ /* And timing pipe */
tmp->fds[AST_MAX_FDS-2] = tmp->timingfd; tmp->fds[AST_TIMING_FD] = tmp->timingfd;
strcpy(tmp->name, "**Unknown**"); strcpy(tmp->name, "**Unknown**");
/* Initial state */ /* Initial state */
tmp->_state = AST_STATE_DOWN; tmp->_state = AST_STATE_DOWN;
@@ -1414,6 +1417,7 @@ void ast_deactivate_generator(struct ast_channel *chan)
chan->generator->release(chan, chan->generatordata); chan->generator->release(chan, chan->generatordata);
chan->generatordata = NULL; chan->generatordata = NULL;
chan->generator = NULL; chan->generator = NULL;
chan->fds[AST_GENERATOR_FD] = -1;
ast_clear_flag(chan, AST_FLAG_WRITE_INT); ast_clear_flag(chan, AST_FLAG_WRITE_INT);
ast_settimeout(chan, 0, NULL, NULL); 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. */ /*! \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) 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 winner = -1;
int spoint; ast_waitfor_nandfds(NULL, 0, fds, n, exception, &winner, ms);
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;
}
return winner; return winner;
} }
@@ -1532,13 +1488,19 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds,
int res; int res;
long rms; long rms;
int x, y, max; int x, y, max;
int spoint; int sz;
time_t now = 0; time_t now = 0;
long whentohangup = 0, havewhen = 0, diff; long whentohangup = 0, diff;
struct ast_channel *winner = NULL; struct ast_channel *winner = NULL;
struct fdmap {
int chan;
int fdno;
} *fdmap;
pfds = alloca(sizeof(struct pollfd) * (n * AST_MAX_FDS + nfds)); sz = n * AST_MAX_FDS + nfds;
if (!pfds) { pfds = alloca(sizeof(struct pollfd) * sz);
fdmap = alloca(sizeof(struct fdmap) * sz);
if (!pfds || !fdmap) {
ast_log(LOG_ERROR, "Out of memory\n"); ast_log(LOG_ERROR, "Out of memory\n");
*outfd = -1; *outfd = -1;
return NULL; return NULL;
@@ -1552,15 +1514,6 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds,
/* Perform any pending masquerades */ /* Perform any pending masquerades */
for (x=0; x < n; x++) { for (x=0; x < n; x++) {
ast_mutex_lock(&c[x]->lock); 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 (c[x]->masq) {
if (ast_do_masquerade(c[x])) { if (ast_do_masquerade(c[x])) {
ast_log(LOG_WARNING, "Masquerade failed\n"); 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; 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); ast_mutex_unlock(&c[x]->lock);
} }
/* Wait full interval */
rms = *ms; rms = *ms;
if (whentohangup) {
if (havewhen) { rms = (whentohangup - now) * 1000; /* timeout in milliseconds */
if ((*ms < 0) || (whentohangup * 1000 < *ms)) { if (*ms >= 0 && *ms < rms) /* original *ms still smaller */
rms = whentohangup * 1000; 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; max = 0;
for (x=0; x < n; x++) { for (x=0; x<n; x++) {
for (y=0; y< AST_MAX_FDS; y++) { for (y=0; y<AST_MAX_FDS; y++) {
if (c[x]->fds[y] > -1) { fdmap[max].fdno = y; /* fd y is linked to this pfds */
pfds[max].fd = c[x]->fds[y]; fdmap[max].chan = x; /* channel x is linked to this pfds */
pfds[max].events = POLLIN | POLLPRI; max += ast_add_fd(&pfds[max], c[x]->fds[y]);
pfds[max].revents = 0;
max++;
}
} }
CHECK_BLOCKING(c[x]); CHECK_BLOCKING(c[x]);
} }
for (x=0; x < nfds; x++) { /* Add the individual fds */
if (fds[x] > -1) { for (x=0; x<nfds; x++) {
pfds[max].fd = fds[x]; fdmap[max].chan = -1;
pfds[max].events = POLLIN | POLLPRI; max += ast_add_fd(&pfds[max], fds[x]);
pfds[max].revents = 0;
max++;
}
} }
if (*ms > 0) if (*ms > 0)
start = ast_tvnow(); start = ast_tvnow();
if (sizeof(int) == 4) { if (sizeof(int) == 4) { /* XXX fix timeout > 600000 on linux x86-32 */
do { do {
int kbrms = rms; int kbrms = rms;
if (kbrms > 600000) if (kbrms > 600000)
@@ -1614,65 +1579,49 @@ struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds,
} else { } else {
res = poll(pfds, max, rms); res = poll(pfds, max, rms);
} }
for (x=0; x<n; x++)
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
}
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)
time(&now);
spoint = 0;
for (x=0; x < n; x++) {
ast_clear_flag(c[x], AST_FLAG_BLOCKING); ast_clear_flag(c[x], AST_FLAG_BLOCKING);
if (havewhen && c[x]->whentohangup && (now > c[x]->whentohangup)) { if (res < 0) { /* Simulate a timeout if we were interrupted */
c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT; *ms = (errno != EINTR) ? -1 : 0;
if (!winner) return NULL;
winner = c[x]; }
} if (whentohangup) { /* if we have a timeout, check who expired */
for (y=0; y < AST_MAX_FDS; y++) { time(&now);
if (c[x]->fds[y] > -1) { for (x=0; x<n; x++) {
if ((res = ast_fdisset(pfds, c[x]->fds[y], max, &spoint))) { if (c[x]->whentohangup && now >= c[x]->whentohangup) {
if (res & POLLPRI) c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
ast_set_flag(c[x], AST_FLAG_EXCEPTION); if (winner == NULL)
else
ast_clear_flag(c[x], AST_FLAG_EXCEPTION);
c[x]->fdno = y;
winner = c[x]; winner = c[x];
}
} }
} }
} }
for (x=0; x < nfds; x++) { if (res == 0) { /* no fd ready, reset timeout and done */
if (fds[x] > -1) { *ms = 0; /* XXX use 0 since we may not have an exact timeout. */
if ((res = ast_fdisset(pfds, fds[x], max, &spoint))) { return winner;
if (outfd) }
*outfd = fds[x]; /*
if (exception) { * Then check if any channel or fd has a pending event.
if (res & POLLPRI) * Remember to check channels first and fds last, as they
*exception = -1; * must have priority on setting 'winner'
else */
*exception = 0; for (x = 0; x < max; x++) {
} res = pfds[x].revents;
winner = NULL; 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) { if (*ms > 0) {
*ms -= ast_tvdiff_ms(ast_tvnow(), start); *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) int ast_waitfor(struct ast_channel *c, int ms)
{ {
struct ast_channel *chan;
int oldms = ms; int oldms = ms;
chan = ast_waitfor_n(&c, 1, &ms); ast_waitfor_nandfds(&c, 1, NULL, 0, NULL, NULL, &ms);
if (ms < 0) { if ((ms < 0) && (oldms < 0))
if (oldms < 0) ms = 0;
return 0;
else
return -1;
}
return ms; 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)); read(chan->alertpipe[0], &blah, sizeof(blah));
} }
#ifdef ZAPTEL_OPTIMIZATIONS #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); ast_clear_flag(chan, AST_FLAG_EXCEPTION);
blah = -1; blah = -1;
/* IF we can't get event, assume it's an expired as-per the old interface */ /* 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; return f;
} else } else
ast_log(LOG_NOTICE, "No/unknown event '%d' on timer for '%s'?\n", blah, chan->name); ast_log(LOG_NOTICE, "No/unknown event '%d' on timer for '%s'?\n", blah, chan->name);
} } else
#endif #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 */ /* Check for pending read queue */
if (chan->readq) { if (chan->readq) {
f = chan->readq; f = chan->readq;
@@ -3088,9 +3043,10 @@ int ast_do_masquerade(struct ast_channel *original)
/* Keep the same language. */ /* Keep the same language. */
ast_copy_string(original->language, clone->language, sizeof(original->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++) { 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); clone_variables(original, clone);
AST_LIST_HEAD_INIT_NOLOCK(&clone->varshead); AST_LIST_HEAD_INIT_NOLOCK(&clone->varshead);
@@ -3114,7 +3070,7 @@ int ast_do_masquerade(struct ast_channel *original)
clone->cid = tmpcid; clone->cid = tmpcid;
/* Restore original timing file descriptor */ /* 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 */ /* Our native formats are different now */
original->nativeformats = clone->nativeformats; original->nativeformats = clone->nativeformats;

View File

@@ -233,10 +233,10 @@ static AST_LIST_HEAD_STATIC(agents, agent_pvt); /**< Holds the list of agents (l
int x; \ int x; \
if (p->chan) { \ if (p->chan) { \
for (x=0;x<AST_MAX_FDS;x++) {\ 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[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) } while(0)
@@ -445,10 +445,7 @@ static struct ast_frame *agent_read(struct ast_channel *ast)
CHECK_FORMATS(ast, p); CHECK_FORMATS(ast, p);
if (p->chan) { if (p->chan) {
ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION); ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION);
if (ast->fdno == AST_MAX_FDS - 3) p->chan->fdno = (ast->fdno == AST_AGENT_FD) ? AST_TIMING_FD : ast->fdno;
p->chan->fdno = AST_MAX_FDS - 2;
else
p->chan->fdno = ast->fdno;
f = ast_read(p->chan); f = ast_read(p->chan);
} else } else
f = &null_frame; f = &null_frame;

View 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->timingfd = p->subs[index].timingfdbackup;
p->subs[index].owner->alertpipe[0] = p->subs[index].alertpipebackup[0]; 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->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_ALERT_FD] = p->subs[index].alertpipebackup[0];
p->subs[index].owner->fds[AST_MAX_FDS-2] = p->subs[index].timingfdbackup; p->subs[index].owner->fds[AST_TIMING_FD] = p->subs[index].timingfdbackup;
} }
static void update_features(struct feature_pvt *p, int index) static void update_features(struct feature_pvt *p, int index)

View File

@@ -119,6 +119,14 @@ extern "C" {
#define MAX_MUSICCLASS 20 #define MAX_MUSICCLASS 20
#define AST_MAX_FDS 8 #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 { enum ast_bridge_result {
AST_BRIDGE_COMPLETE = 0, AST_BRIDGE_COMPLETE = 0,
@@ -1124,16 +1132,31 @@ void ast_channel_stop_silence_generator(struct ast_channel *chan, struct ast_sil
/* Misc. functions below */ /* 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 */ /* Helper function for migrating select to poll */
static inline int ast_fdisset(struct pollfd *pfds, int fd, int max, int *start) static inline int ast_fdisset(struct pollfd *pfds, int fd, int max, int *start)
{ {
int x; 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 (pfds[x].fd == fd) {
if (start) { if (x == *start)
if (x==*start) (*start)++;
(*start)++;
}
return pfds[x].revents; return pfds[x].revents;
} }
return 0; return 0;