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