1911a190fSTom Rhodes /* 2911a190fSTom Rhodes * Copyright (c)1996-2002 by Hartmut Brandt 3911a190fSTom Rhodes * All rights reserved. 4911a190fSTom Rhodes * 5911a190fSTom Rhodes * Author: Hartmut Brandt 6911a190fSTom Rhodes * 7911a190fSTom Rhodes * Redistribution of this software and documentation and use in source and 8911a190fSTom Rhodes * binary forms, with or without modification, are permitted provided that 9911a190fSTom Rhodes * the following conditions are met: 10911a190fSTom Rhodes * 11911a190fSTom Rhodes * 1. Redistributions of source code or documentation must retain the above 12911a190fSTom Rhodes * copyright notice, this list of conditions and the following disclaimer. 13911a190fSTom Rhodes * 2. Redistributions in binary form must reproduce the above copyright 14911a190fSTom Rhodes * notice, this list of conditions and the following disclaimer in the 15911a190fSTom Rhodes * documentation and/or other materials provided with the distribution. 16911a190fSTom Rhodes * 17911a190fSTom Rhodes * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE AUTHOR 18911a190fSTom Rhodes * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 19911a190fSTom Rhodes * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20911a190fSTom Rhodes * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21911a190fSTom Rhodes * THE AUTHOR OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22911a190fSTom Rhodes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23911a190fSTom Rhodes * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 24911a190fSTom Rhodes * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25911a190fSTom Rhodes * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26911a190fSTom Rhodes * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 27911a190fSTom Rhodes * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28911a190fSTom Rhodes */ 29911a190fSTom Rhodes /* 30911a190fSTom Rhodes * These functions try to hide the poll/select/setitimer interface from the 31911a190fSTom Rhodes * user. You associate callback functions with file descriptors and timers. 32911a190fSTom Rhodes * 33911a190fSTom Rhodes * $Begemot: libbegemot/rpoll.c,v 1.14 2004/09/21 15:59:00 brandt Exp $ 34911a190fSTom Rhodes */ 35911a190fSTom Rhodes # include <stdio.h> 36911a190fSTom Rhodes # include <stdlib.h> 37911a190fSTom Rhodes # include <stddef.h> 38911a190fSTom Rhodes # include <stdarg.h> 39911a190fSTom Rhodes # include <signal.h> 40911a190fSTom Rhodes # include <string.h> 41911a190fSTom Rhodes # include <errno.h> 42911a190fSTom Rhodes # include <time.h> 43911a190fSTom Rhodes # include <assert.h> 44911a190fSTom Rhodes # include <unistd.h> 45911a190fSTom Rhodes # include <sys/time.h> 46911a190fSTom Rhodes 47911a190fSTom Rhodes /* 48911a190fSTom Rhodes * There happens to be linuxes which read siginfo.h when including 49911a190fSTom Rhodes * signal.h, which, for no appearent reason, defines these symbols. 50911a190fSTom Rhodes */ 51911a190fSTom Rhodes # ifdef POLL_IN 52911a190fSTom Rhodes # undef POLL_IN 53911a190fSTom Rhodes # endif 54911a190fSTom Rhodes # ifdef POLL_OUT 55911a190fSTom Rhodes # undef POLL_OUT 56911a190fSTom Rhodes # endif 57911a190fSTom Rhodes 58911a190fSTom Rhodes # include "rpoll.h" 59911a190fSTom Rhodes 60911a190fSTom Rhodes /* 61911a190fSTom Rhodes # define DEBUG 62911a190fSTom Rhodes */ 63911a190fSTom Rhodes 64911a190fSTom Rhodes # ifdef USE_POLL 65911a190fSTom Rhodes # ifdef NEED_POLL_XOPEN_TWIDDLE 66911a190fSTom Rhodes # define __USE_XOPEN 67911a190fSTom Rhodes # endif 68911a190fSTom Rhodes # include <poll.h> 69911a190fSTom Rhodes # ifdef NEED_POLL_XOPEN_TWIDDLE 70911a190fSTom Rhodes # undef __USE_XOPEN 71911a190fSTom Rhodes # endif 72911a190fSTom Rhodes # include <stropts.h> 73911a190fSTom Rhodes # endif 74911a190fSTom Rhodes 75911a190fSTom Rhodes /* 76911a190fSTom Rhodes * the second define is for Linux, which sometimes fails to 77911a190fSTom Rhodes * declare INFTIM. 78911a190fSTom Rhodes */ 79911a190fSTom Rhodes # if defined(USE_SELECT) || !defined(INFTIM) 80911a190fSTom Rhodes # define INFTIM (-1) 81911a190fSTom Rhodes # endif 82911a190fSTom Rhodes 83911a190fSTom Rhodes # if defined(SIGPOLL) 84911a190fSTom Rhodes # define SIGNAL SIGPOLL 85911a190fSTom Rhodes # else 86911a190fSTom Rhodes # if defined(SIGIO) 87911a190fSTom Rhodes # define SIGNAL SIGIO 88911a190fSTom Rhodes # endif 89911a190fSTom Rhodes # endif 90911a190fSTom Rhodes 91911a190fSTom Rhodes # ifdef USE_POLL 92911a190fSTom Rhodes # define poll_in (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI) 93911a190fSTom Rhodes # define poll_out (POLLOUT | POLLWRNORM | POLLWRBAND) 94911a190fSTom Rhodes # define poll_except (POLLERR | POLLHUP) 95911a190fSTom Rhodes # endif 96911a190fSTom Rhodes 97911a190fSTom Rhodes # ifdef BROKEN_SELECT_PROTO 98911a190fSTom Rhodes # define SELECT_CAST(P) (int *)P 99911a190fSTom Rhodes # else 100911a190fSTom Rhodes # define SELECT_CAST(P) P 101911a190fSTom Rhodes # endif 102911a190fSTom Rhodes 103911a190fSTom Rhodes 104*ca77a23fSHartmut Brandt typedef int64_t tval_t; 105911a190fSTom Rhodes 106*ca77a23fSHartmut Brandt static inline tval_t GETUSECS(void); 107911a190fSTom Rhodes 108911a190fSTom Rhodes static inline tval_t 109*ca77a23fSHartmut Brandt GETUSECS(void) { 110911a190fSTom Rhodes struct timeval tval; 111911a190fSTom Rhodes 112911a190fSTom Rhodes (void)gettimeofday(&tval, NULL); 113*ca77a23fSHartmut Brandt return (tval_t)tval.tv_sec * 1000000 + tval.tv_usec; 114911a190fSTom Rhodes } 115911a190fSTom Rhodes 116911a190fSTom Rhodes /* 117911a190fSTom Rhodes * Simple fatal exit. 118911a190fSTom Rhodes */ 119911a190fSTom Rhodes static void 120911a190fSTom Rhodes _panic(const char *fmt, ...) 121911a190fSTom Rhodes { 122911a190fSTom Rhodes va_list ap; 123911a190fSTom Rhodes 124911a190fSTom Rhodes va_start(ap, fmt); 125911a190fSTom Rhodes fprintf(stderr, "panic: "); 126911a190fSTom Rhodes vfprintf(stderr, fmt, ap); 127911a190fSTom Rhodes fprintf(stderr, "\n"); 128911a190fSTom Rhodes va_end(ap); 129911a190fSTom Rhodes 130911a190fSTom Rhodes exit(1); 131911a190fSTom Rhodes } 132911a190fSTom Rhodes 133911a190fSTom Rhodes static void * 134911a190fSTom Rhodes _xrealloc(void *p, size_t s) 135911a190fSTom Rhodes { 136911a190fSTom Rhodes void *ptr; 137911a190fSTom Rhodes 138911a190fSTom Rhodes if(p == NULL) { 139911a190fSTom Rhodes if((ptr=malloc(s)) == NULL && (s!=0 || (ptr=malloc(1)) == NULL)) 140911a190fSTom Rhodes _panic("out of memory: xrealloc(%lx, %lu)", 141911a190fSTom Rhodes (unsigned long)p, (unsigned long)s); 142911a190fSTom Rhodes } else if(s == 0) { 143911a190fSTom Rhodes free(p); 144911a190fSTom Rhodes if((ptr=malloc(s)) == NULL && (ptr=malloc(1)) == NULL) 145911a190fSTom Rhodes _panic("out of memory: xrealloc(%lx, %lu)", 146911a190fSTom Rhodes (unsigned long)p, (unsigned long)s); 147911a190fSTom Rhodes } else { 148911a190fSTom Rhodes if((ptr = realloc(p, s)) == NULL) 149911a190fSTom Rhodes _panic("out of memory: xrealloc(%lx, %lu)", 150911a190fSTom Rhodes (unsigned long)p, (unsigned long)s); 151911a190fSTom Rhodes } 152911a190fSTom Rhodes 153911a190fSTom Rhodes return ptr; 154911a190fSTom Rhodes } 155911a190fSTom Rhodes 156911a190fSTom Rhodes /* 157911a190fSTom Rhodes * This structure holds one registration record for files 158911a190fSTom Rhodes */ 159911a190fSTom Rhodes typedef struct { 160911a190fSTom Rhodes int fd; /* file descriptor (-1 if struct unused) */ 161911a190fSTom Rhodes int mask; /* event flags */ 162911a190fSTom Rhodes void * arg; /* client arg */ 163911a190fSTom Rhodes poll_f func; /* handler */ 164911a190fSTom Rhodes # ifdef USE_POLL 165911a190fSTom Rhodes struct pollfd *pfd; /* pointer to corresponding poll() structure */ 166911a190fSTom Rhodes # endif 167911a190fSTom Rhodes } PollReg_t; 168911a190fSTom Rhodes 169911a190fSTom Rhodes /* 170911a190fSTom Rhodes * Now for timers 171911a190fSTom Rhodes */ 172911a190fSTom Rhodes typedef struct { 173*ca77a23fSHartmut Brandt uint64_t usecs; /* microsecond value of the timer */ 174911a190fSTom Rhodes int repeat; /* one shot or repeat? */ 175911a190fSTom Rhodes void *arg; /* client arg */ 176911a190fSTom Rhodes timer_f func; /* handler, 0 means disfunct */ 177*ca77a23fSHartmut Brandt tval_t when; /* next time to trigger in usecs! */ 178911a190fSTom Rhodes } PollTim_t; 179911a190fSTom Rhodes 180911a190fSTom Rhodes /* how many records should our table grow at once? */ 181911a190fSTom Rhodes # define POLL_REG_GROW 100 182911a190fSTom Rhodes 183911a190fSTom Rhodes # ifdef USE_POLL 184911a190fSTom Rhodes static struct pollfd * pfd; /* fd list for poll() */ 185911a190fSTom Rhodes # endif 186911a190fSTom Rhodes 187911a190fSTom Rhodes # ifdef USE_SELECT 188911a190fSTom Rhodes static fd_set rset, wset, xset; /* file descriptor sets for select() */ 189911a190fSTom Rhodes static int maxfd; /* maximum fd number */ 190911a190fSTom Rhodes # endif 191911a190fSTom Rhodes 192911a190fSTom Rhodes static int in_dispatch; 193911a190fSTom Rhodes 194911a190fSTom Rhodes static PollReg_t * regs; /* registration records */ 195911a190fSTom Rhodes static u_int regs_alloc; /* how many are allocated */ 196911a190fSTom Rhodes static u_int regs_used; /* upper used limit */ 197911a190fSTom Rhodes static sigset_t bset; /* blocked signals */ 198911a190fSTom Rhodes static int rebuild; /* rebuild table on next dispatch() */ 199911a190fSTom Rhodes 200911a190fSTom Rhodes static int * tfd; /* sorted entries */ 201911a190fSTom Rhodes static u_int tfd_alloc; /* number of entries allocated */ 202911a190fSTom Rhodes static u_int tfd_used; /* number of entries used */ 203911a190fSTom Rhodes static PollTim_t * tims; /* timer registration records */ 204911a190fSTom Rhodes static u_int tims_alloc; /* how many are allocated */ 205911a190fSTom Rhodes static u_int tims_used; /* how many are used */ 206911a190fSTom Rhodes static int resort; /* resort on next dispatch */ 207911a190fSTom Rhodes 208911a190fSTom Rhodes int rpoll_trace; 209911a190fSTom Rhodes int rpoll_policy; /* if 0 start sched callbacks from 0 else try round robin */ 210911a190fSTom Rhodes 211911a190fSTom Rhodes static void poll_build(void); 212911a190fSTom Rhodes static void poll_blocksig(void); 213911a190fSTom Rhodes static void poll_unblocksig(void); 214911a190fSTom Rhodes static void sort_timers(void); 215911a190fSTom Rhodes 216911a190fSTom Rhodes 217911a190fSTom Rhodes /* 218911a190fSTom Rhodes * Private function to block SIGPOLL or SIGIO for a short time. 219911a190fSTom Rhodes * Don't forget to call poll_unblock before return from the calling function. 220911a190fSTom Rhodes * Don't change the mask between this calls (your changes will be lost). 221911a190fSTom Rhodes */ 222911a190fSTom Rhodes static void 223911a190fSTom Rhodes poll_blocksig(void) 224911a190fSTom Rhodes { 225911a190fSTom Rhodes sigset_t set; 226911a190fSTom Rhodes 227911a190fSTom Rhodes sigemptyset(&set); 228911a190fSTom Rhodes sigaddset(&set, SIGNAL); 229911a190fSTom Rhodes 230911a190fSTom Rhodes if(sigprocmask(SIG_BLOCK, &set, &bset)) 231911a190fSTom Rhodes _panic("sigprocmask(SIG_BLOCK): %s", strerror(errno)); 232911a190fSTom Rhodes } 233911a190fSTom Rhodes 234911a190fSTom Rhodes /* 235911a190fSTom Rhodes * unblock the previously blocked signal 236911a190fSTom Rhodes */ 237911a190fSTom Rhodes static void 238911a190fSTom Rhodes poll_unblocksig(void) 239911a190fSTom Rhodes { 240911a190fSTom Rhodes if(sigprocmask(SIG_SETMASK, &bset, NULL)) 241911a190fSTom Rhodes _panic("sigprocmask(SIG_SETMASK): %s", strerror(errno)); 242911a190fSTom Rhodes } 243911a190fSTom Rhodes 244911a190fSTom Rhodes /* 245911a190fSTom Rhodes * Register the file descriptor fd. If the event corresponding to 246911a190fSTom Rhodes * mask arrives func is called with arg. 247911a190fSTom Rhodes * If fd is already registered with that func and arg, only the mask 248911a190fSTom Rhodes * is changed. 249911a190fSTom Rhodes * We block the IO-signal, so the dispatch function can be called from 250911a190fSTom Rhodes * within the signal handler. 251911a190fSTom Rhodes */ 252911a190fSTom Rhodes int 253911a190fSTom Rhodes poll_register(int fd, poll_f func, void *arg, int mask) 254911a190fSTom Rhodes { 255911a190fSTom Rhodes PollReg_t * p; 256911a190fSTom Rhodes 257911a190fSTom Rhodes poll_blocksig(); 258911a190fSTom Rhodes 259911a190fSTom Rhodes /* already registered? */ 260911a190fSTom Rhodes for(p = regs; p < ®s[regs_alloc]; p++) 261911a190fSTom Rhodes if(p->fd == fd && p->func == func && p->arg == arg) { 262911a190fSTom Rhodes p->mask = mask; 263911a190fSTom Rhodes break; 264911a190fSTom Rhodes } 265911a190fSTom Rhodes 266911a190fSTom Rhodes if(p == ®s[regs_alloc]) { 267911a190fSTom Rhodes /* no - register */ 268911a190fSTom Rhodes 269911a190fSTom Rhodes /* find a free slot */ 270911a190fSTom Rhodes for(p = regs; p < ®s[regs_alloc]; p++) 271911a190fSTom Rhodes if(p->fd == -1) 272911a190fSTom Rhodes break; 273911a190fSTom Rhodes 274911a190fSTom Rhodes if(p == ®s[regs_alloc]) { 275911a190fSTom Rhodes size_t newsize = regs_alloc + POLL_REG_GROW; 276911a190fSTom Rhodes regs = _xrealloc(regs, sizeof(regs[0]) * newsize); 277911a190fSTom Rhodes for(p = ®s[regs_alloc]; p < ®s[newsize]; p++) { 278911a190fSTom Rhodes p->fd = -1; 279911a190fSTom Rhodes # ifdef USE_POLL 280911a190fSTom Rhodes p->pfd = NULL; 281911a190fSTom Rhodes # endif 282911a190fSTom Rhodes } 283911a190fSTom Rhodes p = ®s[regs_alloc]; 284911a190fSTom Rhodes regs_alloc = newsize; 285911a190fSTom Rhodes } 286911a190fSTom Rhodes 287911a190fSTom Rhodes p->fd = fd; 288911a190fSTom Rhodes p->arg = arg; 289911a190fSTom Rhodes p->mask = mask; 290911a190fSTom Rhodes p->func = func; 291911a190fSTom Rhodes 292911a190fSTom Rhodes regs_used++; 293911a190fSTom Rhodes rebuild = 1; 294911a190fSTom Rhodes } 295911a190fSTom Rhodes 296911a190fSTom Rhodes poll_unblocksig(); 297911a190fSTom Rhodes 298911a190fSTom Rhodes if(rpoll_trace) 299*ca77a23fSHartmut Brandt fprintf(stderr, "poll_register(%d, %p, %p, %#x)->%tu", 300*ca77a23fSHartmut Brandt fd, (void *)func, (void *)arg, mask, p - regs); 301911a190fSTom Rhodes return p - regs; 302911a190fSTom Rhodes } 303911a190fSTom Rhodes 304911a190fSTom Rhodes /* 305911a190fSTom Rhodes * remove registration 306911a190fSTom Rhodes */ 307911a190fSTom Rhodes void 308911a190fSTom Rhodes poll_unregister(int handle) 309911a190fSTom Rhodes { 310911a190fSTom Rhodes if(rpoll_trace) 311911a190fSTom Rhodes fprintf(stderr, "poll_unregister(%d)", handle); 312911a190fSTom Rhodes 313911a190fSTom Rhodes poll_blocksig(); 314911a190fSTom Rhodes 315911a190fSTom Rhodes regs[handle].fd = -1; 316911a190fSTom Rhodes # ifdef USE_POLL 317911a190fSTom Rhodes regs[handle].pfd = NULL; 318911a190fSTom Rhodes # endif 319911a190fSTom Rhodes rebuild = 1; 320911a190fSTom Rhodes regs_used--; 321911a190fSTom Rhodes 322911a190fSTom Rhodes poll_unblocksig(); 323911a190fSTom Rhodes } 324911a190fSTom Rhodes 325911a190fSTom Rhodes /* 326911a190fSTom Rhodes * Build the structures used by poll() or select() 327911a190fSTom Rhodes */ 328911a190fSTom Rhodes static void 329911a190fSTom Rhodes poll_build(void) 330911a190fSTom Rhodes { 331911a190fSTom Rhodes PollReg_t * p; 332911a190fSTom Rhodes 333911a190fSTom Rhodes # ifdef USE_POLL 334911a190fSTom Rhodes struct pollfd * f; 335911a190fSTom Rhodes 336911a190fSTom Rhodes f = pfd = _xrealloc(pfd, sizeof(pfd[0]) * regs_used); 337911a190fSTom Rhodes 338911a190fSTom Rhodes for(p = regs; p < ®s[regs_alloc]; p++) 339911a190fSTom Rhodes if(p->fd >= 0) { 340911a190fSTom Rhodes f->fd = p->fd; 341911a190fSTom Rhodes f->events = 0; 342911a190fSTom Rhodes if(p->mask & POLL_IN) 343911a190fSTom Rhodes f->events |= poll_in; 344911a190fSTom Rhodes if(p->mask & POLL_OUT) 345911a190fSTom Rhodes f->events |= poll_out; 346911a190fSTom Rhodes if(p->mask & POLL_EXCEPT) 347911a190fSTom Rhodes f->events |= poll_except; 348911a190fSTom Rhodes f->revents = 0; 349911a190fSTom Rhodes p->pfd = f++; 350911a190fSTom Rhodes } 351911a190fSTom Rhodes assert(f == &pfd[regs_used]); 352911a190fSTom Rhodes # endif 353911a190fSTom Rhodes 354911a190fSTom Rhodes # ifdef USE_SELECT 355911a190fSTom Rhodes FD_ZERO(&rset); 356911a190fSTom Rhodes FD_ZERO(&wset); 357911a190fSTom Rhodes FD_ZERO(&xset); 358911a190fSTom Rhodes maxfd = -1; 359911a190fSTom Rhodes for(p = regs; p < ®s[regs_alloc]; p++) 360911a190fSTom Rhodes if(p->fd >= 0) { 361911a190fSTom Rhodes if(p->fd > maxfd) 362911a190fSTom Rhodes maxfd = p->fd; 363911a190fSTom Rhodes if(p->mask & POLL_IN) 364911a190fSTom Rhodes FD_SET(p->fd, &rset); 365911a190fSTom Rhodes if(p->mask & POLL_OUT) 366911a190fSTom Rhodes FD_SET(p->fd, &wset); 367911a190fSTom Rhodes if(p->mask & POLL_EXCEPT) 368911a190fSTom Rhodes FD_SET(p->fd, &xset); 369911a190fSTom Rhodes } 370911a190fSTom Rhodes # endif 371911a190fSTom Rhodes } 372911a190fSTom Rhodes 373911a190fSTom Rhodes int 374911a190fSTom Rhodes poll_start_timer(u_int msecs, int repeat, timer_f func, void *arg) 375911a190fSTom Rhodes { 376*ca77a23fSHartmut Brandt return (poll_start_utimer((unsigned long long)msecs * 1000, 377*ca77a23fSHartmut Brandt repeat, func, arg)); 378*ca77a23fSHartmut Brandt } 379*ca77a23fSHartmut Brandt 380*ca77a23fSHartmut Brandt int 381*ca77a23fSHartmut Brandt poll_start_utimer(unsigned long long usecs, int repeat, timer_f func, void *arg) 382*ca77a23fSHartmut Brandt { 383911a190fSTom Rhodes PollTim_t *p; 384911a190fSTom Rhodes 385911a190fSTom Rhodes /* find unused entry */ 386911a190fSTom Rhodes for(p = tims; p < &tims[tims_alloc]; p++) 387911a190fSTom Rhodes if(p->func == NULL) 388911a190fSTom Rhodes break; 389911a190fSTom Rhodes 390911a190fSTom Rhodes if(p == &tims[tims_alloc]) { 391911a190fSTom Rhodes if(tims_alloc == tims_used) { 392911a190fSTom Rhodes size_t newsize = tims_alloc + POLL_REG_GROW; 393911a190fSTom Rhodes tims = _xrealloc(tims, sizeof(tims[0]) * newsize); 394911a190fSTom Rhodes for(p = &tims[tims_alloc]; p < &tims[newsize]; p++) 395911a190fSTom Rhodes p->func = NULL; 396911a190fSTom Rhodes p = &tims[tims_alloc]; 397911a190fSTom Rhodes tims_alloc = newsize; 398911a190fSTom Rhodes } 399911a190fSTom Rhodes } 400911a190fSTom Rhodes 401911a190fSTom Rhodes /* create entry */ 402*ca77a23fSHartmut Brandt p->usecs = usecs; 403911a190fSTom Rhodes p->repeat = repeat; 404911a190fSTom Rhodes p->arg = arg; 405911a190fSTom Rhodes p->func = func; 406*ca77a23fSHartmut Brandt p->when = GETUSECS() + usecs; 407911a190fSTom Rhodes 408911a190fSTom Rhodes tims_used++; 409911a190fSTom Rhodes 410911a190fSTom Rhodes resort = 1; 411911a190fSTom Rhodes 412911a190fSTom Rhodes if(rpoll_trace) 413*ca77a23fSHartmut Brandt fprintf(stderr, "poll_start_utimer(%llu, %d, %p, %p)->%tu", 414*ca77a23fSHartmut Brandt usecs, repeat, (void *)func, (void *)arg, p - tims); 415911a190fSTom Rhodes 416911a190fSTom Rhodes return p - tims; 417911a190fSTom Rhodes } 418911a190fSTom Rhodes 419911a190fSTom Rhodes /* 420911a190fSTom Rhodes * Here we have to look into the sorted table, whether any entry there points 421911a190fSTom Rhodes * into the registration table for the deleted entry. This is needed, 422911a190fSTom Rhodes * because a unregistration can occure while we are scanning through the 423911a190fSTom Rhodes * table in dispatch(). Do this only, if we are really there - resorting 424911a190fSTom Rhodes * will sort out things if we are called from outside the loop. 425911a190fSTom Rhodes */ 426911a190fSTom Rhodes void 427911a190fSTom Rhodes poll_stop_timer(int handle) 428911a190fSTom Rhodes { 429911a190fSTom Rhodes u_int i; 430911a190fSTom Rhodes 431911a190fSTom Rhodes if(rpoll_trace) 432911a190fSTom Rhodes fprintf(stderr, "poll_stop_timer(%d)", handle); 433911a190fSTom Rhodes 434911a190fSTom Rhodes tims[handle].func = NULL; 435911a190fSTom Rhodes tims_used--; 436911a190fSTom Rhodes 437911a190fSTom Rhodes resort = 1; 438911a190fSTom Rhodes 439911a190fSTom Rhodes if(!in_dispatch) 440911a190fSTom Rhodes return; 441911a190fSTom Rhodes 442911a190fSTom Rhodes for(i = 0; i < tfd_used; i++) 443911a190fSTom Rhodes if(tfd[i] == handle) { 444911a190fSTom Rhodes tfd[i] = -1; 445911a190fSTom Rhodes break; 446911a190fSTom Rhodes } 447911a190fSTom Rhodes } 448911a190fSTom Rhodes 449911a190fSTom Rhodes /* 450911a190fSTom Rhodes * Squeeze and sort timer table. 451911a190fSTom Rhodes * Should perhaps use a custom sort. 452911a190fSTom Rhodes */ 453911a190fSTom Rhodes static int 454911a190fSTom Rhodes tim_cmp(const void *p1, const void *p2) 455911a190fSTom Rhodes { 456911a190fSTom Rhodes int t1 = *(const int *)p1; 457911a190fSTom Rhodes int t2 = *(const int *)p2; 458911a190fSTom Rhodes 459911a190fSTom Rhodes return tims[t1].when < tims[t2].when ? -1 460911a190fSTom Rhodes : tims[t1].when > tims[t2].when ? +1 461911a190fSTom Rhodes : 0; 462911a190fSTom Rhodes } 463911a190fSTom Rhodes 464911a190fSTom Rhodes /* 465911a190fSTom Rhodes * Reconstruct the tfd-array. This will be an sorted array of indexes 466911a190fSTom Rhodes * to the used entries in tims. The next timer to expire will be infront 467911a190fSTom Rhodes * of the array. tfd_used is the number of used entries. The array is 468911a190fSTom Rhodes * re-allocated if needed. 469911a190fSTom Rhodes */ 470911a190fSTom Rhodes static void 471911a190fSTom Rhodes sort_timers(void) 472911a190fSTom Rhodes { 473911a190fSTom Rhodes int *pp; 474911a190fSTom Rhodes u_int i; 475911a190fSTom Rhodes 476911a190fSTom Rhodes if(tims_used > tfd_alloc) { 477911a190fSTom Rhodes tfd_alloc = tims_used; 478911a190fSTom Rhodes tfd = _xrealloc(tfd, sizeof(int *) * tfd_alloc); 479911a190fSTom Rhodes } 480911a190fSTom Rhodes 481911a190fSTom Rhodes pp = tfd; 482911a190fSTom Rhodes 483911a190fSTom Rhodes for(i = 0; i < tims_alloc; i++) 484911a190fSTom Rhodes if(tims[i].func) 485911a190fSTom Rhodes *pp++ = i; 486911a190fSTom Rhodes assert(pp - tfd == (ptrdiff_t)tims_used); 487911a190fSTom Rhodes 488911a190fSTom Rhodes tfd_used = tims_used; 489911a190fSTom Rhodes if(tfd_used > 1) 490911a190fSTom Rhodes qsort(tfd, tfd_used, sizeof(int), tim_cmp); 491911a190fSTom Rhodes } 492911a190fSTom Rhodes 493911a190fSTom Rhodes /* 494911a190fSTom Rhodes * Poll the file descriptors and dispatch to the right function 495911a190fSTom Rhodes * If wait is true the poll blocks until somewhat happens. 496911a190fSTom Rhodes * Don't use a pointer here, because the called function may cause 497911a190fSTom Rhodes * a reallocation! The check for pfd != NULL is required, because 498911a190fSTom Rhodes * a sequence of unregister/register could make the wrong callback 499911a190fSTom Rhodes * to be called. So we clear pfd in unregister and check here. 500911a190fSTom Rhodes */ 501911a190fSTom Rhodes void 502911a190fSTom Rhodes poll_dispatch(int wait) 503911a190fSTom Rhodes { 504911a190fSTom Rhodes u_int i, idx; 505911a190fSTom Rhodes int ret; 506911a190fSTom Rhodes tval_t now; 507*ca77a23fSHartmut Brandt tval_t tout; 508911a190fSTom Rhodes static u_int last_index; 509911a190fSTom Rhodes 510911a190fSTom Rhodes # ifdef USE_SELECT 511911a190fSTom Rhodes fd_set nrset, nwset, nxset; 512911a190fSTom Rhodes struct timeval tv; 513911a190fSTom Rhodes # endif 514911a190fSTom Rhodes 515911a190fSTom Rhodes in_dispatch = 1; 516911a190fSTom Rhodes 517911a190fSTom Rhodes if(rebuild) { 518911a190fSTom Rhodes rebuild = 0; 519911a190fSTom Rhodes poll_build(); 520911a190fSTom Rhodes } 521911a190fSTom Rhodes if(resort) { 522911a190fSTom Rhodes resort = 0; 523911a190fSTom Rhodes sort_timers(); 524911a190fSTom Rhodes } 525911a190fSTom Rhodes 526911a190fSTom Rhodes /* in wait mode - compute the timeout */ 527911a190fSTom Rhodes if(wait) { 528911a190fSTom Rhodes if(tfd_used) { 529*ca77a23fSHartmut Brandt now = GETUSECS(); 530911a190fSTom Rhodes # ifdef DEBUG 531911a190fSTom Rhodes { 532*ca77a23fSHartmut Brandt fprintf(stderr, "now=%llu", now); 533911a190fSTom Rhodes for(i = 0; i < tims_used; i++) 534*ca77a23fSHartmut Brandt fprintf(stderr, "timers[%2d] = %lld", 535*ca77a23fSHartmut Brandt i, tfd[i]->when - now); 536911a190fSTom Rhodes } 537911a190fSTom Rhodes # endif 538911a190fSTom Rhodes if((tout = tims[tfd[0]].when - now) < 0) 539911a190fSTom Rhodes tout = 0; 540911a190fSTom Rhodes } else 541911a190fSTom Rhodes tout = INFTIM; 542911a190fSTom Rhodes } else 543911a190fSTom Rhodes tout = 0; 544911a190fSTom Rhodes 545911a190fSTom Rhodes # ifdef DEBUG 546911a190fSTom Rhodes fprintf(stderr, "rpoll -- selecting with tout=%u", tout); 547911a190fSTom Rhodes # endif 548911a190fSTom Rhodes 549911a190fSTom Rhodes # ifdef USE_POLL 550*ca77a23fSHartmut Brandt ret = poll(pfd, regs_used, tout == INFTIM ? INFTIM : (tout / 1000)); 551911a190fSTom Rhodes # endif 552911a190fSTom Rhodes 553911a190fSTom Rhodes # ifdef USE_SELECT 554911a190fSTom Rhodes nrset = rset; 555911a190fSTom Rhodes nwset = wset; 556911a190fSTom Rhodes nxset = xset; 557911a190fSTom Rhodes if(tout != INFTIM) { 558*ca77a23fSHartmut Brandt tv.tv_sec = tout / 1000000; 559*ca77a23fSHartmut Brandt tv.tv_usec = tout % 1000000; 560911a190fSTom Rhodes } 561911a190fSTom Rhodes ret = select(maxfd+1, 562911a190fSTom Rhodes SELECT_CAST(&nrset), 563911a190fSTom Rhodes SELECT_CAST(&nwset), 564*ca77a23fSHartmut Brandt SELECT_CAST(&nxset), (tout==INFTIM) ? NULL : &tv); 565911a190fSTom Rhodes # endif 566911a190fSTom Rhodes 567911a190fSTom Rhodes if(ret == -1) { 568911a190fSTom Rhodes if(errno == EINTR) 569911a190fSTom Rhodes return; 570911a190fSTom Rhodes _panic("poll/select: %s", strerror(errno)); 571911a190fSTom Rhodes } 572911a190fSTom Rhodes 573911a190fSTom Rhodes /* dispatch files */ 574911a190fSTom Rhodes if(ret > 0) { 575911a190fSTom Rhodes for(i = 0; i < regs_alloc; i++) { 576911a190fSTom Rhodes idx = rpoll_policy ? ((last_index+i) % regs_alloc) : i; 577911a190fSTom Rhodes 578911a190fSTom Rhodes assert(idx < regs_alloc); 579911a190fSTom Rhodes 580911a190fSTom Rhodes if(regs[idx].fd >= 0) { 581911a190fSTom Rhodes int mask = 0; 582911a190fSTom Rhodes 583911a190fSTom Rhodes # ifdef USE_POLL 584911a190fSTom Rhodes if(regs[idx].pfd) { 585*ca77a23fSHartmut Brandt if ((regs[idx].mask & POLL_IN) && 586*ca77a23fSHartmut Brandt (regs[idx].pfd->revents & poll_in)) 587911a190fSTom Rhodes mask |= POLL_IN; 588*ca77a23fSHartmut Brandt if ((regs[idx].mask & POLL_OUT) && 589*ca77a23fSHartmut Brandt (regs[idx].pfd->revents & poll_out)) 590911a190fSTom Rhodes mask |= POLL_OUT; 591*ca77a23fSHartmut Brandt if((regs[idx].mask & POLL_EXCEPT) && 592*ca77a23fSHartmut Brandt (regs[idx].pfd->revents & poll_except)) 593911a190fSTom Rhodes mask |= POLL_EXCEPT; 594911a190fSTom Rhodes } 595911a190fSTom Rhodes # endif 596911a190fSTom Rhodes # ifdef USE_SELECT 597*ca77a23fSHartmut Brandt if ((regs[idx].mask & POLL_IN) && 598*ca77a23fSHartmut Brandt FD_ISSET(regs[idx].fd, &nrset)) 599911a190fSTom Rhodes mask |= POLL_IN; 600*ca77a23fSHartmut Brandt if ((regs[idx].mask & POLL_OUT) && 601*ca77a23fSHartmut Brandt FD_ISSET(regs[idx].fd, &nwset)) 602911a190fSTom Rhodes mask |= POLL_OUT; 603*ca77a23fSHartmut Brandt if ((regs[idx].mask & POLL_EXCEPT) && 604*ca77a23fSHartmut Brandt FD_ISSET(regs[idx].fd, &nxset)) 605911a190fSTom Rhodes mask |= POLL_EXCEPT; 606911a190fSTom Rhodes # endif 607911a190fSTom Rhodes assert(idx < regs_alloc); 608911a190fSTom Rhodes 609911a190fSTom Rhodes if(mask) { 610911a190fSTom Rhodes if(rpoll_trace) 611911a190fSTom Rhodes fprintf(stderr, "poll_dispatch() -- " 612*ca77a23fSHartmut Brandt "file %d/%d %x", 613*ca77a23fSHartmut Brandt regs[idx].fd, idx, mask); 614911a190fSTom Rhodes (*regs[idx].func)(regs[idx].fd, mask, regs[idx].arg); 615911a190fSTom Rhodes } 616911a190fSTom Rhodes } 617911a190fSTom Rhodes 618911a190fSTom Rhodes } 619911a190fSTom Rhodes last_index++; 620911a190fSTom Rhodes } 621911a190fSTom Rhodes 622911a190fSTom Rhodes /* dispatch timeouts */ 623911a190fSTom Rhodes if(tfd_used) { 624*ca77a23fSHartmut Brandt now = GETUSECS(); 625911a190fSTom Rhodes for(i = 0; i < tfd_used; i++) { 626911a190fSTom Rhodes if(tfd[i] < 0) 627911a190fSTom Rhodes continue; 628911a190fSTom Rhodes if(tims[tfd[i]].when > now) 629911a190fSTom Rhodes break; 630911a190fSTom Rhodes if(rpoll_trace) 631911a190fSTom Rhodes fprintf(stderr, "rpoll_dispatch() -- timeout %d",tfd[i]); 632911a190fSTom Rhodes (*tims[tfd[i]].func)(tfd[i], tims[tfd[i]].arg); 633911a190fSTom Rhodes if(tfd[i] < 0) 634911a190fSTom Rhodes continue; 635911a190fSTom Rhodes if(tims[tfd[i]].repeat) 636*ca77a23fSHartmut Brandt tims[tfd[i]].when = now + tims[tfd[i]].usecs; 637911a190fSTom Rhodes else { 638911a190fSTom Rhodes tims[tfd[i]].func = NULL; 639911a190fSTom Rhodes tims_used--; 640911a190fSTom Rhodes tfd[i] = -1; 641911a190fSTom Rhodes } 642911a190fSTom Rhodes resort = 1; 643911a190fSTom Rhodes } 644911a190fSTom Rhodes } 645911a190fSTom Rhodes in_dispatch = 0; 646911a190fSTom Rhodes } 647911a190fSTom Rhodes 648911a190fSTom Rhodes 649911a190fSTom Rhodes # ifdef TESTME 650911a190fSTom Rhodes struct timeval start, now; 651911a190fSTom Rhodes int t0, t1; 652911a190fSTom Rhodes 653911a190fSTom Rhodes double elaps(void); 654911a190fSTom Rhodes void infunc(int fd, int mask, void *arg); 655911a190fSTom Rhodes 656911a190fSTom Rhodes double 657911a190fSTom Rhodes elaps(void) 658911a190fSTom Rhodes { 659911a190fSTom Rhodes gettimeofday(&now, NULL); 660911a190fSTom Rhodes 661*ca77a23fSHartmut Brandt return (double)(10 * now.tv_sec + now.tv_usec / 100000 - 662*ca77a23fSHartmut Brandt 10 * start.tv_sec - start.tv_usec / 100000) / 10; 663911a190fSTom Rhodes } 664911a190fSTom Rhodes 665911a190fSTom Rhodes void 666911a190fSTom Rhodes infunc(int fd, int mask, void *arg) 667911a190fSTom Rhodes { 668911a190fSTom Rhodes char buf[1024]; 669911a190fSTom Rhodes int ret; 670911a190fSTom Rhodes 671911a190fSTom Rhodes mask = mask; 672911a190fSTom Rhodes arg = arg; 673911a190fSTom Rhodes if((ret = read(fd, buf, sizeof(buf))) < 0) 674911a190fSTom Rhodes _panic("read: %s", strerror(errno)); 675911a190fSTom Rhodes write(1, "stdin:", 6); 676911a190fSTom Rhodes write(1, buf, ret); 677911a190fSTom Rhodes } 678911a190fSTom Rhodes 679911a190fSTom Rhodes void tfunc0(int tid, void *arg); 680911a190fSTom Rhodes void tfunc1(int tid, void *arg); 681911a190fSTom Rhodes 682911a190fSTom Rhodes void 683911a190fSTom Rhodes tfunc0(int tid, void *arg) 684911a190fSTom Rhodes { 685911a190fSTom Rhodes printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg); 686911a190fSTom Rhodes } 687911a190fSTom Rhodes void 688911a190fSTom Rhodes tfunc1(int tid, void *arg) 689911a190fSTom Rhodes { 690911a190fSTom Rhodes printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg); 691911a190fSTom Rhodes } 692*ca77a23fSHartmut Brandt void 693*ca77a23fSHartmut Brandt tfunc2(int tid, void *arg) 694*ca77a23fSHartmut Brandt { 695*ca77a23fSHartmut Brandt static u_int count = 0; 696*ca77a23fSHartmut Brandt 697*ca77a23fSHartmut Brandt if (++count % 10000 == 0) 698*ca77a23fSHartmut Brandt printf("%4.1f -- %d\n", elaps(), tid); 699*ca77a23fSHartmut Brandt } 700911a190fSTom Rhodes 701911a190fSTom Rhodes void first(int tid, void *arg); 702911a190fSTom Rhodes void second(int tid, void *arg); 703911a190fSTom Rhodes 704911a190fSTom Rhodes void 705911a190fSTom Rhodes second(int tid, void *arg) 706911a190fSTom Rhodes { 707911a190fSTom Rhodes printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg); 708*ca77a23fSHartmut Brandt poll_start_utimer(5500000, 0, first, "first"); 709911a190fSTom Rhodes poll_stop_timer(t1); 710911a190fSTom Rhodes t0 = poll_start_timer(1000, 1, tfunc0, "1 second"); 711911a190fSTom Rhodes } 712911a190fSTom Rhodes void 713911a190fSTom Rhodes first(int tid, void *arg) 714911a190fSTom Rhodes { 715911a190fSTom Rhodes printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg); 716911a190fSTom Rhodes poll_start_timer(3700, 0, second, "second"); 717911a190fSTom Rhodes poll_stop_timer(t0); 718911a190fSTom Rhodes t1 = poll_start_timer(250, 1, tfunc1, "1/4 second"); 719911a190fSTom Rhodes } 720911a190fSTom Rhodes 721911a190fSTom Rhodes int 722911a190fSTom Rhodes main(int argc, char *argv[]) 723911a190fSTom Rhodes { 724911a190fSTom Rhodes argv = argv; 725911a190fSTom Rhodes gettimeofday(&start, NULL); 726911a190fSTom Rhodes poll_register(0, infunc, NULL, POLL_IN); 727*ca77a23fSHartmut Brandt 728*ca77a23fSHartmut Brandt if (argc < 2) { 729911a190fSTom Rhodes t0 = poll_start_timer(1000, 1, tfunc0, "1 second"); 730911a190fSTom Rhodes poll_start_timer(2500, 0, first, "first"); 731*ca77a23fSHartmut Brandt } else { 732*ca77a23fSHartmut Brandt t0 = poll_start_utimer(300, 1, tfunc2, NULL); 733*ca77a23fSHartmut Brandt } 734911a190fSTom Rhodes 735911a190fSTom Rhodes while(1) 736911a190fSTom Rhodes poll_dispatch(1); 737911a190fSTom Rhodes 738911a190fSTom Rhodes return 0; 739911a190fSTom Rhodes } 740911a190fSTom Rhodes # endif 741