chiark / gitweb /
Formatting mistake fixed.
[mLib] / sel.c
CommitLineData
97f65b00 1/* -*-c-*-
2 *
932341d9 3 * $Id: sel.c,v 1.7 1999/12/11 11:12:17 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 $
932341d9 33 * Revision 1.7 1999/12/11 11:12:17 mdw
34 * Fix comment formatting error.
35 *
5e1ded7d 36 * Revision 1.6 1999/09/26 14:28:11 mdw
37 * (sel_select): Almost pointless efficiency tweak.
38 *
1101f87a 39 * Revision 1.5 1999/08/31 17:42:22 mdw
40 * New function `sel_force' to force a descriptor to be `selected'.
41 *
1226c514 42 * Revision 1.4 1999/08/19 18:30:26 mdw
43 * Implement hooks for foreign select-using systems (currently not well
44 * tested).
45 *
c05c6d83 46 * Revision 1.3 1999/05/21 22:13:59 mdw
47 * Use new `tv' macros. Fix ordering bug for timeout selectors.
48 *
a3ddb778 49 * Revision 1.2 1999/05/15 10:33:32 mdw
50 * Fix copyright notices.
51 *
97f65b00 52 * Revision 1.1 1999/05/14 21:01:14 mdw
53 * Integrated `select' handling bits from the background resolver project.
54 *
55 */
56
57/*----- Header files ------------------------------------------------------*/
58
59#include <stdio.h>
60#include <stdlib.h>
61#include <string.h>
62
63#include <sys/types.h>
64#include <sys/time.h>
65#include <unistd.h>
66
67#include "sel.h"
68#include "tv.h"
69
70/*----- Main code ---------------------------------------------------------*/
71
72/* --- @sel_init@ --- *
73 *
74 * Arguments: @sel_state *s@ = pointer to a state block to initialize
75 *
76 * Returns: ---
77 *
78 * Use: Initializes a select state block.
79 */
80
81void sel_init(sel_state *s)
82{
1101f87a 83 int i;
84
85 for (i = 0; i < SEL_MODES; i++) {
86 s->files[i] = 0;
87 FD_ZERO(&s->fd[i]);
88 }
97f65b00 89 s->timers = 0;
1226c514 90 s->hooks = 0;
1101f87a 91 s->args = 0;
97f65b00 92}
93
94/* --- @sel_initfile@ --- *
95 *
96 * Arguments: @sel_state *s@ = select state to attach to
97 * @sel_file *f@ = pointer to a file block to initialize
98 * @int fd@ = the file descriptor to listen to
99 * @unsigned mode@ = what to listen for
100 * @void (*func)(int fd, unsigned mode, void *p)@ = handler
101 * @void *p@ = argument to pass to handler
102 *
103 * Returns: ---
104 *
105 * Use: Initializes a file block ready for use. The file block
106 * isn't added to the list of things to do until a call to
107 * @sel_addfile@.
108 */
109
110void sel_initfile(sel_state *s, sel_file *f,
111 int fd, unsigned mode,
112 void (*func)(int /*fd*/, unsigned /*mode*/, void */*p*/),
113 void *p)
114{
115 f->s = s;
116 f->fd = fd;
117 f->mode = mode;
118 f->func = func;
119 f->p = p;
120}
121
122/* --- @sel_addfile@ --- *
123 *
124 * Arguments: @sel_file *f@ = pointer to a file block
125 *
126 * Returns: ---
127 *
128 * Use: Adds a file block into the list of things to listen to.
129 */
130
131void sel_addfile(sel_file *f)
132{
1101f87a 133 sel_file **ff = &f->s->files[f->mode];
97f65b00 134
135 /* --- This little dance looks like line-noise, but it does the job --- */
136
137 while (*ff && (*ff)->fd > f->fd)
138 ff = &(*ff)->next;
139 f->next = *ff;
140 f->prev = (sel_file *)ff;
141 if (*ff)
142 (*ff)->prev = f;
143 *ff = f;
144 FD_SET(f->fd, f->s->fd + f->mode);
145}
146
1101f87a 147/* --- @sel_force@ --- *
148 *
149 * Arguments: @sel_file *f@ = pointer to file selector
150 *
151 * Returns: ---
152 *
153 * Use: Forces a file selector to be considered ready. This is only
154 * useful during a call to @sel_select@. Of particular use is
155 * forcing a write selector when there's something interesting
156 * ready for it.
157 */
158
159void sel_force(sel_file *f)
160{
161 if (f->s->args)
162 FD_SET(f->fd, &f->s->args->fd[f->mode]);
163}
164
97f65b00 165/* --- @sel_rmfile@ --- *
166 *
167 * Arguments: @sel_file *f@ = pointer to a file block
168 *
169 * Returns: ---
170 *
171 * Use: Removes a file block from the list of things to listen to.
172 */
173
174void sel_rmfile(sel_file *f)
175{
176 f->prev->next = f->next;
177 if (f->next)
178 f->next->prev = f->prev;
179 FD_CLR(f->fd, f->s->fd + f->mode);
180}
181
182/* --- @sel_addtimer@ --- *
183 *
184 * Arguments: @sel_state *s@ = pointer to a state block
185 * @sel_timer *t@ = pointer to a timer block
186 * @struct timeval *tv@ = pointer to time to activate
187 * @void (*func)(struct timeval *tv, void *p)@ = handler
188 * @void *p@ = argument for handler function
189 *
190 * Returns: ---
191 *
192 * Use: Registers and sets up a timer.
193 */
194
195void sel_addtimer(sel_state *s, sel_timer *t,
196 struct timeval *tv,
197 void (*func)(struct timeval */*tv*/, void */*p*/),
198 void *p)
199{
200 sel_timer **tt = &s->timers;
201
202 /* --- Set up the timer block --- */
203
204 t->tv = *tv;
205 t->func = func;
206 t->p = p;
207
208 /* --- More line noise --- */
209
c05c6d83 210 while (*tt && TV_CMP(&(*tt)->tv, <, tv))
97f65b00 211 tt = &(*tt)->next;
212 t->next = *tt;
213 t->prev = (sel_timer *)tt;
214 if (*tt)
215 (*tt)->prev = t;
216 *tt = t;
217}
218
219/* --- @sel_rmtimer@ --- *
220 *
221 * Arguments: @sel_timer *t@ = pointer to timer block
222 *
223 * Returns: ---
224 *
225 * Use: Removes a timer from the list of timers.
226 */
227
228void sel_rmtimer(sel_timer *t)
229{
230 t->prev->next = t->next;
231 if (t->next)
232 t->next->prev = t->prev;
233}
234
1226c514 235/* --- @sel_addhook@ --- *
236 *
237 * Arguments: @sel_state *s@ = pointer to state block
238 * @sel_hook *h@ = pointer to hook block
239 * @sel_hookfn before, after@ = hook functions
240 * @void *p@ = pointer argument to pass to hook functions
241 *
242 * Returns: ---
243 *
244 * Use: Registers hook functions to be called on each select call.
245 */
246
247void sel_addhook(sel_state *s, sel_hook *h,
248 sel_hookfn before, sel_hookfn after,
249 void *p)
250{
251 h->before = before;
252 h->after = after;
253 h->p = p;
254 h->next = s->hooks;
255 h->prev = (sel_hook *)&s->hooks;
256 if (s->hooks)
257 s->hooks->prev = h;
258 s->hooks = h;
259}
260
261/* --- @sel_rmhook@ --- *
262 *
263 * Arguments: @sel_hook *h@ = pointer to hook block
264 *
265 * Returns: ---
266 *
267 * Use: Removes hook functions.
268 */
269
270void sel_rmhook(sel_hook *h)
271{
272 if (h->next)
273 h->next->prev = h->prev;
274 h->prev->next = h->next;
275}
276
277/* --- @sel_fdmerge@ --- *
932341d9 278 *
1226c514 279 * Arguments: @fd_set *dest@ = destination FD set
280 * @fd_set *fd@ = pointer to set to merge
281 * @int maxfd@ = highest numbered descriptor in @fd@ + 1
282 *
283 * Returns: Actual highest numbered descriptor.
284 *
285 * Use: Merges file descriptor sets, and returns an accurate @maxfd@
286 * value.
287 */
288
289int sel_fdmerge(fd_set *dest, fd_set *fd, int maxfd)
290{
291 int i, m = -1;
292
293 for (i = 0; i < maxfd; i++) {
294 if (FD_ISSET(i, fd)) {
295 FD_SET(i, dest);
296 m = i;
297 }
298 }
299
300 return (m + 1);
301}
302
97f65b00 303/* --- @sel_select@ --- *
304 *
305 * Arguments: @sel_state *s@ = pointer to state block
306 *
307 * Returns: Zero if all OK, -1 on error.
308 *
309 * Use: Does a @select@ call (or equivalent @poll@).
310 */
311
312int sel_select(sel_state *s)
313{
1226c514 314 sel_args a;
97f65b00 315 int err;
316
1226c514 317 /* --- Initialize the argument block --- */
318
1101f87a 319 {
320 int i;
321 a.maxfd = 0;
322 for (i = 0; i < SEL_MODES; i++) {
323 if (s->files[i] && s->files[i]->fd >= a.maxfd)
324 a.maxfd = s->files[i]->fd + 1;
325 }
326 }
327
1226c514 328 memcpy(a.fd, s->fd, sizeof(a.fd));
329 if (s->timers || s->hooks)
330 gettimeofday(&a.now, 0);
331 if (!s->timers)
332 a.tvp = 0;
333 else {
334 TV_SUB(&a.tv, &s->timers->tv, &a.now);
335 a.tvp = &a.tv;
336 }
1101f87a 337 s->args = &a;
1226c514 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],
1101f87a 355 a.tvp)) < 0) {
356 s->args = 0;
97f65b00 357 return (err);
1101f87a 358 }
97f65b00 359
1226c514 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
97f65b00 377 {
378 sel_timer *t, *tt;
1226c514 379 for (t = s->timers; t && TV_CMP(&t->tv, <=, &a.now); t = tt) {
97f65b00 380 tt = t->next;
381 t->next = t->prev = t;
1226c514 382 t->func(&a.now, t->p);
97f65b00 383 }
384 s->timers = t;
385 if (t)
386 t->prev = (sel_timer *)&s->timers;
387 }
388
1101f87a 389 /* --- And finally run through the files --- *
390 *
391 * Do reads first. It's quite possible that a read might prompt a write,
392 * but the other way around is less likely. Fortunately, the modes are
393 * in the right order for this.
394 */
1226c514 395
97f65b00 396 {
1101f87a 397 int i;
398
399 for (i = 0; i < SEL_MODES; i++) {
400 sel_file *f, *ff;
401 for (f = s->files[i]; f; f = ff) {
402 ff = f->next;
5e1ded7d 403 if (FD_ISSET(f->fd, a.fd + i))
404 f->func(f->fd, i, f->p);
1101f87a 405 }
97f65b00 406 }
407 }
408
1101f87a 409 s->args = 0;
97f65b00 410 return (0);
411}
412
413/*----- That's all, folks -------------------------------------------------*/