| #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); |
| } |