1/*********************************************************************** 2* 3* event.c 4* 5* Abstraction of select call into "event-handling" to make programming 6* easier. 7* 8* Copyright (C) 2001 Roaring Penguin Software Inc. 9* 10* This program may be distributed according to the terms of the GNU 11* General Public License, version 2 or (at your option) any later version. 12* 13* LIC: GPL 14* 15***********************************************************************/ 16 17static char const RCSID[] = 18"$Id: event.c 3323 2011-09-21 18:45:48Z lly.dev $"; 19 20#include "event.h" 21#include <stdlib.h> 22#include <errno.h> 23 24static void DestroySelector(EventSelector *es); 25static void DestroyHandler(EventHandler *eh); 26static void DoPendingChanges(EventSelector *es); 27 28/********************************************************************** 29* %FUNCTION: Event_CreateSelector 30* %ARGUMENTS: 31* None 32* %RETURNS: 33* A newly-allocated EventSelector, or NULL if out of memory. 34* %DESCRIPTION: 35* Creates a new EventSelector. 36***********************************************************************/ 37EventSelector * 38Event_CreateSelector(void) 39{ 40 EventSelector *es = malloc(sizeof(EventSelector)); 41 if (!es) return NULL; 42 es->handlers = NULL; 43 es->nestLevel = 0; 44 es->destroyPending = 0; 45 es->opsPending = 0; 46 EVENT_DEBUG(("CreateSelector() -> %p\n", (void *) es)); 47 return es; 48} 49 50/********************************************************************** 51* %FUNCTION: Event_DestroySelector 52* %ARGUMENTS: 53* es -- EventSelector to destroy 54* %RETURNS: 55* Nothing 56* %DESCRIPTION: 57* Destroys an EventSelector. Destruction may be delayed if we 58* are in the HandleEvent function. 59***********************************************************************/ 60void 61Event_DestroySelector(EventSelector *es) 62{ 63 if (es->nestLevel) { 64 es->destroyPending = 1; 65 es->opsPending = 1; 66 return; 67 } 68 DestroySelector(es); 69} 70 71/********************************************************************** 72* %FUNCTION: Event_HandleEvent 73* %ARGUMENTS: 74* es -- EventSelector 75* %RETURNS: 76* 0 if OK, non-zero on error. errno is set appropriately. 77* %DESCRIPTION: 78* Handles a single event (uses select() to wait for an event.) 79***********************************************************************/ 80int 81Event_HandleEvent(EventSelector *es) 82{ 83 fd_set readfds, writefds; 84 fd_set *rd, *wr; 85 unsigned int flags; 86 87 struct timeval abs_timeout, now; 88 struct timeval timeout; 89 struct timeval *tm; 90 EventHandler *eh; 91 92 int r = 0; 93 int errno_save = 0; 94 int foundTimeoutEvent = 0; 95 int foundReadEvent = 0; 96 int foundWriteEvent = 0; 97 int maxfd = -1; 98 int pastDue; 99 100 EVENT_DEBUG(("Enter Event_HandleEvent(es=%p)\n", (void *) es)); 101 102 /* Build the select sets */ 103 FD_ZERO(&readfds); 104 FD_ZERO(&writefds); 105 106 eh = es->handlers; 107 for (eh=es->handlers; eh; eh=eh->next) { 108 if (eh->flags & EVENT_FLAG_DELETED) continue; 109 if (eh->flags & EVENT_FLAG_READABLE) { 110 foundReadEvent = 1; 111 FD_SET(eh->fd, &readfds); 112 if (eh->fd > maxfd) maxfd = eh->fd; 113 } 114 if (eh->flags & EVENT_FLAG_WRITEABLE) { 115 foundWriteEvent = 1; 116 FD_SET(eh->fd, &writefds); 117 if (eh->fd > maxfd) maxfd = eh->fd; 118 } 119 if (eh->flags & EVENT_TIMER_BITS) { 120 if (!foundTimeoutEvent) { 121 abs_timeout = eh->tmout; 122 foundTimeoutEvent = 1; 123 } else { 124 if (eh->tmout.tv_sec < abs_timeout.tv_sec || 125 (eh->tmout.tv_sec == abs_timeout.tv_sec && 126 eh->tmout.tv_usec < abs_timeout.tv_usec)) { 127 abs_timeout = eh->tmout; 128 } 129 } 130 } 131 } 132 if (foundReadEvent) { 133 rd = &readfds; 134 } else { 135 rd = NULL; 136 } 137 if (foundWriteEvent) { 138 wr = &writefds; 139 } else { 140 wr = NULL; 141 } 142 143 if (foundTimeoutEvent) { 144 gettimeofday(&now, NULL); 145 /* Convert absolute timeout to relative timeout for select */ 146 timeout.tv_usec = abs_timeout.tv_usec - now.tv_usec; 147 timeout.tv_sec = abs_timeout.tv_sec - now.tv_sec; 148 if (timeout.tv_usec < 0) { 149 timeout.tv_usec += 1000000; 150 timeout.tv_sec--; 151 } 152 if (timeout.tv_sec < 0 || 153 (timeout.tv_sec == 0 && timeout.tv_usec < 0)) { 154 timeout.tv_sec = 0; 155 timeout.tv_usec = 0; 156 } 157 tm = &timeout; 158 } else { 159 tm = NULL; 160 } 161 162 if (foundReadEvent || foundWriteEvent || foundTimeoutEvent) { 163 for(;;) { 164 r = select(maxfd+1, rd, wr, NULL, tm); 165 if (r < 0) { 166 if (errno == EINTR) continue; 167 } 168 break; 169 } 170 } 171 172 if (foundTimeoutEvent) gettimeofday(&now, NULL); 173 errno_save = errno; 174 es->nestLevel++; 175 176 if (r >= 0) { 177 /* Call handlers */ 178 for (eh=es->handlers; eh; eh=eh->next) { 179 180 /* Pending delete for this handler? Ignore it */ 181 if (eh->flags & EVENT_FLAG_DELETED) continue; 182 183 flags = 0; 184 if ((eh->flags & EVENT_FLAG_READABLE) && 185 FD_ISSET(eh->fd, &readfds)) { 186 flags |= EVENT_FLAG_READABLE; 187 } 188 if ((eh->flags & EVENT_FLAG_WRITEABLE) && 189 FD_ISSET(eh->fd, &writefds)) { 190 flags |= EVENT_FLAG_WRITEABLE; 191 } 192 if (eh->flags & EVENT_TIMER_BITS) { 193 pastDue = (eh->tmout.tv_sec < now.tv_sec || 194 (eh->tmout.tv_sec == now.tv_sec && 195 eh->tmout.tv_usec <= now.tv_usec)); 196 if (pastDue) { 197 flags |= EVENT_TIMER_BITS; 198 if (eh->flags & EVENT_FLAG_TIMER) { 199 /* Timer events are only called once */ 200 es->opsPending = 1; 201 eh->flags |= EVENT_FLAG_DELETED; 202 } 203 } 204 } 205 /* Do callback */ 206 if (flags) { 207 EVENT_DEBUG(("Enter callback: eh=%p flags=%u\n", eh, flags)); 208 eh->fn(es, eh->fd, flags, eh->data); 209 EVENT_DEBUG(("Leave callback: eh=%p flags=%u\n", eh, flags)); 210 } 211 } 212 } 213 214 es->nestLevel--; 215 216 if (!es->nestLevel && es->opsPending) { 217 DoPendingChanges(es); 218 } 219 errno = errno_save; 220 return r; 221} 222 223/********************************************************************** 224* %FUNCTION: Event_AddHandler 225* %ARGUMENTS: 226* es -- event selector 227* fd -- file descriptor to watch 228* flags -- combination of EVENT_FLAG_READABLE and EVENT_FLAG_WRITEABLE 229* fn -- callback function to call when event is triggered 230* data -- extra data to pass to callback function 231* %RETURNS: 232* A newly-allocated EventHandler, or NULL. 233***********************************************************************/ 234EventHandler * 235Event_AddHandler(EventSelector *es, 236 int fd, 237 unsigned int flags, 238 EventCallbackFunc fn, 239 void *data) 240{ 241 EventHandler *eh; 242 243 /* Specifically disable timer and deleted flags */ 244 flags &= (~(EVENT_TIMER_BITS | EVENT_FLAG_DELETED)); 245 246 /* Bad file descriptor */ 247 if (fd < 0) { 248 errno = EBADF; 249 return NULL; 250 } 251 252 eh = malloc(sizeof(EventHandler)); 253 if (!eh) return NULL; 254 eh->fd = fd; 255 eh->flags = flags; 256 eh->tmout.tv_usec = 0; 257 eh->tmout.tv_sec = 0; 258 eh->fn = fn; 259 eh->data = data; 260 261 /* Add immediately. This is safe even if we are in a handler. */ 262 eh->next = es->handlers; 263 es->handlers = eh; 264 265 EVENT_DEBUG(("Event_AddHandler(es=%p, fd=%d, flags=%u) -> %p\n", es, fd, flags, eh)); 266 return eh; 267} 268 269/********************************************************************** 270* %FUNCTION: Event_AddHandlerWithTimeout 271* %ARGUMENTS: 272* es -- event selector 273* fd -- file descriptor to watch 274* flags -- combination of EVENT_FLAG_READABLE and EVENT_FLAG_WRITEABLE 275* t -- Timeout after which to call handler, even if not readable/writable. 276* If t.tv_sec < 0, calls normal Event_AddHandler with no timeout. 277* fn -- callback function to call when event is triggered 278* data -- extra data to pass to callback function 279* %RETURNS: 280* A newly-allocated EventHandler, or NULL. 281***********************************************************************/ 282EventHandler * 283Event_AddHandlerWithTimeout(EventSelector *es, 284 int fd, 285 unsigned int flags, 286 struct timeval t, 287 EventCallbackFunc fn, 288 void *data) 289{ 290 EventHandler *eh; 291 struct timeval now; 292 293 /* If timeout is negative, just do normal non-timing-out event */ 294 if (t.tv_sec < 0 || t.tv_usec < 0) { 295 return Event_AddHandler(es, fd, flags, fn, data); 296 } 297 298 /* Specifically disable timer and deleted flags */ 299 flags &= (~(EVENT_FLAG_TIMER | EVENT_FLAG_DELETED)); 300 flags |= EVENT_FLAG_TIMEOUT; 301 302 /* Bad file descriptor? */ 303 if (fd < 0) { 304 errno = EBADF; 305 return NULL; 306 } 307 308 /* Bad timeout? */ 309 if (t.tv_usec >= 1000000) { 310 errno = EINVAL; 311 return NULL; 312 } 313 314 eh = malloc(sizeof(EventHandler)); 315 if (!eh) return NULL; 316 317 /* Convert time interval to absolute time */ 318 gettimeofday(&now, NULL); 319 320 t.tv_sec += now.tv_sec; 321 t.tv_usec += now.tv_usec; 322 if (t.tv_usec >= 1000000) { 323 t.tv_usec -= 1000000; 324 t.tv_sec++; 325 } 326 327 eh->fd = fd; 328 eh->flags = flags; 329 eh->tmout = t; 330 eh->fn = fn; 331 eh->data = data; 332 333 /* Add immediately. This is safe even if we are in a handler. */ 334 eh->next = es->handlers; 335 es->handlers = eh; 336 337 EVENT_DEBUG(("Event_AddHandlerWithTimeout(es=%p, fd=%d, flags=%u, t=%d/%d) -> %p\n", es, fd, flags, t.tv_sec, t.tv_usec, eh)); 338 return eh; 339} 340 341 342/********************************************************************** 343* %FUNCTION: Event_AddTimerHandler 344* %ARGUMENTS: 345* es -- event selector 346* t -- time interval after which to trigger event 347* fn -- callback function to call when event is triggered 348* data -- extra data to pass to callback function 349* %RETURNS: 350* A newly-allocated EventHandler, or NULL. 351***********************************************************************/ 352EventHandler * 353Event_AddTimerHandler(EventSelector *es, 354 struct timeval t, 355 EventCallbackFunc fn, 356 void *data) 357{ 358 EventHandler *eh; 359 struct timeval now; 360 361 /* Check time interval for validity */ 362 if (t.tv_sec < 0 || t.tv_usec < 0 || t.tv_usec >= 1000000) { 363 errno = EINVAL; 364 return NULL; 365 } 366 367 eh = malloc(sizeof(EventHandler)); 368 if (!eh) return NULL; 369 370 /* Convert time interval to absolute time */ 371 gettimeofday(&now, NULL); 372 373 t.tv_sec += now.tv_sec; 374 t.tv_usec += now.tv_usec; 375 if (t.tv_usec >= 1000000) { 376 t.tv_usec -= 1000000; 377 t.tv_sec++; 378 } 379 380 eh->fd = -1; 381 eh->flags = EVENT_FLAG_TIMER; 382 eh->tmout = t; 383 eh->fn = fn; 384 eh->data = data; 385 386 /* Add immediately. This is safe even if we are in a handler. */ 387 eh->next = es->handlers; 388 es->handlers = eh; 389 390 EVENT_DEBUG(("Event_AddTimerHandler(es=%p, t=%d/%d) -> %p\n", es, t.tv_sec,t.tv_usec, eh)); 391 return eh; 392} 393 394/********************************************************************** 395* %FUNCTION: Event_DelHandler 396* %ARGUMENTS: 397* es -- event selector 398* eh -- event handler 399* %RETURNS: 400* 0 if OK, non-zero if there is an error 401* %DESCRIPTION: 402* Deletes the event handler eh 403***********************************************************************/ 404int 405Event_DelHandler(EventSelector *es, 406 EventHandler *eh) 407{ 408 /* Scan the handlers list */ 409 EventHandler *cur, *prev; 410 EVENT_DEBUG(("Event_DelHandler(es=%p, eh=%p)\n", es, eh)); 411 for (cur=es->handlers, prev=NULL; cur; prev=cur, cur=cur->next) { 412 if (cur == eh) { 413 if (es->nestLevel) { 414 eh->flags |= EVENT_FLAG_DELETED; 415 es->opsPending = 1; 416 return 0; 417 } else { 418 if (prev) prev->next = cur->next; 419 else es->handlers = cur->next; 420 421 DestroyHandler(cur); 422 return 0; 423 } 424 } 425 } 426 427 /* Handler not found */ 428 return 1; 429} 430 431/********************************************************************** 432* %FUNCTION: DestroySelector 433* %ARGUMENTS: 434* es -- an event selector 435* %RETURNS: 436* Nothing 437* %DESCRIPTION: 438* Destroys selector and all associated handles. 439***********************************************************************/ 440void 441DestroySelector(EventSelector *es) 442{ 443 EventHandler *cur, *next; 444 for (cur=es->handlers; cur; cur=next) { 445 next = cur->next; 446 DestroyHandler(cur); 447 } 448 449 free(es); 450} 451 452/********************************************************************** 453* %FUNCTION: DestroyHandler 454* %ARGUMENTS: 455* eh -- an event handler 456* %RETURNS: 457* Nothing 458* %DESCRIPTION: 459* Destroys handler 460***********************************************************************/ 461void 462DestroyHandler(EventHandler *eh) 463{ 464 EVENT_DEBUG(("DestroyHandler(eh=%p)\n", eh)); 465 free(eh); 466} 467 468/********************************************************************** 469* %FUNCTION: DoPendingChanges 470* %ARGUMENTS: 471* es -- an event selector 472* %RETURNS: 473* Nothing 474* %DESCRIPTION: 475* Makes all pending insertions and deletions happen. 476***********************************************************************/ 477void 478DoPendingChanges(EventSelector *es) 479{ 480 EventHandler *cur, *prev, *next; 481 482 es->opsPending = 0; 483 484 /* If selector is to be deleted, do it and skip everything else */ 485 if (es->destroyPending) { 486 DestroySelector(es); 487 return; 488 } 489 490 /* Do deletions */ 491 cur = es->handlers; 492 prev = NULL; 493 while(cur) { 494 if (!(cur->flags & EVENT_FLAG_DELETED)) { 495 prev = cur; 496 cur = cur->next; 497 continue; 498 } 499 500 /* Unlink from list */ 501 if (prev) { 502 prev->next = cur->next; 503 } else { 504 es->handlers = cur->next; 505 } 506 next = cur->next; 507 DestroyHandler(cur); 508 cur = next; 509 } 510} 511 512/********************************************************************** 513* %FUNCTION: Event_GetCallback 514* %ARGUMENTS: 515* eh -- the event handler 516* %RETURNS: 517* The callback function 518***********************************************************************/ 519EventCallbackFunc 520Event_GetCallback(EventHandler *eh) 521{ 522 return eh->fn; 523} 524 525/********************************************************************** 526* %FUNCTION: Event_GetData 527* %ARGUMENTS: 528* eh -- the event handler 529* %RETURNS: 530* The "data" field. 531***********************************************************************/ 532void * 533Event_GetData(EventHandler *eh) 534{ 535 return eh->data; 536} 537 538/********************************************************************** 539* %FUNCTION: Event_SetCallbackAndData 540* %ARGUMENTS: 541* eh -- the event handler 542* fn -- new callback function 543* data -- new data value 544* %RETURNS: 545* Nothing 546* %DESCRIPTION: 547* Sets the callback function and data fields. 548***********************************************************************/ 549void 550Event_SetCallbackAndData(EventHandler *eh, 551 EventCallbackFunc fn, 552 void *data) 553{ 554 eh->fn = fn; 555 eh->data = data; 556} 557 558#ifdef DEBUG_EVENT 559#include <stdarg.h> 560#include <stdio.h> 561FILE *Event_DebugFP = NULL; 562/********************************************************************** 563* %FUNCTION: Event_DebugMsg 564* %ARGUMENTS: 565* fmt, ... -- format string 566* %RETURNS: 567* Nothing 568* %DESCRIPTION: 569* Writes a debug message to the debug file. 570***********************************************************************/ 571void 572Event_DebugMsg(char const *fmt, ...) 573{ 574 va_list ap; 575 struct timeval now; 576 577 if (!Event_DebugFP) return; 578 579 gettimeofday(&now, NULL); 580 581 fprintf(Event_DebugFP, "%03d.%03d ", (int) now.tv_sec % 1000, 582 (int) now.tv_usec / 1000); 583 584 va_start(ap, fmt); 585 vfprintf(Event_DebugFP, fmt, ap); 586 va_end(ap); 587 fflush(Event_DebugFP); 588} 589 590#endif 591 592/********************************************************************** 593* %FUNCTION: Event_EnableDebugging 594* %ARGUMENTS: 595* fname -- name of file to log debug messages to 596* %RETURNS: 597* 1 if debugging was enabled; 0 otherwise. 598***********************************************************************/ 599int 600Event_EnableDebugging(char const *fname) 601{ 602#ifndef DEBUG_EVENT 603 return 0; 604#else 605 Event_DebugFP = fopen(fname, "w"); 606 return (Event_DebugFP != NULL); 607#endif 608} 609 610/********************************************************************** 611* %FUNCTION: Event_ChangeTimeout 612* %ARGUMENTS: 613* h -- event handler 614* t -- new timeout 615* %RETURNS: 616* Nothing 617* %DESCRIPTION: 618* Changes timeout of event handler to be "t" seconds in the future. 619***********************************************************************/ 620void 621Event_ChangeTimeout(EventHandler *h, struct timeval t) 622{ 623 struct timeval now; 624 625 /* Check time interval for validity */ 626 if (t.tv_sec < 0 || t.tv_usec < 0 || t.tv_usec >= 1000000) { 627 return; 628 } 629 /* Convert time interval to absolute time */ 630 gettimeofday(&now, NULL); 631 632 t.tv_sec += now.tv_sec; 633 t.tv_usec += now.tv_usec; 634 if (t.tv_usec >= 1000000) { 635 t.tv_usec -= 1000000; 636 t.tv_sec++; 637 } 638 639 h->tmout = t; 640} 641