fdevent.c revision 865f6f9869281ff04cf890bfe74f3598d9363a08
1/* http://frotznet.googlecode.com/svn/trunk/utils/fdevent.c 2** 3** Copyright 2006, Brian Swetland <swetland@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 35static 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 48static 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 67static void fdevent_plist_enqueue(fdevent *node); 68static void fdevent_plist_remove(fdevent *node); 69static fdevent *fdevent_plist_dequeue(void); 70 71static fdevent list_pending = { 72 .next = &list_pending, 73 .prev = &list_pending, 74}; 75 76static fdevent **fd_table = 0; 77static int fd_table_max = 0; 78 79#ifdef CRAPTASTIC 80//HAVE_EPOLL 81 82#include <sys/epoll.h> 83 84static int epoll_fd = -1; 85 86static 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 100static 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 116static 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 132static 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 178static 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 221static fd_set read_fds; 222static fd_set write_fds; 223static fd_set error_fds; 224 225static int select_n = 0; 226 227static void fdevent_init(void) 228{ 229 FD_ZERO(&read_fds); 230 FD_ZERO(&write_fds); 231 FD_ZERO(&error_fds); 232} 233 234static void fdevent_connect(fdevent *fde) 235{ 236 if(fde->fd >= select_n) { 237 select_n = fde->fd + 1; 238 } 239} 240 241static 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 255static 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 276static 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 318static 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 346static 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 364static 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 374static 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 382static 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 397fdevent *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 406void 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 415void 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 432void 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 449void 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 475void fdevent_add(fdevent *fde, unsigned events) 476{ 477 fdevent_set( 478 fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK)); 479} 480 481void fdevent_del(fdevent *fde, unsigned events) 482{ 483 fdevent_set( 484 fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK))); 485} 486 487void 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