chiark / gitweb /
Packet handler functions now have a @typedef@ name.
[mLib] / sel.c
1 /* -*-c-*-
2  *
3  * $Id: sel.c,v 1.10 2001/06/22 19:35:58 mdw Exp $
4  *
5  * I/O multiplexing support
6  *
7  * (c) 1999 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of the mLib utilities library.
13  *
14  * mLib is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU Library General Public License as
16  * published by the Free Software Foundation; either version 2 of the
17  * License, or (at your option) any later version.
18  * 
19  * mLib is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU Library General Public License for more details.
23  * 
24  * You should have received a copy of the GNU Library General Public
25  * License along with mLib; if not, write to the Free
26  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27  * MA 02111-1307, USA.
28  */
29
30 /*----- Revision history --------------------------------------------------* 
31  *
32  * $Log: sel.c,v $
33  * Revision 1.10  2001/06/22 19:35:58  mdw
34  * Fix a large number of bugs.
35  *
36  * Revision 1.9  2001/02/03 19:07:08  mdw
37  * Ensure that timers set to go off in the past don't case a problem.
38  *
39  * Revision 1.8  2000/03/23 20:42:08  mdw
40  * Rearrange timeout handling to avoid list corruptions.
41  *
42  * Revision 1.7  1999/12/11 11:12:17  mdw
43  * Fix comment formatting error.
44  *
45  * Revision 1.6  1999/09/26 14:28:11  mdw
46  * (sel_select): Almost pointless efficiency tweak.
47  *
48  * Revision 1.5  1999/08/31 17:42:22  mdw
49  * New function `sel_force' to force a descriptor to be `selected'.
50  *
51  * Revision 1.4  1999/08/19 18:30:26  mdw
52  * Implement hooks for foreign select-using systems (currently not well
53  * tested).
54  *
55  * Revision 1.3  1999/05/21 22:13:59  mdw
56  * Use new `tv' macros.  Fix ordering bug for timeout selectors.
57  *
58  * Revision 1.2  1999/05/15 10:33:32  mdw
59  * Fix copyright notices.
60  *
61  * Revision 1.1  1999/05/14 21:01:14  mdw
62  * Integrated `select' handling bits from the background resolver project.
63  *
64  */
65
66 /*----- Header files ------------------------------------------------------*/
67
68 #include <assert.h>
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <string.h>
72
73 #include <sys/types.h>
74 #include <sys/time.h>
75 #include <unistd.h>
76
77 #include "sel.h"
78 #include "sub.h"
79 #include "tv.h"
80
81 /*----- Data structures ---------------------------------------------------*/
82
83 typedef struct sel_pendfile {
84   struct sel_pendfile *next;
85   sel_file *f;
86 } pfile;
87
88 typedef struct sel_pendtimer {
89   struct sel_pendtimer *next;
90   sel_timer *t;
91 } ptimer;
92
93 /*----- Main code ---------------------------------------------------------*/
94
95 /* --- @sel_init@ --- *
96  *
97  * Arguments:   @sel_state *s@ = pointer to a state block to initialize
98  *
99  * Returns:     ---
100  *
101  * Use:         Initializes a select state block.
102  */
103
104 void sel_init(sel_state *s)
105 {
106   int i;
107
108   for (i = 0; i < SEL_MODES; i++) {
109     s->files[i] = 0;
110     FD_ZERO(&s->fd[i]);
111   }
112   s->timers = 0;
113   s->hooks = 0;
114   s->args = 0;
115 }
116
117 /* --- @sel_initfile@ --- *
118  *
119  * Arguments:   @sel_state *s@ = select state to attach to
120  *              @sel_file *f@ = pointer to a file block to initialize
121  *              @int fd@ = the file descriptor to listen to
122  *              @unsigned mode@ = what to listen for
123  *              @void (*func)(int fd, unsigned mode, void *p)@ = handler
124  *              @void *p@ = argument to pass to handler
125  *
126  * Returns:     ---
127  *
128  * Use:         Initializes a file block ready for use.  The file block
129  *              isn't added to the list of things to do until a call to
130  *              @sel_addfile@.
131  */
132
133 void sel_initfile(sel_state *s, sel_file *f,
134                   int fd, unsigned mode,
135                   void (*func)(int /*fd*/, unsigned /*mode*/, void */*p*/),
136                   void *p)
137 {
138   f->s = s;
139   f->fd = fd;
140   f->mode = mode;
141   f->func = func;
142   f->p = p;
143   f->pend = 0;
144 }
145
146 /* --- @sel_addfile@ --- *
147  *
148  * Arguments:   @sel_file *f@ = pointer to a file block
149  *
150  * Returns:     ---
151  *
152  * Use:         Adds a file block into the list of things to listen to.
153  */
154
155 void sel_addfile(sel_file *f)
156 {
157   sel_file **ff = &f->s->files[f->mode];
158
159   /* --- This little dance looks like line-noise, but it does the job --- */
160
161   while (*ff && (*ff)->fd > f->fd)
162     ff = &(*ff)->next;
163   f->next = *ff;
164   f->prev = (sel_file *)ff;
165   if (*ff)
166     (*ff)->prev = f;
167   *ff = f;
168   FD_SET(f->fd, f->s->fd + f->mode);
169 }
170
171 /* --- @sel_force@ --- *
172  *
173  * Arguments:   @sel_file *f@ = pointer to file selector
174  *
175  * Returns:     ---
176  *
177  * Use:         Forces a file selector to be considered ready.  This is only
178  *              useful during a call to @sel_select@.  Of particular use is
179  *              forcing a write selector when there's something interesting
180  *              ready for it.
181  */
182
183 void sel_force(sel_file *f)
184 {
185   if (f->s->args)
186     FD_SET(f->fd, &f->s->args->fd[f->mode]);
187 }
188
189 /* --- @sel_rmfile@ --- *
190  *
191  * Arguments:   @sel_file *f@ = pointer to a file block
192  *
193  * Returns:     ---
194  *
195  * Use:         Removes a file block from the list of things to listen to.
196  */
197
198 void sel_rmfile(sel_file *f)
199 {
200   f->prev->next = f->next;
201   if (f->next)
202     f->next->prev = f->prev;
203   FD_CLR(f->fd, f->s->fd + f->mode);
204   if (f->pend) {
205     f->pend->f = 0;
206     f->pend = 0;
207   }
208 }
209
210 /* --- @sel_addtimer@ --- *
211  *
212  * Arguments:   @sel_state *s@ = pointer to a state block
213  *              @sel_timer *t@ = pointer to a timer block
214  *              @struct timeval *tv@ = pointer to time to activate
215  *              @void (*func)(struct timeval *tv, void *p)@ = handler
216  *              @void *p@ = argument for handler function
217  *
218  * Returns:     ---
219  *
220  * Use:         Registers and sets up a timer.
221  */
222
223 void sel_addtimer(sel_state *s, sel_timer *t,
224                   struct timeval *tv,
225                   void (*func)(struct timeval */*tv*/, void */*p*/),
226                   void *p)
227 {
228   sel_timer **tt = &s->timers;
229   { sel_timer *q; for (q = s->timers; q; q = q->next) assert(q != t); }
230
231   /* --- Set up the timer block --- */
232
233   t->tv = *tv;
234   t->func = func;
235   t->p = p;
236   t->pend = 0;
237
238   /* --- More line noise --- */
239   
240   while (*tt && TV_CMP(&(*tt)->tv, <, tv))
241     tt = &(*tt)->next;
242   t->next = *tt;
243   t->prev = (sel_timer *)tt;
244   if (*tt)
245     (*tt)->prev = t;
246   *tt = t;
247 }
248
249 /* --- @sel_rmtimer@ --- *
250  *
251  * Arguments:   @sel_timer *t@ = pointer to timer block
252  *
253  * Returns:     ---
254  *
255  * Use:         Removes a timer from the list of timers.
256  */
257
258 void sel_rmtimer(sel_timer *t)
259 {
260   t->prev->next = t->next;
261   if (t->next)
262     t->next->prev = t->prev;
263   if (t->pend) {
264     t->pend->t = 0;
265     t->pend = 0;
266   }
267 }
268
269 /* --- @sel_addhook@ --- *
270  *
271  * Arguments:   @sel_state *s@ = pointer to state block
272  *              @sel_hook *h@ = pointer to hook block
273  *              @sel_hookfn before, after@ = hook functions
274  *              @void *p@ = pointer argument to pass to hook functions
275  *
276  * Returns:     ---
277  *
278  * Use:         Registers hook functions to be called on each select call.
279  */
280
281 void sel_addhook(sel_state *s, sel_hook *h,
282                  sel_hookfn before, sel_hookfn after,
283                  void *p)
284 {
285   h->before = before;
286   h->after = after;
287   h->p = p;
288   h->next = s->hooks;
289   h->prev = (sel_hook *)&s->hooks;
290   if (s->hooks)
291     s->hooks->prev = h;
292   s->hooks = h;
293 }
294
295 /* --- @sel_rmhook@ --- *
296  *
297  * Arguments:   @sel_hook *h@ = pointer to hook block
298  *
299  * Returns:     ---
300  *
301  * Use:         Removes hook functions.
302  */
303
304 void sel_rmhook(sel_hook *h)
305 {
306   if (h->next)
307     h->next->prev = h->prev;
308   h->prev->next = h->next;
309 }
310
311 /* --- @sel_fdmerge@ --- *
312  *
313  * Arguments:   @fd_set *dest@ = destination FD set
314  *              @fd_set *fd@ = pointer to set to merge
315  *              @int maxfd@ = highest numbered descriptor in @fd@ + 1
316  *
317  * Returns:     Actual highest numbered descriptor.
318  *
319  * Use:         Merges file descriptor sets, and returns an accurate @maxfd@
320  *              value.
321  */
322
323 int sel_fdmerge(fd_set *dest, fd_set *fd, int maxfd)
324 {
325   int i, m = -1;
326
327   for (i = 0; i < maxfd; i++) {
328     if (FD_ISSET(i, fd)) {
329       FD_SET(i, dest);
330       m = i;
331     }
332   }
333
334   return (m + 1);
335 }
336
337 /* --- @sel_select@ --- *
338  *
339  * Arguments:   @sel_state *s@ = pointer to state block
340  *
341  * Returns:     Zero if all OK, -1 on error.
342  *
343  * Use:         Does a @select@ call (or equivalent @poll@).
344  */
345
346 int sel_select(sel_state *s)
347 {
348   sel_args a;
349   int err;
350
351   /* --- Initialize the argument block --- */
352
353   {
354     int i;
355     a.maxfd = 0;
356     for (i = 0; i < SEL_MODES; i++) {
357       if (s->files[i] && s->files[i]->fd >= a.maxfd)
358         a.maxfd = s->files[i]->fd + 1;
359     }
360   }
361
362   memcpy(a.fd, s->fd, sizeof(a.fd));
363   if (s->timers || s->hooks)
364     gettimeofday(&a.now, 0);
365   if (!s->timers)
366     a.tvp = 0;
367   else {
368     if (TV_CMP(&s->timers->tv, >, &a.now))
369       TV_SUB(&a.tv, &s->timers->tv, &a.now);
370     else {
371       a.tv.tv_sec = 0;
372       a.tv.tv_usec = 0;
373     }
374     a.tvp = &a.tv;
375   }
376   s->args = &a;
377
378   /* --- Grind through the pre hooks --- */
379
380   {
381     sel_hook *h = s->hooks;
382     while (h) {
383       sel_hook *hh = h;
384       h = h->next;
385       if (hh->before)
386         hh->before(s, &a, hh->p);
387     }
388   }
389
390   /* --- Run the @select@ call --- */
391   
392   if ((err = select(a.maxfd,
393                     &a.fd[SEL_READ], &a.fd[SEL_WRITE], &a.fd[SEL_EXC],
394                     a.tvp)) < 0) {
395     s->args = 0;
396     return (err);
397   }
398
399   if (a.tvp)
400     gettimeofday(&a.now, 0);
401
402   /* --- Run through the hooks again --- */
403
404   {
405     sel_hook *h = s->hooks;
406     while (h) {
407       sel_hook *hh = h;
408       h = h->next;
409       if (hh->after)
410         hh->after(s, &a, hh->p);
411     }
412   }
413
414   /* --- Run through the timers --- */
415
416   if (s->timers) {
417     ptimer *pthead, *pt, **ptt = &pthead;
418     sel_timer *t;
419
420     for (t = s->timers; t && TV_CMP(&t->tv, <=, &a.now); t = t->next) {
421       pt = CREATE(ptimer);
422       pt->t = t;
423       t->pend = pt;
424       *ptt = pt;
425       ptt = &pt->next;
426     }
427     *ptt = 0;
428     if (t) {
429       t->prev->next = 0;
430       t->prev = (sel_timer *)&s->timers;
431     }
432     s->timers = t;
433     while (pthead) {
434       pt = pthead;
435       pthead = pt->next;
436       t = pt->t;
437       if (t) {
438         t->func(&a.now, t->p);
439         t->pend = 0;
440       }
441       DESTROY(pt);
442     }
443   }
444
445   /* --- And finally run through the files --- *
446    *
447    * Do reads first.  It's quite possible that a read might prompt a write,
448    * but the other way around is less likely.  Fortunately, the modes are
449    * in the right order for this.
450    */
451
452   {
453     int i;
454
455     for (i = 0; i < SEL_MODES; i++) {
456       pfile *pfhead, *pf, **pff = &pfhead;
457       sel_file *f;
458
459       for (f = s->files[i]; f; f = f->next) {
460         if (!FD_ISSET(f->fd, &a.fd[i]))
461           continue;
462         pf = CREATE(pfile);
463         pf->f = f;
464         f->pend = pf;
465         *pff = pf;
466         pff = &pf->next;
467       }
468       *pff = 0;
469       while (pfhead) {
470         pf = pfhead;
471         pfhead = pf->next;
472         f = pf->f;
473         if (f) {
474           f->func(f->fd, i, f->p);
475           f->pend = 0;
476         }
477       }
478     }
479   }
480
481   s->args = 0;
482   return (0);
483 }
484
485 /*----- That's all, folks -------------------------------------------------*/