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