xref: /unit/src/nxt_kqueue_engine.c (revision 312)
112Sigor@sysoev.ru 
212Sigor@sysoev.ru /*
312Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
412Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
512Sigor@sysoev.ru  */
612Sigor@sysoev.ru 
712Sigor@sysoev.ru #include <nxt_main.h>
812Sigor@sysoev.ru 
912Sigor@sysoev.ru 
1012Sigor@sysoev.ru /*
1112Sigor@sysoev.ru  * kqueue()      has been introduced in FreeBSD 4.1 and then was ported
1212Sigor@sysoev.ru  *               to OpenBSD 2.9, MacOSX 10.3 (Panther), and NetBSD 2.0.
1312Sigor@sysoev.ru  *               DragonFlyBSD inherited it with FreeBSD 4 code base.
1412Sigor@sysoev.ru  *
1512Sigor@sysoev.ru  * NOTE_REVOKE   has been introduced in FreeBSD 4.3 and then was ported
1612Sigor@sysoev.ru  *               to OpenBSD 2.9, MacOSX 10.3 (Panther), and NetBSD 2.0.
1712Sigor@sysoev.ru  *               DragonFlyBSD inherited it with FreeBSD 4 code base.
1812Sigor@sysoev.ru  *
1912Sigor@sysoev.ru  * EVFILT_TIMER  has been introduced in FreeBSD 4.4-STABLE and then was
2012Sigor@sysoev.ru  *               ported to NetBSD 2.0, MacOSX 10.4 (Tiger), and OpenBSD 4.2.
2112Sigor@sysoev.ru  *               DragonFlyBSD inherited it with FreeBSD 4 code base.
2212Sigor@sysoev.ru  *
2312Sigor@sysoev.ru  * EVFILT_USER and EV_DISPATCH have been introduced in MacOSX 10.6 (Snow
2412Sigor@sysoev.ru  *               Leopard) as part of the Grand Central Dispatch framework
2512Sigor@sysoev.ru  *               and then were ported to FreeBSD 8.0-STABLE as part of the
2612Sigor@sysoev.ru  *               libdispatch support.
2712Sigor@sysoev.ru  */
2812Sigor@sysoev.ru 
2912Sigor@sysoev.ru 
3012Sigor@sysoev.ru /*
3112Sigor@sysoev.ru  * EV_DISPATCH is better because it just disables an event on delivery
3212Sigor@sysoev.ru  * whilst EV_ONESHOT deletes the event.  This eliminates in-kernel memory
3312Sigor@sysoev.ru  * deallocation and probable subsequent allocation with a lock acquiring.
3412Sigor@sysoev.ru  */
3512Sigor@sysoev.ru #ifdef EV_DISPATCH
3612Sigor@sysoev.ru #define NXT_KEVENT_ONESHOT  EV_DISPATCH
3712Sigor@sysoev.ru #else
3812Sigor@sysoev.ru #define NXT_KEVENT_ONESHOT  EV_ONESHOT
3912Sigor@sysoev.ru #endif
4012Sigor@sysoev.ru 
4112Sigor@sysoev.ru 
4212Sigor@sysoev.ru #if (NXT_NETBSD)
4312Sigor@sysoev.ru /* NetBSD defines the kevent.udata field as intptr_t. */
4412Sigor@sysoev.ru 
4512Sigor@sysoev.ru #define nxt_kevent_set_udata(udata)  (intptr_t) (udata)
4612Sigor@sysoev.ru #define nxt_kevent_get_udata(udata)  (void *) (udata)
4712Sigor@sysoev.ru 
4812Sigor@sysoev.ru #else
4912Sigor@sysoev.ru #define nxt_kevent_set_udata(udata)  (void *) (udata)
5012Sigor@sysoev.ru #define nxt_kevent_get_udata(udata)  (udata)
5112Sigor@sysoev.ru #endif
5212Sigor@sysoev.ru 
5312Sigor@sysoev.ru 
5412Sigor@sysoev.ru static nxt_int_t nxt_kqueue_create(nxt_event_engine_t *engine,
5512Sigor@sysoev.ru     nxt_uint_t mchanges, nxt_uint_t mevents);
5612Sigor@sysoev.ru static void nxt_kqueue_free(nxt_event_engine_t *engine);
5712Sigor@sysoev.ru static void nxt_kqueue_enable(nxt_event_engine_t *engine, nxt_fd_event_t *ev);
5812Sigor@sysoev.ru static void nxt_kqueue_disable(nxt_event_engine_t *engine, nxt_fd_event_t *ev);
5912Sigor@sysoev.ru static void nxt_kqueue_delete(nxt_event_engine_t *engine, nxt_fd_event_t *ev);
6012Sigor@sysoev.ru static nxt_bool_t nxt_kqueue_close(nxt_event_engine_t *engine,
6112Sigor@sysoev.ru     nxt_fd_event_t *ev);
6212Sigor@sysoev.ru static void nxt_kqueue_enable_read(nxt_event_engine_t *engine,
6312Sigor@sysoev.ru     nxt_fd_event_t *ev);
6412Sigor@sysoev.ru static void nxt_kqueue_enable_write(nxt_event_engine_t *engine,
6512Sigor@sysoev.ru     nxt_fd_event_t *ev);
6612Sigor@sysoev.ru static void nxt_kqueue_disable_read(nxt_event_engine_t *engine,
6712Sigor@sysoev.ru     nxt_fd_event_t *ev);
6812Sigor@sysoev.ru static void nxt_kqueue_disable_write(nxt_event_engine_t *engine,
6912Sigor@sysoev.ru     nxt_fd_event_t *ev);
7012Sigor@sysoev.ru static void nxt_kqueue_block_read(nxt_event_engine_t *engine,
7112Sigor@sysoev.ru     nxt_fd_event_t *ev);
7212Sigor@sysoev.ru static void nxt_kqueue_block_write(nxt_event_engine_t *engine,
7312Sigor@sysoev.ru     nxt_fd_event_t *ev);
7412Sigor@sysoev.ru static void nxt_kqueue_oneshot_read(nxt_event_engine_t *engine,
7512Sigor@sysoev.ru     nxt_fd_event_t *ev);
7612Sigor@sysoev.ru static void nxt_kqueue_oneshot_write(nxt_event_engine_t *engine,
7712Sigor@sysoev.ru     nxt_fd_event_t *ev);
7812Sigor@sysoev.ru static void nxt_kqueue_enable_accept(nxt_event_engine_t *engine,
7912Sigor@sysoev.ru     nxt_fd_event_t *ev);
8012Sigor@sysoev.ru static void nxt_kqueue_enable_file(nxt_event_engine_t *engine,
8162Sigor@sysoev.ru     nxt_file_event_t *ev);
8212Sigor@sysoev.ru static void nxt_kqueue_close_file(nxt_event_engine_t *engine,
8362Sigor@sysoev.ru     nxt_file_event_t *ev);
8412Sigor@sysoev.ru static void nxt_kqueue_fd_set(nxt_event_engine_t *engine, nxt_fd_event_t *ev,
8512Sigor@sysoev.ru     nxt_int_t filter, nxt_uint_t flags);
8612Sigor@sysoev.ru static struct kevent *nxt_kqueue_get_kevent(nxt_event_engine_t *engine);
8712Sigor@sysoev.ru static void nxt_kqueue_error(nxt_event_engine_t *engine);
8812Sigor@sysoev.ru static void nxt_kqueue_fd_error_handler(nxt_task_t *task, void *obj,
8912Sigor@sysoev.ru     void *data);
9012Sigor@sysoev.ru static void nxt_kqueue_file_error_handler(nxt_task_t *task, void *obj,
9112Sigor@sysoev.ru     void *data);
9212Sigor@sysoev.ru static nxt_int_t nxt_kqueue_add_signal(nxt_event_engine_t *engine,
9312Sigor@sysoev.ru     const nxt_sig_event_t *sigev);
9412Sigor@sysoev.ru #if (NXT_HAVE_EVFILT_USER)
9512Sigor@sysoev.ru static nxt_int_t nxt_kqueue_enable_post(nxt_event_engine_t *engine,
9612Sigor@sysoev.ru     nxt_work_handler_t handler);
9712Sigor@sysoev.ru static void nxt_kqueue_signal(nxt_event_engine_t *engine, nxt_uint_t signo);
9812Sigor@sysoev.ru #endif
9912Sigor@sysoev.ru static void nxt_kqueue_poll(nxt_event_engine_t *engine, nxt_msec_t timeout);
10012Sigor@sysoev.ru 
10162Sigor@sysoev.ru static void nxt_kqueue_conn_io_connect(nxt_task_t *task, void *obj,
10212Sigor@sysoev.ru     void *data);
10362Sigor@sysoev.ru static void nxt_kqueue_conn_connected(nxt_task_t *task, void *obj,
10412Sigor@sysoev.ru     void *data);
10512Sigor@sysoev.ru static void nxt_kqueue_listen_handler(nxt_task_t *task, void *obj, void *data);
10662Sigor@sysoev.ru static void nxt_kqueue_conn_io_accept(nxt_task_t *task, void *obj,
10712Sigor@sysoev.ru     void *data);
10862Sigor@sysoev.ru static void nxt_kqueue_conn_io_read(nxt_task_t *task, void *obj,
10912Sigor@sysoev.ru     void *data);
11062Sigor@sysoev.ru static ssize_t nxt_kqueue_conn_io_recvbuf(nxt_conn_t *c, nxt_buf_t *b);
11112Sigor@sysoev.ru 
11212Sigor@sysoev.ru 
11362Sigor@sysoev.ru static nxt_conn_io_t  nxt_kqueue_conn_io = {
11462Sigor@sysoev.ru     nxt_kqueue_conn_io_connect,
11562Sigor@sysoev.ru     nxt_kqueue_conn_io_accept,
11612Sigor@sysoev.ru 
11762Sigor@sysoev.ru     nxt_kqueue_conn_io_read,
11862Sigor@sysoev.ru     nxt_kqueue_conn_io_recvbuf,
11962Sigor@sysoev.ru     nxt_conn_io_recv,
12012Sigor@sysoev.ru 
12113Sigor@sysoev.ru     nxt_conn_io_write,
12212Sigor@sysoev.ru     nxt_event_conn_io_write_chunk,
12312Sigor@sysoev.ru 
12412Sigor@sysoev.ru #if (NXT_HAVE_FREEBSD_SENDFILE)
12512Sigor@sysoev.ru     nxt_freebsd_event_conn_io_sendfile,
12612Sigor@sysoev.ru #elif (NXT_HAVE_MACOSX_SENDFILE)
12712Sigor@sysoev.ru     nxt_macosx_event_conn_io_sendfile,
12812Sigor@sysoev.ru #else
12912Sigor@sysoev.ru     nxt_event_conn_io_sendbuf,
13012Sigor@sysoev.ru #endif
13112Sigor@sysoev.ru 
13212Sigor@sysoev.ru     nxt_event_conn_io_writev,
13312Sigor@sysoev.ru     nxt_event_conn_io_send,
13412Sigor@sysoev.ru 
13562Sigor@sysoev.ru     nxt_conn_io_shutdown,
13612Sigor@sysoev.ru };
13712Sigor@sysoev.ru 
13812Sigor@sysoev.ru 
13912Sigor@sysoev.ru const nxt_event_interface_t  nxt_kqueue_engine = {
14012Sigor@sysoev.ru     "kqueue",
14112Sigor@sysoev.ru     nxt_kqueue_create,
14212Sigor@sysoev.ru     nxt_kqueue_free,
14312Sigor@sysoev.ru     nxt_kqueue_enable,
14412Sigor@sysoev.ru     nxt_kqueue_disable,
14512Sigor@sysoev.ru     nxt_kqueue_delete,
14612Sigor@sysoev.ru     nxt_kqueue_close,
14712Sigor@sysoev.ru     nxt_kqueue_enable_read,
14812Sigor@sysoev.ru     nxt_kqueue_enable_write,
14912Sigor@sysoev.ru     nxt_kqueue_disable_read,
15012Sigor@sysoev.ru     nxt_kqueue_disable_write,
15112Sigor@sysoev.ru     nxt_kqueue_block_read,
15212Sigor@sysoev.ru     nxt_kqueue_block_write,
15312Sigor@sysoev.ru     nxt_kqueue_oneshot_read,
15412Sigor@sysoev.ru     nxt_kqueue_oneshot_write,
15512Sigor@sysoev.ru     nxt_kqueue_enable_accept,
15612Sigor@sysoev.ru     nxt_kqueue_enable_file,
15712Sigor@sysoev.ru     nxt_kqueue_close_file,
15812Sigor@sysoev.ru #if (NXT_HAVE_EVFILT_USER)
15912Sigor@sysoev.ru     nxt_kqueue_enable_post,
16012Sigor@sysoev.ru     nxt_kqueue_signal,
16112Sigor@sysoev.ru #else
16212Sigor@sysoev.ru     NULL,
16312Sigor@sysoev.ru     NULL,
16412Sigor@sysoev.ru #endif
16512Sigor@sysoev.ru     nxt_kqueue_poll,
16612Sigor@sysoev.ru 
16762Sigor@sysoev.ru     &nxt_kqueue_conn_io,
16812Sigor@sysoev.ru 
16912Sigor@sysoev.ru     NXT_FILE_EVENTS,
17012Sigor@sysoev.ru     NXT_SIGNAL_EVENTS,
17112Sigor@sysoev.ru };
17212Sigor@sysoev.ru 
17312Sigor@sysoev.ru 
17412Sigor@sysoev.ru static nxt_int_t
17512Sigor@sysoev.ru nxt_kqueue_create(nxt_event_engine_t *engine, nxt_uint_t mchanges,
17612Sigor@sysoev.ru     nxt_uint_t mevents)
17712Sigor@sysoev.ru {
17812Sigor@sysoev.ru     const nxt_sig_event_t  *sigev;
17912Sigor@sysoev.ru 
18012Sigor@sysoev.ru     engine->u.kqueue.fd = -1;
18112Sigor@sysoev.ru     engine->u.kqueue.mchanges = mchanges;
18212Sigor@sysoev.ru     engine->u.kqueue.mevents = mevents;
18312Sigor@sysoev.ru     engine->u.kqueue.pid = nxt_pid;
18412Sigor@sysoev.ru 
18512Sigor@sysoev.ru     engine->u.kqueue.changes = nxt_malloc(sizeof(struct kevent) * mchanges);
18612Sigor@sysoev.ru     if (engine->u.kqueue.changes == NULL) {
18712Sigor@sysoev.ru         goto fail;
18812Sigor@sysoev.ru     }
18912Sigor@sysoev.ru 
19012Sigor@sysoev.ru     engine->u.kqueue.events = nxt_malloc(sizeof(struct kevent) * mevents);
19112Sigor@sysoev.ru     if (engine->u.kqueue.events == NULL) {
19212Sigor@sysoev.ru         goto fail;
19312Sigor@sysoev.ru     }
19412Sigor@sysoev.ru 
19512Sigor@sysoev.ru     engine->u.kqueue.fd = kqueue();
19612Sigor@sysoev.ru     if (engine->u.kqueue.fd == -1) {
19712Sigor@sysoev.ru         nxt_log(&engine->task, NXT_LOG_CRIT, "kqueue() failed %E", nxt_errno);
19812Sigor@sysoev.ru         goto fail;
19912Sigor@sysoev.ru     }
20012Sigor@sysoev.ru 
20112Sigor@sysoev.ru     nxt_debug(&engine->task, "kqueue(): %d", engine->u.kqueue.fd);
20212Sigor@sysoev.ru 
20312Sigor@sysoev.ru     if (engine->signals != NULL) {
20412Sigor@sysoev.ru         for (sigev = engine->signals->sigev; sigev->signo != 0; sigev++) {
20512Sigor@sysoev.ru             if (nxt_kqueue_add_signal(engine, sigev) != NXT_OK) {
20612Sigor@sysoev.ru                 goto fail;
20712Sigor@sysoev.ru             }
20812Sigor@sysoev.ru         }
20912Sigor@sysoev.ru     }
21012Sigor@sysoev.ru 
21112Sigor@sysoev.ru     return NXT_OK;
21212Sigor@sysoev.ru 
21312Sigor@sysoev.ru fail:
21412Sigor@sysoev.ru 
21512Sigor@sysoev.ru     nxt_kqueue_free(engine);
21612Sigor@sysoev.ru 
21712Sigor@sysoev.ru     return NXT_ERROR;
21812Sigor@sysoev.ru }
21912Sigor@sysoev.ru 
22012Sigor@sysoev.ru 
22112Sigor@sysoev.ru static void
22212Sigor@sysoev.ru nxt_kqueue_free(nxt_event_engine_t *engine)
22312Sigor@sysoev.ru {
22412Sigor@sysoev.ru     nxt_fd_t  fd;
22512Sigor@sysoev.ru 
22612Sigor@sysoev.ru     fd = engine->u.kqueue.fd;
22712Sigor@sysoev.ru 
22812Sigor@sysoev.ru     nxt_debug(&engine->task, "kqueue %d free", fd);
22912Sigor@sysoev.ru 
23012Sigor@sysoev.ru     if (fd != -1 && engine->u.kqueue.pid == nxt_pid) {
23112Sigor@sysoev.ru         /* kqueue is not inherited by fork() */
23212Sigor@sysoev.ru 
23312Sigor@sysoev.ru         if (close(fd) != 0) {
23412Sigor@sysoev.ru             nxt_log(&engine->task, NXT_LOG_CRIT, "kqueue close(%d) failed %E",
23512Sigor@sysoev.ru                     fd, nxt_errno);
23612Sigor@sysoev.ru         }
23712Sigor@sysoev.ru     }
23812Sigor@sysoev.ru 
23912Sigor@sysoev.ru     nxt_free(engine->u.kqueue.events);
24012Sigor@sysoev.ru     nxt_free(engine->u.kqueue.changes);
24112Sigor@sysoev.ru 
24212Sigor@sysoev.ru     nxt_memzero(&engine->u.kqueue, sizeof(nxt_kqueue_engine_t));
24312Sigor@sysoev.ru }
24412Sigor@sysoev.ru 
24512Sigor@sysoev.ru 
24612Sigor@sysoev.ru static void
24712Sigor@sysoev.ru nxt_kqueue_enable(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
24812Sigor@sysoev.ru {
24912Sigor@sysoev.ru     nxt_kqueue_enable_read(engine, ev);
25012Sigor@sysoev.ru     nxt_kqueue_enable_write(engine, ev);
25112Sigor@sysoev.ru }
25212Sigor@sysoev.ru 
25312Sigor@sysoev.ru 
25412Sigor@sysoev.ru /*
25512Sigor@sysoev.ru  * EV_DISABLE is better because it eliminates in-kernel memory
25612Sigor@sysoev.ru  * deallocation and probable subsequent allocation with a lock acquiring.
25712Sigor@sysoev.ru  */
25812Sigor@sysoev.ru 
25912Sigor@sysoev.ru static void
26012Sigor@sysoev.ru nxt_kqueue_disable(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
26112Sigor@sysoev.ru {
26212Sigor@sysoev.ru     if (ev->read != NXT_EVENT_INACTIVE) {
26312Sigor@sysoev.ru         ev->read = NXT_EVENT_INACTIVE;
26412Sigor@sysoev.ru         nxt_kqueue_fd_set(engine, ev, EVFILT_READ, EV_DISABLE);
26512Sigor@sysoev.ru     }
26612Sigor@sysoev.ru 
26712Sigor@sysoev.ru     if (ev->write != NXT_EVENT_INACTIVE) {
26812Sigor@sysoev.ru         ev->write = NXT_EVENT_INACTIVE;
26912Sigor@sysoev.ru         nxt_kqueue_fd_set(engine, ev, EVFILT_WRITE, EV_DISABLE);
27012Sigor@sysoev.ru     }
27112Sigor@sysoev.ru }
27212Sigor@sysoev.ru 
27312Sigor@sysoev.ru 
27412Sigor@sysoev.ru static void
27512Sigor@sysoev.ru nxt_kqueue_delete(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
27612Sigor@sysoev.ru {
27712Sigor@sysoev.ru     if (ev->read != NXT_EVENT_INACTIVE) {
27812Sigor@sysoev.ru         ev->read = NXT_EVENT_INACTIVE;
27912Sigor@sysoev.ru         nxt_kqueue_fd_set(engine, ev, EVFILT_READ, EV_DELETE);
28012Sigor@sysoev.ru     }
28112Sigor@sysoev.ru 
28212Sigor@sysoev.ru     if (ev->write != NXT_EVENT_INACTIVE) {
28312Sigor@sysoev.ru         ev->write = NXT_EVENT_INACTIVE;
28412Sigor@sysoev.ru         nxt_kqueue_fd_set(engine, ev, EVFILT_WRITE, EV_DELETE);
28512Sigor@sysoev.ru     }
28612Sigor@sysoev.ru }
28712Sigor@sysoev.ru 
28812Sigor@sysoev.ru 
28912Sigor@sysoev.ru /*
29012Sigor@sysoev.ru  * kqueue(2):
29112Sigor@sysoev.ru  *
29212Sigor@sysoev.ru  *   Calling close() on a file descriptor will remove any kevents that
29312Sigor@sysoev.ru  *   reference the descriptor.
29412Sigor@sysoev.ru  *
29555Sigor@sysoev.ru  * So nxt_kqueue_close() returns true only if there are pending events.
29612Sigor@sysoev.ru  */
29712Sigor@sysoev.ru 
29812Sigor@sysoev.ru static nxt_bool_t
29912Sigor@sysoev.ru nxt_kqueue_close(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
30012Sigor@sysoev.ru {
30155Sigor@sysoev.ru     struct kevent  *kev, *end;
30255Sigor@sysoev.ru 
30312Sigor@sysoev.ru     ev->read = NXT_EVENT_INACTIVE;
30412Sigor@sysoev.ru     ev->write = NXT_EVENT_INACTIVE;
30512Sigor@sysoev.ru 
30655Sigor@sysoev.ru     end = &engine->u.kqueue.changes[engine->u.kqueue.nchanges];
30755Sigor@sysoev.ru 
30855Sigor@sysoev.ru     for (kev = engine->u.kqueue.changes; kev < end; kev++) {
30955Sigor@sysoev.ru         if (kev->ident == (uintptr_t) ev->fd) {
31055Sigor@sysoev.ru             return 1;
31155Sigor@sysoev.ru         }
31255Sigor@sysoev.ru     }
31355Sigor@sysoev.ru 
31455Sigor@sysoev.ru     return 0;
31512Sigor@sysoev.ru }
31612Sigor@sysoev.ru 
31712Sigor@sysoev.ru 
31812Sigor@sysoev.ru /*
31912Sigor@sysoev.ru  * The kqueue event engine uses only three states: inactive, blocked, and
32012Sigor@sysoev.ru  * active.  An active oneshot event is marked as it is in the default
32112Sigor@sysoev.ru  * state.  The event will be converted eventually to the default EV_CLEAR
32212Sigor@sysoev.ru  * mode after it will become inactive after delivery.
32312Sigor@sysoev.ru  */
32412Sigor@sysoev.ru 
32512Sigor@sysoev.ru static void
32612Sigor@sysoev.ru nxt_kqueue_enable_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
32712Sigor@sysoev.ru {
32812Sigor@sysoev.ru     if (ev->read == NXT_EVENT_INACTIVE) {
32912Sigor@sysoev.ru         nxt_kqueue_fd_set(engine, ev, EVFILT_READ,
33012Sigor@sysoev.ru                           EV_ADD | EV_ENABLE | EV_CLEAR);
33112Sigor@sysoev.ru     }
33212Sigor@sysoev.ru 
33312Sigor@sysoev.ru     ev->read = NXT_EVENT_ACTIVE;
33412Sigor@sysoev.ru }
33512Sigor@sysoev.ru 
33612Sigor@sysoev.ru 
33712Sigor@sysoev.ru static void
33812Sigor@sysoev.ru nxt_kqueue_enable_write(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
33912Sigor@sysoev.ru {
34012Sigor@sysoev.ru     if (ev->write == NXT_EVENT_INACTIVE) {
34112Sigor@sysoev.ru         nxt_kqueue_fd_set(engine, ev, EVFILT_WRITE,
34212Sigor@sysoev.ru                           EV_ADD | EV_ENABLE | EV_CLEAR);
34312Sigor@sysoev.ru     }
34412Sigor@sysoev.ru 
34512Sigor@sysoev.ru     ev->write = NXT_EVENT_ACTIVE;
34612Sigor@sysoev.ru }
34712Sigor@sysoev.ru 
34812Sigor@sysoev.ru 
34912Sigor@sysoev.ru static void
35012Sigor@sysoev.ru nxt_kqueue_disable_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
35112Sigor@sysoev.ru {
35212Sigor@sysoev.ru     ev->read = NXT_EVENT_INACTIVE;
35312Sigor@sysoev.ru 
35412Sigor@sysoev.ru     nxt_kqueue_fd_set(engine, ev, EVFILT_READ, EV_DISABLE);
35512Sigor@sysoev.ru }
35612Sigor@sysoev.ru 
35712Sigor@sysoev.ru 
35812Sigor@sysoev.ru static void
35912Sigor@sysoev.ru nxt_kqueue_disable_write(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
36012Sigor@sysoev.ru {
36112Sigor@sysoev.ru     ev->write = NXT_EVENT_INACTIVE;
36212Sigor@sysoev.ru 
36312Sigor@sysoev.ru     nxt_kqueue_fd_set(engine, ev, EVFILT_WRITE, EV_DISABLE);
36412Sigor@sysoev.ru }
36512Sigor@sysoev.ru 
36612Sigor@sysoev.ru 
36712Sigor@sysoev.ru static void
36812Sigor@sysoev.ru nxt_kqueue_block_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
36912Sigor@sysoev.ru {
37012Sigor@sysoev.ru     if (ev->read != NXT_EVENT_INACTIVE) {
37112Sigor@sysoev.ru         ev->read = NXT_EVENT_BLOCKED;
37212Sigor@sysoev.ru     }
37312Sigor@sysoev.ru }
37412Sigor@sysoev.ru 
37512Sigor@sysoev.ru 
37612Sigor@sysoev.ru static void
37712Sigor@sysoev.ru nxt_kqueue_block_write(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
37812Sigor@sysoev.ru {
37912Sigor@sysoev.ru     if (ev->write != NXT_EVENT_INACTIVE) {
38012Sigor@sysoev.ru         ev->write = NXT_EVENT_BLOCKED;
38112Sigor@sysoev.ru     }
38212Sigor@sysoev.ru }
38312Sigor@sysoev.ru 
38412Sigor@sysoev.ru 
38512Sigor@sysoev.ru static void
38612Sigor@sysoev.ru nxt_kqueue_oneshot_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
38712Sigor@sysoev.ru {
38812Sigor@sysoev.ru     ev->write = NXT_EVENT_ACTIVE;
38912Sigor@sysoev.ru 
39012Sigor@sysoev.ru     nxt_kqueue_fd_set(engine, ev, EVFILT_WRITE,
39112Sigor@sysoev.ru                       EV_ADD | EV_ENABLE | NXT_KEVENT_ONESHOT);
39212Sigor@sysoev.ru }
39312Sigor@sysoev.ru 
39412Sigor@sysoev.ru 
39512Sigor@sysoev.ru static void
39612Sigor@sysoev.ru nxt_kqueue_oneshot_write(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
39712Sigor@sysoev.ru {
39812Sigor@sysoev.ru     ev->write = NXT_EVENT_ACTIVE;
39912Sigor@sysoev.ru 
40012Sigor@sysoev.ru     nxt_kqueue_fd_set(engine, ev, EVFILT_WRITE,
40112Sigor@sysoev.ru                       EV_ADD | EV_ENABLE | NXT_KEVENT_ONESHOT);
40212Sigor@sysoev.ru }
40312Sigor@sysoev.ru 
40412Sigor@sysoev.ru 
40512Sigor@sysoev.ru static void
40612Sigor@sysoev.ru nxt_kqueue_enable_accept(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
40712Sigor@sysoev.ru {
40812Sigor@sysoev.ru     ev->read = NXT_EVENT_ACTIVE;
40912Sigor@sysoev.ru     ev->read_handler = nxt_kqueue_listen_handler;
41012Sigor@sysoev.ru 
41112Sigor@sysoev.ru     nxt_kqueue_fd_set(engine, ev, EVFILT_READ, EV_ADD | EV_ENABLE);
41212Sigor@sysoev.ru }
41312Sigor@sysoev.ru 
41412Sigor@sysoev.ru 
41512Sigor@sysoev.ru static void
41662Sigor@sysoev.ru nxt_kqueue_enable_file(nxt_event_engine_t *engine, nxt_file_event_t *ev)
41712Sigor@sysoev.ru {
41812Sigor@sysoev.ru     struct kevent  *kev;
41912Sigor@sysoev.ru 
42012Sigor@sysoev.ru     const nxt_int_t   flags = EV_ADD | EV_ENABLE | EV_ONESHOT;
42112Sigor@sysoev.ru     const nxt_uint_t  fflags = NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND
42212Sigor@sysoev.ru                                | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE;
42312Sigor@sysoev.ru 
42412Sigor@sysoev.ru     nxt_debug(&engine->task, "kevent(%d) set: id:%d ft:%i fl:%04Xd, ff:%04XuD",
42512Sigor@sysoev.ru               engine->u.kqueue.fd, ev->file->fd, EVFILT_VNODE, flags, fflags);
42612Sigor@sysoev.ru 
42712Sigor@sysoev.ru     kev = nxt_kqueue_get_kevent(engine);
42812Sigor@sysoev.ru 
42912Sigor@sysoev.ru     kev->ident = ev->file->fd;
43012Sigor@sysoev.ru     kev->filter = EVFILT_VNODE;
43112Sigor@sysoev.ru     kev->flags = flags;
43212Sigor@sysoev.ru     kev->fflags = fflags;
43312Sigor@sysoev.ru     kev->data = 0;
43412Sigor@sysoev.ru     kev->udata = nxt_kevent_set_udata(ev);
43512Sigor@sysoev.ru }
43612Sigor@sysoev.ru 
43712Sigor@sysoev.ru 
43812Sigor@sysoev.ru static void
43962Sigor@sysoev.ru nxt_kqueue_close_file(nxt_event_engine_t *engine, nxt_file_event_t *ev)
44012Sigor@sysoev.ru {
44112Sigor@sysoev.ru     /* TODO: pending event. */
44212Sigor@sysoev.ru }
44312Sigor@sysoev.ru 
44412Sigor@sysoev.ru 
44512Sigor@sysoev.ru static void
44612Sigor@sysoev.ru nxt_kqueue_fd_set(nxt_event_engine_t *engine, nxt_fd_event_t *ev,
44712Sigor@sysoev.ru     nxt_int_t filter, nxt_uint_t flags)
44812Sigor@sysoev.ru {
44912Sigor@sysoev.ru     struct kevent  *kev;
45012Sigor@sysoev.ru 
45112Sigor@sysoev.ru     nxt_debug(ev->task, "kevent(%d) set event: id:%d ft:%i fl:%04Xui",
45212Sigor@sysoev.ru               engine->u.kqueue.fd, ev->fd, filter, flags);
45312Sigor@sysoev.ru 
45412Sigor@sysoev.ru     kev = nxt_kqueue_get_kevent(engine);
45512Sigor@sysoev.ru 
45612Sigor@sysoev.ru     kev->ident = ev->fd;
45712Sigor@sysoev.ru     kev->filter = filter;
45812Sigor@sysoev.ru     kev->flags = flags;
45912Sigor@sysoev.ru     kev->fflags = 0;
46012Sigor@sysoev.ru     kev->data = 0;
46112Sigor@sysoev.ru     kev->udata = nxt_kevent_set_udata(ev);
46212Sigor@sysoev.ru }
46312Sigor@sysoev.ru 
46412Sigor@sysoev.ru 
46512Sigor@sysoev.ru static struct kevent *
46612Sigor@sysoev.ru nxt_kqueue_get_kevent(nxt_event_engine_t *engine)
46712Sigor@sysoev.ru {
46812Sigor@sysoev.ru     int  ret, nchanges;
46912Sigor@sysoev.ru 
47012Sigor@sysoev.ru     nchanges = engine->u.kqueue.nchanges;
47112Sigor@sysoev.ru 
47212Sigor@sysoev.ru     if (nxt_slow_path(nchanges >= engine->u.kqueue.mchanges)) {
47312Sigor@sysoev.ru 
47412Sigor@sysoev.ru         nxt_debug(&engine->task, "kevent(%d) changes:%d",
47512Sigor@sysoev.ru                   engine->u.kqueue.fd, nchanges);
47612Sigor@sysoev.ru 
47712Sigor@sysoev.ru         ret = kevent(engine->u.kqueue.fd, engine->u.kqueue.changes, nchanges,
47812Sigor@sysoev.ru                      NULL, 0, NULL);
47912Sigor@sysoev.ru 
48012Sigor@sysoev.ru         if (nxt_slow_path(ret != 0)) {
48112Sigor@sysoev.ru             nxt_log(&engine->task, NXT_LOG_CRIT, "kevent(%d) failed %E",
48212Sigor@sysoev.ru                     engine->u.kqueue.fd, nxt_errno);
48312Sigor@sysoev.ru 
48412Sigor@sysoev.ru             nxt_kqueue_error(engine);
48512Sigor@sysoev.ru         }
48612Sigor@sysoev.ru 
48712Sigor@sysoev.ru         engine->u.kqueue.nchanges = 0;
48812Sigor@sysoev.ru     }
48912Sigor@sysoev.ru 
49012Sigor@sysoev.ru     return &engine->u.kqueue.changes[engine->u.kqueue.nchanges++];
49112Sigor@sysoev.ru }
49212Sigor@sysoev.ru 
49312Sigor@sysoev.ru 
49412Sigor@sysoev.ru static void
49512Sigor@sysoev.ru nxt_kqueue_error(nxt_event_engine_t *engine)
49612Sigor@sysoev.ru {
49712Sigor@sysoev.ru     struct kevent     *kev, *end;
49812Sigor@sysoev.ru     nxt_fd_event_t    *ev;
49962Sigor@sysoev.ru     nxt_file_event_t  *fev;
50012Sigor@sysoev.ru     nxt_work_queue_t  *wq;
50112Sigor@sysoev.ru 
50212Sigor@sysoev.ru     wq = &engine->fast_work_queue;
50312Sigor@sysoev.ru     end = &engine->u.kqueue.changes[engine->u.kqueue.nchanges];
50412Sigor@sysoev.ru 
50512Sigor@sysoev.ru     for (kev = engine->u.kqueue.changes; kev < end; kev++) {
50612Sigor@sysoev.ru 
50712Sigor@sysoev.ru         switch (kev->filter) {
50812Sigor@sysoev.ru 
50912Sigor@sysoev.ru         case EVFILT_READ:
51012Sigor@sysoev.ru         case EVFILT_WRITE:
51112Sigor@sysoev.ru             ev = nxt_kevent_get_udata(kev->udata);
51212Sigor@sysoev.ru             nxt_work_queue_add(wq, nxt_kqueue_fd_error_handler,
51312Sigor@sysoev.ru                                ev->task, ev, ev->data);
51412Sigor@sysoev.ru             break;
51512Sigor@sysoev.ru 
51612Sigor@sysoev.ru         case EVFILT_VNODE:
51712Sigor@sysoev.ru             fev = nxt_kevent_get_udata(kev->udata);
51812Sigor@sysoev.ru             nxt_work_queue_add(wq, nxt_kqueue_file_error_handler,
51912Sigor@sysoev.ru                                fev->task, fev, fev->data);
52012Sigor@sysoev.ru             break;
52112Sigor@sysoev.ru         }
52212Sigor@sysoev.ru     }
52312Sigor@sysoev.ru }
52412Sigor@sysoev.ru 
52512Sigor@sysoev.ru 
52612Sigor@sysoev.ru static void
52712Sigor@sysoev.ru nxt_kqueue_fd_error_handler(nxt_task_t *task, void *obj, void *data)
52812Sigor@sysoev.ru {
52912Sigor@sysoev.ru     nxt_fd_event_t  *ev;
53012Sigor@sysoev.ru 
53112Sigor@sysoev.ru     ev = obj;
53212Sigor@sysoev.ru 
53353Sigor@sysoev.ru     nxt_debug(task, "kqueue fd error handler fd:%d", ev->fd);
53453Sigor@sysoev.ru 
53512Sigor@sysoev.ru     if (ev->kq_eof && ev->kq_errno != 0) {
53612Sigor@sysoev.ru         ev->error = ev->kq_errno;
53713Sigor@sysoev.ru         nxt_log(task, nxt_socket_error_level(ev->kq_errno),
53812Sigor@sysoev.ru                 "kevent() reported error on descriptor %d %E",
53912Sigor@sysoev.ru                 ev->fd, ev->kq_errno);
54012Sigor@sysoev.ru     }
54112Sigor@sysoev.ru 
54212Sigor@sysoev.ru     ev->read = NXT_EVENT_INACTIVE;
54312Sigor@sysoev.ru     ev->write = NXT_EVENT_INACTIVE;
54412Sigor@sysoev.ru     ev->error = ev->kq_errno;
54512Sigor@sysoev.ru 
54612Sigor@sysoev.ru     ev->error_handler(task, ev, data);
54712Sigor@sysoev.ru }
54812Sigor@sysoev.ru 
54912Sigor@sysoev.ru 
55012Sigor@sysoev.ru static void
55112Sigor@sysoev.ru nxt_kqueue_file_error_handler(nxt_task_t *task, void *obj, void *data)
55212Sigor@sysoev.ru {
55362Sigor@sysoev.ru     nxt_file_event_t  *ev;
55412Sigor@sysoev.ru 
55512Sigor@sysoev.ru     ev = obj;
55612Sigor@sysoev.ru 
55753Sigor@sysoev.ru     nxt_debug(task, "kqueue file error handler fd:%d", ev->file->fd);
55853Sigor@sysoev.ru 
55912Sigor@sysoev.ru     ev->handler(task, ev, data);
56012Sigor@sysoev.ru }
56112Sigor@sysoev.ru 
56212Sigor@sysoev.ru 
56312Sigor@sysoev.ru static nxt_int_t
56412Sigor@sysoev.ru nxt_kqueue_add_signal(nxt_event_engine_t *engine, const nxt_sig_event_t *sigev)
56512Sigor@sysoev.ru {
56612Sigor@sysoev.ru     int               signo;
56712Sigor@sysoev.ru     struct kevent     kev;
56812Sigor@sysoev.ru     struct sigaction  sa;
56912Sigor@sysoev.ru 
57012Sigor@sysoev.ru     signo = sigev->signo;
57112Sigor@sysoev.ru 
57212Sigor@sysoev.ru     nxt_memzero(&sa, sizeof(struct sigaction));
57312Sigor@sysoev.ru     sigemptyset(&sa.sa_mask);
57412Sigor@sysoev.ru 
57512Sigor@sysoev.ru     /*
57612Sigor@sysoev.ru      * SIGCHLD must not be set to SIG_IGN, since kqueue cannot catch
57712Sigor@sysoev.ru      * this signal.  It should be set to SIG_DFL instead.  And although
57812Sigor@sysoev.ru      * SIGCHLD default action is also ignoring, nevertheless SIG_DFL
57912Sigor@sysoev.ru      * allows kqueue to catch the signal.
58012Sigor@sysoev.ru      */
58112Sigor@sysoev.ru     sa.sa_handler = (signo == SIGCHLD) ? SIG_DFL : SIG_IGN;
58212Sigor@sysoev.ru 
58312Sigor@sysoev.ru     if (sigaction(signo, &sa, NULL) != 0) {
58412Sigor@sysoev.ru         nxt_log(&engine->task, NXT_LOG_CRIT, "sigaction(%d) failed %E",
58512Sigor@sysoev.ru                 signo, nxt_errno);
58612Sigor@sysoev.ru 
58712Sigor@sysoev.ru         return NXT_ERROR;
58812Sigor@sysoev.ru     }
58912Sigor@sysoev.ru 
59012Sigor@sysoev.ru     nxt_debug(&engine->task, "kevent(%d) signo:%d (%s)",
59112Sigor@sysoev.ru               engine->u.kqueue.fd, signo, sigev->name);
59212Sigor@sysoev.ru 
59312Sigor@sysoev.ru     kev.ident = signo;
59412Sigor@sysoev.ru     kev.filter = EVFILT_SIGNAL;
59512Sigor@sysoev.ru     kev.flags = EV_ADD;
59612Sigor@sysoev.ru     kev.fflags = 0;
59712Sigor@sysoev.ru     kev.data = 0;
59812Sigor@sysoev.ru     kev.udata = nxt_kevent_set_udata(sigev);
59912Sigor@sysoev.ru 
60012Sigor@sysoev.ru     if (kevent(engine->u.kqueue.fd, &kev, 1, NULL, 0, NULL) == 0) {
60112Sigor@sysoev.ru         return NXT_OK;
60212Sigor@sysoev.ru     }
60312Sigor@sysoev.ru 
60412Sigor@sysoev.ru     nxt_log(&engine->task, NXT_LOG_CRIT, "kevent(%d) failed %E",
60512Sigor@sysoev.ru             kqueue, nxt_errno);
60612Sigor@sysoev.ru 
60712Sigor@sysoev.ru     return NXT_ERROR;
60812Sigor@sysoev.ru }
60912Sigor@sysoev.ru 
61012Sigor@sysoev.ru 
61112Sigor@sysoev.ru #if (NXT_HAVE_EVFILT_USER)
61212Sigor@sysoev.ru 
61312Sigor@sysoev.ru static nxt_int_t
61412Sigor@sysoev.ru nxt_kqueue_enable_post(nxt_event_engine_t *engine, nxt_work_handler_t handler)
61512Sigor@sysoev.ru {
61612Sigor@sysoev.ru     struct kevent  kev;
61712Sigor@sysoev.ru 
61812Sigor@sysoev.ru     /* EVFILT_USER must be added to a kqueue before it can be triggered. */
61912Sigor@sysoev.ru 
62012Sigor@sysoev.ru     kev.ident = 0;
62112Sigor@sysoev.ru     kev.filter = EVFILT_USER;
62212Sigor@sysoev.ru     kev.flags = EV_ADD | EV_CLEAR;
62312Sigor@sysoev.ru     kev.fflags = 0;
62412Sigor@sysoev.ru     kev.data = 0;
62512Sigor@sysoev.ru     kev.udata = NULL;
62612Sigor@sysoev.ru 
62712Sigor@sysoev.ru     engine->u.kqueue.post_handler = handler;
62812Sigor@sysoev.ru 
62912Sigor@sysoev.ru     if (kevent(engine->u.kqueue.fd, &kev, 1, NULL, 0, NULL) == 0) {
63012Sigor@sysoev.ru         return NXT_OK;
63112Sigor@sysoev.ru     }
63212Sigor@sysoev.ru 
63312Sigor@sysoev.ru     nxt_log(&engine->task, NXT_LOG_CRIT, "kevent(%d) failed %E",
63412Sigor@sysoev.ru             engine->u.kqueue.fd, nxt_errno);
63512Sigor@sysoev.ru 
63612Sigor@sysoev.ru     return NXT_ERROR;
63712Sigor@sysoev.ru }
63812Sigor@sysoev.ru 
63912Sigor@sysoev.ru 
64012Sigor@sysoev.ru static void
64112Sigor@sysoev.ru nxt_kqueue_signal(nxt_event_engine_t *engine, nxt_uint_t signo)
64212Sigor@sysoev.ru {
64312Sigor@sysoev.ru     struct kevent  kev;
64412Sigor@sysoev.ru 
64512Sigor@sysoev.ru     /*
64612Sigor@sysoev.ru      * kqueue has a builtin signal processing support, so the function
64712Sigor@sysoev.ru      * is used only to post events and the signo argument is ignored.
64812Sigor@sysoev.ru      */
64912Sigor@sysoev.ru 
65012Sigor@sysoev.ru     kev.ident = 0;
65112Sigor@sysoev.ru     kev.filter = EVFILT_USER;
65212Sigor@sysoev.ru     kev.flags = 0;
65312Sigor@sysoev.ru     kev.fflags = NOTE_TRIGGER;
65412Sigor@sysoev.ru     kev.data = 0;
65512Sigor@sysoev.ru     kev.udata = NULL;
65612Sigor@sysoev.ru 
65712Sigor@sysoev.ru     if (kevent(engine->u.kqueue.fd, &kev, 1, NULL, 0, NULL) != 0) {
65812Sigor@sysoev.ru         nxt_log(&engine->task, NXT_LOG_CRIT, "kevent(%d) failed %E",
65912Sigor@sysoev.ru                 engine->u.kqueue.fd, nxt_errno);
66012Sigor@sysoev.ru     }
66112Sigor@sysoev.ru }
66212Sigor@sysoev.ru 
66312Sigor@sysoev.ru #endif
66412Sigor@sysoev.ru 
66512Sigor@sysoev.ru 
66612Sigor@sysoev.ru static void
66712Sigor@sysoev.ru nxt_kqueue_poll(nxt_event_engine_t *engine, nxt_msec_t timeout)
66812Sigor@sysoev.ru {
66912Sigor@sysoev.ru     int                 nevents;
67012Sigor@sysoev.ru     void                *obj, *data;
67112Sigor@sysoev.ru     nxt_int_t           i;
67212Sigor@sysoev.ru     nxt_err_t           err;
67312Sigor@sysoev.ru     nxt_uint_t          level;
67412Sigor@sysoev.ru     nxt_bool_t          error, eof;
67512Sigor@sysoev.ru     nxt_task_t          *task;
67612Sigor@sysoev.ru     struct kevent       *kev;
67712Sigor@sysoev.ru     nxt_fd_event_t      *ev;
67812Sigor@sysoev.ru     nxt_sig_event_t     *sigev;
67912Sigor@sysoev.ru     struct timespec     ts, *tp;
68062Sigor@sysoev.ru     nxt_file_event_t    *fev;
68112Sigor@sysoev.ru     nxt_work_queue_t    *wq;
68212Sigor@sysoev.ru     nxt_work_handler_t  handler;
68312Sigor@sysoev.ru 
68412Sigor@sysoev.ru     if (timeout == NXT_INFINITE_MSEC) {
68512Sigor@sysoev.ru         tp = NULL;
68612Sigor@sysoev.ru 
68712Sigor@sysoev.ru     } else {
68812Sigor@sysoev.ru         ts.tv_sec = timeout / 1000;
68912Sigor@sysoev.ru         ts.tv_nsec = (timeout % 1000) * 1000000;
69012Sigor@sysoev.ru         tp = &ts;
69112Sigor@sysoev.ru     }
69212Sigor@sysoev.ru 
69312Sigor@sysoev.ru     nxt_debug(&engine->task, "kevent(%d) changes:%d timeout:%M",
69412Sigor@sysoev.ru               engine->u.kqueue.fd, engine->u.kqueue.nchanges, timeout);
69512Sigor@sysoev.ru 
69612Sigor@sysoev.ru     nevents = kevent(engine->u.kqueue.fd,
69712Sigor@sysoev.ru                      engine->u.kqueue.changes, engine->u.kqueue.nchanges,
69812Sigor@sysoev.ru                      engine->u.kqueue.events, engine->u.kqueue.mevents, tp);
69912Sigor@sysoev.ru 
70012Sigor@sysoev.ru     err = (nevents == -1) ? nxt_errno : 0;
70112Sigor@sysoev.ru 
70212Sigor@sysoev.ru     nxt_thread_time_update(engine->task.thread);
70312Sigor@sysoev.ru 
70412Sigor@sysoev.ru     nxt_debug(&engine->task, "kevent(%d): %d", engine->u.kqueue.fd, nevents);
70512Sigor@sysoev.ru 
70612Sigor@sysoev.ru     if (nevents == -1) {
70712Sigor@sysoev.ru         level = (err == NXT_EINTR) ? NXT_LOG_INFO : NXT_LOG_CRIT;
70812Sigor@sysoev.ru 
70912Sigor@sysoev.ru         nxt_log(&engine->task, level, "kevent(%d) failed %E",
71012Sigor@sysoev.ru                 engine->u.kqueue.fd, err);
71112Sigor@sysoev.ru 
71212Sigor@sysoev.ru         nxt_kqueue_error(engine);
71312Sigor@sysoev.ru         return;
71412Sigor@sysoev.ru     }
71512Sigor@sysoev.ru 
71612Sigor@sysoev.ru     engine->u.kqueue.nchanges = 0;
71712Sigor@sysoev.ru 
71812Sigor@sysoev.ru     for (i = 0; i < nevents; i++) {
71912Sigor@sysoev.ru 
72012Sigor@sysoev.ru         kev = &engine->u.kqueue.events[i];
72112Sigor@sysoev.ru 
72212Sigor@sysoev.ru         nxt_debug(&engine->task,
72312Sigor@sysoev.ru                   (kev->ident > 0x8000000 && kev->ident != (uintptr_t) -1) ?
72412Sigor@sysoev.ru                       "kevent: id:%p ft:%d fl:%04Xd ff:%d d:%d ud:%p":
72512Sigor@sysoev.ru                       "kevent: id:%d ft:%d fl:%04Xd ff:%d d:%d ud:%p",
72612Sigor@sysoev.ru                   kev->ident, kev->filter, kev->flags, kev->fflags,
72712Sigor@sysoev.ru                   kev->data, kev->udata);
72812Sigor@sysoev.ru 
72912Sigor@sysoev.ru         error = (kev->flags & EV_ERROR);
73012Sigor@sysoev.ru 
73112Sigor@sysoev.ru         if (nxt_slow_path(error)) {
73212Sigor@sysoev.ru             nxt_log(&engine->task, NXT_LOG_CRIT,
73312Sigor@sysoev.ru                     "kevent(%d) error %E on ident:%d filter:%d",
73412Sigor@sysoev.ru                     engine->u.kqueue.fd, kev->data, kev->ident, kev->filter);
73512Sigor@sysoev.ru         }
73612Sigor@sysoev.ru 
73712Sigor@sysoev.ru         task = &engine->task;
73812Sigor@sysoev.ru         wq = &engine->fast_work_queue;
73912Sigor@sysoev.ru         handler = nxt_kqueue_fd_error_handler;
74012Sigor@sysoev.ru         obj = nxt_kevent_get_udata(kev->udata);
74112Sigor@sysoev.ru 
74212Sigor@sysoev.ru         switch (kev->filter) {
74312Sigor@sysoev.ru 
74412Sigor@sysoev.ru         case EVFILT_READ:
74512Sigor@sysoev.ru             ev = obj;
74612Sigor@sysoev.ru             ev->read_ready = 1;
74712Sigor@sysoev.ru             ev->kq_available = (int32_t) kev->data;
74812Sigor@sysoev.ru             err = kev->fflags;
74912Sigor@sysoev.ru             eof = (kev->flags & EV_EOF) != 0;
75012Sigor@sysoev.ru             ev->kq_errno = err;
75112Sigor@sysoev.ru             ev->kq_eof = eof;
75212Sigor@sysoev.ru 
75320Sigor@sysoev.ru             if (ev->read <= NXT_EVENT_BLOCKED) {
75412Sigor@sysoev.ru                 nxt_debug(ev->task, "blocked read event fd:%d", ev->fd);
75512Sigor@sysoev.ru                 continue;
75612Sigor@sysoev.ru             }
75712Sigor@sysoev.ru 
75812Sigor@sysoev.ru             if ((kev->flags & NXT_KEVENT_ONESHOT) != 0) {
75912Sigor@sysoev.ru                 ev->read = NXT_EVENT_INACTIVE;
76012Sigor@sysoev.ru             }
76112Sigor@sysoev.ru 
76212Sigor@sysoev.ru             if (nxt_slow_path(ev->kq_available == 0 && eof && err != 0)) {
76312Sigor@sysoev.ru                 error = 1;
76412Sigor@sysoev.ru             }
76512Sigor@sysoev.ru 
76612Sigor@sysoev.ru             if (nxt_fast_path(!error)) {
76712Sigor@sysoev.ru                 handler = ev->read_handler;
76812Sigor@sysoev.ru                 wq = ev->read_work_queue;
76912Sigor@sysoev.ru             }
77012Sigor@sysoev.ru 
77112Sigor@sysoev.ru             task = ev->task;
77212Sigor@sysoev.ru             data = ev->data;
77312Sigor@sysoev.ru 
77412Sigor@sysoev.ru             break;
77512Sigor@sysoev.ru 
77612Sigor@sysoev.ru         case EVFILT_WRITE:
77712Sigor@sysoev.ru             ev = obj;
77812Sigor@sysoev.ru             ev->write_ready = 1;
77912Sigor@sysoev.ru             err = kev->fflags;
78012Sigor@sysoev.ru             eof = (kev->flags & EV_EOF) != 0;
78112Sigor@sysoev.ru             ev->kq_errno = err;
78212Sigor@sysoev.ru             ev->kq_eof = eof;
78312Sigor@sysoev.ru 
78420Sigor@sysoev.ru             if (ev->write <= NXT_EVENT_BLOCKED) {
78512Sigor@sysoev.ru                 nxt_debug(ev->task, "blocked write event fd:%d", ev->fd);
78612Sigor@sysoev.ru                 continue;
78712Sigor@sysoev.ru             }
78812Sigor@sysoev.ru 
78912Sigor@sysoev.ru             if ((kev->flags & NXT_KEVENT_ONESHOT) != 0) {
79012Sigor@sysoev.ru                 ev->write = NXT_EVENT_INACTIVE;
79112Sigor@sysoev.ru             }
79212Sigor@sysoev.ru 
79312Sigor@sysoev.ru             if (nxt_slow_path(eof && err != 0)) {
79412Sigor@sysoev.ru                 error = 1;
79512Sigor@sysoev.ru             }
79612Sigor@sysoev.ru 
79712Sigor@sysoev.ru             if (nxt_fast_path(!error)) {
79812Sigor@sysoev.ru                 handler = ev->write_handler;
79912Sigor@sysoev.ru                 wq = ev->write_work_queue;
80012Sigor@sysoev.ru             }
80112Sigor@sysoev.ru 
80212Sigor@sysoev.ru             task = ev->task;
80312Sigor@sysoev.ru             data = ev->data;
80412Sigor@sysoev.ru 
80512Sigor@sysoev.ru             break;
80612Sigor@sysoev.ru 
80712Sigor@sysoev.ru         case EVFILT_VNODE:
80812Sigor@sysoev.ru             fev = obj;
80912Sigor@sysoev.ru             handler = fev->handler;
81012Sigor@sysoev.ru             task = fev->task;
81112Sigor@sysoev.ru             data = fev->data;
81212Sigor@sysoev.ru             break;
81312Sigor@sysoev.ru 
81412Sigor@sysoev.ru         case EVFILT_SIGNAL:
81512Sigor@sysoev.ru             sigev = obj;
81612Sigor@sysoev.ru             obj = (void *) kev->ident;
81712Sigor@sysoev.ru             handler = sigev->handler;
81812Sigor@sysoev.ru             data = (void *) sigev->name;
81912Sigor@sysoev.ru             break;
82012Sigor@sysoev.ru 
82112Sigor@sysoev.ru #if (NXT_HAVE_EVFILT_USER)
82212Sigor@sysoev.ru 
82312Sigor@sysoev.ru         case EVFILT_USER:
82412Sigor@sysoev.ru             handler = engine->u.kqueue.post_handler;
82512Sigor@sysoev.ru             data = NULL;
82612Sigor@sysoev.ru             break;
82712Sigor@sysoev.ru 
82812Sigor@sysoev.ru #endif
82912Sigor@sysoev.ru 
83012Sigor@sysoev.ru         default:
83112Sigor@sysoev.ru 
83212Sigor@sysoev.ru #if (NXT_DEBUG)
83312Sigor@sysoev.ru             nxt_log(&engine->task, NXT_LOG_CRIT,
83412Sigor@sysoev.ru                     "unexpected kevent(%d) filter %d on ident %d",
83512Sigor@sysoev.ru                     engine->u.kqueue.fd, kev->filter, kev->ident);
83612Sigor@sysoev.ru #endif
83712Sigor@sysoev.ru 
83812Sigor@sysoev.ru             continue;
83912Sigor@sysoev.ru         }
84012Sigor@sysoev.ru 
84112Sigor@sysoev.ru         nxt_work_queue_add(wq, handler, task, obj, data);
84212Sigor@sysoev.ru     }
84312Sigor@sysoev.ru }
84412Sigor@sysoev.ru 
84512Sigor@sysoev.ru 
84612Sigor@sysoev.ru /*
84712Sigor@sysoev.ru  * nxt_kqueue_event_conn_io_connect() eliminates the
84812Sigor@sysoev.ru  * getsockopt() syscall to test pending connect() error.
84912Sigor@sysoev.ru  */
85012Sigor@sysoev.ru 
85112Sigor@sysoev.ru static void
85262Sigor@sysoev.ru nxt_kqueue_conn_io_connect(nxt_task_t *task, void *obj, void *data)
85312Sigor@sysoev.ru {
85462Sigor@sysoev.ru     nxt_conn_t                    *c;
85512Sigor@sysoev.ru     nxt_event_engine_t            *engine;
85612Sigor@sysoev.ru     nxt_work_handler_t            handler;
85712Sigor@sysoev.ru     const nxt_event_conn_state_t  *state;
85812Sigor@sysoev.ru 
85912Sigor@sysoev.ru     c = obj;
86012Sigor@sysoev.ru 
86112Sigor@sysoev.ru     state = c->write_state;
86212Sigor@sysoev.ru 
86313Sigor@sysoev.ru     switch (nxt_socket_connect(task, c->socket.fd, c->remote) ){
86412Sigor@sysoev.ru 
86512Sigor@sysoev.ru     case NXT_OK:
86612Sigor@sysoev.ru         c->socket.write_ready = 1;
86712Sigor@sysoev.ru         handler = state->ready_handler;
86812Sigor@sysoev.ru         break;
86912Sigor@sysoev.ru 
87012Sigor@sysoev.ru     case NXT_AGAIN:
87162Sigor@sysoev.ru         c->socket.write_handler = nxt_kqueue_conn_connected;
87262Sigor@sysoev.ru         c->socket.error_handler = nxt_conn_connect_error;
87312Sigor@sysoev.ru 
87412Sigor@sysoev.ru         engine = task->thread->engine;
87562Sigor@sysoev.ru         nxt_conn_timer(engine, c, state, &c->write_timer);
87612Sigor@sysoev.ru 
87712Sigor@sysoev.ru         nxt_kqueue_enable_write(engine, &c->socket);
87812Sigor@sysoev.ru         return;
87912Sigor@sysoev.ru 
88012Sigor@sysoev.ru     case NXT_DECLINED:
88112Sigor@sysoev.ru         handler = state->close_handler;
88212Sigor@sysoev.ru         break;
88312Sigor@sysoev.ru 
88412Sigor@sysoev.ru     default: /* NXT_ERROR */
88512Sigor@sysoev.ru         handler = state->error_handler;
88612Sigor@sysoev.ru         break;
88712Sigor@sysoev.ru     }
88812Sigor@sysoev.ru 
88913Sigor@sysoev.ru     nxt_work_queue_add(c->write_work_queue, handler, task, c, data);
89012Sigor@sysoev.ru }
89112Sigor@sysoev.ru 
89212Sigor@sysoev.ru 
89312Sigor@sysoev.ru static void
89462Sigor@sysoev.ru nxt_kqueue_conn_connected(nxt_task_t *task, void *obj, void *data)
89512Sigor@sysoev.ru {
89662Sigor@sysoev.ru     nxt_conn_t  *c;
89712Sigor@sysoev.ru 
89812Sigor@sysoev.ru     c = obj;
89912Sigor@sysoev.ru 
90062Sigor@sysoev.ru     nxt_debug(task, "kqueue conn connected fd:%d", c->socket.fd);
90112Sigor@sysoev.ru 
90212Sigor@sysoev.ru     c->socket.write = NXT_EVENT_BLOCKED;
90312Sigor@sysoev.ru 
90456Sigor@sysoev.ru     if (c->write_state->timer_autoreset) {
90512Sigor@sysoev.ru         nxt_timer_disable(task->thread->engine, &c->write_timer);
90612Sigor@sysoev.ru     }
90712Sigor@sysoev.ru 
90812Sigor@sysoev.ru     nxt_work_queue_add(c->write_work_queue, c->write_state->ready_handler,
90912Sigor@sysoev.ru                        task, c, data);
91012Sigor@sysoev.ru }
91112Sigor@sysoev.ru 
91212Sigor@sysoev.ru 
91312Sigor@sysoev.ru static void
91412Sigor@sysoev.ru nxt_kqueue_listen_handler(nxt_task_t *task, void *obj, void *data)
91512Sigor@sysoev.ru {
91662Sigor@sysoev.ru     nxt_listen_event_t  *lev;
91712Sigor@sysoev.ru 
91862Sigor@sysoev.ru     lev = obj;
91912Sigor@sysoev.ru 
92012Sigor@sysoev.ru     nxt_debug(task, "kevent fd:%d avail:%D",
92162Sigor@sysoev.ru               lev->socket.fd, lev->socket.kq_available);
92212Sigor@sysoev.ru 
92362Sigor@sysoev.ru     lev->ready = nxt_min(lev->batch, (uint32_t) lev->socket.kq_available);
92412Sigor@sysoev.ru 
92562Sigor@sysoev.ru     nxt_kqueue_conn_io_accept(task, lev, data);
92612Sigor@sysoev.ru }
92712Sigor@sysoev.ru 
92812Sigor@sysoev.ru 
92912Sigor@sysoev.ru static void
93062Sigor@sysoev.ru nxt_kqueue_conn_io_accept(nxt_task_t *task, void *obj, void *data)
93112Sigor@sysoev.ru {
932*312Sigor@sysoev.ru     socklen_t           socklen;
93362Sigor@sysoev.ru     nxt_conn_t          *c;
93462Sigor@sysoev.ru     nxt_socket_t        s;
93562Sigor@sysoev.ru     struct sockaddr     *sa;
93662Sigor@sysoev.ru     nxt_listen_event_t  *lev;
93712Sigor@sysoev.ru 
93862Sigor@sysoev.ru     lev = obj;
93962Sigor@sysoev.ru     c = lev->next;
94012Sigor@sysoev.ru 
94162Sigor@sysoev.ru     lev->ready--;
94262Sigor@sysoev.ru     lev->socket.read_ready = (lev->ready != 0);
94312Sigor@sysoev.ru 
94462Sigor@sysoev.ru     lev->socket.kq_available--;
94562Sigor@sysoev.ru     lev->socket.read_ready = (lev->socket.kq_available != 0);
94612Sigor@sysoev.ru 
947*312Sigor@sysoev.ru     sa = &c->remote->u.sockaddr;
948*312Sigor@sysoev.ru     socklen = c->remote->socklen;
949*312Sigor@sysoev.ru     /*
950*312Sigor@sysoev.ru      * The returned socklen is ignored here,
951*312Sigor@sysoev.ru      * see comment in nxt_conn_io_accept().
952*312Sigor@sysoev.ru      */
953*312Sigor@sysoev.ru     s = accept(lev->socket.fd, sa, &socklen);
95412Sigor@sysoev.ru 
95512Sigor@sysoev.ru     if (s != -1) {
95612Sigor@sysoev.ru         c->socket.fd = s;
95712Sigor@sysoev.ru 
95862Sigor@sysoev.ru         nxt_debug(task, "accept(%d): %d", lev->socket.fd, s);
95912Sigor@sysoev.ru 
96062Sigor@sysoev.ru         nxt_conn_accept(task, lev, c);
96112Sigor@sysoev.ru         return;
96212Sigor@sysoev.ru     }
96312Sigor@sysoev.ru 
96462Sigor@sysoev.ru     nxt_conn_accept_error(task, lev, "accept", nxt_errno);
96512Sigor@sysoev.ru }
96612Sigor@sysoev.ru 
96712Sigor@sysoev.ru 
96812Sigor@sysoev.ru /*
96962Sigor@sysoev.ru  * nxt_kqueue_conn_io_read() is just a wrapper to eliminate the
97012Sigor@sysoev.ru  * readv() or recv() syscall if a remote side just closed connection.
97112Sigor@sysoev.ru  */
97212Sigor@sysoev.ru 
97312Sigor@sysoev.ru static void
97462Sigor@sysoev.ru nxt_kqueue_conn_io_read(nxt_task_t *task, void *obj, void *data)
97512Sigor@sysoev.ru {
97662Sigor@sysoev.ru     nxt_conn_t  *c;
97712Sigor@sysoev.ru 
97812Sigor@sysoev.ru     c = obj;
97912Sigor@sysoev.ru 
98062Sigor@sysoev.ru     nxt_debug(task, "kqueue conn read fd:%d", c->socket.fd);
98112Sigor@sysoev.ru 
98212Sigor@sysoev.ru     if (c->socket.kq_available == 0 && c->socket.kq_eof) {
98312Sigor@sysoev.ru         nxt_debug(task, "kevent fd:%d eof", c->socket.fd);
98412Sigor@sysoev.ru 
98512Sigor@sysoev.ru         c->socket.closed = 1;
98612Sigor@sysoev.ru         nxt_work_queue_add(c->read_work_queue, c->read_state->close_handler,
98712Sigor@sysoev.ru                            task, c, data);
98812Sigor@sysoev.ru         return;
98912Sigor@sysoev.ru     }
99012Sigor@sysoev.ru 
99162Sigor@sysoev.ru     nxt_conn_io_read(task, c, data);
99212Sigor@sysoev.ru }
99312Sigor@sysoev.ru 
99412Sigor@sysoev.ru 
99512Sigor@sysoev.ru /*
99662Sigor@sysoev.ru  * nxt_kqueue_conn_io_recvbuf() is just wrapper around standard
99762Sigor@sysoev.ru  * nxt_conn_io_recvbuf() to eliminate the readv() or recv() syscalls
99812Sigor@sysoev.ru  * if there is no pending data or a remote side closed connection.
99912Sigor@sysoev.ru  */
100012Sigor@sysoev.ru 
100112Sigor@sysoev.ru static ssize_t
100262Sigor@sysoev.ru nxt_kqueue_conn_io_recvbuf(nxt_conn_t *c, nxt_buf_t *b)
100312Sigor@sysoev.ru {
100412Sigor@sysoev.ru     ssize_t  n;
100512Sigor@sysoev.ru 
100612Sigor@sysoev.ru     if (c->socket.kq_available == 0 && c->socket.kq_eof) {
100712Sigor@sysoev.ru         c->socket.closed = 1;
100812Sigor@sysoev.ru         return 0;
100912Sigor@sysoev.ru     }
101012Sigor@sysoev.ru 
101162Sigor@sysoev.ru     n = nxt_conn_io_recvbuf(c, b);
101212Sigor@sysoev.ru 
101312Sigor@sysoev.ru     if (n > 0) {
101412Sigor@sysoev.ru         c->socket.kq_available -= n;
101512Sigor@sysoev.ru 
101612Sigor@sysoev.ru         if (c->socket.kq_available < 0) {
101712Sigor@sysoev.ru             c->socket.kq_available = 0;
101812Sigor@sysoev.ru         }
101912Sigor@sysoev.ru 
102012Sigor@sysoev.ru         nxt_debug(c->socket.task, "kevent fd:%d avail:%D eof:%d",
102112Sigor@sysoev.ru                   c->socket.fd, c->socket.kq_available, c->socket.kq_eof);
102212Sigor@sysoev.ru 
102312Sigor@sysoev.ru         c->socket.read_ready = (c->socket.kq_available != 0
102412Sigor@sysoev.ru                                 || c->socket.kq_eof);
102512Sigor@sysoev.ru     }
102612Sigor@sysoev.ru 
102712Sigor@sysoev.ru     return n;
102812Sigor@sysoev.ru }
1029