xref: /unit/src/nxt_kqueue_engine.c (revision 1008)
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 = {
114771Sigor@sysoev.ru     .connect = nxt_kqueue_conn_io_connect,
115771Sigor@sysoev.ru     .accept = nxt_kqueue_conn_io_accept,
11612Sigor@sysoev.ru 
117771Sigor@sysoev.ru     .read = nxt_kqueue_conn_io_read,
118771Sigor@sysoev.ru     .recvbuf = nxt_kqueue_conn_io_recvbuf,
119771Sigor@sysoev.ru     .recv = nxt_conn_io_recv,
12012Sigor@sysoev.ru 
121771Sigor@sysoev.ru     .write = nxt_conn_io_write,
122771Sigor@sysoev.ru     .sendbuf = nxt_conn_io_sendbuf,
12312Sigor@sysoev.ru 
12412Sigor@sysoev.ru #if (NXT_HAVE_FREEBSD_SENDFILE)
125771Sigor@sysoev.ru     .old_sendbuf = nxt_freebsd_event_conn_io_sendfile,
12612Sigor@sysoev.ru #elif (NXT_HAVE_MACOSX_SENDFILE)
127771Sigor@sysoev.ru     .old_sendbuf = nxt_macosx_event_conn_io_sendfile,
12812Sigor@sysoev.ru #else
129771Sigor@sysoev.ru     .old_sendbuf = nxt_event_conn_io_sendbuf,
13012Sigor@sysoev.ru #endif
13112Sigor@sysoev.ru 
132771Sigor@sysoev.ru     .writev = nxt_event_conn_io_writev,
133771Sigor@sysoev.ru     .send = nxt_event_conn_io_send,
13412Sigor@sysoev.ru };
13512Sigor@sysoev.ru 
13612Sigor@sysoev.ru 
13712Sigor@sysoev.ru const nxt_event_interface_t  nxt_kqueue_engine = {
13812Sigor@sysoev.ru     "kqueue",
13912Sigor@sysoev.ru     nxt_kqueue_create,
14012Sigor@sysoev.ru     nxt_kqueue_free,
14112Sigor@sysoev.ru     nxt_kqueue_enable,
14212Sigor@sysoev.ru     nxt_kqueue_disable,
14312Sigor@sysoev.ru     nxt_kqueue_delete,
14412Sigor@sysoev.ru     nxt_kqueue_close,
14512Sigor@sysoev.ru     nxt_kqueue_enable_read,
14612Sigor@sysoev.ru     nxt_kqueue_enable_write,
14712Sigor@sysoev.ru     nxt_kqueue_disable_read,
14812Sigor@sysoev.ru     nxt_kqueue_disable_write,
14912Sigor@sysoev.ru     nxt_kqueue_block_read,
15012Sigor@sysoev.ru     nxt_kqueue_block_write,
15112Sigor@sysoev.ru     nxt_kqueue_oneshot_read,
15212Sigor@sysoev.ru     nxt_kqueue_oneshot_write,
15312Sigor@sysoev.ru     nxt_kqueue_enable_accept,
15412Sigor@sysoev.ru     nxt_kqueue_enable_file,
15512Sigor@sysoev.ru     nxt_kqueue_close_file,
15612Sigor@sysoev.ru #if (NXT_HAVE_EVFILT_USER)
15712Sigor@sysoev.ru     nxt_kqueue_enable_post,
15812Sigor@sysoev.ru     nxt_kqueue_signal,
15912Sigor@sysoev.ru #else
16012Sigor@sysoev.ru     NULL,
16112Sigor@sysoev.ru     NULL,
16212Sigor@sysoev.ru #endif
16312Sigor@sysoev.ru     nxt_kqueue_poll,
16412Sigor@sysoev.ru 
16562Sigor@sysoev.ru     &nxt_kqueue_conn_io,
16612Sigor@sysoev.ru 
16712Sigor@sysoev.ru     NXT_FILE_EVENTS,
16812Sigor@sysoev.ru     NXT_SIGNAL_EVENTS,
16912Sigor@sysoev.ru };
17012Sigor@sysoev.ru 
17112Sigor@sysoev.ru 
17212Sigor@sysoev.ru static nxt_int_t
17312Sigor@sysoev.ru nxt_kqueue_create(nxt_event_engine_t *engine, nxt_uint_t mchanges,
17412Sigor@sysoev.ru     nxt_uint_t mevents)
17512Sigor@sysoev.ru {
17612Sigor@sysoev.ru     const nxt_sig_event_t  *sigev;
17712Sigor@sysoev.ru 
17812Sigor@sysoev.ru     engine->u.kqueue.fd = -1;
17912Sigor@sysoev.ru     engine->u.kqueue.mchanges = mchanges;
18012Sigor@sysoev.ru     engine->u.kqueue.mevents = mevents;
18112Sigor@sysoev.ru     engine->u.kqueue.pid = nxt_pid;
18212Sigor@sysoev.ru 
18312Sigor@sysoev.ru     engine->u.kqueue.changes = nxt_malloc(sizeof(struct kevent) * mchanges);
18412Sigor@sysoev.ru     if (engine->u.kqueue.changes == NULL) {
18512Sigor@sysoev.ru         goto fail;
18612Sigor@sysoev.ru     }
18712Sigor@sysoev.ru 
18812Sigor@sysoev.ru     engine->u.kqueue.events = nxt_malloc(sizeof(struct kevent) * mevents);
18912Sigor@sysoev.ru     if (engine->u.kqueue.events == NULL) {
19012Sigor@sysoev.ru         goto fail;
19112Sigor@sysoev.ru     }
19212Sigor@sysoev.ru 
19312Sigor@sysoev.ru     engine->u.kqueue.fd = kqueue();
19412Sigor@sysoev.ru     if (engine->u.kqueue.fd == -1) {
195564Svbart@nginx.com         nxt_alert(&engine->task, "kqueue() failed %E", nxt_errno);
19612Sigor@sysoev.ru         goto fail;
19712Sigor@sysoev.ru     }
19812Sigor@sysoev.ru 
19912Sigor@sysoev.ru     nxt_debug(&engine->task, "kqueue(): %d", engine->u.kqueue.fd);
20012Sigor@sysoev.ru 
20112Sigor@sysoev.ru     if (engine->signals != NULL) {
20212Sigor@sysoev.ru         for (sigev = engine->signals->sigev; sigev->signo != 0; sigev++) {
20312Sigor@sysoev.ru             if (nxt_kqueue_add_signal(engine, sigev) != NXT_OK) {
20412Sigor@sysoev.ru                 goto fail;
20512Sigor@sysoev.ru             }
20612Sigor@sysoev.ru         }
20712Sigor@sysoev.ru     }
20812Sigor@sysoev.ru 
20912Sigor@sysoev.ru     return NXT_OK;
21012Sigor@sysoev.ru 
21112Sigor@sysoev.ru fail:
21212Sigor@sysoev.ru 
21312Sigor@sysoev.ru     nxt_kqueue_free(engine);
21412Sigor@sysoev.ru 
21512Sigor@sysoev.ru     return NXT_ERROR;
21612Sigor@sysoev.ru }
21712Sigor@sysoev.ru 
21812Sigor@sysoev.ru 
21912Sigor@sysoev.ru static void
22012Sigor@sysoev.ru nxt_kqueue_free(nxt_event_engine_t *engine)
22112Sigor@sysoev.ru {
22212Sigor@sysoev.ru     nxt_fd_t  fd;
22312Sigor@sysoev.ru 
22412Sigor@sysoev.ru     fd = engine->u.kqueue.fd;
22512Sigor@sysoev.ru 
22612Sigor@sysoev.ru     nxt_debug(&engine->task, "kqueue %d free", fd);
22712Sigor@sysoev.ru 
22812Sigor@sysoev.ru     if (fd != -1 && engine->u.kqueue.pid == nxt_pid) {
22912Sigor@sysoev.ru         /* kqueue is not inherited by fork() */
23012Sigor@sysoev.ru 
23112Sigor@sysoev.ru         if (close(fd) != 0) {
232564Svbart@nginx.com             nxt_alert(&engine->task, "kqueue close(%d) failed %E",
233564Svbart@nginx.com                       fd, nxt_errno);
23412Sigor@sysoev.ru         }
23512Sigor@sysoev.ru     }
23612Sigor@sysoev.ru 
23712Sigor@sysoev.ru     nxt_free(engine->u.kqueue.events);
23812Sigor@sysoev.ru     nxt_free(engine->u.kqueue.changes);
23912Sigor@sysoev.ru 
24012Sigor@sysoev.ru     nxt_memzero(&engine->u.kqueue, sizeof(nxt_kqueue_engine_t));
24112Sigor@sysoev.ru }
24212Sigor@sysoev.ru 
24312Sigor@sysoev.ru 
24412Sigor@sysoev.ru static void
24512Sigor@sysoev.ru nxt_kqueue_enable(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
24612Sigor@sysoev.ru {
24712Sigor@sysoev.ru     nxt_kqueue_enable_read(engine, ev);
24812Sigor@sysoev.ru     nxt_kqueue_enable_write(engine, ev);
24912Sigor@sysoev.ru }
25012Sigor@sysoev.ru 
25112Sigor@sysoev.ru 
25212Sigor@sysoev.ru /*
25312Sigor@sysoev.ru  * EV_DISABLE is better because it eliminates in-kernel memory
25412Sigor@sysoev.ru  * deallocation and probable subsequent allocation with a lock acquiring.
25512Sigor@sysoev.ru  */
25612Sigor@sysoev.ru 
25712Sigor@sysoev.ru static void
25812Sigor@sysoev.ru nxt_kqueue_disable(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
25912Sigor@sysoev.ru {
26012Sigor@sysoev.ru     if (ev->read != NXT_EVENT_INACTIVE) {
26112Sigor@sysoev.ru         ev->read = NXT_EVENT_INACTIVE;
26212Sigor@sysoev.ru         nxt_kqueue_fd_set(engine, ev, EVFILT_READ, EV_DISABLE);
26312Sigor@sysoev.ru     }
26412Sigor@sysoev.ru 
26512Sigor@sysoev.ru     if (ev->write != NXT_EVENT_INACTIVE) {
26612Sigor@sysoev.ru         ev->write = NXT_EVENT_INACTIVE;
26712Sigor@sysoev.ru         nxt_kqueue_fd_set(engine, ev, EVFILT_WRITE, EV_DISABLE);
26812Sigor@sysoev.ru     }
26912Sigor@sysoev.ru }
27012Sigor@sysoev.ru 
27112Sigor@sysoev.ru 
27212Sigor@sysoev.ru static void
27312Sigor@sysoev.ru nxt_kqueue_delete(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
27412Sigor@sysoev.ru {
27512Sigor@sysoev.ru     if (ev->read != NXT_EVENT_INACTIVE) {
27612Sigor@sysoev.ru         ev->read = NXT_EVENT_INACTIVE;
27712Sigor@sysoev.ru         nxt_kqueue_fd_set(engine, ev, EVFILT_READ, EV_DELETE);
27812Sigor@sysoev.ru     }
27912Sigor@sysoev.ru 
28012Sigor@sysoev.ru     if (ev->write != NXT_EVENT_INACTIVE) {
28112Sigor@sysoev.ru         ev->write = NXT_EVENT_INACTIVE;
28212Sigor@sysoev.ru         nxt_kqueue_fd_set(engine, ev, EVFILT_WRITE, EV_DELETE);
28312Sigor@sysoev.ru     }
28412Sigor@sysoev.ru }
28512Sigor@sysoev.ru 
28612Sigor@sysoev.ru 
28712Sigor@sysoev.ru /*
28812Sigor@sysoev.ru  * kqueue(2):
28912Sigor@sysoev.ru  *
29012Sigor@sysoev.ru  *   Calling close() on a file descriptor will remove any kevents that
29112Sigor@sysoev.ru  *   reference the descriptor.
29212Sigor@sysoev.ru  *
29355Sigor@sysoev.ru  * So nxt_kqueue_close() returns true only if there are pending events.
29412Sigor@sysoev.ru  */
29512Sigor@sysoev.ru 
29612Sigor@sysoev.ru static nxt_bool_t
29712Sigor@sysoev.ru nxt_kqueue_close(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
29812Sigor@sysoev.ru {
29955Sigor@sysoev.ru     struct kevent  *kev, *end;
30055Sigor@sysoev.ru 
30112Sigor@sysoev.ru     ev->read = NXT_EVENT_INACTIVE;
30212Sigor@sysoev.ru     ev->write = NXT_EVENT_INACTIVE;
30312Sigor@sysoev.ru 
30455Sigor@sysoev.ru     end = &engine->u.kqueue.changes[engine->u.kqueue.nchanges];
30555Sigor@sysoev.ru 
30655Sigor@sysoev.ru     for (kev = engine->u.kqueue.changes; kev < end; kev++) {
30755Sigor@sysoev.ru         if (kev->ident == (uintptr_t) ev->fd) {
30855Sigor@sysoev.ru             return 1;
30955Sigor@sysoev.ru         }
31055Sigor@sysoev.ru     }
31155Sigor@sysoev.ru 
31255Sigor@sysoev.ru     return 0;
31312Sigor@sysoev.ru }
31412Sigor@sysoev.ru 
31512Sigor@sysoev.ru 
31612Sigor@sysoev.ru /*
31712Sigor@sysoev.ru  * The kqueue event engine uses only three states: inactive, blocked, and
31812Sigor@sysoev.ru  * active.  An active oneshot event is marked as it is in the default
31912Sigor@sysoev.ru  * state.  The event will be converted eventually to the default EV_CLEAR
32012Sigor@sysoev.ru  * mode after it will become inactive after delivery.
32112Sigor@sysoev.ru  */
32212Sigor@sysoev.ru 
32312Sigor@sysoev.ru static void
32412Sigor@sysoev.ru nxt_kqueue_enable_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
32512Sigor@sysoev.ru {
32612Sigor@sysoev.ru     if (ev->read == NXT_EVENT_INACTIVE) {
32712Sigor@sysoev.ru         nxt_kqueue_fd_set(engine, ev, EVFILT_READ,
32812Sigor@sysoev.ru                           EV_ADD | EV_ENABLE | EV_CLEAR);
32912Sigor@sysoev.ru     }
33012Sigor@sysoev.ru 
33112Sigor@sysoev.ru     ev->read = NXT_EVENT_ACTIVE;
33212Sigor@sysoev.ru }
33312Sigor@sysoev.ru 
33412Sigor@sysoev.ru 
33512Sigor@sysoev.ru static void
33612Sigor@sysoev.ru nxt_kqueue_enable_write(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
33712Sigor@sysoev.ru {
33812Sigor@sysoev.ru     if (ev->write == NXT_EVENT_INACTIVE) {
33912Sigor@sysoev.ru         nxt_kqueue_fd_set(engine, ev, EVFILT_WRITE,
34012Sigor@sysoev.ru                           EV_ADD | EV_ENABLE | EV_CLEAR);
34112Sigor@sysoev.ru     }
34212Sigor@sysoev.ru 
34312Sigor@sysoev.ru     ev->write = NXT_EVENT_ACTIVE;
34412Sigor@sysoev.ru }
34512Sigor@sysoev.ru 
34612Sigor@sysoev.ru 
34712Sigor@sysoev.ru static void
34812Sigor@sysoev.ru nxt_kqueue_disable_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
34912Sigor@sysoev.ru {
35012Sigor@sysoev.ru     ev->read = NXT_EVENT_INACTIVE;
35112Sigor@sysoev.ru 
35212Sigor@sysoev.ru     nxt_kqueue_fd_set(engine, ev, EVFILT_READ, EV_DISABLE);
35312Sigor@sysoev.ru }
35412Sigor@sysoev.ru 
35512Sigor@sysoev.ru 
35612Sigor@sysoev.ru static void
35712Sigor@sysoev.ru nxt_kqueue_disable_write(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
35812Sigor@sysoev.ru {
35912Sigor@sysoev.ru     ev->write = NXT_EVENT_INACTIVE;
36012Sigor@sysoev.ru 
36112Sigor@sysoev.ru     nxt_kqueue_fd_set(engine, ev, EVFILT_WRITE, EV_DISABLE);
36212Sigor@sysoev.ru }
36312Sigor@sysoev.ru 
36412Sigor@sysoev.ru 
36512Sigor@sysoev.ru static void
36612Sigor@sysoev.ru nxt_kqueue_block_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
36712Sigor@sysoev.ru {
36812Sigor@sysoev.ru     if (ev->read != NXT_EVENT_INACTIVE) {
36912Sigor@sysoev.ru         ev->read = NXT_EVENT_BLOCKED;
37012Sigor@sysoev.ru     }
37112Sigor@sysoev.ru }
37212Sigor@sysoev.ru 
37312Sigor@sysoev.ru 
37412Sigor@sysoev.ru static void
37512Sigor@sysoev.ru nxt_kqueue_block_write(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
37612Sigor@sysoev.ru {
37712Sigor@sysoev.ru     if (ev->write != NXT_EVENT_INACTIVE) {
37812Sigor@sysoev.ru         ev->write = NXT_EVENT_BLOCKED;
37912Sigor@sysoev.ru     }
38012Sigor@sysoev.ru }
38112Sigor@sysoev.ru 
38212Sigor@sysoev.ru 
38312Sigor@sysoev.ru static void
38412Sigor@sysoev.ru nxt_kqueue_oneshot_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
38512Sigor@sysoev.ru {
38612Sigor@sysoev.ru     ev->write = NXT_EVENT_ACTIVE;
38712Sigor@sysoev.ru 
38812Sigor@sysoev.ru     nxt_kqueue_fd_set(engine, ev, EVFILT_WRITE,
38912Sigor@sysoev.ru                       EV_ADD | EV_ENABLE | NXT_KEVENT_ONESHOT);
39012Sigor@sysoev.ru }
39112Sigor@sysoev.ru 
39212Sigor@sysoev.ru 
39312Sigor@sysoev.ru static void
39412Sigor@sysoev.ru nxt_kqueue_oneshot_write(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
39512Sigor@sysoev.ru {
39612Sigor@sysoev.ru     ev->write = NXT_EVENT_ACTIVE;
39712Sigor@sysoev.ru 
39812Sigor@sysoev.ru     nxt_kqueue_fd_set(engine, ev, EVFILT_WRITE,
39912Sigor@sysoev.ru                       EV_ADD | EV_ENABLE | NXT_KEVENT_ONESHOT);
40012Sigor@sysoev.ru }
40112Sigor@sysoev.ru 
40212Sigor@sysoev.ru 
40312Sigor@sysoev.ru static void
40412Sigor@sysoev.ru nxt_kqueue_enable_accept(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
40512Sigor@sysoev.ru {
40612Sigor@sysoev.ru     ev->read = NXT_EVENT_ACTIVE;
40712Sigor@sysoev.ru     ev->read_handler = nxt_kqueue_listen_handler;
40812Sigor@sysoev.ru 
40912Sigor@sysoev.ru     nxt_kqueue_fd_set(engine, ev, EVFILT_READ, EV_ADD | EV_ENABLE);
41012Sigor@sysoev.ru }
41112Sigor@sysoev.ru 
41212Sigor@sysoev.ru 
41312Sigor@sysoev.ru static void
41462Sigor@sysoev.ru nxt_kqueue_enable_file(nxt_event_engine_t *engine, nxt_file_event_t *ev)
41512Sigor@sysoev.ru {
41612Sigor@sysoev.ru     struct kevent  *kev;
41712Sigor@sysoev.ru 
41812Sigor@sysoev.ru     const nxt_int_t   flags = EV_ADD | EV_ENABLE | EV_ONESHOT;
41912Sigor@sysoev.ru     const nxt_uint_t  fflags = NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND
42012Sigor@sysoev.ru                                | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE;
42112Sigor@sysoev.ru 
42212Sigor@sysoev.ru     nxt_debug(&engine->task, "kevent(%d) set: id:%d ft:%i fl:%04Xd, ff:%04XuD",
42312Sigor@sysoev.ru               engine->u.kqueue.fd, ev->file->fd, EVFILT_VNODE, flags, fflags);
42412Sigor@sysoev.ru 
42512Sigor@sysoev.ru     kev = nxt_kqueue_get_kevent(engine);
42612Sigor@sysoev.ru 
42712Sigor@sysoev.ru     kev->ident = ev->file->fd;
42812Sigor@sysoev.ru     kev->filter = EVFILT_VNODE;
42912Sigor@sysoev.ru     kev->flags = flags;
43012Sigor@sysoev.ru     kev->fflags = fflags;
43112Sigor@sysoev.ru     kev->data = 0;
43212Sigor@sysoev.ru     kev->udata = nxt_kevent_set_udata(ev);
43312Sigor@sysoev.ru }
43412Sigor@sysoev.ru 
43512Sigor@sysoev.ru 
43612Sigor@sysoev.ru static void
43762Sigor@sysoev.ru nxt_kqueue_close_file(nxt_event_engine_t *engine, nxt_file_event_t *ev)
43812Sigor@sysoev.ru {
43912Sigor@sysoev.ru     /* TODO: pending event. */
44012Sigor@sysoev.ru }
44112Sigor@sysoev.ru 
44212Sigor@sysoev.ru 
44312Sigor@sysoev.ru static void
44412Sigor@sysoev.ru nxt_kqueue_fd_set(nxt_event_engine_t *engine, nxt_fd_event_t *ev,
44512Sigor@sysoev.ru     nxt_int_t filter, nxt_uint_t flags)
44612Sigor@sysoev.ru {
44712Sigor@sysoev.ru     struct kevent  *kev;
44812Sigor@sysoev.ru 
44912Sigor@sysoev.ru     nxt_debug(ev->task, "kevent(%d) set event: id:%d ft:%i fl:%04Xui",
45012Sigor@sysoev.ru               engine->u.kqueue.fd, ev->fd, filter, flags);
45112Sigor@sysoev.ru 
45212Sigor@sysoev.ru     kev = nxt_kqueue_get_kevent(engine);
45312Sigor@sysoev.ru 
45412Sigor@sysoev.ru     kev->ident = ev->fd;
45512Sigor@sysoev.ru     kev->filter = filter;
45612Sigor@sysoev.ru     kev->flags = flags;
45712Sigor@sysoev.ru     kev->fflags = 0;
45812Sigor@sysoev.ru     kev->data = 0;
45912Sigor@sysoev.ru     kev->udata = nxt_kevent_set_udata(ev);
46012Sigor@sysoev.ru }
46112Sigor@sysoev.ru 
46212Sigor@sysoev.ru 
46312Sigor@sysoev.ru static struct kevent *
46412Sigor@sysoev.ru nxt_kqueue_get_kevent(nxt_event_engine_t *engine)
46512Sigor@sysoev.ru {
46612Sigor@sysoev.ru     int  ret, nchanges;
46712Sigor@sysoev.ru 
46812Sigor@sysoev.ru     nchanges = engine->u.kqueue.nchanges;
46912Sigor@sysoev.ru 
47012Sigor@sysoev.ru     if (nxt_slow_path(nchanges >= engine->u.kqueue.mchanges)) {
47112Sigor@sysoev.ru 
47212Sigor@sysoev.ru         nxt_debug(&engine->task, "kevent(%d) changes:%d",
47312Sigor@sysoev.ru                   engine->u.kqueue.fd, nchanges);
47412Sigor@sysoev.ru 
47512Sigor@sysoev.ru         ret = kevent(engine->u.kqueue.fd, engine->u.kqueue.changes, nchanges,
47612Sigor@sysoev.ru                      NULL, 0, NULL);
47712Sigor@sysoev.ru 
47812Sigor@sysoev.ru         if (nxt_slow_path(ret != 0)) {
479564Svbart@nginx.com             nxt_alert(&engine->task, "kevent(%d) failed %E",
480564Svbart@nginx.com                       engine->u.kqueue.fd, nxt_errno);
48112Sigor@sysoev.ru 
48212Sigor@sysoev.ru             nxt_kqueue_error(engine);
48312Sigor@sysoev.ru         }
48412Sigor@sysoev.ru 
48512Sigor@sysoev.ru         engine->u.kqueue.nchanges = 0;
48612Sigor@sysoev.ru     }
48712Sigor@sysoev.ru 
48812Sigor@sysoev.ru     return &engine->u.kqueue.changes[engine->u.kqueue.nchanges++];
48912Sigor@sysoev.ru }
49012Sigor@sysoev.ru 
49112Sigor@sysoev.ru 
49212Sigor@sysoev.ru static void
49312Sigor@sysoev.ru nxt_kqueue_error(nxt_event_engine_t *engine)
49412Sigor@sysoev.ru {
49512Sigor@sysoev.ru     struct kevent     *kev, *end;
49612Sigor@sysoev.ru     nxt_fd_event_t    *ev;
49762Sigor@sysoev.ru     nxt_file_event_t  *fev;
49812Sigor@sysoev.ru     nxt_work_queue_t  *wq;
49912Sigor@sysoev.ru 
50012Sigor@sysoev.ru     wq = &engine->fast_work_queue;
50112Sigor@sysoev.ru     end = &engine->u.kqueue.changes[engine->u.kqueue.nchanges];
50212Sigor@sysoev.ru 
50312Sigor@sysoev.ru     for (kev = engine->u.kqueue.changes; kev < end; kev++) {
50412Sigor@sysoev.ru 
50512Sigor@sysoev.ru         switch (kev->filter) {
50612Sigor@sysoev.ru 
50712Sigor@sysoev.ru         case EVFILT_READ:
50812Sigor@sysoev.ru         case EVFILT_WRITE:
50912Sigor@sysoev.ru             ev = nxt_kevent_get_udata(kev->udata);
51012Sigor@sysoev.ru             nxt_work_queue_add(wq, nxt_kqueue_fd_error_handler,
51112Sigor@sysoev.ru                                ev->task, ev, ev->data);
51212Sigor@sysoev.ru             break;
51312Sigor@sysoev.ru 
51412Sigor@sysoev.ru         case EVFILT_VNODE:
51512Sigor@sysoev.ru             fev = nxt_kevent_get_udata(kev->udata);
51612Sigor@sysoev.ru             nxt_work_queue_add(wq, nxt_kqueue_file_error_handler,
51712Sigor@sysoev.ru                                fev->task, fev, fev->data);
51812Sigor@sysoev.ru             break;
51912Sigor@sysoev.ru         }
52012Sigor@sysoev.ru     }
52112Sigor@sysoev.ru }
52212Sigor@sysoev.ru 
52312Sigor@sysoev.ru 
52412Sigor@sysoev.ru static void
52512Sigor@sysoev.ru nxt_kqueue_fd_error_handler(nxt_task_t *task, void *obj, void *data)
52612Sigor@sysoev.ru {
52712Sigor@sysoev.ru     nxt_fd_event_t  *ev;
52812Sigor@sysoev.ru 
52912Sigor@sysoev.ru     ev = obj;
53012Sigor@sysoev.ru 
53153Sigor@sysoev.ru     nxt_debug(task, "kqueue fd error handler fd:%d", ev->fd);
53253Sigor@sysoev.ru 
53312Sigor@sysoev.ru     if (ev->kq_eof && ev->kq_errno != 0) {
53412Sigor@sysoev.ru         ev->error = ev->kq_errno;
53513Sigor@sysoev.ru         nxt_log(task, nxt_socket_error_level(ev->kq_errno),
53612Sigor@sysoev.ru                 "kevent() reported error on descriptor %d %E",
53712Sigor@sysoev.ru                 ev->fd, ev->kq_errno);
53812Sigor@sysoev.ru     }
53912Sigor@sysoev.ru 
54012Sigor@sysoev.ru     ev->read = NXT_EVENT_INACTIVE;
54112Sigor@sysoev.ru     ev->write = NXT_EVENT_INACTIVE;
54212Sigor@sysoev.ru     ev->error = ev->kq_errno;
54312Sigor@sysoev.ru 
54412Sigor@sysoev.ru     ev->error_handler(task, ev, data);
54512Sigor@sysoev.ru }
54612Sigor@sysoev.ru 
54712Sigor@sysoev.ru 
54812Sigor@sysoev.ru static void
54912Sigor@sysoev.ru nxt_kqueue_file_error_handler(nxt_task_t *task, void *obj, void *data)
55012Sigor@sysoev.ru {
55162Sigor@sysoev.ru     nxt_file_event_t  *ev;
55212Sigor@sysoev.ru 
55312Sigor@sysoev.ru     ev = obj;
55412Sigor@sysoev.ru 
55553Sigor@sysoev.ru     nxt_debug(task, "kqueue file error handler fd:%d", ev->file->fd);
55653Sigor@sysoev.ru 
55712Sigor@sysoev.ru     ev->handler(task, ev, data);
55812Sigor@sysoev.ru }
55912Sigor@sysoev.ru 
56012Sigor@sysoev.ru 
56112Sigor@sysoev.ru static nxt_int_t
56212Sigor@sysoev.ru nxt_kqueue_add_signal(nxt_event_engine_t *engine, const nxt_sig_event_t *sigev)
56312Sigor@sysoev.ru {
56412Sigor@sysoev.ru     int               signo;
56512Sigor@sysoev.ru     struct kevent     kev;
56612Sigor@sysoev.ru     struct sigaction  sa;
56712Sigor@sysoev.ru 
56812Sigor@sysoev.ru     signo = sigev->signo;
56912Sigor@sysoev.ru 
57012Sigor@sysoev.ru     nxt_memzero(&sa, sizeof(struct sigaction));
57112Sigor@sysoev.ru     sigemptyset(&sa.sa_mask);
57212Sigor@sysoev.ru 
57312Sigor@sysoev.ru     /*
57412Sigor@sysoev.ru      * SIGCHLD must not be set to SIG_IGN, since kqueue cannot catch
57512Sigor@sysoev.ru      * this signal.  It should be set to SIG_DFL instead.  And although
57612Sigor@sysoev.ru      * SIGCHLD default action is also ignoring, nevertheless SIG_DFL
57712Sigor@sysoev.ru      * allows kqueue to catch the signal.
57812Sigor@sysoev.ru      */
57912Sigor@sysoev.ru     sa.sa_handler = (signo == SIGCHLD) ? SIG_DFL : SIG_IGN;
58012Sigor@sysoev.ru 
58112Sigor@sysoev.ru     if (sigaction(signo, &sa, NULL) != 0) {
582564Svbart@nginx.com         nxt_alert(&engine->task, "sigaction(%d) failed %E", signo, nxt_errno);
58312Sigor@sysoev.ru 
58412Sigor@sysoev.ru         return NXT_ERROR;
58512Sigor@sysoev.ru     }
58612Sigor@sysoev.ru 
58712Sigor@sysoev.ru     nxt_debug(&engine->task, "kevent(%d) signo:%d (%s)",
58812Sigor@sysoev.ru               engine->u.kqueue.fd, signo, sigev->name);
58912Sigor@sysoev.ru 
59012Sigor@sysoev.ru     kev.ident = signo;
59112Sigor@sysoev.ru     kev.filter = EVFILT_SIGNAL;
59212Sigor@sysoev.ru     kev.flags = EV_ADD;
59312Sigor@sysoev.ru     kev.fflags = 0;
59412Sigor@sysoev.ru     kev.data = 0;
59512Sigor@sysoev.ru     kev.udata = nxt_kevent_set_udata(sigev);
59612Sigor@sysoev.ru 
59712Sigor@sysoev.ru     if (kevent(engine->u.kqueue.fd, &kev, 1, NULL, 0, NULL) == 0) {
59812Sigor@sysoev.ru         return NXT_OK;
59912Sigor@sysoev.ru     }
60012Sigor@sysoev.ru 
601564Svbart@nginx.com     nxt_alert(&engine->task, "kevent(%d) failed %E", kqueue, nxt_errno);
60212Sigor@sysoev.ru 
60312Sigor@sysoev.ru     return NXT_ERROR;
60412Sigor@sysoev.ru }
60512Sigor@sysoev.ru 
60612Sigor@sysoev.ru 
60712Sigor@sysoev.ru #if (NXT_HAVE_EVFILT_USER)
60812Sigor@sysoev.ru 
60912Sigor@sysoev.ru static nxt_int_t
61012Sigor@sysoev.ru nxt_kqueue_enable_post(nxt_event_engine_t *engine, nxt_work_handler_t handler)
61112Sigor@sysoev.ru {
61212Sigor@sysoev.ru     struct kevent  kev;
61312Sigor@sysoev.ru 
61412Sigor@sysoev.ru     /* EVFILT_USER must be added to a kqueue before it can be triggered. */
61512Sigor@sysoev.ru 
61612Sigor@sysoev.ru     kev.ident = 0;
61712Sigor@sysoev.ru     kev.filter = EVFILT_USER;
61812Sigor@sysoev.ru     kev.flags = EV_ADD | EV_CLEAR;
61912Sigor@sysoev.ru     kev.fflags = 0;
62012Sigor@sysoev.ru     kev.data = 0;
62112Sigor@sysoev.ru     kev.udata = NULL;
62212Sigor@sysoev.ru 
62312Sigor@sysoev.ru     engine->u.kqueue.post_handler = handler;
62412Sigor@sysoev.ru 
62512Sigor@sysoev.ru     if (kevent(engine->u.kqueue.fd, &kev, 1, NULL, 0, NULL) == 0) {
62612Sigor@sysoev.ru         return NXT_OK;
62712Sigor@sysoev.ru     }
62812Sigor@sysoev.ru 
629564Svbart@nginx.com     nxt_alert(&engine->task, "kevent(%d) failed %E",
630564Svbart@nginx.com               engine->u.kqueue.fd, nxt_errno);
63112Sigor@sysoev.ru 
63212Sigor@sysoev.ru     return NXT_ERROR;
63312Sigor@sysoev.ru }
63412Sigor@sysoev.ru 
63512Sigor@sysoev.ru 
63612Sigor@sysoev.ru static void
63712Sigor@sysoev.ru nxt_kqueue_signal(nxt_event_engine_t *engine, nxt_uint_t signo)
63812Sigor@sysoev.ru {
63912Sigor@sysoev.ru     struct kevent  kev;
64012Sigor@sysoev.ru 
64112Sigor@sysoev.ru     /*
64212Sigor@sysoev.ru      * kqueue has a builtin signal processing support, so the function
64312Sigor@sysoev.ru      * is used only to post events and the signo argument is ignored.
64412Sigor@sysoev.ru      */
64512Sigor@sysoev.ru 
64612Sigor@sysoev.ru     kev.ident = 0;
64712Sigor@sysoev.ru     kev.filter = EVFILT_USER;
64812Sigor@sysoev.ru     kev.flags = 0;
64912Sigor@sysoev.ru     kev.fflags = NOTE_TRIGGER;
65012Sigor@sysoev.ru     kev.data = 0;
65112Sigor@sysoev.ru     kev.udata = NULL;
65212Sigor@sysoev.ru 
65312Sigor@sysoev.ru     if (kevent(engine->u.kqueue.fd, &kev, 1, NULL, 0, NULL) != 0) {
654564Svbart@nginx.com         nxt_alert(&engine->task, "kevent(%d) failed %E",
655564Svbart@nginx.com                   engine->u.kqueue.fd, nxt_errno);
65612Sigor@sysoev.ru     }
65712Sigor@sysoev.ru }
65812Sigor@sysoev.ru 
65912Sigor@sysoev.ru #endif
66012Sigor@sysoev.ru 
66112Sigor@sysoev.ru 
66212Sigor@sysoev.ru static void
66312Sigor@sysoev.ru nxt_kqueue_poll(nxt_event_engine_t *engine, nxt_msec_t timeout)
66412Sigor@sysoev.ru {
66512Sigor@sysoev.ru     int                 nevents;
66612Sigor@sysoev.ru     void                *obj, *data;
66712Sigor@sysoev.ru     nxt_int_t           i;
66812Sigor@sysoev.ru     nxt_err_t           err;
66912Sigor@sysoev.ru     nxt_uint_t          level;
67012Sigor@sysoev.ru     nxt_bool_t          error, eof;
67112Sigor@sysoev.ru     nxt_task_t          *task;
67212Sigor@sysoev.ru     struct kevent       *kev;
67312Sigor@sysoev.ru     nxt_fd_event_t      *ev;
67412Sigor@sysoev.ru     nxt_sig_event_t     *sigev;
67512Sigor@sysoev.ru     struct timespec     ts, *tp;
67662Sigor@sysoev.ru     nxt_file_event_t    *fev;
67712Sigor@sysoev.ru     nxt_work_queue_t    *wq;
67812Sigor@sysoev.ru     nxt_work_handler_t  handler;
67912Sigor@sysoev.ru 
68012Sigor@sysoev.ru     if (timeout == NXT_INFINITE_MSEC) {
68112Sigor@sysoev.ru         tp = NULL;
68212Sigor@sysoev.ru 
68312Sigor@sysoev.ru     } else {
68412Sigor@sysoev.ru         ts.tv_sec = timeout / 1000;
68512Sigor@sysoev.ru         ts.tv_nsec = (timeout % 1000) * 1000000;
68612Sigor@sysoev.ru         tp = &ts;
68712Sigor@sysoev.ru     }
68812Sigor@sysoev.ru 
68912Sigor@sysoev.ru     nxt_debug(&engine->task, "kevent(%d) changes:%d timeout:%M",
69012Sigor@sysoev.ru               engine->u.kqueue.fd, engine->u.kqueue.nchanges, timeout);
69112Sigor@sysoev.ru 
69212Sigor@sysoev.ru     nevents = kevent(engine->u.kqueue.fd,
69312Sigor@sysoev.ru                      engine->u.kqueue.changes, engine->u.kqueue.nchanges,
69412Sigor@sysoev.ru                      engine->u.kqueue.events, engine->u.kqueue.mevents, tp);
69512Sigor@sysoev.ru 
69612Sigor@sysoev.ru     err = (nevents == -1) ? nxt_errno : 0;
69712Sigor@sysoev.ru 
69812Sigor@sysoev.ru     nxt_thread_time_update(engine->task.thread);
69912Sigor@sysoev.ru 
70012Sigor@sysoev.ru     nxt_debug(&engine->task, "kevent(%d): %d", engine->u.kqueue.fd, nevents);
70112Sigor@sysoev.ru 
70212Sigor@sysoev.ru     if (nevents == -1) {
703564Svbart@nginx.com         level = (err == NXT_EINTR) ? NXT_LOG_INFO : NXT_LOG_ALERT;
70412Sigor@sysoev.ru 
70512Sigor@sysoev.ru         nxt_log(&engine->task, level, "kevent(%d) failed %E",
70612Sigor@sysoev.ru                 engine->u.kqueue.fd, err);
70712Sigor@sysoev.ru 
70812Sigor@sysoev.ru         nxt_kqueue_error(engine);
70912Sigor@sysoev.ru         return;
71012Sigor@sysoev.ru     }
71112Sigor@sysoev.ru 
71212Sigor@sysoev.ru     engine->u.kqueue.nchanges = 0;
71312Sigor@sysoev.ru 
71412Sigor@sysoev.ru     for (i = 0; i < nevents; i++) {
71512Sigor@sysoev.ru 
71612Sigor@sysoev.ru         kev = &engine->u.kqueue.events[i];
71712Sigor@sysoev.ru 
71812Sigor@sysoev.ru         nxt_debug(&engine->task,
71912Sigor@sysoev.ru                   (kev->ident > 0x8000000 && kev->ident != (uintptr_t) -1) ?
72012Sigor@sysoev.ru                       "kevent: id:%p ft:%d fl:%04Xd ff:%d d:%d ud:%p":
72112Sigor@sysoev.ru                       "kevent: id:%d ft:%d fl:%04Xd ff:%d d:%d ud:%p",
72212Sigor@sysoev.ru                   kev->ident, kev->filter, kev->flags, kev->fflags,
72312Sigor@sysoev.ru                   kev->data, kev->udata);
72412Sigor@sysoev.ru 
72512Sigor@sysoev.ru         error = (kev->flags & EV_ERROR);
72612Sigor@sysoev.ru 
72712Sigor@sysoev.ru         if (nxt_slow_path(error)) {
728564Svbart@nginx.com             nxt_alert(&engine->task,
729564Svbart@nginx.com                       "kevent(%d) error %E on ident:%d filter:%d",
730564Svbart@nginx.com                       engine->u.kqueue.fd, kev->data, kev->ident, kev->filter);
73112Sigor@sysoev.ru         }
73212Sigor@sysoev.ru 
73312Sigor@sysoev.ru         task = &engine->task;
73412Sigor@sysoev.ru         wq = &engine->fast_work_queue;
73512Sigor@sysoev.ru         handler = nxt_kqueue_fd_error_handler;
73612Sigor@sysoev.ru         obj = nxt_kevent_get_udata(kev->udata);
73712Sigor@sysoev.ru 
73812Sigor@sysoev.ru         switch (kev->filter) {
73912Sigor@sysoev.ru 
74012Sigor@sysoev.ru         case EVFILT_READ:
74112Sigor@sysoev.ru             ev = obj;
74212Sigor@sysoev.ru             ev->read_ready = 1;
74312Sigor@sysoev.ru             ev->kq_available = (int32_t) kev->data;
74412Sigor@sysoev.ru             err = kev->fflags;
74512Sigor@sysoev.ru             eof = (kev->flags & EV_EOF) != 0;
74612Sigor@sysoev.ru             ev->kq_errno = err;
74712Sigor@sysoev.ru             ev->kq_eof = eof;
74812Sigor@sysoev.ru 
74920Sigor@sysoev.ru             if (ev->read <= NXT_EVENT_BLOCKED) {
75012Sigor@sysoev.ru                 nxt_debug(ev->task, "blocked read event fd:%d", ev->fd);
75112Sigor@sysoev.ru                 continue;
75212Sigor@sysoev.ru             }
75312Sigor@sysoev.ru 
75412Sigor@sysoev.ru             if ((kev->flags & NXT_KEVENT_ONESHOT) != 0) {
75512Sigor@sysoev.ru                 ev->read = NXT_EVENT_INACTIVE;
75612Sigor@sysoev.ru             }
75712Sigor@sysoev.ru 
75812Sigor@sysoev.ru             if (nxt_slow_path(ev->kq_available == 0 && eof && err != 0)) {
75912Sigor@sysoev.ru                 error = 1;
76012Sigor@sysoev.ru             }
76112Sigor@sysoev.ru 
76212Sigor@sysoev.ru             if (nxt_fast_path(!error)) {
76312Sigor@sysoev.ru                 handler = ev->read_handler;
76412Sigor@sysoev.ru                 wq = ev->read_work_queue;
76512Sigor@sysoev.ru             }
76612Sigor@sysoev.ru 
76712Sigor@sysoev.ru             task = ev->task;
76812Sigor@sysoev.ru             data = ev->data;
76912Sigor@sysoev.ru 
77012Sigor@sysoev.ru             break;
77112Sigor@sysoev.ru 
77212Sigor@sysoev.ru         case EVFILT_WRITE:
77312Sigor@sysoev.ru             ev = obj;
77412Sigor@sysoev.ru             ev->write_ready = 1;
77512Sigor@sysoev.ru             err = kev->fflags;
77612Sigor@sysoev.ru             eof = (kev->flags & EV_EOF) != 0;
77712Sigor@sysoev.ru             ev->kq_errno = err;
77812Sigor@sysoev.ru             ev->kq_eof = eof;
77912Sigor@sysoev.ru 
78020Sigor@sysoev.ru             if (ev->write <= NXT_EVENT_BLOCKED) {
78112Sigor@sysoev.ru                 nxt_debug(ev->task, "blocked write event fd:%d", ev->fd);
78212Sigor@sysoev.ru                 continue;
78312Sigor@sysoev.ru             }
78412Sigor@sysoev.ru 
78512Sigor@sysoev.ru             if ((kev->flags & NXT_KEVENT_ONESHOT) != 0) {
78612Sigor@sysoev.ru                 ev->write = NXT_EVENT_INACTIVE;
78712Sigor@sysoev.ru             }
78812Sigor@sysoev.ru 
78912Sigor@sysoev.ru             if (nxt_slow_path(eof && err != 0)) {
79012Sigor@sysoev.ru                 error = 1;
79112Sigor@sysoev.ru             }
79212Sigor@sysoev.ru 
79312Sigor@sysoev.ru             if (nxt_fast_path(!error)) {
79412Sigor@sysoev.ru                 handler = ev->write_handler;
79512Sigor@sysoev.ru                 wq = ev->write_work_queue;
79612Sigor@sysoev.ru             }
79712Sigor@sysoev.ru 
79812Sigor@sysoev.ru             task = ev->task;
79912Sigor@sysoev.ru             data = ev->data;
80012Sigor@sysoev.ru 
80112Sigor@sysoev.ru             break;
80212Sigor@sysoev.ru 
80312Sigor@sysoev.ru         case EVFILT_VNODE:
80412Sigor@sysoev.ru             fev = obj;
80512Sigor@sysoev.ru             handler = fev->handler;
80612Sigor@sysoev.ru             task = fev->task;
80712Sigor@sysoev.ru             data = fev->data;
80812Sigor@sysoev.ru             break;
80912Sigor@sysoev.ru 
81012Sigor@sysoev.ru         case EVFILT_SIGNAL:
81112Sigor@sysoev.ru             sigev = obj;
81212Sigor@sysoev.ru             obj = (void *) kev->ident;
81312Sigor@sysoev.ru             handler = sigev->handler;
81412Sigor@sysoev.ru             data = (void *) sigev->name;
81512Sigor@sysoev.ru             break;
81612Sigor@sysoev.ru 
81712Sigor@sysoev.ru #if (NXT_HAVE_EVFILT_USER)
81812Sigor@sysoev.ru 
81912Sigor@sysoev.ru         case EVFILT_USER:
82012Sigor@sysoev.ru             handler = engine->u.kqueue.post_handler;
82112Sigor@sysoev.ru             data = NULL;
82212Sigor@sysoev.ru             break;
82312Sigor@sysoev.ru 
82412Sigor@sysoev.ru #endif
82512Sigor@sysoev.ru 
82612Sigor@sysoev.ru         default:
82712Sigor@sysoev.ru 
82812Sigor@sysoev.ru #if (NXT_DEBUG)
829564Svbart@nginx.com             nxt_alert(&engine->task,
830564Svbart@nginx.com                       "unexpected kevent(%d) filter %d on ident %d",
831564Svbart@nginx.com                       engine->u.kqueue.fd, kev->filter, kev->ident);
83212Sigor@sysoev.ru #endif
83312Sigor@sysoev.ru 
83412Sigor@sysoev.ru             continue;
83512Sigor@sysoev.ru         }
83612Sigor@sysoev.ru 
83712Sigor@sysoev.ru         nxt_work_queue_add(wq, handler, task, obj, data);
83812Sigor@sysoev.ru     }
83912Sigor@sysoev.ru }
84012Sigor@sysoev.ru 
84112Sigor@sysoev.ru 
84212Sigor@sysoev.ru /*
84312Sigor@sysoev.ru  * nxt_kqueue_event_conn_io_connect() eliminates the
84412Sigor@sysoev.ru  * getsockopt() syscall to test pending connect() error.
84512Sigor@sysoev.ru  */
84612Sigor@sysoev.ru 
84712Sigor@sysoev.ru static void
84862Sigor@sysoev.ru nxt_kqueue_conn_io_connect(nxt_task_t *task, void *obj, void *data)
84912Sigor@sysoev.ru {
85062Sigor@sysoev.ru     nxt_conn_t                    *c;
85112Sigor@sysoev.ru     nxt_event_engine_t            *engine;
85212Sigor@sysoev.ru     nxt_work_handler_t            handler;
85312Sigor@sysoev.ru     const nxt_event_conn_state_t  *state;
85412Sigor@sysoev.ru 
85512Sigor@sysoev.ru     c = obj;
85612Sigor@sysoev.ru 
85712Sigor@sysoev.ru     state = c->write_state;
85812Sigor@sysoev.ru 
859*1008Szelenkov@nginx.com     switch (nxt_socket_connect(task, c->socket.fd, c->remote)) {
86012Sigor@sysoev.ru 
86112Sigor@sysoev.ru     case NXT_OK:
86212Sigor@sysoev.ru         c->socket.write_ready = 1;
86312Sigor@sysoev.ru         handler = state->ready_handler;
86412Sigor@sysoev.ru         break;
86512Sigor@sysoev.ru 
86612Sigor@sysoev.ru     case NXT_AGAIN:
86762Sigor@sysoev.ru         c->socket.write_handler = nxt_kqueue_conn_connected;
86862Sigor@sysoev.ru         c->socket.error_handler = nxt_conn_connect_error;
86912Sigor@sysoev.ru 
87012Sigor@sysoev.ru         engine = task->thread->engine;
87162Sigor@sysoev.ru         nxt_conn_timer(engine, c, state, &c->write_timer);
87212Sigor@sysoev.ru 
87312Sigor@sysoev.ru         nxt_kqueue_enable_write(engine, &c->socket);
87412Sigor@sysoev.ru         return;
87512Sigor@sysoev.ru 
87612Sigor@sysoev.ru     case NXT_DECLINED:
87712Sigor@sysoev.ru         handler = state->close_handler;
87812Sigor@sysoev.ru         break;
87912Sigor@sysoev.ru 
88012Sigor@sysoev.ru     default: /* NXT_ERROR */
88112Sigor@sysoev.ru         handler = state->error_handler;
88212Sigor@sysoev.ru         break;
88312Sigor@sysoev.ru     }
88412Sigor@sysoev.ru 
88513Sigor@sysoev.ru     nxt_work_queue_add(c->write_work_queue, handler, task, c, data);
88612Sigor@sysoev.ru }
88712Sigor@sysoev.ru 
88812Sigor@sysoev.ru 
88912Sigor@sysoev.ru static void
89062Sigor@sysoev.ru nxt_kqueue_conn_connected(nxt_task_t *task, void *obj, void *data)
89112Sigor@sysoev.ru {
89262Sigor@sysoev.ru     nxt_conn_t  *c;
89312Sigor@sysoev.ru 
89412Sigor@sysoev.ru     c = obj;
89512Sigor@sysoev.ru 
89662Sigor@sysoev.ru     nxt_debug(task, "kqueue conn connected fd:%d", c->socket.fd);
89712Sigor@sysoev.ru 
89812Sigor@sysoev.ru     c->socket.write = NXT_EVENT_BLOCKED;
89912Sigor@sysoev.ru 
90056Sigor@sysoev.ru     if (c->write_state->timer_autoreset) {
90112Sigor@sysoev.ru         nxt_timer_disable(task->thread->engine, &c->write_timer);
90212Sigor@sysoev.ru     }
90312Sigor@sysoev.ru 
90412Sigor@sysoev.ru     nxt_work_queue_add(c->write_work_queue, c->write_state->ready_handler,
90512Sigor@sysoev.ru                        task, c, data);
90612Sigor@sysoev.ru }
90712Sigor@sysoev.ru 
90812Sigor@sysoev.ru 
90912Sigor@sysoev.ru static void
91012Sigor@sysoev.ru nxt_kqueue_listen_handler(nxt_task_t *task, void *obj, void *data)
91112Sigor@sysoev.ru {
91262Sigor@sysoev.ru     nxt_listen_event_t  *lev;
91312Sigor@sysoev.ru 
91462Sigor@sysoev.ru     lev = obj;
91512Sigor@sysoev.ru 
91612Sigor@sysoev.ru     nxt_debug(task, "kevent fd:%d avail:%D",
91762Sigor@sysoev.ru               lev->socket.fd, lev->socket.kq_available);
91812Sigor@sysoev.ru 
91962Sigor@sysoev.ru     lev->ready = nxt_min(lev->batch, (uint32_t) lev->socket.kq_available);
92012Sigor@sysoev.ru 
92162Sigor@sysoev.ru     nxt_kqueue_conn_io_accept(task, lev, data);
92212Sigor@sysoev.ru }
92312Sigor@sysoev.ru 
92412Sigor@sysoev.ru 
92512Sigor@sysoev.ru static void
92662Sigor@sysoev.ru nxt_kqueue_conn_io_accept(nxt_task_t *task, void *obj, void *data)
92712Sigor@sysoev.ru {
928312Sigor@sysoev.ru     socklen_t           socklen;
92962Sigor@sysoev.ru     nxt_conn_t          *c;
93062Sigor@sysoev.ru     nxt_socket_t        s;
93162Sigor@sysoev.ru     struct sockaddr     *sa;
93262Sigor@sysoev.ru     nxt_listen_event_t  *lev;
93312Sigor@sysoev.ru 
93462Sigor@sysoev.ru     lev = obj;
93562Sigor@sysoev.ru     c = lev->next;
93612Sigor@sysoev.ru 
93762Sigor@sysoev.ru     lev->ready--;
93862Sigor@sysoev.ru     lev->socket.read_ready = (lev->ready != 0);
93912Sigor@sysoev.ru 
94062Sigor@sysoev.ru     lev->socket.kq_available--;
94162Sigor@sysoev.ru     lev->socket.read_ready = (lev->socket.kq_available != 0);
94212Sigor@sysoev.ru 
943312Sigor@sysoev.ru     sa = &c->remote->u.sockaddr;
944312Sigor@sysoev.ru     socklen = c->remote->socklen;
945312Sigor@sysoev.ru     /*
946312Sigor@sysoev.ru      * The returned socklen is ignored here,
947312Sigor@sysoev.ru      * see comment in nxt_conn_io_accept().
948312Sigor@sysoev.ru      */
949312Sigor@sysoev.ru     s = accept(lev->socket.fd, sa, &socklen);
95012Sigor@sysoev.ru 
95112Sigor@sysoev.ru     if (s != -1) {
95212Sigor@sysoev.ru         c->socket.fd = s;
95312Sigor@sysoev.ru 
95462Sigor@sysoev.ru         nxt_debug(task, "accept(%d): %d", lev->socket.fd, s);
95512Sigor@sysoev.ru 
95662Sigor@sysoev.ru         nxt_conn_accept(task, lev, c);
95712Sigor@sysoev.ru         return;
95812Sigor@sysoev.ru     }
95912Sigor@sysoev.ru 
96062Sigor@sysoev.ru     nxt_conn_accept_error(task, lev, "accept", nxt_errno);
96112Sigor@sysoev.ru }
96212Sigor@sysoev.ru 
96312Sigor@sysoev.ru 
96412Sigor@sysoev.ru /*
96562Sigor@sysoev.ru  * nxt_kqueue_conn_io_read() is just a wrapper to eliminate the
96612Sigor@sysoev.ru  * readv() or recv() syscall if a remote side just closed connection.
96712Sigor@sysoev.ru  */
96812Sigor@sysoev.ru 
96912Sigor@sysoev.ru static void
97062Sigor@sysoev.ru nxt_kqueue_conn_io_read(nxt_task_t *task, void *obj, void *data)
97112Sigor@sysoev.ru {
97262Sigor@sysoev.ru     nxt_conn_t  *c;
97312Sigor@sysoev.ru 
97412Sigor@sysoev.ru     c = obj;
97512Sigor@sysoev.ru 
97662Sigor@sysoev.ru     nxt_debug(task, "kqueue conn read fd:%d", c->socket.fd);
97712Sigor@sysoev.ru 
97812Sigor@sysoev.ru     if (c->socket.kq_available == 0 && c->socket.kq_eof) {
97912Sigor@sysoev.ru         nxt_debug(task, "kevent fd:%d eof", c->socket.fd);
98012Sigor@sysoev.ru 
98112Sigor@sysoev.ru         c->socket.closed = 1;
98212Sigor@sysoev.ru         nxt_work_queue_add(c->read_work_queue, c->read_state->close_handler,
98312Sigor@sysoev.ru                            task, c, data);
98412Sigor@sysoev.ru         return;
98512Sigor@sysoev.ru     }
98612Sigor@sysoev.ru 
98762Sigor@sysoev.ru     nxt_conn_io_read(task, c, data);
98812Sigor@sysoev.ru }
98912Sigor@sysoev.ru 
99012Sigor@sysoev.ru 
99112Sigor@sysoev.ru /*
99262Sigor@sysoev.ru  * nxt_kqueue_conn_io_recvbuf() is just wrapper around standard
99362Sigor@sysoev.ru  * nxt_conn_io_recvbuf() to eliminate the readv() or recv() syscalls
99412Sigor@sysoev.ru  * if there is no pending data or a remote side closed connection.
99512Sigor@sysoev.ru  */
99612Sigor@sysoev.ru 
99712Sigor@sysoev.ru static ssize_t
99862Sigor@sysoev.ru nxt_kqueue_conn_io_recvbuf(nxt_conn_t *c, nxt_buf_t *b)
99912Sigor@sysoev.ru {
100012Sigor@sysoev.ru     ssize_t  n;
100112Sigor@sysoev.ru 
100212Sigor@sysoev.ru     if (c->socket.kq_available == 0 && c->socket.kq_eof) {
100312Sigor@sysoev.ru         c->socket.closed = 1;
100412Sigor@sysoev.ru         return 0;
100512Sigor@sysoev.ru     }
100612Sigor@sysoev.ru 
100762Sigor@sysoev.ru     n = nxt_conn_io_recvbuf(c, b);
100812Sigor@sysoev.ru 
100912Sigor@sysoev.ru     if (n > 0) {
101012Sigor@sysoev.ru         c->socket.kq_available -= n;
101112Sigor@sysoev.ru 
101212Sigor@sysoev.ru         if (c->socket.kq_available < 0) {
101312Sigor@sysoev.ru             c->socket.kq_available = 0;
101412Sigor@sysoev.ru         }
101512Sigor@sysoev.ru 
101612Sigor@sysoev.ru         nxt_debug(c->socket.task, "kevent fd:%d avail:%D eof:%d",
101712Sigor@sysoev.ru                   c->socket.fd, c->socket.kq_available, c->socket.kq_eof);
101812Sigor@sysoev.ru 
101912Sigor@sysoev.ru         c->socket.read_ready = (c->socket.kq_available != 0
102012Sigor@sysoev.ru                                 || c->socket.kq_eof);
102112Sigor@sysoev.ru     }
102212Sigor@sysoev.ru 
102312Sigor@sysoev.ru     return n;
102412Sigor@sysoev.ru }
1025