[Asterisk-Dev] for review: ast_waitfor*()

Luigi Rizzo rizzo at icir.org
Wed Jun 22 14:51:19 MST 2005


as mentioned yesterday I have considerably simplified (both in terms
of size and complexity) the ast_waitfor*() functions.
I am attaching them here for review, before doing an additional
cleanup and submitting a patch through the bug repository.

There is stil one issue (which also applies to the original code):

	the first part of the function, scanning channel descriptor for
	timeouts, protects access to the channel using c[x]->lock.
	The remaining parts do not (nor they did in the original code).
	So which part is incorrect ?

Also you will note that this function has some code that looks
at  c[x]->generator to possibly poll on an additional file descriptor
when available (it lets you use musiconhold even without ztdummy).
I'd rather use one of the slots in c[x]->fds[] if possible.
Can someone tell me which slots in fds[] are reserved (it seems
AST_MAX_FDS-1 and AST_MAX_FDS-2 are) and which one i can use for that ?

	thanks
	luigi
-------------- next part --------------
/* 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 int add_fd(struct pollfd *pfd, int fd)
{
	if (fd < 0)
		return 0;
	pfd->fd = fd;
	pfd->events = POLLIN | POLLPRI;
	return 1;
}

int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception)
{
	int winner = -1;
	ast_waitfor_nandfds(NULL, 0, fds, n, exception, &winner, ms);
	return winner;
}

struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds, 
	int *exception, int *outfd, int *ms)
{
	/* Wait for x amount of time on a file descriptor to have input.  */
	struct timeval start;
	struct pollfd *pfds;
	int res;
	long rms;
	int x, y, max, have_timeout = 0;
	time_t now = 0;
	long i, whentohangup = 0;
	struct ast_channel *winner = NULL;
	int sz;
	int *ci, *fdno;	/* map pfds entries to channel index and fd index */

	if (outfd)
		*outfd = -1;
	if (exception)
		*exception = 0;
	
	sz = (n * (AST_MAX_FDS+1) + nfds);	/* XXX room for generator */
	pfds = alloca(sizeof(struct pollfd) * sz);
	ci = alloca(sizeof(int *) * sz);	/* pfds to channel map */
	fdno = alloca(sizeof(int *) * sz);	/* pfds to fd index */

	if (!pfds || !ci || !fdno) {
		ast_log(LOG_ERROR, "Out of memory\n");
		return NULL;
	}
	for (x=0; x < sz; x++)
		ci[x] = -1;	/* no channel linked to this pfd */

	/*
	 * Perform any pending masquerades, and take the min timeout.
	 */
	for (x=0; x<n; x++) {
		ast_mutex_lock(&c[x]->lock);
		if (c[x]->masq) {
			if (ast_do_masquerade(c[x])) {
				ast_log(LOG_WARNING, "Masquerade failed\n");
				*ms = -1;
				ast_mutex_unlock(&c[x]->lock);
				return NULL;
			}
		}
		i = c[x]->whentohangup;
		if (i && (!have_timeout || i < whentohangup)) {
			have_timeout = 1;
			whentohangup = i;
		}
		ast_mutex_unlock(&c[x]->lock);
	}

	rms = *ms;
	
	if (have_timeout) {
		i = (whentohangup - time(NULL)) * 1000; /* timeout in milliseconds */
		if (*ms < 0 || i < *ms)
			rms =  i;
	}
	/*
	 * 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++) {
		struct ast_generator *g = c[x]->generator;
		int gfd;

		if (g && g->magic == AST_GEN_MAGIC &&
				(gfd = g->get_fd(c[x], c[x]->generatordata)) >= 0) {
			if (add_fd(&pfds[max], gfd)) {
				fdno[max] = AST_MAX_FDS;	/* XXX not really used */
				ci[max++] = x;  /* channel x is linked to this pfds */
			}
		}

		for (y=0;y<AST_MAX_FDS;y++)
			if (add_fd(&pfds[max], c[x]->fds[y])) {
				fdno[max] = y;	/* fd y is linked to this pfds */
				ci[max++] = x;  /* channel x is linked to this pfds */
			}
		CHECK_BLOCKING(c[x]);
	}
	/* followed by the individual fds */
	for (x=0;x<nfds; x++)
		max += add_fd(&pfds[max], fds[x]);
	if (*ms > 0) 
		start = ast_tvnow();	/* timestamp before the poll */
	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 */
		*ms = (errno != EINTR) ? -1 : 0;
		return NULL;
	} else if (res == 0) {	/* no fds signalled */
		/* set ms = 0 since we may not have an exact timeout. */
		*ms = 0;
	}

	if (have_timeout)	/* take a timestamp */
		time(&now);
	/* first, check for timeouts on channels */
	for (x=0;x<n;x++) {
		ast_clear_flag(c[x], AST_FLAG_BLOCKING);
		if (c[x]->whentohangup && now > c[x]->whentohangup) {
			c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
			if (winner == NULL)
				winner = c[x];
		}
	}
	/*
	 * 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 (ci[x] >= 0) {	/* this is a channel */
			winner = c[ci[x]];	/* override previous winners */
			if (res & POLLPRI)
				ast_set_flag(winner, AST_FLAG_EXCEPTION);
			else
				ast_clear_flag(winner, AST_FLAG_EXCEPTION);
			winner->fdno = fdno[x];
		} 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(ast_tvnow(), start);
		if (*ms < 0)
			*ms = 0;
	}
	return winner;
}

struct ast_channel *ast_waitfor_n(struct ast_channel **c, int n, int *ms)
{
	return ast_waitfor_nandfds(c, n, NULL, 0, NULL, NULL, 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) {
		return (oldms < 0) ? 0 : -1;
	}
	return ms;
}


More information about the asterisk-dev mailing list