From 592cd40af8d4ae3a33654aa5e1106287ecb591bc Mon Sep 17 00:00:00 2001 From: Erkhes N <71805796+rexes-ND@users.noreply.github.com> Date: Thu, 1 Jul 2021 12:24:35 +0900 Subject: [PATCH 1/8] test: wrong pointer arithmetic multiplier PR-URL: https://github.com/libuv/libuv/pull/3216 Reviewed-By: Ben Noordhuis Reviewed-By: Jameson Nash --- test/runner-unix.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/runner-unix.c b/test/runner-unix.c index b7ca1f857ec..a13648bc883 100644 --- a/test/runner-unix.c +++ b/test/runner-unix.c @@ -197,7 +197,7 @@ static void* dowait(void* data) { process_info_t* p; for (i = 0; i < args->n; i++) { - p = (process_info_t*)(args->vec + i * sizeof(process_info_t)); + p = &args->vec[i]; if (p->terminated) continue; r = waitpid(p->pid, &p->status, 0); if (r < 0) { @@ -323,7 +323,7 @@ int process_wait(process_info_t* vec, int n, int timeout) { } else { /* Timeout. Kill all the children. */ for (i = 0; i < n; i++) { - p = (process_info_t*)(vec + i * sizeof(process_info_t)); + p = &vec[i]; kill(p->pid, SIGTERM); } retval = -2; From 4a27d87a6966d053f67e267fdbd9474433143c2b Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 30 Jun 2021 23:25:20 -0400 Subject: [PATCH 2/8] doc: switch discussion forum to github Freenode is gone (replaced by Libera.chat), but IRC was unused anyways. Fixes: https://github.com/libuv/libuv/issues/3157 Fixes: https://github.com/libuv/libuv/issues/3208 PR-URL: https://github.com/libuv/libuv/pull/3214 Reviewed-By: Ben Noordhuis --- .github/ISSUE_TEMPLATE.md | 2 +- CONTRIBUTING.md | 10 +++++----- README.md | 3 +-- docs/code/dns/main.c | 4 ++-- docs/src/guide/networking.rst | 2 +- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 42538796096..43934de7064 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -2,7 +2,7 @@ If you want to report a bug, you are in the right place! If you need help or have a question, go here: -https://github.com/libuv/help/issues/new +https://github.com/libuv/libuv/discussions If you are reporting a libuv test failure, please ensure that you are not running the test as root. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b0abe3b40ea..d37c51d6363 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,11 +23,11 @@ The stable branch is effectively frozen; patches that change the libuv API/ABI or affect the run-time behavior of applications get rejected. In case of doubt, open an issue in the [issue tracker][], post your question -to the [libuv mailing list], or contact one of [project maintainers][] on [IRC][]. +to the [libuv discussions forum], or message the [libuv mailing list]. -Especially do so if you plan to work on something big. Nothing is more -frustrating than seeing your hard work go to waste because your vision -does not align with that of a project maintainers. +Especially do so if you plan to work on something big. Nothing is more +frustrating than seeing your hard work go to waste because your vision does not +align with that of the [project maintainers]. ### BRANCH @@ -166,6 +166,6 @@ not send out notifications when you add commits. [issue tracker]: https://github.com/libuv/libuv/issues [libuv mailing list]: http://groups.google.com/group/libuv -[IRC]: http://webchat.freenode.net/?channels=libuv +[libuv discussions forum]: https://github.com/libuv/libuv/discussions [Google C/C++ style guide]: https://google.github.io/styleguide/cppguide.html [project maintainers]: https://github.com/libuv/libuv/blob/master/MAINTAINERS.md diff --git a/README.md b/README.md index 23b82439d47..a9a8a9d1179 100644 --- a/README.md +++ b/README.md @@ -48,9 +48,8 @@ The documentation is licensed under the CC BY 4.0 license. Check the [LICENSE-do ## Community - * [Support](https://github.com/libuv/help) + * [Support](https://github.com/libuv/libuv/discussions) * [Mailing list](http://groups.google.com/group/libuv) - * [IRC chatroom (#libuv@irc.freenode.org)](http://webchat.freenode.net?channels=libuv&uio=d4) ## Documentation diff --git a/docs/code/dns/main.c b/docs/code/dns/main.c index 77a7005f4a6..2d63f1aa198 100644 --- a/docs/code/dns/main.c +++ b/docs/code/dns/main.c @@ -69,8 +69,8 @@ int main() { hints.ai_flags = 0; uv_getaddrinfo_t resolver; - fprintf(stderr, "irc.freenode.net is... "); - int r = uv_getaddrinfo(loop, &resolver, on_resolved, "irc.freenode.net", "6667", &hints); + fprintf(stderr, "irc.libera.chat is... "); + int r = uv_getaddrinfo(loop, &resolver, on_resolved, "irc.libera.chat", "6667", &hints); if (r) { fprintf(stderr, "getaddrinfo call error %s\n", uv_err_name(r)); diff --git a/docs/src/guide/networking.rst b/docs/src/guide/networking.rst index 50a191dd802..dcb5643137c 100644 --- a/docs/src/guide/networking.rst +++ b/docs/src/guide/networking.rst @@ -193,7 +193,7 @@ Querying DNS libuv provides asynchronous DNS resolution. For this it provides its own ``getaddrinfo`` replacement [#]_. In the callback you can perform normal socket operations on the retrieved addresses. Let's connect to -Freenode to see an example of DNS resolution. +Libera.chat to see an example of DNS resolution. .. rubric:: dns/main.c .. literalinclude:: ../../code/dns/main.c From b7466e31e4bee160d82a68fca11b1f61d46debae Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 21 May 2021 11:23:36 +0200 Subject: [PATCH 3/8] idna: fix OOB read in punycode decoder libuv was vulnerable to out-of-bounds reads in the uv__idna_toascii() function which is used to convert strings to ASCII. This is called by the DNS resolution function and can lead to information disclosures or crashes. Reported by Eric Sesterhenn in collaboration with Cure53 and ExpressVPN. Reported-By: Eric Sesterhenn Fixes: https://github.com/libuv/libuv/issues/3147 PR-URL: https://github.com/libuv/libuv-private/pull/1 Refs: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-22918 Reviewed-By: Colin Ihrig Reviewed-By: Richard Lau --- src/idna.c | 49 +++++++++++++++++++++++++++++++++++------------- test/test-idna.c | 19 +++++++++++++++++++ test/test-list.h | 2 ++ 3 files changed, 57 insertions(+), 13 deletions(-) diff --git a/src/idna.c b/src/idna.c index 13ffac6be81..b44cb16a1ee 100644 --- a/src/idna.c +++ b/src/idna.c @@ -19,6 +19,7 @@ #include "uv.h" #include "idna.h" +#include #include static unsigned uv__utf8_decode1_slow(const char** p, @@ -32,7 +33,7 @@ static unsigned uv__utf8_decode1_slow(const char** p, if (a > 0xF7) return -1; - switch (*p - pe) { + switch (pe - *p) { default: if (a > 0xEF) { min = 0x10000; @@ -62,6 +63,8 @@ static unsigned uv__utf8_decode1_slow(const char** p, a = 0; break; } + /* Fall through. */ + case 0: return -1; /* Invalid continuation byte. */ } @@ -88,6 +91,8 @@ static unsigned uv__utf8_decode1_slow(const char** p, unsigned uv__utf8_decode1(const char** p, const char* pe) { unsigned a; + assert(*p < pe); + a = (unsigned char) *(*p)++; if (a < 128) @@ -96,9 +101,6 @@ unsigned uv__utf8_decode1(const char** p, const char* pe) { return uv__utf8_decode1_slow(p, pe, a); } -#define foreach_codepoint(c, p, pe) \ - for (; (void) (*p <= pe && (c = uv__utf8_decode1(p, pe))), *p <= pe;) - static int uv__idna_toascii_label(const char* s, const char* se, char** d, char* de) { static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz0123456789"; @@ -121,15 +123,22 @@ static int uv__idna_toascii_label(const char* s, const char* se, ss = s; todo = 0; - foreach_codepoint(c, &s, se) { + /* Note: after this loop we've visited all UTF-8 characters and know + * they're legal so we no longer need to check for decode errors. + */ + while (s < se) { + c = uv__utf8_decode1(&s, se); + + if (c == -1u) + return UV_EINVAL; + if (c < 128) h++; - else if (c == (unsigned) -1) - return UV_EINVAL; else todo++; } + /* Only write "xn--" when there are non-ASCII characters. */ if (todo > 0) { if (*d < de) *(*d)++ = 'x'; if (*d < de) *(*d)++ = 'n'; @@ -137,9 +146,13 @@ static int uv__idna_toascii_label(const char* s, const char* se, if (*d < de) *(*d)++ = '-'; } + /* Write ASCII characters. */ x = 0; s = ss; - foreach_codepoint(c, &s, se) { + while (s < se) { + c = uv__utf8_decode1(&s, se); + assert(c != -1u); + if (c > 127) continue; @@ -166,10 +179,15 @@ static int uv__idna_toascii_label(const char* s, const char* se, while (todo > 0) { m = -1; s = ss; - foreach_codepoint(c, &s, se) + + while (s < se) { + c = uv__utf8_decode1(&s, se); + assert(c != -1u); + if (c >= n) if (c < m) m = c; + } x = m - n; y = h + 1; @@ -181,7 +199,10 @@ static int uv__idna_toascii_label(const char* s, const char* se, n = m; s = ss; - foreach_codepoint(c, &s, se) { + while (s < se) { + c = uv__utf8_decode1(&s, se); + assert(c != -1u); + if (c < n) if (++delta == 0) return UV_E2BIG; /* Overflow. */ @@ -245,8 +266,6 @@ static int uv__idna_toascii_label(const char* s, const char* se, return 0; } -#undef foreach_codepoint - long uv__idna_toascii(const char* s, const char* se, char* d, char* de) { const char* si; const char* st; @@ -256,10 +275,14 @@ long uv__idna_toascii(const char* s, const char* se, char* d, char* de) { ds = d; - for (si = s; si < se; /* empty */) { + si = s; + while (si < se) { st = si; c = uv__utf8_decode1(&si, se); + if (c == -1u) + return UV_EINVAL; + if (c != '.') if (c != 0x3002) /* 。 */ if (c != 0xFF0E) /* . */ diff --git a/test/test-idna.c b/test/test-idna.c index b76853cb996..f4fad9653df 100644 --- a/test/test-idna.c +++ b/test/test-idna.c @@ -96,6 +96,25 @@ TEST_IMPL(utf8_decode1) { return 0; } +TEST_IMPL(utf8_decode1_overrun) { + const char* p; + char b[1]; + + /* Single byte. */ + p = b; + b[0] = 0x7F; + ASSERT_EQ(0x7F, uv__utf8_decode1(&p, b + 1)); + ASSERT_EQ(p, b + 1); + + /* Multi-byte. */ + p = b; + b[0] = 0xC0; + ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + 1)); + ASSERT_EQ(p, b + 1); + + return 0; +} + /* Doesn't work on z/OS because that platform uses EBCDIC, not ASCII. */ #ifndef __MVS__ diff --git a/test/test-list.h b/test/test-list.h index c3c6002ea3d..424eea07fa6 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -528,6 +528,7 @@ TEST_DECLARE (fork_threadpool_queue_work_simple) TEST_DECLARE (idna_toascii) TEST_DECLARE (utf8_decode1) +TEST_DECLARE (utf8_decode1_overrun) TEST_DECLARE (uname) TEST_DECLARE (metrics_idle_time) @@ -1124,6 +1125,7 @@ TASK_LIST_START #endif TEST_ENTRY (utf8_decode1) + TEST_ENTRY (utf8_decode1_overrun) TEST_ENTRY (uname) /* Doesn't work on z/OS because that platform uses EBCDIC, not ASCII. */ From 03d12d1a375b51b67b70c7fb0e7887f21a259c48 Mon Sep 17 00:00:00 2001 From: Santiago Gimeno Date: Fri, 2 Jul 2021 20:50:18 +0200 Subject: [PATCH 4/8] build: make sure -fvisibility=hidden is set PR-URL: https://github.com/libuv/libuv/pull/3005 Reviewed-By: Jameson Nash --- configure.ac | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 3c13d4015b1..8ac49276e50 100644 --- a/configure.ac +++ b/configure.ac @@ -24,7 +24,10 @@ AC_ENABLE_SHARED AC_ENABLE_STATIC AC_PROG_CC AM_PROG_CC_C_O -CC_FLAG_VISIBILITY #[-fvisibility=hidden] + +CC_ATTRIBUTE_VISIBILITY([default], [ + CC_FLAG_VISIBILITY([CFLAGS="${CFLAGS} -fvisibility=hidden"]) +]) CC_CHECK_CFLAGS_APPEND([-fno-strict-aliasing]) CC_CHECK_CFLAGS_APPEND([-g]) CC_CHECK_CFLAGS_APPEND([-std=gnu89]) From caf22ddbf5b1b8d06006b24f3b50c5e698fe2d8c Mon Sep 17 00:00:00 2001 From: tjarlama Date: Sat, 3 Jul 2021 00:30:37 +0530 Subject: [PATCH 5/8] illumos: event ports to epoll Sunos has added epoll wrappers to offer compatability for binaries build specifically for linux (https://illumos.org/man/5/epoll). With this, it's now possible to develop epoll as a generic interface and share between Linux and SUNOS, similar to kqueue. Fixes: https://github.com/libuv/libuv/issues/3008 PR-URL: https://github.com/libuv/libuv/pull/3046 Reviewed-By: Ben Noordhuis --- CMakeLists.txt | 11 +- Makefile.am | 7 +- src/unix/epoll.c | 422 ++++++++++++++++++++++++++++++++++++++++++ src/unix/internal.h | 1 + src/unix/linux-core.c | 395 +-------------------------------------- src/unix/sunos.c | 275 +-------------------------- 6 files changed, 439 insertions(+), 672 deletions(-) create mode 100644 src/unix/epoll.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0217c01b2c7..de1272a6de8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -210,7 +210,8 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Android") src/unix/pthread-fixes.c src/unix/random-getentropy.c src/unix/random-getrandom.c - src/unix/random-sysctl-linux.c) + src/unix/random-sysctl-linux.c + src/unix/epoll.c) endif() if(APPLE OR CMAKE_SYSTEM_NAME MATCHES "Android|Linux") @@ -254,7 +255,8 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") src/unix/linux-syscalls.c src/unix/procfs-exepath.c src/unix/random-getrandom.c - src/unix/random-sysctl-linux.c) + src/unix/random-sysctl-linux.c + src/unix/epoll.c) endif() if(CMAKE_SYSTEM_NAME STREQUAL "NetBSD") @@ -325,7 +327,10 @@ endif() if(CMAKE_SYSTEM_NAME STREQUAL "SunOS") list(APPEND uv_defines __EXTENSIONS__ _XOPEN_SOURCE=500) list(APPEND uv_libraries kstat nsl sendfile socket) - list(APPEND uv_sources src/unix/no-proctitle.c src/unix/sunos.c) + list(APPEND uv_sources + src/unix/no-proctitle.c + src/unix/sunos.c + src/unix/epoll.c) endif() if(CMAKE_SYSTEM_NAME STREQUAL "Haiku") diff --git a/Makefile.am b/Makefile.am index 6f2d018c4a7..4d8fb40da92 100644 --- a/Makefile.am +++ b/Makefile.am @@ -391,7 +391,8 @@ libuv_la_CFLAGS += -D_GNU_SOURCE libuv_la_SOURCES += src/unix/android-ifaddrs.c \ src/unix/pthread-fixes.c \ src/unix/random-getrandom.c \ - src/unix/random-sysctl-linux.c + src/unix/random-sysctl-linux.c \ + src/unix/epoll.c endif if CYGWIN @@ -472,7 +473,8 @@ libuv_la_SOURCES += src/unix/linux-core.c \ src/unix/procfs-exepath.c \ src/unix/proctitle.c \ src/unix/random-getrandom.c \ - src/unix/random-sysctl-linux.c + src/unix/random-sysctl-linux.c \ + src/unix/epoll.c test_run_tests_LDFLAGS += -lutil endif @@ -516,6 +518,7 @@ libuv_la_CFLAGS += -D__EXTENSIONS__ \ -D_XOPEN_SOURCE=500 \ -D_REENTRANT libuv_la_SOURCES += src/unix/no-proctitle.c \ + src/unix/epoll.c \ src/unix/sunos.c endif diff --git a/src/unix/epoll.c b/src/unix/epoll.c new file mode 100644 index 00000000000..97348e254b4 --- /dev/null +++ b/src/unix/epoll.c @@ -0,0 +1,422 @@ +/* Copyright libuv contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" +#include +#include + +int uv__epoll_init(uv_loop_t* loop) { + int fd; + fd = epoll_create1(O_CLOEXEC); + + /* epoll_create1() can fail either because it's not implemented (old kernel) + * or because it doesn't understand the O_CLOEXEC flag. + */ + if (fd == -1 && (errno == ENOSYS || errno == EINVAL)) { + fd = epoll_create(256); + + if (fd != -1) + uv__cloexec(fd, 1); + } + + loop->backend_fd = fd; + if (fd == -1) + return UV__ERR(errno); + + return 0; +} + + +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + struct epoll_event* events; + struct epoll_event dummy; + uintptr_t i; + uintptr_t nfds; + + assert(loop->watchers != NULL); + assert(fd >= 0); + + events = (struct epoll_event*) loop->watchers[loop->nwatchers]; + nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; + if (events != NULL) + /* Invalidate events with same file descriptor */ + for (i = 0; i < nfds; i++) + if (events[i].data.fd == fd) + events[i].data.fd = -1; + + /* Remove the file descriptor from the epoll. + * This avoids a problem where the same file description remains open + * in another process, causing repeated junk epoll events. + * + * We pass in a dummy epoll_event, to work around a bug in old kernels. + */ + if (loop->backend_fd >= 0) { + /* Work around a bug in kernels 3.10 to 3.19 where passing a struct that + * has the EPOLLWAKEUP flag set generates spurious audit syslog warnings. + */ + memset(&dummy, 0, sizeof(dummy)); + epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &dummy); + } +} + + +int uv__io_check_fd(uv_loop_t* loop, int fd) { + struct epoll_event e; + int rc; + + memset(&e, 0, sizeof(e)); + e.events = POLLIN; + e.data.fd = -1; + + rc = 0; + if (epoll_ctl(loop->backend_fd, EPOLL_CTL_ADD, fd, &e)) + if (errno != EEXIST) + rc = UV__ERR(errno); + + if (rc == 0) + if (epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &e)) + abort(); + + return rc; +} + + +void uv__io_poll(uv_loop_t* loop, int timeout) { + /* A bug in kernels < 2.6.37 makes timeouts larger than ~30 minutes + * effectively infinite on 32 bits architectures. To avoid blocking + * indefinitely, we cap the timeout and poll again if necessary. + * + * Note that "30 minutes" is a simplification because it depends on + * the value of CONFIG_HZ. The magic constant assumes CONFIG_HZ=1200, + * that being the largest value I have seen in the wild (and only once.) + */ + static const int max_safe_timeout = 1789569; + static int no_epoll_pwait_cached; + static int no_epoll_wait_cached; + int no_epoll_pwait; + int no_epoll_wait; + struct epoll_event events[1024]; + struct epoll_event* pe; + struct epoll_event e; + int real_timeout; + QUEUE* q; + uv__io_t* w; + sigset_t sigset; + uint64_t sigmask; + uint64_t base; + int have_signals; + int nevents; + int count; + int nfds; + int fd; + int op; + int i; + int user_timeout; + int reset_timeout; + + if (loop->nfds == 0) { + assert(QUEUE_EMPTY(&loop->watcher_queue)); + return; + } + + memset(&e, 0, sizeof(e)); + + while (!QUEUE_EMPTY(&loop->watcher_queue)) { + q = QUEUE_HEAD(&loop->watcher_queue); + QUEUE_REMOVE(q); + QUEUE_INIT(q); + + w = QUEUE_DATA(q, uv__io_t, watcher_queue); + assert(w->pevents != 0); + assert(w->fd >= 0); + assert(w->fd < (int) loop->nwatchers); + + e.events = w->pevents; + e.data.fd = w->fd; + + if (w->events == 0) + op = EPOLL_CTL_ADD; + else + op = EPOLL_CTL_MOD; + + /* XXX Future optimization: do EPOLL_CTL_MOD lazily if we stop watching + * events, skip the syscall and squelch the events after epoll_wait(). + */ + if (epoll_ctl(loop->backend_fd, op, w->fd, &e)) { + if (errno != EEXIST) + abort(); + + assert(op == EPOLL_CTL_ADD); + + /* We've reactivated a file descriptor that's been watched before. */ + if (epoll_ctl(loop->backend_fd, EPOLL_CTL_MOD, w->fd, &e)) + abort(); + } + + w->events = w->pevents; + } + + sigmask = 0; + if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGPROF); + sigmask |= 1 << (SIGPROF - 1); + } + + assert(timeout >= -1); + base = loop->time; + count = 48; /* Benchmarks suggest this gives the best throughput. */ + real_timeout = timeout; + + if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + user_timeout = 0; + } + + /* You could argue there is a dependency between these two but + * ultimately we don't care about their ordering with respect + * to one another. Worst case, we make a few system calls that + * could have been avoided because another thread already knows + * they fail with ENOSYS. Hardly the end of the world. + */ + no_epoll_pwait = uv__load_relaxed(&no_epoll_pwait_cached); + no_epoll_wait = uv__load_relaxed(&no_epoll_wait_cached); + + for (;;) { + /* Only need to set the provider_entry_time if timeout != 0. The function + * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. + */ + if (timeout != 0) + uv__metrics_set_provider_entry_time(loop); + + /* See the comment for max_safe_timeout for an explanation of why + * this is necessary. Executive summary: kernel bug workaround. + */ + if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout) + timeout = max_safe_timeout; + + if (sigmask != 0 && no_epoll_pwait != 0) + if (pthread_sigmask(SIG_BLOCK, &sigset, NULL)) + abort(); + + if (no_epoll_wait != 0 || (sigmask != 0 && no_epoll_pwait == 0)) { + nfds = epoll_pwait(loop->backend_fd, + events, + ARRAY_SIZE(events), + timeout, + &sigset); + if (nfds == -1 && errno == ENOSYS) { + uv__store_relaxed(&no_epoll_pwait_cached, 1); + no_epoll_pwait = 1; + } + } else { + nfds = epoll_wait(loop->backend_fd, + events, + ARRAY_SIZE(events), + timeout); + if (nfds == -1 && errno == ENOSYS) { + uv__store_relaxed(&no_epoll_wait_cached, 1); + no_epoll_wait = 1; + } + } + + if (sigmask != 0 && no_epoll_pwait != 0) + if (pthread_sigmask(SIG_UNBLOCK, &sigset, NULL)) + abort(); + + /* 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. + */ + SAVE_ERRNO(uv__update_time(loop)); + + if (nfds == 0) { + assert(timeout != -1); + + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (timeout == -1) + continue; + + if (timeout == 0) + return; + + /* We may have been inside the system call for longer than |timeout| + * milliseconds so we need to update the timestamp to avoid drift. + */ + goto update_timeout; + } + + if (nfds == -1) { + if (errno == ENOSYS) { + /* epoll_wait() or epoll_pwait() failed, try the other system call. */ + assert(no_epoll_wait == 0 || no_epoll_pwait == 0); + continue; + } + + if (errno != EINTR) + abort(); + + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (timeout == -1) + continue; + + if (timeout == 0) + return; + + /* Interrupted by a signal. Update timeout and poll again. */ + goto update_timeout; + } + + have_signals = 0; + nevents = 0; + + { + /* Squelch a -Waddress-of-packed-member warning with gcc >= 9. */ + union { + struct epoll_event* events; + uv__io_t* watchers; + } x; + + x.events = events; + assert(loop->watchers != NULL); + loop->watchers[loop->nwatchers] = x.watchers; + loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; + } + + for (i = 0; i < nfds; i++) { + pe = events + i; + fd = pe->data.fd; + + /* Skip invalidated events, see uv__platform_invalidate_fd */ + if (fd == -1) + continue; + + assert(fd >= 0); + assert((unsigned) fd < loop->nwatchers); + + w = loop->watchers[fd]; + + if (w == NULL) { + /* File descriptor that we've stopped watching, disarm it. + * + * Ignore all errors because we may be racing with another thread + * when the file descriptor is closed. + */ + epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, pe); + continue; + } + + /* Give users only events they're interested in. Prevents spurious + * callbacks when previous callback invocation in this loop has stopped + * the current watcher. Also, filters out events that users has not + * requested us to watch. + */ + pe->events &= w->pevents | POLLERR | POLLHUP; + + /* Work around an epoll quirk where it sometimes reports just the + * EPOLLERR or EPOLLHUP event. In order to force the event loop to + * move forward, we merge in the read/write events that the watcher + * is interested in; uv__read() and uv__write() will then deal with + * the error or hangup in the usual fashion. + * + * Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user + * reads the available data, calls uv_read_stop(), then sometime later + * calls uv_read_start() again. By then, libuv has forgotten about the + * hangup and the kernel won't report EPOLLIN again because there's + * nothing left to read. If anything, libuv is to blame here. The + * current hack is just a quick bandaid; to properly fix it, libuv + * needs to remember the error/hangup event. We should get that for + * free when we switch over to edge-triggered I/O. + */ + if (pe->events == POLLERR || pe->events == POLLHUP) + pe->events |= + w->pevents & (POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI); + + if (pe->events != 0) { + /* Run signal watchers last. This also affects child process watchers + * because those are implemented in terms of signal watchers. + */ + if (w == &loop->signal_io_watcher) { + have_signals = 1; + } else { + uv__metrics_update_idle_time(loop); + w->cb(loop, w, pe->events); + } + + nevents++; + } + } + + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (have_signals != 0) { + uv__metrics_update_idle_time(loop); + loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); + } + + loop->watchers[loop->nwatchers] = NULL; + loop->watchers[loop->nwatchers + 1] = NULL; + + if (have_signals != 0) + return; /* Event loop should cycle now so don't poll again. */ + + if (nevents != 0) { + if (nfds == ARRAY_SIZE(events) && --count != 0) { + /* Poll for more events but don't block this time. */ + timeout = 0; + continue; + } + return; + } + + if (timeout == 0) + return; + + if (timeout == -1) + continue; + +update_timeout: + assert(timeout > 0); + + real_timeout -= (loop->time - base); + if (real_timeout <= 0) + return; + + timeout = real_timeout; + } +} + diff --git a/src/unix/internal.h b/src/unix/internal.h index 69084ed2706..5365cc71bca 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -259,6 +259,7 @@ int uv__signal_loop_fork(uv_loop_t* loop); /* platform specific */ uint64_t uv__hrtime(uv_clocktype_t type); int uv__kqueue_init(uv_loop_t* loop); +int uv__epoll_init(uv_loop_t* loop); int uv__platform_loop_init(uv_loop_t* loop); void uv__platform_loop_delete(uv_loop_t* loop); void uv__platform_invalidate_fd(uv_loop_t* loop, int fd); diff --git a/src/unix/linux-core.c b/src/unix/linux-core.c index 8746744d563..eaf97599ca6 100644 --- a/src/unix/linux-core.c +++ b/src/unix/linux-core.c @@ -82,29 +82,12 @@ static int read_times(FILE* statfile_fp, static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci); static uint64_t read_cpufreq(unsigned int cpunum); - int uv__platform_loop_init(uv_loop_t* loop) { - int fd; - fd = epoll_create1(O_CLOEXEC); - - /* epoll_create1() can fail either because it's not implemented (old kernel) - * or because it doesn't understand the O_CLOEXEC flag. - */ - if (fd == -1 && (errno == ENOSYS || errno == EINVAL)) { - fd = epoll_create(256); - - if (fd != -1) - uv__cloexec(fd, 1); - } - - loop->backend_fd = fd; + loop->inotify_fd = -1; loop->inotify_watchers = NULL; - if (fd == -1) - return UV__ERR(errno); - - return 0; + return uv__epoll_init(loop); } @@ -134,380 +117,6 @@ void uv__platform_loop_delete(uv_loop_t* loop) { } -void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { - struct epoll_event* events; - struct epoll_event dummy; - uintptr_t i; - uintptr_t nfds; - - assert(loop->watchers != NULL); - assert(fd >= 0); - - events = (struct epoll_event*) loop->watchers[loop->nwatchers]; - nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; - if (events != NULL) - /* Invalidate events with same file descriptor */ - for (i = 0; i < nfds; i++) - if (events[i].data.fd == fd) - events[i].data.fd = -1; - - /* Remove the file descriptor from the epoll. - * This avoids a problem where the same file description remains open - * in another process, causing repeated junk epoll events. - * - * We pass in a dummy epoll_event, to work around a bug in old kernels. - */ - if (loop->backend_fd >= 0) { - /* Work around a bug in kernels 3.10 to 3.19 where passing a struct that - * has the EPOLLWAKEUP flag set generates spurious audit syslog warnings. - */ - memset(&dummy, 0, sizeof(dummy)); - epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &dummy); - } -} - - -int uv__io_check_fd(uv_loop_t* loop, int fd) { - struct epoll_event e; - int rc; - - memset(&e, 0, sizeof(e)); - e.events = POLLIN; - e.data.fd = -1; - - rc = 0; - if (epoll_ctl(loop->backend_fd, EPOLL_CTL_ADD, fd, &e)) - if (errno != EEXIST) - rc = UV__ERR(errno); - - if (rc == 0) - if (epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &e)) - abort(); - - return rc; -} - - -void uv__io_poll(uv_loop_t* loop, int timeout) { - /* A bug in kernels < 2.6.37 makes timeouts larger than ~30 minutes - * effectively infinite on 32 bits architectures. To avoid blocking - * indefinitely, we cap the timeout and poll again if necessary. - * - * Note that "30 minutes" is a simplification because it depends on - * the value of CONFIG_HZ. The magic constant assumes CONFIG_HZ=1200, - * that being the largest value I have seen in the wild (and only once.) - */ - static const int max_safe_timeout = 1789569; - static int no_epoll_pwait_cached; - static int no_epoll_wait_cached; - int no_epoll_pwait; - int no_epoll_wait; - struct epoll_event events[1024]; - struct epoll_event* pe; - struct epoll_event e; - int real_timeout; - QUEUE* q; - uv__io_t* w; - sigset_t sigset; - uint64_t sigmask; - uint64_t base; - int have_signals; - int nevents; - int count; - int nfds; - int fd; - int op; - int i; - int user_timeout; - int reset_timeout; - - if (loop->nfds == 0) { - assert(QUEUE_EMPTY(&loop->watcher_queue)); - return; - } - - memset(&e, 0, sizeof(e)); - - while (!QUEUE_EMPTY(&loop->watcher_queue)) { - q = QUEUE_HEAD(&loop->watcher_queue); - QUEUE_REMOVE(q); - QUEUE_INIT(q); - - w = QUEUE_DATA(q, uv__io_t, watcher_queue); - assert(w->pevents != 0); - assert(w->fd >= 0); - assert(w->fd < (int) loop->nwatchers); - - e.events = w->pevents; - e.data.fd = w->fd; - - if (w->events == 0) - op = EPOLL_CTL_ADD; - else - op = EPOLL_CTL_MOD; - - /* XXX Future optimization: do EPOLL_CTL_MOD lazily if we stop watching - * events, skip the syscall and squelch the events after epoll_wait(). - */ - if (epoll_ctl(loop->backend_fd, op, w->fd, &e)) { - if (errno != EEXIST) - abort(); - - assert(op == EPOLL_CTL_ADD); - - /* We've reactivated a file descriptor that's been watched before. */ - if (epoll_ctl(loop->backend_fd, EPOLL_CTL_MOD, w->fd, &e)) - abort(); - } - - w->events = w->pevents; - } - - sigmask = 0; - if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { - sigemptyset(&sigset); - sigaddset(&sigset, SIGPROF); - sigmask |= 1 << (SIGPROF - 1); - } - - assert(timeout >= -1); - base = loop->time; - count = 48; /* Benchmarks suggest this gives the best throughput. */ - real_timeout = timeout; - - if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { - reset_timeout = 1; - user_timeout = timeout; - timeout = 0; - } else { - reset_timeout = 0; - user_timeout = 0; - } - - /* You could argue there is a dependency between these two but - * ultimately we don't care about their ordering with respect - * to one another. Worst case, we make a few system calls that - * could have been avoided because another thread already knows - * they fail with ENOSYS. Hardly the end of the world. - */ - no_epoll_pwait = uv__load_relaxed(&no_epoll_pwait_cached); - no_epoll_wait = uv__load_relaxed(&no_epoll_wait_cached); - - for (;;) { - /* Only need to set the provider_entry_time if timeout != 0. The function - * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. - */ - if (timeout != 0) - uv__metrics_set_provider_entry_time(loop); - - /* See the comment for max_safe_timeout for an explanation of why - * this is necessary. Executive summary: kernel bug workaround. - */ - if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout) - timeout = max_safe_timeout; - - if (sigmask != 0 && no_epoll_pwait != 0) - if (pthread_sigmask(SIG_BLOCK, &sigset, NULL)) - abort(); - - if (no_epoll_wait != 0 || (sigmask != 0 && no_epoll_pwait == 0)) { - nfds = epoll_pwait(loop->backend_fd, - events, - ARRAY_SIZE(events), - timeout, - &sigset); - if (nfds == -1 && errno == ENOSYS) { - uv__store_relaxed(&no_epoll_pwait_cached, 1); - no_epoll_pwait = 1; - } - } else { - nfds = epoll_wait(loop->backend_fd, - events, - ARRAY_SIZE(events), - timeout); - if (nfds == -1 && errno == ENOSYS) { - uv__store_relaxed(&no_epoll_wait_cached, 1); - no_epoll_wait = 1; - } - } - - if (sigmask != 0 && no_epoll_pwait != 0) - if (pthread_sigmask(SIG_UNBLOCK, &sigset, NULL)) - abort(); - - /* 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. - */ - SAVE_ERRNO(uv__update_time(loop)); - - if (nfds == 0) { - assert(timeout != -1); - - if (reset_timeout != 0) { - timeout = user_timeout; - reset_timeout = 0; - } - - if (timeout == -1) - continue; - - if (timeout == 0) - return; - - /* We may have been inside the system call for longer than |timeout| - * milliseconds so we need to update the timestamp to avoid drift. - */ - goto update_timeout; - } - - if (nfds == -1) { - if (errno == ENOSYS) { - /* epoll_wait() or epoll_pwait() failed, try the other system call. */ - assert(no_epoll_wait == 0 || no_epoll_pwait == 0); - continue; - } - - if (errno != EINTR) - abort(); - - if (reset_timeout != 0) { - timeout = user_timeout; - reset_timeout = 0; - } - - if (timeout == -1) - continue; - - if (timeout == 0) - return; - - /* Interrupted by a signal. Update timeout and poll again. */ - goto update_timeout; - } - - have_signals = 0; - nevents = 0; - - { - /* Squelch a -Waddress-of-packed-member warning with gcc >= 9. */ - union { - struct epoll_event* events; - uv__io_t* watchers; - } x; - - x.events = events; - assert(loop->watchers != NULL); - loop->watchers[loop->nwatchers] = x.watchers; - loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; - } - - for (i = 0; i < nfds; i++) { - pe = events + i; - fd = pe->data.fd; - - /* Skip invalidated events, see uv__platform_invalidate_fd */ - if (fd == -1) - continue; - - assert(fd >= 0); - assert((unsigned) fd < loop->nwatchers); - - w = loop->watchers[fd]; - - if (w == NULL) { - /* File descriptor that we've stopped watching, disarm it. - * - * Ignore all errors because we may be racing with another thread - * when the file descriptor is closed. - */ - epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, pe); - continue; - } - - /* Give users only events they're interested in. Prevents spurious - * callbacks when previous callback invocation in this loop has stopped - * the current watcher. Also, filters out events that users has not - * requested us to watch. - */ - pe->events &= w->pevents | POLLERR | POLLHUP; - - /* Work around an epoll quirk where it sometimes reports just the - * EPOLLERR or EPOLLHUP event. In order to force the event loop to - * move forward, we merge in the read/write events that the watcher - * is interested in; uv__read() and uv__write() will then deal with - * the error or hangup in the usual fashion. - * - * Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user - * reads the available data, calls uv_read_stop(), then sometime later - * calls uv_read_start() again. By then, libuv has forgotten about the - * hangup and the kernel won't report EPOLLIN again because there's - * nothing left to read. If anything, libuv is to blame here. The - * current hack is just a quick bandaid; to properly fix it, libuv - * needs to remember the error/hangup event. We should get that for - * free when we switch over to edge-triggered I/O. - */ - if (pe->events == POLLERR || pe->events == POLLHUP) - pe->events |= - w->pevents & (POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI); - - if (pe->events != 0) { - /* Run signal watchers last. This also affects child process watchers - * because those are implemented in terms of signal watchers. - */ - if (w == &loop->signal_io_watcher) { - have_signals = 1; - } else { - uv__metrics_update_idle_time(loop); - w->cb(loop, w, pe->events); - } - - nevents++; - } - } - - if (reset_timeout != 0) { - timeout = user_timeout; - reset_timeout = 0; - } - - if (have_signals != 0) { - uv__metrics_update_idle_time(loop); - loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); - } - - loop->watchers[loop->nwatchers] = NULL; - loop->watchers[loop->nwatchers + 1] = NULL; - - if (have_signals != 0) - return; /* Event loop should cycle now so don't poll again. */ - - if (nevents != 0) { - if (nfds == ARRAY_SIZE(events) && --count != 0) { - /* Poll for more events but don't block this time. */ - timeout = 0; - continue; - } - return; - } - - if (timeout == 0) - return; - - if (timeout == -1) - continue; - -update_timeout: - assert(timeout > 0); - - real_timeout -= (loop->time - base); - if (real_timeout <= 0) - return; - - timeout = real_timeout; - } -} - uint64_t uv__hrtime(uv_clocktype_t type) { static clock_t fast_clock_id = -1; diff --git a/src/unix/sunos.c b/src/unix/sunos.c index d511c18b85e..5e2ec688430 100644 --- a/src/unix/sunos.c +++ b/src/unix/sunos.c @@ -65,24 +65,9 @@ int uv__platform_loop_init(uv_loop_t* loop) { - int err; - int fd; - loop->fs_fd = -1; - loop->backend_fd = -1; - - fd = port_create(); - if (fd == -1) - return UV__ERR(errno); - err = uv__cloexec(fd, 1); - if (err) { - uv__close(fd); - return err; - } - loop->backend_fd = fd; - - return 0; + return uv__epoll_init(loop); } @@ -111,264 +96,6 @@ int uv__io_fork(uv_loop_t* loop) { } -void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { - struct port_event* events; - uintptr_t i; - uintptr_t nfds; - - assert(loop->watchers != NULL); - assert(fd >= 0); - - events = (struct port_event*) loop->watchers[loop->nwatchers]; - nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; - if (events == NULL) - return; - - /* Invalidate events with same file descriptor */ - for (i = 0; i < nfds; i++) - if ((int) events[i].portev_object == fd) - events[i].portev_object = -1; -} - - -int uv__io_check_fd(uv_loop_t* loop, int fd) { - if (port_associate(loop->backend_fd, PORT_SOURCE_FD, fd, POLLIN, 0)) - return UV__ERR(errno); - - if (port_dissociate(loop->backend_fd, PORT_SOURCE_FD, fd)) { - perror("(libuv) port_dissociate()"); - abort(); - } - - return 0; -} - - -void uv__io_poll(uv_loop_t* loop, int timeout) { - struct port_event events[1024]; - struct port_event* pe; - struct timespec spec; - QUEUE* q; - uv__io_t* w; - sigset_t* pset; - sigset_t set; - uint64_t base; - uint64_t diff; - uint64_t idle_poll; - unsigned int nfds; - unsigned int i; - int saved_errno; - int have_signals; - int nevents; - int count; - int err; - int fd; - int user_timeout; - int reset_timeout; - - if (loop->nfds == 0) { - assert(QUEUE_EMPTY(&loop->watcher_queue)); - return; - } - - while (!QUEUE_EMPTY(&loop->watcher_queue)) { - q = QUEUE_HEAD(&loop->watcher_queue); - QUEUE_REMOVE(q); - QUEUE_INIT(q); - - w = QUEUE_DATA(q, uv__io_t, watcher_queue); - assert(w->pevents != 0); - - if (port_associate(loop->backend_fd, - PORT_SOURCE_FD, - w->fd, - w->pevents, - 0)) { - perror("(libuv) port_associate()"); - abort(); - } - - 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. */ - - if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { - reset_timeout = 1; - user_timeout = timeout; - timeout = 0; - } else { - reset_timeout = 0; - } - - for (;;) { - /* Only need to set the provider_entry_time if timeout != 0. The function - * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. - */ - if (timeout != 0) - uv__metrics_set_provider_entry_time(loop); - - if (timeout != -1) { - spec.tv_sec = timeout / 1000; - spec.tv_nsec = (timeout % 1000) * 1000000; - } - - /* Work around a kernel bug where nfds is not updated. */ - events[0].portev_source = 0; - - nfds = 1; - saved_errno = 0; - - 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. - */ - if (errno == EINTR || errno == ETIME) { - saved_errno = errno; - } else { - perror("(libuv) port_getn()"); - abort(); - } - } - - /* 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. - */ - SAVE_ERRNO(uv__update_time(loop)); - - if (events[0].portev_source == 0) { - if (reset_timeout != 0) { - timeout = user_timeout; - reset_timeout = 0; - } - - if (timeout == 0) - return; - - if (timeout == -1) - continue; - - goto update_timeout; - } - - if (nfds == 0) { - assert(timeout != -1); - return; - } - - have_signals = 0; - nevents = 0; - - assert(loop->watchers != NULL); - loop->watchers[loop->nwatchers] = (void*) events; - loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; - for (i = 0; i < nfds; i++) { - pe = events + i; - fd = pe->portev_object; - - /* Skip invalidated events, see uv__platform_invalidate_fd */ - if (fd == -1) - continue; - - assert(fd >= 0); - assert((unsigned) fd < loop->nwatchers); - - w = loop->watchers[fd]; - - /* File descriptor that we've stopped watching, ignore. */ - if (w == NULL) - continue; - - /* Run signal watchers last. This also affects child process watchers - * because those are implemented in terms of signal watchers. - */ - if (w == &loop->signal_io_watcher) { - have_signals = 1; - } else { - uv__metrics_update_idle_time(loop); - w->cb(loop, w, pe->portev_events); - } - - nevents++; - - if (w != loop->watchers[fd]) - continue; /* Disabled by callback. */ - - /* Events Ports operates in oneshot mode, rearm timer on next run. */ - if (w->pevents != 0 && QUEUE_EMPTY(&w->watcher_queue)) - QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); - } - - if (reset_timeout != 0) { - timeout = user_timeout; - reset_timeout = 0; - } - - if (have_signals != 0) { - uv__metrics_update_idle_time(loop); - loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); - } - - loop->watchers[loop->nwatchers] = NULL; - loop->watchers[loop->nwatchers + 1] = NULL; - - if (have_signals != 0) - return; /* Event loop should cycle now so don't poll again. */ - - if (nevents != 0) { - if (nfds == ARRAY_SIZE(events) && --count != 0) { - /* Poll for more events but don't block this time. */ - timeout = 0; - continue; - } - return; - } - - if (saved_errno == ETIME) { - assert(timeout != -1); - return; - } - - if (timeout == 0) - return; - - if (timeout == -1) - continue; - -update_timeout: - assert(timeout > 0); - - diff = loop->time - base; - if (diff >= (uint64_t) timeout) - return; - - timeout -= diff; - } -} - - uint64_t uv__hrtime(uv_clocktype_t type) { return gethrtime(); } From b6d51dc40eb420d81a28c3b3c7b6de4ee9319dfc Mon Sep 17 00:00:00 2001 From: "Joshua M. Clulow" Date: Fri, 2 Jul 2021 12:06:18 -0700 Subject: [PATCH 6/8] illumos,tty: UV_TTY_MODE_IO waits for 4 bytes uv_tty_set_mode() allows a tty device to be set to raw mode with UV_TTY_MODE_RAW, which correctly sets MIN and TIME to appropriate values for character input. When UV_TTY_MODE_IO is passed, on illumos systems a compatibility implementation of cfmakeraw() is used that does _not_ set MIN or TIME. As a result, consumers of IO mode will block until a minimum of 4 bytes is available on the tty instead of just 1 as is expected. PR-URL: https://github.com/libuv/libuv/pull/3219 Reviewed-By: Jameson Nash --- src/unix/tty.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/unix/tty.c b/src/unix/tty.c index 6f60abaadb0..9442cf16360 100644 --- a/src/unix/tty.c +++ b/src/unix/tty.c @@ -242,6 +242,24 @@ static void uv__tty_make_raw(struct termios* tio) { tio->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); tio->c_cflag &= ~(CSIZE | PARENB); tio->c_cflag |= CS8; + + /* + * By default, most software expects a pending read to block until at + * least one byte becomes available. As per termio(7I), this requires + * setting the MIN and TIME parameters appropriately. + * + * As a somewhat unfortunate artifact of history, the MIN and TIME slots + * in the control character array overlap with the EOF and EOL slots used + * for canonical mode processing. Because the EOF character needs to be + * the ASCII EOT value (aka Control-D), it has the byte value 4. When + * switching to raw mode, this is interpreted as a MIN value of 4; i.e., + * reads will block until at least four bytes have been input. + * + * Other platforms with a distinct MIN slot like Linux and FreeBSD appear + * to default to a MIN value of 1, so we'll force that value here: + */ + tio->c_cc[VMIN] = 1; + tio->c_cc[VTIME] = 0; #else cfmakeraw(tio); #endif /* #ifdef __sun */ From 337dbfd968d211b7f2fe0a90e019dc90fb61153f Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Wed, 7 Jul 2021 15:59:20 -0400 Subject: [PATCH 7/8] doc: add vtjnash GPG ID PR-URL: https://github.com/libuv/libuv/pull/3235 Reviewed-By: Richard Lau --- MAINTAINERS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 268251e615a..fb7e5ef3417 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -16,6 +16,7 @@ libuv is currently managed by the following individuals: * **Imran Iqbal** ([@imran-iq](https://github.com/imran-iq)) - GPG key: 9DFE AA5F 481B BF77 2D90 03CE D592 4925 2F8E C41A (pubkey-iwuzhere) * **Jameson Nash** ([@vtjnash](https://github.com/vtjnash)) + - GPG key: AEAD 0A4B 6867 6775 1A0E 4AEF 34A2 5FB1 2824 6514 (pubkey-vtjnash) * **John Barboza** ([@jbarz](https://github.com/jbarz)) * **Kaoru Takanashi** ([@erw7](https://github.com/erw7)) - GPG Key: 5804 F999 8A92 2AFB A398 47A0 7183 5090 6134 887F (pubkey-erw7) From b12699b1efabfd241324f4ab6cfd6ce576db491e Mon Sep 17 00:00:00 2001 From: Richard Lau Date: Wed, 7 Jul 2021 21:19:47 +0100 Subject: [PATCH 8/8] linux: read CPU model information on ppc Fixes: https://github.com/libuv/libuv/issues/3217 PR-URL: https://github.com/libuv/libuv/pull/3232 Reviewed-By: Jameson Nash --- src/unix/linux-core.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/unix/linux-core.c b/src/unix/linux-core.c index eaf97599ca6..2716e2be65d 100644 --- a/src/unix/linux-core.c +++ b/src/unix/linux-core.c @@ -349,14 +349,19 @@ static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci) { } -/* Also reads the CPU frequency on x86. The other architectures only have - * a BogoMIPS field, which may not be very accurate. +/* Also reads the CPU frequency on ppc and x86. The other architectures only + * have a BogoMIPS field, which may not be very accurate. * * Note: Simply returns on error, uv_cpu_info() takes care of the cleanup. */ static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) { +#if defined(__PPC__) + static const char model_marker[] = "cpu\t\t: "; + static const char speed_marker[] = "clock\t\t: "; +#else static const char model_marker[] = "model name\t: "; static const char speed_marker[] = "cpu MHz\t\t: "; +#endif const char* inferred_model; unsigned int model_idx; unsigned int speed_idx; @@ -378,6 +383,7 @@ static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) { #if defined(__arm__) || \ defined(__i386__) || \ defined(__mips__) || \ + defined(__PPC__) || \ defined(__x86_64__) fp = uv__open_file("/proc/cpuinfo"); if (fp == NULL) @@ -426,7 +432,7 @@ static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) { } fclose(fp); -#endif /* __arm__ || __i386__ || __mips__ || __x86_64__ */ +#endif /* __arm__ || __i386__ || __mips__ || __PPC__ || __x86_64__ */ /* Now we want to make sure that all the models contain *something* because * it's not safe to leave them as null. Copy the last entry unless there