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