From 9c14b616f5fb84bfd7d45707bab4bbb85894443e Mon Sep 17 00:00:00 2001 From: Timothy J Fontaine Date: Thu, 31 Jul 2014 09:28:58 -0700 Subject: [PATCH 01/13] 2014.07.32, Version 0.10.28 (Stable) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes since version 0.10.27: * windows: fix handling closed socket while poll handle is closing (Saúl Ibarra Corretgé) * unix: return system error on EAI_SYSTEM (Saúl Ibarra Corretgé) * unix: fix bogus structure field name (Saúl Ibarra Corretgé) * darwin: invoke `mach_timebase_info` only once (Fedor Indutny) --- ChangeLog | 14 ++++++++++++++ src/version.c | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index efb0a20bb..a185a9f3b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2014.07.32, Version 0.10.28 (Stable) + +Changes since version 0.10.27: + +* windows: fix handling closed socket while poll handle is closing (Saúl Ibarra + Corretgé) + +* unix: return system error on EAI_SYSTEM (Saúl Ibarra Corretgé) + +* unix: fix bogus structure field name (Saúl Ibarra Corretgé) + +* darwin: invoke `mach_timebase_info` only once (Fedor Indutny) + + 2014.05.02, Version 0.10.27 (Stable), 6e24ce23b1e7576059f85a608eca13b766458a01 Changes since version 0.10.26: diff --git a/src/version.c b/src/version.c index ac6f43fe8..4267b97cc 100644 --- a/src/version.c +++ b/src/version.c @@ -35,7 +35,7 @@ #define UV_VERSION_MAJOR 0 #define UV_VERSION_MINOR 10 #define UV_VERSION_PATCH 28 -#define UV_VERSION_IS_RELEASE 0 +#define UV_VERSION_IS_RELEASE 1 #define UV_VERSION ((UV_VERSION_MAJOR << 16) | \ From 71b71289871e31b83a60ad9e278c511b8f2a4a95 Mon Sep 17 00:00:00 2001 From: Timothy J Fontaine Date: Thu, 31 Jul 2014 09:29:01 -0700 Subject: [PATCH 02/13] Now working on v0.10.29 --- ChangeLog | 2 +- src/version.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index a185a9f3b..9433d0e13 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -2014.07.32, Version 0.10.28 (Stable) +2014.07.32, Version 0.10.28 (Stable), 9c14b616f5fb84bfd7d45707bab4bbb85894443e Changes since version 0.10.27: diff --git a/src/version.c b/src/version.c index 4267b97cc..67d4852e4 100644 --- a/src/version.c +++ b/src/version.c @@ -34,8 +34,8 @@ #define UV_VERSION_MAJOR 0 #define UV_VERSION_MINOR 10 -#define UV_VERSION_PATCH 28 -#define UV_VERSION_IS_RELEASE 1 +#define UV_VERSION_PATCH 29 +#define UV_VERSION_IS_RELEASE 0 #define UV_VERSION ((UV_VERSION_MAJOR << 16) | \ From c60d6af011646ed40be93fdf1d83ed2d6d7cc786 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Sun, 7 Sep 2014 23:56:15 +0100 Subject: [PATCH 03/13] darwin: allocate enough space for select() hack `fd_set`s are way too small for `select()` hack when stream's fd is bigger than 1023. Make `fd_set`s a part of `uv__stream_select_t` structure. fix #1461 Conflicts: src/unix/stream.c --- config-unix.mk | 1 + src/unix/stream.c | 115 +++++++++++++++++++++++++++-------------- test/test-list.h | 2 + test/test-osx-select.c | 47 +++++++++++++++++ uv.gyp | 1 + 5 files changed, 128 insertions(+), 38 deletions(-) diff --git a/config-unix.mk b/config-unix.mk index d230bb257..37c4a72f6 100644 --- a/config-unix.mk +++ b/config-unix.mk @@ -85,6 +85,7 @@ ifeq (__clang__,$(shell sh -c "$(CC) -dM -E - sread, 0, s->sread_sz); + memset(s->swrite, 0, s->swrite_sz); if (uv_is_readable(stream)) - FD_SET(fd, &sread); + FD_SET(fd, s->sread); if (uv_is_writable(stream)) - FD_SET(fd, &swrite); - FD_SET(s->int_fd, &sread); + FD_SET(fd, s->swrite); + FD_SET(s->int_fd, s->sread); /* Wait indefinitely for fd events */ - r = select(max_fd + 1, &sread, &swrite, NULL, NULL); + r = select(max_fd + 1, s->sread, s->swrite, NULL, NULL); if (r == -1) { if (errno == EINTR) continue; @@ -177,7 +179,7 @@ static void uv__stream_osx_select(void* arg) { continue; /* Empty socketpair's buffer in case of interruption */ - if (FD_ISSET(s->int_fd, &sread)) + if (FD_ISSET(s->int_fd, s->sread)) while (1) { r = read(s->int_fd, buf, sizeof(buf)); @@ -198,12 +200,12 @@ static void uv__stream_osx_select(void* arg) { /* Handle events */ events = 0; - if (FD_ISSET(fd, &sread)) + if (FD_ISSET(fd, s->sread)) events |= UV__POLLIN; - if (FD_ISSET(fd, &swrite)) + if (FD_ISSET(fd, s->swrite)) events |= UV__POLLOUT; - assert(events != 0 || FD_ISSET(s->int_fd, &sread)); + assert(events != 0 || FD_ISSET(s->int_fd, s->sread)); if (events != 0) { ACCESS_ONCE(int, s->events) = events; @@ -283,6 +285,10 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) { int ret; int kq; int old_fd; + int max_fd; + size_t sread_sz; + size_t swrite_sz; + int err; kq = kqueue(); if (kq == -1) { @@ -306,30 +312,52 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) { return 0; /* At this point we definitely know that this fd won't work with kqueue */ - s = malloc(sizeof(*s)); - if (s == NULL) - return uv__set_artificial_error(stream->loop, UV_ENOMEM); + + /* + * Create fds for io watcher and to interrupt the select() loop. + * NOTE: do it ahead of malloc below to allocate enough space for fd_sets + */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) + return uv__set_sys_error(stream->loop, errno); + + max_fd = *fd; + if (fds[1] > max_fd) + max_fd = fds[1]; + + sread_sz = (max_fd + NBBY) / NBBY; + swrite_sz = sread_sz; + + s = malloc(sizeof(*s) + sread_sz + swrite_sz); + if (s == NULL) { + err = uv__set_artificial_error(stream->loop, UV_ENOMEM); + goto failed_malloc; + } s->events = 0; s->fd = *fd; + s->sread = (fd_set*) ((char*) s + sizeof(*s)); + s->sread_sz = sread_sz; + s->swrite = (fd_set*) ((char*) s->sread + sread_sz); + s->swrite_sz = swrite_sz; - if (uv_async_init(stream->loop, &s->async, uv__stream_osx_select_cb)) { - SAVE_ERRNO(free(s)); - return uv__set_sys_error(stream->loop, errno); - } + err = uv_async_init(stream->loop, &s->async, uv__stream_osx_select_cb); + if (err) + goto failed_async_init; s->async.flags |= UV__HANDLE_INTERNAL; uv__handle_unref(&s->async); - if (uv_sem_init(&s->close_sem, 0)) - goto fatal1; + err = uv_sem_init(&s->close_sem, 0); + if (err != 0) { + err = uv__set_sys_error(stream->loop, UV_UNKNOWN); + goto failed_close_sem_init; + } - if (uv_sem_init(&s->async_sem, 0)) - goto fatal2; - - /* Create fds for io watcher and to interrupt the select() loop. */ - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) - goto fatal3; + err = uv_sem_init(&s->async_sem, 0); + if (err != 0) { + err = uv__set_sys_error(stream->loop, UV_UNKNOWN); + goto failed_async_sem_init; + } s->fake_fd = fds[0]; s->int_fd = fds[1]; @@ -339,26 +367,37 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) { stream->select = s; *fd = s->fake_fd; - if (uv_thread_create(&s->thread, uv__stream_osx_select, stream)) - goto fatal4; + err = uv_thread_create(&s->thread, uv__stream_osx_select, stream); + if (err != 0) { + err = uv__set_sys_error(stream->loop, UV_UNKNOWN); + goto failed_thread_create; + } return 0; -fatal4: +failed_thread_create: s->stream = NULL; stream->select = NULL; *fd = old_fd; - close(s->fake_fd); - close(s->int_fd); - s->fake_fd = -1; - s->int_fd = -1; -fatal3: uv_sem_destroy(&s->async_sem); -fatal2: + +failed_async_sem_init: uv_sem_destroy(&s->close_sem); -fatal1: + +failed_close_sem_init: + close(fds[0]); + close(fds[1]); uv_close((uv_handle_t*) &s->async, uv__stream_osx_cb_close); - return uv__set_sys_error(stream->loop, errno); + return err; + +failed_async_init: + free(s); + +failed_malloc: + close(fds[0]); + close(fds[1]); + + return err; } #endif /* defined(__APPLE__) */ diff --git a/test/test-list.h b/test/test-list.h index 7930c1c36..474442588 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -232,6 +232,7 @@ TEST_DECLARE (closed_fd_events) #endif #ifdef __APPLE__ TEST_DECLARE (osx_select) +TEST_DECLARE (osx_select_many_fds) #endif HELPER_DECLARE (tcp4_echo_server) HELPER_DECLARE (tcp6_echo_server) @@ -468,6 +469,7 @@ TASK_LIST_START #ifdef __APPLE__ TEST_ENTRY (osx_select) + TEST_ENTRY (osx_select_many_fds) #endif TEST_ENTRY (fs_file_noent) diff --git a/test/test-osx-select.c b/test/test-osx-select.c index bf4c3952b..249614a1f 100644 --- a/test/test-osx-select.c +++ b/test/test-osx-select.c @@ -79,4 +79,51 @@ TEST_IMPL(osx_select) { return 0; } + +TEST_IMPL(osx_select_many_fds) { + int r; + int fd; + size_t i; + size_t len; + const char* str; + struct sockaddr_in addr; + uv_tty_t tty; + uv_tcp_t tcps[1500]; + + addr = uv_ip4_addr("127.0.0.1", 0); + + for (i = 0; i < ARRAY_SIZE(tcps); i++) { + r = uv_tcp_init(uv_default_loop(), &tcps[i]); + ASSERT(r == 0); + r = uv_tcp_bind(&tcps[i], addr); + ASSERT(r == 0); + uv_unref((uv_handle_t*) &tcps[i]); + } + + fd = open("/dev/tty", O_RDONLY); + ASSERT(fd >= 0); + + r = uv_tty_init(uv_default_loop(), &tty, fd, 1); + ASSERT(r == 0); + + r = uv_read_start((uv_stream_t*) &tty, alloc_cb, read_cb); + ASSERT(r == 0); + + /* Emulate user-input */ + str = "got some input\n" + "with a couple of lines\n" + "feel pretty happy\n"; + for (i = 0, len = strlen(str); i < len; i++) { + r = ioctl(fd, TIOCSTI, str + i); + ASSERT(r == 0); + } + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT(read_count == 3); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + #endif /* __APPLE__ */ diff --git a/uv.gyp b/uv.gyp index 7cd0f7d45..5af99bd59 100644 --- a/uv.gyp +++ b/uv.gyp @@ -194,6 +194,7 @@ }, 'defines': [ '_DARWIN_USE_64_BIT_INODE=1', + '_DARWIN_UNLIMITED_SELECT=1', ] }], [ 'OS!="mac"', { From ef7b783d8d581f49a68f32f426c18b0fff9c78e3 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Mon, 15 Sep 2014 21:27:05 +1200 Subject: [PATCH 04/13] linux: try epoll_pwait if epoll_wait is missing It seems that epoll_wait is implemented in glibc in terms of epoll_pwait and new architectures (like arm64) do not implement the epoll_wait syscall at all. So if epoll_wait errors with ENOSYS, just call epoll_pwait. This is a backport of 861de3d71d40bdd22712f0079cd38f41be75010d for v0.10 branch. --- src/unix/linux-core.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/unix/linux-core.c b/src/unix/linux-core.c index 69a60b05b..4846bd3fc 100644 --- a/src/unix/linux-core.c +++ b/src/unix/linux-core.c @@ -138,6 +138,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int fd; int op; int i; + static int no_epoll_wait; if (loop->nfds == 0) { assert(ngx_queue_empty(&loop->watcher_queue)); @@ -184,10 +185,22 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { count = 48; /* Benchmarks suggest this gives the best throughput. */ for (;;) { - nfds = uv__epoll_wait(loop->backend_fd, - events, - ARRAY_SIZE(events), - timeout); + if (!no_epoll_wait) { + nfds = uv__epoll_wait(loop->backend_fd, + events, + ARRAY_SIZE(events), + timeout); + if (nfds == -1 && errno == ENOSYS) { + no_epoll_wait = 1; + continue; + } + } else { + nfds = uv__epoll_pwait(loop->backend_fd, + events, + ARRAY_SIZE(events), + timeout, + NULL); + } /* Update loop->time unconditionally. It's tempting to skip the update when * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the From 5e507159c93e2f3f2102552dc56ae8530aec6e49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Sun, 6 Jul 2014 21:00:59 +0200 Subject: [PATCH 05/13] windows: map ERROR_INVALID_DRIVE to UV_ENOENT Backport c44f264641bd60bdc53b6e3f2e56d82ba43011d8 for v0.10 branch. --- src/win/error.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/win/error.c b/src/win/error.c index 2a72ff2f6..820c2603d 100644 --- a/src/win/error.c +++ b/src/win/error.c @@ -128,6 +128,7 @@ uv_err_code uv_translate_sys_error(int sys_errno) { case ERROR_DIRECTORY: return UV_ENOENT; case ERROR_FILE_NOT_FOUND: return UV_ENOENT; case ERROR_INVALID_NAME: return UV_ENOENT; + case ERROR_INVALID_DRIVE: return UV_ENOENT; case ERROR_INVALID_REPARSE_DATA: return UV_ENOENT; case ERROR_MOD_NOT_FOUND: return UV_ENOENT; case ERROR_PATH_NOT_FOUND: return UV_ENOENT; From 2d728542d3790183417f8f122a110693cd85db14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 21 Oct 2014 01:01:03 +0200 Subject: [PATCH 06/13] 2014.10.21, Version 0.10.29 (Stable) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes since version 0.10.28: * darwin: allocate enough space for select() hack (Fedor Indutny) * linux: try epoll_pwait if epoll_wait is missing (Michael Hudson-Doyle) * windows: map ERROR_INVALID_DRIVE to UV_ENOENT (Saúl Ibarra Corretgé) --- AUTHORS | 1 + ChangeLog | 11 +++++++++++ src/version.c | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index 060701ffa..b27d17716 100644 --- a/AUTHORS +++ b/AUTHORS @@ -130,3 +130,4 @@ David Capello Paul Tan Javier Hernández Tonis Tiigi +Michael Hudson-Doyle diff --git a/ChangeLog b/ChangeLog index 9433d0e13..3f010a0c3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2014.10.21, Version 0.10.29 (Stable) + +Changes since version 0.10.28: + +* darwin: allocate enough space for select() hack (Fedor Indutny) + +* linux: try epoll_pwait if epoll_wait is missing (Michael Hudson-Doyle) + +* windows: map ERROR_INVALID_DRIVE to UV_ENOENT (Saúl Ibarra Corretgé) + + 2014.07.32, Version 0.10.28 (Stable), 9c14b616f5fb84bfd7d45707bab4bbb85894443e Changes since version 0.10.27: diff --git a/src/version.c b/src/version.c index 67d4852e4..ca9640084 100644 --- a/src/version.c +++ b/src/version.c @@ -35,7 +35,7 @@ #define UV_VERSION_MAJOR 0 #define UV_VERSION_MINOR 10 #define UV_VERSION_PATCH 29 -#define UV_VERSION_IS_RELEASE 0 +#define UV_VERSION_IS_RELEASE 1 #define UV_VERSION ((UV_VERSION_MAJOR << 16) | \ From 39a5728dd9457e44e3582a999d07e6549790825c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 21 Oct 2014 01:01:08 +0200 Subject: [PATCH 07/13] Now working on v0.10.30 --- ChangeLog | 2 +- src/version.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3f010a0c3..de77a4f2e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -2014.10.21, Version 0.10.29 (Stable) +2014.10.21, Version 0.10.29 (Stable), 2d728542d3790183417f8f122a110693cd85db14 Changes since version 0.10.28: diff --git a/src/version.c b/src/version.c index ca9640084..010d4f228 100644 --- a/src/version.c +++ b/src/version.c @@ -34,8 +34,8 @@ #define UV_VERSION_MAJOR 0 #define UV_VERSION_MINOR 10 -#define UV_VERSION_PATCH 29 -#define UV_VERSION_IS_RELEASE 1 +#define UV_VERSION_PATCH 30 +#define UV_VERSION_IS_RELEASE 0 #define UV_VERSION ((UV_VERSION_MAJOR << 16) | \ From b705b53edd1b1e166391861cf9a0460b11a188a6 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Tue, 25 Nov 2014 01:48:56 +0100 Subject: [PATCH 08/13] linux: fix sigmask size arg in epoll_pwait() call sizeof(sigset_t) = 128 whereas the kernel expects 8, the size of a long. It made the system call fail with EINVAL when a non-NULL sigset was passed in. Fortunately, it's academic because there is just one call site and it passes in NULL. Fixes libuv/libuv#4. --- src/unix/linux-syscalls.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/unix/linux-syscalls.c b/src/unix/linux-syscalls.c index 06cc5943c..c9945438f 100644 --- a/src/unix/linux-syscalls.c +++ b/src/unix/linux-syscalls.c @@ -21,6 +21,7 @@ #include "linux-syscalls.h" #include +#include #include #include #include @@ -298,7 +299,7 @@ int uv__epoll_pwait(int epfd, nevents, timeout, sigmask, - sizeof(*sigmask)); + _NSIG / 8); #else return errno = ENOSYS, -1; #endif From 5672bbd68a8ac47644a7e17b71b2d88234795f08 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Tue, 25 Nov 2014 17:06:22 +0100 Subject: [PATCH 09/13] linux: handle O_NONBLOCK != SOCK_NONBLOCK case linux-syscalls.h assumes that on all Linux platforms the value of O_NONBLOCK is the same as SOCK_NONBLOCK. This commit fixes it, as it is at least not true for hppa, and resolves #1442. Signed-off-by: Luca Bruno --- src/unix/linux-syscalls.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/unix/linux-syscalls.h b/src/unix/linux-syscalls.h index ba44974a0..62eb5c5a3 100644 --- a/src/unix/linux-syscalls.h +++ b/src/unix/linux-syscalls.h @@ -43,7 +43,7 @@ #if defined(__alpha__) # define UV__O_NONBLOCK 0x4 #elif defined(__hppa__) -# define UV__O_NONBLOCK 0x10004 +# define UV__O_NONBLOCK O_NONBLOCK #elif defined(__mips__) # define UV__O_NONBLOCK 0x80 #elif defined(__sparc__) @@ -59,7 +59,11 @@ #define UV__IN_NONBLOCK UV__O_NONBLOCK #define UV__SOCK_CLOEXEC UV__O_CLOEXEC -#define UV__SOCK_NONBLOCK UV__O_NONBLOCK +#if defined(SOCK_NONBLOCK) +# define UV__SOCK_NONBLOCK SOCK_NONBLOCK +#else +# define UV__SOCK_NONBLOCK UV__O_NONBLOCK +#endif /* epoll flags */ #define UV__EPOLL_CLOEXEC UV__O_CLOEXEC From fa35193bb1c6cbd6d6ae1afda0d8542bd98b83ae Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Tue, 25 Nov 2014 17:20:23 +0100 Subject: [PATCH 10/13] doc: update project links This is like commit 7ce1113 from the v1.x branch but for the v0.10 branch. The project home has moved, update the links in the README. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1e1aa25cb..5fee16cfe 100644 --- a/README.md +++ b/README.md @@ -42,13 +42,13 @@ http://nodejs.org/ ## Documentation - * [include/uv.h](https://github.com/joyent/libuv/blob/master/include/uv.h) + * [include/uv.h](https://github.com/libuv/libuv/blob/master/include/uv.h) — API documentation in the form of detailed header comments. * [An Introduction to libuv](http://nikhilm.github.com/uvbook/) — An overview of libuv with tutorials. * [LXJS 2012 talk](http://www.youtube.com/watch?v=nGn60vDSxQ4) - High-level introductory talk about libuv. - * [Tests and benchmarks](https://github.com/joyent/libuv/tree/master/test) - + * [Tests and benchmarks](https://github.com/libuv/libuv/tree/master/test) - API specification and usage examples. ## Build Instructions From 0bcac64512d937d8fe8dbb7c51f7ce0e230c6898 Mon Sep 17 00:00:00 2001 From: schlamar Date: Tue, 25 Nov 2014 19:24:51 +0100 Subject: [PATCH 11/13] windows: fix compilation of tests --- test/test-poll-closesocket.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/test-poll-closesocket.c b/test/test-poll-closesocket.c index 4db74a01f..28114de07 100644 --- a/test/test-poll-closesocket.c +++ b/test/test-poll-closesocket.c @@ -67,8 +67,7 @@ TEST_IMPL(poll_closesocket) { r = ioctlsocket(sock, FIONBIO, &on); ASSERT(r == 0); - r = uv_ip4_addr("127.0.0.1", TEST_PORT, &addr); - ASSERT(r == 0); + addr = uv_ip4_addr("127.0.0.1", TEST_PORT); r = connect(sock, (const struct sockaddr*) &addr, sizeof addr); ASSERT(r != 0); From 2daf9448b127f2849de735a76ff4f9a95184d64f Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Thu, 27 Nov 2014 08:09:10 +0100 Subject: [PATCH 12/13] unix: add flag for blocking SIGPROF during poll MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a per-event loop flag for blocking SIGPROF signals when polling for events. The motivation for this addition is to reduce the number of wakeups and subsequent clock_gettime() system calls when using a sampling profiler. On Linux, this switches from epoll_wait() to epoll_pwait() when enabled. Other platforms bracket the poll syscall with pthread_sigmask() calls. Refs strongloop/strong-agent#3 and strongloop-internal/scrum-cs#37. PR-URL: https://github.com/libuv/libuv/pull/15 Reviewed-By: Saúl Ibarra Corretgé --- src/unix/internal.h | 5 +++++ src/unix/kqueue.c | 17 ++++++++++++++++- src/unix/linux-core.c | 24 +++++++++++++++++------- src/unix/sunos.c | 29 ++++++++++++++++++++++++----- 4 files changed, 62 insertions(+), 13 deletions(-) diff --git a/src/unix/internal.h b/src/unix/internal.h index 35aa894a5..003e1a509 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -111,6 +111,11 @@ enum { UV_TCP_SINGLE_ACCEPT = 0x400 /* Only accept() when idle. */ }; +/* loop flags */ +enum { + UV_LOOP_BLOCK_SIGPROF = 1 +}; + /* core */ int uv__nonblock(int fd, int set); int uv__cloexec(int fd, int set); diff --git a/src/unix/kqueue.c b/src/unix/kqueue.c index 124f4fd57..b43cc6764 100644 --- a/src/unix/kqueue.c +++ b/src/unix/kqueue.c @@ -56,9 +56,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { unsigned int nevents; unsigned int revents; ngx_queue_t* q; + uv__io_t* w; + sigset_t* pset; + sigset_t set; uint64_t base; uint64_t diff; - uv__io_t* w; int filter; int fflags; int count; @@ -118,6 +120,13 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { w->events = w->pevents; } + pset = NULL; + if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { + pset = &set; + sigemptyset(pset); + sigaddset(pset, SIGPROF); + } + assert(timeout >= -1); base = loop->time; count = 48; /* Benchmarks suggest this gives the best throughput. */ @@ -128,6 +137,9 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { spec.tv_nsec = (timeout % 1000) * 1000000; } + if (pset != NULL) + pthread_sigmask(SIG_BLOCK, pset, NULL); + nfds = kevent(loop->backend_fd, events, nevents, @@ -135,6 +147,9 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { ARRAY_SIZE(events), timeout == -1 ? NULL : &spec); + if (pset != NULL) + pthread_sigmask(SIG_UNBLOCK, pset, NULL); + /* Update loop->time unconditionally. It's tempting to skip the update when * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the * operating system didn't reschedule our process while in the syscall. diff --git a/src/unix/linux-core.c b/src/unix/linux-core.c index 4846bd3fc..47938f2b2 100644 --- a/src/unix/linux-core.c +++ b/src/unix/linux-core.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -130,6 +131,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { struct uv__epoll_event e; ngx_queue_t* q; uv__io_t* w; + sigset_t* pset; + sigset_t set; uint64_t base; uint64_t diff; int nevents; @@ -180,12 +183,25 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { w->events = w->pevents; } + pset = NULL; + if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { + pset = &set; + sigemptyset(pset); + sigaddset(pset, SIGPROF); + } + assert(timeout >= -1); base = loop->time; count = 48; /* Benchmarks suggest this gives the best throughput. */ for (;;) { - if (!no_epoll_wait) { + if (no_epoll_wait || pset != NULL) { + nfds = uv__epoll_pwait(loop->backend_fd, + events, + ARRAY_SIZE(events), + timeout, + pset); + } else { nfds = uv__epoll_wait(loop->backend_fd, events, ARRAY_SIZE(events), @@ -194,12 +210,6 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { no_epoll_wait = 1; continue; } - } else { - nfds = uv__epoll_pwait(loop->backend_fd, - events, - ARRAY_SIZE(events), - timeout, - NULL); } /* Update loop->time unconditionally. It's tempting to skip the update when diff --git a/src/unix/sunos.c b/src/unix/sunos.c index 9030bfa6b..0f4b32f4e 100644 --- a/src/unix/sunos.c +++ b/src/unix/sunos.c @@ -112,6 +112,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { struct timespec spec; ngx_queue_t* q; uv__io_t* w; + sigset_t* pset; + sigset_t set; uint64_t base; uint64_t diff; unsigned int nfds; @@ -119,6 +121,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int saved_errno; int nevents; int count; + int err; int fd; if (loop->nfds == 0) { @@ -140,6 +143,13 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { w->events = w->pevents; } + pset = NULL; + if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { + pset = &set; + sigemptyset(pset); + sigaddset(pset, SIGPROF); + } + assert(timeout >= -1); base = loop->time; count = 48; /* Benchmarks suggest this gives the best throughput. */ @@ -155,11 +165,20 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { nfds = 1; saved_errno = 0; - if (port_getn(loop->backend_fd, - events, - ARRAY_SIZE(events), - &nfds, - timeout == -1 ? NULL : &spec)) { + + if (pset != NULL) + pthread_sigmask(SIG_BLOCK, pset, NULL); + + err = port_getn(loop->backend_fd, + events, + ARRAY_SIZE(events), + &nfds, + timeout == -1 ? NULL : &spec); + + if (pset != NULL) + pthread_sigmask(SIG_UNBLOCK, pset, NULL); + + if (err) { /* Work around another kernel bug: port_getn() may return events even * on error. */ From 9da5fd443ebc2c56a7f5946f0a1e04801b370a33 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Thu, 27 Nov 2014 08:43:48 +0100 Subject: [PATCH 13/13] unix, windows: add uv_loop_configure() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only supported option right now is UV_LOOP_BLOCK_SIGNAL, which only supports the SIGPROF signal and only on UNIX platforms. So yes, it is kind of limited right now. But everything has to start somewhere. Refs strongloop/strong-agent#3 and strongloop-internal/scrum-cs#37. PR-URL: https://github.com/libuv/libuv/pull/15 Reviewed-By: Saúl Ibarra Corretgé --- include/uv.h | 19 +++++++++++++++++++ src/unix/loop.c | 12 ++++++++++++ src/uv-common.c | 14 ++++++++++++++ src/uv-common.h | 2 ++ src/win/core.c | 5 +++++ 5 files changed, 52 insertions(+) diff --git a/include/uv.h b/include/uv.h index 7022bc669..be789987b 100644 --- a/include/uv.h +++ b/include/uv.h @@ -226,6 +226,16 @@ typedef struct uv_work_s uv_work_t; typedef struct uv_cpu_info_s uv_cpu_info_t; typedef struct uv_interface_address_s uv_interface_address_t; +typedef enum { + /* Block a signal when polling for new events. The second argument to + * uv_loop_configure() is the signal number. + * + * This operation is currently only implemented for SIGPROF signals, + * to suppress unnecessary wakeups when using a sampling profiler. + * Requesting other signals will fail with UV_EINVAL. + */ + UV_LOOP_BLOCK_SIGNAL +} uv_loop_option; typedef enum { UV_RUN_DEFAULT = 0, @@ -264,6 +274,15 @@ UV_EXTERN void uv_loop_delete(uv_loop_t*); */ UV_EXTERN uv_loop_t* uv_default_loop(void); +/* + * Set additional loop options. You should normally call this before the + * first call to uv_run() unless mentioned otherwise. + * + * Returns 0 on success or a UV_E* error code on failure. Be prepared to + * handle UV_ENOSYS; it means the loop option is not supported by the platform. + */ +UV_EXTERN int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...); + /* * This function runs the event loop. It will act differently depending on the * specified mode: diff --git a/src/unix/loop.c b/src/unix/loop.c index 75b0702a3..a75c93456 100644 --- a/src/unix/loop.c +++ b/src/unix/loop.c @@ -112,3 +112,15 @@ void uv__loop_delete(uv_loop_t* loop) { loop->watchers = NULL; loop->nwatchers = 0; } + + +int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) { + if (option != UV_LOOP_BLOCK_SIGNAL) + return UV_ENOSYS; + + if (va_arg(ap, int) != SIGPROF) + return UV_EINVAL; + + loop->flags |= UV_LOOP_BLOCK_SIGPROF; + return 0; +} diff --git a/src/uv-common.c b/src/uv-common.c index f0792f117..69da523e6 100644 --- a/src/uv-common.c +++ b/src/uv-common.c @@ -24,6 +24,7 @@ #include #include +#include #include /* NULL */ #include /* malloc */ #include /* memset */ @@ -434,3 +435,16 @@ void uv_stop(uv_loop_t* loop) { uint64_t uv_now(uv_loop_t* loop) { return loop->time; } + + +int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...) { + va_list ap; + int err; + + va_start(ap, option); + /* Any platform-agnostic options should be handled here. */ + err = uv__loop_configure(loop, option, ap); + va_end(ap); + + return err; +} diff --git a/src/uv-common.h b/src/uv-common.h index bbf248592..ad45f1905 100644 --- a/src/uv-common.h +++ b/src/uv-common.h @@ -28,6 +28,7 @@ #define UV_COMMON_H_ #include +#include #include #if defined(_MSC_VER) && _MSC_VER < 1600 @@ -77,6 +78,7 @@ int uv__set_artificial_error(uv_loop_t* loop, uv_err_code code); uv_err_t uv__new_sys_error(int sys_error); uv_err_t uv__new_artificial_error(uv_err_code code); +int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap); int uv__tcp_bind(uv_tcp_t* handle, struct sockaddr_in addr); int uv__tcp_bind6(uv_tcp_t* handle, struct sockaddr_in6 addr); diff --git a/src/win/core.c b/src/win/core.c index 62d6bf8fb..74c602fb1 100644 --- a/src/win/core.c +++ b/src/win/core.c @@ -174,6 +174,11 @@ void uv_loop_delete(uv_loop_t* loop) { } +int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) { + return UV_ENOSYS; +} + + int uv_backend_fd(const uv_loop_t* loop) { return -1; }