1 /* $OpenBSD: kqueue.c,v 1.5 2002/07/10 14:41:31 art Exp $ */
4 * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
35 #include <sys/types.h>
36 #ifdef HAVE_SYS_TIME_H
39 #include <sys/_libevent_time.h>
41 #include <sys/queue.h>
42 #include <sys/event.h>
50 #ifdef HAVE_INTTYPES_H
54 /* Some platforms apparently define the udata field of struct kevent as
55 * intptr_t, whereas others define it as void*. There doesn't seem to be an
56 * easy way to tell them apart via autoconf, so we need to use OS macros. */
57 #if defined(HAVE_INTTYPES_H) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__darwin__) && !defined(__APPLE__)
58 #define PTR_TO_UDATA(x) ((intptr_t)(x))
60 #define PTR_TO_UDATA(x) (x)
64 #include "event-internal.h"
68 #define EVLIST_X_KQINKERNEL 0x1000
73 struct kevent
*changes
;
75 struct kevent
*events
;
76 struct event_list evsigevents
[NSIG
];
82 static void *kq_init (struct event_base
*);
83 static int kq_add (void *, struct event
*);
84 static int kq_del (void *, struct event
*);
85 static int kq_dispatch (struct event_base
*, void *, struct timeval
*);
86 static int kq_insert (struct kqop
*, struct kevent
*);
87 static void kq_dealloc (struct event_base
*, void *);
89 const struct eventop kqops
= {
100 kq_init(struct event_base
*base
)
103 struct kqop
*kqueueop
;
105 /* Disable kqueue when this environment variable is set */
106 if (evutil_getenv("EVENT_NOKQUEUE"))
109 if (!(kqueueop
= calloc(1, sizeof(struct kqop
))))
112 /* Initalize the kernel queue */
114 if ((kq
= kqueue()) == -1) {
115 event_warn("kqueue");
122 kqueueop
->pid
= getpid();
124 /* Initalize fields */
125 kqueueop
->changes
= malloc(NEVENT
* sizeof(struct kevent
));
126 if (kqueueop
->changes
== NULL
) {
130 kqueueop
->events
= malloc(NEVENT
* sizeof(struct kevent
));
131 if (kqueueop
->events
== NULL
) {
132 free (kqueueop
->changes
);
136 kqueueop
->nevents
= NEVENT
;
138 /* we need to keep track of multiple events per signal */
139 for (i
= 0; i
< NSIG
; ++i
) {
140 TAILQ_INIT(&kqueueop
->evsigevents
[i
]);
143 /* Check for Mac OS X kqueue bug. */
144 memset(&kqueueop
->changes
[0], 0, sizeof kqueueop
->changes
[0]);
145 kqueueop
->changes
[0].ident
= -1;
146 kqueueop
->changes
[0].filter
= EVFILT_READ
;
147 kqueueop
->changes
[0].flags
= EV_ADD
;
149 * If kqueue works, then kevent will succeed, and it will
150 * stick an error in events[0]. If kqueue is broken, then
154 kqueueop
->changes
, 1, kqueueop
->events
, NEVENT
, NULL
) != 1 ||
155 kqueueop
->events
[0].ident
!= -1 ||
156 kqueueop
->events
[0].flags
!= EV_ERROR
) {
157 event_warn("%s: detected broken kqueue; not using.", __func__
);
158 free(kqueueop
->changes
);
159 free(kqueueop
->events
);
169 kq_insert(struct kqop
*kqop
, struct kevent
*kev
)
171 int nevents
= kqop
->nevents
;
173 if (kqop
->nchanges
== nevents
) {
174 struct kevent
*newchange
;
175 struct kevent
*newresult
;
179 newchange
= realloc(kqop
->changes
,
180 nevents
* sizeof(struct kevent
));
181 if (newchange
== NULL
) {
182 event_warn("%s: malloc", __func__
);
185 kqop
->changes
= newchange
;
187 newresult
= realloc(kqop
->events
,
188 nevents
* sizeof(struct kevent
));
191 * If we fail, we don't have to worry about freeing,
192 * the next realloc will pick it up.
194 if (newresult
== NULL
) {
195 event_warn("%s: malloc", __func__
);
198 kqop
->events
= newresult
;
200 kqop
->nevents
= nevents
;
203 memcpy(&kqop
->changes
[kqop
->nchanges
++], kev
, sizeof(struct kevent
));
205 event_debug(("%s: fd %d %s%s",
206 __func__
, (int)kev
->ident
,
207 kev
->filter
== EVFILT_READ
? "EVFILT_READ" : "EVFILT_WRITE",
208 kev
->flags
== EV_DELETE
? " (del)" : ""));
214 kq_sighandler(int sig
)
216 /* Do nothing here */
220 kq_dispatch(struct event_base
*base
, void *arg
, struct timeval
*tv
)
222 struct kqop
*kqop
= arg
;
223 struct kevent
*changes
= kqop
->changes
;
224 struct kevent
*events
= kqop
->events
;
226 struct timespec ts
, *ts_p
= NULL
;
230 TIMEVAL_TO_TIMESPEC(tv
, &ts
);
234 res
= kevent(kqop
->kq
, changes
, kqop
->nchanges
,
235 events
, kqop
->nevents
, ts_p
);
238 if (errno
!= EINTR
) {
239 event_warn("kevent");
246 event_debug(("%s: kevent reports %d", __func__
, res
));
248 for (i
= 0; i
< res
; i
++) {
251 if (events
[i
].flags
& EV_ERROR
) {
253 * Error messages that can happen, when a delete fails.
254 * EBADF happens when the file discriptor has been
256 * ENOENT when the file discriptor was closed and
258 * EINVAL for some reasons not understood; EINVAL
259 * should not be returned ever; but FreeBSD does :-\
260 * An error is also indicated when a callback deletes
261 * an event we are still processing. In that case
262 * the data field is set to ENOENT.
264 if (events
[i
].data
== EBADF
||
265 events
[i
].data
== EINVAL
||
266 events
[i
].data
== ENOENT
)
268 errno
= events
[i
].data
;
272 if (events
[i
].filter
== EVFILT_READ
) {
274 } else if (events
[i
].filter
== EVFILT_WRITE
) {
276 } else if (events
[i
].filter
== EVFILT_SIGNAL
) {
283 if (events
[i
].filter
== EVFILT_SIGNAL
) {
284 struct event_list
*head
=
285 (struct event_list
*)events
[i
].udata
;
286 TAILQ_FOREACH(ev
, head
, ev_signal_next
) {
287 event_active(ev
, which
, events
[i
].data
);
290 ev
= (struct event
*)events
[i
].udata
;
292 if (!(ev
->ev_events
& EV_PERSIST
))
293 ev
->ev_flags
&= ~EVLIST_X_KQINKERNEL
;
295 event_active(ev
, which
, 1);
304 kq_add(void *arg
, struct event
*ev
)
306 struct kqop
*kqop
= arg
;
309 if (ev
->ev_events
& EV_SIGNAL
) {
310 int nsignal
= EVENT_SIGNAL(ev
);
312 assert(nsignal
>= 0 && nsignal
< NSIG
);
313 if (TAILQ_EMPTY(&kqop
->evsigevents
[nsignal
])) {
314 struct timespec timeout
= { 0, 0 };
316 memset(&kev
, 0, sizeof(kev
));
318 kev
.filter
= EVFILT_SIGNAL
;
320 kev
.udata
= PTR_TO_UDATA(&kqop
->evsigevents
[nsignal
]);
322 /* Be ready for the signal if it is sent any
323 * time between now and the next call to
325 if (kevent(kqop
->kq
, &kev
, 1, NULL
, 0, &timeout
) == -1)
328 if (_evsignal_set_handler(ev
->ev_base
, nsignal
,
329 kq_sighandler
) == -1)
333 TAILQ_INSERT_TAIL(&kqop
->evsigevents
[nsignal
], ev
,
335 ev
->ev_flags
|= EVLIST_X_KQINKERNEL
;
339 if (ev
->ev_events
& EV_READ
) {
340 memset(&kev
, 0, sizeof(kev
));
341 kev
.ident
= ev
->ev_fd
;
342 kev
.filter
= EVFILT_READ
;
344 /* Make it behave like select() and poll() */
345 kev
.fflags
= NOTE_EOF
;
348 if (!(ev
->ev_events
& EV_PERSIST
))
349 kev
.flags
|= EV_ONESHOT
;
350 kev
.udata
= PTR_TO_UDATA(ev
);
352 if (kq_insert(kqop
, &kev
) == -1)
355 ev
->ev_flags
|= EVLIST_X_KQINKERNEL
;
358 if (ev
->ev_events
& EV_WRITE
) {
359 memset(&kev
, 0, sizeof(kev
));
360 kev
.ident
= ev
->ev_fd
;
361 kev
.filter
= EVFILT_WRITE
;
363 if (!(ev
->ev_events
& EV_PERSIST
))
364 kev
.flags
|= EV_ONESHOT
;
365 kev
.udata
= PTR_TO_UDATA(ev
);
367 if (kq_insert(kqop
, &kev
) == -1)
370 ev
->ev_flags
|= EVLIST_X_KQINKERNEL
;
377 kq_del(void *arg
, struct event
*ev
)
379 struct kqop
*kqop
= arg
;
382 if (!(ev
->ev_flags
& EVLIST_X_KQINKERNEL
))
385 if (ev
->ev_events
& EV_SIGNAL
) {
386 int nsignal
= EVENT_SIGNAL(ev
);
387 struct timespec timeout
= { 0, 0 };
389 assert(nsignal
>= 0 && nsignal
< NSIG
);
390 TAILQ_REMOVE(&kqop
->evsigevents
[nsignal
], ev
, ev_signal_next
);
391 if (TAILQ_EMPTY(&kqop
->evsigevents
[nsignal
])) {
392 memset(&kev
, 0, sizeof(kev
));
394 kev
.filter
= EVFILT_SIGNAL
;
395 kev
.flags
= EV_DELETE
;
397 /* Because we insert signal events
398 * immediately, we need to delete them
399 * immediately, too */
400 if (kevent(kqop
->kq
, &kev
, 1, NULL
, 0, &timeout
) == -1)
403 if (_evsignal_restore_handler(ev
->ev_base
,
408 ev
->ev_flags
&= ~EVLIST_X_KQINKERNEL
;
412 if (ev
->ev_events
& EV_READ
) {
413 memset(&kev
, 0, sizeof(kev
));
414 kev
.ident
= ev
->ev_fd
;
415 kev
.filter
= EVFILT_READ
;
416 kev
.flags
= EV_DELETE
;
418 if (kq_insert(kqop
, &kev
) == -1)
421 ev
->ev_flags
&= ~EVLIST_X_KQINKERNEL
;
424 if (ev
->ev_events
& EV_WRITE
) {
425 memset(&kev
, 0, sizeof(kev
));
426 kev
.ident
= ev
->ev_fd
;
427 kev
.filter
= EVFILT_WRITE
;
428 kev
.flags
= EV_DELETE
;
430 if (kq_insert(kqop
, &kev
) == -1)
433 ev
->ev_flags
&= ~EVLIST_X_KQINKERNEL
;
440 kq_dealloc(struct event_base
*base
, void *arg
)
442 struct kqop
*kqop
= arg
;
444 evsignal_dealloc(base
);
450 if (kqop
->kq
>= 0 && kqop
->pid
== getpid())
453 memset(kqop
, 0, sizeof(struct kqop
));