1/* 2 * Copyright (c)1996-2002 by Hartmut Brandt 3 * All rights reserved. 4 * 5 * Author: Hartmut Brandt 6 * 7 * Redistribution of this software and documentation and use in source and 8 * binary forms, with or without modification, are permitted provided that 9 * the following conditions are met: 10 * 11 * 1. Redistributions of source code or documentation must retain the above 12 * copyright notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE AUTHOR 18 * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 19 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21 * THE AUTHOR OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 24 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29/* 30 * These functions try to hide the poll/select/setitimer interface from the 31 * user. You associate callback functions with file descriptors and timers. 32 * 33 * $Begemot: libbegemot/rpoll.c,v 1.14 2004/09/21 15:59:00 brandt Exp $ 34 */ 35# include <stdio.h> 36# include <stdlib.h> 37# include <stddef.h> 38# include <stdarg.h> 39# include <signal.h> 40# include <string.h> 41# include <errno.h> 42# include <time.h> 43# include <assert.h> 44# include <unistd.h> 45# include <sys/time.h> 46 47# include "rpoll.h" 48 49/* 50# define DEBUG 51*/ 52 53# ifdef USE_POLL 54# ifdef NEED_POLL_XOPEN_TWIDDLE 55# define __USE_XOPEN 56# endif 57# include <poll.h> 58# ifdef NEED_POLL_XOPEN_TWIDDLE 59# undef __USE_XOPEN 60# endif 61# include <stropts.h> 62# endif 63 64/* 65 * the second define is for Linux, which sometimes fails to 66 * declare INFTIM. 67 */ 68# if defined(USE_SELECT) || !defined(INFTIM) 69# define INFTIM (-1) 70# endif 71 72# if defined(SIGPOLL) 73# define SIGNAL SIGPOLL 74# else 75# if defined(SIGIO) 76# define SIGNAL SIGIO 77# endif 78# endif 79 80# ifdef USE_POLL 81# define poll_in (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI) 82# define poll_out (POLLOUT | POLLWRNORM | POLLWRBAND) 83# define poll_except (POLLERR | POLLHUP) 84# endif 85 86# ifdef BROKEN_SELECT_PROTO 87# define SELECT_CAST(P) (int *)P 88# else 89# define SELECT_CAST(P) P 90# endif 91 92 93typedef int64_t tval_t; 94 95static inline tval_t GETUSECS(void); 96 97static inline tval_t 98GETUSECS(void) { 99 struct timeval tval; 100 101 (void)gettimeofday(&tval, NULL); 102 return (tval_t)tval.tv_sec * 1000000 + tval.tv_usec; 103} 104 105/* 106 * Simple fatal exit. 107 */ 108static void 109_panic(const char *fmt, ...) 110{ 111 va_list ap; 112 113 va_start(ap, fmt); 114 fprintf(stderr, "panic: "); 115 vfprintf(stderr, fmt, ap); 116 fprintf(stderr, "\n"); 117 va_end(ap); 118 119 exit(1); 120} 121 122static void * 123_xrealloc(void *p, size_t s) 124{ 125 void *ptr; 126 127 if(p == NULL) { 128 if((ptr=malloc(s)) == NULL && (s!=0 || (ptr=malloc(1)) == NULL)) 129 _panic("out of memory: xrealloc(%lx, %lu)", 130 (unsigned long)p, (unsigned long)s); 131 } else if(s == 0) { 132 free(p); 133 if((ptr=malloc(s)) == NULL && (ptr=malloc(1)) == NULL) 134 _panic("out of memory: xrealloc(%lx, %lu)", 135 (unsigned long)p, (unsigned long)s); 136 } else { 137 if((ptr = realloc(p, s)) == NULL) 138 _panic("out of memory: xrealloc(%lx, %lu)", 139 (unsigned long)p, (unsigned long)s); 140 } 141 142 return ptr; 143} 144 145/* 146 * This structure holds one registration record for files 147 */ 148typedef struct { 149 int fd; /* file descriptor (-1 if struct unused) */ 150 int mask; /* event flags */ 151 void * arg; /* client arg */ 152 poll_f func; /* handler */ 153# ifdef USE_POLL 154 struct pollfd *pfd; /* pointer to corresponding poll() structure */ 155# endif 156} PollReg_t; 157 158/* 159 * Now for timers 160 */ 161typedef struct { 162 uint64_t usecs; /* microsecond value of the timer */ 163 int repeat; /* one shot or repeat? */ 164 void *arg; /* client arg */ 165 timer_f func; /* handler, 0 means disfunct */ 166 tval_t when; /* next time to trigger in usecs! */ 167} PollTim_t; 168 169/* how many records should our table grow at once? */ 170# define POLL_REG_GROW 100 171 172# ifdef USE_POLL 173static struct pollfd * pfd; /* fd list for poll() */ 174# endif 175 176# ifdef USE_SELECT 177static fd_set rset, wset, xset; /* file descriptor sets for select() */ 178static int maxfd; /* maximum fd number */ 179# endif 180 181static int in_dispatch; 182 183static PollReg_t * regs; /* registration records */ 184static u_int regs_alloc; /* how many are allocated */ 185static u_int regs_used; /* upper used limit */ 186static sigset_t bset; /* blocked signals */ 187static int rebuild; /* rebuild table on next dispatch() */ 188 189static int * tfd; /* sorted entries */ 190static u_int tfd_alloc; /* number of entries allocated */ 191static u_int tfd_used; /* number of entries used */ 192static PollTim_t * tims; /* timer registration records */ 193static u_int tims_alloc; /* how many are allocated */ 194static u_int tims_used; /* how many are used */ 195static int resort; /* resort on next dispatch */ 196 197int rpoll_trace; 198int rpoll_policy; /* if 0 start sched callbacks from 0 else try round robin */ 199 200static void poll_build(void); 201static void poll_blocksig(void); 202static void poll_unblocksig(void); 203static void sort_timers(void); 204 205 206/* 207 * Private function to block SIGPOLL or SIGIO for a short time. 208 * Don't forget to call poll_unblock before return from the calling function. 209 * Don't change the mask between this calls (your changes will be lost). 210 */ 211static void 212poll_blocksig(void) 213{ 214 sigset_t set; 215 216 sigemptyset(&set); 217 sigaddset(&set, SIGNAL); 218 219 if(sigprocmask(SIG_BLOCK, &set, &bset)) 220 _panic("sigprocmask(SIG_BLOCK): %s", strerror(errno)); 221} 222 223/* 224 * unblock the previously blocked signal 225 */ 226static void 227poll_unblocksig(void) 228{ 229 if(sigprocmask(SIG_SETMASK, &bset, NULL)) 230 _panic("sigprocmask(SIG_SETMASK): %s", strerror(errno)); 231} 232 233/* 234 * Register the file descriptor fd. If the event corresponding to 235 * mask arrives func is called with arg. 236 * If fd is already registered with that func and arg, only the mask 237 * is changed. 238 * We block the IO-signal, so the dispatch function can be called from 239 * within the signal handler. 240 */ 241int 242poll_register(int fd, poll_f func, void *arg, int mask) 243{ 244 PollReg_t * p; 245 246 poll_blocksig(); 247 248 /* already registered? */ 249 for(p = regs; p < ®s[regs_alloc]; p++) 250 if(p->fd == fd && p->func == func && p->arg == arg) { 251 p->mask = mask; 252 break; 253 } 254 255 if(p == ®s[regs_alloc]) { 256 /* no - register */ 257 258 /* find a free slot */ 259 for(p = regs; p < ®s[regs_alloc]; p++) 260 if(p->fd == -1) 261 break; 262 263 if(p == ®s[regs_alloc]) { 264 size_t newsize = regs_alloc + POLL_REG_GROW; 265 regs = _xrealloc(regs, sizeof(regs[0]) * newsize); 266 for(p = ®s[regs_alloc]; p < ®s[newsize]; p++) { 267 p->fd = -1; 268# ifdef USE_POLL 269 p->pfd = NULL; 270# endif 271 } 272 p = ®s[regs_alloc]; 273 regs_alloc = newsize; 274 } 275 276 p->fd = fd; 277 p->arg = arg; 278 p->mask = mask; 279 p->func = func; 280 281 regs_used++; 282 rebuild = 1; 283 } 284 285 poll_unblocksig(); 286 287 if(rpoll_trace) 288 fprintf(stderr, "poll_register(%d, %p, %p, %#x)->%tu", 289 fd, (void *)func, (void *)arg, mask, p - regs); 290 return p - regs; 291} 292 293/* 294 * remove registration 295 */ 296void 297poll_unregister(int handle) 298{ 299 if(rpoll_trace) 300 fprintf(stderr, "poll_unregister(%d)", handle); 301 302 poll_blocksig(); 303 304 regs[handle].fd = -1; 305# ifdef USE_POLL 306 regs[handle].pfd = NULL; 307# endif 308 rebuild = 1; 309 regs_used--; 310 311 poll_unblocksig(); 312} 313 314/* 315 * Build the structures used by poll() or select() 316 */ 317static void 318poll_build(void) 319{ 320 PollReg_t * p; 321 322# ifdef USE_POLL 323 struct pollfd * f; 324 325 f = pfd = _xrealloc(pfd, sizeof(pfd[0]) * regs_used); 326 327 for(p = regs; p < ®s[regs_alloc]; p++) 328 if(p->fd >= 0) { 329 f->fd = p->fd; 330 f->events = 0; 331 if(p->mask & RPOLL_IN) 332 f->events |= poll_in; 333 if(p->mask & RPOLL_OUT) 334 f->events |= poll_out; 335 if(p->mask & RPOLL_EXCEPT) 336 f->events |= poll_except; 337 f->revents = 0; 338 p->pfd = f++; 339 } 340 assert(f == &pfd[regs_used]); 341# endif 342 343# ifdef USE_SELECT 344 FD_ZERO(&rset); 345 FD_ZERO(&wset); 346 FD_ZERO(&xset); 347 maxfd = -1; 348 for(p = regs; p < ®s[regs_alloc]; p++) 349 if(p->fd >= 0) { 350 if(p->fd > maxfd) 351 maxfd = p->fd; 352 if(p->mask & RPOLL_IN) 353 FD_SET(p->fd, &rset); 354 if(p->mask & RPOLL_OUT) 355 FD_SET(p->fd, &wset); 356 if(p->mask & RPOLL_EXCEPT) 357 FD_SET(p->fd, &xset); 358 } 359# endif 360} 361 362int 363poll_start_timer(u_int msecs, int repeat, timer_f func, void *arg) 364{ 365 return (poll_start_utimer((unsigned long long)msecs * 1000, 366 repeat, func, arg)); 367} 368 369int 370poll_start_utimer(unsigned long long usecs, int repeat, timer_f func, void *arg) 371{ 372 PollTim_t *p; 373 374 /* find unused entry */ 375 for(p = tims; p < &tims[tims_alloc]; p++) 376 if(p->func == NULL) 377 break; 378 379 if(p == &tims[tims_alloc]) { 380 if(tims_alloc == tims_used) { 381 size_t newsize = tims_alloc + POLL_REG_GROW; 382 tims = _xrealloc(tims, sizeof(tims[0]) * newsize); 383 for(p = &tims[tims_alloc]; p < &tims[newsize]; p++) 384 p->func = NULL; 385 p = &tims[tims_alloc]; 386 tims_alloc = newsize; 387 } 388 } 389 390 /* create entry */ 391 p->usecs = usecs; 392 p->repeat = repeat; 393 p->arg = arg; 394 p->func = func; 395 p->when = GETUSECS() + usecs; 396 397 tims_used++; 398 399 resort = 1; 400 401 if(rpoll_trace) 402 fprintf(stderr, "poll_start_utimer(%llu, %d, %p, %p)->%tu", 403 usecs, repeat, (void *)func, (void *)arg, p - tims); 404 405 return p - tims; 406} 407 408/* 409 * Here we have to look into the sorted table, whether any entry there points 410 * into the registration table for the deleted entry. This is needed, 411 * because a unregistration can occure while we are scanning through the 412 * table in dispatch(). Do this only, if we are really there - resorting 413 * will sort out things if we are called from outside the loop. 414 */ 415void 416poll_stop_timer(int handle) 417{ 418 u_int i; 419 420 if(rpoll_trace) 421 fprintf(stderr, "poll_stop_timer(%d)", handle); 422 423 tims[handle].func = NULL; 424 tims_used--; 425 426 resort = 1; 427 428 if(!in_dispatch) 429 return; 430 431 for(i = 0; i < tfd_used; i++) 432 if(tfd[i] == handle) { 433 tfd[i] = -1; 434 break; 435 } 436} 437 438/* 439 * Squeeze and sort timer table. 440 * Should perhaps use a custom sort. 441 */ 442static int 443tim_cmp(const void *p1, const void *p2) 444{ 445 int t1 = *(const int *)p1; 446 int t2 = *(const int *)p2; 447 448 return tims[t1].when < tims[t2].when ? -1 449 : tims[t1].when > tims[t2].when ? +1 450 : 0; 451} 452 453/* 454 * Reconstruct the tfd-array. This will be an sorted array of indexes 455 * to the used entries in tims. The next timer to expire will be infront 456 * of the array. tfd_used is the number of used entries. The array is 457 * re-allocated if needed. 458 */ 459static void 460sort_timers(void) 461{ 462 int *pp; 463 u_int i; 464 465 if(tims_used > tfd_alloc) { 466 tfd_alloc = tims_used; 467 tfd = _xrealloc(tfd, sizeof(int *) * tfd_alloc); 468 } 469 470 pp = tfd; 471 472 for(i = 0; i < tims_alloc; i++) 473 if(tims[i].func) 474 *pp++ = i; 475 assert(pp - tfd == (ptrdiff_t)tims_used); 476 477 tfd_used = tims_used; 478 if(tfd_used > 1) 479 qsort(tfd, tfd_used, sizeof(int), tim_cmp); 480} 481 482/* 483 * Poll the file descriptors and dispatch to the right function 484 * If wait is true the poll blocks until somewhat happens. 485 * Don't use a pointer here, because the called function may cause 486 * a reallocation! The check for pfd != NULL is required, because 487 * a sequence of unregister/register could make the wrong callback 488 * to be called. So we clear pfd in unregister and check here. 489 */ 490void 491poll_dispatch(int wait) 492{ 493 u_int i, idx; 494 int ret; 495 tval_t now; 496 tval_t tout; 497 static u_int last_index; 498 499# ifdef USE_SELECT 500 fd_set nrset, nwset, nxset; 501 struct timeval tv; 502# endif 503 504 in_dispatch = 1; 505 506 if(rebuild) { 507 rebuild = 0; 508 poll_build(); 509 } 510 if(resort) { 511 resort = 0; 512 sort_timers(); 513 } 514 515 /* in wait mode - compute the timeout */ 516 if(wait) { 517 if(tfd_used) { 518 now = GETUSECS(); 519# ifdef DEBUG 520 { 521 fprintf(stderr, "now=%llu", now); 522 for(i = 0; i < tims_used; i++) 523 fprintf(stderr, "timers[%2d] = %lld", 524 i, tfd[i]->when - now); 525 } 526# endif 527 if((tout = tims[tfd[0]].when - now) < 0) 528 tout = 0; 529 } else 530 tout = INFTIM; 531 } else 532 tout = 0; 533 534# ifdef DEBUG 535 fprintf(stderr, "rpoll -- selecting with tout=%u", tout); 536# endif 537 538# ifdef USE_POLL 539 ret = poll(pfd, regs_used, tout == INFTIM ? INFTIM : (tout / 1000)); 540# endif 541 542# ifdef USE_SELECT 543 nrset = rset; 544 nwset = wset; 545 nxset = xset; 546 if(tout != INFTIM) { 547 tv.tv_sec = tout / 1000000; 548 tv.tv_usec = tout % 1000000; 549 } 550 ret = select(maxfd+1, 551 SELECT_CAST(&nrset), 552 SELECT_CAST(&nwset), 553 SELECT_CAST(&nxset), (tout==INFTIM) ? NULL : &tv); 554# endif 555 556 if(ret == -1) { 557 if(errno == EINTR) 558 return; 559 _panic("poll/select: %s", strerror(errno)); 560 } 561 562 /* dispatch files */ 563 if(ret > 0) { 564 for(i = 0; i < regs_alloc; i++) { 565 idx = rpoll_policy ? ((last_index+i) % regs_alloc) : i; 566 567 assert(idx < regs_alloc); 568 569 if(regs[idx].fd >= 0) { 570 int mask = 0; 571 572# ifdef USE_POLL 573 if(regs[idx].pfd) { 574 if ((regs[idx].mask & RPOLL_IN) && 575 (regs[idx].pfd->revents & poll_in)) 576 mask |= RPOLL_IN; 577 if ((regs[idx].mask & RPOLL_OUT) && 578 (regs[idx].pfd->revents & poll_out)) 579 mask |= RPOLL_OUT; 580 if((regs[idx].mask & RPOLL_EXCEPT) && 581 (regs[idx].pfd->revents & poll_except)) 582 mask |= RPOLL_EXCEPT; 583 } 584# endif 585# ifdef USE_SELECT 586 if ((regs[idx].mask & RPOLL_IN) && 587 FD_ISSET(regs[idx].fd, &nrset)) 588 mask |= RPOLL_IN; 589 if ((regs[idx].mask & RPOLL_OUT) && 590 FD_ISSET(regs[idx].fd, &nwset)) 591 mask |= RPOLL_OUT; 592 if ((regs[idx].mask & RPOLL_EXCEPT) && 593 FD_ISSET(regs[idx].fd, &nxset)) 594 mask |= RPOLL_EXCEPT; 595# endif 596 assert(idx < regs_alloc); 597 598 if(mask) { 599 if(rpoll_trace) 600 fprintf(stderr, "poll_dispatch() -- " 601 "file %d/%d %x", 602 regs[idx].fd, idx, mask); 603 (*regs[idx].func)(regs[idx].fd, mask, regs[idx].arg); 604 } 605 } 606 607 } 608 last_index++; 609 } 610 611 /* dispatch timeouts */ 612 if(tfd_used) { 613 now = GETUSECS(); 614 for(i = 0; i < tfd_used; i++) { 615 if(tfd[i] < 0) 616 continue; 617 if(tims[tfd[i]].when > now) 618 break; 619 if(rpoll_trace) 620 fprintf(stderr, "rpoll_dispatch() -- timeout %d",tfd[i]); 621 (*tims[tfd[i]].func)(tfd[i], tims[tfd[i]].arg); 622 if(tfd[i] < 0) 623 continue; 624 if(tims[tfd[i]].repeat) 625 tims[tfd[i]].when = now + tims[tfd[i]].usecs; 626 else { 627 tims[tfd[i]].func = NULL; 628 tims_used--; 629 tfd[i] = -1; 630 } 631 resort = 1; 632 } 633 } 634 in_dispatch = 0; 635} 636 637 638# ifdef TESTME 639struct timeval start, now; 640int t0, t1; 641 642double elaps(void); 643void infunc(int fd, int mask, void *arg); 644 645double 646elaps(void) 647{ 648 gettimeofday(&now, NULL); 649 650 return (double)(10 * now.tv_sec + now.tv_usec / 100000 - 651 10 * start.tv_sec - start.tv_usec / 100000) / 10; 652} 653 654void 655infunc(int fd, int mask, void *arg) 656{ 657 char buf[1024]; 658 int ret; 659 660 mask = mask; 661 arg = arg; 662 if((ret = read(fd, buf, sizeof(buf))) < 0) 663 _panic("read: %s", strerror(errno)); 664 write(1, "stdin:", 6); 665 write(1, buf, ret); 666} 667 668void tfunc0(int tid, void *arg); 669void tfunc1(int tid, void *arg); 670 671void 672tfunc0(int tid, void *arg) 673{ 674 printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg); 675} 676void 677tfunc1(int tid, void *arg) 678{ 679 printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg); 680} 681void 682tfunc2(int tid, void *arg) 683{ 684 static u_int count = 0; 685 686 if (++count % 10000 == 0) 687 printf("%4.1f -- %d\n", elaps(), tid); 688} 689 690void first(int tid, void *arg); 691void second(int tid, void *arg); 692 693void 694second(int tid, void *arg) 695{ 696 printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg); 697 poll_start_utimer(5500000, 0, first, "first"); 698 poll_stop_timer(t1); 699 t0 = poll_start_timer(1000, 1, tfunc0, "1 second"); 700} 701void 702first(int tid, void *arg) 703{ 704 printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg); 705 poll_start_timer(3700, 0, second, "second"); 706 poll_stop_timer(t0); 707 t1 = poll_start_timer(250, 1, tfunc1, "1/4 second"); 708} 709 710int 711main(int argc, char *argv[]) 712{ 713 argv = argv; 714 gettimeofday(&start, NULL); 715 poll_register(0, infunc, NULL, RPOLL_IN); 716 717 if (argc < 2) { 718 t0 = poll_start_timer(1000, 1, tfunc0, "1 second"); 719 poll_start_timer(2500, 0, first, "first"); 720 } else { 721 t0 = poll_start_utimer(300, 1, tfunc2, NULL); 722 } 723 724 while(1) 725 poll_dispatch(1); 726 727 return 0; 728} 729# endif 730