1#include "iolooper.h" 2#include "qemu-common.h" 3 4/* An implementation of iolooper.h based on Unix select() */ 5#ifdef _WIN32 6# include <winsock2.h> 7#else 8# include <sys/types.h> 9# include <sys/select.h> 10#endif 11#include "sockets.h" 12 13struct IoLooper { 14 fd_set reads[1]; 15 fd_set writes[1]; 16 fd_set reads_result[1]; 17 fd_set writes_result[1]; 18 int max_fd; 19 int max_fd_valid; 20}; 21 22IoLooper* 23iolooper_new(void) 24{ 25 IoLooper* iol = malloc(sizeof(*iol)); 26 iolooper_reset(iol); 27 return iol; 28} 29 30void 31iolooper_free( IoLooper* iol ) 32{ 33 free(iol); 34} 35 36void 37iolooper_reset( IoLooper* iol ) 38{ 39 FD_ZERO(iol->reads); 40 FD_ZERO(iol->writes); 41 iol->max_fd = -1; 42 iol->max_fd_valid = 1; 43} 44 45static void 46iolooper_add_fd( IoLooper* iol, int fd ) 47{ 48 if (iol->max_fd_valid && fd > iol->max_fd) { 49 iol->max_fd = fd; 50 } 51} 52 53static void 54iolooper_del_fd( IoLooper* iol, int fd ) 55{ 56 if (iol->max_fd_valid && fd == iol->max_fd) 57 iol->max_fd_valid = 0; 58} 59 60void 61iolooper_modify( IoLooper* iol, int fd, int oldflags, int newflags ) 62{ 63 if (fd < 0) 64 return; 65 66 int changed = oldflags ^ newflags; 67 68 if ((changed & IOLOOPER_READ) != 0) { 69 if ((newflags & IOLOOPER_READ) != 0) 70 iolooper_add_read(iol, fd); 71 else 72 iolooper_del_read(iol, fd); 73 } 74 if ((changed & IOLOOPER_WRITE) != 0) { 75 if ((newflags & IOLOOPER_WRITE) != 0) 76 iolooper_add_write(iol, fd); 77 else 78 iolooper_del_write(iol, fd); 79 } 80} 81 82 83static int 84iolooper_fd_count( IoLooper* iol ) 85{ 86 int max_fd = iol->max_fd; 87 int fd; 88 89 if (iol->max_fd_valid) 90 return max_fd + 1; 91 92 /* recompute max fd */ 93 for (fd = 0; fd < FD_SETSIZE; fd++) { 94 if (!FD_ISSET(fd, iol->reads) && !FD_ISSET(fd, iol->writes)) 95 continue; 96 97 max_fd = fd; 98 } 99 iol->max_fd = max_fd; 100 iol->max_fd_valid = 1; 101 102 return max_fd + 1; 103} 104 105void 106iolooper_add_read( IoLooper* iol, int fd ) 107{ 108 if (fd >= 0) { 109 iolooper_add_fd(iol, fd); 110 FD_SET(fd, iol->reads); 111 } 112} 113 114void 115iolooper_add_write( IoLooper* iol, int fd ) 116{ 117 if (fd >= 0) { 118 iolooper_add_fd(iol, fd); 119 FD_SET(fd, iol->writes); 120 } 121} 122 123void 124iolooper_del_read( IoLooper* iol, int fd ) 125{ 126 if (fd >= 0) { 127 iolooper_del_fd(iol, fd); 128 FD_CLR(fd, iol->reads); 129 } 130} 131 132void 133iolooper_del_write( IoLooper* iol, int fd ) 134{ 135 if (fd >= 0) { 136 iolooper_del_fd(iol, fd); 137 FD_CLR(fd, iol->writes); 138 } 139} 140 141int 142iolooper_poll( IoLooper* iol ) 143{ 144 int count = iolooper_fd_count(iol); 145 int ret; 146 fd_set errs; 147 148 if (count == 0) 149 return 0; 150 151 FD_ZERO(&errs); 152 153 do { 154 struct timeval tv; 155 156 tv.tv_sec = tv.tv_usec = 0; 157 158 iol->reads_result[0] = iol->reads[0]; 159 iol->writes_result[0] = iol->writes[0]; 160 161 ret = select( count, iol->reads_result, iol->writes_result, &errs, &tv); 162 } while (ret < 0 && errno == EINTR); 163 164 return ret; 165} 166 167int 168iolooper_wait( IoLooper* iol, int64_t duration ) 169{ 170 int count = iolooper_fd_count(iol); 171 int ret; 172 fd_set errs; 173 struct timeval tm0, *tm = NULL; 174 175 if (count == 0) 176 return 0; 177 178 CLAMP_MAC_TIMEOUT(duration); 179 180 if (duration < 0) 181 tm = NULL; 182 else { 183 tm = &tm0; 184 tm->tv_sec = duration / 1000; 185 tm->tv_usec = (duration - 1000*tm->tv_sec) * 1000; 186 } 187 188 FD_ZERO(&errs); 189 190 do { 191 iol->reads_result[0] = iol->reads[0]; 192 iol->writes_result[0] = iol->writes[0]; 193 194 ret = select( count, iol->reads_result, iol->writes_result, &errs, tm); 195 if (ret == 0) { 196 // Indicates timeout 197 errno = ETIMEDOUT; 198 } 199 } while (ret < 0 && errno == EINTR); 200 201 return ret; 202} 203 204 205int 206iolooper_is_read( IoLooper* iol, int fd ) 207{ 208 return FD_ISSET(fd, iol->reads_result); 209} 210 211int 212iolooper_is_write( IoLooper* iol, int fd ) 213{ 214 return FD_ISSET(fd, iol->writes_result); 215} 216 217int 218iolooper_has_operations( IoLooper* iol ) 219{ 220 return iolooper_fd_count(iol) > 0; 221} 222 223int64_t 224iolooper_now(void) 225{ 226 struct timeval time_now; 227 return gettimeofday(&time_now, NULL) ? -1 : (int64_t)time_now.tv_sec * 1000LL + 228 time_now.tv_usec / 1000; 229} 230 231int 232iolooper_wait_absolute(IoLooper* iol, int64_t deadline) 233{ 234 int64_t timeout = deadline - iolooper_now(); 235 236 /* If the deadline has passed, set the timeout to 0, this allows us 237 * to poll the file descriptor nonetheless */ 238 if (timeout < 0) 239 timeout = 0; 240 241 return iolooper_wait(iol, timeout); 242} 243