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 93 typedef int64_t tval_t; 94 95 static inline tval_t GETUSECS(void); 96 97 static inline tval_t 98 GETUSECS(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 */ 108 static 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 122 static 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 */ 148 typedef 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 */ 161 typedef 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 173 static struct pollfd * pfd; /* fd list for poll() */ 174 # endif 175 176 # ifdef USE_SELECT 177 static fd_set rset, wset, xset; /* file descriptor sets for select() */ 178 static int maxfd; /* maximum fd number */ 179 # endif 180 181 static int in_dispatch; 182 183 static PollReg_t * regs; /* registration records */ 184 static u_int regs_alloc; /* how many are allocated */ 185 static u_int regs_used; /* upper used limit */ 186 static sigset_t bset; /* blocked signals */ 187 static int rebuild; /* rebuild table on next dispatch() */ 188 189 static int * tfd; /* sorted entries */ 190 static u_int tfd_alloc; /* number of entries allocated */ 191 static u_int tfd_used; /* number of entries used */ 192 static PollTim_t * tims; /* timer registration records */ 193 static u_int tims_alloc; /* how many are allocated */ 194 static u_int tims_used; /* how many are used */ 195 static int resort; /* resort on next dispatch */ 196 197 int rpoll_trace; 198 int rpoll_policy; /* if 0 start sched callbacks from 0 else try round robin */ 199 200 static void poll_build(void); 201 static void poll_blocksig(void); 202 static void poll_unblocksig(void); 203 static 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 */ 211 static void 212 poll_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 */ 226 static void 227 poll_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 */ 241 int 242 poll_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 */ 296 void 297 poll_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 */ 317 static void 318 poll_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 362 int 363 poll_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 369 int 370 poll_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 */ 415 void 416 poll_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 */ 442 static int 443 tim_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 */ 459 static void 460 sort_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 */ 490 void 491 poll_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 639 struct timeval start, now; 640 int t0, t1; 641 642 double elaps(void); 643 void infunc(int fd, int mask, void *arg); 644 645 double 646 elaps(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 654 void 655 infunc(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 668 void tfunc0(int tid, void *arg); 669 void tfunc1(int tid, void *arg); 670 671 void 672 tfunc0(int tid, void *arg) 673 { 674 printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg); 675 } 676 void 677 tfunc1(int tid, void *arg) 678 { 679 printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg); 680 } 681 void 682 tfunc2(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 690 void first(int tid, void *arg); 691 void second(int tid, void *arg); 692 693 void 694 second(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 } 701 void 702 first(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 710 int 711 main(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