xref: /unit/src/nxt_devpoll_engine.c (revision 564:762f8c976ead)
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  * "/dev/poll" has been introduced in Solaris 7 (11/99), HP-UX 11.22 (named
1212Sigor@sysoev.ru  * "eventport pseudo driver" internally, not to be confused with Solaris 10
1312Sigor@sysoev.ru  * event ports), IRIX 6.5.15, and Tru64 UNIX 5.1A.
1412Sigor@sysoev.ru  *
1512Sigor@sysoev.ru  * Although "/dev/poll" descriptor is a file descriptor, nevertheless
1612Sigor@sysoev.ru  * it cannot be added to another poll set, Solaris poll(7d):
1712Sigor@sysoev.ru  *
1812Sigor@sysoev.ru  *   The /dev/poll driver does not yet support polling.  Polling on a
1912Sigor@sysoev.ru  *   /dev/poll file descriptor will result in POLLERR being returned
2012Sigor@sysoev.ru  *   in the revents field of pollfd structure.
2112Sigor@sysoev.ru  */
2212Sigor@sysoev.ru 
2312Sigor@sysoev.ru 
2412Sigor@sysoev.ru #define NXT_DEVPOLL_ADD     0
2512Sigor@sysoev.ru #define NXT_DEVPOLL_UPDATE  1
2612Sigor@sysoev.ru #define NXT_DEVPOLL_CHANGE  2
2712Sigor@sysoev.ru #define NXT_DEVPOLL_DELETE  3
2812Sigor@sysoev.ru 
2912Sigor@sysoev.ru 
3012Sigor@sysoev.ru static nxt_int_t nxt_devpoll_create(nxt_event_engine_t *engine,
3112Sigor@sysoev.ru     nxt_uint_t mchanges, nxt_uint_t mevents);
3212Sigor@sysoev.ru static void nxt_devpoll_free(nxt_event_engine_t *engine);
3312Sigor@sysoev.ru static void nxt_devpoll_enable(nxt_event_engine_t *engine, nxt_fd_event_t *ev);
3412Sigor@sysoev.ru static void nxt_devpoll_disable(nxt_event_engine_t *engine, nxt_fd_event_t *ev);
3512Sigor@sysoev.ru static nxt_bool_t nxt_devpoll_close(nxt_event_engine_t *engine,
3612Sigor@sysoev.ru     nxt_fd_event_t *ev);
3712Sigor@sysoev.ru static void nxt_devpoll_enable_read(nxt_event_engine_t *engine,
3812Sigor@sysoev.ru     nxt_fd_event_t *ev);
3912Sigor@sysoev.ru static void nxt_devpoll_enable_write(nxt_event_engine_t *engine,
4012Sigor@sysoev.ru     nxt_fd_event_t *ev);
4112Sigor@sysoev.ru static void nxt_devpoll_disable_read(nxt_event_engine_t *engine,
4212Sigor@sysoev.ru     nxt_fd_event_t *ev);
4312Sigor@sysoev.ru static void nxt_devpoll_disable_write(nxt_event_engine_t *engine,
4412Sigor@sysoev.ru     nxt_fd_event_t *ev);
4512Sigor@sysoev.ru static void nxt_devpoll_block_read(nxt_event_engine_t *engine,
4612Sigor@sysoev.ru     nxt_fd_event_t *ev);
4712Sigor@sysoev.ru static void nxt_devpoll_block_write(nxt_event_engine_t *engine,
4812Sigor@sysoev.ru     nxt_fd_event_t *ev);
4912Sigor@sysoev.ru static void nxt_devpoll_oneshot_read(nxt_event_engine_t *engine,
5012Sigor@sysoev.ru     nxt_fd_event_t *ev);
5112Sigor@sysoev.ru static void nxt_devpoll_oneshot_write(nxt_event_engine_t *engine,
5212Sigor@sysoev.ru     nxt_fd_event_t *ev);
5312Sigor@sysoev.ru static void nxt_devpoll_change(nxt_event_engine_t *engine, nxt_fd_event_t *ev,
5412Sigor@sysoev.ru     nxt_uint_t op, nxt_uint_t events);
5512Sigor@sysoev.ru static nxt_int_t nxt_devpoll_commit_changes(nxt_event_engine_t *engine);
5612Sigor@sysoev.ru static void nxt_devpoll_change_error(nxt_event_engine_t *engine,
5712Sigor@sysoev.ru     nxt_fd_event_t *ev);
5812Sigor@sysoev.ru static void nxt_devpoll_remove(nxt_event_engine_t *engine, nxt_fd_t fd);
5912Sigor@sysoev.ru static nxt_int_t nxt_devpoll_write(nxt_event_engine_t *engine,
6012Sigor@sysoev.ru     struct pollfd *pfd, size_t n);
6112Sigor@sysoev.ru static void nxt_devpoll_poll(nxt_event_engine_t *engine,
6212Sigor@sysoev.ru     nxt_msec_t timeout);
6312Sigor@sysoev.ru 
6412Sigor@sysoev.ru 
6512Sigor@sysoev.ru const nxt_event_interface_t  nxt_devpoll_engine = {
6612Sigor@sysoev.ru     "devpoll",
6712Sigor@sysoev.ru     nxt_devpoll_create,
6812Sigor@sysoev.ru     nxt_devpoll_free,
6912Sigor@sysoev.ru     nxt_devpoll_enable,
7012Sigor@sysoev.ru     nxt_devpoll_disable,
7112Sigor@sysoev.ru     nxt_devpoll_disable,
7212Sigor@sysoev.ru     nxt_devpoll_close,
7312Sigor@sysoev.ru     nxt_devpoll_enable_read,
7412Sigor@sysoev.ru     nxt_devpoll_enable_write,
7512Sigor@sysoev.ru     nxt_devpoll_disable_read,
7612Sigor@sysoev.ru     nxt_devpoll_disable_write,
7712Sigor@sysoev.ru     nxt_devpoll_block_read,
7812Sigor@sysoev.ru     nxt_devpoll_block_write,
7912Sigor@sysoev.ru     nxt_devpoll_oneshot_read,
8012Sigor@sysoev.ru     nxt_devpoll_oneshot_write,
8112Sigor@sysoev.ru     nxt_devpoll_enable_read,
8212Sigor@sysoev.ru     NULL,
8312Sigor@sysoev.ru     NULL,
8412Sigor@sysoev.ru     NULL,
8512Sigor@sysoev.ru     NULL,
8612Sigor@sysoev.ru     nxt_devpoll_poll,
8712Sigor@sysoev.ru 
8862Sigor@sysoev.ru     &nxt_unix_conn_io,
8912Sigor@sysoev.ru 
9012Sigor@sysoev.ru     NXT_NO_FILE_EVENTS,
9112Sigor@sysoev.ru     NXT_NO_SIGNAL_EVENTS,
9212Sigor@sysoev.ru };
9312Sigor@sysoev.ru 
9412Sigor@sysoev.ru 
9512Sigor@sysoev.ru static nxt_int_t
nxt_devpoll_create(nxt_event_engine_t * engine,nxt_uint_t mchanges,nxt_uint_t mevents)9612Sigor@sysoev.ru nxt_devpoll_create(nxt_event_engine_t *engine, nxt_uint_t mchanges,
9712Sigor@sysoev.ru     nxt_uint_t mevents)
9812Sigor@sysoev.ru {
9912Sigor@sysoev.ru     void  *changes;
10012Sigor@sysoev.ru 
10112Sigor@sysoev.ru     engine->u.devpoll.fd = -1;
10212Sigor@sysoev.ru     engine->u.devpoll.mchanges = mchanges;
10312Sigor@sysoev.ru     engine->u.devpoll.mevents = mevents;
10412Sigor@sysoev.ru 
10512Sigor@sysoev.ru     changes = nxt_malloc(sizeof(nxt_devpoll_change_t) * mchanges);
10612Sigor@sysoev.ru     if (changes == NULL) {
10712Sigor@sysoev.ru         goto fail;
10812Sigor@sysoev.ru     }
10912Sigor@sysoev.ru 
11012Sigor@sysoev.ru     engine->u.devpoll.changes = changes;
11112Sigor@sysoev.ru 
11212Sigor@sysoev.ru     /*
11312Sigor@sysoev.ru      * NXT_DEVPOLL_CHANGE requires two struct pollfd's:
11412Sigor@sysoev.ru      * for POLLREMOVE and subsequent POLLIN or POLLOUT.
11512Sigor@sysoev.ru      */
11612Sigor@sysoev.ru     changes = nxt_malloc(2 * sizeof(struct pollfd) * mchanges);
11712Sigor@sysoev.ru     if (changes == NULL) {
11812Sigor@sysoev.ru         goto fail;
11912Sigor@sysoev.ru     }
12012Sigor@sysoev.ru 
12112Sigor@sysoev.ru     engine->u.devpoll.write_changes = changes;
12212Sigor@sysoev.ru 
12312Sigor@sysoev.ru     engine->u.devpoll.events = nxt_malloc(sizeof(struct pollfd) * mevents);
12412Sigor@sysoev.ru     if (engine->u.devpoll.events == NULL) {
12512Sigor@sysoev.ru         goto fail;
12612Sigor@sysoev.ru     }
12712Sigor@sysoev.ru 
12812Sigor@sysoev.ru     engine->u.devpoll.fd = open("/dev/poll", O_RDWR);
12912Sigor@sysoev.ru 
13012Sigor@sysoev.ru     if (engine->u.devpoll.fd == -1) {
131*564Svbart@nginx.com         nxt_alert(&engine->task, "open(\"/dev/poll\") failed %E", nxt_errno);
13212Sigor@sysoev.ru         goto fail;
13312Sigor@sysoev.ru     }
13412Sigor@sysoev.ru 
13512Sigor@sysoev.ru     nxt_debug(&engine->task, "open(\"/dev/poll\"): %d", engine->u.devpoll.fd);
13612Sigor@sysoev.ru 
13712Sigor@sysoev.ru     return NXT_OK;
13812Sigor@sysoev.ru 
13912Sigor@sysoev.ru fail:
14012Sigor@sysoev.ru 
14112Sigor@sysoev.ru     nxt_devpoll_free(engine);
14212Sigor@sysoev.ru 
14312Sigor@sysoev.ru     return NXT_ERROR;
14412Sigor@sysoev.ru }
14512Sigor@sysoev.ru 
14612Sigor@sysoev.ru 
14712Sigor@sysoev.ru static void
nxt_devpoll_free(nxt_event_engine_t * engine)14812Sigor@sysoev.ru nxt_devpoll_free(nxt_event_engine_t *engine)
14912Sigor@sysoev.ru {
15012Sigor@sysoev.ru     nxt_fd_t  fd;
15112Sigor@sysoev.ru 
15212Sigor@sysoev.ru     fd = engine->u.devpoll.fd;
15312Sigor@sysoev.ru 
15412Sigor@sysoev.ru     nxt_debug(&engine->task, "devpoll %d free", fd);
15512Sigor@sysoev.ru 
156277Sigor@sysoev.ru     if (fd != -1 && close(fd) != 0) {
157*564Svbart@nginx.com         nxt_alert(&engine->task, "devpoll close(%d) failed %E", fd, nxt_errno);
15812Sigor@sysoev.ru     }
15912Sigor@sysoev.ru 
16012Sigor@sysoev.ru     nxt_free(engine->u.devpoll.events);
16112Sigor@sysoev.ru     nxt_free(engine->u.devpoll.write_changes);
16212Sigor@sysoev.ru     nxt_free(engine->u.devpoll.changes);
16312Sigor@sysoev.ru     nxt_fd_event_hash_destroy(&engine->u.devpoll.fd_hash);
16412Sigor@sysoev.ru 
16512Sigor@sysoev.ru     nxt_memzero(&engine->u.devpoll, sizeof(nxt_devpoll_engine_t));
16612Sigor@sysoev.ru }
16712Sigor@sysoev.ru 
16812Sigor@sysoev.ru 
16912Sigor@sysoev.ru static void
nxt_devpoll_enable(nxt_event_engine_t * engine,nxt_fd_event_t * ev)17012Sigor@sysoev.ru nxt_devpoll_enable(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
17112Sigor@sysoev.ru {
17212Sigor@sysoev.ru     ev->read = NXT_EVENT_ACTIVE;
17312Sigor@sysoev.ru     ev->write = NXT_EVENT_ACTIVE;
17412Sigor@sysoev.ru 
17512Sigor@sysoev.ru     nxt_devpoll_change(engine, ev, NXT_DEVPOLL_ADD, POLLIN | POLLOUT);
17612Sigor@sysoev.ru }
17712Sigor@sysoev.ru 
17812Sigor@sysoev.ru 
17912Sigor@sysoev.ru static void
nxt_devpoll_disable(nxt_event_engine_t * engine,nxt_fd_event_t * ev)18012Sigor@sysoev.ru nxt_devpoll_disable(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
18112Sigor@sysoev.ru {
18212Sigor@sysoev.ru     if (ev->read != NXT_EVENT_INACTIVE || ev->write != NXT_EVENT_INACTIVE) {
18312Sigor@sysoev.ru 
18412Sigor@sysoev.ru         ev->read = NXT_EVENT_INACTIVE;
18512Sigor@sysoev.ru         ev->write = NXT_EVENT_INACTIVE;
18612Sigor@sysoev.ru 
18712Sigor@sysoev.ru         nxt_devpoll_change(engine, ev, NXT_DEVPOLL_DELETE, POLLREMOVE);
18812Sigor@sysoev.ru     }
18912Sigor@sysoev.ru }
19012Sigor@sysoev.ru 
19112Sigor@sysoev.ru 
19212Sigor@sysoev.ru /*
19312Sigor@sysoev.ru  * Solaris does not automatically remove a closed file descriptor from
19412Sigor@sysoev.ru  * a "/dev/poll" set: ioctl(DP_ISPOLLED) for the descriptor returns 1,
19512Sigor@sysoev.ru  * significative of active descriptor.  POLLREMOVE can remove already
19612Sigor@sysoev.ru  * closed file descriptor, so the removal can be batched, Solaris poll(7d):
19712Sigor@sysoev.ru  *
19812Sigor@sysoev.ru  *   When using the "/dev/poll" driver, you should remove a closed file
19912Sigor@sysoev.ru  *   descriptor from a monitored poll set.  Failure to do so may result
20012Sigor@sysoev.ru  *   in a POLLNVAL revents being returned for the closed file descriptor.
20112Sigor@sysoev.ru  *   When a file descriptor is closed but not removed from the monitored
20212Sigor@sysoev.ru  *   set, and is reused in subsequent open of a different device, you
20312Sigor@sysoev.ru  *   will be polling the device associated with the reused file descriptor.
20412Sigor@sysoev.ru  *   In a multithreaded application, careful coordination among threads
20512Sigor@sysoev.ru  *   doing close and DP_POLL ioctl is recommended for consistent results.
20612Sigor@sysoev.ru  *
20712Sigor@sysoev.ru  * Besides Solaris and HP-UX allow to add invalid descriptors to an
20812Sigor@sysoev.ru  * "/dev/poll" set, although the descriptors are not marked as polled,
20912Sigor@sysoev.ru  * that is, ioctl(DP_ISPOLLED) returns 0.
21012Sigor@sysoev.ru  *
21112Sigor@sysoev.ru  * HP-UX poll(7):
21212Sigor@sysoev.ru  *
21312Sigor@sysoev.ru  *   When a polled file descriptor is closed, it is automatically
21412Sigor@sysoev.ru  *   deregistered.
21512Sigor@sysoev.ru  */
21612Sigor@sysoev.ru 
21712Sigor@sysoev.ru static nxt_bool_t
nxt_devpoll_close(nxt_event_engine_t * engine,nxt_fd_event_t * ev)21812Sigor@sysoev.ru nxt_devpoll_close(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
21912Sigor@sysoev.ru {
22012Sigor@sysoev.ru     nxt_devpoll_disable(engine, ev);
22112Sigor@sysoev.ru 
22212Sigor@sysoev.ru     return ev->changing;
22312Sigor@sysoev.ru }
22412Sigor@sysoev.ru 
22512Sigor@sysoev.ru 
22612Sigor@sysoev.ru /*
22712Sigor@sysoev.ru  * Solaris poll(7d):
22812Sigor@sysoev.ru  *
22912Sigor@sysoev.ru  *   The fd field specifies the file descriptor being polled.  The events
23012Sigor@sysoev.ru  *   field indicates the interested poll events on the file descriptor.
23112Sigor@sysoev.ru  *   If a pollfd array contains multiple pollfd entries with the same fd field,
23212Sigor@sysoev.ru  *   the "events" field in each pollfd entry is OR'ed.  A special POLLREMOVE
23312Sigor@sysoev.ru  *   event in the events field of the pollfd structure removes the fd from
23412Sigor@sysoev.ru  *   the monitored set. The revents field is not used.
23512Sigor@sysoev.ru  */
23612Sigor@sysoev.ru 
23712Sigor@sysoev.ru static void
nxt_devpoll_enable_read(nxt_event_engine_t * engine,nxt_fd_event_t * ev)23812Sigor@sysoev.ru nxt_devpoll_enable_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
23912Sigor@sysoev.ru {
24012Sigor@sysoev.ru     nxt_uint_t  op, events;
24112Sigor@sysoev.ru 
24212Sigor@sysoev.ru     if (ev->read != NXT_EVENT_BLOCKED) {
24312Sigor@sysoev.ru 
24412Sigor@sysoev.ru         events = POLLIN;
24512Sigor@sysoev.ru 
24612Sigor@sysoev.ru         if (ev->write == NXT_EVENT_INACTIVE) {
24712Sigor@sysoev.ru             op = NXT_DEVPOLL_ADD;
24812Sigor@sysoev.ru 
24912Sigor@sysoev.ru         } else if (ev->write == NXT_EVENT_BLOCKED) {
25012Sigor@sysoev.ru             ev->write = NXT_EVENT_INACTIVE;
25112Sigor@sysoev.ru             op = NXT_DEVPOLL_CHANGE;
25212Sigor@sysoev.ru 
25312Sigor@sysoev.ru         } else {
25412Sigor@sysoev.ru             op = NXT_DEVPOLL_UPDATE;
25512Sigor@sysoev.ru             events = POLLIN | POLLOUT;
25612Sigor@sysoev.ru         }
25712Sigor@sysoev.ru 
25812Sigor@sysoev.ru         nxt_devpoll_change(engine, ev, op, events);
25912Sigor@sysoev.ru     }
26012Sigor@sysoev.ru 
26112Sigor@sysoev.ru     ev->read = NXT_EVENT_ACTIVE;
26212Sigor@sysoev.ru }
26312Sigor@sysoev.ru 
26412Sigor@sysoev.ru 
26512Sigor@sysoev.ru static void
nxt_devpoll_enable_write(nxt_event_engine_t * engine,nxt_fd_event_t * ev)26612Sigor@sysoev.ru nxt_devpoll_enable_write(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
26712Sigor@sysoev.ru {
26812Sigor@sysoev.ru     nxt_uint_t  op, events;
26912Sigor@sysoev.ru 
27012Sigor@sysoev.ru     if (ev->write != NXT_EVENT_BLOCKED) {
27112Sigor@sysoev.ru 
27212Sigor@sysoev.ru         events = POLLOUT;
27312Sigor@sysoev.ru 
27412Sigor@sysoev.ru         if (ev->read == NXT_EVENT_INACTIVE) {
27512Sigor@sysoev.ru             op = NXT_DEVPOLL_ADD;
27612Sigor@sysoev.ru 
27712Sigor@sysoev.ru         } else if (ev->read == NXT_EVENT_BLOCKED) {
27812Sigor@sysoev.ru             ev->read = NXT_EVENT_INACTIVE;
27912Sigor@sysoev.ru             op = NXT_DEVPOLL_CHANGE;
28012Sigor@sysoev.ru 
28112Sigor@sysoev.ru         } else {
28212Sigor@sysoev.ru             op = NXT_DEVPOLL_UPDATE;
28312Sigor@sysoev.ru             events = POLLIN | POLLOUT;
28412Sigor@sysoev.ru         }
28512Sigor@sysoev.ru 
28612Sigor@sysoev.ru         nxt_devpoll_change(engine, ev, op, events);
28712Sigor@sysoev.ru     }
28812Sigor@sysoev.ru 
28912Sigor@sysoev.ru     ev->write = NXT_EVENT_ACTIVE;
29012Sigor@sysoev.ru }
29112Sigor@sysoev.ru 
29212Sigor@sysoev.ru 
29312Sigor@sysoev.ru static void
nxt_devpoll_disable_read(nxt_event_engine_t * engine,nxt_fd_event_t * ev)29412Sigor@sysoev.ru nxt_devpoll_disable_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
29512Sigor@sysoev.ru {
29612Sigor@sysoev.ru     nxt_uint_t  op, events;
29712Sigor@sysoev.ru 
29812Sigor@sysoev.ru     ev->read = NXT_EVENT_INACTIVE;
29912Sigor@sysoev.ru 
30012Sigor@sysoev.ru     if (ev->write <= NXT_EVENT_BLOCKED) {
30112Sigor@sysoev.ru         ev->write = NXT_EVENT_INACTIVE;
30212Sigor@sysoev.ru         op = NXT_DEVPOLL_DELETE;
30312Sigor@sysoev.ru         events = POLLREMOVE;
30412Sigor@sysoev.ru 
30512Sigor@sysoev.ru     } else {
30612Sigor@sysoev.ru         op = NXT_DEVPOLL_CHANGE;
30712Sigor@sysoev.ru         events = POLLOUT;
30812Sigor@sysoev.ru     }
30912Sigor@sysoev.ru 
31012Sigor@sysoev.ru     nxt_devpoll_change(engine, ev, op, events);
31112Sigor@sysoev.ru }
31212Sigor@sysoev.ru 
31312Sigor@sysoev.ru 
31412Sigor@sysoev.ru static void
nxt_devpoll_disable_write(nxt_event_engine_t * engine,nxt_fd_event_t * ev)31512Sigor@sysoev.ru nxt_devpoll_disable_write(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
31612Sigor@sysoev.ru {
31712Sigor@sysoev.ru     nxt_uint_t  op, events;
31812Sigor@sysoev.ru 
31912Sigor@sysoev.ru     ev->write = NXT_EVENT_INACTIVE;
32012Sigor@sysoev.ru 
32112Sigor@sysoev.ru     if (ev->read <= NXT_EVENT_BLOCKED) {
32212Sigor@sysoev.ru         ev->read = NXT_EVENT_INACTIVE;
32312Sigor@sysoev.ru         op = NXT_DEVPOLL_DELETE;
32412Sigor@sysoev.ru         events = POLLREMOVE;
32512Sigor@sysoev.ru 
32612Sigor@sysoev.ru     } else {
32712Sigor@sysoev.ru         op = NXT_DEVPOLL_CHANGE;
32812Sigor@sysoev.ru         events = POLLIN;
32912Sigor@sysoev.ru     }
33012Sigor@sysoev.ru 
33112Sigor@sysoev.ru     nxt_devpoll_change(engine, ev, op, events);
33212Sigor@sysoev.ru }
33312Sigor@sysoev.ru 
33412Sigor@sysoev.ru 
33512Sigor@sysoev.ru static void
nxt_devpoll_block_read(nxt_event_engine_t * engine,nxt_fd_event_t * ev)33612Sigor@sysoev.ru nxt_devpoll_block_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
33712Sigor@sysoev.ru {
33812Sigor@sysoev.ru     if (ev->read != NXT_EVENT_INACTIVE) {
33912Sigor@sysoev.ru         ev->read = NXT_EVENT_BLOCKED;
34012Sigor@sysoev.ru     }
34112Sigor@sysoev.ru }
34212Sigor@sysoev.ru 
34312Sigor@sysoev.ru 
34412Sigor@sysoev.ru static void
nxt_devpoll_block_write(nxt_event_engine_t * engine,nxt_fd_event_t * ev)34512Sigor@sysoev.ru nxt_devpoll_block_write(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
34612Sigor@sysoev.ru {
34712Sigor@sysoev.ru     if (ev->write != NXT_EVENT_INACTIVE) {
34812Sigor@sysoev.ru         ev->write = NXT_EVENT_BLOCKED;
34912Sigor@sysoev.ru     }
35012Sigor@sysoev.ru }
35112Sigor@sysoev.ru 
35212Sigor@sysoev.ru 
35312Sigor@sysoev.ru static void
nxt_devpoll_oneshot_read(nxt_event_engine_t * engine,nxt_fd_event_t * ev)35412Sigor@sysoev.ru nxt_devpoll_oneshot_read(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
35512Sigor@sysoev.ru {
35612Sigor@sysoev.ru     nxt_devpoll_enable_read(engine, ev);
35712Sigor@sysoev.ru 
35812Sigor@sysoev.ru     ev->read = NXT_EVENT_ONESHOT;
35912Sigor@sysoev.ru }
36012Sigor@sysoev.ru 
36112Sigor@sysoev.ru 
36212Sigor@sysoev.ru static void
nxt_devpoll_oneshot_write(nxt_event_engine_t * engine,nxt_fd_event_t * ev)36312Sigor@sysoev.ru nxt_devpoll_oneshot_write(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
36412Sigor@sysoev.ru {
36512Sigor@sysoev.ru     nxt_devpoll_enable_write(engine, ev);
36612Sigor@sysoev.ru 
36712Sigor@sysoev.ru     ev->write = NXT_EVENT_ONESHOT;
36812Sigor@sysoev.ru }
36912Sigor@sysoev.ru 
37012Sigor@sysoev.ru 
37112Sigor@sysoev.ru static void
nxt_devpoll_change(nxt_event_engine_t * engine,nxt_fd_event_t * ev,nxt_uint_t op,nxt_uint_t events)37212Sigor@sysoev.ru nxt_devpoll_change(nxt_event_engine_t *engine, nxt_fd_event_t *ev,
37312Sigor@sysoev.ru     nxt_uint_t op, nxt_uint_t events)
37412Sigor@sysoev.ru {
37512Sigor@sysoev.ru     nxt_devpoll_change_t  *change;
37612Sigor@sysoev.ru 
37712Sigor@sysoev.ru     nxt_debug(ev->task, "devpoll %d change fd:%d op:%ui ev:%04Xi",
37812Sigor@sysoev.ru               engine->u.devpoll.fd, ev->fd, op, events);
37912Sigor@sysoev.ru 
38012Sigor@sysoev.ru     if (engine->u.devpoll.nchanges >= engine->u.devpoll.mchanges) {
38112Sigor@sysoev.ru         (void) nxt_devpoll_commit_changes(engine);
38212Sigor@sysoev.ru     }
38312Sigor@sysoev.ru 
38412Sigor@sysoev.ru     ev->changing = 1;
38512Sigor@sysoev.ru 
38612Sigor@sysoev.ru     change = &engine->u.devpoll.changes[engine->u.devpoll.nchanges++];
38712Sigor@sysoev.ru     change->op = op;
38812Sigor@sysoev.ru     change->events = events;
38912Sigor@sysoev.ru     change->event = ev;
39012Sigor@sysoev.ru }
39112Sigor@sysoev.ru 
39212Sigor@sysoev.ru 
39312Sigor@sysoev.ru static nxt_int_t
nxt_devpoll_commit_changes(nxt_event_engine_t * engine)39412Sigor@sysoev.ru nxt_devpoll_commit_changes(nxt_event_engine_t *engine)
39512Sigor@sysoev.ru {
39612Sigor@sysoev.ru     size_t                n;
39712Sigor@sysoev.ru     nxt_int_t             ret, retval;
39812Sigor@sysoev.ru     struct pollfd         *pfd, *write_changes;
39912Sigor@sysoev.ru     nxt_fd_event_t        *ev;
40012Sigor@sysoev.ru     nxt_devpoll_change_t  *change, *end;
40112Sigor@sysoev.ru 
40212Sigor@sysoev.ru     nxt_debug(&engine->task, "devpoll %d changes:%ui",
40312Sigor@sysoev.ru               engine->u.devpoll.fd, engine->u.devpoll.nchanges);
40412Sigor@sysoev.ru 
40512Sigor@sysoev.ru     retval = NXT_OK;
40612Sigor@sysoev.ru     n = 0;
40712Sigor@sysoev.ru     write_changes = engine->u.devpoll.write_changes;
40812Sigor@sysoev.ru     change = engine->u.devpoll.changes;
40912Sigor@sysoev.ru     end = change + engine->u.devpoll.nchanges;
41012Sigor@sysoev.ru 
41112Sigor@sysoev.ru     do {
41212Sigor@sysoev.ru         ev = change->event;
41312Sigor@sysoev.ru 
41412Sigor@sysoev.ru         nxt_debug(&engine->task, "devpoll fd:%d op:%d ev:%04Xd",
41512Sigor@sysoev.ru                   ev->fd, change->op, change->events);
41612Sigor@sysoev.ru 
41712Sigor@sysoev.ru         if (change->op == NXT_DEVPOLL_CHANGE) {
41812Sigor@sysoev.ru             pfd = &write_changes[n++];
41912Sigor@sysoev.ru             pfd->fd = ev->fd;
42012Sigor@sysoev.ru             pfd->events = POLLREMOVE;
42112Sigor@sysoev.ru             pfd->revents = 0;
42212Sigor@sysoev.ru         }
42312Sigor@sysoev.ru 
42412Sigor@sysoev.ru         pfd = &write_changes[n++];
42512Sigor@sysoev.ru         pfd->fd = ev->fd;
42612Sigor@sysoev.ru         pfd->events = change->events;
42712Sigor@sysoev.ru         pfd->revents = 0;
42812Sigor@sysoev.ru 
42912Sigor@sysoev.ru         ev->changing = 0;
43012Sigor@sysoev.ru 
43112Sigor@sysoev.ru         change++;
43212Sigor@sysoev.ru 
43312Sigor@sysoev.ru     } while (change < end);
43412Sigor@sysoev.ru 
43512Sigor@sysoev.ru     change = engine->u.devpoll.changes;
43612Sigor@sysoev.ru     end = change + engine->u.devpoll.nchanges;
43712Sigor@sysoev.ru 
43812Sigor@sysoev.ru     ret = nxt_devpoll_write(engine, write_changes, n);
43912Sigor@sysoev.ru 
44012Sigor@sysoev.ru     if (nxt_slow_path(ret != NXT_OK)) {
44112Sigor@sysoev.ru 
44212Sigor@sysoev.ru         do {
44312Sigor@sysoev.ru             nxt_devpoll_change_error(engine, change->event);
44412Sigor@sysoev.ru             change++;
44512Sigor@sysoev.ru         } while (change < end);
44612Sigor@sysoev.ru 
44712Sigor@sysoev.ru         engine->u.devpoll.nchanges = 0;
44812Sigor@sysoev.ru 
44912Sigor@sysoev.ru         return NXT_ERROR;
45012Sigor@sysoev.ru     }
45112Sigor@sysoev.ru 
45212Sigor@sysoev.ru     do {
45312Sigor@sysoev.ru         ev = change->event;
45412Sigor@sysoev.ru 
45512Sigor@sysoev.ru         if (change->op == NXT_DEVPOLL_ADD) {
45612Sigor@sysoev.ru             ret = nxt_fd_event_hash_add(&engine->u.devpoll.fd_hash, ev->fd, ev);
45712Sigor@sysoev.ru 
45812Sigor@sysoev.ru             if (nxt_slow_path(ret != NXT_OK)) {
45912Sigor@sysoev.ru                 nxt_devpoll_change_error(engine, ev);
46012Sigor@sysoev.ru                 retval = NXT_ERROR;
46112Sigor@sysoev.ru             }
46212Sigor@sysoev.ru 
46312Sigor@sysoev.ru         } else if (change->op == NXT_DEVPOLL_DELETE) {
46412Sigor@sysoev.ru             nxt_fd_event_hash_delete(&engine->task, &engine->u.devpoll.fd_hash,
46512Sigor@sysoev.ru                                      ev->fd, 0);
46612Sigor@sysoev.ru         }
46712Sigor@sysoev.ru 
46812Sigor@sysoev.ru         /* Nothing tp do for NXT_DEVPOLL_UPDATE and NXT_DEVPOLL_CHANGE. */
46912Sigor@sysoev.ru 
47012Sigor@sysoev.ru         change++;
47112Sigor@sysoev.ru 
47212Sigor@sysoev.ru     } while (change < end);
47312Sigor@sysoev.ru 
47412Sigor@sysoev.ru     engine->u.devpoll.nchanges = 0;
47512Sigor@sysoev.ru 
47612Sigor@sysoev.ru     return retval;
47712Sigor@sysoev.ru }
47812Sigor@sysoev.ru 
47912Sigor@sysoev.ru 
48012Sigor@sysoev.ru static void
nxt_devpoll_change_error(nxt_event_engine_t * engine,nxt_fd_event_t * ev)48112Sigor@sysoev.ru nxt_devpoll_change_error(nxt_event_engine_t *engine, nxt_fd_event_t *ev)
48212Sigor@sysoev.ru {
48312Sigor@sysoev.ru     ev->read = NXT_EVENT_INACTIVE;
48412Sigor@sysoev.ru     ev->write = NXT_EVENT_INACTIVE;
48512Sigor@sysoev.ru 
48612Sigor@sysoev.ru     nxt_work_queue_add(&engine->fast_work_queue, ev->error_handler,
48712Sigor@sysoev.ru                        ev->task, ev, ev->data);
48812Sigor@sysoev.ru 
48912Sigor@sysoev.ru     nxt_fd_event_hash_delete(ev->task, &engine->u.devpoll.fd_hash, ev->fd, 1);
49012Sigor@sysoev.ru 
49112Sigor@sysoev.ru     nxt_devpoll_remove(engine, ev->fd);
49212Sigor@sysoev.ru }
49312Sigor@sysoev.ru 
49412Sigor@sysoev.ru 
49512Sigor@sysoev.ru static void
nxt_devpoll_remove(nxt_event_engine_t * engine,nxt_fd_t fd)49612Sigor@sysoev.ru nxt_devpoll_remove(nxt_event_engine_t *engine, nxt_fd_t fd)
49712Sigor@sysoev.ru {
49812Sigor@sysoev.ru     int            n;
49912Sigor@sysoev.ru     struct pollfd  pfd;
50012Sigor@sysoev.ru 
50112Sigor@sysoev.ru     pfd.fd = fd;
50212Sigor@sysoev.ru     pfd.events = 0;
50312Sigor@sysoev.ru     pfd.revents = 0;
50412Sigor@sysoev.ru 
50512Sigor@sysoev.ru     n = ioctl(engine->u.devpoll.fd, DP_ISPOLLED, &pfd);
50612Sigor@sysoev.ru 
50712Sigor@sysoev.ru     nxt_debug(&engine->task, "ioctl(%d, DP_ISPOLLED, %d): %d",
50812Sigor@sysoev.ru               engine->u.devpoll.fd, fd, n);
50912Sigor@sysoev.ru 
51012Sigor@sysoev.ru     if (n == 0) {
51112Sigor@sysoev.ru         /* The file descriptor is not in the set. */
51212Sigor@sysoev.ru         return;
51312Sigor@sysoev.ru     }
51412Sigor@sysoev.ru 
51512Sigor@sysoev.ru     if (n == -1) {
516*564Svbart@nginx.com         nxt_alert(&engine->task, "ioctl(%d, DP_ISPOLLED, %d) failed %E",
517*564Svbart@nginx.com                   engine->u.devpoll.fd, fd, nxt_errno);
51812Sigor@sysoev.ru         /* Fall through. */
51912Sigor@sysoev.ru     }
52012Sigor@sysoev.ru 
52112Sigor@sysoev.ru     /* n == 1: the file descriptor is in the set. */
52212Sigor@sysoev.ru 
52312Sigor@sysoev.ru     nxt_debug(&engine->task, "devpoll %d remove fd:%d",
52412Sigor@sysoev.ru               engine->u.devpoll.fd, fd);
52512Sigor@sysoev.ru 
52612Sigor@sysoev.ru     pfd.fd = fd;
52712Sigor@sysoev.ru     pfd.events = POLLREMOVE;
52812Sigor@sysoev.ru     pfd.revents = 0;
52912Sigor@sysoev.ru 
53012Sigor@sysoev.ru     nxt_devpoll_write(engine, &pfd, 1);
53112Sigor@sysoev.ru }
53212Sigor@sysoev.ru 
53312Sigor@sysoev.ru 
53412Sigor@sysoev.ru static nxt_int_t
nxt_devpoll_write(nxt_event_engine_t * engine,struct pollfd * pfd,size_t n)53512Sigor@sysoev.ru nxt_devpoll_write(nxt_event_engine_t *engine, struct pollfd *pfd, size_t n)
53612Sigor@sysoev.ru {
53712Sigor@sysoev.ru     int  fd;
53812Sigor@sysoev.ru 
53912Sigor@sysoev.ru     fd = engine->u.devpoll.fd;
54012Sigor@sysoev.ru 
54112Sigor@sysoev.ru     nxt_debug(&engine->task, "devpoll write(%d) changes:%uz", fd, n);
54212Sigor@sysoev.ru 
54312Sigor@sysoev.ru     n *= sizeof(struct pollfd);
54412Sigor@sysoev.ru 
54512Sigor@sysoev.ru     if (nxt_slow_path(write(fd, pfd, n) == (ssize_t) n)) {
54612Sigor@sysoev.ru         return NXT_OK;
54712Sigor@sysoev.ru     }
54812Sigor@sysoev.ru 
549*564Svbart@nginx.com     nxt_alert(&engine->task, "devpoll write(%d) failed %E", fd, nxt_errno);
55012Sigor@sysoev.ru 
55112Sigor@sysoev.ru     return NXT_ERROR;
55212Sigor@sysoev.ru }
55312Sigor@sysoev.ru 
55412Sigor@sysoev.ru 
55512Sigor@sysoev.ru static void
nxt_devpoll_poll(nxt_event_engine_t * engine,nxt_msec_t timeout)55612Sigor@sysoev.ru nxt_devpoll_poll(nxt_event_engine_t *engine, nxt_msec_t timeout)
55712Sigor@sysoev.ru {
55812Sigor@sysoev.ru     int             nevents;
55912Sigor@sysoev.ru     nxt_fd_t        fd;
56012Sigor@sysoev.ru     nxt_int_t       i;
56112Sigor@sysoev.ru     nxt_err_t       err;
56212Sigor@sysoev.ru     nxt_uint_t      events, level;
56312Sigor@sysoev.ru     struct dvpoll   dvp;
56412Sigor@sysoev.ru     struct pollfd   *pfd;
56512Sigor@sysoev.ru     nxt_fd_event_t  *ev;
56612Sigor@sysoev.ru 
56712Sigor@sysoev.ru     if (engine->u.devpoll.nchanges != 0) {
56812Sigor@sysoev.ru         if (nxt_devpoll_commit_changes(engine) != NXT_OK) {
56912Sigor@sysoev.ru             /* Error handlers have been enqueued on failure. */
57012Sigor@sysoev.ru             timeout = 0;
57112Sigor@sysoev.ru         }
57212Sigor@sysoev.ru     }
57312Sigor@sysoev.ru 
57412Sigor@sysoev.ru     nxt_debug(&engine->task, "ioctl(%d, DP_POLL) timeout:%M",
57512Sigor@sysoev.ru               engine->u.devpoll.fd, timeout);
57612Sigor@sysoev.ru 
57712Sigor@sysoev.ru     dvp.dp_fds = engine->u.devpoll.events;
57812Sigor@sysoev.ru     dvp.dp_nfds = engine->u.devpoll.mevents;
57912Sigor@sysoev.ru     dvp.dp_timeout = timeout;
58012Sigor@sysoev.ru 
58112Sigor@sysoev.ru     nevents = ioctl(engine->u.devpoll.fd, DP_POLL, &dvp);
58212Sigor@sysoev.ru 
58312Sigor@sysoev.ru     err = (nevents == -1) ? nxt_errno : 0;
58412Sigor@sysoev.ru 
58512Sigor@sysoev.ru     nxt_thread_time_update(engine->task.thread);
58612Sigor@sysoev.ru 
58712Sigor@sysoev.ru     nxt_debug(&engine->task, "ioctl(%d, DP_POLL): %d",
58812Sigor@sysoev.ru               engine->u.devpoll.fd, nevents);
58912Sigor@sysoev.ru 
59012Sigor@sysoev.ru     if (nevents == -1) {
591*564Svbart@nginx.com         level = (err == NXT_EINTR) ? NXT_LOG_INFO : NXT_LOG_ALERT;
59212Sigor@sysoev.ru 
59312Sigor@sysoev.ru         nxt_log(&engine->task, level, "ioctl(%d, DP_POLL) failed %E",
59412Sigor@sysoev.ru                 engine->u.devpoll.fd, err);
59512Sigor@sysoev.ru 
59612Sigor@sysoev.ru         return;
59712Sigor@sysoev.ru     }
59812Sigor@sysoev.ru 
59912Sigor@sysoev.ru     for (i = 0; i < nevents; i++) {
60012Sigor@sysoev.ru 
60112Sigor@sysoev.ru         pfd = &engine->u.devpoll.events[i];
60212Sigor@sysoev.ru         fd = pfd->fd;
60312Sigor@sysoev.ru         events = pfd->revents;
60412Sigor@sysoev.ru 
60512Sigor@sysoev.ru         ev = nxt_fd_event_hash_get(&engine->task, &engine->u.devpoll.fd_hash,
60612Sigor@sysoev.ru                                    fd);
60712Sigor@sysoev.ru 
60812Sigor@sysoev.ru         if (nxt_slow_path(ev == NULL)) {
609*564Svbart@nginx.com             nxt_alert(&engine->task,
610*564Svbart@nginx.com                       "ioctl(%d, DP_POLL) returned invalid "
611*564Svbart@nginx.com                       "fd:%d ev:%04Xd rev:%04uXi",
612*564Svbart@nginx.com                       engine->u.devpoll.fd, fd, pfd->events, events);
61312Sigor@sysoev.ru 
61412Sigor@sysoev.ru             nxt_devpoll_remove(engine, fd);
61512Sigor@sysoev.ru             continue;
61612Sigor@sysoev.ru         }
61712Sigor@sysoev.ru 
61812Sigor@sysoev.ru         nxt_debug(ev->task, "devpoll: fd:%d ev:%04uXi rd:%d wr:%d",
61912Sigor@sysoev.ru                   fd, events, ev->read, ev->write);
62012Sigor@sysoev.ru 
62112Sigor@sysoev.ru         if (nxt_slow_path(events & (POLLERR | POLLHUP | POLLNVAL)) != 0) {
622*564Svbart@nginx.com             nxt_alert(ev->task,
623*564Svbart@nginx.com                       "ioctl(%d, DP_POLL) error fd:%d ev:%04Xd rev:%04uXi",
624*564Svbart@nginx.com                       engine->u.devpoll.fd, fd, pfd->events, events);
62512Sigor@sysoev.ru 
62612Sigor@sysoev.ru             nxt_work_queue_add(&engine->fast_work_queue, ev->error_handler,
62712Sigor@sysoev.ru                                ev->task, ev, ev->data);
62812Sigor@sysoev.ru             continue;
62912Sigor@sysoev.ru         }
63012Sigor@sysoev.ru 
63112Sigor@sysoev.ru         if (events & POLLIN) {
63212Sigor@sysoev.ru             ev->read_ready = 1;
63312Sigor@sysoev.ru 
63412Sigor@sysoev.ru             if (ev->read != NXT_EVENT_BLOCKED) {
63512Sigor@sysoev.ru                 nxt_work_queue_add(ev->read_work_queue, ev->read_handler,
63612Sigor@sysoev.ru                                    ev->task, ev, ev->data);
63712Sigor@sysoev.ru             }
63812Sigor@sysoev.ru 
63912Sigor@sysoev.ru             if (ev->read == NXT_EVENT_BLOCKED
64012Sigor@sysoev.ru                 || ev->read == NXT_EVENT_ONESHOT)
64112Sigor@sysoev.ru             {
64212Sigor@sysoev.ru                 nxt_devpoll_disable_read(engine, ev);
64312Sigor@sysoev.ru             }
64412Sigor@sysoev.ru         }
64512Sigor@sysoev.ru 
64612Sigor@sysoev.ru         if (events & POLLOUT) {
64712Sigor@sysoev.ru             ev->write_ready = 1;
64812Sigor@sysoev.ru 
64912Sigor@sysoev.ru             if (ev->write != NXT_EVENT_BLOCKED) {
65012Sigor@sysoev.ru                 nxt_work_queue_add(ev->write_work_queue, ev->write_handler,
65112Sigor@sysoev.ru                                    ev->task, ev, ev->data);
65212Sigor@sysoev.ru             }
65312Sigor@sysoev.ru 
65412Sigor@sysoev.ru             if (ev->write == NXT_EVENT_BLOCKED
65512Sigor@sysoev.ru                 || ev->write == NXT_EVENT_ONESHOT)
65612Sigor@sysoev.ru             {
65712Sigor@sysoev.ru                 nxt_devpoll_disable_write(engine, ev);
65812Sigor@sysoev.ru             }
65912Sigor@sysoev.ru         }
66012Sigor@sysoev.ru     }
66112Sigor@sysoev.ru }
662