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