chiark / gitweb /
Infrastructure: Split the files into subdirectories.
[mLib] / sel / sel.c
... / ...
CommitLineData
1/* -*-c-*-
2 *
3 * I/O multiplexing support
4 *
5 * (c) 1999 Straylight/Edgeware
6 */
7
8/*----- Licensing notice --------------------------------------------------*
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.
16 *
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.
21 *
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
28/*----- Header files ------------------------------------------------------*/
29
30#include <assert.h>
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"
40#include "sub.h"
41#include "tv.h"
42
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
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{
68 int i;
69
70 for (i = 0; i < SEL_MODES; i++) {
71 s->files[i] = 0;
72 FD_ZERO(&s->fd[i]);
73 }
74 s->timers = 0;
75 s->hooks = 0;
76 s->args = 0;
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;
105 f->pend = 0;
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{
119 sel_file **ff = &f->s->files[f->mode];
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;
126 f->prev = ff;
127 if (*ff)
128 (*ff)->prev = &f->next;
129 *ff = f;
130 FD_SET(f->fd, f->s->fd + f->mode);
131}
132
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
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{
162 *f->prev = f->next;
163 if (f->next)
164 f->next->prev = f->prev;
165 FD_CLR(f->fd, f->s->fd + f->mode);
166 if (f->pend) {
167 f->pend->f = 0;
168 f->pend = 0;
169 }
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;
191 { sel_timer *q; for (q = s->timers; q; q = q->next) assert(q != t); }
192
193 /* --- Set up the timer block --- */
194
195 t->tv = *tv;
196 t->func = func;
197 t->p = p;
198 t->pend = 0;
199
200 /* --- More line noise --- */
201
202 while (*tt && TV_CMP(&(*tt)->tv, <, tv))
203 tt = &(*tt)->next;
204 t->next = *tt;
205 t->prev = tt;
206 if (*tt)
207 (*tt)->prev = &t->next;
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{
222 if (t->pend) {
223 t->pend->t = 0;
224 t->pend = 0;
225 } else {
226 *t->prev = t->next;
227 if (t->next)
228 t->next->prev = t->prev;
229 }
230}
231
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;
252 h->prev = &s->hooks;
253 if (s->hooks)
254 s->hooks->prev = &h->next;
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;
271 *h->prev = h->next;
272}
273
274/* --- @sel_fdmerge@ --- *
275 *
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
279 *
280 * Returns: Actual highest numbered descriptor.
281 *
282 * Use: Merges file descriptor sets, and returns an accurate @maxfd@
283 * value.
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
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{
311 sel_args a;
312 int err;
313
314 /* --- Initialize the argument block --- */
315
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
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 {
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 }
337 a.tvp = &a.tv;
338 }
339 s->args = &a;
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 --- */
354
355 if ((err = select(a.maxfd,
356 &a.fd[SEL_READ], &a.fd[SEL_WRITE], &a.fd[SEL_EXC],
357 a.tvp)) < 0) {
358 s->args = 0;
359 return (err);
360 }
361
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
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;
391 if (t) {
392 *t->prev = 0;
393 t->prev = &s->timers;
394 }
395 s->timers = t;
396 while (pthead) {
397 pt = pthead;
398 pthead = pt->next;
399 t = pt->t;
400 if (t) {
401 t->pend = 0;
402 t->next = 0;
403 t->prev = &t->next;
404 t->func(&a.now, t->p);
405 }
406 DESTROY(pt);
407 }
408 }
409
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 */
416
417 {
418 int i;
419
420 for (i = 0; i < SEL_MODES; i++) {
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) {
439 f->pend = 0;
440 f->func(f->fd, i, f->p);
441 }
442 DESTROY(pf);
443 }
444 }
445 }
446
447 s->args = 0;
448 return (0);
449}
450
451/*----- That's all, folks -------------------------------------------------*/