2 * Copyright (c)1996-2002 by Hartmut Brandt
\r
3 * All rights reserved.
\r
5 * Author: Hartmut Brandt
\r
7 * Redistribution of this software and documentation and use in source and
\r
8 * binary forms, with or without modification, are permitted provided that
\r
9 * the following conditions are met:
\r
11 * 1. Redistributions of source code or documentation must retain the above
\r
12 * copyright notice, this list of conditions and the following disclaimer.
\r
13 * 2. Redistributions in binary form must reproduce the above copyright
\r
14 * notice, this list of conditions and the following disclaimer in the
\r
15 * documentation and/or other materials provided with the distribution.
\r
17 * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE AUTHOR
\r
18 * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
\r
19 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
\r
20 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
\r
21 * THE AUTHOR OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
\r
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
\r
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
\r
24 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
\r
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
\r
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
\r
27 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\r
30 * These functions try to hide the poll/select/setitimer interface from the
\r
31 * user. You associate callback functions with file descriptors and timers.
\r
33 * $Begemot: libbegemot/rpoll.c,v 1.14 2004/09/21 15:59:00 brandt Exp $
\r
36 # include <stdlib.h>
\r
37 # include <stddef.h>
\r
38 # include <stdarg.h>
\r
39 # include <signal.h>
\r
40 # include <string.h>
\r
43 # include <assert.h>
\r
44 # include <unistd.h>
\r
45 # include <sys/time.h>
\r
48 * There happens to be linuxes which read siginfo.h when including
\r
49 * signal.h, which, for no appearent reason, defines these symbols.
\r
65 # ifdef NEED_POLL_XOPEN_TWIDDLE
\r
66 # define __USE_XOPEN
\r
69 # ifdef NEED_POLL_XOPEN_TWIDDLE
\r
72 # include <stropts.h>
\r
76 * the second define is for Linux, which sometimes fails to
\r
79 # if defined(USE_SELECT) || !defined(INFTIM)
\r
80 # define INFTIM (-1)
\r
83 # if defined(SIGPOLL)
\r
84 # define SIGNAL SIGPOLL
\r
87 # define SIGNAL SIGIO
\r
92 # define poll_in (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)
\r
93 # define poll_out (POLLOUT | POLLWRNORM | POLLWRBAND)
\r
94 # define poll_except (POLLERR | POLLHUP)
\r
97 # ifdef BROKEN_SELECT_PROTO
\r
98 # define SELECT_CAST(P) (int *)P
\r
100 # define SELECT_CAST(P) P
\r
104 typedef signed long long tval_t;
\r
106 static inline tval_t GETMSECS(void);
\r
108 static inline tval_t
\r
110 struct timeval tval;
\r
112 (void)gettimeofday(&tval, NULL);
\r
113 return (tval_t)tval.tv_sec*1000+tval.tv_usec/1000;
\r
117 * Simple fatal exit.
\r
120 _panic(const char *fmt, ...)
\r
125 fprintf(stderr, "panic: ");
\r
126 vfprintf(stderr, fmt, ap);
\r
127 fprintf(stderr, "\n");
\r
134 _xrealloc(void *p, size_t s)
\r
139 if((ptr=malloc(s)) == NULL && (s!=0 || (ptr=malloc(1)) == NULL))
\r
140 _panic("out of memory: xrealloc(%lx, %lu)",
\r
141 (unsigned long)p, (unsigned long)s);
\r
142 } else if(s == 0) {
\r
144 if((ptr=malloc(s)) == NULL && (ptr=malloc(1)) == NULL)
\r
145 _panic("out of memory: xrealloc(%lx, %lu)",
\r
146 (unsigned long)p, (unsigned long)s);
\r
148 if((ptr = realloc(p, s)) == NULL)
\r
149 _panic("out of memory: xrealloc(%lx, %lu)",
\r
150 (unsigned long)p, (unsigned long)s);
\r
157 * This structure holds one registration record for files
\r
160 int fd; /* file descriptor (-1 if struct unused) */
\r
161 int mask; /* event flags */
\r
162 void * arg; /* client arg */
\r
163 poll_f func; /* handler */
\r
165 struct pollfd *pfd; /* pointer to corresponding poll() structure */
\r
173 u_int msecs; /* millisecond value of the timer */
\r
174 int repeat; /* one shot or repeat? */
\r
175 void *arg; /* client arg */
\r
176 timer_f func; /* handler, 0 means disfunct */
\r
177 tval_t when; /* next time to trigger in msecs! */
\r
180 /* how many records should our table grow at once? */
\r
181 # define POLL_REG_GROW 100
\r
184 static struct pollfd * pfd; /* fd list for poll() */
\r
188 static fd_set rset, wset, xset; /* file descriptor sets for select() */
\r
189 static int maxfd; /* maximum fd number */
\r
192 static int in_dispatch;
\r
194 static PollReg_t * regs; /* registration records */
\r
195 static u_int regs_alloc; /* how many are allocated */
\r
196 static u_int regs_used; /* upper used limit */
\r
197 static sigset_t bset; /* blocked signals */
\r
198 static int rebuild; /* rebuild table on next dispatch() */
\r
200 static int * tfd; /* sorted entries */
\r
201 static u_int tfd_alloc; /* number of entries allocated */
\r
202 static u_int tfd_used; /* number of entries used */
\r
203 static PollTim_t * tims; /* timer registration records */
\r
204 static u_int tims_alloc; /* how many are allocated */
\r
205 static u_int tims_used; /* how many are used */
\r
206 static int resort; /* resort on next dispatch */
\r
209 int rpoll_policy; /* if 0 start sched callbacks from 0 else try round robin */
\r
211 static void poll_build(void);
\r
212 static void poll_blocksig(void);
\r
213 static void poll_unblocksig(void);
\r
214 static void sort_timers(void);
\r
218 * Private function to block SIGPOLL or SIGIO for a short time.
\r
219 * Don't forget to call poll_unblock before return from the calling function.
\r
220 * Don't change the mask between this calls (your changes will be lost).
\r
223 poll_blocksig(void)
\r
228 sigaddset(&set, SIGNAL);
\r
230 if(sigprocmask(SIG_BLOCK, &set, &bset))
\r
231 _panic("sigprocmask(SIG_BLOCK): %s", strerror(errno));
\r
235 * unblock the previously blocked signal
\r
238 poll_unblocksig(void)
\r
240 if(sigprocmask(SIG_SETMASK, &bset, NULL))
\r
241 _panic("sigprocmask(SIG_SETMASK): %s", strerror(errno));
\r
245 * Register the file descriptor fd. If the event corresponding to
\r
246 * mask arrives func is called with arg.
\r
247 * If fd is already registered with that func and arg, only the mask
\r
249 * We block the IO-signal, so the dispatch function can be called from
\r
250 * within the signal handler.
\r
253 poll_register(int fd, poll_f func, void *arg, int mask)
\r
259 /* already registered? */
\r
260 for(p = regs; p < ®s[regs_alloc]; p++)
\r
261 if(p->fd == fd && p->func == func && p->arg == arg) {
\r
266 if(p == ®s[regs_alloc]) {
\r
267 /* no - register */
\r
269 /* find a free slot */
\r
270 for(p = regs; p < ®s[regs_alloc]; p++)
\r
274 if(p == ®s[regs_alloc]) {
\r
275 size_t newsize = regs_alloc + POLL_REG_GROW;
\r
276 regs = _xrealloc(regs, sizeof(regs[0]) * newsize);
\r
277 for(p = ®s[regs_alloc]; p < ®s[newsize]; p++) {
\r
283 p = ®s[regs_alloc];
\r
284 regs_alloc = newsize;
\r
299 fprintf(stderr, "poll_register(%d, %#lx, %#lx, %#x)->%d",
\r
300 fd, (u_long)func, (u_long)arg, mask, p - regs);
\r
305 * remove registration
\r
308 poll_unregister(int handle)
\r
311 fprintf(stderr, "poll_unregister(%d)", handle);
\r
315 regs[handle].fd = -1;
\r
317 regs[handle].pfd = NULL;
\r
326 * Build the structures used by poll() or select()
\r
336 f = pfd = _xrealloc(pfd, sizeof(pfd[0]) * regs_used);
\r
338 for(p = regs; p < ®s[regs_alloc]; p++)
\r
342 if(p->mask & POLL_IN)
\r
343 f->events |= poll_in;
\r
344 if(p->mask & POLL_OUT)
\r
345 f->events |= poll_out;
\r
346 if(p->mask & POLL_EXCEPT)
\r
347 f->events |= poll_except;
\r
351 assert(f == &pfd[regs_used]);
\r
359 for(p = regs; p < ®s[regs_alloc]; p++)
\r
363 if(p->mask & POLL_IN)
\r
364 FD_SET(p->fd, &rset);
\r
365 if(p->mask & POLL_OUT)
\r
366 FD_SET(p->fd, &wset);
\r
367 if(p->mask & POLL_EXCEPT)
\r
368 FD_SET(p->fd, &xset);
\r
374 poll_start_timer(u_int msecs, int repeat, timer_f func, void *arg)
\r
378 /* find unused entry */
\r
379 for(p = tims; p < &tims[tims_alloc]; p++)
\r
380 if(p->func == NULL)
\r
383 if(p == &tims[tims_alloc]) {
\r
384 if(tims_alloc == tims_used) {
\r
385 size_t newsize = tims_alloc + POLL_REG_GROW;
\r
386 tims = _xrealloc(tims, sizeof(tims[0]) * newsize);
\r
387 for(p = &tims[tims_alloc]; p < &tims[newsize]; p++)
\r
389 p = &tims[tims_alloc];
\r
390 tims_alloc = newsize;
\r
396 p->repeat = repeat;
\r
399 p->when = GETMSECS() + msecs;
\r
406 fprintf(stderr, "poll_start_timer(%u, %d, %#lx, %#lx)->%u",
\r
407 msecs, repeat, (u_long)func, (u_long)arg, p - tims);
\r
413 * Here we have to look into the sorted table, whether any entry there points
\r
414 * into the registration table for the deleted entry. This is needed,
\r
415 * because a unregistration can occure while we are scanning through the
\r
416 * table in dispatch(). Do this only, if we are really there - resorting
\r
417 * will sort out things if we are called from outside the loop.
\r
420 poll_stop_timer(int handle)
\r
425 fprintf(stderr, "poll_stop_timer(%d)", handle);
\r
427 tims[handle].func = NULL;
\r
435 for(i = 0; i < tfd_used; i++)
\r
436 if(tfd[i] == handle) {
\r
443 * Squeeze and sort timer table.
\r
444 * Should perhaps use a custom sort.
\r
447 tim_cmp(const void *p1, const void *p2)
\r
449 int t1 = *(const int *)p1;
\r
450 int t2 = *(const int *)p2;
\r
452 return tims[t1].when < tims[t2].when ? -1
\r
453 : tims[t1].when > tims[t2].when ? +1
\r
458 * Reconstruct the tfd-array. This will be an sorted array of indexes
\r
459 * to the used entries in tims. The next timer to expire will be infront
\r
460 * of the array. tfd_used is the number of used entries. The array is
\r
461 * re-allocated if needed.
\r
469 if(tims_used > tfd_alloc) {
\r
470 tfd_alloc = tims_used;
\r
471 tfd = _xrealloc(tfd, sizeof(int *) * tfd_alloc);
\r
476 for(i = 0; i < tims_alloc; i++)
\r
479 assert(pp - tfd == (ptrdiff_t)tims_used);
\r
481 tfd_used = tims_used;
\r
483 qsort(tfd, tfd_used, sizeof(int), tim_cmp);
\r
487 * Poll the file descriptors and dispatch to the right function
\r
488 * If wait is true the poll blocks until somewhat happens.
\r
489 * Don't use a pointer here, because the called function may cause
\r
490 * a reallocation! The check for pfd != NULL is required, because
\r
491 * a sequence of unregister/register could make the wrong callback
\r
492 * to be called. So we clear pfd in unregister and check here.
\r
495 poll_dispatch(int wait)
\r
501 static u_int last_index;
\r
504 fd_set nrset, nwset, nxset;
\r
519 /* in wait mode - compute the timeout */
\r
525 fprintf(stderr, "now=%"QUADFMT"u", now);
\r
526 for(i = 0; i < tims_used; i++)
\r
527 fprintf(stderr, "timers[%2d] = %"QUADFMT"d", i, tfd[i]->when - now);
\r
530 if((tout = tims[tfd[0]].when - now) < 0)
\r
538 fprintf(stderr, "rpoll -- selecting with tout=%u", tout);
\r
542 ret = poll(pfd, regs_used, tout);
\r
549 if(tout != INFTIM) {
\r
550 tv.tv_sec = tout / 1000;
\r
551 tv.tv_usec = (tout % 1000) * 1000;
\r
553 ret = select(maxfd+1,
\r
554 SELECT_CAST(&nrset),
\r
555 SELECT_CAST(&nwset),
\r
556 SELECT_CAST(&nxset), (tout==INFTIM) ? 0 : &tv);
\r
562 _panic("poll/select: %s", strerror(errno));
\r
565 /* dispatch files */
\r
567 for(i = 0; i < regs_alloc; i++) {
\r
568 idx = rpoll_policy ? ((last_index+i) % regs_alloc) : i;
\r
570 assert(idx < regs_alloc);
\r
572 if(regs[idx].fd >= 0) {
\r
576 if(regs[idx].pfd) {
\r
577 if(regs[idx].pfd->revents & poll_in)
\r
579 if(regs[idx].pfd->revents & poll_out)
\r
581 if(regs[idx].pfd->revents & poll_except)
\r
582 mask |= POLL_EXCEPT;
\r
586 if(FD_ISSET(regs[idx].fd, &nrset))
\r
588 if(FD_ISSET(regs[idx].fd, &nwset))
\r
590 if(FD_ISSET(regs[idx].fd, &nxset))
\r
591 mask |= POLL_EXCEPT;
\r
593 assert(idx < regs_alloc);
\r
597 fprintf(stderr, "poll_dispatch() -- "
\r
599 regs[idx].fd, idx);
\r
600 (*regs[idx].func)(regs[idx].fd, mask, regs[idx].arg);
\r
608 /* dispatch timeouts */
\r
611 for(i = 0; i < tfd_used; i++) {
\r
614 if(tims[tfd[i]].when > now)
\r
617 fprintf(stderr, "rpoll_dispatch() -- timeout %d",tfd[i]);
\r
618 (*tims[tfd[i]].func)(tfd[i], tims[tfd[i]].arg);
\r
621 if(tims[tfd[i]].repeat)
\r
622 tims[tfd[i]].when = now + tims[tfd[i]].msecs;
\r
624 tims[tfd[i]].func = NULL;
\r
636 struct timeval start, now;
\r
639 double elaps(void);
\r
640 void infunc(int fd, int mask, void *arg);
\r
645 gettimeofday(&now, NULL);
\r
647 return (double)(10 * now.tv_sec + now.tv_usec / 100000 - 10 * start.tv_sec - start.tv_usec / 100000)
\r
652 infunc(int fd, int mask, void *arg)
\r
659 if((ret = read(fd, buf, sizeof(buf))) < 0)
\r
660 _panic("read: %s", strerror(errno));
\r
661 write(1, "stdin:", 6);
\r
662 write(1, buf, ret);
\r
665 void tfunc0(int tid, void *arg);
\r
666 void tfunc1(int tid, void *arg);
\r
669 tfunc0(int tid, void *arg)
\r
671 printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
\r
674 tfunc1(int tid, void *arg)
\r
676 printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
\r
679 void first(int tid, void *arg);
\r
680 void second(int tid, void *arg);
\r
683 second(int tid, void *arg)
\r
685 printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
\r
686 poll_start_timer(5500, 0, first, "first");
\r
687 poll_stop_timer(t1);
\r
688 t0 = poll_start_timer(1000, 1, tfunc0, "1 second");
\r
691 first(int tid, void *arg)
\r
693 printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
\r
694 poll_start_timer(3700, 0, second, "second");
\r
695 poll_stop_timer(t0);
\r
696 t1 = poll_start_timer(250, 1, tfunc1, "1/4 second");
\r
700 main(int argc, char *argv[])
\r
704 gettimeofday(&start, NULL);
\r
705 poll_register(0, infunc, NULL, POLL_IN);
\r
706 t0 = poll_start_timer(1000, 1, tfunc0, "1 second");
\r
707 poll_start_timer(2500, 0, first, "first");
\r