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