OLD | NEW |
| (Empty) |
1 /* $OpenBSD: poll.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */ | |
2 | |
3 /* | |
4 * Copyright 2000-2003 Niels Provos <provos@citi.umich.edu> | |
5 * All rights reserved. | |
6 * | |
7 * Redistribution and use in source and binary forms, with or without | |
8 * modification, are permitted provided that the following conditions | |
9 * are met: | |
10 * 1. Redistributions of source code must retain the above copyright | |
11 * notice, this list of conditions and the following disclaimer. | |
12 * 2. Redistributions in binary form must reproduce the above copyright | |
13 * notice, this list of conditions and the following disclaimer in the | |
14 * documentation and/or other materials provided with the distribution. | |
15 * 3. The name of the author may not be used to endorse or promote products | |
16 * derived from this software without specific prior written permission. | |
17 * | |
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 */ | |
29 #ifdef HAVE_CONFIG_H | |
30 #include "config.h" | |
31 #endif | |
32 | |
33 #include <sys/types.h> | |
34 #ifdef HAVE_SYS_TIME_H | |
35 #include <sys/time.h> | |
36 #else | |
37 #include <sys/_libevent_time.h> | |
38 #endif | |
39 #include <sys/queue.h> | |
40 #include <poll.h> | |
41 #include <signal.h> | |
42 #include <stdio.h> | |
43 #include <stdlib.h> | |
44 #include <string.h> | |
45 #include <unistd.h> | |
46 #include <errno.h> | |
47 #ifdef CHECK_INVARIANTS | |
48 #include <assert.h> | |
49 #endif | |
50 | |
51 #include "event.h" | |
52 #include "event-internal.h" | |
53 #include "evsignal.h" | |
54 #include "log.h" | |
55 | |
56 struct pollop { | |
57 int event_count; /* Highest number alloc */ | |
58 int nfds; /* Size of event_* */ | |
59 int fd_count; /* Size of idxplus1_by_fd */ | |
60 struct pollfd *event_set; | |
61 struct event **event_r_back; | |
62 struct event **event_w_back; | |
63 int *idxplus1_by_fd; /* Index into event_set by fd; we add 1 so | |
64 * that 0 (which is easy to memset) can mean | |
65 * "no entry." */ | |
66 }; | |
67 | |
68 static void *poll_init (struct event_base *); | |
69 static int poll_add (void *, struct event *); | |
70 static int poll_del (void *, struct event *); | |
71 static int poll_dispatch (struct event_base *, void *, struct timeval *); | |
72 static void poll_dealloc (struct event_base *, void *); | |
73 | |
74 const struct eventop pollops = { | |
75 "poll", | |
76 poll_init, | |
77 poll_add, | |
78 poll_del, | |
79 poll_dispatch, | |
80 poll_dealloc, | |
81 0 | |
82 }; | |
83 | |
84 static void * | |
85 poll_init(struct event_base *base) | |
86 { | |
87 struct pollop *pollop; | |
88 | |
89 /* Disable poll when this environment variable is set */ | |
90 if (evutil_getenv("EVENT_NOPOLL")) | |
91 return (NULL); | |
92 | |
93 if (!(pollop = calloc(1, sizeof(struct pollop)))) | |
94 return (NULL); | |
95 | |
96 evsignal_init(base); | |
97 | |
98 return (pollop); | |
99 } | |
100 | |
101 #ifdef CHECK_INVARIANTS | |
102 static void | |
103 poll_check_ok(struct pollop *pop) | |
104 { | |
105 int i, idx; | |
106 struct event *ev; | |
107 | |
108 for (i = 0; i < pop->fd_count; ++i) { | |
109 idx = pop->idxplus1_by_fd[i]-1; | |
110 if (idx < 0) | |
111 continue; | |
112 assert(pop->event_set[idx].fd == i); | |
113 if (pop->event_set[idx].events & POLLIN) { | |
114 ev = pop->event_r_back[idx]; | |
115 assert(ev); | |
116 assert(ev->ev_events & EV_READ); | |
117 assert(ev->ev_fd == i); | |
118 } | |
119 if (pop->event_set[idx].events & POLLOUT) { | |
120 ev = pop->event_w_back[idx]; | |
121 assert(ev); | |
122 assert(ev->ev_events & EV_WRITE); | |
123 assert(ev->ev_fd == i); | |
124 } | |
125 } | |
126 for (i = 0; i < pop->nfds; ++i) { | |
127 struct pollfd *pfd = &pop->event_set[i]; | |
128 assert(pop->idxplus1_by_fd[pfd->fd] == i+1); | |
129 } | |
130 } | |
131 #else | |
132 #define poll_check_ok(pop) | |
133 #endif | |
134 | |
135 static int | |
136 poll_dispatch(struct event_base *base, void *arg, struct timeval *tv) | |
137 { | |
138 int res, i, j, msec = -1, nfds; | |
139 struct pollop *pop = arg; | |
140 | |
141 poll_check_ok(pop); | |
142 | |
143 if (tv != NULL) | |
144 msec = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000; | |
145 | |
146 nfds = pop->nfds; | |
147 res = poll(pop->event_set, nfds, msec); | |
148 | |
149 if (res == -1) { | |
150 if (errno != EINTR) { | |
151 event_warn("poll"); | |
152 return (-1); | |
153 } | |
154 | |
155 evsignal_process(base); | |
156 return (0); | |
157 } else if (base->sig.evsignal_caught) { | |
158 evsignal_process(base); | |
159 } | |
160 | |
161 event_debug(("%s: poll reports %d", __func__, res)); | |
162 | |
163 if (res == 0 || nfds == 0) | |
164 return (0); | |
165 | |
166 i = random() % nfds; | |
167 for (j = 0; j < nfds; j++) { | |
168 struct event *r_ev = NULL, *w_ev = NULL; | |
169 int what; | |
170 if (++i == nfds) | |
171 i = 0; | |
172 what = pop->event_set[i].revents; | |
173 | |
174 if (!what) | |
175 continue; | |
176 | |
177 res = 0; | |
178 | |
179 /* If the file gets closed notify */ | |
180 if (what & (POLLHUP|POLLERR)) | |
181 what |= POLLIN|POLLOUT; | |
182 if (what & POLLIN) { | |
183 res |= EV_READ; | |
184 r_ev = pop->event_r_back[i]; | |
185 } | |
186 if (what & POLLOUT) { | |
187 res |= EV_WRITE; | |
188 w_ev = pop->event_w_back[i]; | |
189 } | |
190 if (res == 0) | |
191 continue; | |
192 | |
193 if (r_ev && (res & r_ev->ev_events)) { | |
194 event_active(r_ev, res & r_ev->ev_events, 1); | |
195 } | |
196 if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) { | |
197 event_active(w_ev, res & w_ev->ev_events, 1); | |
198 } | |
199 } | |
200 | |
201 return (0); | |
202 } | |
203 | |
204 static int | |
205 poll_add(void *arg, struct event *ev) | |
206 { | |
207 struct pollop *pop = arg; | |
208 struct pollfd *pfd = NULL; | |
209 int i; | |
210 | |
211 if (ev->ev_events & EV_SIGNAL) | |
212 return (evsignal_add(ev)); | |
213 if (!(ev->ev_events & (EV_READ|EV_WRITE))) | |
214 return (0); | |
215 | |
216 poll_check_ok(pop); | |
217 if (pop->nfds + 1 >= pop->event_count) { | |
218 struct pollfd *tmp_event_set; | |
219 struct event **tmp_event_r_back; | |
220 struct event **tmp_event_w_back; | |
221 int tmp_event_count; | |
222 | |
223 if (pop->event_count < 32) | |
224 tmp_event_count = 32; | |
225 else | |
226 tmp_event_count = pop->event_count * 2; | |
227 | |
228 /* We need more file descriptors */ | |
229 tmp_event_set = realloc(pop->event_set, | |
230 tmp_event_count * sizeof(struct pollfd)); | |
231 if (tmp_event_set == NULL) { | |
232 event_warn("realloc"); | |
233 return (-1); | |
234 } | |
235 pop->event_set = tmp_event_set; | |
236 | |
237 tmp_event_r_back = realloc(pop->event_r_back, | |
238 tmp_event_count * sizeof(struct event *)); | |
239 if (tmp_event_r_back == NULL) { | |
240 /* event_set overallocated; that's okay. */ | |
241 event_warn("realloc"); | |
242 return (-1); | |
243 } | |
244 pop->event_r_back = tmp_event_r_back; | |
245 | |
246 tmp_event_w_back = realloc(pop->event_w_back, | |
247 tmp_event_count * sizeof(struct event *)); | |
248 if (tmp_event_w_back == NULL) { | |
249 /* event_set and event_r_back overallocated; that's | |
250 * okay. */ | |
251 event_warn("realloc"); | |
252 return (-1); | |
253 } | |
254 pop->event_w_back = tmp_event_w_back; | |
255 | |
256 pop->event_count = tmp_event_count; | |
257 } | |
258 if (ev->ev_fd >= pop->fd_count) { | |
259 int *tmp_idxplus1_by_fd; | |
260 int new_count; | |
261 if (pop->fd_count < 32) | |
262 new_count = 32; | |
263 else | |
264 new_count = pop->fd_count * 2; | |
265 while (new_count <= ev->ev_fd) | |
266 new_count *= 2; | |
267 tmp_idxplus1_by_fd = | |
268 realloc(pop->idxplus1_by_fd, new_count * sizeof(int)); | |
269 if (tmp_idxplus1_by_fd == NULL) { | |
270 event_warn("realloc"); | |
271 return (-1); | |
272 } | |
273 pop->idxplus1_by_fd = tmp_idxplus1_by_fd; | |
274 memset(pop->idxplus1_by_fd + pop->fd_count, | |
275 0, sizeof(int)*(new_count - pop->fd_count)); | |
276 pop->fd_count = new_count; | |
277 } | |
278 | |
279 i = pop->idxplus1_by_fd[ev->ev_fd] - 1; | |
280 if (i >= 0) { | |
281 pfd = &pop->event_set[i]; | |
282 } else { | |
283 i = pop->nfds++; | |
284 pfd = &pop->event_set[i]; | |
285 pfd->events = 0; | |
286 pfd->fd = ev->ev_fd; | |
287 pop->event_w_back[i] = pop->event_r_back[i] = NULL; | |
288 pop->idxplus1_by_fd[ev->ev_fd] = i + 1; | |
289 } | |
290 | |
291 pfd->revents = 0; | |
292 if (ev->ev_events & EV_WRITE) { | |
293 pfd->events |= POLLOUT; | |
294 pop->event_w_back[i] = ev; | |
295 } | |
296 if (ev->ev_events & EV_READ) { | |
297 pfd->events |= POLLIN; | |
298 pop->event_r_back[i] = ev; | |
299 } | |
300 poll_check_ok(pop); | |
301 | |
302 return (0); | |
303 } | |
304 | |
305 /* | |
306 * Nothing to be done here. | |
307 */ | |
308 | |
309 static int | |
310 poll_del(void *arg, struct event *ev) | |
311 { | |
312 struct pollop *pop = arg; | |
313 struct pollfd *pfd = NULL; | |
314 int i; | |
315 | |
316 if (ev->ev_events & EV_SIGNAL) | |
317 return (evsignal_del(ev)); | |
318 | |
319 if (!(ev->ev_events & (EV_READ|EV_WRITE))) | |
320 return (0); | |
321 | |
322 poll_check_ok(pop); | |
323 i = pop->idxplus1_by_fd[ev->ev_fd] - 1; | |
324 if (i < 0) | |
325 return (-1); | |
326 | |
327 /* Do we still want to read or write? */ | |
328 pfd = &pop->event_set[i]; | |
329 if (ev->ev_events & EV_READ) { | |
330 pfd->events &= ~POLLIN; | |
331 pop->event_r_back[i] = NULL; | |
332 } | |
333 if (ev->ev_events & EV_WRITE) { | |
334 pfd->events &= ~POLLOUT; | |
335 pop->event_w_back[i] = NULL; | |
336 } | |
337 poll_check_ok(pop); | |
338 if (pfd->events) | |
339 /* Another event cares about that fd. */ | |
340 return (0); | |
341 | |
342 /* Okay, so we aren't interested in that fd anymore. */ | |
343 pop->idxplus1_by_fd[ev->ev_fd] = 0; | |
344 | |
345 --pop->nfds; | |
346 if (i != pop->nfds) { | |
347 /* | |
348 * Shift the last pollfd down into the now-unoccupied | |
349 * position. | |
350 */ | |
351 memcpy(&pop->event_set[i], &pop->event_set[pop->nfds], | |
352 sizeof(struct pollfd)); | |
353 pop->event_r_back[i] = pop->event_r_back[pop->nfds]; | |
354 pop->event_w_back[i] = pop->event_w_back[pop->nfds]; | |
355 pop->idxplus1_by_fd[pop->event_set[i].fd] = i + 1; | |
356 } | |
357 | |
358 poll_check_ok(pop); | |
359 return (0); | |
360 } | |
361 | |
362 static void | |
363 poll_dealloc(struct event_base *base, void *arg) | |
364 { | |
365 struct pollop *pop = arg; | |
366 | |
367 evsignal_dealloc(base); | |
368 if (pop->event_set) | |
369 free(pop->event_set); | |
370 if (pop->event_r_back) | |
371 free(pop->event_r_back); | |
372 if (pop->event_w_back) | |
373 free(pop->event_w_back); | |
374 if (pop->idxplus1_by_fd) | |
375 free(pop->idxplus1_by_fd); | |
376 | |
377 memset(pop, 0, sizeof(struct pollop)); | |
378 free(pop); | |
379 } | |
OLD | NEW |