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