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