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