chiark / gitweb /
daemonize, versioncmp: Generally useful functions from tripe.
[mLib] / sel.c
1 /* -*-c-*-
2  *
3  * $Id: sel.c,v 1.13 2004/04/08 01:36:13 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 /*----- Header files ------------------------------------------------------*/
31
32 #include <assert.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include <sys/types.h>
38 #include <sys/time.h>
39 #include <unistd.h>
40
41 #include "sel.h"
42 #include "sub.h"
43 #include "tv.h"
44
45 /*----- Data structures ---------------------------------------------------*/
46
47 typedef struct sel_pendfile {
48   struct sel_pendfile *next;
49   sel_file *f;
50 } pfile;
51
52 typedef struct sel_pendtimer {
53   struct sel_pendtimer *next;
54   sel_timer *t;
55 } ptimer;
56
57 /*----- Main code ---------------------------------------------------------*/
58
59 /* --- @sel_init@ --- *
60  *
61  * Arguments:   @sel_state *s@ = pointer to a state block to initialize
62  *
63  * Returns:     ---
64  *
65  * Use:         Initializes a select state block.
66  */
67
68 void sel_init(sel_state *s)
69 {
70   int i;
71
72   for (i = 0; i < SEL_MODES; i++) {
73     s->files[i] = 0;
74     FD_ZERO(&s->fd[i]);
75   }
76   s->timers = 0;
77   s->hooks = 0;
78   s->args = 0;
79 }
80
81 /* --- @sel_initfile@ --- *
82  *
83  * Arguments:   @sel_state *s@ = select state to attach to
84  *              @sel_file *f@ = pointer to a file block to initialize
85  *              @int fd@ = the file descriptor to listen to
86  *              @unsigned mode@ = what to listen for
87  *              @void (*func)(int fd, unsigned mode, void *p)@ = handler
88  *              @void *p@ = argument to pass to handler
89  *
90  * Returns:     ---
91  *
92  * Use:         Initializes a file block ready for use.  The file block
93  *              isn't added to the list of things to do until a call to
94  *              @sel_addfile@.
95  */
96
97 void sel_initfile(sel_state *s, sel_file *f,
98                   int fd, unsigned mode,
99                   void (*func)(int /*fd*/, unsigned /*mode*/, void */*p*/),
100                   void *p)
101 {
102   f->s = s;
103   f->fd = fd;
104   f->mode = mode;
105   f->func = func;
106   f->p = p;
107   f->pend = 0;
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[f->mode];
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 = ff;
129   if (*ff)
130     (*ff)->prev = &f->next;
131   *ff = f;
132   FD_SET(f->fd, f->s->fd + f->mode);
133 }
134
135 /* --- @sel_force@ --- *
136  *
137  * Arguments:   @sel_file *f@ = pointer to file selector
138  *
139  * Returns:     ---
140  *
141  * Use:         Forces a file selector to be considered ready.  This is only
142  *              useful during a call to @sel_select@.  Of particular use is
143  *              forcing a write selector when there's something interesting
144  *              ready for it.
145  */
146
147 void sel_force(sel_file *f)
148 {
149   if (f->s->args)
150     FD_SET(f->fd, &f->s->args->fd[f->mode]);
151 }
152
153 /* --- @sel_rmfile@ --- *
154  *
155  * Arguments:   @sel_file *f@ = pointer to a file block
156  *
157  * Returns:     ---
158  *
159  * Use:         Removes a file block from the list of things to listen to.
160  */
161
162 void sel_rmfile(sel_file *f)
163 {
164   *f->prev = f->next;
165   if (f->next)
166     f->next->prev = f->prev;
167   FD_CLR(f->fd, f->s->fd + f->mode);
168   if (f->pend) {
169     f->pend->f = 0;
170     f->pend = 0;
171   }
172 }
173
174 /* --- @sel_addtimer@ --- *
175  *
176  * Arguments:   @sel_state *s@ = pointer to a state block
177  *              @sel_timer *t@ = pointer to a timer block
178  *              @struct timeval *tv@ = pointer to time to activate
179  *              @void (*func)(struct timeval *tv, void *p)@ = handler
180  *              @void *p@ = argument for handler function
181  *
182  * Returns:     ---
183  *
184  * Use:         Registers and sets up a timer.
185  */
186
187 void sel_addtimer(sel_state *s, sel_timer *t,
188                   struct timeval *tv,
189                   void (*func)(struct timeval */*tv*/, void */*p*/),
190                   void *p)
191 {
192   sel_timer **tt = &s->timers;
193   { sel_timer *q; for (q = s->timers; q; q = q->next) assert(q != t); }
194
195   /* --- Set up the timer block --- */
196
197   t->tv = *tv;
198   t->func = func;
199   t->p = p;
200   t->pend = 0;
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 = tt;
208   if (*tt)
209     (*tt)->prev = &t->next;
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   if (t->pend) {
225     t->pend->t = 0;
226     t->pend = 0;
227   } else {
228     *t->prev = t->next;
229     if (t->next)
230       t->next->prev = t->prev;
231   }
232 }
233
234 /* --- @sel_addhook@ --- *
235  *
236  * Arguments:   @sel_state *s@ = pointer to state block
237  *              @sel_hook *h@ = pointer to hook block
238  *              @sel_hookfn before, after@ = hook functions
239  *              @void *p@ = pointer argument to pass to hook functions
240  *
241  * Returns:     ---
242  *
243  * Use:         Registers hook functions to be called on each select call.
244  */
245
246 void sel_addhook(sel_state *s, sel_hook *h,
247                  sel_hookfn before, sel_hookfn after,
248                  void *p)
249 {
250   h->before = before;
251   h->after = after;
252   h->p = p;
253   h->next = s->hooks;
254   h->prev = &s->hooks;
255   if (s->hooks)
256     s->hooks->prev = &h->next;
257   s->hooks = h;
258 }
259
260 /* --- @sel_rmhook@ --- *
261  *
262  * Arguments:   @sel_hook *h@ = pointer to hook block
263  *
264  * Returns:     ---
265  *
266  * Use:         Removes hook functions.
267  */
268
269 void sel_rmhook(sel_hook *h)
270 {
271   if (h->next)
272     h->next->prev = h->prev;
273   *h->prev = h->next;
274 }
275
276 /* --- @sel_fdmerge@ --- *
277  *
278  * Arguments:   @fd_set *dest@ = destination FD set
279  *              @fd_set *fd@ = pointer to set to merge
280  *              @int maxfd@ = highest numbered descriptor in @fd@ + 1
281  *
282  * Returns:     Actual highest numbered descriptor.
283  *
284  * Use:         Merges file descriptor sets, and returns an accurate @maxfd@
285  *              value.
286  */
287
288 int sel_fdmerge(fd_set *dest, fd_set *fd, int maxfd)
289 {
290   int i, m = -1;
291
292   for (i = 0; i < maxfd; i++) {
293     if (FD_ISSET(i, fd)) {
294       FD_SET(i, dest);
295       m = i;
296     }
297   }
298
299   return (m + 1);
300 }
301
302 /* --- @sel_select@ --- *
303  *
304  * Arguments:   @sel_state *s@ = pointer to state block
305  *
306  * Returns:     Zero if all OK, -1 on error.
307  *
308  * Use:         Does a @select@ call (or equivalent @poll@).
309  */
310
311 int sel_select(sel_state *s)
312 {
313   sel_args a;
314   int err;
315
316   /* --- Initialize the argument block --- */
317
318   {
319     int i;
320     a.maxfd = 0;
321     for (i = 0; i < SEL_MODES; i++) {
322       if (s->files[i] && s->files[i]->fd >= a.maxfd)
323         a.maxfd = s->files[i]->fd + 1;
324     }
325   }
326
327   memcpy(a.fd, s->fd, sizeof(a.fd));
328   if (s->timers || s->hooks)
329     gettimeofday(&a.now, 0);
330   if (!s->timers)
331     a.tvp = 0;
332   else {
333     if (TV_CMP(&s->timers->tv, >, &a.now))
334       TV_SUB(&a.tv, &s->timers->tv, &a.now);
335     else {
336       a.tv.tv_sec = 0;
337       a.tv.tv_usec = 0;
338     }
339     a.tvp = &a.tv;
340   }
341   s->args = &a;
342
343   /* --- Grind through the pre hooks --- */
344
345   {
346     sel_hook *h = s->hooks;
347     while (h) {
348       sel_hook *hh = h;
349       h = h->next;
350       if (hh->before)
351         hh->before(s, &a, hh->p);
352     }
353   }
354
355   /* --- Run the @select@ call --- */
356   
357   if ((err = select(a.maxfd,
358                     &a.fd[SEL_READ], &a.fd[SEL_WRITE], &a.fd[SEL_EXC],
359                     a.tvp)) < 0) {
360     s->args = 0;
361     return (err);
362   }
363
364   if (a.tvp)
365     gettimeofday(&a.now, 0);
366
367   /* --- Run through the hooks again --- */
368
369   {
370     sel_hook *h = s->hooks;
371     while (h) {
372       sel_hook *hh = h;
373       h = h->next;
374       if (hh->after)
375         hh->after(s, &a, hh->p);
376     }
377   }
378
379   /* --- Run through the timers --- */
380
381   if (s->timers) {
382     ptimer *pthead, *pt, **ptt = &pthead;
383     sel_timer *t;
384
385     for (t = s->timers; t && TV_CMP(&t->tv, <=, &a.now); t = t->next) {
386       pt = CREATE(ptimer);
387       pt->t = t;
388       t->pend = pt;
389       *ptt = pt;
390       ptt = &pt->next;
391     }
392     *ptt = 0;
393     if (t) {
394       *t->prev = 0;
395       t->prev = &s->timers;
396     }
397     s->timers = t;
398     while (pthead) {
399       pt = pthead;
400       pthead = pt->next;
401       t = pt->t;
402       if (t) {
403         t->pend = 0;
404         t->next = 0;
405         t->prev = &t->next;
406         t->func(&a.now, t->p);
407       }
408       DESTROY(pt);
409     }
410   }
411
412   /* --- And finally run through the files --- *
413    *
414    * Do reads first.  It's quite possible that a read might prompt a write,
415    * but the other way around is less likely.  Fortunately, the modes are
416    * in the right order for this.
417    */
418
419   {
420     int i;
421
422     for (i = 0; i < SEL_MODES; i++) {
423       pfile *pfhead, *pf, **pff = &pfhead;
424       sel_file *f;
425
426       for (f = s->files[i]; f; f = f->next) {
427         if (!FD_ISSET(f->fd, &a.fd[i]))
428           continue;
429         pf = CREATE(pfile);
430         pf->f = f;
431         f->pend = pf;
432         *pff = pf;
433         pff = &pf->next;
434       }
435       *pff = 0;
436       while (pfhead) {
437         pf = pfhead;
438         pfhead = pf->next;
439         f = pf->f;
440         if (f) {
441           f->pend = 0;
442           f->func(f->fd, i, f->p);
443         }
444         DESTROY(pf);
445       }
446     }
447   }
448
449   s->args = 0;
450   return (0);
451 }
452
453 /*----- That's all, folks -------------------------------------------------*/