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
GETUSECS(void)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
_panic(const char * fmt,...)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 *
_xrealloc(void * p,size_t s)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
poll_blocksig(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
poll_unblocksig(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
poll_register(int fd,poll_f func,void * arg,int mask)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
poll_unregister(int handle)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
poll_build(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
poll_start_timer(u_int msecs,int repeat,timer_f func,void * arg)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
poll_start_utimer(unsigned long long usecs,int repeat,timer_f func,void * arg)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
poll_stop_timer(int handle)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
tim_cmp(const void * p1,const void * p2)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
sort_timers(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
poll_dispatch(int wait)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
elaps(void)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
infunc(int fd,int mask,void * arg)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
tfunc0(int tid,void * arg)672 tfunc0(int tid, void *arg)
673 {
674 printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
675 }
676 void
tfunc1(int tid,void * arg)677 tfunc1(int tid, void *arg)
678 {
679 printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
680 }
681 void
tfunc2(int tid,void * arg)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
second(int tid,void * arg)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
first(int tid,void * arg)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
main(int argc,char * argv[])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