Line data Source code
1 : /* $OpenBSD: poll.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
2 :
3 : /*
4 : * Copyright 2000-2007 Niels Provos <provos@citi.umich.edu>
5 : * Copyright 2007-2012 Niels Provos and Nick Mathewson
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 : #include "event2/event-config.h"
30 : #include "evconfig-private.h"
31 :
32 : #ifdef EVENT__HAVE_POLL
33 :
34 : #include <sys/types.h>
35 : #ifdef EVENT__HAVE_SYS_TIME_H
36 : #include <sys/time.h>
37 : #endif
38 : #include <sys/queue.h>
39 : #include <poll.h>
40 : #include <signal.h>
41 : #include <limits.h>
42 : #include <stdio.h>
43 : #include <stdlib.h>
44 : #include <string.h>
45 : #include <unistd.h>
46 : #include <errno.h>
47 :
48 : #include "event-internal.h"
49 : #include "evsignal-internal.h"
50 : #include "log-internal.h"
51 : #include "evmap-internal.h"
52 : #include "event2/thread.h"
53 : #include "evthread-internal.h"
54 : #include "time-internal.h"
55 :
56 : struct pollidx {
57 : int idxplus1;
58 : };
59 :
60 : struct pollop {
61 : int event_count; /* Highest number alloc */
62 : int nfds; /* Highest number used */
63 : int realloc_copy; /* True iff we must realloc
64 : * event_set_copy */
65 : struct pollfd *event_set;
66 : struct pollfd *event_set_copy;
67 : };
68 :
69 : static void *poll_init(struct event_base *);
70 : static int poll_add(struct event_base *, int, short old, short events, void *idx);
71 : static int poll_del(struct event_base *, int, short old, short events, void *idx);
72 : static int poll_dispatch(struct event_base *, struct timeval *);
73 : static void poll_dealloc(struct event_base *);
74 :
75 : const struct eventop pollops = {
76 : "poll",
77 : poll_init,
78 : poll_add,
79 : poll_del,
80 : poll_dispatch,
81 : poll_dealloc,
82 : 0, /* doesn't need_reinit */
83 : EV_FEATURE_FDS,
84 : sizeof(struct pollidx),
85 : };
86 :
87 : static void *
88 0 : poll_init(struct event_base *base)
89 : {
90 : struct pollop *pollop;
91 :
92 0 : if (!(pollop = mm_calloc(1, sizeof(struct pollop))))
93 0 : return (NULL);
94 :
95 0 : evsig_init_(base);
96 :
97 0 : evutil_weakrand_seed_(&base->weakrand_seed, 0);
98 :
99 0 : return (pollop);
100 : }
101 :
102 : #ifdef CHECK_INVARIANTS
103 : static void
104 : poll_check_ok(struct pollop *pop)
105 : {
106 : int i, idx;
107 : struct event *ev;
108 :
109 : for (i = 0; i < pop->fd_count; ++i) {
110 : idx = pop->idxplus1_by_fd[i]-1;
111 : if (idx < 0)
112 : continue;
113 : EVUTIL_ASSERT(pop->event_set[idx].fd == i);
114 : }
115 : for (i = 0; i < pop->nfds; ++i) {
116 : struct pollfd *pfd = &pop->event_set[i];
117 : EVUTIL_ASSERT(pop->idxplus1_by_fd[pfd->fd] == i+1);
118 : }
119 : }
120 : #else
121 : #define poll_check_ok(pop)
122 : #endif
123 :
124 : static int
125 0 : poll_dispatch(struct event_base *base, struct timeval *tv)
126 : {
127 : int res, i, j, nfds;
128 0 : long msec = -1;
129 0 : struct pollop *pop = base->evbase;
130 : struct pollfd *event_set;
131 :
132 : poll_check_ok(pop);
133 :
134 0 : nfds = pop->nfds;
135 :
136 : #ifndef EVENT__DISABLE_THREAD_SUPPORT
137 0 : if (base->th_base_lock) {
138 : /* If we're using this backend in a multithreaded setting,
139 : * then we need to work on a copy of event_set, so that we can
140 : * let other threads modify the main event_set while we're
141 : * polling. If we're not multithreaded, then we'll skip the
142 : * copy step here to save memory and time. */
143 0 : if (pop->realloc_copy) {
144 0 : struct pollfd *tmp = mm_realloc(pop->event_set_copy,
145 : pop->event_count * sizeof(struct pollfd));
146 0 : if (tmp == NULL) {
147 0 : event_warn("realloc");
148 0 : return -1;
149 : }
150 0 : pop->event_set_copy = tmp;
151 0 : pop->realloc_copy = 0;
152 : }
153 0 : memcpy(pop->event_set_copy, pop->event_set,
154 : sizeof(struct pollfd)*nfds);
155 0 : event_set = pop->event_set_copy;
156 : } else {
157 0 : event_set = pop->event_set;
158 : }
159 : #else
160 : event_set = pop->event_set;
161 : #endif
162 :
163 0 : if (tv != NULL) {
164 0 : msec = evutil_tv_to_msec_(tv);
165 0 : if (msec < 0 || msec > INT_MAX)
166 0 : msec = INT_MAX;
167 : }
168 :
169 0 : EVBASE_RELEASE_LOCK(base, th_base_lock);
170 :
171 0 : res = poll(event_set, nfds, msec);
172 :
173 0 : EVBASE_ACQUIRE_LOCK(base, th_base_lock);
174 :
175 0 : if (res == -1) {
176 0 : if (errno != EINTR) {
177 0 : event_warn("poll");
178 0 : return (-1);
179 : }
180 :
181 0 : return (0);
182 : }
183 :
184 0 : event_debug(("%s: poll reports %d", __func__, res));
185 :
186 0 : if (res == 0 || nfds == 0)
187 0 : return (0);
188 :
189 0 : i = evutil_weakrand_range_(&base->weakrand_seed, nfds);
190 0 : for (j = 0; j < nfds; j++) {
191 : int what;
192 0 : if (++i == nfds)
193 0 : i = 0;
194 0 : what = event_set[i].revents;
195 0 : if (!what)
196 0 : continue;
197 :
198 0 : res = 0;
199 :
200 : /* If the file gets closed notify */
201 0 : if (what & (POLLHUP|POLLERR|POLLNVAL))
202 0 : what |= POLLIN|POLLOUT;
203 0 : if (what & POLLIN)
204 0 : res |= EV_READ;
205 0 : if (what & POLLOUT)
206 0 : res |= EV_WRITE;
207 0 : if (res == 0)
208 0 : continue;
209 :
210 0 : evmap_io_active_(base, event_set[i].fd, res);
211 : }
212 :
213 0 : return (0);
214 : }
215 :
216 : static int
217 0 : poll_add(struct event_base *base, int fd, short old, short events, void *idx_)
218 : {
219 0 : struct pollop *pop = base->evbase;
220 0 : struct pollfd *pfd = NULL;
221 0 : struct pollidx *idx = idx_;
222 : int i;
223 :
224 0 : EVUTIL_ASSERT((events & EV_SIGNAL) == 0);
225 0 : if (!(events & (EV_READ|EV_WRITE)))
226 0 : return (0);
227 :
228 : poll_check_ok(pop);
229 0 : if (pop->nfds + 1 >= pop->event_count) {
230 : struct pollfd *tmp_event_set;
231 : int tmp_event_count;
232 :
233 0 : if (pop->event_count < 32)
234 0 : tmp_event_count = 32;
235 : else
236 0 : tmp_event_count = pop->event_count * 2;
237 :
238 : /* We need more file descriptors */
239 0 : tmp_event_set = mm_realloc(pop->event_set,
240 : tmp_event_count * sizeof(struct pollfd));
241 0 : if (tmp_event_set == NULL) {
242 0 : event_warn("realloc");
243 0 : return (-1);
244 : }
245 0 : pop->event_set = tmp_event_set;
246 :
247 0 : pop->event_count = tmp_event_count;
248 0 : pop->realloc_copy = 1;
249 : }
250 :
251 0 : i = idx->idxplus1 - 1;
252 :
253 0 : if (i >= 0) {
254 0 : pfd = &pop->event_set[i];
255 : } else {
256 0 : i = pop->nfds++;
257 0 : pfd = &pop->event_set[i];
258 0 : pfd->events = 0;
259 0 : pfd->fd = fd;
260 0 : idx->idxplus1 = i + 1;
261 : }
262 :
263 0 : pfd->revents = 0;
264 0 : if (events & EV_WRITE)
265 0 : pfd->events |= POLLOUT;
266 0 : if (events & EV_READ)
267 0 : pfd->events |= POLLIN;
268 : poll_check_ok(pop);
269 :
270 0 : return (0);
271 : }
272 :
273 : /*
274 : * Nothing to be done here.
275 : */
276 :
277 : static int
278 0 : poll_del(struct event_base *base, int fd, short old, short events, void *idx_)
279 : {
280 0 : struct pollop *pop = base->evbase;
281 0 : struct pollfd *pfd = NULL;
282 0 : struct pollidx *idx = idx_;
283 : int i;
284 :
285 0 : EVUTIL_ASSERT((events & EV_SIGNAL) == 0);
286 0 : if (!(events & (EV_READ|EV_WRITE)))
287 0 : return (0);
288 :
289 : poll_check_ok(pop);
290 0 : i = idx->idxplus1 - 1;
291 0 : if (i < 0)
292 0 : return (-1);
293 :
294 : /* Do we still want to read or write? */
295 0 : pfd = &pop->event_set[i];
296 0 : if (events & EV_READ)
297 0 : pfd->events &= ~POLLIN;
298 0 : if (events & EV_WRITE)
299 0 : pfd->events &= ~POLLOUT;
300 : poll_check_ok(pop);
301 0 : if (pfd->events)
302 : /* Another event cares about that fd. */
303 0 : return (0);
304 :
305 : /* Okay, so we aren't interested in that fd anymore. */
306 0 : idx->idxplus1 = 0;
307 :
308 0 : --pop->nfds;
309 0 : if (i != pop->nfds) {
310 : /*
311 : * Shift the last pollfd down into the now-unoccupied
312 : * position.
313 : */
314 0 : memcpy(&pop->event_set[i], &pop->event_set[pop->nfds],
315 : sizeof(struct pollfd));
316 0 : idx = evmap_io_get_fdinfo_(&base->io, pop->event_set[i].fd);
317 0 : EVUTIL_ASSERT(idx);
318 0 : EVUTIL_ASSERT(idx->idxplus1 == pop->nfds + 1);
319 0 : idx->idxplus1 = i + 1;
320 : }
321 :
322 : poll_check_ok(pop);
323 0 : return (0);
324 : }
325 :
326 : static void
327 0 : poll_dealloc(struct event_base *base)
328 : {
329 0 : struct pollop *pop = base->evbase;
330 :
331 0 : evsig_dealloc_(base);
332 0 : if (pop->event_set)
333 0 : mm_free(pop->event_set);
334 0 : if (pop->event_set_copy)
335 0 : mm_free(pop->event_set_copy);
336 :
337 0 : memset(pop, 0, sizeof(struct pollop));
338 0 : mm_free(pop);
339 0 : }
340 :
341 : #endif /* EVENT__HAVE_POLL */
|