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