/*	asyn_wait() - wait for asynch operations	Author: Kees J. Bot
 *								7 Jul 1997
 */
#define DEBUG 0

#include "asyn.h"
#include <time.h>
#if DEBUG
#include <stdio.h>
#endif

#define TBOUND_MIN	1
#define TBOUND_MAX	16

int asyn_wait(asynchio_t *asyn, int flags, struct timeval *to)
/* Wait for one or more nonblocking operations to return a result. */
{
	int r;
	static struct timeval zero_time;
	struct timeval t;
	static time_t tbound= TBOUND_MIN;

	/* Are there more things to do before we can block? */
	if (asyn->asyn_more > 0) { asyn->asyn_more= 0; return 0; }

	if (flags & ASYN_NONBLOCK) {
		/* Don't block by using a zero second timeout. */
		to= &zero_time;
	} else
	if (to != nil) {
		/* asyn_wait() uses an absolute time. */
		if (to->tv_usec >= 1000000L) {
			to->tv_sec+= to->tv_usec / 1000000L;
			to->tv_usec%= 1000000L;
		}
		(void) gettimeofday(&t, nil);
		if (t.tv_sec > to->tv_sec || (t.tv_sec == to->tv_sec
						&& t.tv_usec >= to->tv_usec)) {
			to= &zero_time;
		} else {
			t.tv_sec= to->tv_sec - t.tv_sec;
			t.tv_usec= to->tv_usec - t.tv_usec;
			if (t.tv_usec < 0) {
				t.tv_sec--;
				t.tv_usec+= 1000000L;
			}
			to= &t;
		}

		/* Don't sleep too long, we don't trust select(). */
		if (to->tv_sec > tbound) goto bound;
	} else {
	bound:
		/* No timeout?  Don't hang in (buggy?) select() forever. */
		to= &t;
		t.tv_sec= tbound;
		t.tv_usec= 0;
	}

#if DEBUG
	{
		int op;

		fprintf(stderr, "select: ");
		for (op= 0; op < SEL_NR; op++) {
			fd_set *fdsetp= &asyn->asyn_fdset[op];
			int fd;

			for (fd= 0; fd < FD_SETSIZE; fd++) {
				if (FD_ISSET(fd, fdsetp)) {
					asyn->asyn_afd[fd].afd_state[op]=
								PENDING;
					fprintf(stderr, "%d%c", fd, "rwx"[op]);
				}
			}
		}
		fflush(stderr);
	}
#endif
	r= select(FD_SETSIZE, &asyn->asyn_fdset[SEL_READ],
				&asyn->asyn_fdset[SEL_WRITE],
				&asyn->asyn_fdset[SEL_EXCEPT], to);
#if DEBUG
	fprintf(stderr, " (%d) ", r);
#endif
	if (r > 0) {
		/* An event occurred on one or more file descriptors. */
		int op;

		for (op= 0; op < SEL_NR; op++) {
			fd_set *fdsetp= &asyn->asyn_fdset[op];
			int fd;

			for (fd= 0; fd < FD_SETSIZE; fd++) {
				if (FD_ISSET(fd, fdsetp)) {
					asyn->asyn_afd[fd].afd_state[op]=
								PENDING;
#if DEBUG
					fprintf(stderr, "%d%c", fd, "rwx"[op]);
#endif
				}
			}
		}
		tbound= TBOUND_MIN;
	} else
	if (r == 0) {
		/* If nothing happened then let the time boundary slip a bit. */
		if (tbound < TBOUND_MAX) tbound <<= 1;
	}
#if DEBUG
	fputc('\n', stderr);
#endif

	FD_ZERO(&asyn->asyn_fdset[SEL_READ]);
	FD_ZERO(&asyn->asyn_fdset[SEL_WRITE]);
	FD_ZERO(&asyn->asyn_fdset[SEL_EXCEPT]);

	return r == 0 ? (errno= EINTR, -1) : r;
}
