chiark / gitweb /
Integrated `select' handling bits from the background resolver project.
[mLib] / sel.c
1 /* -*-c-*-
2  *
3  * $Id: sel.c,v 1.1 1999/05/14 21:01:14 mdw Exp $
4  *
5  * I/O multiplexing support
6  *
7  * (c) 1999 Mark Wooding
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.1  1999/05/14 21:01:14  mdw
34  * Integrated `select' handling bits from the background resolver project.
35  *
36  */
37
38 /*----- Header files ------------------------------------------------------*/
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #include <sys/types.h>
45 #include <sys/time.h>
46 #include <unistd.h>
47
48 #include "sel.h"
49 #include "tv.h"
50
51 /*----- Main code ---------------------------------------------------------*/
52
53 /* --- @sel_init@ --- *
54  *
55  * Arguments:   @sel_state *s@ = pointer to a state block to initialize
56  *
57  * Returns:     ---
58  *
59  * Use:         Initializes a select state block.
60  */
61
62 void sel_init(sel_state *s)
63 {
64   s->files = 0;
65   s->timers = 0;
66   FD_ZERO(&s->fd[SEL_READ]);
67   FD_ZERO(&s->fd[SEL_WRITE]);
68   FD_ZERO(&s->fd[SEL_EXC]);
69 }
70
71 /* --- @sel_initfile@ --- *
72  *
73  * Arguments:   @sel_state *s@ = select state to attach to
74  *              @sel_file *f@ = pointer to a file block to initialize
75  *              @int fd@ = the file descriptor to listen to
76  *              @unsigned mode@ = what to listen for
77  *              @void (*func)(int fd, unsigned mode, void *p)@ = handler
78  *              @void *p@ = argument to pass to handler
79  *
80  * Returns:     ---
81  *
82  * Use:         Initializes a file block ready for use.  The file block
83  *              isn't added to the list of things to do until a call to
84  *              @sel_addfile@.
85  */
86
87 void sel_initfile(sel_state *s, sel_file *f,
88                   int fd, unsigned mode,
89                   void (*func)(int /*fd*/, unsigned /*mode*/, void */*p*/),
90                   void *p)
91 {
92   f->s = s;
93   f->fd = fd;
94   f->mode = mode;
95   f->func = func;
96   f->p = p;
97 }
98
99 /* --- @sel_addfile@ --- *
100  *
101  * Arguments:   @sel_file *f@ = pointer to a file block
102  *
103  * Returns:     ---
104  *
105  * Use:         Adds a file block into the list of things to listen to.
106  */
107
108 void sel_addfile(sel_file *f)
109 {
110   sel_file **ff = &f->s->files;
111
112   /* --- This little dance looks like line-noise, but it does the job --- */
113
114   while (*ff && (*ff)->fd > f->fd)
115     ff = &(*ff)->next;
116   f->next = *ff;
117   f->prev = (sel_file *)ff;
118   if (*ff)
119     (*ff)->prev = f;
120   *ff = f;
121   FD_SET(f->fd, f->s->fd + f->mode);
122 }
123
124 /* --- @sel_rmfile@ --- *
125  *
126  * Arguments:   @sel_file *f@ = pointer to a file block
127  *
128  * Returns:     ---
129  *
130  * Use:         Removes a file block from the list of things to listen to.
131  */
132
133 void sel_rmfile(sel_file *f)
134 {
135   f->prev->next = f->next;
136   if (f->next)
137     f->next->prev = f->prev;
138   FD_CLR(f->fd, f->s->fd + f->mode);
139 }
140
141 /* --- @sel_addtimer@ --- *
142  *
143  * Arguments:   @sel_state *s@ = pointer to a state block
144  *              @sel_timer *t@ = pointer to a timer block
145  *              @struct timeval *tv@ = pointer to time to activate
146  *              @void (*func)(struct timeval *tv, void *p)@ = handler
147  *              @void *p@ = argument for handler function
148  *
149  * Returns:     ---
150  *
151  * Use:         Registers and sets up a timer.
152  */
153
154 void sel_addtimer(sel_state *s, sel_timer *t,
155                   struct timeval *tv,
156                   void (*func)(struct timeval */*tv*/, void */*p*/),
157                   void *p)
158 {
159   sel_timer **tt = &s->timers;
160
161   /* --- Set up the timer block --- */
162
163   t->tv = *tv;
164   t->func = func;
165   t->p = p;
166
167   /* --- More line noise --- */
168   
169   while (*tt && tv_cmp(&(*tt)->tv, tv) > 0)
170     tt = &(*tt)->next;
171   t->next = *tt;
172   t->prev = (sel_timer *)tt;
173   if (*tt)
174     (*tt)->prev = t;
175   *tt = t;
176 }
177
178 /* --- @sel_rmtimer@ --- *
179  *
180  * Arguments:   @sel_timer *t@ = pointer to timer block
181  *
182  * Returns:     ---
183  *
184  * Use:         Removes a timer from the list of timers.
185  */
186
187 void sel_rmtimer(sel_timer *t)
188 {
189   t->prev->next = t->next;
190   if (t->next)
191     t->next->prev = t->prev;
192 }
193
194 /* --- @sel_select@ --- *
195  *
196  * Arguments:   @sel_state *s@ = pointer to state block
197  *
198  * Returns:     Zero if all OK, -1 on error.
199  *
200  * Use:         Does a @select@ call (or equivalent @poll@).
201  */
202
203 int sel_select(sel_state *s)
204 {
205   fd_set fd[SEL_MODES];
206   struct timeval tv;
207   int err;
208
209   memcpy(fd, s->fd, sizeof(s->fd));
210   if (s->timers) {
211     struct timeval now;
212     gettimeofday(&now, 0);
213     tv_sub(&tv, &now, &s->timers->tv);
214     err = select(s->files ? s->files->fd + 1 : 0,
215                  fd + SEL_READ, fd + SEL_WRITE, fd + SEL_EXC,
216                  &tv);
217     gettimeofday(&tv, 0);
218   } else
219     err = select(s->files ? s->files->fd + 1 : 0,
220                  fd + SEL_READ, fd + SEL_WRITE, fd + SEL_EXC,
221                  0);
222
223   if (err < 0)
224     return (err);
225
226   {
227     sel_timer *t, *tt;
228     for (t = s->timers; t && tv_cmp(&t->tv, &tv) <= 0; t = tt) {
229       tt = t->next;
230       t->next = t->prev = t;
231       t->func(&tv, t->p);
232     }
233     s->timers = t;
234     if (t)
235       t->prev = (sel_timer *)&s->timers;
236   }
237
238   {
239     sel_file *f, *ff;
240     for (f = s->files; f; f = ff) {
241       ff = f->next;
242       if (FD_ISSET(f->fd, fd + f->mode))
243         f->func(f->fd, f->mode, f->p);
244     }
245   }
246
247   return (0);
248 }
249
250 /*----- That's all, folks -------------------------------------------------*/