Integrated `select' handling bits from the background resolver project.
[mLib] / sel.c
1 /* -*-c-*-
2 *
3 * $Id: sel.c,v 1.1 1999/05/14 21:01:14 mdw Exp $
4 *
5 * I/O multiplexing support
6 *
7 * (c) 1999 Mark Wooding
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 $
33 * Revision 1.1 1999/05/14 21:01:14 mdw
34 * Integrated `select' handling bits from the background resolver project.
35 *
36 */
37
38 /*----- Header files ------------------------------------------------------*/
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #include <sys/types.h>
45 #include <sys/time.h>
46 #include <unistd.h>
47
48 #include "sel.h"
49 #include "tv.h"
50
51 /*----- Main code ---------------------------------------------------------*/
52
53 /* --- @sel_init@ --- *
54 *
55 * Arguments: @sel_state *s@ = pointer to a state block to initialize
56 *
57 * Returns: ---
58 *
59 * Use: Initializes a select state block.
60 */
61
62 void sel_init(sel_state *s)
63 {
64 s->files = 0;
65 s->timers = 0;
66 FD_ZERO(&s->fd[SEL_READ]);
67 FD_ZERO(&s->fd[SEL_WRITE]);
68 FD_ZERO(&s->fd[SEL_EXC]);
69 }
70
71 /* --- @sel_initfile@ --- *
72 *
73 * Arguments: @sel_state *s@ = select state to attach to
74 * @sel_file *f@ = pointer to a file block to initialize
75 * @int fd@ = the file descriptor to listen to
76 * @unsigned mode@ = what to listen for
77 * @void (*func)(int fd, unsigned mode, void *p)@ = handler
78 * @void *p@ = argument to pass to handler
79 *
80 * Returns: ---
81 *
82 * Use: Initializes a file block ready for use. The file block
83 * isn't added to the list of things to do until a call to
84 * @sel_addfile@.
85 */
86
87 void sel_initfile(sel_state *s, sel_file *f,
88 int fd, unsigned mode,
89 void (*func)(int /*fd*/, unsigned /*mode*/, void */*p*/),
90 void *p)
91 {
92 f->s = s;
93 f->fd = fd;
94 f->mode = mode;
95 f->func = func;
96 f->p = p;
97 }
98
99 /* --- @sel_addfile@ --- *
100 *
101 * Arguments: @sel_file *f@ = pointer to a file block
102 *
103 * Returns: ---
104 *
105 * Use: Adds a file block into the list of things to listen to.
106 */
107
108 void sel_addfile(sel_file *f)
109 {
110 sel_file **ff = &f->s->files;
111
112 /* --- This little dance looks like line-noise, but it does the job --- */
113
114 while (*ff && (*ff)->fd > f->fd)
115 ff = &(*ff)->next;
116 f->next = *ff;
117 f->prev = (sel_file *)ff;
118 if (*ff)
119 (*ff)->prev = f;
120 *ff = f;
121 FD_SET(f->fd, f->s->fd + f->mode);
122 }
123
124 /* --- @sel_rmfile@ --- *
125 *
126 * Arguments: @sel_file *f@ = pointer to a file block
127 *
128 * Returns: ---
129 *
130 * Use: Removes a file block from the list of things to listen to.
131 */
132
133 void sel_rmfile(sel_file *f)
134 {
135 f->prev->next = f->next;
136 if (f->next)
137 f->next->prev = f->prev;
138 FD_CLR(f->fd, f->s->fd + f->mode);
139 }
140
141 /* --- @sel_addtimer@ --- *
142 *
143 * Arguments: @sel_state *s@ = pointer to a state block
144 * @sel_timer *t@ = pointer to a timer block
145 * @struct timeval *tv@ = pointer to time to activate
146 * @void (*func)(struct timeval *tv, void *p)@ = handler
147 * @void *p@ = argument for handler function
148 *
149 * Returns: ---
150 *
151 * Use: Registers and sets up a timer.
152 */
153
154 void sel_addtimer(sel_state *s, sel_timer *t,
155 struct timeval *tv,
156 void (*func)(struct timeval */*tv*/, void */*p*/),
157 void *p)
158 {
159 sel_timer **tt = &s->timers;
160
161 /* --- Set up the timer block --- */
162
163 t->tv = *tv;
164 t->func = func;
165 t->p = p;
166
167 /* --- More line noise --- */
168
169 while (*tt && tv_cmp(&(*tt)->tv, tv) > 0)
170 tt = &(*tt)->next;
171 t->next = *tt;
172 t->prev = (sel_timer *)tt;
173 if (*tt)
174 (*tt)->prev = t;
175 *tt = t;
176 }
177
178 /* --- @sel_rmtimer@ --- *
179 *
180 * Arguments: @sel_timer *t@ = pointer to timer block
181 *
182 * Returns: ---
183 *
184 * Use: Removes a timer from the list of timers.
185 */
186
187 void sel_rmtimer(sel_timer *t)
188 {
189 t->prev->next = t->next;
190 if (t->next)
191 t->next->prev = t->prev;
192 }
193
194 /* --- @sel_select@ --- *
195 *
196 * Arguments: @sel_state *s@ = pointer to state block
197 *
198 * Returns: Zero if all OK, -1 on error.
199 *
200 * Use: Does a @select@ call (or equivalent @poll@).
201 */
202
203 int sel_select(sel_state *s)
204 {
205 fd_set fd[SEL_MODES];
206 struct timeval tv;
207 int err;
208
209 memcpy(fd, s->fd, sizeof(s->fd));
210 if (s->timers) {
211 struct timeval now;
212 gettimeofday(&now, 0);
213 tv_sub(&tv, &now, &s->timers->tv);
214 err = select(s->files ? s->files->fd + 1 : 0,
215 fd + SEL_READ, fd + SEL_WRITE, fd + SEL_EXC,
216 &tv);
217 gettimeofday(&tv, 0);
218 } else
219 err = select(s->files ? s->files->fd + 1 : 0,
220 fd + SEL_READ, fd + SEL_WRITE, fd + SEL_EXC,
221 0);
222
223 if (err < 0)
224 return (err);
225
226 {
227 sel_timer *t, *tt;
228 for (t = s->timers; t && tv_cmp(&t->tv, &tv) <= 0; t = tt) {
229 tt = t->next;
230 t->next = t->prev = t;
231 t->func(&tv, t->p);
232 }
233 s->timers = t;
234 if (t)
235 t->prev = (sel_timer *)&s->timers;
236 }
237
238 {
239 sel_file *f, *ff;
240 for (f = s->files; f; f = ff) {
241 ff = f->next;
242 if (FD_ISSET(f->fd, fd + f->mode))
243 f->func(f->fd, f->mode, f->p);
244 }
245 }
246
247 return (0);
248 }
249
250 /*----- That's all, folks -------------------------------------------------*/