1 /* http://frotznet.googlecode.com/svn/trunk/utils/fdevent.c 2 ** 3 ** Copyright 2006, Brian Swetland <swetland (at) frotz.net> 4 ** 5 ** Licensed under the Apache License, Version 2.0 (the "License"); 6 ** you may not use this file except in compliance with the License. 7 ** You may obtain a copy of the License at 8 ** 9 ** http://www.apache.org/licenses/LICENSE-2.0 10 ** 11 ** Unless required by applicable law or agreed to in writing, software 12 ** distributed under the License is distributed on an "AS IS" BASIS, 13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 ** See the License for the specific language governing permissions and 15 ** limitations under the License. 16 */ 17 18 #include <stdlib.h> 19 #include <stdio.h> 20 #include <string.h> 21 #include <unistd.h> 22 #include <errno.h> 23 24 #include <fcntl.h> 25 26 #include <stdarg.h> 27 #include <stddef.h> 28 29 #include "fdevent.h" 30 31 #define TRACE(x...) fprintf(stderr,x) 32 33 #define DEBUG 0 34 35 static void fatal(const char *fn, const char *fmt, ...) 36 { 37 va_list ap; 38 va_start(ap, fmt); 39 fprintf(stderr, "%s:", fn); 40 vfprintf(stderr, fmt, ap); 41 va_end(ap); 42 abort(); 43 } 44 45 #define FATAL(x...) fatal(__FUNCTION__, x) 46 47 #if DEBUG 48 static void dump_fde(fdevent *fde, const char *info) 49 { 50 fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd, 51 fde->state & FDE_READ ? 'R' : ' ', 52 fde->state & FDE_WRITE ? 'W' : ' ', 53 fde->state & FDE_ERROR ? 'E' : ' ', 54 info); 55 } 56 #else 57 #define dump_fde(fde, info) do { } while(0) 58 #endif 59 60 #define FDE_EVENTMASK 0x00ff 61 #define FDE_STATEMASK 0xff00 62 63 #define FDE_ACTIVE 0x0100 64 #define FDE_PENDING 0x0200 65 #define FDE_CREATED 0x0400 66 67 static void fdevent_plist_enqueue(fdevent *node); 68 static void fdevent_plist_remove(fdevent *node); 69 static fdevent *fdevent_plist_dequeue(void); 70 71 static fdevent list_pending = { 72 .next = &list_pending, 73 .prev = &list_pending, 74 }; 75 76 static fdevent **fd_table = 0; 77 static int fd_table_max = 0; 78 79 #ifdef CRAPTASTIC 80 //HAVE_EPOLL 81 82 #include <sys/epoll.h> 83 84 static int epoll_fd = -1; 85 86 static void fdevent_init() 87 { 88 /* XXX: what's a good size for the passed in hint? */ 89 epoll_fd = epoll_create(256); 90 91 if(epoll_fd < 0) { 92 perror("epoll_create() failed"); 93 exit(1); 94 } 95 96 /* mark for close-on-exec */ 97 fcntl(epoll_fd, F_SETFD, FD_CLOEXEC); 98 } 99 100 static void fdevent_connect(fdevent *fde) 101 { 102 struct epoll_event ev; 103 104 memset(&ev, 0, sizeof(ev)); 105 ev.events = 0; 106 ev.data.ptr = fde; 107 108 #if 0 109 if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) { 110 perror("epoll_ctl() failed\n"); 111 exit(1); 112 } 113 #endif 114 } 115 116 static void fdevent_disconnect(fdevent *fde) 117 { 118 struct epoll_event ev; 119 120 memset(&ev, 0, sizeof(ev)); 121 ev.events = 0; 122 ev.data.ptr = fde; 123 124 /* technically we only need to delete if we 125 ** were actively monitoring events, but let's 126 ** be aggressive and do it anyway, just in case 127 ** something's out of sync 128 */ 129 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev); 130 } 131 132 static void fdevent_update(fdevent *fde, unsigned events) 133 { 134 struct epoll_event ev; 135 int active; 136 137 active = (fde->state & FDE_EVENTMASK) != 0; 138 139 memset(&ev, 0, sizeof(ev)); 140 ev.events = 0; 141 ev.data.ptr = fde; 142 143 if(events & FDE_READ) ev.events |= EPOLLIN; 144 if(events & FDE_WRITE) ev.events |= EPOLLOUT; 145 if(events & FDE_ERROR) ev.events |= (EPOLLERR | EPOLLHUP); 146 147 fde->state = (fde->state & FDE_STATEMASK) | events; 148 149 if(active) { 150 /* we're already active. if we're changing to *no* 151 ** events being monitored, we need to delete, otherwise 152 ** we need to just modify 153 */ 154 if(ev.events) { 155 if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fde->fd, &ev)) { 156 perror("epoll_ctl() failed\n"); 157 exit(1); 158 } 159 } else { 160 if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fde->fd, &ev)) { 161 perror("epoll_ctl() failed\n"); 162 exit(1); 163 } 164 } 165 } else { 166 /* we're not active. if we're watching events, we need 167 ** to add, otherwise we can just do nothing 168 */ 169 if(ev.events) { 170 if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fde->fd, &ev)) { 171 perror("epoll_ctl() failed\n"); 172 exit(1); 173 } 174 } 175 } 176 } 177 178 static void fdevent_process() 179 { 180 struct epoll_event events[256]; 181 fdevent *fde; 182 int i, n; 183 184 n = epoll_wait(epoll_fd, events, 256, -1); 185 186 if(n < 0) { 187 if(errno == EINTR) return; 188 perror("epoll_wait"); 189 exit(1); 190 } 191 192 for(i = 0; i < n; i++) { 193 struct epoll_event *ev = events + i; 194 fde = ev->data.ptr; 195 196 if(ev->events & EPOLLIN) { 197 fde->events |= FDE_READ; 198 } 199 if(ev->events & EPOLLOUT) { 200 fde->events |= FDE_WRITE; 201 } 202 if(ev->events & (EPOLLERR | EPOLLHUP)) { 203 fde->events |= FDE_ERROR; 204 } 205 if(fde->events) { 206 if(fde->state & FDE_PENDING) continue; 207 fde->state |= FDE_PENDING; 208 fdevent_plist_enqueue(fde); 209 } 210 } 211 } 212 213 #else /* USE_SELECT */ 214 215 #ifdef HAVE_WINSOCK 216 #include <winsock2.h> 217 #else 218 #include <sys/select.h> 219 #endif 220 221 static fd_set read_fds; 222 static fd_set write_fds; 223 static fd_set error_fds; 224 225 static int select_n = 0; 226 227 static void fdevent_init(void) 228 { 229 FD_ZERO(&read_fds); 230 FD_ZERO(&write_fds); 231 FD_ZERO(&error_fds); 232 } 233 234 static void fdevent_connect(fdevent *fde) 235 { 236 if(fde->fd >= select_n) { 237 select_n = fde->fd + 1; 238 } 239 } 240 241 static void fdevent_disconnect(fdevent *fde) 242 { 243 int i, n; 244 245 FD_CLR(fde->fd, &read_fds); 246 FD_CLR(fde->fd, &write_fds); 247 FD_CLR(fde->fd, &error_fds); 248 249 for(n = 0, i = 0; i < select_n; i++) { 250 if(fd_table[i] != 0) n = i; 251 } 252 select_n = n + 1; 253 } 254 255 static void fdevent_update(fdevent *fde, unsigned events) 256 { 257 if(events & FDE_READ) { 258 FD_SET(fde->fd, &read_fds); 259 } else { 260 FD_CLR(fde->fd, &read_fds); 261 } 262 if(events & FDE_WRITE) { 263 FD_SET(fde->fd, &write_fds); 264 } else { 265 FD_CLR(fde->fd, &write_fds); 266 } 267 if(events & FDE_ERROR) { 268 FD_SET(fde->fd, &error_fds); 269 } else { 270 FD_CLR(fde->fd, &error_fds); 271 } 272 273 fde->state = (fde->state & FDE_STATEMASK) | events; 274 } 275 276 static void fdevent_process() 277 { 278 int i, n; 279 fdevent *fde; 280 unsigned events; 281 fd_set rfd, wfd, efd; 282 283 memcpy(&rfd, &read_fds, sizeof(fd_set)); 284 memcpy(&wfd, &write_fds, sizeof(fd_set)); 285 memcpy(&efd, &error_fds, sizeof(fd_set)); 286 287 n = select(select_n, &rfd, &wfd, &efd, 0); 288 289 if(n < 0) { 290 if(errno == EINTR) return; 291 perror("select"); 292 return; 293 } 294 295 for(i = 0; (i < select_n) && (n > 0); i++) { 296 events = 0; 297 if(FD_ISSET(i, &rfd)) events |= FDE_READ; 298 if(FD_ISSET(i, &wfd)) events |= FDE_WRITE; 299 if(FD_ISSET(i, &efd)) events |= FDE_ERROR; 300 301 if(events) { 302 n--; 303 304 fde = fd_table[i]; 305 if(fde == 0) FATAL("missing fde for fd %d\n", i); 306 307 fde->events |= events; 308 309 if(fde->state & FDE_PENDING) continue; 310 fde->state |= FDE_PENDING; 311 fdevent_plist_enqueue(fde); 312 } 313 } 314 } 315 316 #endif 317 318 static void fdevent_register(fdevent *fde) 319 { 320 if(fde->fd < 0) { 321 FATAL("bogus negative fd (%d)\n", fde->fd); 322 } 323 324 if(fde->fd >= fd_table_max) { 325 int oldmax = fd_table_max; 326 if(fde->fd > 32000) { 327 FATAL("bogus huuuuge fd (%d)\n", fde->fd); 328 } 329 if(fd_table_max == 0) { 330 fdevent_init(); 331 fd_table_max = 256; 332 } 333 while(fd_table_max <= fde->fd) { 334 fd_table_max *= 2; 335 } 336 fd_table = realloc(fd_table, sizeof(fdevent*) * fd_table_max); 337 if(fd_table == 0) { 338 FATAL("could not expand fd_table to %d entries\n", fd_table_max); 339 } 340 memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax)); 341 } 342 343 fd_table[fde->fd] = fde; 344 } 345 346 static void fdevent_unregister(fdevent *fde) 347 { 348 if((fde->fd < 0) || (fde->fd >= fd_table_max)) { 349 FATAL("fd out of range (%d)\n", fde->fd); 350 } 351 352 if(fd_table[fde->fd] != fde) { 353 FATAL("fd_table out of sync"); 354 } 355 356 fd_table[fde->fd] = 0; 357 358 if(!(fde->state & FDE_DONT_CLOSE)) { 359 dump_fde(fde, "close"); 360 close(fde->fd); 361 } 362 } 363 364 static void fdevent_plist_enqueue(fdevent *node) 365 { 366 fdevent *list = &list_pending; 367 368 node->next = list; 369 node->prev = list->prev; 370 node->prev->next = node; 371 list->prev = node; 372 } 373 374 static void fdevent_plist_remove(fdevent *node) 375 { 376 node->prev->next = node->next; 377 node->next->prev = node->prev; 378 node->next = 0; 379 node->prev = 0; 380 } 381 382 static fdevent *fdevent_plist_dequeue(void) 383 { 384 fdevent *list = &list_pending; 385 fdevent *node = list->next; 386 387 if(node == list) return 0; 388 389 list->next = node->next; 390 list->next->prev = list; 391 node->next = 0; 392 node->prev = 0; 393 394 return node; 395 } 396 397 fdevent *fdevent_create(int fd, fd_func func, void *arg) 398 { 399 fdevent *fde = (fdevent*) malloc(sizeof(fdevent)); 400 if(fde == 0) return 0; 401 fdevent_install(fde, fd, func, arg); 402 fde->state |= FDE_CREATED; 403 return fde; 404 } 405 406 void fdevent_destroy(fdevent *fde) 407 { 408 if(fde == 0) return; 409 if(!(fde->state & FDE_CREATED)) { 410 FATAL("fde %p not created by fdevent_create()\n", fde); 411 } 412 fdevent_remove(fde); 413 } 414 415 void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg) 416 { 417 memset(fde, 0, sizeof(fdevent)); 418 fde->state = FDE_ACTIVE; 419 fde->fd = fd; 420 fde->func = func; 421 fde->arg = arg; 422 423 #ifndef HAVE_WINSOCK 424 fcntl(fd, F_SETFL, O_NONBLOCK); 425 #endif 426 fdevent_register(fde); 427 dump_fde(fde, "connect"); 428 fdevent_connect(fde); 429 fde->state |= FDE_ACTIVE; 430 } 431 432 void fdevent_remove(fdevent *fde) 433 { 434 if(fde->state & FDE_PENDING) { 435 fdevent_plist_remove(fde); 436 } 437 438 if(fde->state & FDE_ACTIVE) { 439 fdevent_disconnect(fde); 440 dump_fde(fde, "disconnect"); 441 fdevent_unregister(fde); 442 } 443 444 fde->state = 0; 445 fde->events = 0; 446 } 447 448 449 void fdevent_set(fdevent *fde, unsigned events) 450 { 451 events &= FDE_EVENTMASK; 452 453 if((fde->state & FDE_EVENTMASK) == events) return; 454 455 if(fde->state & FDE_ACTIVE) { 456 fdevent_update(fde, events); 457 dump_fde(fde, "update"); 458 } 459 460 fde->state = (fde->state & FDE_STATEMASK) | events; 461 462 if(fde->state & FDE_PENDING) { 463 /* if we're pending, make sure 464 ** we don't signal an event that 465 ** is no longer wanted. 466 */ 467 fde->events &= (~events); 468 if(fde->events == 0) { 469 fdevent_plist_remove(fde); 470 fde->state &= (~FDE_PENDING); 471 } 472 } 473 } 474 475 void fdevent_add(fdevent *fde, unsigned events) 476 { 477 fdevent_set( 478 fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK)); 479 } 480 481 void fdevent_del(fdevent *fde, unsigned events) 482 { 483 fdevent_set( 484 fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK))); 485 } 486 487 void fdevent_loop() 488 { 489 fdevent *fde; 490 491 for(;;) { 492 #if DEBUG 493 fprintf(stderr,"--- ---- waiting for events\n"); 494 #endif 495 fdevent_process(); 496 497 while((fde = fdevent_plist_dequeue())) { 498 unsigned events = fde->events; 499 fde->events = 0; 500 fde->state &= (~FDE_PENDING); 501 dump_fde(fde, "callback"); 502 fde->func(fde->fd, events, fde->arg); 503 } 504 } 505 } 506 507