chiark / gitweb /
Infrastructure: Split the files into subdirectories.
[mLib] / sel / sel.c
1 /* -*-c-*-
2  *
3  * I/O multiplexing support
4  *
5  * (c) 1999 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of the mLib utilities library.
11  *
12  * mLib is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU Library General Public License as
14  * published by the Free Software Foundation; either version 2 of the
15  * License, or (at your option) any later version.
16  *
17  * mLib is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public
23  * License along with mLib; if not, write to the Free
24  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25  * MA 02111-1307, USA.
26  */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #include <assert.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include <sys/types.h>
36 #include <sys/time.h>
37 #include <unistd.h>
38
39 #include "sel.h"
40 #include "sub.h"
41 #include "tv.h"
42
43 /*----- Data structures ---------------------------------------------------*/
44
45 typedef struct sel_pendfile {
46   struct sel_pendfile *next;
47   sel_file *f;
48 } pfile;
49
50 typedef struct sel_pendtimer {
51   struct sel_pendtimer *next;
52   sel_timer *t;
53 } ptimer;
54
55 /*----- Main code ---------------------------------------------------------*/
56
57 /* --- @sel_init@ --- *
58  *
59  * Arguments:   @sel_state *s@ = pointer to a state block to initialize
60  *
61  * Returns:     ---
62  *
63  * Use:         Initializes a select state block.
64  */
65
66 void sel_init(sel_state *s)
67 {
68   int i;
69
70   for (i = 0; i < SEL_MODES; i++) {
71     s->files[i] = 0;
72     FD_ZERO(&s->fd[i]);
73   }
74   s->timers = 0;
75   s->hooks = 0;
76   s->args = 0;
77 }
78
79 /* --- @sel_initfile@ --- *
80  *
81  * Arguments:   @sel_state *s@ = select state to attach to
82  *              @sel_file *f@ = pointer to a file block to initialize
83  *              @int fd@ = the file descriptor to listen to
84  *              @unsigned mode@ = what to listen for
85  *              @void (*func)(int fd, unsigned mode, void *p)@ = handler
86  *              @void *p@ = argument to pass to handler
87  *
88  * Returns:     ---
89  *
90  * Use:         Initializes a file block ready for use.  The file block
91  *              isn't added to the list of things to do until a call to
92  *              @sel_addfile@.
93  */
94
95 void sel_initfile(sel_state *s, sel_file *f,
96                   int fd, unsigned mode,
97                   void (*func)(int /*fd*/, unsigned /*mode*/, void */*p*/),
98                   void *p)
99 {
100   f->s = s;
101   f->fd = fd;
102   f->mode = mode;
103   f->func = func;
104   f->p = p;
105   f->pend = 0;
106 }
107
108 /* --- @sel_addfile@ --- *
109  *
110  * Arguments:   @sel_file *f@ = pointer to a file block
111  *
112  * Returns:     ---
113  *
114  * Use:         Adds a file block into the list of things to listen to.
115  */
116
117 void sel_addfile(sel_file *f)
118 {
119   sel_file **ff = &f->s->files[f->mode];
120
121   /* --- This little dance looks like line-noise, but it does the job --- */
122
123   while (*ff && (*ff)->fd > f->fd)
124     ff = &(*ff)->next;
125   f->next = *ff;
126   f->prev = ff;
127   if (*ff)
128     (*ff)->prev = &f->next;
129   *ff = f;
130   FD_SET(f->fd, f->s->fd + f->mode);
131 }
132
133 /* --- @sel_force@ --- *
134  *
135  * Arguments:   @sel_file *f@ = pointer to file selector
136  *
137  * Returns:     ---
138  *
139  * Use:         Forces a file selector to be considered ready.  This is only
140  *              useful during a call to @sel_select@.  Of particular use is
141  *              forcing a write selector when there's something interesting
142  *              ready for it.
143  */
144
145 void sel_force(sel_file *f)
146 {
147   if (f->s->args)
148     FD_SET(f->fd, &f->s->args->fd[f->mode]);
149 }
150
151 /* --- @sel_rmfile@ --- *
152  *
153  * Arguments:   @sel_file *f@ = pointer to a file block
154  *
155  * Returns:     ---
156  *
157  * Use:         Removes a file block from the list of things to listen to.
158  */
159
160 void sel_rmfile(sel_file *f)
161 {
162   *f->prev = f->next;
163   if (f->next)
164     f->next->prev = f->prev;
165   FD_CLR(f->fd, f->s->fd + f->mode);
166   if (f->pend) {
167     f->pend->f = 0;
168     f->pend = 0;
169   }
170 }
171
172 /* --- @sel_addtimer@ --- *
173  *
174  * Arguments:   @sel_state *s@ = pointer to a state block
175  *              @sel_timer *t@ = pointer to a timer block
176  *              @struct timeval *tv@ = pointer to time to activate
177  *              @void (*func)(struct timeval *tv, void *p)@ = handler
178  *              @void *p@ = argument for handler function
179  *
180  * Returns:     ---
181  *
182  * Use:         Registers and sets up a timer.
183  */
184
185 void sel_addtimer(sel_state *s, sel_timer *t,
186                   struct timeval *tv,
187                   void (*func)(struct timeval */*tv*/, void */*p*/),
188                   void *p)
189 {
190   sel_timer **tt = &s->timers;
191   { sel_timer *q; for (q = s->timers; q; q = q->next) assert(q != t); }
192
193   /* --- Set up the timer block --- */
194
195   t->tv = *tv;
196   t->func = func;
197   t->p = p;
198   t->pend = 0;
199
200   /* --- More line noise --- */
201
202   while (*tt && TV_CMP(&(*tt)->tv, <, tv))
203     tt = &(*tt)->next;
204   t->next = *tt;
205   t->prev = tt;
206   if (*tt)
207     (*tt)->prev = &t->next;
208   *tt = t;
209 }
210
211 /* --- @sel_rmtimer@ --- *
212  *
213  * Arguments:   @sel_timer *t@ = pointer to timer block
214  *
215  * Returns:     ---
216  *
217  * Use:         Removes a timer from the list of timers.
218  */
219
220 void sel_rmtimer(sel_timer *t)
221 {
222   if (t->pend) {
223     t->pend->t = 0;
224     t->pend = 0;
225   } else {
226     *t->prev = t->next;
227     if (t->next)
228       t->next->prev = t->prev;
229   }
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 = &s->hooks;
253   if (s->hooks)
254     s->hooks->prev = &h->next;
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 = 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     if (TV_CMP(&s->timers->tv, >, &a.now))
332       TV_SUB(&a.tv, &s->timers->tv, &a.now);
333     else {
334       a.tv.tv_sec = 0;
335       a.tv.tv_usec = 0;
336     }
337     a.tvp = &a.tv;
338   }
339   s->args = &a;
340
341   /* --- Grind through the pre hooks --- */
342
343   {
344     sel_hook *h = s->hooks;
345     while (h) {
346       sel_hook *hh = h;
347       h = h->next;
348       if (hh->before)
349         hh->before(s, &a, hh->p);
350     }
351   }
352
353   /* --- Run the @select@ call --- */
354
355   if ((err = select(a.maxfd,
356                     &a.fd[SEL_READ], &a.fd[SEL_WRITE], &a.fd[SEL_EXC],
357                     a.tvp)) < 0) {
358     s->args = 0;
359     return (err);
360   }
361
362   if (a.tvp)
363     gettimeofday(&a.now, 0);
364
365   /* --- Run through the hooks again --- */
366
367   {
368     sel_hook *h = s->hooks;
369     while (h) {
370       sel_hook *hh = h;
371       h = h->next;
372       if (hh->after)
373         hh->after(s, &a, hh->p);
374     }
375   }
376
377   /* --- Run through the timers --- */
378
379   if (s->timers) {
380     ptimer *pthead, *pt, **ptt = &pthead;
381     sel_timer *t;
382
383     for (t = s->timers; t && TV_CMP(&t->tv, <=, &a.now); t = t->next) {
384       pt = CREATE(ptimer);
385       pt->t = t;
386       t->pend = pt;
387       *ptt = pt;
388       ptt = &pt->next;
389     }
390     *ptt = 0;
391     if (t) {
392       *t->prev = 0;
393       t->prev = &s->timers;
394     }
395     s->timers = t;
396     while (pthead) {
397       pt = pthead;
398       pthead = pt->next;
399       t = pt->t;
400       if (t) {
401         t->pend = 0;
402         t->next = 0;
403         t->prev = &t->next;
404         t->func(&a.now, t->p);
405       }
406       DESTROY(pt);
407     }
408   }
409
410   /* --- And finally run through the files --- *
411    *
412    * Do reads first.  It's quite possible that a read might prompt a write,
413    * but the other way around is less likely.  Fortunately, the modes are
414    * in the right order for this.
415    */
416
417   {
418     int i;
419
420     for (i = 0; i < SEL_MODES; i++) {
421       pfile *pfhead, *pf, **pff = &pfhead;
422       sel_file *f;
423
424       for (f = s->files[i]; f; f = f->next) {
425         if (!FD_ISSET(f->fd, &a.fd[i]))
426           continue;
427         pf = CREATE(pfile);
428         pf->f = f;
429         f->pend = pf;
430         *pff = pf;
431         pff = &pf->next;
432       }
433       *pff = 0;
434       while (pfhead) {
435         pf = pfhead;
436         pfhead = pf->next;
437         f = pf->f;
438         if (f) {
439           f->pend = 0;
440           f->func(f->fd, i, f->p);
441         }
442         DESTROY(pf);
443       }
444     }
445   }
446
447   s->args = 0;
448   return (0);
449 }
450
451 /*----- That's all, folks -------------------------------------------------*/