From dcff5d565c000904cabf48c627534a779a40527a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 24 Feb 2015 11:05:39 +0100 Subject: [PATCH] deps: update libuv to 0.10.36 PR: #9274 PR-URL: https://github.com/joyent/node/pull/9274 Reviewed-By: Julien Gilli --- deps/uv/AUTHORS | 3 + deps/uv/ChangeLog | 70 ++++++++- deps/uv/build.mk | 2 + deps/uv/checksparse.sh | 1 + deps/uv/config-unix.mk | 4 +- deps/uv/include/uv-private/uv-win.h | 5 +- deps/uv/src/unix/async.c | 43 ++---- deps/uv/src/unix/atomic-ops.h | 60 ++++++++ deps/uv/src/unix/internal.h | 15 ++ deps/uv/src/unix/linux-core.c | 40 +++-- deps/uv/src/unix/linux-syscalls.c | 6 +- deps/uv/src/unix/linux-syscalls.h | 2 +- deps/uv/src/unix/process.c | 15 ++ deps/uv/src/unix/stream.c | 13 ++ deps/uv/src/version.c | 2 +- deps/uv/src/win/dl.c | 35 ++++- deps/uv/src/win/fs.c | 4 +- deps/uv/src/win/internal.h | 4 +- deps/uv/src/win/poll.c | 40 +++-- deps/uv/src/win/winsock.c | 12 +- deps/uv/test/test-dlerror.c | 19 +-- deps/uv/test/test-list.h | 10 ++ deps/uv/test/test-loop-configure.c | 40 +++++ .../test-poll-close-doesnt-corrupt-stack.c | 114 +++++++++++++++ deps/uv/test/test-tcp-oob.c | 137 ++++++++++++++++++ deps/uv/uv.gyp | 4 + 26 files changed, 595 insertions(+), 105 deletions(-) create mode 100644 deps/uv/src/unix/atomic-ops.h create mode 100644 deps/uv/test/test-loop-configure.c create mode 100644 deps/uv/test/test-poll-close-doesnt-corrupt-stack.c create mode 100644 deps/uv/test/test-tcp-oob.c diff --git a/deps/uv/AUTHORS b/deps/uv/AUTHORS index 48a74db32f6087..1123dd8ec4c977 100644 --- a/deps/uv/AUTHORS +++ b/deps/uv/AUTHORS @@ -132,3 +132,6 @@ Javier Hernández Tonis Tiigi Michael Hudson-Doyle Helge Deller +Logan Rosen +Kenneth Perry +Michael Penick diff --git a/deps/uv/ChangeLog b/deps/uv/ChangeLog index 4ba348d571b587..bd03d53b5c3d94 100644 --- a/deps/uv/ChangeLog +++ b/deps/uv/ChangeLog @@ -1,4 +1,72 @@ -2014.12.10, Version 0.10.30 (Stable) +2015.02.27, Version 0.10.36 (Stable) + +Changes since version 0.10.35: + +* stream: ignore EINVAL for SO_OOBINLINE on OS X (Fedor Indutny) + + +2015.02.25, Version 0.10.35 (Stable), 4dc978825d870643bbaa4660f71d22975efba29e + +Changes since version 0.10.34: + +* stream: use SO_OOBINLINE on OS X (Fedor Indutny) + + +2015.02.21, Version 0.10.34 (Stable), 37aa4aa9b9712c778d7b249563e868cabfdb8332 + +Changes since version 0.10.33: + +* unix: add atomic-ops.h (Ben Noordhuis) + +* unix: fix for uv_async data race (Michael Penick) + +* unix: call setgoups before calling setuid/setgid (Saúl Ibarra Corretgé) + + +2015.01.29, Version 0.10.33 (Stable), 7a2253d33ad8215a26c1b34f1952aee7242dd687 + +Changes since version 0.10.32: + +* linux: fix epoll_pwait() regression with < 2.6.19 (Ben Noordhuis) + +* test: back-port uv_loop_configure() test (Ben Noordhuis) + + +2015.01.06, Version 0.10.32 (Stable), 378de30c59aef5fdb6d130fa5cfcb0a68fce571c + +Changes since version 0.10.31: + +* linux: fix epoll_pwait() sigmask size calculation (Ben Noordhuis) + + +2014.12.25, Version 0.10.31 (Stable), 4dbd27e2219069a6daa769fb37f98673b77b4261 + +Changes since version 0.10.30: + +* test: test that closing a poll handle doesn't corrupt the stack (Bert Belder) + +* win: fix compilation of tests (Marc Schlaich) + +* Revert "win: keep a reference to AFD_POLL_INFO in cancel poll" (Bert Belder) + +* win: avoid stack corruption when closing a poll handle (Bert Belder) + +* gitignore: ignore Visual Studio files (Marc Schlaich) + +* win: set fallback message if FormatMessage fails (Marc Schlaich) + +* win: fall back to default language in uv_dlerror (Marc Schlaich) + +* test: improve compatibility for dlerror test (Marc Schlaich) + +* test: check dlerror is "no error" in no error case (Marc Schlaich) + +* build: link against -pthread (Logan Rosen) + +* win: scandir use 'ls' for formatting long strings (Kenneth Perry) + + +2014.12.10, Version 0.10.30 (Stable), 5a63f5e9546dca482eeebc3054139b21f509f21f Changes since version 0.10.29: diff --git a/deps/uv/build.mk b/deps/uv/build.mk index a2fdac7d453abf..efa42e1bcc808b 100644 --- a/deps/uv/build.mk +++ b/deps/uv/build.mk @@ -91,6 +91,7 @@ TESTS= \ test/test-ipc-send-recv.o \ test/test-loop-handles.o \ test/test-loop-stop.o \ + test/test-loop-configure.o \ test/test-multiple-listen.o \ test/test-mutexes.o \ test/test-osx-select.o \ @@ -126,6 +127,7 @@ TESTS= \ test/test-tcp-connect-timeout.o \ test/test-tcp-flags.o \ test/test-tcp-open.o \ + test/test-tcp-oob.o \ test/test-tcp-read-stop.o \ test/test-tcp-shutdown-after-write.o \ test/test-tcp-unexpected-read.o \ diff --git a/deps/uv/checksparse.sh b/deps/uv/checksparse.sh index 01fac07a39fead..d2b1d5e05d34d3 100755 --- a/deps/uv/checksparse.sh +++ b/deps/uv/checksparse.sh @@ -145,6 +145,7 @@ test/test-tcp-open.c test/test-tcp-read-stop.c test/test-tcp-shutdown-after-write.c test/test-tcp-unexpected-read.c +test/test-tcp-oob.c test/test-tcp-write-error.c test/test-tcp-write-to-half-open-connection.c test/test-tcp-writealot.c diff --git a/deps/uv/config-unix.mk b/deps/uv/config-unix.mk index 37c4a72f6ee6bd..db21daa0ba5b1f 100644 --- a/deps/uv/config-unix.mk +++ b/deps/uv/config-unix.mk @@ -22,7 +22,7 @@ E= CSTDFLAG=--std=c89 -pedantic -Wall -Wextra -Wno-unused-parameter CFLAGS += -g CPPFLAGS += -I$(SRCDIR)/src -LDFLAGS=-lm +LDFLAGS=-lm -pthread CPPFLAGS += -D_LARGEFILE_SOURCE CPPFLAGS += -D_FILE_OFFSET_BITS=64 @@ -186,7 +186,7 @@ src/.buildstamp src/unix/.buildstamp test/.buildstamp: mkdir -p $(@D) touch $@ -src/unix/%.o src/unix/%.pic.o: src/unix/%.c include/uv.h include/uv-private/uv-unix.h src/unix/internal.h src/unix/.buildstamp $(DTRACE_HEADER) +src/unix/%.o src/unix/%.pic.o: src/unix/%.c include/uv.h include/uv-private/uv-unix.h src/unix/atomic-ops.h src/unix/internal.h src/unix/.buildstamp $(DTRACE_HEADER) $(CC) $(CSTDFLAG) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ src/%.o src/%.pic.o: src/%.c include/uv.h include/uv-private/uv-unix.h src/.buildstamp diff --git a/deps/uv/include/uv-private/uv-win.h b/deps/uv/include/uv-private/uv-win.h index e5f2f3b4c8b9a9..c9ec38ef4ca302 100644 --- a/deps/uv/include/uv-private/uv-win.h +++ b/deps/uv/include/uv-private/uv-win.h @@ -474,10 +474,7 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s); /* Used in fast mode */ \ SOCKET peer_socket; \ AFD_POLL_INFO afd_poll_info_1; \ - union { \ - AFD_POLL_INFO* afd_poll_info_ptr; \ - AFD_POLL_INFO afd_poll_info; \ - } afd_poll_info_2; \ + AFD_POLL_INFO afd_poll_info_2; \ /* Used in fast and slow mode. */ \ uv_req_t poll_req_1; \ uv_req_t poll_req_2; \ diff --git a/deps/uv/src/unix/async.c b/deps/uv/src/unix/async.c index 43f54ed4b28652..8adf046a697161 100644 --- a/deps/uv/src/unix/async.c +++ b/deps/uv/src/unix/async.c @@ -24,6 +24,7 @@ #include "uv.h" #include "internal.h" +#include "atomic-ops.h" #include #include @@ -34,7 +35,6 @@ static void uv__async_event(uv_loop_t* loop, struct uv__async* w, unsigned int nevents); -static int uv__async_make_pending(int* pending); static int uv__async_eventfd(void); @@ -54,7 +54,11 @@ int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) { int uv_async_send(uv_async_t* handle) { - if (uv__async_make_pending(&handle->pending) == 0) + /* Do a cheap read first. */ + if (ACCESS_ONCE(int, handle->pending) != 0) + return 0; + + if (cmpxchgi(&handle->pending, 0, 1) == 0) uv__async_send(&handle->loop->async_watcher); return 0; @@ -75,41 +79,12 @@ static void uv__async_event(uv_loop_t* loop, ngx_queue_foreach(q, &loop->async_handles) { h = ngx_queue_data(q, uv_async_t, queue); - if (!h->pending) continue; - h->pending = 0; - h->async_cb(h, 0); - } -} + if (cmpxchgi(&h->pending, 1, 0) == 0) + continue; -static int uv__async_make_pending(int* pending) { - /* Do a cheap read first. */ - if (ACCESS_ONCE(int, *pending) != 0) - return 1; - - /* Micro-optimization: use atomic memory operations to detect if we've been - * preempted by another thread and don't have to make an expensive syscall. - * This speeds up the heavily contended case by about 1-2% and has little - * if any impact on the non-contended case. - * - * Use XCHG instead of the CMPXCHG that __sync_val_compare_and_swap() emits - * on x86, it's about 4x faster. It probably makes zero difference in the - * grand scheme of things but I'm OCD enough not to let this one pass. - */ -#if defined(__i386__) || defined(__x86_64__) - { - unsigned int val = 1; - __asm__ __volatile__ ("xchgl %0, %1" - : "+r" (val) - : "m" (*pending)); - return val != 0; + h->async_cb(h, 0); } -#elif defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ > 0) - return __sync_val_compare_and_swap(pending, 0, 1) != 0; -#else - ACCESS_ONCE(int, *pending) = 1; - return 0; -#endif } diff --git a/deps/uv/src/unix/atomic-ops.h b/deps/uv/src/unix/atomic-ops.h new file mode 100644 index 00000000000000..7e4e64beda101d --- /dev/null +++ b/deps/uv/src/unix/atomic-ops.h @@ -0,0 +1,60 @@ +/* Copyright (c) 2013, Ben Noordhuis + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef UV_ATOMIC_OPS_H_ +#define UV_ATOMIC_OPS_H_ + +#include "internal.h" /* UV_UNUSED */ + +UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)); +UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval)); +UV_UNUSED(static void cpu_relax(void)); + +/* Prefer hand-rolled assembly over the gcc builtins because the latter also + * issue full memory barriers. + */ +UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) { +#if defined(__i386__) || defined(__x86_64__) + int out; + __asm__ __volatile__ ("lock; cmpxchg %2, %1;" + : "=a" (out), "+m" (*(volatile int*) ptr) + : "r" (newval), "0" (oldval) + : "memory"); + return out; +#else + return __sync_val_compare_and_swap(ptr, oldval, newval); +#endif +} + +UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval)) { +#if defined(__i386__) || defined(__x86_64__) + long out; + __asm__ __volatile__ ("lock; cmpxchg %2, %1;" + : "=a" (out), "+m" (*(volatile long*) ptr) + : "r" (newval), "0" (oldval) + : "memory"); + return out; +#else + return __sync_val_compare_and_swap(ptr, oldval, newval); +#endif +} + +UV_UNUSED(static void cpu_relax(void)) { +#if defined(__i386__) || defined(__x86_64__) + __asm__ __volatile__ ("rep; nop"); /* a.k.a. PAUSE */ +#endif +} + +#endif /* UV_ATOMIC_OPS_H_ */ diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h index 003e1a5093be90..af360539ed6056 100644 --- a/deps/uv/src/unix/internal.h +++ b/deps/uv/src/unix/internal.h @@ -66,6 +66,21 @@ } \ while (0) +/* The __clang__ and __INTEL_COMPILER checks are superfluous because they + * define __GNUC__. They are here to convey to you, dear reader, that these + * macros are enabled when compiling with clang or icc. + */ +#if defined(__clang__) || \ + defined(__GNUC__) || \ + defined(__INTEL_COMPILER) || \ + defined(__SUNPRO_C) +# define UV_DESTRUCTOR(declaration) __attribute__((destructor)) declaration +# define UV_UNUSED(declaration) __attribute__((unused)) declaration +#else +# define UV_DESTRUCTOR(declaration) declaration +# define UV_UNUSED(declaration) declaration +#endif + #if defined(__linux__) # define UV__POLLIN UV__EPOLLIN # define UV__POLLOUT UV__EPOLLOUT diff --git a/deps/uv/src/unix/linux-core.c b/deps/uv/src/unix/linux-core.c index 47938f2b202067..6cd5a7f003f16b 100644 --- a/deps/uv/src/unix/linux-core.c +++ b/deps/uv/src/unix/linux-core.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include @@ -126,13 +125,15 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { void uv__io_poll(uv_loop_t* loop, int timeout) { + static int no_epoll_pwait; + static int no_epoll_wait; struct uv__epoll_event events[1024]; struct uv__epoll_event* pe; struct uv__epoll_event e; ngx_queue_t* q; uv__io_t* w; - sigset_t* pset; - sigset_t set; + sigset_t sigset; + uint64_t sigmask; uint64_t base; uint64_t diff; int nevents; @@ -141,7 +142,6 @@ 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)); @@ -183,11 +183,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { w->events = w->pevents; } - pset = NULL; + sigmask = 0; if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { - pset = &set; - sigemptyset(pset); - sigaddset(pset, SIGPROF); + sigemptyset(&sigset); + sigaddset(&sigset, SIGPROF); + sigmask |= 1 << (SIGPROF - 1); } assert(timeout >= -1); @@ -195,23 +195,31 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { count = 48; /* Benchmarks suggest this gives the best throughput. */ for (;;) { - if (no_epoll_wait || pset != NULL) { + if (sigmask != 0 && no_epoll_pwait != 0) + if (pthread_sigmask(SIG_BLOCK, &sigset, NULL)) + abort(); + + if (sigmask != 0 && no_epoll_pwait == 0) { nfds = uv__epoll_pwait(loop->backend_fd, events, ARRAY_SIZE(events), timeout, - pset); + sigmask); + if (nfds == -1 && errno == ENOSYS) + no_epoll_pwait = 1; } else { nfds = uv__epoll_wait(loop->backend_fd, events, ARRAY_SIZE(events), timeout); - if (nfds == -1 && errno == ENOSYS) { + if (nfds == -1 && errno == ENOSYS) no_epoll_wait = 1; - continue; - } } + 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. @@ -224,6 +232,12 @@ void uv__io_poll(uv_loop_t* loop, int 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(); diff --git a/deps/uv/src/unix/linux-syscalls.c b/deps/uv/src/unix/linux-syscalls.c index c9945438f49dd6..267915d879b969 100644 --- a/deps/uv/src/unix/linux-syscalls.c +++ b/deps/uv/src/unix/linux-syscalls.c @@ -291,15 +291,15 @@ int uv__epoll_pwait(int epfd, struct uv__epoll_event* events, int nevents, int timeout, - const sigset_t* sigmask) { + uint64_t sigmask) { #if defined(__NR_epoll_pwait) return syscall(__NR_epoll_pwait, epfd, events, nevents, timeout, - sigmask, - _NSIG / 8); + &sigmask, + sizeof(sigmask)); #else return errno = ENOSYS, -1; #endif diff --git a/deps/uv/src/unix/linux-syscalls.h b/deps/uv/src/unix/linux-syscalls.h index 62eb5c5a3680fa..7be73bb6e46713 100644 --- a/deps/uv/src/unix/linux-syscalls.h +++ b/deps/uv/src/unix/linux-syscalls.h @@ -130,7 +130,7 @@ int uv__epoll_pwait(int epfd, struct uv__epoll_event* events, int nevents, int timeout, - const sigset_t* sigmask); + uint64_t sigmask); int uv__eventfd2(unsigned int count, int flags); int uv__inotify_init(void); int uv__inotify_init1(int flags); diff --git a/deps/uv/src/unix/process.c b/deps/uv/src/unix/process.c index 19686a291f07fe..d1f9440c53d74b 100644 --- a/deps/uv/src/unix/process.c +++ b/deps/uv/src/unix/process.c @@ -40,6 +40,10 @@ extern char **environ; #endif +#ifdef __linux__ +# include +#endif + static ngx_queue_t* uv__process_queue(uv_loop_t* loop, int pid) { assert(pid > 0); @@ -331,6 +335,17 @@ static void uv__process_child_init(uv_process_options_t options, _exit(127); } + if (options.flags & (UV_PROCESS_SETUID | UV_PROCESS_SETGID)) { + /* When dropping privileges from root, the `setgroups` call will + * remove any extraneous groups. If we don't call this, then + * even though our uid has dropped, we may still have groups + * that enable us to do super-user things. This will fail if we + * aren't root, so don't bother checking the return value, this + * is just done as an optimistic privilege dropping function. + */ + SAVE_ERRNO(setgroups(0, NULL)); + } + if ((options.flags & UV_PROCESS_SETGID) && setgid(options.gid)) { uv__write_int(error_fd, errno); _exit(127); diff --git a/deps/uv/src/unix/stream.c b/deps/uv/src/unix/stream.c index 81933e6462f276..a8802c85a5bfec 100644 --- a/deps/uv/src/unix/stream.c +++ b/deps/uv/src/unix/stream.c @@ -403,6 +403,10 @@ int uv__stream_try_select(uv_stream_t* stream, int* fd) { int uv__stream_open(uv_stream_t* stream, int fd, int flags) { +#if defined(__APPLE__) + int enable; +#endif + assert(fd >= 0); stream->flags |= flags; @@ -415,6 +419,15 @@ int uv__stream_open(uv_stream_t* stream, int fd, int flags) { return uv__set_sys_error(stream->loop, errno); } +#if defined(__APPLE__) + enable = 1; + if (setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &enable, sizeof(enable)) && + errno != ENOTSOCK && + errno != EINVAL) { + return uv__set_sys_error(stream->loop, errno); + } +#endif + stream->io_watcher.fd = fd; return 0; diff --git a/deps/uv/src/version.c b/deps/uv/src/version.c index 1b5a1e4cdbb625..55408660640be9 100644 --- a/deps/uv/src/version.c +++ b/deps/uv/src/version.c @@ -34,7 +34,7 @@ #define UV_VERSION_MAJOR 0 #define UV_VERSION_MINOR 10 -#define UV_VERSION_PATCH 30 +#define UV_VERSION_PATCH 36 #define UV_VERSION_IS_RELEASE 1 diff --git a/deps/uv/src/win/dl.c b/deps/uv/src/win/dl.c index d5b8f7c7d31831..f7fca81ca26f4e 100644 --- a/deps/uv/src/win/dl.c +++ b/deps/uv/src/win/dl.c @@ -69,17 +69,44 @@ const char* uv_dlerror(uv_lib_t* lib) { } +static void uv__format_fallback_error(uv_lib_t* lib, int errorno){ + DWORD_PTR args[1] = { (DWORD_PTR) errorno }; + LPSTR fallback_error = "error: %1!d!"; + + FormatMessageA(FORMAT_MESSAGE_FROM_STRING | + FORMAT_MESSAGE_ARGUMENT_ARRAY | + FORMAT_MESSAGE_ALLOCATE_BUFFER, + fallback_error, 0, 0, + (LPSTR) &lib->errmsg, + 0, (va_list*) args); +} + + + static int uv__dlerror(uv_lib_t* lib, int errorno) { + DWORD res; + if (lib->errmsg) { LocalFree((void*)lib->errmsg); lib->errmsg = NULL; } if (errorno) { - FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, - MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), - (LPSTR)&lib->errmsg, 0, NULL); + res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (LPSTR) &lib->errmsg, 0, NULL); + if (!res && GetLastError() == ERROR_MUI_FILE_NOT_FOUND) { + res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, + 0, (LPSTR) &lib->errmsg, 0, NULL); + } + + if (!res) { + uv__format_fallback_error(lib, errorno); + } } return errorno ? -1 : 0; diff --git a/deps/uv/src/win/fs.c b/deps/uv/src/win/fs.c index 7f3704e8aad9ca..c5a4b2b70343b8 100644 --- a/deps/uv/src/win/fs.c +++ b/deps/uv/src/win/fs.c @@ -732,9 +732,9 @@ void fs__readdir(uv_fs_t* req) { if (len == 0) { fmt = L"./*"; } else if (pathw[len - 1] == L'/' || pathw[len - 1] == L'\\') { - fmt = L"%s*"; + fmt = L"%ls*"; } else { - fmt = L"%s\\*"; + fmt = L"%ls\\*"; } /* Figure out whether path is a file or a directory. */ diff --git a/deps/uv/src/win/internal.h b/deps/uv/src/win/internal.h index 9920f704ab0963..f32d24a7789035 100644 --- a/deps/uv/src/win/internal.h +++ b/deps/uv/src/win/internal.h @@ -332,8 +332,8 @@ int WSAAPI uv_wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers, int* addr_len, WSAOVERLAPPED *overlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine); -int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info, - OVERLAPPED* overlapped); +int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in, + AFD_POLL_INFO* info_out, OVERLAPPED* overlapped); /* Whether there are any non-IFS LSPs stacked on TCP */ extern int uv_tcp_non_ifs_lsp_ipv4; diff --git a/deps/uv/src/win/poll.c b/deps/uv/src/win/poll.c index 5c5c7d80f0d9f2..372427927870fa 100644 --- a/deps/uv/src/win/poll.c +++ b/deps/uv/src/win/poll.c @@ -46,6 +46,8 @@ typedef struct uv_single_fd_set_s { static OVERLAPPED overlapped_dummy_; static uv_once_t overlapped_dummy_init_guard_ = UV_ONCE_INIT; +static AFD_POLL_INFO afd_poll_info_dummy_; + static void uv__init_overlapped_dummy(void) { HANDLE event; @@ -65,6 +67,11 @@ static OVERLAPPED* uv__get_overlapped_dummy() { } +static AFD_POLL_INFO* uv__get_afd_poll_info_dummy() { + return &afd_poll_info_dummy_; +} + + static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) { uv_req_t* req; AFD_POLL_INFO* afd_poll_info; @@ -79,7 +86,7 @@ static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) { handle->mask_events_2 = handle->events; } else if (handle->submitted_events_2 == 0) { req = &handle->poll_req_2; - afd_poll_info = &handle->afd_poll_info_2.afd_poll_info_ptr[0]; + afd_poll_info = &handle->afd_poll_info_2; handle->submitted_events_2 = handle->events; handle->mask_events_1 = handle->events; handle->mask_events_2 = 0; @@ -107,6 +114,7 @@ static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) { memset(&req->overlapped, 0, sizeof req->overlapped); result = uv_msafd_poll((SOCKET) handle->peer_socket, + afd_poll_info, afd_poll_info, &req->overlapped); if (result != 0 && WSAGetLastError() != WSA_IO_PENDING) { @@ -118,19 +126,19 @@ static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) { static int uv__fast_poll_cancel_poll_req(uv_loop_t* loop, uv_poll_t* handle) { - AFD_POLL_INFO* afd_poll_info; + AFD_POLL_INFO afd_poll_info; DWORD result; - afd_poll_info = &handle->afd_poll_info_2.afd_poll_info_ptr[1]; - afd_poll_info->Exclusive = TRUE; - afd_poll_info->NumberOfHandles = 1; - afd_poll_info->Timeout.QuadPart = INT64_MAX; - afd_poll_info->Handles[0].Handle = (HANDLE) handle->socket; - afd_poll_info->Handles[0].Status = 0; - afd_poll_info->Handles[0].Events = AFD_POLL_ALL; + afd_poll_info.Exclusive = TRUE; + afd_poll_info.NumberOfHandles = 1; + afd_poll_info.Timeout.QuadPart = INT64_MAX; + afd_poll_info.Handles[0].Handle = (HANDLE) handle->socket; + afd_poll_info.Handles[0].Status = 0; + afd_poll_info.Handles[0].Events = AFD_POLL_ALL; result = uv_msafd_poll(handle->socket, - afd_poll_info, + &afd_poll_info, + uv__get_afd_poll_info_dummy(), uv__get_overlapped_dummy()); if (result == SOCKET_ERROR) { @@ -155,7 +163,7 @@ static void uv__fast_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, handle->submitted_events_1 = 0; mask_events = handle->mask_events_1; } else if (req == &handle->poll_req_2) { - afd_poll_info = &handle->afd_poll_info_2.afd_poll_info_ptr[0]; + afd_poll_info = &handle->afd_poll_info_2; handle->submitted_events_2 = 0; mask_events = handle->mask_events_2; } else { @@ -552,12 +560,6 @@ int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle, handle->poll_req_2.type = UV_POLL_REQ; handle->poll_req_2.data = handle; - handle->afd_poll_info_2.afd_poll_info_ptr = malloc(sizeof(*handle->afd_poll_info_2.afd_poll_info_ptr) * 2); - if (handle->afd_poll_info_2.afd_poll_info_ptr == NULL) { - uv__set_artificial_error(loop, UV_ENOMEM); - return -1; - } - return 0; } @@ -611,9 +613,5 @@ void uv_poll_endgame(uv_loop_t* loop, uv_poll_t* handle) { assert(handle->submitted_events_1 == 0); assert(handle->submitted_events_2 == 0); - if (handle->afd_poll_info_2.afd_poll_info_ptr) { - free(handle->afd_poll_info_2.afd_poll_info_ptr); - handle->afd_poll_info_2.afd_poll_info_ptr = NULL; - } uv__handle_close(handle); } diff --git a/deps/uv/src/win/winsock.c b/deps/uv/src/win/winsock.c index cf6d0318918547..b91a237338b595 100644 --- a/deps/uv/src/win/winsock.c +++ b/deps/uv/src/win/winsock.c @@ -467,8 +467,8 @@ int WSAAPI uv_wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers, } -int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info, - OVERLAPPED* overlapped) { +int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in, + AFD_POLL_INFO* info_out, OVERLAPPED* overlapped) { IO_STATUS_BLOCK iosb; IO_STATUS_BLOCK* iosb_ptr; HANDLE event = NULL; @@ -506,10 +506,10 @@ int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info, apc_context, iosb_ptr, IOCTL_AFD_POLL, - info, - sizeof *info, - info, - sizeof *info); + info_in, + sizeof *info_in, + info_out, + sizeof *info_out); if (overlapped == NULL) { /* If this is a blocking operation, wait for the event to become */ diff --git a/deps/uv/test/test-dlerror.c b/deps/uv/test/test-dlerror.c index 877ebf3712a213..091200edbed591 100644 --- a/deps/uv/test/test-dlerror.c +++ b/deps/uv/test/test-dlerror.c @@ -26,31 +26,28 @@ TEST_IMPL(dlerror) { const char* path = "test/fixtures/load_error.node"; + const char* dlerror_no_error = "no error"; const char* msg; uv_lib_t lib; int r; -#ifdef __linux__ - const char* dlerror_desc = "file too short"; -#elif defined (__sun__) - const char* dlerror_desc = "unknown file type"; -#elif defined (_WIN32) - const char* dlerror_desc = "%1 is not a valid Win32 application"; -#else - const char* dlerror_desc = ""; -#endif + lib.errmsg = NULL; + lib.handle = NULL; + msg = uv_dlerror(&lib); + ASSERT(msg != NULL); + ASSERT(strstr(msg, dlerror_no_error) != NULL); r = uv_dlopen(path, &lib); ASSERT(r == -1); msg = uv_dlerror(&lib); ASSERT(msg != NULL); - ASSERT(strstr(msg, dlerror_desc) != NULL); + ASSERT(strstr(msg, dlerror_no_error) == NULL); /* Should return the same error twice in a row. */ msg = uv_dlerror(&lib); ASSERT(msg != NULL); - ASSERT(strstr(msg, dlerror_desc) != NULL); + ASSERT(strstr(msg, dlerror_no_error) == NULL); uv_dlclose(&lib); diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index 47444258821237..0bfb9e41e0d49e 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -24,6 +24,7 @@ TEST_DECLARE (callback_order) TEST_DECLARE (run_once) TEST_DECLARE (run_nowait) TEST_DECLARE (loop_stop) +TEST_DECLARE (loop_configure) TEST_DECLARE (barrier_1) TEST_DECLARE (barrier_2) TEST_DECLARE (barrier_3) @@ -64,7 +65,10 @@ TEST_DECLARE (tcp_connect_error_fault) TEST_DECLARE (tcp_connect_timeout) TEST_DECLARE (tcp_close_while_connecting) TEST_DECLARE (tcp_close) +#ifndef _WIN32 TEST_DECLARE (tcp_close_accept) +TEST_DECLARE (tcp_oob) +#endif TEST_DECLARE (tcp_flags) TEST_DECLARE (tcp_write_to_half_open_connection) TEST_DECLARE (tcp_unexpected_read) @@ -215,6 +219,7 @@ TEST_DECLARE (poll_duplex) TEST_DECLARE (poll_unidirectional) TEST_DECLARE (poll_close) #ifdef _WIN32 +TEST_DECLARE (poll_close_doesnt_corrupt_stack) TEST_DECLARE (poll_closesocket) TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows) TEST_DECLARE (argument_escaping) @@ -249,6 +254,7 @@ TASK_LIST_START TEST_ENTRY (run_once) TEST_ENTRY (run_nowait) TEST_ENTRY (loop_stop) + TEST_ENTRY (loop_configure) TEST_ENTRY (barrier_1) TEST_ENTRY (barrier_2) TEST_ENTRY (barrier_3) @@ -308,7 +314,10 @@ TASK_LIST_START TEST_ENTRY (tcp_connect_timeout) TEST_ENTRY (tcp_close_while_connecting) TEST_ENTRY (tcp_close) +#ifndef _WIN32 TEST_ENTRY (tcp_close_accept) + TEST_ENTRY (tcp_oob) +#endif TEST_ENTRY (tcp_flags) TEST_ENTRY (tcp_write_to_half_open_connection) TEST_ENTRY (tcp_unexpected_read) @@ -451,6 +460,7 @@ TASK_LIST_START TEST_ENTRY (kill) #ifdef _WIN32 + TEST_ENTRY (poll_close_doesnt_corrupt_stack) TEST_ENTRY (poll_closesocket) TEST_ENTRY (spawn_detect_pipe_name_collisions_on_windows) TEST_ENTRY (argument_escaping) diff --git a/deps/uv/test/test-loop-configure.c b/deps/uv/test/test-loop-configure.c new file mode 100644 index 00000000000000..9fefc5920195ec --- /dev/null +++ b/deps/uv/test/test-loop-configure.c @@ -0,0 +1,40 @@ +/* Copyright (c) 2014, Ben Noordhuis + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +static void timer_cb(uv_timer_t* handle, int status) { + uv_close((uv_handle_t*) handle, NULL); +} + + +TEST_IMPL(loop_configure) { + uv_timer_t timer_handle; + uv_loop_t* loop; + + loop = uv_default_loop(); +#ifdef _WIN32 + ASSERT(UV_ENOSYS == uv_loop_configure(loop, UV_LOOP_BLOCK_SIGNAL, 0)); +#else + ASSERT(0 == uv_loop_configure(loop, UV_LOOP_BLOCK_SIGNAL, SIGPROF)); +#endif + ASSERT(0 == uv_timer_init(loop, &timer_handle)); + ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 10, 0)); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/deps/uv/test/test-poll-close-doesnt-corrupt-stack.c b/deps/uv/test/test-poll-close-doesnt-corrupt-stack.c new file mode 100644 index 00000000000000..eb10507ac1383d --- /dev/null +++ b/deps/uv/test/test-poll-close-doesnt-corrupt-stack.c @@ -0,0 +1,114 @@ +/* Copyright Bert Belder, and other 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. + */ + +#ifdef _WIN32 + +#include +#include + +#include "uv.h" +#include "task.h" + +#ifdef _MSC_VER /* msvc */ +# define NO_INLINE __declspec(noinline) +#else /* gcc */ +# define NO_INLINE __attribute__ ((noinline)) +#endif + + +uv_os_sock_t sock; +uv_poll_t handle; + +static int close_cb_called = 0; + + +static void close_cb(uv_handle_t* h) { + close_cb_called++; +} + + +static void poll_cb(uv_poll_t* h, int status, int events) { + ASSERT(0 && "should never get here"); +} + + +static void NO_INLINE close_socket_and_verify_stack() { + const uint32_t MARKER = 0xDEADBEEF; + const int VERIFY_AFTER = 10; /* ms */ + int r; + + volatile uint32_t data[65536]; + size_t i; + + for (i = 0; i < ARRAY_SIZE(data); i++) + data[i] = MARKER; + + r = closesocket(sock); + ASSERT(r == 0); + + uv_sleep(VERIFY_AFTER); + + for (i = 0; i < ARRAY_SIZE(data); i++) + ASSERT(data[i] == MARKER); +} + + +TEST_IMPL(poll_close_doesnt_corrupt_stack) { + struct WSAData wsa_data; + int r; + unsigned long on; + struct sockaddr_in addr; + + r = WSAStartup(MAKEWORD(2, 2), &wsa_data); + ASSERT(r == 0); + + sock = socket(AF_INET, SOCK_STREAM, 0); + ASSERT(sock != INVALID_SOCKET); + on = 1; + r = ioctlsocket(sock, FIONBIO, &on); + ASSERT(r == 0); + + addr = uv_ip4_addr("127.0.0.1", TEST_PORT); + ASSERT(r == 0); + + r = connect(sock, (const struct sockaddr*) &addr, sizeof addr); + ASSERT(r != 0); + ASSERT(WSAGetLastError() == WSAEWOULDBLOCK); + + r = uv_poll_init_socket(uv_default_loop(), &handle, sock); + ASSERT(r == 0); + r = uv_poll_start(&handle, UV_READABLE | UV_WRITABLE, poll_cb); + ASSERT(r == 0); + + uv_close((uv_handle_t*) &handle, close_cb); + + close_socket_and_verify_stack(); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(close_cb_called == 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +#endif /* _WIN32 */ diff --git a/deps/uv/test/test-tcp-oob.c b/deps/uv/test/test-tcp-oob.c new file mode 100644 index 00000000000000..35a242041665ec --- /dev/null +++ b/deps/uv/test/test-tcp-oob.c @@ -0,0 +1,137 @@ +/* Copyright Fedor Indutny. 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. + */ + +#if !defined(_WIN32) + +#include "uv.h" +#include "task.h" + +#include +#include +#include + +static uv_tcp_t server_handle; +static uv_tcp_t client_handle; +static uv_tcp_t peer_handle; +static uv_idle_t idle; +static uv_connect_t connect_req; +static int ticks; +static const int kMaxTicks = 10; + + +static void set_nonblocking(int fd, int set) { + int r; + int flags = fcntl(fd, F_GETFL, 0); + ASSERT(flags >= 0); + if (set) + flags |= O_NONBLOCK; + else + flags &= ~O_NONBLOCK; + r = fcntl(fd, F_SETFL, flags); + ASSERT(r >= 0); +} + + +static uv_buf_t alloc_cb(uv_handle_t* handle, size_t size) { + static char storage[1024]; + return uv_buf_init(storage, sizeof(storage)); +} + + +static void idle_cb(uv_idle_t* idle, int status) { + ASSERT(status == 0); + if (++ticks < kMaxTicks) + return; + + uv_close((uv_handle_t*) &server_handle, NULL); + uv_close((uv_handle_t*) &client_handle, NULL); + uv_close((uv_handle_t*) &peer_handle, NULL); + uv_close((uv_handle_t*) idle, NULL); +} + + +static void read_cb(uv_stream_t* tcp, ssize_t nread, uv_buf_t buf) { + ASSERT(nread > 0); + ASSERT(0 == uv_idle_start(&idle, idle_cb)); +} + + +static void connect_cb(uv_connect_t* req, int status) { + ASSERT(req->handle == (uv_stream_t*) &client_handle); + ASSERT(0 == status); +} + + +static void connection_cb(uv_stream_t* handle, int status) { + int r; + int fd; + + ASSERT(0 == status); + ASSERT(0 == uv_accept(handle, (uv_stream_t*) &peer_handle)); + ASSERT(0 == uv_read_start((uv_stream_t*) &peer_handle, alloc_cb, read_cb)); + + /* Send some OOB data */ + fd = client_handle.io_watcher.fd; + set_nonblocking(fd, 0); + + /* The problem triggers only on a second message, it seem that xnu is not + * triggering `kevent()` for the first one + */ + do { + r = send(fd, "hello", 5, MSG_OOB); + } while (r < 0 && errno == EINTR); + ASSERT(5 == r); + + do { + r = send(fd, "hello", 5, MSG_OOB); + } while (r < 0 && errno == EINTR); + ASSERT(5 == r); + + set_nonblocking(fd, 1); +} + + +TEST_IMPL(tcp_oob) { + uv_loop_t* loop; + loop = uv_default_loop(); + + ASSERT(0 == uv_tcp_init(loop, &server_handle)); + ASSERT(0 == uv_tcp_init(loop, &client_handle)); + ASSERT(0 == uv_tcp_init(loop, &peer_handle)); + ASSERT(0 == uv_idle_init(loop, &idle)); + ASSERT(0 == uv_tcp_bind(&server_handle, uv_ip4_addr("127.0.0.1", TEST_PORT))); + ASSERT(0 == uv_listen((uv_stream_t*) &server_handle, 1, connection_cb)); + + /* Ensure two separate packets */ + ASSERT(0 == uv_tcp_nodelay(&client_handle, 1)); + + ASSERT(0 == uv_tcp_connect(&connect_req, + &client_handle, + uv_ip4_addr("127.0.0.1", TEST_PORT), + connect_cb)); + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + + ASSERT(ticks == kMaxTicks); + + MAKE_VALGRIND_HAPPY(); + return 0; +} +#endif diff --git a/deps/uv/uv.gyp b/deps/uv/uv.gyp index 5af99bd59cd23d..d5d0f940783544 100644 --- a/deps/uv/uv.gyp +++ b/deps/uv/uv.gyp @@ -133,6 +133,7 @@ 'include/uv-private/uv-darwin.h', 'include/uv-private/uv-bsd.h', 'src/unix/async.c', + 'src/unix/atomic-ops.h', 'src/unix/core.c', 'src/unix/dl.c', 'src/unix/error.c', @@ -311,6 +312,7 @@ 'test/test-list.h', 'test/test-loop-handles.c', 'test/test-loop-stop.c', + 'test/test-loop-configure.c', 'test/test-walk-handles.c', 'test/test-watcher-cross-stop.c', 'test/test-multiple-listen.c', @@ -323,6 +325,7 @@ 'test/test-platform-output.c', 'test/test-poll.c', 'test/test-poll-close.c', + 'test/test-poll-close-doesnt-corrupt-stack.c', 'test/test-poll-closesocket.c', 'test/test-process-title.c', 'test/test-ref.c', @@ -352,6 +355,7 @@ 'test/test-tcp-write-to-half-open-connection.c', 'test/test-tcp-writealot.c', 'test/test-tcp-unexpected-read.c', + 'test/test-tcp-oob.c', 'test/test-tcp-read-stop.c', 'test/test-threadpool.c', 'test/test-threadpool-cancel.c',