2 * Copyright 2000-2004 Niels Provos <provos@citi.umich.edu>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #include <sys/types.h>
32 #include <sys/resource.h>
33 #ifdef HAVE_SYS_TIME_H
36 #include <sys/_libevent_time.h>
38 #include <sys/queue.h>
39 #include <sys/devpoll.h>
50 #include "event-internal.h"
54 /* due to limitations in the devpoll interface, we need to keep track of
55 * all file descriptors outself.
59 struct event
*evwrite
;
63 struct evdevpoll
*fds
;
65 struct pollfd
*events
;
68 struct pollfd
*changes
;
72 static void *devpoll_init (struct event_base
*);
73 static int devpoll_add (void *, struct event
*);
74 static int devpoll_del (void *, struct event
*);
75 static int devpoll_dispatch (struct event_base
*, void *, struct timeval
*);
76 static void devpoll_dealloc (struct event_base
*, void *);
78 const struct eventop devpollops
= {
91 devpoll_commit(struct devpollop
*devpollop
)
94 * Due to a bug in Solaris, we have to use pwrite with an offset of 0.
95 * Write is limited to 2GB of data, until it will fail.
97 if (pwrite(devpollop
->dpfd
, devpollop
->changes
,
98 sizeof(struct pollfd
) * devpollop
->nchanges
, 0) == -1)
101 devpollop
->nchanges
= 0;
106 devpoll_queue(struct devpollop
*devpollop
, int fd
, int events
) {
109 if (devpollop
->nchanges
>= devpollop
->nevents
) {
111 * Change buffer is full, must commit it to /dev/poll before
114 if (devpoll_commit(devpollop
) != 0)
118 pfd
= &devpollop
->changes
[devpollop
->nchanges
++];
120 pfd
->events
= events
;
127 devpoll_init(struct event_base
*base
)
129 int dpfd
, nfiles
= NEVENT
;
131 struct devpollop
*devpollop
;
133 /* Disable devpoll when this environment variable is set */
134 if (evutil_getenv("EVENT_NODEVPOLL"))
137 if (!(devpollop
= calloc(1, sizeof(struct devpollop
))))
140 if (getrlimit(RLIMIT_NOFILE
, &rl
) == 0 &&
141 rl
.rlim_cur
!= RLIM_INFINITY
)
142 nfiles
= rl
.rlim_cur
;
144 /* Initialize the kernel queue */
145 if ((dpfd
= open("/dev/poll", O_RDWR
)) == -1) {
146 event_warn("open: /dev/poll");
151 devpollop
->dpfd
= dpfd
;
153 /* Initialize fields */
154 devpollop
->events
= calloc(nfiles
, sizeof(struct pollfd
));
155 if (devpollop
->events
== NULL
) {
160 devpollop
->nevents
= nfiles
;
162 devpollop
->fds
= calloc(nfiles
, sizeof(struct evdevpoll
));
163 if (devpollop
->fds
== NULL
) {
164 free(devpollop
->events
);
169 devpollop
->nfds
= nfiles
;
171 devpollop
->changes
= calloc(nfiles
, sizeof(struct pollfd
));
172 if (devpollop
->changes
== NULL
) {
173 free(devpollop
->fds
);
174 free(devpollop
->events
);
186 devpoll_recalc(struct event_base
*base
, void *arg
, int max
)
188 struct devpollop
*devpollop
= arg
;
190 if (max
>= devpollop
->nfds
) {
191 struct evdevpoll
*fds
;
194 nfds
= devpollop
->nfds
;
198 fds
= realloc(devpollop
->fds
, nfds
* sizeof(struct evdevpoll
));
200 event_warn("realloc");
203 devpollop
->fds
= fds
;
204 memset(fds
+ devpollop
->nfds
, 0,
205 (nfds
- devpollop
->nfds
) * sizeof(struct evdevpoll
));
206 devpollop
->nfds
= nfds
;
213 devpoll_dispatch(struct event_base
*base
, void *arg
, struct timeval
*tv
)
215 struct devpollop
*devpollop
= arg
;
216 struct pollfd
*events
= devpollop
->events
;
218 struct evdevpoll
*evdp
;
219 int i
, res
, timeout
= -1;
221 if (devpollop
->nchanges
)
222 devpoll_commit(devpollop
);
225 timeout
= tv
->tv_sec
* 1000 + (tv
->tv_usec
+ 999) / 1000;
227 dvp
.dp_fds
= devpollop
->events
;
228 dvp
.dp_nfds
= devpollop
->nevents
;
229 dvp
.dp_timeout
= timeout
;
231 res
= ioctl(devpollop
->dpfd
, DP_POLL
, &dvp
);
234 if (errno
!= EINTR
) {
235 event_warn("ioctl: DP_POLL");
239 evsignal_process(base
);
241 } else if (base
->sig
.evsignal_caught
) {
242 evsignal_process(base
);
245 event_debug(("%s: devpoll_wait reports %d", __func__
, res
));
247 for (i
= 0; i
< res
; i
++) {
249 int what
= events
[i
].revents
;
250 struct event
*evread
= NULL
, *evwrite
= NULL
;
252 assert(events
[i
].fd
< devpollop
->nfds
);
253 evdp
= &devpollop
->fds
[events
[i
].fd
];
256 what
|= POLLIN
| POLLOUT
;
257 else if (what
& POLLERR
)
258 what
|= POLLIN
| POLLOUT
;
261 evread
= evdp
->evread
;
265 if (what
& POLLOUT
) {
266 evwrite
= evdp
->evwrite
;
273 if (evread
!= NULL
&& !(evread
->ev_events
& EV_PERSIST
))
275 if (evwrite
!= NULL
&& evwrite
!= evread
&&
276 !(evwrite
->ev_events
& EV_PERSIST
))
280 event_active(evread
, EV_READ
, 1);
282 event_active(evwrite
, EV_WRITE
, 1);
290 devpoll_add(void *arg
, struct event
*ev
)
292 struct devpollop
*devpollop
= arg
;
293 struct evdevpoll
*evdp
;
296 if (ev
->ev_events
& EV_SIGNAL
)
297 return (evsignal_add(ev
));
300 if (fd
>= devpollop
->nfds
) {
301 /* Extend the file descriptor array as necessary */
302 if (devpoll_recalc(ev
->ev_base
, devpollop
, fd
) == -1)
305 evdp
= &devpollop
->fds
[fd
];
308 * It's not necessary to OR the existing read/write events that we
309 * are currently interested in with the new event we are adding.
310 * The /dev/poll driver ORs any new events with the existing events
311 * that it has cached for the fd.
315 if (ev
->ev_events
& EV_READ
) {
316 if (evdp
->evread
&& evdp
->evread
!= ev
) {
317 /* There is already a different read event registered */
323 if (ev
->ev_events
& EV_WRITE
) {
324 if (evdp
->evwrite
&& evdp
->evwrite
!= ev
) {
325 /* There is already a different write event registered */
331 if (devpoll_queue(devpollop
, fd
, events
) != 0)
334 /* Update events responsible */
335 if (ev
->ev_events
& EV_READ
)
337 if (ev
->ev_events
& EV_WRITE
)
344 devpoll_del(void *arg
, struct event
*ev
)
346 struct devpollop
*devpollop
= arg
;
347 struct evdevpoll
*evdp
;
349 int needwritedelete
= 1, needreaddelete
= 1;
351 if (ev
->ev_events
& EV_SIGNAL
)
352 return (evsignal_del(ev
));
355 if (fd
>= devpollop
->nfds
)
357 evdp
= &devpollop
->fds
[fd
];
360 if (ev
->ev_events
& EV_READ
)
362 if (ev
->ev_events
& EV_WRITE
)
366 * The only way to remove an fd from the /dev/poll monitored set is
367 * to use POLLREMOVE by itself. This removes ALL events for the fd
368 * provided so if we care about two events and are only removing one
369 * we must re-add the other event after POLLREMOVE.
372 if (devpoll_queue(devpollop
, fd
, POLLREMOVE
) != 0)
375 if ((events
& (POLLIN
|POLLOUT
)) != (POLLIN
|POLLOUT
)) {
377 * We're not deleting all events, so we must resubmit the
378 * event that we are still interested in if one exists.
381 if ((events
& POLLIN
) && evdp
->evwrite
!= NULL
) {
382 /* Deleting read, still care about write */
383 devpoll_queue(devpollop
, fd
, POLLOUT
);
385 } else if ((events
& POLLOUT
) && evdp
->evread
!= NULL
) {
386 /* Deleting write, still care about read */
387 devpoll_queue(devpollop
, fd
, POLLIN
);
395 evdp
->evwrite
= NULL
;
401 devpoll_dealloc(struct event_base
*base
, void *arg
)
403 struct devpollop
*devpollop
= arg
;
405 evsignal_dealloc(base
);
407 free(devpollop
->fds
);
408 if (devpollop
->events
)
409 free(devpollop
->events
);
410 if (devpollop
->changes
)
411 free(devpollop
->changes
);
412 if (devpollop
->dpfd
>= 0)
413 close(devpollop
->dpfd
);
415 memset(devpollop
, 0, sizeof(struct devpollop
));