xref: /unit/src/nxt_poll_engine.c (revision 1008:84f2370bd642)
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 #define NXT_POLL_ADD     0
1112Sigor@sysoev.ru #define NXT_POLL_CHANGE  1
1212Sigor@sysoev.ru #define NXT_POLL_DELETE  2
1312Sigor@sysoev.ru 
1412Sigor@sysoev.ru 
1512Sigor@sysoev.ru typedef struct {
1612Sigor@sysoev.ru     /*
1712Sigor@sysoev.ru      * A file descriptor is stored in hash entry to allow
1812Sigor@sysoev.ru      * nxt_poll_fd_hash_test() to not dereference a pointer to
1912Sigor@sysoev.ru      * nxt_fd_event_t which may be invalid if the file descriptor has
2012Sigor@sysoev.ru      * been already closed and the nxt_fd_event_t's memory has been freed.
2112Sigor@sysoev.ru      */
2212Sigor@sysoev.ru     nxt_socket_t         fd;
2312Sigor@sysoev.ru 
2412Sigor@sysoev.ru     uint32_t             index;
2512Sigor@sysoev.ru     void                 *event;
2612Sigor@sysoev.ru } nxt_poll_hash_entry_t;
2712Sigor@sysoev.ru 
2812Sigor@sysoev.ru 
2912Sigor@sysoev.ru static nxt_int_t nxt_poll_create(nxt_event_engine_t *engine,
3012Sigor@sysoev.ru     nxt_uint_t mchanges, nxt_uint_t mevents);
3112Sigor@sysoev.ru static void nxt_poll_free(nxt_event_engine_t *engine);
3212Sigor@sysoev.ru static void nxt_poll_enable(nxt_event_engine_t *engine, nxt_fd_event_t *ev);
3312Sigor@sysoev.ru static void nxt_poll_disable(nxt_event_engine_t *engine,
3412Sigor@sysoev.ru     nxt_fd_event_t *ev);
3512Sigor@sysoev.ru static nxt_bool_t nxt_poll_close(nxt_event_engine_t *engine,
3612Sigor@sysoev.ru     nxt_fd_event_t *ev);
3712Sigor@sysoev.ru static void nxt_poll_enable_read(nxt_event_engine_t *engine,
3812Sigor@sysoev.ru     nxt_fd_event_t *ev);
3912Sigor@sysoev.ru static void nxt_poll_enable_write(nxt_event_engine_t *engine,
4012Sigor@sysoev.ru     nxt_fd_event_t *ev);
4112Sigor@sysoev.ru static void nxt_poll_disable_read(nxt_event_engine_t *engine,
4212Sigor@sysoev.ru     nxt_fd_event_t *ev);
4312Sigor@sysoev.ru static void nxt_poll_disable_write(nxt_event_engine_t *engine,
4412Sigor@sysoev.ru     nxt_fd_event_t *ev);
4512Sigor@sysoev.ru static void nxt_poll_block_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev);
4612Sigor@sysoev.ru static void nxt_poll_block_write(nxt_event_engine_t *engine,
4712Sigor@sysoev.ru     nxt_fd_event_t *ev);
4812Sigor@sysoev.ru static void nxt_poll_oneshot_read(nxt_event_engine_t *engine,
4912Sigor@sysoev.ru     nxt_fd_event_t *ev);
5012Sigor@sysoev.ru static void nxt_poll_oneshot_write(nxt_event_engine_t *engine,
5112Sigor@sysoev.ru     nxt_fd_event_t *ev);
5212Sigor@sysoev.ru static void nxt_poll_change(nxt_event_engine_t *engine, nxt_fd_event_t *ev,
5312Sigor@sysoev.ru     nxt_uint_t op, nxt_uint_t events);
5412Sigor@sysoev.ru static nxt_int_t nxt_poll_commit_changes(nxt_event_engine_t *engine);
5512Sigor@sysoev.ru static nxt_int_t nxt_poll_set_add(nxt_event_engine_t *engine,
5612Sigor@sysoev.ru     nxt_fd_event_t *ev, int events);
5712Sigor@sysoev.ru static nxt_int_t nxt_poll_set_change(nxt_event_engine_t *engine,
5812Sigor@sysoev.ru     nxt_fd_t fd, int events);
5912Sigor@sysoev.ru static nxt_int_t nxt_poll_set_delete(nxt_event_engine_t *engine, nxt_fd_t fd);
6012Sigor@sysoev.ru static void nxt_poll(nxt_event_engine_t *engine, nxt_msec_t timeout);
6112Sigor@sysoev.ru static nxt_poll_hash_entry_t *nxt_poll_fd_hash_get(nxt_event_engine_t *engine,
6212Sigor@sysoev.ru     nxt_fd_t fd);
6312Sigor@sysoev.ru static nxt_int_t nxt_poll_fd_hash_test(nxt_lvlhsh_query_t *lhq, void *data);
6412Sigor@sysoev.ru static void nxt_poll_fd_hash_destroy(nxt_event_engine_t *engine,
6512Sigor@sysoev.ru     nxt_lvlhsh_t *lh);
6612Sigor@sysoev.ru 
6712Sigor@sysoev.ru 
6812Sigor@sysoev.ru const nxt_event_interface_t  nxt_poll_engine = {
6912Sigor@sysoev.ru     "poll",
7012Sigor@sysoev.ru     nxt_poll_create,
7112Sigor@sysoev.ru     nxt_poll_free,
7212Sigor@sysoev.ru     nxt_poll_enable,
7312Sigor@sysoev.ru     nxt_poll_disable,
7412Sigor@sysoev.ru     nxt_poll_disable,
7512Sigor@sysoev.ru     nxt_poll_close,
7612Sigor@sysoev.ru     nxt_poll_enable_read,
7712Sigor@sysoev.ru     nxt_poll_enable_write,
7812Sigor@sysoev.ru     nxt_poll_disable_read,
7912Sigor@sysoev.ru     nxt_poll_disable_write,
8012Sigor@sysoev.ru     nxt_poll_block_read,
8112Sigor@sysoev.ru     nxt_poll_block_write,
8212Sigor@sysoev.ru     nxt_poll_oneshot_read,
8312Sigor@sysoev.ru     nxt_poll_oneshot_write,
8412Sigor@sysoev.ru     nxt_poll_enable_read,
8512Sigor@sysoev.ru     NULL,
8612Sigor@sysoev.ru     NULL,
8712Sigor@sysoev.ru     NULL,
8812Sigor@sysoev.ru     NULL,
8912Sigor@sysoev.ru     nxt_poll,
9012Sigor@sysoev.ru 
9162Sigor@sysoev.ru     &nxt_unix_conn_io,
9212Sigor@sysoev.ru 
9312Sigor@sysoev.ru     NXT_NO_FILE_EVENTS,
9412Sigor@sysoev.ru     NXT_NO_SIGNAL_EVENTS,
9512Sigor@sysoev.ru };
9612Sigor@sysoev.ru 
9712Sigor@sysoev.ru 
9812Sigor@sysoev.ru static const nxt_lvlhsh_proto_t  nxt_poll_fd_hash_proto  nxt_aligned(64) =
9912Sigor@sysoev.ru {
10012Sigor@sysoev.ru     NXT_LVLHSH_LARGE_MEMALIGN,
10112Sigor@sysoev.ru     nxt_poll_fd_hash_test,
10212Sigor@sysoev.ru     nxt_lvlhsh_alloc,
10312Sigor@sysoev.ru     nxt_lvlhsh_free,
10412Sigor@sysoev.ru };
10512Sigor@sysoev.ru 
10612Sigor@sysoev.ru 
10712Sigor@sysoev.ru static nxt_int_t
nxt_poll_create(nxt_event_engine_t * engine,nxt_uint_t mchanges,nxt_uint_t mevents)10812Sigor@sysoev.ru nxt_poll_create(nxt_event_engine_t *engine, nxt_uint_t mchanges,
10912Sigor@sysoev.ru     nxt_uint_t mevents)
11012Sigor@sysoev.ru {
11112Sigor@sysoev.ru     engine->u.poll.mchanges = mchanges;
11212Sigor@sysoev.ru 
11312Sigor@sysoev.ru     engine->u.poll.changes = nxt_malloc(sizeof(nxt_poll_change_t) * mchanges);
11412Sigor@sysoev.ru 
11512Sigor@sysoev.ru     if (engine->u.poll.changes != NULL) {
11612Sigor@sysoev.ru         return NXT_OK;
11712Sigor@sysoev.ru     }
11812Sigor@sysoev.ru 
11912Sigor@sysoev.ru     return NXT_ERROR;
12012Sigor@sysoev.ru }
12112Sigor@sysoev.ru 
12212Sigor@sysoev.ru 
12312Sigor@sysoev.ru static void
nxt_poll_free(nxt_event_engine_t * engine)12412Sigor@sysoev.ru nxt_poll_free(nxt_event_engine_t *engine)
12512Sigor@sysoev.ru {
12612Sigor@sysoev.ru     nxt_debug(&engine->task, "poll free");
12712Sigor@sysoev.ru 
12812Sigor@sysoev.ru     nxt_free(engine->u.poll.set);
12912Sigor@sysoev.ru     nxt_free(engine->u.poll.changes);
13012Sigor@sysoev.ru     nxt_poll_fd_hash_destroy(engine, &engine->u.poll.fd_hash);
13112Sigor@sysoev.ru 
13212Sigor@sysoev.ru     nxt_memzero(&engine->u.poll, sizeof(nxt_poll_engine_t));
13312Sigor@sysoev.ru }
13412Sigor@sysoev.ru 
13512Sigor@sysoev.ru 
13612Sigor@sysoev.ru static void
nxt_poll_enable(nxt_event_engine_t * engine,nxt_fd_event_t * ev)13712Sigor@sysoev.ru nxt_poll_enable(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
13812Sigor@sysoev.ru {
13912Sigor@sysoev.ru     ev->read = NXT_EVENT_ACTIVE;
14012Sigor@sysoev.ru     ev->write = NXT_EVENT_ACTIVE;
14112Sigor@sysoev.ru 
14212Sigor@sysoev.ru     nxt_poll_change(engine, ev, NXT_POLL_ADD, POLLIN | POLLOUT);
14312Sigor@sysoev.ru }
14412Sigor@sysoev.ru 
14512Sigor@sysoev.ru 
14612Sigor@sysoev.ru static void
nxt_poll_disable(nxt_event_engine_t * engine,nxt_fd_event_t * ev)14712Sigor@sysoev.ru nxt_poll_disable(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
14812Sigor@sysoev.ru {
14912Sigor@sysoev.ru     if (ev->read != NXT_EVENT_INACTIVE && ev->write != NXT_EVENT_INACTIVE) {
15012Sigor@sysoev.ru         ev->read = NXT_EVENT_INACTIVE;
15112Sigor@sysoev.ru         ev->write = NXT_EVENT_INACTIVE;
15212Sigor@sysoev.ru 
15312Sigor@sysoev.ru         nxt_poll_change(engine, ev, NXT_POLL_DELETE, 0);
15412Sigor@sysoev.ru     }
15512Sigor@sysoev.ru }
15612Sigor@sysoev.ru 
15712Sigor@sysoev.ru 
15812Sigor@sysoev.ru static nxt_bool_t
nxt_poll_close(nxt_event_engine_t * engine,nxt_fd_event_t * ev)15912Sigor@sysoev.ru nxt_poll_close(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
16012Sigor@sysoev.ru {
16112Sigor@sysoev.ru     nxt_poll_disable(engine, ev);
16212Sigor@sysoev.ru 
16312Sigor@sysoev.ru     return ev->changing;
16412Sigor@sysoev.ru }
16512Sigor@sysoev.ru 
16612Sigor@sysoev.ru 
16712Sigor@sysoev.ru static void
nxt_poll_enable_read(nxt_event_engine_t * engine,nxt_fd_event_t * ev)16812Sigor@sysoev.ru nxt_poll_enable_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
16912Sigor@sysoev.ru {
17012Sigor@sysoev.ru     nxt_uint_t  op, events;
17112Sigor@sysoev.ru 
17212Sigor@sysoev.ru     ev->read = NXT_EVENT_ACTIVE;
17312Sigor@sysoev.ru 
17412Sigor@sysoev.ru     if (ev->write == NXT_EVENT_INACTIVE) {
17512Sigor@sysoev.ru         op = NXT_POLL_ADD;
17612Sigor@sysoev.ru         events = POLLIN;
17712Sigor@sysoev.ru 
17812Sigor@sysoev.ru     } else {
17912Sigor@sysoev.ru         op = NXT_POLL_CHANGE;
18012Sigor@sysoev.ru         events = POLLIN | POLLOUT;
18112Sigor@sysoev.ru     }
18212Sigor@sysoev.ru 
18312Sigor@sysoev.ru     nxt_poll_change(engine, ev, op, events);
18412Sigor@sysoev.ru }
18512Sigor@sysoev.ru 
18612Sigor@sysoev.ru 
18712Sigor@sysoev.ru static void
nxt_poll_enable_write(nxt_event_engine_t * engine,nxt_fd_event_t * ev)18812Sigor@sysoev.ru nxt_poll_enable_write(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
18912Sigor@sysoev.ru {
19012Sigor@sysoev.ru     nxt_uint_t  op, events;
19112Sigor@sysoev.ru 
19212Sigor@sysoev.ru     ev->write = NXT_EVENT_ACTIVE;
19312Sigor@sysoev.ru 
19412Sigor@sysoev.ru     if (ev->read == NXT_EVENT_INACTIVE) {
19512Sigor@sysoev.ru         op = NXT_POLL_ADD;
19612Sigor@sysoev.ru         events = POLLOUT;
19712Sigor@sysoev.ru 
19812Sigor@sysoev.ru     } else {
19912Sigor@sysoev.ru         op = NXT_POLL_CHANGE;
20012Sigor@sysoev.ru         events = POLLIN | POLLOUT;
20112Sigor@sysoev.ru     }
20212Sigor@sysoev.ru 
20312Sigor@sysoev.ru     nxt_poll_change(engine, ev, op, events);
20412Sigor@sysoev.ru }
20512Sigor@sysoev.ru 
20612Sigor@sysoev.ru 
20712Sigor@sysoev.ru static void
nxt_poll_disable_read(nxt_event_engine_t * engine,nxt_fd_event_t * ev)20812Sigor@sysoev.ru nxt_poll_disable_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
20912Sigor@sysoev.ru {
21012Sigor@sysoev.ru     nxt_uint_t  op, events;
21112Sigor@sysoev.ru 
21212Sigor@sysoev.ru     ev->read = NXT_EVENT_INACTIVE;
21312Sigor@sysoev.ru 
21412Sigor@sysoev.ru     if (ev->write == NXT_EVENT_INACTIVE) {
21512Sigor@sysoev.ru         op = NXT_POLL_DELETE;
21612Sigor@sysoev.ru         events = 0;
21712Sigor@sysoev.ru 
21812Sigor@sysoev.ru     } else {
21912Sigor@sysoev.ru         op = NXT_POLL_CHANGE;
22012Sigor@sysoev.ru         events = POLLOUT;
22112Sigor@sysoev.ru     }
22212Sigor@sysoev.ru 
22312Sigor@sysoev.ru     nxt_poll_change(engine, ev, op, events);
22412Sigor@sysoev.ru }
22512Sigor@sysoev.ru 
22612Sigor@sysoev.ru 
22712Sigor@sysoev.ru static void
nxt_poll_disable_write(nxt_event_engine_t * engine,nxt_fd_event_t * ev)22812Sigor@sysoev.ru nxt_poll_disable_write(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
22912Sigor@sysoev.ru {
23012Sigor@sysoev.ru     nxt_uint_t  op, events;
23112Sigor@sysoev.ru 
23212Sigor@sysoev.ru     ev->write = NXT_EVENT_INACTIVE;
23312Sigor@sysoev.ru 
23412Sigor@sysoev.ru     if (ev->read == NXT_EVENT_INACTIVE) {
23512Sigor@sysoev.ru         op = NXT_POLL_DELETE;
23612Sigor@sysoev.ru         events = 0;
23712Sigor@sysoev.ru 
23812Sigor@sysoev.ru     } else {
23912Sigor@sysoev.ru         op = NXT_POLL_CHANGE;
24012Sigor@sysoev.ru         events = POLLIN;
24112Sigor@sysoev.ru     }
24212Sigor@sysoev.ru 
24312Sigor@sysoev.ru     nxt_poll_change(engine, ev, op, events);
24412Sigor@sysoev.ru }
24512Sigor@sysoev.ru 
24612Sigor@sysoev.ru 
24712Sigor@sysoev.ru static void
nxt_poll_block_read(nxt_event_engine_t * engine,nxt_fd_event_t * ev)24812Sigor@sysoev.ru nxt_poll_block_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
24912Sigor@sysoev.ru {
25012Sigor@sysoev.ru     if (ev->read != NXT_EVENT_INACTIVE) {
25112Sigor@sysoev.ru         nxt_poll_disable_read(engine, ev);
25212Sigor@sysoev.ru     }
25312Sigor@sysoev.ru }
25412Sigor@sysoev.ru 
25512Sigor@sysoev.ru 
25612Sigor@sysoev.ru static void
nxt_poll_block_write(nxt_event_engine_t * engine,nxt_fd_event_t * ev)25712Sigor@sysoev.ru nxt_poll_block_write(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
25812Sigor@sysoev.ru {
25912Sigor@sysoev.ru     if (ev->write != NXT_EVENT_INACTIVE) {
26012Sigor@sysoev.ru         nxt_poll_disable_write(engine, ev);
26112Sigor@sysoev.ru     }
26212Sigor@sysoev.ru }
26312Sigor@sysoev.ru 
26412Sigor@sysoev.ru 
26512Sigor@sysoev.ru static void
nxt_poll_oneshot_read(nxt_event_engine_t * engine,nxt_fd_event_t * ev)26612Sigor@sysoev.ru nxt_poll_oneshot_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
26712Sigor@sysoev.ru {
26812Sigor@sysoev.ru     nxt_uint_t  op;
26912Sigor@sysoev.ru 
27012Sigor@sysoev.ru     op = (ev->read == NXT_EVENT_INACTIVE && ev->write == NXT_EVENT_INACTIVE) ?
27112Sigor@sysoev.ru              NXT_POLL_ADD : NXT_POLL_CHANGE;
27212Sigor@sysoev.ru 
27312Sigor@sysoev.ru     ev->read = NXT_EVENT_ONESHOT;
27412Sigor@sysoev.ru     ev->write = NXT_EVENT_INACTIVE;
27512Sigor@sysoev.ru 
27612Sigor@sysoev.ru     nxt_poll_change(engine, ev, op, POLLIN);
27712Sigor@sysoev.ru }
27812Sigor@sysoev.ru 
27912Sigor@sysoev.ru 
28012Sigor@sysoev.ru static void
nxt_poll_oneshot_write(nxt_event_engine_t * engine,nxt_fd_event_t * ev)28112Sigor@sysoev.ru nxt_poll_oneshot_write(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
28212Sigor@sysoev.ru {
28312Sigor@sysoev.ru     nxt_uint_t  op;
28412Sigor@sysoev.ru 
28512Sigor@sysoev.ru     op = (ev->read == NXT_EVENT_INACTIVE && ev->write == NXT_EVENT_INACTIVE) ?
28612Sigor@sysoev.ru              NXT_POLL_ADD : NXT_POLL_CHANGE;
28712Sigor@sysoev.ru 
28812Sigor@sysoev.ru     ev->read = NXT_EVENT_INACTIVE;
28912Sigor@sysoev.ru     ev->write = NXT_EVENT_ONESHOT;
29012Sigor@sysoev.ru 
29112Sigor@sysoev.ru     nxt_poll_change(engine, ev, op, POLLOUT);
29212Sigor@sysoev.ru }
29312Sigor@sysoev.ru 
29412Sigor@sysoev.ru 
29512Sigor@sysoev.ru /*
29612Sigor@sysoev.ru  * poll changes are batched to improve instruction and data cache
29712Sigor@sysoev.ru  * locality of several lvlhsh operations followed by poll() call.
29812Sigor@sysoev.ru  */
29912Sigor@sysoev.ru 
30012Sigor@sysoev.ru static void
nxt_poll_change(nxt_event_engine_t * engine,nxt_fd_event_t * ev,nxt_uint_t op,nxt_uint_t events)30112Sigor@sysoev.ru nxt_poll_change(nxt_event_engine_t *engine, nxt_fd_event_t *ev, nxt_uint_t op,
30212Sigor@sysoev.ru     nxt_uint_t events)
30312Sigor@sysoev.ru {
30412Sigor@sysoev.ru     nxt_poll_change_t  *change;
30512Sigor@sysoev.ru 
30612Sigor@sysoev.ru     nxt_debug(ev->task, "poll change: fd:%d op:%d ev:%XD", ev->fd, op, events);
30712Sigor@sysoev.ru 
30812Sigor@sysoev.ru     if (engine->u.poll.nchanges >= engine->u.poll.mchanges) {
30912Sigor@sysoev.ru         (void) nxt_poll_commit_changes(engine);
31012Sigor@sysoev.ru     }
31112Sigor@sysoev.ru 
31212Sigor@sysoev.ru     ev->changing = 1;
31312Sigor@sysoev.ru 
31412Sigor@sysoev.ru     change = &engine->u.poll.changes[engine->u.poll.nchanges++];
31512Sigor@sysoev.ru     change->op = op;
31612Sigor@sysoev.ru     change->events = events;
31712Sigor@sysoev.ru     change->event = ev;
31812Sigor@sysoev.ru }
31912Sigor@sysoev.ru 
32012Sigor@sysoev.ru 
32112Sigor@sysoev.ru static nxt_int_t
nxt_poll_commit_changes(nxt_event_engine_t * engine)32212Sigor@sysoev.ru nxt_poll_commit_changes(nxt_event_engine_t *engine)
32312Sigor@sysoev.ru {
32412Sigor@sysoev.ru     nxt_int_t          ret, retval;
32512Sigor@sysoev.ru     nxt_fd_event_t     *ev;
32612Sigor@sysoev.ru     nxt_poll_change_t  *change, *end;
32712Sigor@sysoev.ru 
32812Sigor@sysoev.ru     nxt_debug(&engine->task, "poll changes:%ui", engine->u.poll.nchanges);
32912Sigor@sysoev.ru 
33012Sigor@sysoev.ru     retval = NXT_OK;
33112Sigor@sysoev.ru     change = engine->u.poll.changes;
33212Sigor@sysoev.ru     end = change + engine->u.poll.nchanges;
33312Sigor@sysoev.ru 
33412Sigor@sysoev.ru     do {
33512Sigor@sysoev.ru         ev = change->event;
33612Sigor@sysoev.ru         ev->changing = 0;
33712Sigor@sysoev.ru 
33812Sigor@sysoev.ru         switch (change->op) {
33912Sigor@sysoev.ru 
34012Sigor@sysoev.ru         case NXT_POLL_ADD:
34112Sigor@sysoev.ru             ret = nxt_poll_set_add(engine, ev, change->events);
34212Sigor@sysoev.ru 
34312Sigor@sysoev.ru             if (nxt_fast_path(ret == NXT_OK)) {
34412Sigor@sysoev.ru                 goto next;
34512Sigor@sysoev.ru             }
34612Sigor@sysoev.ru 
34712Sigor@sysoev.ru             break;
34812Sigor@sysoev.ru 
34912Sigor@sysoev.ru         case NXT_POLL_CHANGE:
35012Sigor@sysoev.ru             ret = nxt_poll_set_change(engine, ev->fd, change->events);
35112Sigor@sysoev.ru 
35212Sigor@sysoev.ru             if (nxt_fast_path(ret == NXT_OK)) {
35312Sigor@sysoev.ru                 goto next;
35412Sigor@sysoev.ru             }
35512Sigor@sysoev.ru 
35612Sigor@sysoev.ru             break;
35712Sigor@sysoev.ru 
35812Sigor@sysoev.ru         case NXT_POLL_DELETE:
35912Sigor@sysoev.ru             ret = nxt_poll_set_delete(engine, ev->fd);
36012Sigor@sysoev.ru 
36112Sigor@sysoev.ru             if (nxt_fast_path(ret == NXT_OK)) {
36212Sigor@sysoev.ru                 goto next;
36312Sigor@sysoev.ru             }
36412Sigor@sysoev.ru 
36512Sigor@sysoev.ru             break;
36612Sigor@sysoev.ru         }
36712Sigor@sysoev.ru 
36812Sigor@sysoev.ru         nxt_work_queue_add(&engine->fast_work_queue, ev->error_handler,
36912Sigor@sysoev.ru                            ev->task, ev, ev->data);
37012Sigor@sysoev.ru 
37112Sigor@sysoev.ru         retval = NXT_ERROR;
37212Sigor@sysoev.ru 
373*1008Szelenkov@nginx.com     next:
37412Sigor@sysoev.ru 
37512Sigor@sysoev.ru         change++;
37612Sigor@sysoev.ru 
37712Sigor@sysoev.ru     } while (change < end);
37812Sigor@sysoev.ru 
37912Sigor@sysoev.ru     engine->u.poll.nchanges = 0;
38012Sigor@sysoev.ru 
38112Sigor@sysoev.ru     return retval;
38212Sigor@sysoev.ru }
38312Sigor@sysoev.ru 
38412Sigor@sysoev.ru 
38512Sigor@sysoev.ru static nxt_int_t
nxt_poll_set_add(nxt_event_engine_t * engine,nxt_fd_event_t * ev,int events)38612Sigor@sysoev.ru nxt_poll_set_add(nxt_event_engine_t *engine, nxt_fd_event_t *ev, int events)
38712Sigor@sysoev.ru {
38812Sigor@sysoev.ru     nxt_int_t              ret;
38912Sigor@sysoev.ru     nxt_uint_t             max_nfds;
39012Sigor@sysoev.ru     struct pollfd          *pfd;
39112Sigor@sysoev.ru     nxt_lvlhsh_query_t     lhq;
39212Sigor@sysoev.ru     nxt_poll_hash_entry_t  *phe;
39312Sigor@sysoev.ru 
39412Sigor@sysoev.ru     nxt_debug(&engine->task, "poll add event: fd:%d ev:%04Xi", ev->fd, events);
39512Sigor@sysoev.ru 
39612Sigor@sysoev.ru     if (engine->u.poll.nfds >= engine->u.poll.max_nfds) {
39712Sigor@sysoev.ru         max_nfds = engine->u.poll.max_nfds + 512; /* 4K */
39812Sigor@sysoev.ru 
39912Sigor@sysoev.ru         pfd = nxt_realloc(engine->u.poll.set, sizeof(struct pollfd) * max_nfds);
40012Sigor@sysoev.ru         if (nxt_slow_path(pfd == NULL)) {
40112Sigor@sysoev.ru             return NXT_ERROR;
40212Sigor@sysoev.ru         }
40312Sigor@sysoev.ru 
40412Sigor@sysoev.ru         engine->u.poll.set = pfd;
40512Sigor@sysoev.ru         engine->u.poll.max_nfds = max_nfds;
40612Sigor@sysoev.ru     }
40712Sigor@sysoev.ru 
40812Sigor@sysoev.ru     phe = nxt_malloc(sizeof(nxt_poll_hash_entry_t));
40912Sigor@sysoev.ru     if (nxt_slow_path(phe == NULL)) {
41012Sigor@sysoev.ru         return NXT_ERROR;
41112Sigor@sysoev.ru     }
41212Sigor@sysoev.ru 
41312Sigor@sysoev.ru     phe->fd = ev->fd;
41412Sigor@sysoev.ru     phe->index = engine->u.poll.nfds;
41512Sigor@sysoev.ru     phe->event = ev;
41612Sigor@sysoev.ru 
41712Sigor@sysoev.ru     pfd = &engine->u.poll.set[engine->u.poll.nfds++];
41812Sigor@sysoev.ru     pfd->fd = ev->fd;
41912Sigor@sysoev.ru     pfd->events = events;
42012Sigor@sysoev.ru     pfd->revents = 0;
42112Sigor@sysoev.ru 
42212Sigor@sysoev.ru     lhq.key_hash = nxt_murmur_hash2(&ev->fd, sizeof(nxt_fd_t));
42312Sigor@sysoev.ru     lhq.replace = 0;
42412Sigor@sysoev.ru     lhq.value = phe;
42512Sigor@sysoev.ru     lhq.proto = &nxt_poll_fd_hash_proto;
42612Sigor@sysoev.ru     lhq.data = engine;
42712Sigor@sysoev.ru 
42812Sigor@sysoev.ru     ret = nxt_lvlhsh_insert(&engine->u.poll.fd_hash, &lhq);
42912Sigor@sysoev.ru 
43012Sigor@sysoev.ru     if (nxt_fast_path(ret == NXT_OK)) {
43112Sigor@sysoev.ru         return NXT_OK;
43212Sigor@sysoev.ru     }
43312Sigor@sysoev.ru 
43412Sigor@sysoev.ru     nxt_free(phe);
43512Sigor@sysoev.ru 
43612Sigor@sysoev.ru     return NXT_ERROR;
43712Sigor@sysoev.ru }
43812Sigor@sysoev.ru 
43912Sigor@sysoev.ru 
44012Sigor@sysoev.ru static nxt_int_t
nxt_poll_set_change(nxt_event_engine_t * engine,nxt_fd_t fd,int events)44112Sigor@sysoev.ru nxt_poll_set_change(nxt_event_engine_t *engine, nxt_fd_t fd, int events)
44212Sigor@sysoev.ru {
44312Sigor@sysoev.ru     nxt_poll_hash_entry_t  *phe;
44412Sigor@sysoev.ru 
44512Sigor@sysoev.ru     nxt_debug(&engine->task, "poll change event: fd:%d ev:%04Xi",
44612Sigor@sysoev.ru               fd, events);
44712Sigor@sysoev.ru 
44812Sigor@sysoev.ru     phe = nxt_poll_fd_hash_get(engine, fd);
44912Sigor@sysoev.ru 
45012Sigor@sysoev.ru     if (nxt_fast_path(phe != NULL)) {
45112Sigor@sysoev.ru         engine->u.poll.set[phe->index].events = events;
45212Sigor@sysoev.ru         return NXT_OK;
45312Sigor@sysoev.ru     }
45412Sigor@sysoev.ru 
45512Sigor@sysoev.ru     return NXT_ERROR;
45612Sigor@sysoev.ru }
45712Sigor@sysoev.ru 
45812Sigor@sysoev.ru 
45912Sigor@sysoev.ru static nxt_int_t
nxt_poll_set_delete(nxt_event_engine_t * engine,nxt_fd_t fd)46012Sigor@sysoev.ru nxt_poll_set_delete(nxt_event_engine_t *engine, nxt_fd_t fd)
46112Sigor@sysoev.ru {
46212Sigor@sysoev.ru     nxt_int_t              ret;
46312Sigor@sysoev.ru     nxt_uint_t             index, nfds;
46412Sigor@sysoev.ru     nxt_lvlhsh_query_t     lhq;
46512Sigor@sysoev.ru     nxt_poll_hash_entry_t  *phe;
46612Sigor@sysoev.ru 
46712Sigor@sysoev.ru     nxt_debug(&engine->task, "poll delete event: fd:%d", fd);
46812Sigor@sysoev.ru 
46912Sigor@sysoev.ru     lhq.key_hash = nxt_murmur_hash2(&fd, sizeof(nxt_fd_t));
47012Sigor@sysoev.ru     lhq.proto = &nxt_poll_fd_hash_proto;
47112Sigor@sysoev.ru     lhq.data = engine;
47212Sigor@sysoev.ru 
47312Sigor@sysoev.ru     ret = nxt_lvlhsh_delete(&engine->u.poll.fd_hash, &lhq);
47412Sigor@sysoev.ru 
47512Sigor@sysoev.ru     if (nxt_slow_path(ret != NXT_OK)) {
47612Sigor@sysoev.ru         return NXT_ERROR;
47712Sigor@sysoev.ru     }
47812Sigor@sysoev.ru 
47912Sigor@sysoev.ru     phe = lhq.value;
48012Sigor@sysoev.ru 
48112Sigor@sysoev.ru     index = phe->index;
48212Sigor@sysoev.ru     engine->u.poll.nfds--;
48312Sigor@sysoev.ru     nfds = engine->u.poll.nfds;
48412Sigor@sysoev.ru 
48512Sigor@sysoev.ru     if (index != nfds) {
48612Sigor@sysoev.ru         engine->u.poll.set[index] = engine->u.poll.set[nfds];
48712Sigor@sysoev.ru 
48812Sigor@sysoev.ru         phe = nxt_poll_fd_hash_get(engine, engine->u.poll.set[nfds].fd);
48912Sigor@sysoev.ru 
49012Sigor@sysoev.ru         phe->index = index;
49112Sigor@sysoev.ru     }
49212Sigor@sysoev.ru 
49312Sigor@sysoev.ru     nxt_free(lhq.value);
49412Sigor@sysoev.ru 
49512Sigor@sysoev.ru     return NXT_OK;
49612Sigor@sysoev.ru }
49712Sigor@sysoev.ru 
49812Sigor@sysoev.ru 
49912Sigor@sysoev.ru static void
nxt_poll(nxt_event_engine_t * engine,nxt_msec_t timeout)50012Sigor@sysoev.ru nxt_poll(nxt_event_engine_t *engine, nxt_msec_t timeout)
50112Sigor@sysoev.ru {
50212Sigor@sysoev.ru     int                    nevents;
50312Sigor@sysoev.ru     nxt_fd_t               fd;
50412Sigor@sysoev.ru     nxt_err_t              err;
50512Sigor@sysoev.ru     nxt_bool_t             error;
50612Sigor@sysoev.ru     nxt_uint_t             i, events, level;
50712Sigor@sysoev.ru     struct pollfd          *pfd;
50812Sigor@sysoev.ru     nxt_fd_event_t         *ev;
50912Sigor@sysoev.ru     nxt_poll_hash_entry_t  *phe;
51012Sigor@sysoev.ru 
51112Sigor@sysoev.ru     if (engine->u.poll.nchanges != 0) {
51212Sigor@sysoev.ru         if (nxt_poll_commit_changes(engine) != NXT_OK) {
51312Sigor@sysoev.ru             /* Error handlers have been enqueued on failure. */
51412Sigor@sysoev.ru             timeout = 0;
51512Sigor@sysoev.ru         }
51612Sigor@sysoev.ru     }
51712Sigor@sysoev.ru 
51812Sigor@sysoev.ru     nxt_debug(&engine->task, "poll() events:%ui timeout:%M",
51912Sigor@sysoev.ru               engine->u.poll.nfds, timeout);
52012Sigor@sysoev.ru 
52112Sigor@sysoev.ru     nevents = poll(engine->u.poll.set, engine->u.poll.nfds, timeout);
52212Sigor@sysoev.ru 
52312Sigor@sysoev.ru     err = (nevents == -1) ? nxt_errno : 0;
52412Sigor@sysoev.ru 
52512Sigor@sysoev.ru     nxt_thread_time_update(engine->task.thread);
52612Sigor@sysoev.ru 
52712Sigor@sysoev.ru     nxt_debug(&engine->task, "poll(): %d", nevents);
52812Sigor@sysoev.ru 
52912Sigor@sysoev.ru     if (nevents == -1) {
53012Sigor@sysoev.ru         level = (err == NXT_EINTR) ? NXT_LOG_INFO : NXT_LOG_ALERT;
53112Sigor@sysoev.ru         nxt_log(&engine->task, level, "poll() failed %E", err);
53212Sigor@sysoev.ru         return;
53312Sigor@sysoev.ru     }
53412Sigor@sysoev.ru 
53512Sigor@sysoev.ru     for (i = 0; i < engine->u.poll.nfds && nevents != 0; i++) {
53612Sigor@sysoev.ru 
53712Sigor@sysoev.ru         pfd = &engine->u.poll.set[i];
53812Sigor@sysoev.ru         events = pfd->revents;
53912Sigor@sysoev.ru 
54012Sigor@sysoev.ru         if (events == 0) {
54112Sigor@sysoev.ru             continue;
54212Sigor@sysoev.ru         }
54312Sigor@sysoev.ru 
54412Sigor@sysoev.ru         fd = pfd->fd;
54512Sigor@sysoev.ru 
54612Sigor@sysoev.ru         phe = nxt_poll_fd_hash_get(engine, fd);
54712Sigor@sysoev.ru 
54812Sigor@sysoev.ru         if (nxt_slow_path(phe == NULL)) {
549564Svbart@nginx.com             nxt_alert(&engine->task,
550564Svbart@nginx.com                       "poll() returned invalid fd:%d ev:%04Xd rev:%04uXi",
551564Svbart@nginx.com                       fd, pfd->events, events);
55212Sigor@sysoev.ru 
55312Sigor@sysoev.ru             /* Mark the poll entry to ignore it by the kernel. */
55412Sigor@sysoev.ru             pfd->fd = -1;
55512Sigor@sysoev.ru             goto next;
55612Sigor@sysoev.ru         }
55712Sigor@sysoev.ru 
55812Sigor@sysoev.ru         ev = phe->event;
55912Sigor@sysoev.ru 
560494Spluknet@nginx.com         nxt_debug(ev->task, "poll: fd:%d ev:%04uXi rd:%d wr:%d",
56112Sigor@sysoev.ru                   fd, events, ev->read, ev->write);
56212Sigor@sysoev.ru 
56312Sigor@sysoev.ru         if (nxt_slow_path((events & POLLNVAL) != 0)) {
564564Svbart@nginx.com             nxt_alert(ev->task, "poll() error fd:%d ev:%04Xd rev:%04uXi",
565564Svbart@nginx.com                       fd, pfd->events, events);
56612Sigor@sysoev.ru 
56712Sigor@sysoev.ru             /* Mark the poll entry to ignore it by the kernel. */
56812Sigor@sysoev.ru             pfd->fd = -1;
56912Sigor@sysoev.ru 
57012Sigor@sysoev.ru             nxt_work_queue_add(&engine->fast_work_queue,
57112Sigor@sysoev.ru                                ev->error_handler, ev->task, ev, ev->data);
57212Sigor@sysoev.ru             goto next;
57312Sigor@sysoev.ru         }
57412Sigor@sysoev.ru 
57512Sigor@sysoev.ru         /*
57612Sigor@sysoev.ru          * On a socket's remote end close:
57712Sigor@sysoev.ru          *
57812Sigor@sysoev.ru          *   Linux, FreeBSD, and Solaris set POLLIN;
57912Sigor@sysoev.ru          *   MacOSX sets POLLIN and POLLHUP;
58012Sigor@sysoev.ru          *   NetBSD sets POLLIN, and poll(2) claims this explicitly:
58112Sigor@sysoev.ru          *
58212Sigor@sysoev.ru          *     If the remote end of a socket is closed, poll()
58312Sigor@sysoev.ru          *     returns a POLLIN event, rather than a POLLHUP.
58412Sigor@sysoev.ru          *
58512Sigor@sysoev.ru          * On error:
58612Sigor@sysoev.ru          *
58712Sigor@sysoev.ru          *   Linux sets POLLHUP and POLLERR only;
58812Sigor@sysoev.ru          *   FreeBSD adds POLLHUP to POLLIN or POLLOUT, although poll(2)
58912Sigor@sysoev.ru          *   claims the opposite:
59012Sigor@sysoev.ru          *
59112Sigor@sysoev.ru          *     Note that POLLHUP and POLLOUT should never be
59212Sigor@sysoev.ru          *     present in the revents bitmask at the same time.
59312Sigor@sysoev.ru          *
59412Sigor@sysoev.ru          *   Solaris and NetBSD do not add POLLHUP or POLLERR;
59512Sigor@sysoev.ru          *   MacOSX sets POLLHUP only.
59612Sigor@sysoev.ru          *
59712Sigor@sysoev.ru          * If an implementation sets POLLERR or POLLHUP only without POLLIN
59812Sigor@sysoev.ru          * or POLLOUT, the "error" variable enqueues only one active handler.
59912Sigor@sysoev.ru          */
60012Sigor@sysoev.ru 
60112Sigor@sysoev.ru         error = (((events & (POLLERR | POLLHUP)) != 0)
60212Sigor@sysoev.ru                  && ((events & (POLLIN | POLLOUT)) == 0));
60312Sigor@sysoev.ru 
60412Sigor@sysoev.ru         if ((events & POLLIN) || (error && ev->read_handler != NULL)) {
60512Sigor@sysoev.ru             error = 0;
60612Sigor@sysoev.ru             ev->read_ready = 1;
60712Sigor@sysoev.ru 
60812Sigor@sysoev.ru             if (ev->read == NXT_EVENT_ONESHOT) {
60912Sigor@sysoev.ru                 ev->read = NXT_EVENT_INACTIVE;
61012Sigor@sysoev.ru                 nxt_poll_change(engine, ev, NXT_POLL_DELETE, 0);
61112Sigor@sysoev.ru             }
61212Sigor@sysoev.ru 
61312Sigor@sysoev.ru             nxt_work_queue_add(ev->read_work_queue, ev->read_handler,
61412Sigor@sysoev.ru                                ev->task, ev, ev->data);
61512Sigor@sysoev.ru         }
61612Sigor@sysoev.ru 
61712Sigor@sysoev.ru         if ((events & POLLOUT) || (error && ev->write_handler != NULL)) {
61812Sigor@sysoev.ru             ev->write_ready = 1;
61912Sigor@sysoev.ru 
62012Sigor@sysoev.ru             if (ev->write == NXT_EVENT_ONESHOT) {
62112Sigor@sysoev.ru                 ev->write = NXT_EVENT_INACTIVE;
62212Sigor@sysoev.ru                 nxt_poll_change(engine, ev, NXT_POLL_DELETE, 0);
62312Sigor@sysoev.ru             }
62412Sigor@sysoev.ru 
62512Sigor@sysoev.ru             nxt_work_queue_add(ev->write_work_queue, ev->write_handler,
62612Sigor@sysoev.ru                                ev->task, ev, ev->data);
62712Sigor@sysoev.ru         }
62812Sigor@sysoev.ru 
62912Sigor@sysoev.ru     next:
63012Sigor@sysoev.ru 
63112Sigor@sysoev.ru         nevents--;
63212Sigor@sysoev.ru     }
63312Sigor@sysoev.ru }
63412Sigor@sysoev.ru 
63512Sigor@sysoev.ru 
63612Sigor@sysoev.ru static nxt_poll_hash_entry_t *
nxt_poll_fd_hash_get(nxt_event_engine_t * engine,nxt_fd_t fd)63712Sigor@sysoev.ru nxt_poll_fd_hash_get(nxt_event_engine_t *engine, nxt_fd_t fd)
63812Sigor@sysoev.ru {
63912Sigor@sysoev.ru     nxt_lvlhsh_query_t     lhq;
64012Sigor@sysoev.ru     nxt_poll_hash_entry_t  *phe;
64112Sigor@sysoev.ru 
64212Sigor@sysoev.ru     lhq.key_hash = nxt_murmur_hash2(&fd, sizeof(nxt_fd_t));
64312Sigor@sysoev.ru     lhq.proto = &nxt_poll_fd_hash_proto;
64412Sigor@sysoev.ru     lhq.data = engine;
64512Sigor@sysoev.ru 
64612Sigor@sysoev.ru     if (nxt_lvlhsh_find(&engine->u.poll.fd_hash, &lhq) == NXT_OK) {
64712Sigor@sysoev.ru         phe = lhq.value;
64812Sigor@sysoev.ru         return phe;
64912Sigor@sysoev.ru     }
65012Sigor@sysoev.ru 
651564Svbart@nginx.com     nxt_alert(&engine->task, "fd %d not found in hash", fd);
65212Sigor@sysoev.ru 
65312Sigor@sysoev.ru     return NULL;
65412Sigor@sysoev.ru }
65512Sigor@sysoev.ru 
65612Sigor@sysoev.ru 
65712Sigor@sysoev.ru static nxt_int_t
nxt_poll_fd_hash_test(nxt_lvlhsh_query_t * lhq,void * data)65812Sigor@sysoev.ru nxt_poll_fd_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
65912Sigor@sysoev.ru {
66012Sigor@sysoev.ru     nxt_event_engine_t     *engine;
66112Sigor@sysoev.ru     nxt_poll_hash_entry_t  *phe;
66212Sigor@sysoev.ru 
66312Sigor@sysoev.ru     phe = data;
66412Sigor@sysoev.ru 
66512Sigor@sysoev.ru     /* nxt_murmur_hash2() is unique for 4 bytes. */
66612Sigor@sysoev.ru 
66712Sigor@sysoev.ru     engine = lhq->data;
66812Sigor@sysoev.ru 
66912Sigor@sysoev.ru     if (nxt_fast_path(phe->fd == engine->u.poll.set[phe->index].fd)) {
67012Sigor@sysoev.ru         return NXT_OK;
67112Sigor@sysoev.ru     }
67212Sigor@sysoev.ru 
673564Svbart@nginx.com     nxt_alert(&engine->task, "fd %d in hash mismatches fd %d in poll set",
674564Svbart@nginx.com               phe->fd, engine->u.poll.set[phe->index].fd);
67512Sigor@sysoev.ru 
67612Sigor@sysoev.ru     return NXT_DECLINED;
67712Sigor@sysoev.ru }
67812Sigor@sysoev.ru 
67912Sigor@sysoev.ru 
68012Sigor@sysoev.ru static void
nxt_poll_fd_hash_destroy(nxt_event_engine_t * engine,nxt_lvlhsh_t * lh)68112Sigor@sysoev.ru nxt_poll_fd_hash_destroy(nxt_event_engine_t *engine, nxt_lvlhsh_t *lh)
68212Sigor@sysoev.ru {
68312Sigor@sysoev.ru     nxt_poll_hash_entry_t  *phe;
68412Sigor@sysoev.ru 
68512Sigor@sysoev.ru     for ( ;; ) {
686596Sigor@sysoev.ru         phe = nxt_lvlhsh_retrieve(lh, &nxt_poll_fd_hash_proto, NULL);
68712Sigor@sysoev.ru 
68812Sigor@sysoev.ru         if (phe == NULL) {
68912Sigor@sysoev.ru             return;
69012Sigor@sysoev.ru         }
69112Sigor@sysoev.ru 
69212Sigor@sysoev.ru         nxt_free(phe);
69312Sigor@sysoev.ru     }
69412Sigor@sysoev.ru }
695