]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/libbegemot/rpoll.c
This commit was generated by cvs2svn to compensate for changes in r157043,
[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 /*
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
104 typedef signed long long tval_t;
105
106 static inline tval_t GETMSECS(void);
107
108 static inline tval_t
109 GETMSECS(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  */
119 static 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
133 static 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  */
159 typedef 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  */
172 typedef 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
184 static struct pollfd *  pfd;            /* fd list for poll() */
185 # endif
186
187 # ifdef USE_SELECT
188 static fd_set rset, wset, xset;         /* file descriptor sets for select() */
189 static int maxfd;                       /* maximum fd number */
190 # endif
191
192 static int              in_dispatch;
193
194 static PollReg_t *      regs;           /* registration records */
195 static u_int            regs_alloc;     /* how many are allocated */
196 static u_int            regs_used;      /* upper used limit */
197 static sigset_t         bset;           /* blocked signals */
198 static int              rebuild;        /* rebuild table on next dispatch() */
199
200 static int *            tfd;            /* sorted entries */
201 static u_int            tfd_alloc;      /* number of entries allocated */
202 static u_int            tfd_used;       /* number of entries used */
203 static PollTim_t *      tims;           /* timer registration records */
204 static u_int            tims_alloc;     /* how many are allocated */
205 static u_int            tims_used;      /* how many are used */
206 static int              resort;         /* resort on next dispatch */
207
208 int     rpoll_trace;
209 int     rpoll_policy;   /* if 0 start sched callbacks from 0 else try round robin */
210
211 static void poll_build(void);
212 static void poll_blocksig(void);
213 static void poll_unblocksig(void);
214 static 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  */
222 static void
223 poll_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  */
237 static void
238 poll_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  */
252 int
253 poll_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 < &regs[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 == &regs[regs_alloc]) {
267                 /* no - register */
268
269                 /* find a free slot */
270                 for(p = regs; p < &regs[regs_alloc]; p++)
271                         if(p->fd == -1)
272                                 break;
273
274                 if(p == &regs[regs_alloc]) {
275                         size_t newsize = regs_alloc + POLL_REG_GROW;
276                         regs = _xrealloc(regs, sizeof(regs[0]) * newsize);
277                         for(p = &regs[regs_alloc]; p < &regs[newsize]; p++) {
278                                 p->fd = -1;
279 # ifdef USE_POLL
280                                 p->pfd = NULL;
281 # endif
282                         }
283                         p = &regs[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  */
307 void
308 poll_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  */
328 static void
329 poll_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 < &regs[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 < &regs[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
373 int
374 poll_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  */
419 void
420 poll_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  */
446 static int
447 tim_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  */
463 static void
464 sort_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  */
494 void
495 poll_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
636 struct timeval start, now;
637 int t0, t1;
638
639 double elaps(void);
640 void infunc(int fd, int mask, void *arg);
641
642 double
643 elaps(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
651 void
652 infunc(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
665 void tfunc0(int tid, void *arg);
666 void tfunc1(int tid, void *arg);
667
668 void
669 tfunc0(int tid, void *arg)
670 {
671         printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
672 }
673 void
674 tfunc1(int tid, void *arg)
675 {
676         printf("%4.1f -- %d: %s\n", elaps(), tid, (char *)arg);
677 }
678
679 void first(int tid, void *arg);
680 void second(int tid, void *arg);
681
682 void
683 second(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 }
690 void
691 first(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
699 int
700 main(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