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