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