]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/libbegemot/rpoll.c
Remove spurious newline
[FreeBSD/FreeBSD.git] / contrib / libbegemot / rpoll.c
1 /*
2  * Copyright (c)1996-2002 by Hartmut Brandt
3  *      All rights reserved.
4  *
5  * Author: Hartmut Brandt
6  *
7  * Redistribution of this software and documentation and use in source and
8  * binary forms, with or without modification, are permitted provided that
9  * the following conditions are met:
10  * 
11  * 1. Redistributions of source code or documentation must retain the above
12  *   copyright notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in the
15  *   documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE AUTHOR 
18  * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
19  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
21  * THE AUTHOR OR ITS CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
24  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 /*
30  * These functions try to hide the poll/select/setitimer interface from the
31  * user. You associate callback functions with file descriptors and timers.
32  *
33  * $Begemot: libbegemot/rpoll.c,v 1.14 2004/09/21 15:59:00 brandt Exp $
34  */
35 # include <stdio.h>
36 # include <stdlib.h>
37 # include <stddef.h>
38 # include <stdarg.h>
39 # include <signal.h>
40 # include <string.h>
41 # include <errno.h>
42 # include <time.h>
43 # include <assert.h>
44 # include <unistd.h>
45 # include <sys/time.h>
46
47 # include "rpoll.h"
48
49 /*
50 # define DEBUG
51 */
52
53 # ifdef USE_POLL
54 #  ifdef NEED_POLL_XOPEN_TWIDDLE
55 #   define __USE_XOPEN
56 #  endif
57 #  include <poll.h>
58 #  ifdef NEED_POLL_XOPEN_TWIDDLE
59 #   undef __USE_XOPEN
60 #  endif
61 #  include <stropts.h>
62 # endif
63
64 /*
65  * the second define is for Linux, which sometimes fails to
66  * declare INFTIM.
67  */
68 # if defined(USE_SELECT) || !defined(INFTIM)
69 #  define INFTIM (-1)
70 # endif
71
72 # if defined(SIGPOLL)
73 #  define SIGNAL        SIGPOLL
74 # else
75 #  if defined(SIGIO)
76 #   define SIGNAL       SIGIO
77 #  endif
78 # endif
79
80 # ifdef USE_POLL
81 #  define poll_in       (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)
82 #  define poll_out      (POLLOUT | POLLWRNORM | POLLWRBAND)
83 #  define poll_except   (POLLERR | POLLHUP)
84 # endif
85
86 # ifdef BROKEN_SELECT_PROTO
87 #  define SELECT_CAST(P)        (int *)P
88 # else
89 #  define SELECT_CAST(P)        P
90 # endif
91
92
93 typedef int64_t tval_t;
94
95 static inline tval_t GETUSECS(void);
96
97 static inline tval_t
98 GETUSECS(void) {
99         struct timeval tval;
100
101         (void)gettimeofday(&tval, NULL);
102         return (tval_t)tval.tv_sec * 1000000 + tval.tv_usec;
103 }
104
105 /*
106  * Simple fatal exit.
107  */
108 static void
109 _panic(const char *fmt, ...)
110 {
111         va_list ap;
112
113         va_start(ap, fmt);
114         fprintf(stderr, "panic: ");
115         vfprintf(stderr, fmt, ap);
116         fprintf(stderr, "\n");
117         va_end(ap);
118
119         exit(1);
120 }
121
122 static void *
123 _xrealloc(void *p, size_t s)
124 {
125         void *ptr;
126
127         if(p == NULL) {
128                 if((ptr=malloc(s)) == NULL && (s!=0 || (ptr=malloc(1)) == NULL))
129                         _panic("out of memory: xrealloc(%lx, %lu)",
130                                 (unsigned long)p, (unsigned long)s);
131         } else if(s == 0) {
132                 free(p);
133                 if((ptr=malloc(s)) == NULL && (ptr=malloc(1)) == NULL)
134                         _panic("out of memory: xrealloc(%lx, %lu)",
135                                 (unsigned long)p, (unsigned long)s);
136         } else {
137                 if((ptr = realloc(p, s)) == NULL)
138                         _panic("out of memory: xrealloc(%lx, %lu)",
139                                 (unsigned long)p, (unsigned long)s);
140         }
141
142         return ptr;
143 }
144
145 /*
146  * This structure holds one registration record for files
147  */
148 typedef struct {
149         int     fd;             /* file descriptor (-1 if struct unused) */
150         int     mask;           /* event flags */
151         void *  arg;            /* client arg */
152         poll_f  func;           /* handler */
153 # ifdef USE_POLL
154         struct pollfd *pfd;     /* pointer to corresponding poll() structure */
155 # endif
156 } PollReg_t;
157
158 /*
159  * Now for timers
160  */
161 typedef struct {
162         uint64_t usecs;         /* microsecond value of the timer */
163         int     repeat;         /* one shot or repeat? */
164         void    *arg;           /* client arg */
165         timer_f func;           /* handler, 0 means disfunct */
166         tval_t  when;           /* next time to trigger in usecs! */
167 } PollTim_t;
168
169 /* how many records should our table grow at once? */
170 # define POLL_REG_GROW  100
171
172 # ifdef USE_POLL
173 static struct pollfd *  pfd;            /* fd list for poll() */
174 # endif
175
176 # ifdef USE_SELECT
177 static fd_set rset, wset, xset;         /* file descriptor sets for select() */
178 static int maxfd;                       /* maximum fd number */
179 # endif
180
181 static int              in_dispatch;
182
183 static PollReg_t *      regs;           /* registration records */
184 static u_int            regs_alloc;     /* how many are allocated */
185 static u_int            regs_used;      /* upper used limit */
186 static sigset_t         bset;           /* blocked signals */
187 static int              rebuild;        /* rebuild table on next dispatch() */
188
189 static int *            tfd;            /* sorted entries */
190 static u_int            tfd_alloc;      /* number of entries allocated */
191 static u_int            tfd_used;       /* number of entries used */
192 static PollTim_t *      tims;           /* timer registration records */
193 static u_int            tims_alloc;     /* how many are allocated */
194 static u_int            tims_used;      /* how many are used */
195 static int              resort;         /* resort on next dispatch */
196
197 int     rpoll_trace;
198 int     rpoll_policy;   /* if 0 start sched callbacks from 0 else try round robin */
199
200 static void poll_build(void);
201 static void poll_blocksig(void);
202 static void poll_unblocksig(void);
203 static void sort_timers(void);
204
205
206 /*
207  * Private function to block SIGPOLL or SIGIO for a short time.
208  * Don't forget to call poll_unblock before return from the calling function.
209  * Don't change the mask between this calls (your changes will be lost).
210  */
211 static void
212 poll_blocksig(void)
213 {
214         sigset_t set;
215
216         sigemptyset(&set);
217         sigaddset(&set, SIGNAL);
218
219         if(sigprocmask(SIG_BLOCK, &set, &bset))
220                 _panic("sigprocmask(SIG_BLOCK): %s", strerror(errno));
221 }
222
223 /*
224  * unblock the previously blocked signal
225  */
226 static void
227 poll_unblocksig(void)
228 {
229         if(sigprocmask(SIG_SETMASK, &bset, NULL))
230                 _panic("sigprocmask(SIG_SETMASK): %s", strerror(errno));
231 }
232  
233 /*
234  * Register the file descriptor fd. If the event corresponding to
235  * mask arrives func is called with arg.
236  * If fd is already registered with that func and arg, only the mask
237  * is changed.
238  * We block the IO-signal, so the dispatch function can be called from
239  * within the signal handler.
240  */
241 int
242 poll_register(int fd, poll_f func, void *arg, int mask)
243 {
244         PollReg_t * p;
245
246         poll_blocksig();
247
248         /* already registered? */
249         for(p = regs; p < &regs[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 == &regs[regs_alloc]) {
256                 /* no - register */
257
258                 /* find a free slot */
259                 for(p = regs; p < &regs[regs_alloc]; p++)
260                         if(p->fd == -1)
261                                 break;
262
263                 if(p == &regs[regs_alloc]) {
264                         size_t newsize = regs_alloc + POLL_REG_GROW;
265                         regs = _xrealloc(regs, sizeof(regs[0]) * newsize);
266                         for(p = &regs[regs_alloc]; p < &regs[newsize]; p++) {
267                                 p->fd = -1;
268 # ifdef USE_POLL
269                                 p->pfd = NULL;
270 # endif
271                         }
272                         p = &regs[regs_alloc];
273                         regs_alloc = newsize;
274                 }
275
276                 p->fd = fd;
277                 p->arg = arg;
278                 p->mask = mask;
279                 p->func = func;
280
281                 regs_used++;
282                 rebuild = 1;
283         }
284
285         poll_unblocksig();
286
287         if(rpoll_trace)
288                 fprintf(stderr, "poll_register(%d, %p, %p, %#x)->%tu",
289                         fd, (void *)func, (void *)arg, mask, p - regs);
290         return p - regs;
291 }
292
293 /*
294  * remove registration
295  */
296 void
297 poll_unregister(int handle)
298 {
299         if(rpoll_trace)
300                 fprintf(stderr, "poll_unregister(%d)", handle);
301
302         poll_blocksig();
303
304         regs[handle].fd = -1;
305 # ifdef USE_POLL
306         regs[handle].pfd = NULL;
307 # endif
308         rebuild = 1;
309         regs_used--;
310
311         poll_unblocksig();
312 }
313
314 /*
315  * Build the structures used by poll() or select() 
316  */
317 static void
318 poll_build(void)
319 {
320         PollReg_t * p;
321
322 # ifdef USE_POLL
323         struct pollfd * f;
324
325         f = pfd = _xrealloc(pfd, sizeof(pfd[0]) * regs_used);
326
327         for(p = regs; p < &regs[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 < &regs[regs_alloc]; p++)
349                 if(p->fd >= 0) {
350                         if(p->fd > maxfd)
351                                 maxfd = p->fd;
352                         if(p->mask & RPOLL_IN)
353                                 FD_SET(p->fd, &rset);
354                         if(p->mask & RPOLL_OUT)
355                                 FD_SET(p->fd, &wset);
356                         if(p->mask & RPOLL_EXCEPT)
357                                 FD_SET(p->fd, &xset);
358                 }
359 # endif
360 }
361
362 int
363 poll_start_timer(u_int msecs, int repeat, timer_f func, void *arg)
364 {
365         return (poll_start_utimer((unsigned long long)msecs * 1000,
366             repeat, func, arg));
367 }
368
369 int
370 poll_start_utimer(unsigned long long usecs, int repeat, timer_f func, void *arg)
371 {
372         PollTim_t *p;
373
374         /* find unused entry */
375         for(p = tims; p < &tims[tims_alloc]; p++)
376                 if(p->func == NULL)
377                         break;
378
379         if(p == &tims[tims_alloc]) {
380                 if(tims_alloc == tims_used) {
381                         size_t newsize = tims_alloc + POLL_REG_GROW;
382                         tims = _xrealloc(tims, sizeof(tims[0]) * newsize);
383                         for(p = &tims[tims_alloc]; p < &tims[newsize]; p++)
384                                 p->func = NULL;
385                         p = &tims[tims_alloc];
386                         tims_alloc = newsize;
387                 }
388         }
389
390         /* create entry */
391         p->usecs = usecs;
392         p->repeat = repeat;
393         p->arg = arg;
394         p->func = func;
395         p->when = GETUSECS() + usecs;
396
397         tims_used++;
398
399         resort = 1;
400
401         if(rpoll_trace)
402                 fprintf(stderr, "poll_start_utimer(%llu, %d, %p, %p)->%tu",
403                         usecs, repeat, (void *)func, (void *)arg, p - tims);
404
405         return p - tims;
406 }
407
408 /*
409  * Here we have to look into the sorted table, whether any entry there points
410  * into the registration table for the deleted entry. This is needed,
411  * because a unregistration can occure while we are scanning through the
412  * table in dispatch(). Do this only, if we are really there - resorting
413  * will sort out things if we are called from outside the loop.
414  */
415 void
416 poll_stop_timer(int handle)
417 {
418         u_int i;
419
420         if(rpoll_trace)
421                 fprintf(stderr, "poll_stop_timer(%d)", handle);
422
423         tims[handle].func = NULL;
424         tims_used--;
425
426         resort = 1;
427
428         if(!in_dispatch)
429                 return;
430
431         for(i = 0; i < tfd_used; i++)
432                 if(tfd[i] == handle) {
433                         tfd[i] = -1;
434                         break;
435                 }
436 }
437
438 /*
439  * Squeeze and sort timer table.
440  * Should perhaps use a custom sort.
441  */
442 static int
443 tim_cmp(const void *p1, const void *p2)
444 {
445         int t1 = *(const int *)p1;
446         int t2 = *(const int *)p2;
447
448         return tims[t1].when < tims[t2].when ? -1
449              : tims[t1].when > tims[t2].when ? +1
450              :                                    0;
451 }
452
453 /*
454  * Reconstruct the tfd-array. This will be an sorted array of indexes
455  * to the used entries in tims. The next timer to expire will be infront
456  * of the array. tfd_used is the number of used entries. The array is
457  * re-allocated if needed.
458  */
459 static void
460 sort_timers(void)
461 {
462         int *pp;
463         u_int i;
464
465         if(tims_used > tfd_alloc) {
466                 tfd_alloc = tims_used;
467                 tfd  = _xrealloc(tfd, sizeof(int *) * tfd_alloc);
468         }
469
470         pp = tfd;
471
472         for(i = 0; i < tims_alloc; i++)
473                 if(tims[i].func)
474                         *pp++ = i;
475         assert(pp - tfd == (ptrdiff_t)tims_used);
476
477         tfd_used = tims_used;
478         if(tfd_used > 1)
479                 qsort(tfd, tfd_used, sizeof(int), tim_cmp);
480 }
481
482 /*
483  * Poll the file descriptors and dispatch to the right function
484  * If wait is true the poll blocks until somewhat happens.
485  * Don't use a pointer here, because the called function may cause
486  * a reallocation! The check for pfd != NULL is required, because
487  * a sequence of unregister/register could make the wrong callback
488  * to be called. So we clear pfd in unregister and check here.
489  */
490 void
491 poll_dispatch(int wait)
492 {
493         u_int i, idx;
494         int ret;
495         tval_t now;
496         tval_t tout;
497         static u_int last_index;
498
499 # ifdef USE_SELECT
500         fd_set nrset, nwset, nxset;
501         struct timeval tv;
502 # endif
503
504         in_dispatch = 1;
505
506         if(rebuild) {
507                 rebuild = 0;
508                 poll_build();
509         }
510         if(resort) {
511                 resort = 0;
512                 sort_timers();
513         }
514
515         /* in wait mode - compute the timeout */
516         if(wait) {
517                 if(tfd_used) {
518                         now = GETUSECS();
519 # ifdef DEBUG
520                         {
521                                 fprintf(stderr, "now=%llu", now);
522                                 for(i = 0; i < tims_used; i++)
523                                         fprintf(stderr, "timers[%2d] = %lld",
524                                             i, tfd[i]->when - now);
525                         }
526 # endif
527                         if((tout = tims[tfd[0]].when - now) < 0)
528                                 tout = 0;
529                 } else
530                         tout = INFTIM;
531         } else
532                 tout = 0;
533
534 # ifdef DEBUG
535         fprintf(stderr, "rpoll -- selecting with tout=%u", tout);
536 # endif
537
538 # ifdef USE_POLL
539         ret = poll(pfd, regs_used, tout == INFTIM ? INFTIM : (tout / 1000));
540 # endif
541
542 # ifdef USE_SELECT
543         nrset = rset;
544         nwset = wset;
545         nxset = xset;
546         if(tout != INFTIM) {
547                 tv.tv_sec = tout / 1000000;
548                 tv.tv_usec = tout % 1000000;
549         }
550         ret = select(maxfd+1,
551                 SELECT_CAST(&nrset),
552                 SELECT_CAST(&nwset),
553                 SELECT_CAST(&nxset), (tout==INFTIM) ? NULL : &tv);
554 # endif
555
556         if(ret == -1) {
557                 if(errno == EINTR)
558                         return;
559                 _panic("poll/select: %s", strerror(errno));
560         }
561
562         /* dispatch files */
563         if(ret > 0) {
564                 for(i = 0; i < regs_alloc; i++) {
565                         idx = rpoll_policy ? ((last_index+i) % regs_alloc) : i;
566
567                         assert(idx < regs_alloc);
568
569                         if(regs[idx].fd >= 0) {
570                                 int mask = 0;
571
572 # ifdef USE_POLL
573                                 if(regs[idx].pfd) {
574                                         if ((regs[idx].mask & RPOLL_IN) &&
575                                             (regs[idx].pfd->revents & poll_in))
576                                                 mask |= RPOLL_IN;
577                                         if ((regs[idx].mask & RPOLL_OUT) &&
578                                             (regs[idx].pfd->revents & poll_out))
579                                                 mask |= RPOLL_OUT;
580                                         if((regs[idx].mask & RPOLL_EXCEPT) &&
581                                             (regs[idx].pfd->revents & poll_except))
582                                                 mask |= RPOLL_EXCEPT;
583                                 }
584 # endif
585 # ifdef USE_SELECT
586                                 if ((regs[idx].mask & RPOLL_IN) &&
587                                     FD_ISSET(regs[idx].fd, &nrset))
588                                         mask |= RPOLL_IN;
589                                 if ((regs[idx].mask & RPOLL_OUT) &&
590                                     FD_ISSET(regs[idx].fd, &nwset))
591                                         mask |= RPOLL_OUT;
592                                 if ((regs[idx].mask & RPOLL_EXCEPT) &&
593                                     FD_ISSET(regs[idx].fd, &nxset))
594                                         mask |= RPOLL_EXCEPT;
595 # endif
596                                 assert(idx < regs_alloc);
597
598                                 if(mask) {
599                                         if(rpoll_trace)
600                                                 fprintf(stderr, "poll_dispatch() -- "
601                                                     "file %d/%d %x",
602                                                     regs[idx].fd, idx, mask);
603                                         (*regs[idx].func)(regs[idx].fd, mask, regs[idx].arg);
604                                 }
605                         }
606
607                 }
608                 last_index++;
609         }
610
611         /* dispatch timeouts */
612         if(tfd_used) {
613                 now = GETUSECS();
614                 for(i = 0; i < tfd_used; i++) {
615                         if(tfd[i] < 0)
616                                 continue;
617                         if(tims[tfd[i]].when > now)
618                                 break;
619                         if(rpoll_trace)
620                                 fprintf(stderr, "rpoll_dispatch() -- timeout %d",tfd[i]);
621                         (*tims[tfd[i]].func)(tfd[i], tims[tfd[i]].arg);
622                         if(tfd[i] < 0)
623                                 continue;
624                         if(tims[tfd[i]].repeat)
625                                 tims[tfd[i]].when = now + tims[tfd[i]].usecs;
626                         else {
627                                 tims[tfd[i]].func = NULL;
628                                 tims_used--;
629                                 tfd[i] = -1;
630                         }
631                         resort = 1;
632                 }
633         }
634         in_dispatch = 0;
635 }
636
637
638 # ifdef TESTME
639 struct timeval start, now;
640 int t0, t1;
641
642 double elaps(void);
643 void infunc(int fd, int mask, void *arg);
644
645 double
646 elaps(void)
647 {
648         gettimeofday(&now, NULL);
649
650         return (double)(10 * now.tv_sec + now.tv_usec / 100000 -
651             10 * start.tv_sec - start.tv_usec / 100000) / 10;
652 }
653
654 void
655 infunc(int fd, int mask, void *arg)
656 {
657         char buf[1024];
658         int ret;
659
660         mask = mask;
661         arg = arg;
662         if((ret = read(fd, buf, sizeof(buf))) < 0)
663                 _panic("read: %s", strerror(errno));
664         write(1, "stdin:", 6);
665         write(1, buf, ret);
666 }
667
668 void tfunc0(int tid, void *arg);
669 void tfunc1(int tid, void *arg);
670
671 void
672 tfunc0(int tid, void *arg)
673 {
674         printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
675 }
676 void
677 tfunc1(int tid, void *arg)
678 {
679         printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
680 }
681 void
682 tfunc2(int tid, void *arg)
683 {
684         static u_int count = 0;
685
686         if (++count % 10000 == 0)
687                 printf("%4.1f -- %d\n", elaps(), tid);
688 }
689
690 void first(int tid, void *arg);
691 void second(int tid, void *arg);
692
693 void
694 second(int tid, void *arg)
695 {
696         printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
697         poll_start_utimer(5500000, 0, first, "first");
698         poll_stop_timer(t1);
699         t0 = poll_start_timer(1000, 1, tfunc0, "1 second");
700 }
701 void
702 first(int tid, void *arg)
703 {
704         printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
705         poll_start_timer(3700, 0, second, "second");
706         poll_stop_timer(t0);
707         t1 = poll_start_timer(250, 1, tfunc1, "1/4 second");
708 }
709
710 int
711 main(int argc, char *argv[])
712 {
713         argv = argv;
714         gettimeofday(&start, NULL);
715         poll_register(0, infunc, NULL, RPOLL_IN);
716
717         if (argc < 2) {
718                 t0 = poll_start_timer(1000, 1, tfunc0, "1 second");
719                 poll_start_timer(2500, 0, first, "first");
720         } else {
721                 t0 = poll_start_utimer(300, 1, tfunc2, NULL);
722         }
723
724         while(1)
725                 poll_dispatch(1);
726
727         return 0;
728 }
729 # endif