chiark / gitweb /
Add debugging mode which just uses the underlying arena.
[mLib] / sel.c
CommitLineData
97f65b00 1/* -*-c-*-
2 *
4790f418 3 * $Id: sel.c,v 1.11 2003/05/17 10:34:04 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 $
4790f418 33 * Revision 1.11 2003/05/17 10:34:04 mdw
34 * Tidying and bugfixing.
35 *
0a2a19b4 36 * Revision 1.10 2001/06/22 19:35:58 mdw
37 * Fix a large number of bugs.
38 *
c1f23e99 39 * Revision 1.9 2001/02/03 19:07:08 mdw
40 * Ensure that timers set to go off in the past don't case a problem.
41 *
38472b8e 42 * Revision 1.8 2000/03/23 20:42:08 mdw
43 * Rearrange timeout handling to avoid list corruptions.
44 *
932341d9 45 * Revision 1.7 1999/12/11 11:12:17 mdw
46 * Fix comment formatting error.
47 *
5e1ded7d 48 * Revision 1.6 1999/09/26 14:28:11 mdw
49 * (sel_select): Almost pointless efficiency tweak.
50 *
1101f87a 51 * Revision 1.5 1999/08/31 17:42:22 mdw
52 * New function `sel_force' to force a descriptor to be `selected'.
53 *
1226c514 54 * Revision 1.4 1999/08/19 18:30:26 mdw
55 * Implement hooks for foreign select-using systems (currently not well
56 * tested).
57 *
c05c6d83 58 * Revision 1.3 1999/05/21 22:13:59 mdw
59 * Use new `tv' macros. Fix ordering bug for timeout selectors.
60 *
a3ddb778 61 * Revision 1.2 1999/05/15 10:33:32 mdw
62 * Fix copyright notices.
63 *
97f65b00 64 * Revision 1.1 1999/05/14 21:01:14 mdw
65 * Integrated `select' handling bits from the background resolver project.
66 *
67 */
68
69/*----- Header files ------------------------------------------------------*/
70
0a2a19b4 71#include <assert.h>
97f65b00 72#include <stdio.h>
73#include <stdlib.h>
74#include <string.h>
75
76#include <sys/types.h>
77#include <sys/time.h>
78#include <unistd.h>
79
80#include "sel.h"
0a2a19b4 81#include "sub.h"
97f65b00 82#include "tv.h"
83
0a2a19b4 84/*----- Data structures ---------------------------------------------------*/
85
86typedef struct sel_pendfile {
87 struct sel_pendfile *next;
88 sel_file *f;
89} pfile;
90
91typedef struct sel_pendtimer {
92 struct sel_pendtimer *next;
93 sel_timer *t;
94} ptimer;
95
97f65b00 96/*----- Main code ---------------------------------------------------------*/
97
98/* --- @sel_init@ --- *
99 *
100 * Arguments: @sel_state *s@ = pointer to a state block to initialize
101 *
102 * Returns: ---
103 *
104 * Use: Initializes a select state block.
105 */
106
107void sel_init(sel_state *s)
108{
1101f87a 109 int i;
110
111 for (i = 0; i < SEL_MODES; i++) {
112 s->files[i] = 0;
113 FD_ZERO(&s->fd[i]);
114 }
97f65b00 115 s->timers = 0;
1226c514 116 s->hooks = 0;
1101f87a 117 s->args = 0;
97f65b00 118}
119
120/* --- @sel_initfile@ --- *
121 *
122 * Arguments: @sel_state *s@ = select state to attach to
123 * @sel_file *f@ = pointer to a file block to initialize
124 * @int fd@ = the file descriptor to listen to
125 * @unsigned mode@ = what to listen for
126 * @void (*func)(int fd, unsigned mode, void *p)@ = handler
127 * @void *p@ = argument to pass to handler
128 *
129 * Returns: ---
130 *
131 * Use: Initializes a file block ready for use. The file block
132 * isn't added to the list of things to do until a call to
133 * @sel_addfile@.
134 */
135
136void sel_initfile(sel_state *s, sel_file *f,
137 int fd, unsigned mode,
138 void (*func)(int /*fd*/, unsigned /*mode*/, void */*p*/),
139 void *p)
140{
141 f->s = s;
142 f->fd = fd;
143 f->mode = mode;
144 f->func = func;
145 f->p = p;
0a2a19b4 146 f->pend = 0;
97f65b00 147}
148
149/* --- @sel_addfile@ --- *
150 *
151 * Arguments: @sel_file *f@ = pointer to a file block
152 *
153 * Returns: ---
154 *
155 * Use: Adds a file block into the list of things to listen to.
156 */
157
158void sel_addfile(sel_file *f)
159{
1101f87a 160 sel_file **ff = &f->s->files[f->mode];
97f65b00 161
162 /* --- This little dance looks like line-noise, but it does the job --- */
163
164 while (*ff && (*ff)->fd > f->fd)
165 ff = &(*ff)->next;
166 f->next = *ff;
4790f418 167 f->prev = ff;
97f65b00 168 if (*ff)
4790f418 169 (*ff)->prev = &f->next;
97f65b00 170 *ff = f;
171 FD_SET(f->fd, f->s->fd + f->mode);
172}
173
1101f87a 174/* --- @sel_force@ --- *
175 *
176 * Arguments: @sel_file *f@ = pointer to file selector
177 *
178 * Returns: ---
179 *
180 * Use: Forces a file selector to be considered ready. This is only
181 * useful during a call to @sel_select@. Of particular use is
182 * forcing a write selector when there's something interesting
183 * ready for it.
184 */
185
186void sel_force(sel_file *f)
187{
188 if (f->s->args)
189 FD_SET(f->fd, &f->s->args->fd[f->mode]);
190}
191
97f65b00 192/* --- @sel_rmfile@ --- *
193 *
194 * Arguments: @sel_file *f@ = pointer to a file block
195 *
196 * Returns: ---
197 *
198 * Use: Removes a file block from the list of things to listen to.
199 */
200
201void sel_rmfile(sel_file *f)
202{
4790f418 203 *f->prev = f->next;
97f65b00 204 if (f->next)
205 f->next->prev = f->prev;
206 FD_CLR(f->fd, f->s->fd + f->mode);
0a2a19b4 207 if (f->pend) {
208 f->pend->f = 0;
209 f->pend = 0;
210 }
97f65b00 211}
212
213/* --- @sel_addtimer@ --- *
214 *
215 * Arguments: @sel_state *s@ = pointer to a state block
216 * @sel_timer *t@ = pointer to a timer block
217 * @struct timeval *tv@ = pointer to time to activate
218 * @void (*func)(struct timeval *tv, void *p)@ = handler
219 * @void *p@ = argument for handler function
220 *
221 * Returns: ---
222 *
223 * Use: Registers and sets up a timer.
224 */
225
226void sel_addtimer(sel_state *s, sel_timer *t,
227 struct timeval *tv,
228 void (*func)(struct timeval */*tv*/, void */*p*/),
229 void *p)
230{
231 sel_timer **tt = &s->timers;
0a2a19b4 232 { sel_timer *q; for (q = s->timers; q; q = q->next) assert(q != t); }
97f65b00 233
234 /* --- Set up the timer block --- */
235
236 t->tv = *tv;
237 t->func = func;
238 t->p = p;
0a2a19b4 239 t->pend = 0;
97f65b00 240
241 /* --- More line noise --- */
242
c05c6d83 243 while (*tt && TV_CMP(&(*tt)->tv, <, tv))
97f65b00 244 tt = &(*tt)->next;
245 t->next = *tt;
4790f418 246 t->prev = tt;
97f65b00 247 if (*tt)
4790f418 248 (*tt)->prev = &t->next;
97f65b00 249 *tt = t;
250}
251
252/* --- @sel_rmtimer@ --- *
253 *
254 * Arguments: @sel_timer *t@ = pointer to timer block
255 *
256 * Returns: ---
257 *
258 * Use: Removes a timer from the list of timers.
259 */
260
261void sel_rmtimer(sel_timer *t)
262{
0a2a19b4 263 if (t->pend) {
264 t->pend->t = 0;
265 t->pend = 0;
4790f418 266 } else {
267 *t->prev = t->next;
268 if (t->next)
269 t->next->prev = t->prev;
0a2a19b4 270 }
97f65b00 271}
272
1226c514 273/* --- @sel_addhook@ --- *
274 *
275 * Arguments: @sel_state *s@ = pointer to state block
276 * @sel_hook *h@ = pointer to hook block
277 * @sel_hookfn before, after@ = hook functions
278 * @void *p@ = pointer argument to pass to hook functions
279 *
280 * Returns: ---
281 *
282 * Use: Registers hook functions to be called on each select call.
283 */
284
285void sel_addhook(sel_state *s, sel_hook *h,
286 sel_hookfn before, sel_hookfn after,
287 void *p)
288{
289 h->before = before;
290 h->after = after;
291 h->p = p;
292 h->next = s->hooks;
4790f418 293 h->prev = &s->hooks;
1226c514 294 if (s->hooks)
4790f418 295 s->hooks->prev = &h->next;
1226c514 296 s->hooks = h;
297}
298
299/* --- @sel_rmhook@ --- *
300 *
301 * Arguments: @sel_hook *h@ = pointer to hook block
302 *
303 * Returns: ---
304 *
305 * Use: Removes hook functions.
306 */
307
308void sel_rmhook(sel_hook *h)
309{
310 if (h->next)
311 h->next->prev = h->prev;
4790f418 312 *h->prev = h->next;
1226c514 313}
314
315/* --- @sel_fdmerge@ --- *
932341d9 316 *
1226c514 317 * Arguments: @fd_set *dest@ = destination FD set
318 * @fd_set *fd@ = pointer to set to merge
319 * @int maxfd@ = highest numbered descriptor in @fd@ + 1
320 *
321 * Returns: Actual highest numbered descriptor.
322 *
323 * Use: Merges file descriptor sets, and returns an accurate @maxfd@
324 * value.
325 */
326
327int sel_fdmerge(fd_set *dest, fd_set *fd, int maxfd)
328{
329 int i, m = -1;
330
331 for (i = 0; i < maxfd; i++) {
332 if (FD_ISSET(i, fd)) {
333 FD_SET(i, dest);
334 m = i;
335 }
336 }
337
338 return (m + 1);
339}
340
97f65b00 341/* --- @sel_select@ --- *
342 *
343 * Arguments: @sel_state *s@ = pointer to state block
344 *
345 * Returns: Zero if all OK, -1 on error.
346 *
347 * Use: Does a @select@ call (or equivalent @poll@).
348 */
349
350int sel_select(sel_state *s)
351{
1226c514 352 sel_args a;
97f65b00 353 int err;
354
1226c514 355 /* --- Initialize the argument block --- */
356
1101f87a 357 {
358 int i;
359 a.maxfd = 0;
360 for (i = 0; i < SEL_MODES; i++) {
361 if (s->files[i] && s->files[i]->fd >= a.maxfd)
362 a.maxfd = s->files[i]->fd + 1;
363 }
364 }
365
1226c514 366 memcpy(a.fd, s->fd, sizeof(a.fd));
367 if (s->timers || s->hooks)
368 gettimeofday(&a.now, 0);
369 if (!s->timers)
370 a.tvp = 0;
371 else {
c1f23e99 372 if (TV_CMP(&s->timers->tv, >, &a.now))
373 TV_SUB(&a.tv, &s->timers->tv, &a.now);
374 else {
375 a.tv.tv_sec = 0;
376 a.tv.tv_usec = 0;
377 }
1226c514 378 a.tvp = &a.tv;
379 }
1101f87a 380 s->args = &a;
1226c514 381
382 /* --- Grind through the pre hooks --- */
383
384 {
385 sel_hook *h = s->hooks;
386 while (h) {
387 sel_hook *hh = h;
388 h = h->next;
389 if (hh->before)
390 hh->before(s, &a, hh->p);
391 }
392 }
393
394 /* --- Run the @select@ call --- */
395
396 if ((err = select(a.maxfd,
397 &a.fd[SEL_READ], &a.fd[SEL_WRITE], &a.fd[SEL_EXC],
1101f87a 398 a.tvp)) < 0) {
399 s->args = 0;
97f65b00 400 return (err);
1101f87a 401 }
97f65b00 402
1226c514 403 if (a.tvp)
404 gettimeofday(&a.now, 0);
405
406 /* --- Run through the hooks again --- */
407
408 {
409 sel_hook *h = s->hooks;
410 while (h) {
411 sel_hook *hh = h;
412 h = h->next;
413 if (hh->after)
414 hh->after(s, &a, hh->p);
415 }
416 }
417
418 /* --- Run through the timers --- */
419
0a2a19b4 420 if (s->timers) {
421 ptimer *pthead, *pt, **ptt = &pthead;
422 sel_timer *t;
423
424 for (t = s->timers; t && TV_CMP(&t->tv, <=, &a.now); t = t->next) {
425 pt = CREATE(ptimer);
426 pt->t = t;
427 t->pend = pt;
428 *ptt = pt;
429 ptt = &pt->next;
430 }
431 *ptt = 0;
38472b8e 432 if (t) {
4790f418 433 *t->prev = 0;
434 t->prev = &s->timers;
38472b8e 435 }
436 s->timers = t;
0a2a19b4 437 while (pthead) {
438 pt = pthead;
439 pthead = pt->next;
440 t = pt->t;
441 if (t) {
0a2a19b4 442 t->pend = 0;
4790f418 443 t->next = 0;
444 t->prev = &t->next;
445 t->func(&a.now, t->p);
0a2a19b4 446 }
447 DESTROY(pt);
97f65b00 448 }
97f65b00 449 }
450
1101f87a 451 /* --- And finally run through the files --- *
452 *
453 * Do reads first. It's quite possible that a read might prompt a write,
454 * but the other way around is less likely. Fortunately, the modes are
455 * in the right order for this.
456 */
1226c514 457
97f65b00 458 {
1101f87a 459 int i;
460
461 for (i = 0; i < SEL_MODES; i++) {
0a2a19b4 462 pfile *pfhead, *pf, **pff = &pfhead;
463 sel_file *f;
464
465 for (f = s->files[i]; f; f = f->next) {
466 if (!FD_ISSET(f->fd, &a.fd[i]))
467 continue;
468 pf = CREATE(pfile);
469 pf->f = f;
470 f->pend = pf;
471 *pff = pf;
472 pff = &pf->next;
473 }
474 *pff = 0;
475 while (pfhead) {
476 pf = pfhead;
477 pfhead = pf->next;
478 f = pf->f;
479 if (f) {
0a2a19b4 480 f->pend = 0;
4790f418 481 f->func(f->fd, i, f->p);
0a2a19b4 482 }
1101f87a 483 }
97f65b00 484 }
485 }
486
1101f87a 487 s->args = 0;
97f65b00 488 return (0);
489}
490
491/*----- That's all, folks -------------------------------------------------*/