blob: 5024c9abb0aa1c24ffad9d34a8723f95ce212ca5 [file] [log] [blame]
#include "iolooper.h"
#include "qemu-common.h"
/* An implementation of iolooper.h based on Unix select() */
#ifdef _WIN32
# include <winsock2.h>
#else
# include <sys/types.h>
# include <sys/select.h>
#endif
#include "sockets.h"
struct IoLooper {
fd_set reads[1];
fd_set writes[1];
fd_set reads_result[1];
fd_set writes_result[1];
int max_fd;
int max_fd_valid;
};
IoLooper*
iolooper_new(void)
{
IoLooper* iol = malloc(sizeof(*iol));
iolooper_reset(iol);
return iol;
}
void
iolooper_free( IoLooper* iol )
{
free(iol);
}
void
iolooper_reset( IoLooper* iol )
{
FD_ZERO(iol->reads);
FD_ZERO(iol->writes);
iol->max_fd = -1;
iol->max_fd_valid = 1;
}
static void
iolooper_add_fd( IoLooper* iol, int fd )
{
if (iol->max_fd_valid && fd > iol->max_fd) {
iol->max_fd = fd;
}
}
static void
iolooper_del_fd( IoLooper* iol, int fd )
{
if (iol->max_fd_valid && fd == iol->max_fd)
iol->max_fd_valid = 0;
}
void
iolooper_modify( IoLooper* iol, int fd, int oldflags, int newflags )
{
if (fd < 0)
return;
int changed = oldflags ^ newflags;
if ((changed & IOLOOPER_READ) != 0) {
if ((newflags & IOLOOPER_READ) != 0)
iolooper_add_read(iol, fd);
else
iolooper_del_read(iol, fd);
}
if ((changed & IOLOOPER_WRITE) != 0) {
if ((newflags & IOLOOPER_WRITE) != 0)
iolooper_add_write(iol, fd);
else
iolooper_del_write(iol, fd);
}
}
static int
iolooper_fd_count( IoLooper* iol )
{
int max_fd = iol->max_fd;
int fd;
if (iol->max_fd_valid)
return max_fd + 1;
/* recompute max fd */
for (fd = 0; fd < FD_SETSIZE; fd++) {
if (!FD_ISSET(fd, iol->reads) && !FD_ISSET(fd, iol->writes))
continue;
max_fd = fd;
}
iol->max_fd = max_fd;
iol->max_fd_valid = 1;
return max_fd + 1;
}
void
iolooper_add_read( IoLooper* iol, int fd )
{
if (fd >= 0) {
iolooper_add_fd(iol, fd);
FD_SET(fd, iol->reads);
}
}
void
iolooper_add_write( IoLooper* iol, int fd )
{
if (fd >= 0) {
iolooper_add_fd(iol, fd);
FD_SET(fd, iol->writes);
}
}
void
iolooper_del_read( IoLooper* iol, int fd )
{
if (fd >= 0) {
iolooper_del_fd(iol, fd);
FD_CLR(fd, iol->reads);
}
}
void
iolooper_del_write( IoLooper* iol, int fd )
{
if (fd >= 0) {
iolooper_del_fd(iol, fd);
FD_CLR(fd, iol->writes);
}
}
int
iolooper_poll( IoLooper* iol )
{
int count = iolooper_fd_count(iol);
int ret;
fd_set errs;
if (count == 0)
return 0;
FD_ZERO(&errs);
do {
struct timeval tv;
tv.tv_sec = tv.tv_usec = 0;
iol->reads_result[0] = iol->reads[0];
iol->writes_result[0] = iol->writes[0];
ret = select( count, iol->reads_result, iol->writes_result, &errs, &tv);
} while (ret < 0 && errno == EINTR);
return ret;
}
int
iolooper_wait( IoLooper* iol, int64_t duration )
{
int count = iolooper_fd_count(iol);
int ret;
fd_set errs;
struct timeval tm0, *tm = NULL;
if (count == 0)
return 0;
CLAMP_MAC_TIMEOUT(duration);
if (duration < 0)
tm = NULL;
else {
tm = &tm0;
tm->tv_sec = duration / 1000;
tm->tv_usec = (duration - 1000*tm->tv_sec) * 1000;
}
FD_ZERO(&errs);
do {
iol->reads_result[0] = iol->reads[0];
iol->writes_result[0] = iol->writes[0];
ret = select( count, iol->reads_result, iol->writes_result, &errs, tm);
if (ret == 0) {
// Indicates timeout
errno = ETIMEDOUT;
}
} while (ret < 0 && errno == EINTR);
return ret;
}
int
iolooper_is_read( IoLooper* iol, int fd )
{
return FD_ISSET(fd, iol->reads_result);
}
int
iolooper_is_write( IoLooper* iol, int fd )
{
return FD_ISSET(fd, iol->writes_result);
}
int
iolooper_has_operations( IoLooper* iol )
{
return iolooper_fd_count(iol) > 0;
}
int64_t
iolooper_now(void)
{
struct timeval time_now;
return gettimeofday(&time_now, NULL) ? -1 : (int64_t)time_now.tv_sec * 1000LL +
time_now.tv_usec / 1000;
}
int
iolooper_wait_absolute(IoLooper* iol, int64_t deadline)
{
int64_t timeout = deadline - iolooper_now();
/* If the deadline has passed, set the timeout to 0, this allows us
* to poll the file descriptor nonetheless */
if (timeout < 0)
timeout = 0;
return iolooper_wait(iol, timeout);
}