chiark / gitweb /
Implement hooks for foreign select-using systems (currently not well
[mLib] / sel.c
CommitLineData
97f65b00 1/* -*-c-*-
2 *
1226c514 3 * $Id: sel.c,v 1.4 1999/08/19 18:30:26 mdw Exp $
97f65b00 4 *
5 * I/O multiplexing support
6 *
a3ddb778 7 * (c) 1999 Straylight/Edgeware
97f65b00 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 $
1226c514 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 *
c05c6d83 37 * Revision 1.3 1999/05/21 22:13:59 mdw
38 * Use new `tv' macros. Fix ordering bug for timeout selectors.
39 *
a3ddb778 40 * Revision 1.2 1999/05/15 10:33:32 mdw
41 * Fix copyright notices.
42 *
97f65b00 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
72void sel_init(sel_state *s)
73{
74 s->files = 0;
75 s->timers = 0;
1226c514 76 s->hooks = 0;
97f65b00 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
98void 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
119void 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
144void 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
165void 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
c05c6d83 180 while (*tt && TV_CMP(&(*tt)->tv, <, tv))
97f65b00 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
198void sel_rmtimer(sel_timer *t)
199{
200 t->prev->next = t->next;
201 if (t->next)
202 t->next->prev = t->prev;
203}
204
1226c514 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
217void 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
240void 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
259int 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
97f65b00 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
282int sel_select(sel_state *s)
283{
1226c514 284 sel_args a;
97f65b00 285 int err;
286
1226c514 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)
97f65b00 317 return (err);
318
1226c514 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
97f65b00 336 {
337 sel_timer *t, *tt;
1226c514 338 for (t = s->timers; t && TV_CMP(&t->tv, <=, &a.now); t = tt) {
97f65b00 339 tt = t->next;
340 t->next = t->prev = t;
1226c514 341 t->func(&a.now, t->p);
97f65b00 342 }
343 s->timers = t;
344 if (t)
345 t->prev = (sel_timer *)&s->timers;
346 }
347
1226c514 348 /* --- And finally run through the files --- */
349
97f65b00 350 {
351 sel_file *f, *ff;
352 for (f = s->files; f; f = ff) {
353 ff = f->next;
1226c514 354 if (FD_ISSET(f->fd, a.fd + f->mode))
97f65b00 355 f->func(f->fd, f->mode, f->p);
356 }
357 }
358
359 return (0);
360}
361
362/*----- That's all, folks -------------------------------------------------*/