1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include <errno.h> 17 #include <stdlib.h> 18 #include "iolooper.h" 19 #include "sockets.h" 20 21 /* An implementation of iolooper.h based on Unix select() */ 22 #ifdef _WIN32 23 # include <winsock2.h> 24 # include <time.h> 25 #else 26 # include <sys/types.h> 27 # include <sys/select.h> 28 # include <sys/time.h> 29 #endif 30 31 struct IoLooper { 32 fd_set reads[1]; 33 fd_set writes[1]; 34 fd_set reads_result[1]; 35 fd_set writes_result[1]; 36 int max_fd; 37 int max_fd_valid; 38 }; 39 40 IoLooper* 41 iolooper_new(void) 42 { 43 IoLooper* iol = malloc(sizeof(*iol)); 44 iolooper_reset(iol); 45 return iol; 46 } 47 48 void 49 iolooper_free( IoLooper* iol ) 50 { 51 free(iol); 52 } 53 54 void 55 iolooper_reset( IoLooper* iol ) 56 { 57 FD_ZERO(iol->reads); 58 FD_ZERO(iol->writes); 59 iol->max_fd = -1; 60 iol->max_fd_valid = 1; 61 } 62 63 static void 64 iolooper_add_fd( IoLooper* iol, int fd ) 65 { 66 if (iol->max_fd_valid && fd > iol->max_fd) { 67 iol->max_fd = fd; 68 } 69 } 70 71 static void 72 iolooper_del_fd( IoLooper* iol, int fd ) 73 { 74 if (iol->max_fd_valid && fd == iol->max_fd) 75 iol->max_fd_valid = 0; 76 } 77 78 void 79 iolooper_modify( IoLooper* iol, int fd, int oldflags, int newflags ) 80 { 81 if (fd < 0) 82 return; 83 84 int changed = oldflags ^ newflags; 85 86 if ((changed & IOLOOPER_READ) != 0) { 87 if ((newflags & IOLOOPER_READ) != 0) 88 iolooper_add_read(iol, fd); 89 else 90 iolooper_del_read(iol, fd); 91 } 92 if ((changed & IOLOOPER_WRITE) != 0) { 93 if ((newflags & IOLOOPER_WRITE) != 0) 94 iolooper_add_write(iol, fd); 95 else 96 iolooper_del_write(iol, fd); 97 } 98 } 99 100 101 static int 102 iolooper_fd_count( IoLooper* iol ) 103 { 104 int max_fd = iol->max_fd; 105 int fd; 106 107 if (iol->max_fd_valid) 108 return max_fd + 1; 109 110 /* recompute max fd */ 111 for (fd = 0; fd < FD_SETSIZE; fd++) { 112 if (!FD_ISSET(fd, iol->reads) && !FD_ISSET(fd, iol->writes)) 113 continue; 114 115 max_fd = fd; 116 } 117 iol->max_fd = max_fd; 118 iol->max_fd_valid = 1; 119 120 return max_fd + 1; 121 } 122 123 void 124 iolooper_add_read( IoLooper* iol, int fd ) 125 { 126 if (fd >= 0) { 127 iolooper_add_fd(iol, fd); 128 FD_SET(fd, iol->reads); 129 } 130 } 131 132 void 133 iolooper_add_write( IoLooper* iol, int fd ) 134 { 135 if (fd >= 0) { 136 iolooper_add_fd(iol, fd); 137 FD_SET(fd, iol->writes); 138 } 139 } 140 141 void 142 iolooper_del_read( IoLooper* iol, int fd ) 143 { 144 if (fd >= 0) { 145 iolooper_del_fd(iol, fd); 146 FD_CLR(fd, iol->reads); 147 } 148 } 149 150 void 151 iolooper_del_write( IoLooper* iol, int fd ) 152 { 153 if (fd >= 0) { 154 iolooper_del_fd(iol, fd); 155 FD_CLR(fd, iol->writes); 156 } 157 } 158 159 int 160 iolooper_poll( IoLooper* iol ) 161 { 162 int count = iolooper_fd_count(iol); 163 int ret; 164 fd_set errs; 165 166 if (count == 0) 167 return 0; 168 169 FD_ZERO(&errs); 170 171 do { 172 struct timeval tv; 173 174 tv.tv_sec = tv.tv_usec = 0; 175 176 iol->reads_result[0] = iol->reads[0]; 177 iol->writes_result[0] = iol->writes[0]; 178 179 ret = select( count, iol->reads_result, iol->writes_result, &errs, &tv); 180 } while (ret < 0 && errno == EINTR); 181 182 return ret; 183 } 184 185 int 186 iolooper_wait( IoLooper* iol, int64_t duration ) 187 { 188 int count = iolooper_fd_count(iol); 189 int ret; 190 fd_set errs; 191 struct timeval tm0, *tm = NULL; 192 193 if (count == 0) 194 return 0; 195 196 if (duration < 0) 197 tm = NULL; 198 else { 199 tm = &tm0; 200 tm->tv_sec = duration / 1000; 201 tm->tv_usec = (duration - 1000*tm->tv_sec) * 1000; 202 } 203 204 FD_ZERO(&errs); 205 206 do { 207 iol->reads_result[0] = iol->reads[0]; 208 iol->writes_result[0] = iol->writes[0]; 209 210 ret = select( count, iol->reads_result, iol->writes_result, &errs, tm); 211 if (ret == 0) { 212 // Indicates timeout 213 errno = ETIMEDOUT; 214 } 215 } while (ret < 0 && errno == EINTR); 216 217 return ret; 218 } 219 220 221 int 222 iolooper_is_read( IoLooper* iol, int fd ) 223 { 224 return FD_ISSET(fd, iol->reads_result); 225 } 226 227 int 228 iolooper_is_write( IoLooper* iol, int fd ) 229 { 230 return FD_ISSET(fd, iol->writes_result); 231 } 232 233 int 234 iolooper_has_operations( IoLooper* iol ) 235 { 236 return iolooper_fd_count(iol) > 0; 237 } 238 239 int64_t 240 iolooper_now(void) 241 { 242 #ifdef _WIN32 243 FILETIME now; 244 int64_t now_100ns; 245 246 GetSystemTimeAsFileTime(&now); 247 248 /* Get the time as hundreds of nanosecond intervals since 249 12:00 AM January 1t 1601 UTC. We don't really need 250 to compute the value relative to the Posix epoch */ 251 now_100ns = ((int64_t)now.dwHighDateTime << 32) | now.dwLowDateTime; 252 253 /* 100 ns == 0.1 us == 0.0001 ms */ 254 return now_100ns / 10000LL; 255 256 #else /* !_WIN32 */ 257 struct timeval time_now; 258 return gettimeofday(&time_now, NULL) ? -1 : (int64_t)time_now.tv_sec * 1000LL + 259 time_now.tv_usec / 1000; 260 #endif /* !_WIN32 */ 261 } 262 263 int 264 iolooper_wait_absolute(IoLooper* iol, int64_t deadline) 265 { 266 int64_t timeout = deadline - iolooper_now(); 267 268 /* If the deadline has passed, set the timeout to 0, this allows us 269 * to poll the file descriptor nonetheless */ 270 if (timeout < 0) 271 timeout = 0; 272 273 return iolooper_wait(iol, timeout); 274 } 275