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