xref: /openbsd-src/lib/libevent/poll.c (revision 8698d772fa8e7a9c21b357c7c0f44e0b4c3e93ec)
1*8698d772Snayden /*	$OpenBSD: poll.c,v 1.22 2016/09/03 11:31:17 nayden Exp $	*/
2378aa367Smarkus 
3378aa367Smarkus /*
4378aa367Smarkus  * Copyright 2000-2003 Niels Provos <provos@citi.umich.edu>
5378aa367Smarkus  * All rights reserved.
6378aa367Smarkus  *
7378aa367Smarkus  * Redistribution and use in source and binary forms, with or without
8378aa367Smarkus  * modification, are permitted provided that the following conditions
9378aa367Smarkus  * are met:
10378aa367Smarkus  * 1. Redistributions of source code must retain the above copyright
11378aa367Smarkus  *    notice, this list of conditions and the following disclaimer.
12378aa367Smarkus  * 2. Redistributions in binary form must reproduce the above copyright
13378aa367Smarkus  *    notice, this list of conditions and the following disclaimer in the
14378aa367Smarkus  *    documentation and/or other materials provided with the distribution.
15ff9272daSbrad  * 3. The name of the author may not be used to endorse or promote products
16378aa367Smarkus  *    derived from this software without specific prior written permission.
17378aa367Smarkus  *
18378aa367Smarkus  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19378aa367Smarkus  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20378aa367Smarkus  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21378aa367Smarkus  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22378aa367Smarkus  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23378aa367Smarkus  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24378aa367Smarkus  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25378aa367Smarkus  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26378aa367Smarkus  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27378aa367Smarkus  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28378aa367Smarkus  */
29378aa367Smarkus 
30378aa367Smarkus #include <sys/types.h>
31378aa367Smarkus #include <sys/time.h>
32378aa367Smarkus #include <sys/queue.h>
33defc4074Sbluhm 
34378aa367Smarkus #include <poll.h>
35378aa367Smarkus #include <signal.h>
36378aa367Smarkus #include <stdio.h>
37378aa367Smarkus #include <stdlib.h>
38378aa367Smarkus #include <string.h>
39378aa367Smarkus #include <unistd.h>
40378aa367Smarkus #include <errno.h>
41348ce57bSbrad #ifdef CHECK_INVARIANTS
42348ce57bSbrad #include <assert.h>
43348ce57bSbrad #endif
44378aa367Smarkus 
45378aa367Smarkus #include "event.h"
464643be29Sbrad #include "event-internal.h"
47378aa367Smarkus #include "evsignal.h"
484643be29Sbrad #include "log.h"
49378aa367Smarkus 
50378aa367Smarkus struct pollop {
51378aa367Smarkus 	int event_count;		/* Highest number alloc */
52348ce57bSbrad 	int nfds;                       /* Size of event_* */
531085edd8Sbrad 	int fd_count;                   /* Size of idxplus1_by_fd */
54378aa367Smarkus 	struct pollfd *event_set;
551085edd8Sbrad 	struct event **event_r_back;
561085edd8Sbrad 	struct event **event_w_back;
571085edd8Sbrad 	int *idxplus1_by_fd; /* Index into event_set by fd; we add 1 so
581085edd8Sbrad 			      * that 0 (which is easy to memset) can mean
591085edd8Sbrad 			      * "no entry." */
604643be29Sbrad };
61378aa367Smarkus 
628ead113eSnicm static void *poll_init	(struct event_base *);
638ead113eSnicm static int poll_add		(void *, struct event *);
648ead113eSnicm static int poll_del		(void *, struct event *);
658ead113eSnicm static int poll_dispatch	(struct event_base *, void *, struct timeval *);
668ead113eSnicm static void poll_dealloc	(struct event_base *, void *);
67378aa367Smarkus 
68ae8df7afSderaadt const struct eventop pollops = {
69378aa367Smarkus 	"poll",
70378aa367Smarkus 	poll_init,
71378aa367Smarkus 	poll_add,
72378aa367Smarkus 	poll_del,
733ac1ba99Sbrad 	poll_dispatch,
748ead113eSnicm 	poll_dealloc,
758ead113eSnicm     0
76378aa367Smarkus };
77378aa367Smarkus 
788ead113eSnicm static void *
poll_init(struct event_base * base)79bdce580dSbrad poll_init(struct event_base *base)
80378aa367Smarkus {
814643be29Sbrad 	struct pollop *pollop;
824643be29Sbrad 
83110bbf6fSbrad 	/* Disable poll when this environment variable is set */
8484ea67e3Sbluhm 	if (!issetugid() && getenv("EVENT_NOPOLL"))
85378aa367Smarkus 		return (NULL);
86378aa367Smarkus 
874643be29Sbrad 	if (!(pollop = calloc(1, sizeof(struct pollop))))
884643be29Sbrad 		return (NULL);
89378aa367Smarkus 
90bdce580dSbrad 	evsignal_init(base);
91378aa367Smarkus 
924643be29Sbrad 	return (pollop);
93378aa367Smarkus }
94378aa367Smarkus 
95348ce57bSbrad #ifdef CHECK_INVARIANTS
96348ce57bSbrad static void
poll_check_ok(struct pollop * pop)97348ce57bSbrad poll_check_ok(struct pollop *pop)
98348ce57bSbrad {
99348ce57bSbrad 	int i, idx;
100348ce57bSbrad 	struct event *ev;
101348ce57bSbrad 
102348ce57bSbrad 	for (i = 0; i < pop->fd_count; ++i) {
103348ce57bSbrad 		idx = pop->idxplus1_by_fd[i]-1;
104348ce57bSbrad 		if (idx < 0)
105348ce57bSbrad 			continue;
106348ce57bSbrad 		assert(pop->event_set[idx].fd == i);
107348ce57bSbrad 		if (pop->event_set[idx].events & POLLIN) {
108348ce57bSbrad 			ev = pop->event_r_back[idx];
109348ce57bSbrad 			assert(ev);
110348ce57bSbrad 			assert(ev->ev_events & EV_READ);
111348ce57bSbrad 			assert(ev->ev_fd == i);
112348ce57bSbrad 		}
113348ce57bSbrad 		if (pop->event_set[idx].events & POLLOUT) {
114348ce57bSbrad 			ev = pop->event_w_back[idx];
115348ce57bSbrad 			assert(ev);
116348ce57bSbrad 			assert(ev->ev_events & EV_WRITE);
117348ce57bSbrad 			assert(ev->ev_fd == i);
118348ce57bSbrad 		}
119348ce57bSbrad 	}
120348ce57bSbrad 	for (i = 0; i < pop->nfds; ++i) {
121348ce57bSbrad 		struct pollfd *pfd = &pop->event_set[i];
122348ce57bSbrad 		assert(pop->idxplus1_by_fd[pfd->fd] == i+1);
123348ce57bSbrad 	}
124348ce57bSbrad }
125348ce57bSbrad #else
126348ce57bSbrad #define poll_check_ok(pop)
127348ce57bSbrad #endif
128348ce57bSbrad 
1298ead113eSnicm static int
poll_dispatch(struct event_base * base,void * arg,struct timeval * tv)1304643be29Sbrad poll_dispatch(struct event_base *base, void *arg, struct timeval *tv)
131378aa367Smarkus {
1328ead113eSnicm 	int res, i, j, msec = -1, nfds;
133378aa367Smarkus 	struct pollop *pop = arg;
134378aa367Smarkus 
135348ce57bSbrad 	poll_check_ok(pop);
136378aa367Smarkus 
137bdce580dSbrad 	if (tv != NULL)
138bdce580dSbrad 		msec = tv->tv_sec * 1000 + (tv->tv_usec + 999) / 1000;
139bdce580dSbrad 
140bdce580dSbrad 	nfds = pop->nfds;
141bdce580dSbrad 	res = poll(pop->event_set, nfds, msec);
142378aa367Smarkus 
143378aa367Smarkus 	if (res == -1) {
144378aa367Smarkus 		if (errno != EINTR) {
1454643be29Sbrad 			event_warn("poll");
146378aa367Smarkus 			return (-1);
147378aa367Smarkus 		}
148378aa367Smarkus 
149bdce580dSbrad 		evsignal_process(base);
150378aa367Smarkus 		return (0);
151bdce580dSbrad 	} else if (base->sig.evsignal_caught) {
152bdce580dSbrad 		evsignal_process(base);
153bdce580dSbrad 	}
154378aa367Smarkus 
1554643be29Sbrad 	event_debug(("%s: poll reports %d", __func__, res));
156378aa367Smarkus 
1578ead113eSnicm 	if (res == 0 || nfds == 0)
158378aa367Smarkus 		return (0);
159378aa367Smarkus 
1609ce3ea75Sdlg 	i = arc4random_uniform(nfds);
1618ead113eSnicm 	for (j = 0; j < nfds; j++) {
1621085edd8Sbrad 		struct event *r_ev = NULL, *w_ev = NULL;
1638ead113eSnicm 		int what;
1648ead113eSnicm 		if (++i == nfds)
1658ead113eSnicm 			i = 0;
1668ead113eSnicm 		what = pop->event_set[i].revents;
167ff9272daSbrad 
168348ce57bSbrad 		if (!what)
169348ce57bSbrad 			continue;
170348ce57bSbrad 
171378aa367Smarkus 		res = 0;
172ff9272daSbrad 
1735b97e645Smarkus 		/* If the file gets closed notify */
174348ce57bSbrad 		if (what & (POLLHUP|POLLERR))
175ff9272daSbrad 			what |= POLLIN|POLLOUT;
1761085edd8Sbrad 		if (what & POLLIN) {
1775b97e645Smarkus 			res |= EV_READ;
1781085edd8Sbrad 			r_ev = pop->event_r_back[i];
1791085edd8Sbrad 		}
1801085edd8Sbrad 		if (what & POLLOUT) {
1815b97e645Smarkus 			res |= EV_WRITE;
1821085edd8Sbrad 			w_ev = pop->event_w_back[i];
1831085edd8Sbrad 		}
184378aa367Smarkus 		if (res == 0)
185378aa367Smarkus 			continue;
186378aa367Smarkus 
1871085edd8Sbrad 		if (r_ev && (res & r_ev->ev_events)) {
1881085edd8Sbrad 			event_active(r_ev, res & r_ev->ev_events, 1);
1891085edd8Sbrad 		}
1901085edd8Sbrad 		if (w_ev && w_ev != r_ev && (res & w_ev->ev_events)) {
1911085edd8Sbrad 			event_active(w_ev, res & w_ev->ev_events, 1);
192378aa367Smarkus 		}
193378aa367Smarkus 	}
194378aa367Smarkus 
195378aa367Smarkus 	return (0);
196378aa367Smarkus }
197378aa367Smarkus 
1988ead113eSnicm static int
poll_add(void * arg,struct event * ev)199378aa367Smarkus poll_add(void *arg, struct event *ev)
200378aa367Smarkus {
201378aa367Smarkus 	struct pollop *pop = arg;
202348ce57bSbrad 	struct pollfd *pfd = NULL;
203348ce57bSbrad 	int i;
204378aa367Smarkus 
205378aa367Smarkus 	if (ev->ev_events & EV_SIGNAL)
206bdce580dSbrad 		return (evsignal_add(ev));
207348ce57bSbrad 	if (!(ev->ev_events & (EV_READ|EV_WRITE)))
208348ce57bSbrad 		return (0);
209348ce57bSbrad 
210348ce57bSbrad 	poll_check_ok(pop);
211348ce57bSbrad 	if (pop->nfds + 1 >= pop->event_count) {
212a04202bdSbrad 		struct pollfd *tmp_event_set;
213a04202bdSbrad 		struct event **tmp_event_r_back;
214a04202bdSbrad 		struct event **tmp_event_w_back;
215a04202bdSbrad 		int tmp_event_count;
216a04202bdSbrad 
217348ce57bSbrad 		if (pop->event_count < 32)
218a04202bdSbrad 			tmp_event_count = 32;
219348ce57bSbrad 		else
220a04202bdSbrad 			tmp_event_count = pop->event_count * 2;
221348ce57bSbrad 
222348ce57bSbrad 		/* We need more file descriptors */
2232af34b00Sderaadt 		tmp_event_set = reallocarray(pop->event_set,
2242af34b00Sderaadt 		    tmp_event_count, sizeof(struct pollfd));
225a04202bdSbrad 		if (tmp_event_set == NULL) {
226348ce57bSbrad 			event_warn("realloc");
227348ce57bSbrad 			return (-1);
228348ce57bSbrad 		}
229a04202bdSbrad 		pop->event_set = tmp_event_set;
230a04202bdSbrad 
2312af34b00Sderaadt 		tmp_event_r_back = reallocarray(pop->event_r_back,
2322af34b00Sderaadt 		    tmp_event_count, sizeof(struct event *));
233a04202bdSbrad 		if (tmp_event_r_back == NULL) {
234a04202bdSbrad 			/* event_set overallocated; that's okay. */
235348ce57bSbrad 			event_warn("realloc");
236348ce57bSbrad 			return (-1);
237348ce57bSbrad 		}
238a04202bdSbrad 		pop->event_r_back = tmp_event_r_back;
239a04202bdSbrad 
2402af34b00Sderaadt 		tmp_event_w_back = reallocarray(pop->event_w_back,
2412af34b00Sderaadt 		    tmp_event_count, sizeof(struct event *));
242a04202bdSbrad 		if (tmp_event_w_back == NULL) {
243a04202bdSbrad 			/* event_set and event_r_back overallocated; that's
244a04202bdSbrad 			 * okay. */
245a04202bdSbrad 			event_warn("realloc");
246a04202bdSbrad 			return (-1);
247a04202bdSbrad 		}
248a04202bdSbrad 		pop->event_w_back = tmp_event_w_back;
249a04202bdSbrad 
250a04202bdSbrad 		pop->event_count = tmp_event_count;
251348ce57bSbrad 	}
252348ce57bSbrad 	if (ev->ev_fd >= pop->fd_count) {
253a04202bdSbrad 		int *tmp_idxplus1_by_fd;
254348ce57bSbrad 		int new_count;
255348ce57bSbrad 		if (pop->fd_count < 32)
256348ce57bSbrad 			new_count = 32;
257348ce57bSbrad 		else
258348ce57bSbrad 			new_count = pop->fd_count * 2;
259348ce57bSbrad 		while (new_count <= ev->ev_fd)
260348ce57bSbrad 			new_count *= 2;
2612af34b00Sderaadt 		tmp_idxplus1_by_fd = reallocarray(pop->idxplus1_by_fd,
2622af34b00Sderaadt 		    new_count, sizeof(int));
263a04202bdSbrad 		if (tmp_idxplus1_by_fd == NULL) {
264348ce57bSbrad 			event_warn("realloc");
265348ce57bSbrad 			return (-1);
266348ce57bSbrad 		}
267a04202bdSbrad 		pop->idxplus1_by_fd = tmp_idxplus1_by_fd;
268348ce57bSbrad 		memset(pop->idxplus1_by_fd + pop->fd_count,
269348ce57bSbrad 		       0, sizeof(int)*(new_count - pop->fd_count));
270348ce57bSbrad 		pop->fd_count = new_count;
271348ce57bSbrad 	}
272348ce57bSbrad 
273348ce57bSbrad 	i = pop->idxplus1_by_fd[ev->ev_fd] - 1;
274348ce57bSbrad 	if (i >= 0) {
275348ce57bSbrad 		pfd = &pop->event_set[i];
276348ce57bSbrad 	} else {
277348ce57bSbrad 		i = pop->nfds++;
278348ce57bSbrad 		pfd = &pop->event_set[i];
279348ce57bSbrad 		pfd->events = 0;
280348ce57bSbrad 		pfd->fd = ev->ev_fd;
281348ce57bSbrad 		pop->event_w_back[i] = pop->event_r_back[i] = NULL;
282348ce57bSbrad 		pop->idxplus1_by_fd[ev->ev_fd] = i + 1;
283348ce57bSbrad 	}
284348ce57bSbrad 
285348ce57bSbrad 	pfd->revents = 0;
286348ce57bSbrad 	if (ev->ev_events & EV_WRITE) {
287348ce57bSbrad 		pfd->events |= POLLOUT;
288348ce57bSbrad 		pop->event_w_back[i] = ev;
289348ce57bSbrad 	}
290348ce57bSbrad 	if (ev->ev_events & EV_READ) {
291348ce57bSbrad 		pfd->events |= POLLIN;
292348ce57bSbrad 		pop->event_r_back[i] = ev;
293348ce57bSbrad 	}
294348ce57bSbrad 	poll_check_ok(pop);
295378aa367Smarkus 
296378aa367Smarkus 	return (0);
297378aa367Smarkus }
298378aa367Smarkus 
299378aa367Smarkus /*
300378aa367Smarkus  * Nothing to be done here.
301378aa367Smarkus  */
302378aa367Smarkus 
3038ead113eSnicm static int
poll_del(void * arg,struct event * ev)304378aa367Smarkus poll_del(void *arg, struct event *ev)
305378aa367Smarkus {
306378aa367Smarkus 	struct pollop *pop = arg;
307348ce57bSbrad 	struct pollfd *pfd = NULL;
308348ce57bSbrad 	int i;
309378aa367Smarkus 
310348ce57bSbrad 	if (ev->ev_events & EV_SIGNAL)
311bdce580dSbrad 		return (evsignal_del(ev));
312348ce57bSbrad 
313348ce57bSbrad 	if (!(ev->ev_events & (EV_READ|EV_WRITE)))
314378aa367Smarkus 		return (0);
315378aa367Smarkus 
316348ce57bSbrad 	poll_check_ok(pop);
317348ce57bSbrad 	i = pop->idxplus1_by_fd[ev->ev_fd] - 1;
318348ce57bSbrad 	if (i < 0)
319348ce57bSbrad 		return (-1);
320348ce57bSbrad 
321348ce57bSbrad 	/* Do we still want to read or write? */
322348ce57bSbrad 	pfd = &pop->event_set[i];
323348ce57bSbrad 	if (ev->ev_events & EV_READ) {
324348ce57bSbrad 		pfd->events &= ~POLLIN;
325348ce57bSbrad 		pop->event_r_back[i] = NULL;
326348ce57bSbrad 	}
327348ce57bSbrad 	if (ev->ev_events & EV_WRITE) {
328348ce57bSbrad 		pfd->events &= ~POLLOUT;
329348ce57bSbrad 		pop->event_w_back[i] = NULL;
330348ce57bSbrad 	}
331348ce57bSbrad 	poll_check_ok(pop);
332348ce57bSbrad 	if (pfd->events)
333348ce57bSbrad 		/* Another event cares about that fd. */
334348ce57bSbrad 		return (0);
335348ce57bSbrad 
336348ce57bSbrad 	/* Okay, so we aren't interested in that fd anymore. */
337348ce57bSbrad 	pop->idxplus1_by_fd[ev->ev_fd] = 0;
338348ce57bSbrad 
339348ce57bSbrad 	--pop->nfds;
340348ce57bSbrad 	if (i != pop->nfds) {
341348ce57bSbrad 		/*
342348ce57bSbrad 		 * Shift the last pollfd down into the now-unoccupied
343348ce57bSbrad 		 * position.
344348ce57bSbrad 		 */
345348ce57bSbrad 		memcpy(&pop->event_set[i], &pop->event_set[pop->nfds],
346348ce57bSbrad 		       sizeof(struct pollfd));
347348ce57bSbrad 		pop->event_r_back[i] = pop->event_r_back[pop->nfds];
348348ce57bSbrad 		pop->event_w_back[i] = pop->event_w_back[pop->nfds];
349348ce57bSbrad 		pop->idxplus1_by_fd[pop->event_set[i].fd] = i + 1;
350348ce57bSbrad 	}
351348ce57bSbrad 
352348ce57bSbrad 	poll_check_ok(pop);
353348ce57bSbrad 	return (0);
354378aa367Smarkus }
3553ac1ba99Sbrad 
3568ead113eSnicm static void
poll_dealloc(struct event_base * base,void * arg)357bdce580dSbrad poll_dealloc(struct event_base *base, void *arg)
3583ac1ba99Sbrad {
3593ac1ba99Sbrad 	struct pollop *pop = arg;
3603ac1ba99Sbrad 
361bdce580dSbrad 	evsignal_dealloc(base);
3623ac1ba99Sbrad 	free(pop->event_set);
3633ac1ba99Sbrad 	free(pop->event_r_back);
3643ac1ba99Sbrad 	free(pop->event_w_back);
3653ac1ba99Sbrad 	free(pop->idxplus1_by_fd);
3663ac1ba99Sbrad 
3673ac1ba99Sbrad 	memset(pop, 0, sizeof(struct pollop));
3683ac1ba99Sbrad 	free(pop);
3693ac1ba99Sbrad }
370