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