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