From fadb1d93ce823c8e9bd95133a370fb32db8cf029 Mon Sep 17 00:00:00 2001 From: cjihrig Date: Mon, 24 Aug 2020 20:34:47 -0400 Subject: [PATCH] deps: upgrade to libuv 1.39.0 Notable changes: - uv_metrics_idle_time() and UV_METRICS_IDLE_TIME have been added for measuring the amount of time the event loop spends idle. - uv_udp_using_recvmmsg() has been added to determine if a buffer is large enough for multiple datagrams should be allocated in the allocation callback of uv_udp_recvstart(). - On MinGW, the installation location has been updated to match Unix systems rather than Windows. - uv_fs_copyfile() now tries to use copy_file_range() when possible. - The test suite is now reported to pass on Darwin ARM64 (Apple Silicon). - uv_{get,set}_process_title() now returns an error on platforms where uv_setup_args() is required, but has not yet been called. - The _POSIX_PATH_MAX constant is no longer used, which could lead to buffer overflows in uv_fs_readlink() and uv_fs_realpath(). PR-URL: https://github.com/nodejs/node/pull/34915 Reviewed-By: Jiawen Geng Reviewed-By: James M Snell Reviewed-By: Gireesh Punathil Reviewed-By: Rich Trott Reviewed-By: Trivikram Kamat Reviewed-By: David Carlier Reviewed-By: Ben Noordhuis Reviewed-By: Richard Lau Reviewed-By: Beth Griggs --- deps/uv/.mailmap | 3 + deps/uv/AUTHORS | 8 + deps/uv/CMakeLists.txt | 15 +- deps/uv/ChangeLog | 76 ++++++++++ deps/uv/Makefile.am | 7 +- deps/uv/configure.ac | 2 +- deps/uv/docs/src/api.rst | 1 + deps/uv/docs/src/async.rst | 2 +- deps/uv/docs/src/errors.rst | 14 +- deps/uv/docs/src/fs.rst | 8 +- deps/uv/docs/src/handle.rst | 4 +- deps/uv/docs/src/loop.rst | 7 +- deps/uv/docs/src/metrics.rst | 25 ++++ deps/uv/docs/src/misc.rst | 42 ++++-- deps/uv/docs/src/process.rst | 33 ++--- deps/uv/docs/src/request.rst | 2 +- deps/uv/docs/src/sphinx-plugins/manpage.py | 2 +- deps/uv/docs/src/tty.rst | 5 +- deps/uv/docs/src/udp.rst | 12 +- deps/uv/include/uv.h | 21 ++- deps/uv/include/uv/version.h | 4 +- deps/uv/src/strscpy.c | 21 +++ deps/uv/src/strscpy.h | 21 +++ deps/uv/src/unix/aix-common.c | 110 +++----------- deps/uv/src/unix/aix.c | 70 ++++++++- deps/uv/src/unix/core.c | 83 +++++++++++ deps/uv/src/unix/darwin-stub.h | 16 ++ deps/uv/src/unix/darwin.c | 145 ++++++++++++++++++- deps/uv/src/unix/fs.c | 57 ++++++-- deps/uv/src/unix/ibmi.c | 40 +++++ deps/uv/src/unix/internal.h | 1 + deps/uv/src/unix/kqueue.c | 46 +++++- deps/uv/src/unix/linux-core.c | 68 +++++++-- deps/uv/src/unix/linux-syscalls.c | 40 +++++ deps/uv/src/unix/linux-syscalls.h | 7 + deps/uv/src/unix/loop.c | 34 ++++- deps/uv/src/unix/os390.c | 110 ++++++-------- deps/uv/src/unix/posix-poll.c | 40 ++++- deps/uv/src/unix/proctitle.c | 8 + deps/uv/src/unix/signal.c | 2 + deps/uv/src/unix/sunos.c | 37 ++++- deps/uv/src/unix/udp.c | 24 ++- deps/uv/src/uv-common.c | 59 ++++++++ deps/uv/src/uv-common.h | 23 +++ deps/uv/src/uv-data-getter-setters.c | 21 +++ deps/uv/src/win/core.c | 91 ++++++++++++ deps/uv/src/win/detect-wakeup.c | 21 +++ deps/uv/src/win/fs-fd-hash-inl.h | 24 ++- deps/uv/src/win/fs.c | 93 ++++++------ deps/uv/src/win/pipe.c | 12 +- deps/uv/src/win/tcp.c | 62 +++++++- deps/uv/src/win/tty.c | 1 + deps/uv/src/win/udp.c | 5 + deps/uv/src/win/util.c | 8 +- deps/uv/src/win/winapi.h | 12 ++ deps/uv/test/task.h | 44 ++++-- deps/uv/test/test-close-fd.c | 4 +- deps/uv/test/test-fs-copyfile.c | 5 + deps/uv/test/test-fs-open-flags.c | 6 +- deps/uv/test/test-fs.c | 15 +- deps/uv/test/test-get-currentexe.c | 14 ++ deps/uv/test/test-getaddrinfo.c | 16 ++ deps/uv/test/test-getnameinfo.c | 10 ++ deps/uv/test/test-getters-setters.c | 21 +++ deps/uv/test/test-list.h | 21 ++- deps/uv/test/test-metrics.c | 135 +++++++++++++++++ deps/uv/test/test-ping-pong.c | 20 +-- deps/uv/test/test-pipe-set-non-blocking.c | 34 +++-- deps/uv/test/test-process-title-threadsafe.c | 15 +- deps/uv/test/test-spawn.c | 72 ++++----- deps/uv/test/test-tcp-connect-timeout.c | 105 ++++++++++++++ deps/uv/test/test-tcp-read-stop-start.c | 136 +++++++++++++++++ deps/uv/test/test-test-macros.c | 42 ++++++ deps/uv/test/test-thread.c | 5 + deps/uv/test/test-tty.c | 5 + deps/uv/test/test-udp-mmsg.c | 136 +++++++++++++++++ 76 files changed, 2152 insertions(+), 414 deletions(-) create mode 100644 deps/uv/docs/src/metrics.rst create mode 100644 deps/uv/test/test-metrics.c create mode 100644 deps/uv/test/test-tcp-read-stop-start.c create mode 100644 deps/uv/test/test-test-macros.c create mode 100644 deps/uv/test/test-udp-mmsg.c diff --git a/deps/uv/.mailmap b/deps/uv/.mailmap index 2ae2968c83208d..56a80f586b3c17 100644 --- a/deps/uv/.mailmap +++ b/deps/uv/.mailmap @@ -27,6 +27,7 @@ Maciej Małecki Marc Schlaich Michael Michael Neumann +Michael Penick Nicholas Vavilov Nick Logan Rasmus Christian Pedersen @@ -41,10 +42,12 @@ Santiago Gimeno Saúl Ibarra Corretgé Saúl Ibarra Corretgé Shigeki Ohtsu +TK-one Timothy J. Fontaine Yasuhiro Matsumoto Yazhong Liu Yuki Okumura +gengjiawen jBarz jBarz ptlomholt diff --git a/deps/uv/AUTHORS b/deps/uv/AUTHORS index 38fee1f4498284..9078925bb07669 100644 --- a/deps/uv/AUTHORS +++ b/deps/uv/AUTHORS @@ -435,3 +435,11 @@ MasterDuke17 Alexander Tokmakov Arenoros lander0s +Turbinya +OleksandrKvl +Carter Li +Juan Sebastian velez Posada +escherstair +Evan Lucas +tjarlama <59913901+tjarlama@users.noreply.github.com> +司徒玟琅 diff --git a/deps/uv/CMakeLists.txt b/deps/uv/CMakeLists.txt index 2518c74748be5b..e9bf77f7c36de0 100644 --- a/deps/uv/CMakeLists.txt +++ b/deps/uv/CMakeLists.txt @@ -56,6 +56,8 @@ check_c_compiler_flag(-Wno-unused-parameter UV_LINT_NO_UNUSED_PARAMETER) check_c_compiler_flag(-Wstrict-prototypes UV_LINT_STRICT_PROTOTYPES) check_c_compiler_flag(-Wextra UV_LINT_EXTRA) +check_c_compiler_flag(/utf-8 UV_LINT_UTF8_MSVC) + set(lint-no-unused-parameter $<$:-Wno-unused-parameter>) set(lint-strict-prototypes $<$:-Wstrict-prototypes>) set(lint-extra $<$:-Wextra>) @@ -76,6 +78,7 @@ set(lint-no-unsafe-msvc $<$:/wd4996>) string(CONCAT lint-default $< $,$>:-Wall >) +set(lint-utf8-msvc $<$:/utf-8>) list(APPEND uv_cflags ${lint-strict-prototypes} ${lint-extra} ${lint-default} ${lint-w4}) list(APPEND uv_cflags ${lint-no-unused-parameter}) @@ -90,6 +93,7 @@ list(APPEND uv_cflags ${lint-no-hides-param-msvc}) list(APPEND uv_cflags ${lint-no-hides-global-msvc}) list(APPEND uv_cflags ${lint-no-conditional-assignment-msvc}) list(APPEND uv_cflags ${lint-no-unsafe-msvc}) +list(APPEND uv_cflags ${lint-utf8-msvc} ) set(uv_sources src/fs-poll.c @@ -107,6 +111,8 @@ if(WIN32) list(APPEND uv_defines WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0600) list(APPEND uv_libraries psapi + user32 + advapi32 iphlpapi userenv ws2_32) @@ -283,7 +289,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL "OS400") src/unix/aix-common.c src/unix/ibmi.c src/unix/no-fsevents.c - src/unix/no-proctitle.c src/unix/posix-poll.c) endif() @@ -416,6 +421,7 @@ if(LIBUV_BUILD_TESTS) test/test-loop-handles.c test/test-loop-stop.c test/test-loop-time.c + test/test-metrics.c test/test-multiple-listen.c test/test-mutexes.c test/test-osx-select.c @@ -473,6 +479,7 @@ if(LIBUV_BUILD_TESTS) test/test-tcp-oob.c test/test-tcp-open.c test/test-tcp-read-stop.c + test/test-tcp-read-stop-start.c test/test-tcp-shutdown-after-write.c test/test-tcp-try-write.c test/test-tcp-try-write-error.c @@ -482,6 +489,7 @@ if(LIBUV_BUILD_TESTS) test/test-tcp-write-queue-order.c test/test-tcp-write-to-half-open-connection.c test/test-tcp-writealot.c + test/test-test-macros.c test/test-thread-equal.c test/test-thread.c test/test-threadpool-cancel.c @@ -499,6 +507,7 @@ if(LIBUV_BUILD_TESTS) test/test-udp-create-socket-early.c test/test-udp-dgram-too-big.c test/test-udp-ipv6.c + test/test-udp-mmsg.c test/test-udp-multicast-interface.c test/test-udp-multicast-interface6.c test/test-udp-multicast-join.c @@ -541,7 +550,7 @@ if(LIBUV_BUILD_TESTS) WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) endif() -if(UNIX) +if(UNIX OR MINGW) # Now for some gibbering horrors from beyond the stars... foreach(lib IN LISTS uv_libraries) list(APPEND LIBS "-l${lib}") @@ -568,7 +577,7 @@ if(UNIX) install(TARGETS uv_a ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() -if(WIN32) +if(MSVC) install(DIRECTORY include/ DESTINATION include) install(FILES LICENSE DESTINATION .) install(TARGETS uv uv_a diff --git a/deps/uv/ChangeLog b/deps/uv/ChangeLog index 7da3f9902efbf9..06509e7d15857d 100644 --- a/deps/uv/ChangeLog +++ b/deps/uv/ChangeLog @@ -1,3 +1,79 @@ +2020.08.26, Version 1.39.0 (Stable), 25f4b8b8a3c0f934158cd37a37b0525d75ca488e + +Changes since version 1.38.1: + +* unix: use relaxed loads/stores for clock id (Ben Noordhuis) + +* build,win: link to user32.lib and advapi32.lib (George Zhao) + +* unix: squelch harmless valgrind warning (ssrlive) + +* include: fx c++ style comments warnings (Turbinya) + +* build,cmake: Change installation location on MinGW (erw7) + +* linux: use copy_file_range for uv_fs_copyfile when possible (Carter Li) + +* win,tcp: avoid reinserting a pending request ( + +* docs: improve the descriptions for get memory info (Juan Sebastian velez + Posada) + +* test: add udp-mmsg test (Ryan Liptak) + +* udp: add uv_udp_using_recvmmsg query (Ryan Liptak) + +* doc: add more error constants (TK-one) + +* zos: fix potential event loop stall (Trevor Norris) + +* include: add internal fields struct to uv_loop_t (Trevor Norris) + +* core: add API to measure event loop idle time (Trevor Norris) + +* win,fs: use CreateDirectoryW instead of _wmkdir (Mustafa M) + +* win,nfc: fix integer comparison signedness (escherstair) + +* win,nfc: use + +* win,nfc: removed some unused variables (escherstair) + +* win,nfc: add missing return statement (escherstair) + +* win,nfc: disable clang-format for + +* darwin: use IOKit for uv_cpu_info (Evan Lucas) + +* test: fix thread race in process_title_threadsafe (Ben Noordhuis) + +* win,fs: avoid implicit access to _doserrno (Jameson Nash) + +* test: give hrtime test a custom 20s timeout (Jameson Nash) + +* build: add more failed test, for qemu version bump (gengjiawen) + +* unix: handle src, dest same in uv_fs_copyfile() (cjihrig) + +* unix: error when uv_setup_args() is not called (Ryan Liptak) + +* aix: protect uv_exepath() from uv_set_process_title() (Richard Lau) + +* fs: clobber req->path on uv_fs_mkstemp() error (tjarlama) + +* cmake: fix compile error C2001 on Chinese Windows (司徒玟琅) + +* test: avoid double evaluation in ASSERT_BASE macro (tjarlama) + +* tcp: fail instantly if local port is unbound (Bartosz Sosnowski) + +* doc: fix most sphinx warnings (Jameson Nash) + +* nfci: address some style nits (Jameson Nash) + +* unix: don't use _POSIX_PATH_MAX (Ben Noordhuis) + + 2020.07.04, Version 1.38.1 (Stable), e8b989ea1f7f9d4083511a2caec7791e9abd1871 Changes since version 1.38.0: diff --git a/deps/uv/Makefile.am b/deps/uv/Makefile.am index 13f27bc4c443b4..46308eaae28ee4 100644 --- a/deps/uv/Makefile.am +++ b/deps/uv/Makefile.am @@ -203,6 +203,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-loop-stop.c \ test/test-loop-time.c \ test/test-loop-configure.c \ + test/test-metrics.c \ test/test-multiple-listen.c \ test/test-mutexes.c \ test/test-osx-select.c \ @@ -259,6 +260,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-tcp-flags.c \ test/test-tcp-open.c \ test/test-tcp-read-stop.c \ + test/test-tcp-read-stop-start.c \ test/test-tcp-shutdown-after-write.c \ test/test-tcp-unexpected-read.c \ test/test-tcp-oob.c \ @@ -269,6 +271,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-tcp-try-write.c \ test/test-tcp-try-write-error.c \ test/test-tcp-write-queue-order.c \ + test/test-test-macros.c \ test/test-thread-equal.c \ test/test-thread.c \ test/test-threadpool-cancel.c \ @@ -286,6 +289,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-udp-create-socket-early.c \ test/test-udp-dgram-too-big.c \ test/test-udp-ipv6.c \ + test/test-udp-mmsg.c \ test/test-udp-multicast-interface.c \ test/test-udp-multicast-interface6.c \ test/test-udp-multicast-join.c \ @@ -374,8 +378,7 @@ uvinclude_HEADERS += include/uv/posix.h libuv_la_SOURCES += src/unix/aix-common.c \ src/unix/ibmi.c \ src/unix/posix-poll.c \ - src/unix/no-fsevents.c \ - src/unix/no-proctitle.c + src/unix/no-fsevents.c endif if ANDROID diff --git a/deps/uv/configure.ac b/deps/uv/configure.ac index b39ba69e46ec89..8f5c89b1a99ffb 100644 --- a/deps/uv/configure.ac +++ b/deps/uv/configure.ac @@ -13,7 +13,7 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. AC_PREREQ(2.57) -AC_INIT([libuv], [1.38.1], [https://github.com/libuv/libuv/issues]) +AC_INIT([libuv], [1.39.0], [https://github.com/libuv/libuv/issues]) AC_CONFIG_MACRO_DIR([m4]) m4_include([m4/libuv-extra-automake-flags.m4]) m4_include([m4/as_case.m4]) diff --git a/deps/uv/docs/src/api.rst b/deps/uv/docs/src/api.rst index 22f0640f549ec8..c8e837dd15fdfa 100644 --- a/deps/uv/docs/src/api.rst +++ b/deps/uv/docs/src/api.rst @@ -32,4 +32,5 @@ API documentation dll threading misc + metrics diff --git a/deps/uv/docs/src/async.rst b/deps/uv/docs/src/async.rst index bf611692f460a2..029c051cfcd0ef 100644 --- a/deps/uv/docs/src/async.rst +++ b/deps/uv/docs/src/async.rst @@ -51,7 +51,7 @@ API loop thread. .. note:: - :c:func:`uv_async_send` is `async-signal-safe `_. + :c:func:`uv_async_send` is `async-signal-safe `_. It's safe to call this function from a signal handler. .. warning:: diff --git a/deps/uv/docs/src/errors.rst b/deps/uv/docs/src/errors.rst index b8f971f5763511..c2daa8584eb9c3 100644 --- a/deps/uv/docs/src/errors.rst +++ b/deps/uv/docs/src/errors.rst @@ -319,11 +319,23 @@ Error constants too many links +.. c:macro:: UV_ENOTTY + + inappropriate ioctl for device + +.. c:macro:: UV_EFTYPE + + inappropriate file type or format + +.. c:macro:: UV_EILSEQ + + illegal byte sequence + API --- -.. c:function:: UV_ERRNO_MAP(iter_macro) +.. c:macro:: UV_ERRNO_MAP(iter_macro) Macro that expands to a series of invocations of `iter_macro` for each of the error constants above. `iter_macro` is invoked with two diff --git a/deps/uv/docs/src/fs.rst b/deps/uv/docs/src/fs.rst index 73666f3cd87c29..0bf2abed5e128a 100644 --- a/deps/uv/docs/src/fs.rst +++ b/deps/uv/docs/src/fs.rst @@ -58,7 +58,7 @@ Data types uv_timespec_t st_birthtim; } uv_stat_t; -.. c:type:: uv_fs_type +.. c:enum:: uv_fs_type File system request type. @@ -122,7 +122,7 @@ Data types uint64_t f_spare[4]; } uv_statfs_t; -.. c:type:: uv_dirent_t +.. c:enum:: uv_dirent_t Cross platform (reduced) equivalent of ``struct dirent``. Used in :c:func:`uv_fs_scandir_next`. @@ -535,8 +535,8 @@ Helper functions For a OS-dependent handle, get the file descriptor in the C runtime. On UNIX, returns the ``os_fd`` intact. On Windows, this calls `_open_osfhandle `_. - Note that the return value is still owned by the CRT, - any attempts to close it or to use it after closing the handle may lead to malfunction. + Note that this consumes the argument, any attempts to close it or to use it + after closing the return value may lead to malfunction. .. versionadded:: 1.23.0 diff --git a/deps/uv/docs/src/handle.rst b/deps/uv/docs/src/handle.rst index 943c51d94ba6c4..0edb7d7adf23ed 100644 --- a/deps/uv/docs/src/handle.rst +++ b/deps/uv/docs/src/handle.rst @@ -20,7 +20,7 @@ Data types The base libuv handle type. -.. c:type:: uv_handle_type +.. c:enum:: uv_handle_type The kind of the libuv handle. @@ -104,7 +104,7 @@ Public members API --- -.. c:function:: UV_HANDLE_TYPE_MAP(iter_macro) +.. c:macro:: UV_HANDLE_TYPE_MAP(iter_macro) Macro that expands to a series of invocations of `iter_macro` for each of the handle types. `iter_macro` is invoked with two diff --git a/deps/uv/docs/src/loop.rst b/deps/uv/docs/src/loop.rst index d642ac1d2f6ee8..f9ebb9d4a4f0c2 100644 --- a/deps/uv/docs/src/loop.rst +++ b/deps/uv/docs/src/loop.rst @@ -16,7 +16,7 @@ Data types Loop data type. -.. c:type:: uv_run_mode +.. c:enum:: uv_run_mode Mode used to run the loop with :c:func:`uv_run`. @@ -68,6 +68,11 @@ API to suppress unnecessary wakeups when using a sampling profiler. Requesting other signals will fail with UV_EINVAL. + - UV_METRICS_IDLE_TIME: Accumulate the amount of idle time the event loop + spends in the event provider. + + This option is necessary to use :c:func:`uv_metrics_idle_time`. + .. c:function:: int uv_loop_close(uv_loop_t* loop) Releases all internal loop resources. Call this function only when the loop diff --git a/deps/uv/docs/src/metrics.rst b/deps/uv/docs/src/metrics.rst new file mode 100644 index 00000000000000..223f7feb8fdfee --- /dev/null +++ b/deps/uv/docs/src/metrics.rst @@ -0,0 +1,25 @@ + +.. _metrics: + +Metrics operations +====================== + +libuv provides a metrics API to track the amount of time the event loop has +spent idle in the kernel's event provider. + +API +--- + +.. c:function:: uint64_t uv_metrics_idle_time(uv_loop_t* loop) + + Retrieve the amount of time the event loop has been idle in the kernel's + event provider (e.g. ``epoll_wait``). The call is thread safe. + + The return value is the accumulated time spent idle in the kernel's event + provider starting from when the :c:type:`uv_loop_t` was configured to + collect the idle time. + + .. note:: + The event loop will not begin accumulating the event provider's idle + time until calling :c:type:`uv_loop_configure` with + :c:type:`UV_METRICS_IDLE_TIME`. diff --git a/deps/uv/docs/src/misc.rst b/deps/uv/docs/src/misc.rst index 906ca8ff75d345..b2725c399e431d 100644 --- a/deps/uv/docs/src/misc.rst +++ b/deps/uv/docs/src/misc.rst @@ -261,9 +261,9 @@ API .. c:function:: char** uv_setup_args(int argc, char** argv) - Store the program arguments. Required for getting / setting the process title. - Libuv may take ownership of the memory that `argv` points to. This function - should be called exactly once, at program start-up. + Store the program arguments. Required for getting / setting the process title + or the executable path. Libuv may take ownership of the memory that `argv` + points to. This function should be called exactly once, at program start-up. Example: @@ -275,22 +275,37 @@ API .. c:function:: int uv_get_process_title(char* buffer, size_t size) Gets the title of the current process. You *must* call `uv_setup_args` - before calling this function. If `buffer` is `NULL` or `size` is zero, - `UV_EINVAL` is returned. If `size` cannot accommodate the process title and - terminating `NULL` character, the function returns `UV_ENOBUFS`. + before calling this function on Unix and AIX systems. If `uv_setup_args` + has not been called on systems that require it, then `UV_ENOBUFS` is + returned. If `buffer` is `NULL` or `size` is zero, `UV_EINVAL` is returned. + If `size` cannot accommodate the process title and terminating `nul` + character, the function returns `UV_ENOBUFS`. + + .. note:: + On BSD systems, `uv_setup_args` is needed for getting the initial process + title. The process title returned will be an empty string until either + `uv_setup_args` or `uv_set_process_title` is called. .. versionchanged:: 1.18.1 now thread-safe on all supported platforms. + .. versionchanged:: 1.39.0 now returns an error if `uv_setup_args` is needed + but hasn't been called. + .. c:function:: int uv_set_process_title(const char* title) Sets the current process title. You *must* call `uv_setup_args` before - calling this function. On platforms with a fixed size buffer for the process - title the contents of `title` will be copied to the buffer and truncated if - larger than the available space. Other platforms will return `UV_ENOMEM` if - they cannot allocate enough space to duplicate the contents of `title`. + calling this function on Unix and AIX systems. If `uv_setup_args` has not + been called on systems that require it, then `UV_ENOBUFS` is returned. On + platforms with a fixed size buffer for the process title the contents of + `title` will be copied to the buffer and truncated if larger than the + available space. Other platforms will return `UV_ENOMEM` if they cannot + allocate enough space to duplicate the contents of `title`. .. versionchanged:: 1.18.1 now thread-safe on all supported platforms. + .. versionchanged:: 1.39.0 now returns an error if `uv_setup_args` is needed + but hasn't been called. + .. c:function:: int uv_resident_set_memory(size_t* rss) Gets the resident set size (RSS) for the current process. @@ -425,7 +440,8 @@ API .. c:function:: int uv_exepath(char* buffer, size_t* size) - Gets the executable path. + Gets the executable path. You *must* call `uv_setup_args` before calling + this function. .. c:function:: int uv_cwd(char* buffer, size_t* size) @@ -502,11 +518,11 @@ API .. c:function:: uint64_t uv_get_free_memory(void) - Gets memory information (in bytes). + Gets the amount of free memory available in the system, as reported by the kernel (in bytes). .. c:function:: uint64_t uv_get_total_memory(void) - Gets memory information (in bytes). + Gets the total amount of physical memory in the system (in bytes). .. c:function:: uint64_t uv_get_constrained_memory(void) diff --git a/deps/uv/docs/src/process.rst b/deps/uv/docs/src/process.rst index f2b3be219bf299..8ff19add57849f 100644 --- a/deps/uv/docs/src/process.rst +++ b/deps/uv/docs/src/process.rst @@ -102,7 +102,7 @@ Data types } data; } uv_stdio_container_t; -.. c:type:: uv_stdio_flags +.. c:enum:: uv_stdio_flags Flags specifying how a stdio should be transmitted to the child process. @@ -131,43 +131,43 @@ Data types Public members ^^^^^^^^^^^^^^ -.. c:member:: uv_process_t.pid +.. c:member:: int uv_process_t.pid The PID of the spawned process. It's set after calling :c:func:`uv_spawn`. .. note:: The :c:type:`uv_handle_t` members also apply. -.. c:member:: uv_process_options_t.exit_cb +.. c:member:: uv_exit_cb uv_process_options_t.exit_cb Callback called after the process exits. -.. c:member:: uv_process_options_t.file +.. c:member:: const char* uv_process_options_t.file Path pointing to the program to be executed. -.. c:member:: uv_process_options_t.args +.. c:member:: char** uv_process_options_t.args Command line arguments. args[0] should be the path to the program. On Windows this uses `CreateProcess` which concatenates the arguments into a string this can cause some strange errors. See the ``UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS`` flag on :c:type:`uv_process_flags`. -.. c:member:: uv_process_options_t.env +.. c:member:: char** uv_process_options_t.env Environment for the new process. If NULL the parents environment is used. -.. c:member:: uv_process_options_t.cwd +.. c:member:: const char* uv_process_options_t.cwd Current working directory for the subprocess. -.. c:member:: uv_process_options_t.flags +.. c:member:: unsigned int uv_process_options_t.flags Various flags that control how :c:func:`uv_spawn` behaves. See :c:type:`uv_process_flags`. -.. c:member:: uv_process_options_t.stdio_count -.. c:member:: uv_process_options_t.stdio +.. c:member:: int uv_process_options_t.stdio_count +.. c:member:: uv_stdio_container_t* uv_process_options_t.stdio The `stdio` field points to an array of :c:type:`uv_stdio_container_t` structs that describe the file descriptors that will be made available to @@ -178,8 +178,8 @@ Public members On Windows file descriptors greater than 2 are available to the child process only if the child processes uses the MSVCRT runtime. -.. c:member:: uv_process_options_t.uid -.. c:member:: uv_process_options_t.gid +.. c:member:: uv_uid_t uv_process_options_t.uid +.. c:member:: uv_gid_t uv_process_options_t.gid Libuv can change the child process' user/group id. This happens only when the appropriate bits are set in the flags fields. @@ -188,14 +188,13 @@ Public members This is not supported on Windows, :c:func:`uv_spawn` will fail and set the error to ``UV_ENOTSUP``. -.. c:member:: uv_stdio_container_t.flags +.. c:member:: uv_stdio_flags uv_stdio_container_t.flags - Flags specifying how the stdio container should be passed to the child. See - :c:type:`uv_stdio_flags`. + Flags specifying how the stdio container should be passed to the child. -.. c:member:: uv_stdio_container_t.data +.. c:member:: union @0 uv_stdio_container_t.data - Union containing either the stream or fd to be passed on to the child + Union containing either the `stream` or `fd` to be passed on to the child process. diff --git a/deps/uv/docs/src/request.rst b/deps/uv/docs/src/request.rst index 5807ccba4a748e..a0414431b0e092 100644 --- a/deps/uv/docs/src/request.rst +++ b/deps/uv/docs/src/request.rst @@ -53,7 +53,7 @@ Public members API --- -.. c:function:: UV_REQ_TYPE_MAP(iter_macro) +.. c:macro:: UV_REQ_TYPE_MAP(iter_macro) Macro that expands to a series of invocations of `iter_macro` for each of the request types. `iter_macro` is invoked with two diff --git a/deps/uv/docs/src/sphinx-plugins/manpage.py b/deps/uv/docs/src/sphinx-plugins/manpage.py index 672b0020bddb2c..6570aeaf33ebce 100644 --- a/deps/uv/docs/src/sphinx-plugins/manpage.py +++ b/deps/uv/docs/src/sphinx-plugins/manpage.py @@ -18,7 +18,7 @@ def make_link_node(rawtext, app, name, manpage_num, options): ref = app.config.man_url_regex if not ref: - ref = "http://man7.org/linux/man-pages/man%s/%s.%s.html" %(manpage_num, name, manpage_num) + ref = "https://man7.org/linux/man-pages/man%s/%s.%s.html" %(manpage_num, name, manpage_num) else: s = Template(ref) ref = s.substitute(num=manpage_num, topic=name) diff --git a/deps/uv/docs/src/tty.rst b/deps/uv/docs/src/tty.rst index ad379dab0dd001..f1acfdc1372940 100644 --- a/deps/uv/docs/src/tty.rst +++ b/deps/uv/docs/src/tty.rst @@ -16,7 +16,7 @@ Data types TTY handle type. -.. c:type:: uv_tty_mode_t +.. c:enum:: uv_tty_mode_t .. versionadded:: 1.2.0 @@ -33,7 +33,8 @@ Data types UV_TTY_MODE_IO } uv_tty_mode_t; -.. c:type:: uv_tty_vtermstate_t +.. c:enum:: uv_tty_vtermstate_t + Console virtual terminal mode type: :: diff --git a/deps/uv/docs/src/udp.rst b/deps/uv/docs/src/udp.rst index 6be20345280d90..aed7ce22716557 100644 --- a/deps/uv/docs/src/udp.rst +++ b/deps/uv/docs/src/udp.rst @@ -88,7 +88,7 @@ Data types nothing to read, and with `nread` == 0 and `addr` != NULL when an empty UDP packet is received. -.. c:type:: uv_membership +.. c:enum:: uv_membership Membership type for a multicast address. @@ -391,6 +391,16 @@ API .. versionchanged:: 1.37.0 :man:`recvmmsg(2)` support is no longer enabled implicitly, it must be explicitly requested by passing the `UV_UDP_RECVMMSG` flag to :c:func:`uv_udp_init_ex`. + .. versionchanged:: 1.39.0 :c:func:`uv_udp_using_recvmmsg` can be used in `alloc_cb` to + determine if a buffer sized for use with :man:`recvmmsg(2)` should be + allocated for the current handle/platform. + +.. c:function:: int uv_udp_using_recvmmsg(uv_udp_t* handle) + + Returns 1 if the UDP handle was created with the `UV_UDP_RECVMMSG` flag + and the platform supports :man:`recvmmsg(2)`, 0 otherwise. + + .. versionadded:: 1.39.0 .. c:function:: int uv_udp_recv_stop(uv_udp_t* handle) diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h index fec663136a4ff1..06b6d001040e04 100644 --- a/deps/uv/include/uv.h +++ b/deps/uv/include/uv.h @@ -247,7 +247,8 @@ typedef struct uv_utsname_s uv_utsname_t; typedef struct uv_statfs_s uv_statfs_t; typedef enum { - UV_LOOP_BLOCK_SIGNAL + UV_LOOP_BLOCK_SIGNAL = 0, + UV_METRICS_IDLE_TIME } uv_loop_option; typedef enum { @@ -693,6 +694,7 @@ UV_EXTERN int uv_udp_try_send(uv_udp_t* handle, UV_EXTERN int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb); +UV_EXTERN int uv_udp_using_recvmmsg(const uv_udp_t* handle); UV_EXTERN int uv_udp_recv_stop(uv_udp_t* handle); UV_EXTERN size_t uv_udp_get_send_queue_size(const uv_udp_t* handle); UV_EXTERN size_t uv_udp_get_send_queue_count(const uv_udp_t* handle); @@ -1191,12 +1193,12 @@ UV_EXTERN uv_pid_t uv_os_getppid(void); #if defined(__PASE__) /* On IBM i PASE, the highest process priority is -10 */ -# define UV_PRIORITY_LOW 39 // RUNPTY(99) -# define UV_PRIORITY_BELOW_NORMAL 15 // RUNPTY(50) -# define UV_PRIORITY_NORMAL 0 // RUNPTY(20) -# define UV_PRIORITY_ABOVE_NORMAL -4 // RUNTY(12) -# define UV_PRIORITY_HIGH -7 // RUNPTY(6) -# define UV_PRIORITY_HIGHEST -10 // RUNPTY(1) +# define UV_PRIORITY_LOW 39 /* RUNPTY(99) */ +# define UV_PRIORITY_BELOW_NORMAL 15 /* RUNPTY(50) */ +# define UV_PRIORITY_NORMAL 0 /* RUNPTY(20) */ +# define UV_PRIORITY_ABOVE_NORMAL -4 /* RUNTY(12) */ +# define UV_PRIORITY_HIGH -7 /* RUNPTY(6) */ +# define UV_PRIORITY_HIGHEST -10 /* RUNPTY(1) */ #else # define UV_PRIORITY_LOW 19 # define UV_PRIORITY_BELOW_NORMAL 10 @@ -1243,6 +1245,7 @@ UV_EXTERN int uv_os_gethostname(char* buffer, size_t* size); UV_EXTERN int uv_os_uname(uv_utsname_t* buffer); +UV_EXTERN uint64_t uv_metrics_idle_time(uv_loop_t* loop); typedef enum { UV_FS_UNKNOWN = -1, @@ -1774,9 +1777,11 @@ struct uv_loop_s { unsigned int active_handles; void* handle_queue[2]; union { - void* unused[2]; + void* unused; unsigned int count; } active_reqs; + /* Internal storage for future extensions. */ + void* internal_fields; /* Internal flag to signal loop stop. */ unsigned int stop_flag; UV_LOOP_PRIVATE_FIELDS diff --git a/deps/uv/include/uv/version.h b/deps/uv/include/uv/version.h index 16b0914c41ede4..3219e9637f4510 100644 --- a/deps/uv/include/uv/version.h +++ b/deps/uv/include/uv/version.h @@ -31,8 +31,8 @@ */ #define UV_VERSION_MAJOR 1 -#define UV_VERSION_MINOR 38 -#define UV_VERSION_PATCH 1 +#define UV_VERSION_MINOR 39 +#define UV_VERSION_PATCH 0 #define UV_VERSION_IS_RELEASE 1 #define UV_VERSION_SUFFIX "" diff --git a/deps/uv/src/strscpy.c b/deps/uv/src/strscpy.c index 2a2bdce7450113..20df6fcbed29e9 100644 --- a/deps/uv/src/strscpy.c +++ b/deps/uv/src/strscpy.c @@ -1,3 +1,24 @@ +/* Copyright libuv project 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 "strscpy.h" #include /* SSIZE_MAX */ diff --git a/deps/uv/src/strscpy.h b/deps/uv/src/strscpy.h index fbe0a393f20542..cc78149db5f4cb 100644 --- a/deps/uv/src/strscpy.h +++ b/deps/uv/src/strscpy.h @@ -1,3 +1,24 @@ +/* Copyright libuv project 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. + */ + #ifndef UV_STRSCPY_H_ #define UV_STRSCPY_H_ diff --git a/deps/uv/src/unix/aix-common.c b/deps/uv/src/unix/aix-common.c index c18a5298cec6a4..abc4c901a574dc 100644 --- a/deps/uv/src/unix/aix-common.c +++ b/deps/uv/src/unix/aix-common.c @@ -22,42 +22,23 @@ #include "uv.h" #include "internal.h" -#include #include #include #include -#include -#include #include -#include -#include -#include -#include -#include -#include #include #include -#include -#include -#include -#include #include -#include -#include -#include - -#include #include -#include -#include -#include -#include -#include +extern char* original_exepath; +extern uv_mutex_t process_title_mutex; +extern uv_once_t process_title_mutex_once; +extern void init_process_title_mutex_once(void); uint64_t uv__hrtime(uv_clocktype_t type) { uint64_t G = 1000000000; @@ -78,80 +59,31 @@ uint64_t uv__hrtime(uv_clocktype_t type) { */ int uv_exepath(char* buffer, size_t* size) { int res; - char args[PATH_MAX]; - char abspath[PATH_MAX]; - size_t abspath_size; + char args[UV__PATH_MAX]; + size_t cached_len; struct procsinfo pi; if (buffer == NULL || size == NULL || *size == 0) return UV_EINVAL; - pi.pi_pid = getpid(); - res = getargs(&pi, sizeof(pi), args, sizeof(args)); - if (res < 0) - return UV_EINVAL; - - /* - * Possibilities for args: - * i) an absolute path such as: /home/user/myprojects/nodejs/node - * ii) a relative path such as: ./node or ../myprojects/nodejs/node - * iii) a bare filename such as "node", after exporting PATH variable - * to its location. - */ - - /* Case i) and ii) absolute or relative paths */ - if (strchr(args, '/') != NULL) { - if (realpath(args, abspath) != abspath) - return UV__ERR(errno); - - abspath_size = strlen(abspath); - + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + if (original_exepath != NULL) { + cached_len = strlen(original_exepath); *size -= 1; - if (*size > abspath_size) - *size = abspath_size; - - memcpy(buffer, abspath, *size); + if (*size > cached_len) + *size = cached_len; + memcpy(buffer, original_exepath, *size); buffer[*size] = '\0'; - + uv_mutex_unlock(&process_title_mutex); return 0; - } else { - /* Case iii). Search PATH environment variable */ - char trypath[PATH_MAX]; - char *clonedpath = NULL; - char *token = NULL; - char *path = getenv("PATH"); - - if (path == NULL) - return UV_EINVAL; - - clonedpath = uv__strdup(path); - if (clonedpath == NULL) - return UV_ENOMEM; - - token = strtok(clonedpath, ":"); - while (token != NULL) { - snprintf(trypath, sizeof(trypath) - 1, "%s/%s", token, args); - if (realpath(trypath, abspath) == abspath) { - /* Check the match is executable */ - if (access(abspath, X_OK) == 0) { - abspath_size = strlen(abspath); - - *size -= 1; - if (*size > abspath_size) - *size = abspath_size; - - memcpy(buffer, abspath, *size); - buffer[*size] = '\0'; - - uv__free(clonedpath); - return 0; - } - } - token = strtok(NULL, ":"); - } - uv__free(clonedpath); + } + uv_mutex_unlock(&process_title_mutex); + pi.pi_pid = getpid(); + res = getargs(&pi, sizeof(pi), args, sizeof(args)); - /* Out of tokens (path entries), and no match found */ + if (res < 0) return UV_EINVAL; - } + + return uv__search_path(args, buffer, size); } diff --git a/deps/uv/src/unix/aix.c b/deps/uv/src/unix/aix.c index 6b4594b43e9777..6a013d43e3ae4b 100644 --- a/deps/uv/src/unix/aix.c +++ b/deps/uv/src/unix/aix.c @@ -65,14 +65,15 @@ #define RDWR_BUF_SIZE 4096 #define EQ(a,b) (strcmp(a,b) == 0) -static uv_mutex_t process_title_mutex; -static uv_once_t process_title_mutex_once = UV_ONCE_INIT; +char* original_exepath = NULL; +uv_mutex_t process_title_mutex; +uv_once_t process_title_mutex_once = UV_ONCE_INIT; static void* args_mem = NULL; static char** process_argv = NULL; static int process_argc = 0; static char* process_title_ptr = NULL; -static void init_process_title_mutex_once(void) { +void init_process_title_mutex_once(void) { uv_mutex_init(&process_title_mutex); } @@ -145,6 +146,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int i; int rc; int add_failed; + int user_timeout; + int reset_timeout; if (loop->nfds == 0) { assert(QUEUE_EMPTY(&loop->watcher_queue)); @@ -214,7 +217,21 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { 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); + nfds = pollset_poll(loop->backend_fd, events, ARRAY_SIZE(events), @@ -227,6 +244,15 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { SAVE_ERRNO(uv__update_time(loop)); if (nfds == 0) { + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + if (timeout == -1) + continue; + if (timeout > 0) + goto update_timeout; + } + assert(timeout != -1); return; } @@ -236,6 +262,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { abort(); } + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + if (timeout == -1) continue; @@ -280,16 +311,25 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { /* 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) + if (w == &loop->signal_io_watcher) { have_signals = 1; - else + } else { + uv__metrics_update_idle_time(loop); w->cb(loop, w, pe->revents); + } nevents++; } - if (have_signals != 0) + 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; @@ -830,6 +870,7 @@ void uv__fs_event_close(uv_fs_event_t* handle) { char** uv_setup_args(int argc, char** argv) { + char exepath[UV__PATH_MAX]; char** new_argv; size_t size; char* s; @@ -845,6 +886,15 @@ char** uv_setup_args(int argc, char** argv) { process_argv = argv; process_argc = argc; + /* Use argv[0] to determine value for uv_exepath(). */ + size = sizeof(exepath); + if (uv__search_path(argv[0], exepath, &size) == 0) { + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + original_exepath = uv__strdup(exepath); + uv_mutex_unlock(&process_title_mutex); + } + /* Calculate how much memory we need for the argv strings. */ size = 0; for (i = 0; i < argc; i++) @@ -875,6 +925,10 @@ char** uv_setup_args(int argc, char** argv) { int uv_set_process_title(const char* title) { char* new_title; + /* If uv_setup_args wasn't called or failed, we can't continue. */ + if (process_argv == NULL || args_mem == NULL) + return UV_ENOBUFS; + /* We cannot free this pointer when libuv shuts down, * the process may still be using it. */ @@ -908,6 +962,10 @@ int uv_get_process_title(char* buffer, size_t size) { if (buffer == NULL || size == 0) return UV_EINVAL; + /* If uv_setup_args wasn't called, we can't continue. */ + if (process_argv == NULL) + return UV_ENOBUFS; + uv_once(&process_title_mutex_once, init_process_title_mutex_once); uv_mutex_lock(&process_title_mutex); diff --git a/deps/uv/src/unix/core.c b/deps/uv/src/unix/core.c index 5b0b64dd4b778f..1597828c868b38 100644 --- a/deps/uv/src/unix/core.c +++ b/deps/uv/src/unix/core.c @@ -383,6 +383,14 @@ int uv_run(uv_loop_t* loop, uv_run_mode mode) { timeout = uv_backend_timeout(loop); uv__io_poll(loop, timeout); + + /* Run one final update on the provider_idle_time in case uv__io_poll + * returned because the timeout expired, but no events were received. This + * call will be ignored if the provider_entry_time was either never set (if + * the timeout == 0) or was already updated b/c an event was received. + */ + uv__metrics_update_idle_time(loop); + uv__run_check(loop); uv__run_closing_handles(loop); @@ -1528,3 +1536,78 @@ void uv_sleep(unsigned int msec) { assert(rc == 0); } + +int uv__search_path(const char* prog, char* buf, size_t* buflen) { + char abspath[UV__PATH_MAX]; + size_t abspath_size; + char trypath[UV__PATH_MAX]; + char* cloned_path; + char* path_env; + char* token; + + if (buf == NULL || buflen == NULL || *buflen == 0) + return UV_EINVAL; + + /* + * Possibilities for prog: + * i) an absolute path such as: /home/user/myprojects/nodejs/node + * ii) a relative path such as: ./node or ../myprojects/nodejs/node + * iii) a bare filename such as "node", after exporting PATH variable + * to its location. + */ + + /* Case i) and ii) absolute or relative paths */ + if (strchr(prog, '/') != NULL) { + if (realpath(prog, abspath) != abspath) + return UV__ERR(errno); + + abspath_size = strlen(abspath); + + *buflen -= 1; + if (*buflen > abspath_size) + *buflen = abspath_size; + + memcpy(buf, abspath, *buflen); + buf[*buflen] = '\0'; + + return 0; + } + + /* Case iii). Search PATH environment variable */ + cloned_path = NULL; + token = NULL; + path_env = getenv("PATH"); + + if (path_env == NULL) + return UV_EINVAL; + + cloned_path = uv__strdup(path_env); + if (cloned_path == NULL) + return UV_ENOMEM; + + token = strtok(cloned_path, ":"); + while (token != NULL) { + snprintf(trypath, sizeof(trypath) - 1, "%s/%s", token, prog); + if (realpath(trypath, abspath) == abspath) { + /* Check the match is executable */ + if (access(abspath, X_OK) == 0) { + abspath_size = strlen(abspath); + + *buflen -= 1; + if (*buflen > abspath_size) + *buflen = abspath_size; + + memcpy(buf, abspath, *buflen); + buf[*buflen] = '\0'; + + uv__free(cloned_path); + return 0; + } + } + token = strtok(NULL, ":"); + } + uv__free(cloned_path); + + /* Out of tokens (path entries), and no match found */ + return UV_EINVAL; +} diff --git a/deps/uv/src/unix/darwin-stub.h b/deps/uv/src/unix/darwin-stub.h index b93cf67c596285..433e3efa73079e 100644 --- a/deps/uv/src/unix/darwin-stub.h +++ b/deps/uv/src/unix/darwin-stub.h @@ -27,6 +27,7 @@ struct CFArrayCallBacks; struct CFRunLoopSourceContext; struct FSEventStreamContext; +struct CFRange; typedef double CFAbsoluteTime; typedef double CFTimeInterval; @@ -42,13 +43,23 @@ typedef unsigned CFStringEncoding; typedef void* CFAllocatorRef; typedef void* CFArrayRef; typedef void* CFBundleRef; +typedef void* CFDataRef; typedef void* CFDictionaryRef; +typedef void* CFMutableDictionaryRef; +typedef struct CFRange CFRange; typedef void* CFRunLoopRef; typedef void* CFRunLoopSourceRef; typedef void* CFStringRef; typedef void* CFTypeRef; typedef void* FSEventStreamRef; +typedef uint32_t IOOptionBits; +typedef unsigned int io_iterator_t; +typedef unsigned int io_object_t; +typedef unsigned int io_service_t; +typedef unsigned int io_registry_entry_t; + + typedef void (*FSEventStreamCallback)(const FSEventStreamRef, void*, size_t, @@ -69,6 +80,11 @@ struct FSEventStreamContext { void* pad[3]; }; +struct CFRange { + CFIndex location; + CFIndex length; +}; + static const CFStringEncoding kCFStringEncodingUTF8 = 0x8000100; static const OSStatus noErr = 0; diff --git a/deps/uv/src/unix/darwin.c b/deps/uv/src/unix/darwin.c index 4f53ad1fc7f190..d0ecd452d87c68 100644 --- a/deps/uv/src/unix/darwin.c +++ b/deps/uv/src/unix/darwin.c @@ -33,10 +33,15 @@ #include #include /* sysconf */ +#if !TARGET_OS_IPHONE +#include "darwin-stub.h" +#endif + static uv_once_t once = UV_ONCE_INIT; static uint64_t (*time_func)(void); static mach_timebase_info_data_t timebase; +typedef unsigned char UInt8; int uv__platform_loop_init(uv_loop_t* loop) { loop->cf_state = NULL; @@ -180,17 +185,149 @@ int uv_uptime(double* uptime) { return 0; } +static int uv__get_cpu_speed(uint64_t* speed) { + /* IOKit */ + void (*pIOObjectRelease)(io_object_t); + kern_return_t (*pIOMasterPort)(mach_port_t, mach_port_t*); + CFMutableDictionaryRef (*pIOServiceMatching)(const char*); + kern_return_t (*pIOServiceGetMatchingServices)(mach_port_t, + CFMutableDictionaryRef, + io_iterator_t*); + io_service_t (*pIOIteratorNext)(io_iterator_t); + CFTypeRef (*pIORegistryEntryCreateCFProperty)(io_registry_entry_t, + CFStringRef, + CFAllocatorRef, + IOOptionBits); + + /* CoreFoundation */ + CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef, + const char*, + CFStringEncoding); + CFStringEncoding (*pCFStringGetSystemEncoding)(void); + UInt8 *(*pCFDataGetBytePtr)(CFDataRef); + CFIndex (*pCFDataGetLength)(CFDataRef); + void (*pCFDataGetBytes)(CFDataRef, CFRange, UInt8*); + void (*pCFRelease)(CFTypeRef); + + void* core_foundation_handle; + void* iokit_handle; + int err; + + kern_return_t kr; + mach_port_t mach_port; + io_iterator_t it; + io_object_t service; + + mach_port = 0; + + err = UV_ENOENT; + core_foundation_handle = dlopen("/System/Library/Frameworks/" + "CoreFoundation.framework/" + "Versions/A/CoreFoundation", + RTLD_LAZY | RTLD_LOCAL); + iokit_handle = dlopen("/System/Library/Frameworks/IOKit.framework/" + "Versions/A/IOKit", + RTLD_LAZY | RTLD_LOCAL); + + if (core_foundation_handle == NULL || iokit_handle == NULL) + goto out; + +#define V(handle, symbol) \ + do { \ + *(void **)(&p ## symbol) = dlsym((handle), #symbol); \ + if (p ## symbol == NULL) \ + goto out; \ + } \ + while (0) + V(iokit_handle, IOMasterPort); + V(iokit_handle, IOServiceMatching); + V(iokit_handle, IOServiceGetMatchingServices); + V(iokit_handle, IOIteratorNext); + V(iokit_handle, IOObjectRelease); + V(iokit_handle, IORegistryEntryCreateCFProperty); + V(core_foundation_handle, CFStringCreateWithCString); + V(core_foundation_handle, CFStringGetSystemEncoding); + V(core_foundation_handle, CFDataGetBytePtr); + V(core_foundation_handle, CFDataGetLength); + V(core_foundation_handle, CFDataGetBytes); + V(core_foundation_handle, CFRelease); +#undef V + +#define S(s) pCFStringCreateWithCString(NULL, (s), kCFStringEncodingUTF8) + + kr = pIOMasterPort(MACH_PORT_NULL, &mach_port); + assert(kr == KERN_SUCCESS); + CFMutableDictionaryRef classes_to_match + = pIOServiceMatching("IOPlatformDevice"); + kr = pIOServiceGetMatchingServices(mach_port, classes_to_match, &it); + assert(kr == KERN_SUCCESS); + service = pIOIteratorNext(it); + + CFStringRef device_type_str = S("device_type"); + CFStringRef clock_frequency_str = S("clock-frequency"); + + while (service != 0) { + CFDataRef data; + data = pIORegistryEntryCreateCFProperty(service, + device_type_str, + NULL, + 0); + if (data) { + const UInt8* raw = pCFDataGetBytePtr(data); + if (strncmp((char*)raw, "cpu", 3) == 0 || + strncmp((char*)raw, "processor", 9) == 0) { + CFDataRef freq_ref; + freq_ref = pIORegistryEntryCreateCFProperty(service, + clock_frequency_str, + NULL, + 0); + if (freq_ref) { + uint32_t freq; + CFIndex len = pCFDataGetLength(freq_ref); + CFRange range; + range.location = 0; + range.length = len; + + pCFDataGetBytes(freq_ref, range, (UInt8*)&freq); + *speed = freq; + pCFRelease(freq_ref); + pCFRelease(data); + break; + } + } + pCFRelease(data); + } + + service = pIOIteratorNext(it); + } + + pIOObjectRelease(it); + + err = 0; +out: + if (core_foundation_handle != NULL) + dlclose(core_foundation_handle); + + if (iokit_handle != NULL) + dlclose(iokit_handle); + + mach_port_deallocate(mach_task_self(), mach_port); + + return err; +} + int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), multiplier = ((uint64_t)1000L / ticks); char model[512]; - uint64_t cpuspeed; size_t size; unsigned int i; natural_t numcpus; mach_msg_type_number_t msg_type; processor_cpu_load_info_data_t *info; uv_cpu_info_t* cpu_info; + uint64_t cpuspeed; + int err; size = sizeof(model); if (sysctlbyname("machdep.cpu.brand_string", &model, &size, NULL, 0) && @@ -198,9 +335,9 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { return UV__ERR(errno); } - size = sizeof(cpuspeed); - if (sysctlbyname("hw.cpufrequency", &cpuspeed, &size, NULL, 0)) - return UV__ERR(errno); + err = uv__get_cpu_speed(&cpuspeed); + if (err < 0) + return err; if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numcpus, (processor_info_array_t*)&info, diff --git a/deps/uv/src/unix/fs.c b/deps/uv/src/unix/fs.c index f158d11787b9e1..87cb8b816aea39 100644 --- a/deps/uv/src/unix/fs.c +++ b/deps/uv/src/unix/fs.c @@ -306,7 +306,8 @@ static int uv__fs_mkstemp(uv_fs_t* req) { if (path_length < pattern_size || strcmp(path + path_length - pattern_size, pattern)) { errno = EINVAL; - return -1; + r = -1; + goto clobber; } uv_once(&once, uv__mkostemp_initonce); @@ -321,7 +322,7 @@ static int uv__fs_mkstemp(uv_fs_t* req) { /* If mkostemp() returns EINVAL, it means the kernel doesn't support O_CLOEXEC, so we just fallback to mkstemp() below. */ if (errno != EINVAL) - return r; + goto clobber; /* We set the static variable so that next calls don't even try to use mkostemp. */ @@ -347,6 +348,9 @@ static int uv__fs_mkstemp(uv_fs_t* req) { if (req->cb != NULL) uv_rwlock_rdunlock(&req->loop->cloexec_lock); +clobber: + if (r < 0) + path[0] = '\0'; return r; } @@ -883,8 +887,27 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) { ssize_t r; off = req->off; + +#ifdef __linux__ + { + static int copy_file_range_support = 1; + + if (copy_file_range_support) { + r = uv__fs_copy_file_range(in_fd, NULL, out_fd, &off, req->bufsml[0].len, 0); + + if (r == -1 && errno == ENOSYS) { + errno = 0; + copy_file_range_support = 0; + } else { + goto ok; + } + } + } +#endif + r = sendfile(out_fd, in_fd, &off, req->bufsml[0].len); +ok: /* sendfile() on SunOS returns EINVAL if the target fd is not a socket but * it still writes out data. Fortunately, we can detect it by checking if * the offset has been updated. @@ -1127,7 +1150,7 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { goto out; } - dst_flags = O_WRONLY | O_CREAT | O_TRUNC; + dst_flags = O_WRONLY | O_CREAT; if (req->flags & UV_FS_COPYFILE_EXCL) dst_flags |= O_EXCL; @@ -1146,16 +1169,26 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { goto out; } - /* Get the destination file's mode. */ - if (fstat(dstfd, &dst_statsbuf)) { - err = UV__ERR(errno); - goto out; - } + /* If the file is not being opened exclusively, verify that the source and + destination are not the same file. If they are the same, bail out early. */ + if ((req->flags & UV_FS_COPYFILE_EXCL) == 0) { + /* Get the destination file's mode. */ + if (fstat(dstfd, &dst_statsbuf)) { + err = UV__ERR(errno); + goto out; + } - /* Check if srcfd and dstfd refer to the same file */ - if (src_statsbuf.st_dev == dst_statsbuf.st_dev && - src_statsbuf.st_ino == dst_statsbuf.st_ino) { - goto out; + /* Check if srcfd and dstfd refer to the same file */ + if (src_statsbuf.st_dev == dst_statsbuf.st_dev && + src_statsbuf.st_ino == dst_statsbuf.st_ino) { + goto out; + } + + /* Truncate the file in case the destination already existed. */ + if (ftruncate(dstfd, 0) != 0) { + err = UV__ERR(errno); + goto out; + } } if (fchmod(dstfd, src_statsbuf.st_mode) == -1) { diff --git a/deps/uv/src/unix/ibmi.c b/deps/uv/src/unix/ibmi.c index ff300ea5f8f79f..96efc02bad6071 100644 --- a/deps/uv/src/unix/ibmi.c +++ b/deps/uv/src/unix/ibmi.c @@ -58,6 +58,9 @@ #include #include +char* original_exepath = NULL; +uv_mutex_t process_title_mutex; +uv_once_t process_title_mutex_once = UV_ONCE_INIT; typedef struct { int bytes_available; @@ -171,6 +174,9 @@ static void iconv_a2e(const char* src, unsigned char dst[], size_t length) { dst[i] = a2e[' ']; } +void init_process_title_mutex_once(void) { + uv_mutex_init(&process_title_mutex); +} static int get_ibmi_system_status(SSTS0200* rcvr) { /* rcvrlen is input parameter 2 to QWCRSSTS */ @@ -459,3 +465,37 @@ void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) { uv__free(addresses); } + +char** uv_setup_args(int argc, char** argv) { + char exepath[UV__PATH_MAX]; + char* s; + size_t size; + + if (argc > 0) { + /* Use argv[0] to determine value for uv_exepath(). */ + size = sizeof(exepath); + if (uv__search_path(argv[0], exepath, &size) == 0) { + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + original_exepath = uv__strdup(exepath); + uv_mutex_unlock(&process_title_mutex); + } + } + + return argv; +} + +int uv_set_process_title(const char* title) { + return 0; +} + +int uv_get_process_title(char* buffer, size_t size) { + if (buffer == NULL || size == 0) + return UV_EINVAL; + + buffer[0] = '\0'; + return 0; +} + +void uv__process_title_cleanup(void) { +} \ No newline at end of file diff --git a/deps/uv/src/unix/internal.h b/deps/uv/src/unix/internal.h index ab4f58c532e024..9d3c2297f8d764 100644 --- a/deps/uv/src/unix/internal.h +++ b/deps/uv/src/unix/internal.h @@ -266,6 +266,7 @@ void uv__udp_finish_close(uv_udp_t* handle); uv_handle_type uv__handle_type(int fd); FILE* uv__open_file(const char* path); int uv__getpwuid_r(uv_passwd_t* pwd); +int uv__search_path(const char* prog, char* buf, size_t* buflen); /* random */ int uv__random_devurandom(void* buf, size_t buflen); diff --git a/deps/uv/src/unix/kqueue.c b/deps/uv/src/unix/kqueue.c index 27a0d3df5e3176..bf183d5fdc0ba8 100644 --- a/deps/uv/src/unix/kqueue.c +++ b/deps/uv/src/unix/kqueue.c @@ -129,6 +129,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int fd; int op; int i; + int user_timeout; + int reset_timeout; if (loop->nfds == 0) { assert(QUEUE_EMPTY(&loop->watcher_queue)); @@ -202,7 +204,21 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { 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 (;; nevents = 0) { + /* 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; @@ -228,6 +244,15 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { SAVE_ERRNO(uv__update_time(loop)); if (nfds == 0) { + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + if (timeout == -1) + continue; + if (timeout > 0) + goto update_timeout; + } + assert(timeout != -1); return; } @@ -236,6 +261,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (errno != EINTR) abort(); + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + if (timeout == 0) return; @@ -276,6 +306,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (ev->filter == EVFILT_VNODE) { assert(w->events == POLLIN); assert(w->pevents == POLLIN); + uv__metrics_update_idle_time(loop); w->cb(loop, w, ev->fflags); /* XXX always uv__fs_event() */ nevents++; continue; @@ -337,16 +368,25 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { /* 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) + if (w == &loop->signal_io_watcher) { have_signals = 1; - else + } else { + uv__metrics_update_idle_time(loop); w->cb(loop, w, revents); + } nevents++; } - if (have_signals != 0) + 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; diff --git a/deps/uv/src/unix/linux-core.c b/deps/uv/src/unix/linux-core.c index 80ca75eea3d678..14d5f0c04a93bc 100644 --- a/deps/uv/src/unix/linux-core.c +++ b/deps/uv/src/unix/linux-core.c @@ -218,6 +218,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int fd; int op; int i; + int user_timeout; + int reset_timeout; if (loop->nfds == 0) { assert(QUEUE_EMPTY(&loop->watcher_queue)); @@ -273,6 +275,14 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { 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; + } + /* 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 @@ -283,6 +293,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { 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. */ @@ -327,6 +343,14 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (nfds == 0) { assert(timeout != -1); + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (timeout == -1) + continue; + if (timeout == 0) return; @@ -346,6 +370,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (errno != EINTR) abort(); + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + if (timeout == -1) continue; @@ -425,17 +454,26 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { /* 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) + if (w == &loop->signal_io_watcher) { have_signals = 1; - else + } else { + uv__metrics_update_idle_time(loop); w->cb(loop, w, pe->events); + } nevents++; } } - if (have_signals != 0) + 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; @@ -483,18 +521,22 @@ uint64_t uv__hrtime(uv_clocktype_t type) { /* TODO(bnoordhuis) Use CLOCK_MONOTONIC_COARSE for UV_CLOCK_PRECISE * when it has microsecond granularity or better (unlikely). */ - if (type == UV_CLOCK_FAST && fast_clock_id == -1) { - if (clock_getres(CLOCK_MONOTONIC_COARSE, &t) == 0 && - t.tv_nsec <= 1 * 1000 * 1000) { - fast_clock_id = CLOCK_MONOTONIC_COARSE; - } else { - fast_clock_id = CLOCK_MONOTONIC; - } - } + clock_id = CLOCK_MONOTONIC; + if (type != UV_CLOCK_FAST) + goto done; + + clock_id = uv__load_relaxed(&fast_clock_id); + if (clock_id != -1) + goto done; clock_id = CLOCK_MONOTONIC; - if (type == UV_CLOCK_FAST) - clock_id = fast_clock_id; + if (0 == clock_getres(CLOCK_MONOTONIC_COARSE, &t)) + if (t.tv_nsec <= 1 * 1000 * 1000) + clock_id = CLOCK_MONOTONIC_COARSE; + + uv__store_relaxed(&fast_clock_id, clock_id); + +done: if (clock_gettime(clock_id, &t)) return 0; /* Not really possible. */ diff --git a/deps/uv/src/unix/linux-syscalls.c b/deps/uv/src/unix/linux-syscalls.c index 742f26ada8218c..160056b46ec383 100644 --- a/deps/uv/src/unix/linux-syscalls.c +++ b/deps/uv/src/unix/linux-syscalls.c @@ -94,6 +94,24 @@ # endif #endif /* __NR_pwritev */ +#ifndef __NR_copy_file_range +# if defined(__x86_64__) +# define __NR_copy_file_range 326 +# elif defined(__i386__) +# define __NR_copy_file_range 377 +# elif defined(__s390__) +# define __NR_copy_file_range 375 +# elif defined(__arm__) +# define __NR_copy_file_range (UV_SYSCALL_BASE + 391) +# elif defined(__aarch64__) +# define __NR_copy_file_range 285 +# elif defined(__powerpc__) +# define __NR_copy_file_range 379 +# elif defined(__arc__) +# define __NR_copy_file_range 285 +# endif +#endif /* __NR_copy_file_range */ + #ifndef __NR_statx # if defined(__x86_64__) # define __NR_statx 332 @@ -180,6 +198,28 @@ int uv__dup3(int oldfd, int newfd, int flags) { } +ssize_t +uv__fs_copy_file_range(int fd_in, + ssize_t* off_in, + int fd_out, + ssize_t* off_out, + size_t len, + unsigned int flags) +{ +#ifdef __NR_copy_file_range + return syscall(__NR_copy_file_range, + fd_in, + off_in, + fd_out, + off_out, + len, + flags); +#else + return errno = ENOSYS, -1; +#endif +} + + int uv__statx(int dirfd, const char* path, int flags, diff --git a/deps/uv/src/unix/linux-syscalls.h b/deps/uv/src/unix/linux-syscalls.h index 2e8fa2a51979c6..761ff32e21bc53 100644 --- a/deps/uv/src/unix/linux-syscalls.h +++ b/deps/uv/src/unix/linux-syscalls.h @@ -64,6 +64,13 @@ struct uv__statx { ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset); ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset); int uv__dup3(int oldfd, int newfd, int flags); +ssize_t +uv__fs_copy_file_range(int fd_in, + ssize_t* off_in, + int fd_out, + ssize_t* off_out, + size_t len, + unsigned int flags); int uv__statx(int dirfd, const char* path, int flags, diff --git a/deps/uv/src/unix/loop.c b/deps/uv/src/unix/loop.c index e5b2889560a516..a88e71c339351f 100644 --- a/deps/uv/src/unix/loop.c +++ b/deps/uv/src/unix/loop.c @@ -28,6 +28,7 @@ #include int uv_loop_init(uv_loop_t* loop) { + uv__loop_internal_fields_t* lfields; void* saved_data; int err; @@ -36,6 +37,15 @@ int uv_loop_init(uv_loop_t* loop) { memset(loop, 0, sizeof(*loop)); loop->data = saved_data; + lfields = (uv__loop_internal_fields_t*) uv__calloc(1, sizeof(*lfields)); + if (lfields == NULL) + return UV_ENOMEM; + loop->internal_fields = lfields; + + err = uv_mutex_init(&lfields->loop_metrics.lock); + if (err) + goto fail_metrics_mutex_init; + heap_init((struct heap*) &loop->timer_heap); QUEUE_INIT(&loop->wq); QUEUE_INIT(&loop->idle_handles); @@ -66,7 +76,7 @@ int uv_loop_init(uv_loop_t* loop) { err = uv__platform_loop_init(loop); if (err) - return err; + goto fail_platform_init; uv__signal_global_once_init(); err = uv_signal_init(loop, &loop->child_watcher); @@ -106,6 +116,13 @@ int uv_loop_init(uv_loop_t* loop) { fail_signal_init: uv__platform_loop_delete(loop); +fail_platform_init: + uv_mutex_destroy(&lfields->loop_metrics.lock); + +fail_metrics_mutex_init: + uv__free(lfields); + loop->internal_fields = NULL; + uv__free(loop->watchers); loop->nwatchers = 0; return err; @@ -146,6 +163,8 @@ int uv_loop_fork(uv_loop_t* loop) { void uv__loop_close(uv_loop_t* loop) { + uv__loop_internal_fields_t* lfields; + uv__signal_loop_cleanup(loop); uv__platform_loop_delete(loop); uv__async_stop(loop); @@ -181,10 +200,23 @@ void uv__loop_close(uv_loop_t* loop) { uv__free(loop->watchers); loop->watchers = NULL; loop->nwatchers = 0; + + lfields = uv__get_internal_fields(loop); + uv_mutex_destroy(&lfields->loop_metrics.lock); + uv__free(lfields); + loop->internal_fields = NULL; } int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) { + uv__loop_internal_fields_t* lfields; + + lfields = uv__get_internal_fields(loop); + if (option == UV_METRICS_IDLE_TIME) { + lfields->flags |= UV_METRICS_IDLE_TIME; + return 0; + } + if (option != UV_LOOP_BLOCK_SIGNAL) return UV_ENOSYS; diff --git a/deps/uv/src/unix/os390.c b/deps/uv/src/unix/os390.c index dce169b9fb4268..3bb44266d93cc1 100644 --- a/deps/uv/src/unix/os390.c +++ b/deps/uv/src/unix/os390.c @@ -254,8 +254,6 @@ static int getexe(const int pid, char* buf, size_t len) { int uv_exepath(char* buffer, size_t* size) { int res; char args[PATH_MAX]; - char abspath[PATH_MAX]; - size_t abspath_size; int pid; if (buffer == NULL || size == NULL || *size == 0) @@ -266,69 +264,7 @@ int uv_exepath(char* buffer, size_t* size) { if (res < 0) return UV_EINVAL; - /* - * Possibilities for args: - * i) an absolute path such as: /home/user/myprojects/nodejs/node - * ii) a relative path such as: ./node or ../myprojects/nodejs/node - * iii) a bare filename such as "node", after exporting PATH variable - * to its location. - */ - - /* Case i) and ii) absolute or relative paths */ - if (strchr(args, '/') != NULL) { - if (realpath(args, abspath) != abspath) - return UV__ERR(errno); - - abspath_size = strlen(abspath); - - *size -= 1; - if (*size > abspath_size) - *size = abspath_size; - - memcpy(buffer, abspath, *size); - buffer[*size] = '\0'; - - return 0; - } else { - /* Case iii). Search PATH environment variable */ - char trypath[PATH_MAX]; - char* clonedpath = NULL; - char* token = NULL; - char* path = getenv("PATH"); - - if (path == NULL) - return UV_EINVAL; - - clonedpath = uv__strdup(path); - if (clonedpath == NULL) - return UV_ENOMEM; - - token = strtok(clonedpath, ":"); - while (token != NULL) { - snprintf(trypath, sizeof(trypath) - 1, "%s/%s", token, args); - if (realpath(trypath, abspath) == abspath) { - /* Check the match is executable */ - if (access(abspath, X_OK) == 0) { - abspath_size = strlen(abspath); - - *size -= 1; - if (*size > abspath_size) - *size = abspath_size; - - memcpy(buffer, abspath, *size); - buffer[*size] = '\0'; - - uv__free(clonedpath); - return 0; - } - } - token = strtok(NULL, ":"); - } - uv__free(clonedpath); - - /* Out of tokens (path entries), and no match found */ - return UV_EINVAL; - } + return uv__search_path(args, buffer, size); } @@ -818,6 +754,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int fd; int op; int i; + int user_timeout; + int reset_timeout; if (loop->nfds == 0) { assert(QUEUE_EMPTY(&loop->watcher_queue)); @@ -870,8 +808,22 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { real_timeout = timeout; int nevents = 0; + if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + } + nfds = 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 (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout) timeout = max_safe_timeout; @@ -887,12 +839,21 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (nfds == 0) { assert(timeout != -1); - if (timeout > 0) { - timeout = real_timeout - timeout; - continue; + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; } - return; + 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) { @@ -900,6 +861,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (errno != EINTR) abort(); + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + if (timeout == -1) continue; @@ -954,6 +920,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { pe->events |= w->pevents & (POLLIN | POLLOUT); if (pe->events != 0) { + uv__metrics_update_idle_time(loop); w->cb(loop, w, pe->events); nevents++; } @@ -961,6 +928,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { loop->watchers[loop->nwatchers] = NULL; loop->watchers[loop->nwatchers + 1] = NULL; + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { /* Poll for more events but don't block this time. */ diff --git a/deps/uv/src/unix/posix-poll.c b/deps/uv/src/unix/posix-poll.c index 766e83205d053f..0f4bf93874be89 100644 --- a/deps/uv/src/unix/posix-poll.c +++ b/deps/uv/src/unix/posix-poll.c @@ -144,6 +144,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int have_signals; struct pollfd* pe; int fd; + int user_timeout; + int reset_timeout; if (loop->nfds == 0) { assert(QUEUE_EMPTY(&loop->watcher_queue)); @@ -177,11 +179,25 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { assert(timeout >= -1); time_base = loop->time; + if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + } + /* Loop calls to poll() and processing of results. If we get some * results from poll() but they turn out not to be interesting to * our caller then we need to loop around and poll() again. */ 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 (pset != NULL) if (pthread_sigmask(SIG_BLOCK, pset, NULL)) abort(); @@ -197,6 +213,15 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { SAVE_ERRNO(uv__update_time(loop)); if (nfds == 0) { + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + if (timeout == -1) + continue; + if (timeout > 0) + goto update_timeout; + } + assert(timeout != -1); return; } @@ -205,6 +230,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (errno != EINTR) abort(); + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + if (timeout == -1) continue; @@ -254,6 +284,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (w == &loop->signal_io_watcher) { have_signals = 1; } else { + uv__metrics_update_idle_time(loop); w->cb(loop, w, pe->revents); } @@ -261,8 +292,15 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { } } - if (have_signals != 0) + 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->poll_fds_iterating = 0; diff --git a/deps/uv/src/unix/proctitle.c b/deps/uv/src/unix/proctitle.c index 4ee991fcc32466..9ffe5b629c2554 100644 --- a/deps/uv/src/unix/proctitle.c +++ b/deps/uv/src/unix/proctitle.c @@ -100,6 +100,10 @@ int uv_set_process_title(const char* title) { struct uv__process_title* pt; size_t len; + /* If uv_setup_args wasn't called or failed, we can't continue. */ + if (args_mem == NULL) + return UV_ENOBUFS; + pt = &process_title; len = strlen(title); @@ -126,6 +130,10 @@ int uv_get_process_title(char* buffer, size_t size) { if (buffer == NULL || size == 0) return UV_EINVAL; + /* If uv_setup_args wasn't called or failed, we can't continue. */ + if (args_mem == NULL) + return UV_ENOBUFS; + uv_once(&process_title_mutex_once, init_process_title_mutex_once); uv_mutex_lock(&process_title_mutex); diff --git a/deps/uv/src/unix/signal.c b/deps/uv/src/unix/signal.c index 1c83e095bcdead..f40a3e54ebb74e 100644 --- a/deps/uv/src/unix/signal.c +++ b/deps/uv/src/unix/signal.c @@ -143,6 +143,8 @@ static void uv__signal_block_and_lock(sigset_t* saved_sigmask) { if (sigfillset(&new_mask)) abort(); + /* to shut up valgrind */ + sigemptyset(saved_sigmask); if (pthread_sigmask(SIG_SETMASK, &new_mask, saved_sigmask)) abort(); diff --git a/deps/uv/src/unix/sunos.c b/deps/uv/src/unix/sunos.c index 180cc84651db37..d511c18b85e8ee 100644 --- a/deps/uv/src/unix/sunos.c +++ b/deps/uv/src/unix/sunos.c @@ -154,6 +154,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { sigset_t set; uint64_t base; uint64_t diff; + uint64_t idle_poll; unsigned int nfds; unsigned int i; int saved_errno; @@ -162,6 +163,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int count; int err; int fd; + int user_timeout; + int reset_timeout; if (loop->nfds == 0) { assert(QUEUE_EMPTY(&loop->watcher_queue)); @@ -199,7 +202,21 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { 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; @@ -242,6 +259,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { 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; @@ -282,10 +304,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { /* 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) + if (w == &loop->signal_io_watcher) { have_signals = 1; - else + } else { + uv__metrics_update_idle_time(loop); w->cb(loop, w, pe->portev_events); + } nevents++; @@ -297,8 +321,15 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); } - if (have_signals != 0) + 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; diff --git a/deps/uv/src/unix/udp.c b/deps/uv/src/unix/udp.c index 508c619fa84310..16c7f38ae82473 100644 --- a/deps/uv/src/unix/udp.c +++ b/deps/uv/src/unix/udp.c @@ -270,14 +270,11 @@ static void uv__udp_recvmsg(uv_udp_t* handle) { assert(buf.base != NULL); #if HAVE_MMSG - if (handle->flags & UV_HANDLE_UDP_RECVMMSG) { - uv_once(&once, uv__udp_mmsg_init); - if (uv__recvmmsg_avail) { - nread = uv__udp_recvmmsg(handle, &buf); - if (nread > 0) - count -= nread; - continue; - } + if (uv_udp_using_recvmmsg(handle)) { + nread = uv__udp_recvmmsg(handle, &buf); + if (nread > 0) + count -= nread; + continue; } #endif @@ -976,6 +973,17 @@ int uv__udp_init_ex(uv_loop_t* loop, } +int uv_udp_using_recvmmsg(const uv_udp_t* handle) { +#if HAVE_MMSG + if (handle->flags & UV_HANDLE_UDP_RECVMMSG) { + uv_once(&once, uv__udp_mmsg_init); + return uv__recvmmsg_avail; + } +#endif + return 0; +} + + int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { int err; diff --git a/deps/uv/src/uv-common.c b/deps/uv/src/uv-common.c index 0cfb921e697447..602e5f492fd2be 100644 --- a/deps/uv/src/uv-common.c +++ b/deps/uv/src/uv-common.c @@ -867,3 +867,62 @@ void uv_library_shutdown(void) { uv__threadpool_cleanup(); uv__store_relaxed(&was_shutdown, 1); } + + +void uv__metrics_update_idle_time(uv_loop_t* loop) { + uv__loop_metrics_t* loop_metrics; + uint64_t entry_time; + uint64_t exit_time; + + if (!(uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME)) + return; + + loop_metrics = uv__get_loop_metrics(loop); + + /* The thread running uv__metrics_update_idle_time() is always the same + * thread that sets provider_entry_time. So it's unnecessary to lock before + * retrieving this value. + */ + if (loop_metrics->provider_entry_time == 0) + return; + + exit_time = uv_hrtime(); + + uv_mutex_lock(&loop_metrics->lock); + entry_time = loop_metrics->provider_entry_time; + loop_metrics->provider_entry_time = 0; + loop_metrics->provider_idle_time += exit_time - entry_time; + uv_mutex_unlock(&loop_metrics->lock); +} + + +void uv__metrics_set_provider_entry_time(uv_loop_t* loop) { + uv__loop_metrics_t* loop_metrics; + uint64_t now; + + if (!(uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME)) + return; + + now = uv_hrtime(); + loop_metrics = uv__get_loop_metrics(loop); + uv_mutex_lock(&loop_metrics->lock); + loop_metrics->provider_entry_time = now; + uv_mutex_unlock(&loop_metrics->lock); +} + + +uint64_t uv_metrics_idle_time(uv_loop_t* loop) { + uv__loop_metrics_t* loop_metrics; + uint64_t entry_time; + uint64_t idle_time; + + loop_metrics = uv__get_loop_metrics(loop); + uv_mutex_lock(&loop_metrics->lock); + idle_time = loop_metrics->provider_idle_time; + entry_time = loop_metrics->provider_entry_time; + uv_mutex_unlock(&loop_metrics->lock); + + if (entry_time > 0) + idle_time += uv_hrtime() - entry_time; + return idle_time; +} diff --git a/deps/uv/src/uv-common.h b/deps/uv/src/uv-common.h index 063588eac97773..e851291cc06e01 100644 --- a/deps/uv/src/uv-common.h +++ b/deps/uv/src/uv-common.h @@ -333,6 +333,12 @@ void uv__threadpool_cleanup(void); } \ while (0) +#define uv__get_internal_fields(loop) \ + ((uv__loop_internal_fields_t*) loop->internal_fields) + +#define uv__get_loop_metrics(loop) \ + (&uv__get_internal_fields(loop)->loop_metrics) + /* Allocator prototypes */ void *uv__calloc(size_t count, size_t size); char *uv__strdup(const char* s); @@ -342,4 +348,21 @@ void uv__free(void* ptr); void* uv__realloc(void* ptr, size_t size); void* uv__reallocf(void* ptr, size_t size); +typedef struct uv__loop_metrics_s uv__loop_metrics_t; +typedef struct uv__loop_internal_fields_s uv__loop_internal_fields_t; + +struct uv__loop_metrics_s { + uint64_t provider_entry_time; + uint64_t provider_idle_time; + uv_mutex_t lock; +}; + +void uv__metrics_update_idle_time(uv_loop_t* loop); +void uv__metrics_set_provider_entry_time(uv_loop_t* loop); + +struct uv__loop_internal_fields_s { + unsigned int flags; + uv__loop_metrics_t loop_metrics; +}; + #endif /* UV_COMMON_H_ */ diff --git a/deps/uv/src/uv-data-getter-setters.c b/deps/uv/src/uv-data-getter-setters.c index c3025662fa108b..0bd04486117b72 100644 --- a/deps/uv/src/uv-data-getter-setters.c +++ b/deps/uv/src/uv-data-getter-setters.c @@ -1,3 +1,24 @@ +/* Copyright libuv project 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" const char* uv_handle_type_name(uv_handle_type type) { diff --git a/deps/uv/src/win/core.c b/deps/uv/src/win/core.c index 9974a115534320..e53a0f8e28637a 100644 --- a/deps/uv/src/win/core.c +++ b/deps/uv/src/win/core.c @@ -222,6 +222,7 @@ static void uv_init(void) { int uv_loop_init(uv_loop_t* loop) { + uv__loop_internal_fields_t* lfields; struct heap* timer_heap; int err; @@ -233,6 +234,15 @@ int uv_loop_init(uv_loop_t* loop) { if (loop->iocp == NULL) return uv_translate_sys_error(GetLastError()); + lfields = (uv__loop_internal_fields_t*) uv__calloc(1, sizeof(*lfields)); + if (lfields == NULL) + return UV_ENOMEM; + loop->internal_fields = lfields; + + err = uv_mutex_init(&lfields->loop_metrics.lock); + if (err) + goto fail_metrics_mutex_init; + /* To prevent uninitialized memory access, loop->time must be initialized * to zero before calling uv_update_time for the first time. */ @@ -297,6 +307,11 @@ int uv_loop_init(uv_loop_t* loop) { loop->timer_heap = NULL; fail_timers_alloc: + uv_mutex_destroy(&lfields->loop_metrics.lock); + +fail_metrics_mutex_init: + uv__free(lfields); + loop->internal_fields = NULL; CloseHandle(loop->iocp); loop->iocp = INVALID_HANDLE_VALUE; @@ -317,6 +332,7 @@ void uv__once_init(void) { void uv__loop_close(uv_loop_t* loop) { + uv__loop_internal_fields_t* lfields; size_t i; uv__loops_remove(loop); @@ -347,11 +363,24 @@ void uv__loop_close(uv_loop_t* loop) { uv__free(loop->timer_heap); loop->timer_heap = NULL; + lfields = uv__get_internal_fields(loop); + uv_mutex_destroy(&lfields->loop_metrics.lock); + uv__free(lfields); + loop->internal_fields = NULL; + CloseHandle(loop->iocp); } int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) { + uv__loop_internal_fields_t* lfields; + + lfields = uv__get_internal_fields(loop); + if (option == UV_METRICS_IDLE_TIME) { + lfields->flags |= UV_METRICS_IDLE_TIME; + return 0; + } + return UV_ENOSYS; } @@ -393,16 +422,44 @@ static void uv__poll_wine(uv_loop_t* loop, DWORD timeout) { uv_req_t* req; int repeat; uint64_t timeout_time; + uint64_t user_timeout; + int reset_timeout; timeout_time = loop->time + timeout; + if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + } + for (repeat = 0; ; repeat++) { + /* 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); + GetQueuedCompletionStatus(loop->iocp, &bytes, &key, &overlapped, timeout); + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + /* Placed here because on success the loop will break whether there is an + * empty package or not, or if GetQueuedCompletionStatus returned early then + * the timeout will be updated and the loop will run again. In either case + * the idle time will need to be updated. + */ + uv__metrics_update_idle_time(loop); + if (overlapped) { /* Package was dequeued */ req = uv_overlapped_to_req(overlapped); @@ -445,10 +502,26 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) { ULONG i; int repeat; uint64_t timeout_time; + uint64_t user_timeout; + int reset_timeout; timeout_time = loop->time + timeout; + if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + } + for (repeat = 0; ; repeat++) { + /* 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); + success = pGetQueuedCompletionStatusEx(loop->iocp, overlappeds, ARRAY_SIZE(overlappeds), @@ -456,6 +529,18 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) { timeout, FALSE); + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + /* Placed here because on success the loop will break whether there is an + * empty package or not, or if GetQueuedCompletionStatus returned early then + * the timeout will be updated and the loop will run again. In either case + * the idle time will need to be updated. + */ + uv__metrics_update_idle_time(loop); + if (success) { for (i = 0; i < count; i++) { /* Package was dequeued, but see if it is not a empty package @@ -534,6 +619,12 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) { else uv__poll_wine(loop, timeout); + /* Run one final update on the provider_idle_time in case uv__poll* + * returned because the timeout expired, but no events were received. This + * call will be ignored if the provider_entry_time was either never set (if + * the timeout == 0) or was already updated b/c an event was received. + */ + uv__metrics_update_idle_time(loop); uv_check_invoke(loop); uv_process_endgames(loop); diff --git a/deps/uv/src/win/detect-wakeup.c b/deps/uv/src/win/detect-wakeup.c index 72dfb7a1771765..ab193615783ea7 100644 --- a/deps/uv/src/win/detect-wakeup.c +++ b/deps/uv/src/win/detect-wakeup.c @@ -1,3 +1,24 @@ +/* Copyright libuv project 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 "winapi.h" diff --git a/deps/uv/src/win/fs-fd-hash-inl.h b/deps/uv/src/win/fs-fd-hash-inl.h index 7a203d232d35b4..0b532af12d4371 100644 --- a/deps/uv/src/win/fs-fd-hash-inl.h +++ b/deps/uv/src/win/fs-fd-hash-inl.h @@ -1,3 +1,24 @@ +/* Copyright libuv project 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. + */ + #ifndef UV_WIN_FS_FD_HASH_INL_H_ #define UV_WIN_FS_FD_HASH_INL_H_ @@ -53,7 +74,8 @@ static struct uv__fd_hash_bucket_s uv__fd_hash[UV__FD_HASH_SIZE]; INLINE static void uv__fd_hash_init(void) { - int i, err; + size_t i; + int err; err = uv_mutex_init(&uv__fd_hash_mutex); if (err) { diff --git a/deps/uv/src/win/fs.c b/deps/uv/src/win/fs.c index 9577bc02d3cf24..8a801749d472b0 100644 --- a/deps/uv/src/win/fs.c +++ b/deps/uv/src/win/fs.c @@ -70,10 +70,7 @@ #define SET_REQ_RESULT(req, result_value) \ do { \ req->result = (result_value); \ - if (req->result == -1) { \ - req->sys_errno_ = _doserrno; \ - req->result = uv_translate_sys_error(req->sys_errno_); \ - } \ + assert(req->result != -1); \ } while (0) #define SET_REQ_WIN32_ERROR(req, sys_errno) \ @@ -730,14 +727,14 @@ void fs__close(uv_fs_t* req) { assert(errno == EBADF); SET_REQ_UV_ERROR(req, UV_EBADF, ERROR_INVALID_HANDLE); } else { - req->result = 0; + SET_REQ_RESULT(req, 0); } } LONG fs__filemap_ex_filter(LONG excode, PEXCEPTION_POINTERS pep, int* perror) { - if (excode != EXCEPTION_IN_PAGE_ERROR) { + if (excode != (LONG)EXCEPTION_IN_PAGE_ERROR) { return EXCEPTION_CONTINUE_SEARCH; } @@ -816,10 +813,10 @@ void fs__read_filemap(uv_fs_t* req, struct uv__fd_info_s* fd_info) { for (index = 0; index < req->fs.info.nbufs && done_read < read_size; ++index) { - int err = 0; size_t this_read_size = MIN(req->fs.info.bufs[index].len, read_size - done_read); #ifdef _MSC_VER + int err = 0; __try { #endif memcpy(req->fs.info.bufs[index].base, @@ -938,7 +935,7 @@ void fs__write_filemap(uv_fs_t* req, HANDLE file, (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); size_t write_size, done_write; unsigned int index; - LARGE_INTEGER zero, pos, end_pos; + LARGE_INTEGER pos, end_pos; size_t view_offset; LARGE_INTEGER view_base; void* view; @@ -963,7 +960,6 @@ void fs__write_filemap(uv_fs_t* req, HANDLE file, return; } - zero.QuadPart = 0; if (force_append) { pos = fd_info->size; } else if (req->fs.info.offset == -1) { @@ -1014,8 +1010,8 @@ void fs__write_filemap(uv_fs_t* req, HANDLE file, done_write = 0; for (index = 0; index < req->fs.info.nbufs; ++index) { - int err = 0; #ifdef _MSC_VER + int err = 0; __try { #endif memcpy((char*)view + view_offset + done_write, @@ -1128,7 +1124,10 @@ void fs__write(uv_fs_t* req) { void fs__rmdir(uv_fs_t* req) { int result = _wrmdir(req->file.pathw); - SET_REQ_RESULT(req, result); + if (result == -1) + SET_REQ_WIN32_ERROR(req, _doserrno); + else + SET_REQ_RESULT(req, 0); } @@ -1221,12 +1220,12 @@ void fs__unlink(uv_fs_t* req) { void fs__mkdir(uv_fs_t* req) { /* TODO: use req->mode. */ - req->result = _wmkdir(req->file.pathw); - if (req->result == -1) { - req->sys_errno_ = _doserrno; - req->result = req->sys_errno_ == ERROR_INVALID_NAME - ? UV_EINVAL - : uv_translate_sys_error(req->sys_errno_); + if (CreateDirectoryW(req->file.pathw, NULL)) { + SET_REQ_RESULT(req, 0); + } else { + SET_REQ_WIN32_ERROR(req, GetLastError()); + if (req->sys_errno_ == ERROR_INVALID_NAME) + req->result = UV_EINVAL; } } @@ -1242,19 +1241,21 @@ void fs__mktemp(uv_fs_t* req, uv__fs_mktemp_func func) { unsigned int tries, i; size_t len; uint64_t v; - + char* path; + + path = req->path; len = wcslen(req->file.pathw); ep = req->file.pathw + len; if (len < num_x || wcsncmp(ep - num_x, L"XXXXXX", num_x)) { SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); - return; + goto clobber; } tries = TMP_MAX; do { if (uv__random_rtlgenrandom((void *)&v, sizeof(v)) < 0) { SET_REQ_UV_ERROR(req, UV_EIO, ERROR_IO_DEVICE); - break; + goto clobber; } cp = ep - num_x; @@ -1265,25 +1266,29 @@ void fs__mktemp(uv_fs_t* req, uv__fs_mktemp_func func) { if (func(req)) { if (req->result >= 0) { - len = strlen(req->path); - wcstombs((char*) req->path + len - num_x, ep - num_x, num_x); + len = strlen(path); + wcstombs(path + len - num_x, ep - num_x, num_x); } - break; + return; } } while (--tries); - if (tries == 0) { - SET_REQ_RESULT(req, -1); - } + SET_REQ_WIN32_ERROR(req, GetLastError()); + +clobber: + path[0] = '\0'; } static int fs__mkdtemp_func(uv_fs_t* req) { - if (_wmkdir(req->file.pathw) == 0) { + DWORD error; + if (CreateDirectoryW(req->file.pathw, NULL)) { SET_REQ_RESULT(req, 0); return 1; - } else if (errno != EEXIST) { - SET_REQ_RESULT(req, -1); + } + error = GetLastError(); + if (error != ERROR_ALREADY_EXISTS) { + SET_REQ_WIN32_ERROR(req, error); return 1; } @@ -1404,7 +1409,7 @@ void fs__scandir(uv_fs_t* req) { /* If the handle is not a directory, we'll get STATUS_INVALID_PARAMETER. * This should be reported back as UV_ENOTDIR. */ - if (status == STATUS_INVALID_PARAMETER) + if (status == (NTSTATUS)STATUS_INVALID_PARAMETER) goto not_a_directory_error; while (NT_SUCCESS(status)) { @@ -1895,7 +1900,7 @@ INLINE static void fs__stat_impl(uv_fs_t* req, int do_lstat) { } req->ptr = &req->statbuf; - req->result = 0; + SET_REQ_RESULT(req, 0); } @@ -1930,7 +1935,7 @@ static void fs__fstat(uv_fs_t* req) { } req->ptr = &req->statbuf; - req->result = 0; + SET_REQ_RESULT(req, 0); } @@ -2157,7 +2162,10 @@ static void fs__access(uv_fs_t* req) { static void fs__chmod(uv_fs_t* req) { int result = _wchmod(req->file.pathw, req->fs.info.mode); - SET_REQ_RESULT(req, result); + if (result == -1) + SET_REQ_WIN32_ERROR(req, _doserrno); + else + SET_REQ_RESULT(req, 0); } @@ -2315,7 +2323,7 @@ INLINE static void fs__utime_impl(uv_fs_t* req, int do_lutime) { return; } - req->result = 0; + SET_REQ_RESULT(req, 0); } static void fs__utime(uv_fs_t* req) { @@ -2340,7 +2348,7 @@ static void fs__futime(uv_fs_t* req) { return; } - req->result = 0; + SET_REQ_RESULT(req, 0); } static void fs__lutime(uv_fs_t* req) { @@ -2350,11 +2358,10 @@ static void fs__lutime(uv_fs_t* req) { static void fs__link(uv_fs_t* req) { DWORD r = CreateHardLinkW(req->fs.info.new_pathw, req->file.pathw, NULL); - if (r == 0) { + if (r == 0) SET_REQ_WIN32_ERROR(req, GetLastError()); - } else { - req->result = 0; - } + else + SET_REQ_RESULT(req, 0); } @@ -2674,17 +2681,17 @@ static void fs__realpath(uv_fs_t* req) { static void fs__chown(uv_fs_t* req) { - req->result = 0; + SET_REQ_RESULT(req, 0); } static void fs__fchown(uv_fs_t* req) { - req->result = 0; + SET_REQ_RESULT(req, 0); } static void fs__lchown(uv_fs_t* req) { - req->result = 0; + SET_REQ_RESULT(req, 0); } @@ -2829,7 +2836,7 @@ static void uv__fs_done(struct uv__work* w, int status) { if (status == UV_ECANCELED) { assert(req->result == 0); - req->result = UV_ECANCELED; + SET_REQ_UV_ERROR(req, UV_ECANCELED, 0); } req->cb(req); diff --git a/deps/uv/src/win/pipe.c b/deps/uv/src/win/pipe.c index fc0112a33cf62f..f81245ec606fcb 100644 --- a/deps/uv/src/win/pipe.c +++ b/deps/uv/src/win/pipe.c @@ -244,9 +244,8 @@ int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, return 0; error: - if (pipeHandle != INVALID_HANDLE_VALUE) { + if (pipeHandle != INVALID_HANDLE_VALUE) CloseHandle(pipeHandle); - } return err; } @@ -554,7 +553,7 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { /* Convert name to UTF16. */ nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR); - handle->name = (WCHAR*)uv__malloc(nameSize); + handle->name = uv__malloc(nameSize); if (!handle->name) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } @@ -621,9 +620,8 @@ static DWORD WINAPI pipe_connect_thread_proc(void* parameter) { while (WaitNamedPipeW(handle->name, 30000)) { /* The pipe is now available, try to connect. */ pipeHandle = open_named_pipe(handle->name, &duplex_flags); - if (pipeHandle != INVALID_HANDLE_VALUE) { + if (pipeHandle != INVALID_HANDLE_VALUE) break; - } SwitchToThread(); } @@ -655,7 +653,7 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, /* Convert name to UTF16. */ nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR); - handle->name = (WCHAR*)uv__malloc(nameSize); + handle->name = uv__malloc(nameSize); if (!handle->name) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } @@ -2147,7 +2145,7 @@ int uv_pipe_open(uv_pipe_t* pipe, uv_file file) { if (pipe->ipc) { assert(!(pipe->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)); pipe->pipe.conn.ipc_remote_pid = uv_os_getppid(); - assert(pipe->pipe.conn.ipc_remote_pid != (DWORD) -1); + assert(pipe->pipe.conn.ipc_remote_pid != (DWORD)(uv_pid_t) -1); } return 0; } diff --git a/deps/uv/src/win/tcp.c b/deps/uv/src/win/tcp.c index 941c8010d3f699..0dcaa97df70821 100644 --- a/deps/uv/src/win/tcp.c +++ b/deps/uv/src/win/tcp.c @@ -523,16 +523,15 @@ static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { &req->u.io.overlapped, NULL); + handle->flags |= UV_HANDLE_READ_PENDING; + handle->reqs_pending++; + if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { /* Process the req without IOCP. */ - handle->flags |= UV_HANDLE_READ_PENDING; req->u.io.overlapped.InternalHigh = bytes; - handle->reqs_pending++; uv_insert_pending_req(loop, (uv_req_t*)req); } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { /* The req will be processed with IOCP. */ - handle->flags |= UV_HANDLE_READ_PENDING; - handle->reqs_pending++; if (handle->flags & UV_HANDLE_EMULATE_IOCP && req->wait_handle == INVALID_HANDLE_VALUE && !RegisterWaitForSingleObject(&req->wait_handle, @@ -545,7 +544,6 @@ static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, WSAGetLastError()); uv_insert_pending_req(loop, (uv_req_t*)req); - handle->reqs_pending++; } } @@ -750,6 +748,40 @@ int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb, return 0; } +static int uv__is_loopback(const struct sockaddr_storage* storage) { + const struct sockaddr_in* in4; + const struct sockaddr_in6* in6; + int i; + + if (storage->ss_family == AF_INET) { + in4 = (const struct sockaddr_in*) storage; + return in4->sin_addr.S_un.S_un_b.s_b1 == 127; + } + if (storage->ss_family == AF_INET6) { + in6 = (const struct sockaddr_in6*) storage; + for (i = 0; i < 7; ++i) { + if (in6->sin6_addr.u.Word[i] != 0) + return 0; + } + return in6->sin6_addr.u.Word[7] == htons(1); + } + return 0; +} + +// Check if Windows version is 10.0.16299 or later +static int uv__is_fast_loopback_fail_supported() { + OSVERSIONINFOW os_info; + if (!pRtlGetVersion) + return 0; + pRtlGetVersion(&os_info); + if (os_info.dwMajorVersion < 10) + return 0; + if (os_info.dwMajorVersion > 10) + return 1; + if (os_info.dwMinorVersion > 0) + return 1; + return os_info.dwBuildNumber >= 16299; +} static int uv_tcp_try_connect(uv_connect_t* req, uv_tcp_t* handle, @@ -757,6 +789,7 @@ static int uv_tcp_try_connect(uv_connect_t* req, unsigned int addrlen, uv_connect_cb cb) { uv_loop_t* loop = handle->loop; + TCP_INITIAL_RTO_PARAMETERS retransmit_ioctl; const struct sockaddr* bind_addr; struct sockaddr_storage converted; BOOL success; @@ -792,6 +825,25 @@ static int uv_tcp_try_connect(uv_connect_t* req, } } + /* This makes connect() fail instantly if the target port on the localhost + * is not reachable, instead of waiting for 2s. We do not care if this fails. + * This only works on Windows version 10.0.16299 and later. + */ + if (uv__is_fast_loopback_fail_supported() && uv__is_loopback(&converted)) { + memset(&retransmit_ioctl, 0, sizeof(retransmit_ioctl)); + retransmit_ioctl.Rtt = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS; + retransmit_ioctl.MaxSynRetransmissions = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS; + WSAIoctl(handle->socket, + SIO_TCP_INITIAL_RTO, + &retransmit_ioctl, + sizeof(retransmit_ioctl), + NULL, + 0, + &bytes, + NULL, + NULL); + } + UV_REQ_INIT(req, UV_CONNECT); req->handle = (uv_stream_t*) handle; req->cb = cb; diff --git a/deps/uv/src/win/tty.c b/deps/uv/src/win/tty.c index 046aa3e46e1ffb..4604fb0c874694 100644 --- a/deps/uv/src/win/tty.c +++ b/deps/uv/src/win/tty.c @@ -2413,6 +2413,7 @@ static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param) { uv__tty_console_signal_resize(); ResetEvent(uv__tty_console_resized); } + return 0; } static void uv__tty_console_signal_resize(void) { diff --git a/deps/uv/src/win/udp.c b/deps/uv/src/win/udp.c index 33407a89460e1c..7032b685dedfd6 100644 --- a/deps/uv/src/win/udp.c +++ b/deps/uv/src/win/udp.c @@ -189,6 +189,11 @@ void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle) { } +int uv_udp_using_recvmmsg(const uv_udp_t* handle) { + return 0; +} + + static int uv_udp_maybe_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int addrlen, diff --git a/deps/uv/src/win/util.c b/deps/uv/src/win/util.c index 8acdbd7c21f741..aad8f1a15e9cb6 100644 --- a/deps/uv/src/win/util.c +++ b/deps/uv/src/win/util.c @@ -30,12 +30,14 @@ #include "uv.h" #include "internal.h" +/* clang-format off */ #include #include #include #include #include #include +/* clang-format on */ #include #include @@ -1806,7 +1808,9 @@ int uv_os_uname(uv_utsname_t* buffer) { pRtlGetVersion(&os_info); } else { /* Silence GetVersionEx() deprecation warning. */ + #ifdef _MSC_VER #pragma warning(suppress : 4996) + #endif if (GetVersionExW(&os_info) == 0) { r = uv_translate_sys_error(GetLastError()); goto error; @@ -1873,7 +1877,7 @@ int uv_os_uname(uv_utsname_t* buffer) { "MINGW32_NT-%u.%u", (unsigned int) os_info.dwMajorVersion, (unsigned int) os_info.dwMinorVersion); - assert(r < sizeof(buffer->sysname)); + assert((size_t)r < sizeof(buffer->sysname)); #else uv__strscpy(buffer->sysname, "Windows_NT", sizeof(buffer->sysname)); #endif @@ -1885,7 +1889,7 @@ int uv_os_uname(uv_utsname_t* buffer) { (unsigned int) os_info.dwMajorVersion, (unsigned int) os_info.dwMinorVersion, (unsigned int) os_info.dwBuildNumber); - assert(r < sizeof(buffer->release)); + assert((size_t)r < sizeof(buffer->release)); /* Populate the machine field. */ GetSystemInfo(&system_info); diff --git a/deps/uv/src/win/winapi.h b/deps/uv/src/win/winapi.h index cbe1437a42e6bd..0b66b5634bca88 100644 --- a/deps/uv/src/win/winapi.h +++ b/deps/uv/src/win/winapi.h @@ -4726,6 +4726,18 @@ typedef HWINEVENTHOOK (WINAPI *sSetWinEventHook) DWORD idThread, UINT dwflags); +/* From mstcpip.h */ +typedef struct _TCP_INITIAL_RTO_PARAMETERS { + USHORT Rtt; + UCHAR MaxSynRetransmissions; +} TCP_INITIAL_RTO_PARAMETERS, *PTCP_INITIAL_RTO_PARAMETERS; + +#ifndef TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS +# define TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS ((UCHAR) -2) +#endif +#ifndef SIO_TCP_INITIAL_RTO +# define SIO_TCP_INITIAL_RTO _WSAIOW(IOC_VENDOR,17) +#endif /* Ntdll function pointers */ extern sRtlGetVersion pRtlGetVersion; diff --git a/deps/uv/test/task.h b/deps/uv/test/task.h index e95e3bde5a7e3e..8250f949b2bfbc 100644 --- a/deps/uv/test/task.h +++ b/deps/uv/test/task.h @@ -111,24 +111,44 @@ typedef enum { } \ } while (0) -#define ASSERT_BASE(expr, a, operator, b, type, conv) \ +#define ASSERT_BASE(a, operator, b, type, conv) \ do { \ - if (!(expr)) { \ + type eval_a = (type) (a); \ + type eval_b = (type) (b); \ + if (!(eval_a operator eval_b)) { \ fprintf(stderr, \ "Assertion failed in %s on line %d: `%s %s %s` " \ - "(%"conv" %s %"conv")\n", \ + "(%"conv" %s %"conv")\n", \ __FILE__, \ __LINE__, \ #a, \ #operator, \ #b, \ - (type)a, \ + eval_a, \ #operator, \ - (type)b); \ + eval_b); \ abort(); \ } \ } while (0) +#define ASSERT_BASE_STR(expr, a, operator, b, type, conv) \ + do { \ + if (!(expr)) { \ + fprintf(stderr, \ + "Assertion failed in %s on line %d: `%s %s %s` " \ + "(%"conv" %s %"conv")\n", \ + __FILE__, \ + __LINE__, \ + #a, \ + #operator, \ + #b, \ + (type)a, \ + #operator, \ + (type)b); \ + abort(); \ + } \ + } while (0) + #define ASSERT_BASE_LEN(expr, a, operator, b, conv, len) \ do { \ if (!(expr)) { \ @@ -177,7 +197,7 @@ typedef enum { } while (0) #define ASSERT_INT_BASE(a, operator, b, type, conv) \ - ASSERT_BASE(a operator b, a, operator, b, type, conv) + ASSERT_BASE(a, operator, b, type, conv) #define ASSERT_EQ(a, b) ASSERT_INT_BASE(a, ==, b, int64_t, PRId64) #define ASSERT_GE(a, b) ASSERT_INT_BASE(a, >=, b, int64_t, PRId64) @@ -194,10 +214,10 @@ typedef enum { #define ASSERT_UINT64_NE(a, b) ASSERT_INT_BASE(a, !=, b, uint64_t, PRIu64) #define ASSERT_STR_EQ(a, b) \ - ASSERT_BASE(strcmp(a, b) == 0, a, ==, b, char*, "s") + ASSERT_BASE_STR(strcmp(a, b) == 0, a, == , b, char*, "s") #define ASSERT_STR_NE(a, b) \ - ASSERT_BASE(strcmp(a, b) != 0, a, !=, b, char*, "s") + ASSERT_BASE_STR(strcmp(a, b) != 0, a, !=, b, char*, "s") #define ASSERT_MEM_EQ(a, b, size) \ ASSERT_BASE_LEN(memcmp(a, b, size) == 0, a, ==, b, s, size) @@ -212,16 +232,16 @@ typedef enum { ASSERT_BASE_HEX(memcmp(a, b, size) != 0, a, !=, b, size) #define ASSERT_NULL(a) \ - ASSERT_BASE(a == NULL, a, ==, NULL, void*, "p") + ASSERT_BASE(a, ==, NULL, void*, "p") #define ASSERT_NOT_NULL(a) \ - ASSERT_BASE(a != NULL, a, !=, NULL, void*, "p") + ASSERT_BASE(a, !=, NULL, void*, "p") #define ASSERT_PTR_EQ(a, b) \ - ASSERT_BASE((void*)a == (void*)b, a, ==, b, void*, "p") + ASSERT_BASE(a, ==, b, void*, "p") #define ASSERT_PTR_NE(a, b) \ - ASSERT_BASE((void*)a != (void*)b, a, !=, b, void*, "p") + ASSERT_BASE(a, !=, b, void*, "p") /* This macro cleans up the main loop. This is used to avoid valgrind * warnings about memory being "leaked" by the main event loop. diff --git a/deps/uv/test/test-close-fd.c b/deps/uv/test/test-close-fd.c index 2ed9a10032dfe6..cea4a1b0b80bb4 100644 --- a/deps/uv/test/test-close-fd.c +++ b/deps/uv/test/test-close-fd.c @@ -28,13 +28,13 @@ static unsigned int read_cb_called; -static void alloc_cb(uv_handle_t *handle, size_t size, uv_buf_t *buf) { +static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { static char slab[1]; buf->base = slab; buf->len = sizeof(slab); } -static void read_cb(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) { +static void read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { switch (++read_cb_called) { case 1: ASSERT(nread == 1); diff --git a/deps/uv/test/test-fs-copyfile.c b/deps/uv/test/test-fs-copyfile.c index 3335c881064ffd..e6f06e6eac5a2b 100644 --- a/deps/uv/test/test-fs-copyfile.c +++ b/deps/uv/test/test-fs-copyfile.c @@ -125,6 +125,11 @@ TEST_IMPL(fs_copyfile) { r = uv_fs_copyfile(NULL, &req, src, src, 0, NULL); ASSERT(r == 0); uv_fs_req_cleanup(&req); + /* Verify that the src file did not get truncated. */ + r = uv_fs_stat(NULL, &req, src, NULL); + ASSERT_EQ(r, 0); + ASSERT_EQ(req.statbuf.st_size, 12); + uv_fs_req_cleanup(&req); unlink(src); /* Copies file synchronously. Creates new file. */ diff --git a/deps/uv/test/test-fs-open-flags.c b/deps/uv/test/test-fs-open-flags.c index fcef2fb0819f74..5f61007adde65a 100644 --- a/deps/uv/test/test-fs-open-flags.c +++ b/deps/uv/test/test-fs-open-flags.c @@ -58,7 +58,7 @@ static char empty_file[FILE_NAME_SIZE]; static char dummy_file[FILE_NAME_SIZE]; static char empty_dir[] = "empty_dir"; -static void setup() { +static void setup(void) { int r; /* empty_dir */ @@ -73,7 +73,7 @@ static void setup() { uv_fs_req_cleanup(&mkdir_req); } -static void refresh() { +static void refresh(void) { int r; /* absent_file */ @@ -119,7 +119,7 @@ static void refresh() { uv_fs_req_cleanup(&close_req); } -static void cleanup() { +static void cleanup(void) { unlink(absent_file); unlink(empty_file); unlink(dummy_file); diff --git a/deps/uv/test/test-fs.c b/deps/uv/test/test-fs.c index ae9923a9a16ec3..63189d01d5adc1 100644 --- a/deps/uv/test/test-fs.c +++ b/deps/uv/test/test-fs.c @@ -28,12 +28,8 @@ #include #include /* INT_MAX, PATH_MAX, IOV_MAX */ -/* FIXME we shouldn't need to branch in this file */ -#if defined(__unix__) || defined(__POSIX__) || \ - defined(__APPLE__) || defined(__sun) || \ - defined(_AIX) || defined(__MVS__) || \ - defined(__HAIKU__) -#include /* unlink, rmdir, etc. */ +#ifndef _WIN32 +# include /* unlink, rmdir, etc. */ #else # include # include @@ -1290,7 +1286,10 @@ TEST_IMPL(fs_mkstemp) { ASSERT(strcmp(mkstemp_req1.path, mkstemp_req2.path) != 0); /* invalid template returns EINVAL */ - ASSERT(uv_fs_mkstemp(NULL, &mkstemp_req3, "test_file", NULL) == UV_EINVAL); + ASSERT_EQ(UV_EINVAL, uv_fs_mkstemp(NULL, &mkstemp_req3, "test_file", NULL)); + + /* Make sure that path is empty string */ + ASSERT_EQ(0, strlen(mkstemp_req3.path)); /* We can write to the opened file */ iov = uv_buf_init(test_buf, sizeof(test_buf)); @@ -3885,7 +3884,7 @@ TEST_IMPL(fs_file_pos_after_op_with_offset) { } #ifdef _WIN32 -static void fs_file_pos_common() { +static void fs_file_pos_common(void) { int r; iov = uv_buf_init("abc", 3); diff --git a/deps/uv/test/test-get-currentexe.c b/deps/uv/test/test-get-currentexe.c index 5e4a083f3060b7..8eba7b73b7b036 100644 --- a/deps/uv/test/test-get-currentexe.c +++ b/deps/uv/test/test-get-currentexe.c @@ -88,5 +88,19 @@ TEST_IMPL(get_currentexe) { ASSERT(buffer[0] != '\0'); ASSERT(buffer[1] == '\0'); + /* Verify uv_exepath is not affected by uv_set_process_title(). */ + r = uv_set_process_title("foobar"); + ASSERT_EQ(r, 0); + size = sizeof(buffer); + r = uv_exepath(buffer, &size); + ASSERT_EQ(r, 0); + + match = strstr(buffer, path); + /* Verify that the path returned from uv_exepath is a subdirectory of + * executable_path. + */ + ASSERT_NOT_NULL(match); + ASSERT_STR_EQ(match, path); + ASSERT_EQ(size, strlen(buffer)); return 0; } diff --git a/deps/uv/test/test-getaddrinfo.c b/deps/uv/test/test-getaddrinfo.c index f2b4e03cd74cdb..628e4d13cc60db 100644 --- a/deps/uv/test/test-getaddrinfo.c +++ b/deps/uv/test/test-getaddrinfo.c @@ -39,6 +39,7 @@ static int fail_cb_called; static void getaddrinfo_fail_cb(uv_getaddrinfo_t* req, int status, struct addrinfo* res) { + ASSERT(fail_cb_called == 0); ASSERT(status < 0); ASSERT(res == NULL); @@ -81,6 +82,11 @@ static void getaddrinfo_cuncurrent_cb(uv_getaddrinfo_t* handle, TEST_IMPL(getaddrinfo_fail) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + uv_getaddrinfo_t req; ASSERT(UV_EINVAL == uv_getaddrinfo(uv_default_loop(), @@ -127,6 +133,11 @@ TEST_IMPL(getaddrinfo_fail_sync) { TEST_IMPL(getaddrinfo_basic) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + int r; getaddrinfo_handle = (uv_getaddrinfo_t*)malloc(sizeof(uv_getaddrinfo_t)); @@ -168,6 +179,11 @@ TEST_IMPL(getaddrinfo_basic_sync) { TEST_IMPL(getaddrinfo_concurrent) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + int i, r; int* data; diff --git a/deps/uv/test/test-getnameinfo.c b/deps/uv/test/test-getnameinfo.c index 3767ffd9aea5de..eb3264515ce509 100644 --- a/deps/uv/test/test-getnameinfo.c +++ b/deps/uv/test/test-getnameinfo.c @@ -46,6 +46,11 @@ static void getnameinfo_req(uv_getnameinfo_t* handle, TEST_IMPL(getnameinfo_basic_ip4) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + int r; r = uv_ip4_addr(address_ip4, port, &addr4); @@ -87,6 +92,11 @@ TEST_IMPL(getnameinfo_basic_ip4_sync) { TEST_IMPL(getnameinfo_basic_ip6) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + int r; r = uv_ip6_addr(address_ip6, port, &addr6); diff --git a/deps/uv/test/test-getters-setters.c b/deps/uv/test/test-getters-setters.c index 60a1b9264da179..42c9dcaf0cb2c7 100644 --- a/deps/uv/test/test-getters-setters.c +++ b/deps/uv/test/test-getters-setters.c @@ -1,3 +1,24 @@ +/* Copyright libuv project 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 "task.h" #include diff --git a/deps/uv/test/test-list.h b/deps/uv/test/test-list.h index 58e174d1deeedd..52b17a69147aa0 100644 --- a/deps/uv/test/test-list.h +++ b/deps/uv/test/test-list.h @@ -127,6 +127,8 @@ TEST_DECLARE (tcp_bind_writable_flags) TEST_DECLARE (tcp_listen_without_bind) TEST_DECLARE (tcp_connect_error_fault) TEST_DECLARE (tcp_connect_timeout) +TEST_DECLARE (tcp_local_connect_timeout) +TEST_DECLARE (tcp6_local_connect_timeout) TEST_DECLARE (tcp_close_while_connecting) TEST_DECLARE (tcp_close) TEST_DECLARE (tcp_close_reset_accepted) @@ -145,6 +147,7 @@ TEST_DECLARE (tcp_flags) TEST_DECLARE (tcp_write_to_half_open_connection) TEST_DECLARE (tcp_unexpected_read) TEST_DECLARE (tcp_read_stop) +TEST_DECLARE (tcp_read_stop_start) TEST_DECLARE (tcp_bind6_error_addrinuse) TEST_DECLARE (tcp_bind6_error_addrnotavail) TEST_DECLARE (tcp_bind6_error_fault) @@ -162,6 +165,7 @@ TEST_DECLARE (udp_send_and_recv) TEST_DECLARE (udp_send_hang_loop) TEST_DECLARE (udp_send_immediate) TEST_DECLARE (udp_send_unreachable) +TEST_DECLARE (udp_mmsg) TEST_DECLARE (udp_multicast_join) TEST_DECLARE (udp_multicast_join6) TEST_DECLARE (udp_multicast_ttl) @@ -281,6 +285,7 @@ TEST_DECLARE (getnameinfo_basic_ip6) TEST_DECLARE (getsockname_tcp) TEST_DECLARE (getsockname_udp) TEST_DECLARE (gettimeofday) +TEST_DECLARE (test_macros) TEST_DECLARE (fail_always) TEST_DECLARE (pass_always) TEST_DECLARE (socket_buffer_size) @@ -518,12 +523,17 @@ TEST_DECLARE (idna_toascii) TEST_DECLARE (utf8_decode1) TEST_DECLARE (uname) +TEST_DECLARE (metrics_idle_time) +TEST_DECLARE (metrics_idle_time_thread) +TEST_DECLARE (metrics_idle_time_zero) + TASK_LIST_START TEST_ENTRY_CUSTOM (platform_output, 0, 1, 5000) #if 0 TEST_ENTRY (callback_order) #endif + TEST_ENTRY (test_macros) TEST_ENTRY (close_order) TEST_ENTRY (run_once) TEST_ENTRY (run_nowait) @@ -672,6 +682,8 @@ TASK_LIST_START TEST_ENTRY (tcp_listen_without_bind) TEST_ENTRY (tcp_connect_error_fault) TEST_ENTRY (tcp_connect_timeout) + TEST_ENTRY (tcp_local_connect_timeout) + TEST_ENTRY (tcp6_local_connect_timeout) TEST_ENTRY (tcp_close_while_connecting) TEST_ENTRY (tcp_close) TEST_ENTRY (tcp_close_reset_accepted) @@ -693,6 +705,8 @@ TASK_LIST_START TEST_ENTRY (tcp_read_stop) TEST_HELPER (tcp_read_stop, tcp4_echo_server) + TEST_ENTRY (tcp_read_stop_start) + TEST_ENTRY (tcp_bind6_error_addrinuse) TEST_ENTRY (tcp_bind6_error_addrnotavail) TEST_ENTRY (tcp_bind6_error_fault) @@ -716,6 +730,7 @@ TASK_LIST_START TEST_ENTRY (udp_options) TEST_ENTRY (udp_options6) TEST_ENTRY (udp_no_autobind) + TEST_ENTRY (udp_mmsg) TEST_ENTRY (udp_multicast_interface) TEST_ENTRY (udp_multicast_interface6) TEST_ENTRY (udp_multicast_join) @@ -844,7 +859,7 @@ TASK_LIST_START TEST_ENTRY (tmpdir) - TEST_ENTRY_CUSTOM (hrtime, 0, 0, 10000) + TEST_ENTRY_CUSTOM (hrtime, 0, 0, 20000) TEST_ENTRY_CUSTOM (getaddrinfo_fail, 0, 0, 10000) TEST_ENTRY_CUSTOM (getaddrinfo_fail_sync, 0, 0, 10000) @@ -1100,6 +1115,10 @@ TASK_LIST_START TEST_ENTRY (idna_toascii) #endif + TEST_ENTRY (metrics_idle_time) + TEST_ENTRY (metrics_idle_time_thread) + TEST_ENTRY (metrics_idle_time_zero) + #if 0 /* These are for testing the test runner. */ TEST_ENTRY (fail_always) diff --git a/deps/uv/test/test-metrics.c b/deps/uv/test/test-metrics.c new file mode 100644 index 00000000000000..f527494470e920 --- /dev/null +++ b/deps/uv/test/test-metrics.c @@ -0,0 +1,135 @@ +/* Copyright libuv project 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 "task.h" +#include /* memset */ + +#define UV_NS_TO_MS 1000000 + + +static void timer_spin_cb(uv_timer_t* handle) { + uint64_t t; + + (*(int*) handle->data)++; + t = uv_hrtime(); + /* Spin for 500 ms to spin loop time out of the delta check. */ + while (uv_hrtime() - t < 600 * UV_NS_TO_MS) { } +} + + +TEST_IMPL(metrics_idle_time) { + const uint64_t timeout = 1000; + uv_timer_t timer; + uint64_t idle_time; + int cntr; + + cntr = 0; + timer.data = &cntr; + + ASSERT_EQ(0, uv_loop_configure(uv_default_loop(), UV_METRICS_IDLE_TIME)); + ASSERT_EQ(0, uv_timer_init(uv_default_loop(), &timer)); + ASSERT_EQ(0, uv_timer_start(&timer, timer_spin_cb, timeout, 0)); + + ASSERT_EQ(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_GT(cntr, 0); + + idle_time = uv_metrics_idle_time(uv_default_loop()); + + /* Permissive check that the idle time matches within the timeout ±500 ms. */ + ASSERT((idle_time <= (timeout + 500) * UV_NS_TO_MS) && + (idle_time >= (timeout - 500) * UV_NS_TO_MS)); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +static void metrics_routine_cb(void* arg) { + const uint64_t timeout = 1000; + uv_loop_t loop; + uv_timer_t timer; + uint64_t idle_time; + int cntr; + + cntr = 0; + timer.data = &cntr; + + ASSERT_EQ(0, uv_loop_init(&loop)); + ASSERT_EQ(0, uv_loop_configure(&loop, UV_METRICS_IDLE_TIME)); + ASSERT_EQ(0, uv_timer_init(&loop, &timer)); + ASSERT_EQ(0, uv_timer_start(&timer, timer_spin_cb, timeout, 0)); + + ASSERT_EQ(0, uv_run(&loop, UV_RUN_DEFAULT)); + ASSERT_GT(cntr, 0); + + idle_time = uv_metrics_idle_time(&loop); + + /* Only checking that idle time is greater than the lower bound since there + * may have been thread contention, causing the event loop to be delayed in + * the idle phase longer than expected. + */ + ASSERT_GE(idle_time, (timeout - 500) * UV_NS_TO_MS); + + close_loop(&loop); + ASSERT_EQ(0, uv_loop_close(&loop)); +} + + +TEST_IMPL(metrics_idle_time_thread) { + uv_thread_t threads[5]; + int i; + + for (i = 0; i < 5; i++) { + ASSERT_EQ(0, uv_thread_create(&threads[i], metrics_routine_cb, NULL)); + } + + for (i = 0; i < 5; i++) { + uv_thread_join(&threads[i]); + } + + return 0; +} + + +static void timer_noop_cb(uv_timer_t* handle) { + (*(int*) handle->data)++; +} + + +TEST_IMPL(metrics_idle_time_zero) { + uv_timer_t timer; + int cntr; + + cntr = 0; + timer.data = &cntr; + ASSERT_EQ(0, uv_loop_configure(uv_default_loop(), UV_METRICS_IDLE_TIME)); + ASSERT_EQ(0, uv_timer_init(uv_default_loop(), &timer)); + ASSERT_EQ(0, uv_timer_start(&timer, timer_noop_cb, 0, 0)); + + ASSERT_EQ(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT_GT(cntr, 0); + ASSERT_EQ(0, uv_metrics_idle_time(uv_default_loop())); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/deps/uv/test/test-ping-pong.c b/deps/uv/test/test-ping-pong.c index c86a3f4a66592f..7f7758b3b2ebea 100644 --- a/deps/uv/test/test-ping-pong.c +++ b/deps/uv/test/test-ping-pong.c @@ -70,14 +70,14 @@ static void pinger_on_close(uv_handle_t* handle) { } -static void pinger_after_write(uv_write_t *req, int status) { +static void pinger_after_write(uv_write_t* req, int status) { ASSERT(status == 0); free(req); } static void pinger_write_ping(pinger_t* pinger) { - uv_write_t *req; + uv_write_t* req; uv_buf_t bufs[sizeof PING - 1]; int i, nbufs; @@ -112,7 +112,7 @@ static void pinger_read_cb(uv_stream_t* stream, ssize_t i; pinger_t* pinger; - pinger = (pinger_t*)stream->data; + pinger = (pinger_t*) stream->data; if (nread < 0) { ASSERT(nread == UV_EOF); @@ -148,8 +148,8 @@ static void pinger_read_cb(uv_stream_t* stream, } -static void pinger_on_connect(uv_connect_t *req, int status) { - pinger_t *pinger = (pinger_t*)req->handle->data; +static void pinger_on_connect(uv_connect_t* req, int status) { + pinger_t* pinger = (pinger_t*)req->handle->data; pinger_on_connect_count++; @@ -169,10 +169,10 @@ static void pinger_on_connect(uv_connect_t *req, int status) { static void tcp_pinger_v6_new(int vectored_writes) { int r; struct sockaddr_in6 server_addr; - pinger_t *pinger; + pinger_t* pinger; - ASSERT(0 ==uv_ip6_addr("::1", TEST_PORT, &server_addr)); + ASSERT(0 == uv_ip6_addr("::1", TEST_PORT, &server_addr)); pinger = malloc(sizeof(*pinger)); ASSERT(pinger != NULL); pinger->vectored_writes = vectored_writes; @@ -200,7 +200,7 @@ static void tcp_pinger_v6_new(int vectored_writes) { static void tcp_pinger_new(int vectored_writes) { int r; struct sockaddr_in server_addr; - pinger_t *pinger; + pinger_t* pinger; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); pinger = malloc(sizeof(*pinger)); @@ -229,9 +229,9 @@ static void tcp_pinger_new(int vectored_writes) { static void pipe_pinger_new(int vectored_writes) { int r; - pinger_t *pinger; + pinger_t* pinger; - pinger = (pinger_t*)malloc(sizeof(*pinger)); + pinger = malloc(sizeof(*pinger)); ASSERT(pinger != NULL); pinger->vectored_writes = vectored_writes; pinger->state = 0; diff --git a/deps/uv/test/test-pipe-set-non-blocking.c b/deps/uv/test/test-pipe-set-non-blocking.c index fcc9fc0da85e99..626b53f09a2776 100644 --- a/deps/uv/test/test-pipe-set-non-blocking.c +++ b/deps/uv/test/test-pipe-set-non-blocking.c @@ -24,29 +24,35 @@ TEST_IMPL(pipe_set_non_blocking) { #else /* !_WIN32 */ -#include -#include -#include +#include /* memset */ +#include /* close */ #include -#include -#include +#include struct thread_ctx { uv_barrier_t barrier; - int fd; + uv_file fd; }; static void thread_main(void* arg) { struct thread_ctx* ctx; + uv_fs_t req; + uv_buf_t bufs[1]; char buf[4096]; ssize_t n; + int uv_errno; + + bufs[0] = uv_buf_init(buf, sizeof(buf)); ctx = arg; uv_barrier_wait(&ctx->barrier); - do - n = read(ctx->fd, buf, sizeof(buf)); - while (n > 0 || (n == -1 && errno == EINTR)); + uv_sleep(100); /* make sure we are forcing the writer to block a bit */ + do { + uv_errno = uv_fs_read(NULL, &req, ctx->fd, bufs, 1, -1, NULL); + n = req.result; + uv_fs_req_cleanup(&req); + } while (n > 0 || (n == -1 && uv_errno == UV_EINTR)); ASSERT(n == 0); } @@ -58,15 +64,16 @@ TEST_IMPL(pipe_set_non_blocking) { size_t nwritten; char data[4096]; uv_buf_t buf; - int fd[2]; + uv_file fd[2]; int n; ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0)); ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, fd)); - ASSERT(0 == uv_pipe_open(&pipe_handle, fd[0])); + ASSERT(0 == uv_pipe_open(&pipe_handle, fd[1])); ASSERT(0 == uv_stream_set_blocking((uv_stream_t*) &pipe_handle, 1)); + fd[1] = -1; /* fd[1] is owned by pipe_handle now. */ - ctx.fd = fd[1]; + ctx.fd = fd[0]; ASSERT(0 == uv_barrier_init(&ctx.barrier, 2)); ASSERT(0 == uv_thread_create(&thread, thread_main, &ctx)); uv_barrier_wait(&ctx.barrier); @@ -89,7 +96,8 @@ TEST_IMPL(pipe_set_non_blocking) { ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT(0 == uv_thread_join(&thread)); - ASSERT(0 == close(fd[1])); /* fd[0] is closed by uv_close(). */ + ASSERT(0 == close(fd[0])); /* fd[1] is closed by uv_close(). */ + fd[0] = -1; uv_barrier_destroy(&ctx.barrier); MAKE_VALGRIND_HAPPY(); diff --git a/deps/uv/test/test-process-title-threadsafe.c b/deps/uv/test/test-process-title-threadsafe.c index 3b4168be84c6b4..927643cc8c90c9 100644 --- a/deps/uv/test/test-process-title-threadsafe.c +++ b/deps/uv/test/test-process-title-threadsafe.c @@ -39,10 +39,13 @@ static const char* titles[] = { }; static void getter_thread_body(void* arg) { + uv_sem_t* getter_sem; char buffer[512]; size_t len; - for (;;) { + getter_sem = arg; + + while (UV_EAGAIN == uv_sem_trywait(getter_sem)) { ASSERT(0 == uv_get_process_title(buffer, sizeof(buffer))); /* The maximum size of the process title on some platforms depends on @@ -78,6 +81,7 @@ static void setter_thread_body(void* arg) { TEST_IMPL(process_title_threadsafe) { uv_thread_t setter_threads[4]; uv_thread_t getter_thread; + uv_sem_t getter_sem; int i; #if defined(__sun) || defined(__CYGWIN__) || defined(__MSYS__) || \ @@ -86,7 +90,10 @@ TEST_IMPL(process_title_threadsafe) { #endif ASSERT(0 == uv_set_process_title(titles[0])); - ASSERT(0 == uv_thread_create(&getter_thread, getter_thread_body, NULL)); + + ASSERT_EQ(0, uv_sem_init(&getter_sem, 0)); + ASSERT_EQ(0, + uv_thread_create(&getter_thread, getter_thread_body, &getter_sem)); for (i = 0; i < (int) ARRAY_SIZE(setter_threads); i++) ASSERT(0 == uv_thread_create(&setter_threads[i], setter_thread_body, NULL)); @@ -94,5 +101,9 @@ TEST_IMPL(process_title_threadsafe) { for (i = 0; i < (int) ARRAY_SIZE(setter_threads); i++) ASSERT(0 == uv_thread_join(&setter_threads[i])); + uv_sem_post(&getter_sem); + ASSERT_EQ(0, uv_thread_join(&getter_thread)); + uv_sem_destroy(&getter_sem); + return 0; } diff --git a/deps/uv/test/test-spawn.c b/deps/uv/test/test-spawn.c index 5e1c6d613003c0..d1757337a6d468 100644 --- a/deps/uv/test/test-spawn.c +++ b/deps/uv/test/test-spawn.c @@ -71,7 +71,7 @@ static void exit_cb(uv_process_t* process, exit_cb_called++; ASSERT(exit_status == 1); ASSERT(term_signal == 0); - uv_close((uv_handle_t*)process, close_cb); + uv_close((uv_handle_t*) process, close_cb); } @@ -104,7 +104,7 @@ static void kill_cb(uv_process_t* process, #else ASSERT(no_term_signal || term_signal == SIGTERM); #endif - uv_close((uv_handle_t*)process, close_cb); + uv_close((uv_handle_t*) process, close_cb); /* * Sending signum == 0 should check if the @@ -135,7 +135,7 @@ static void on_read(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { output_used += nread; } else if (nread < 0) { ASSERT(nread == UV_EOF); - uv_close((uv_handle_t*)tcp, close_cb); + uv_close((uv_handle_t*) tcp, close_cb); } } @@ -150,7 +150,7 @@ static void on_read_once(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { static void write_cb(uv_write_t* req, int status) { ASSERT(status == 0); - uv_close((uv_handle_t*)req->handle, close_cb); + uv_close((uv_handle_t*) req->handle, close_cb); } @@ -172,8 +172,8 @@ static void init_process_options(char* test, uv_exit_cb exit_cb) { static void timer_cb(uv_timer_t* handle) { - uv_process_kill(&process, /* SIGTERM */ 15); - uv_close((uv_handle_t*)handle, close_cb); + uv_process_kill(&process, SIGTERM); + uv_close((uv_handle_t*) handle, close_cb); } @@ -291,7 +291,7 @@ TEST_IMPL(spawn_stdout) { options.stdio = stdio; options.stdio[0].flags = UV_IGNORE; options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio[1].data.stream = (uv_stream_t*) &out; options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, &options); @@ -595,9 +595,9 @@ TEST_IMPL(spawn_stdin) { uv_pipe_init(uv_default_loop(), &in, 0); options.stdio = stdio; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; - options.stdio[0].data.stream = (uv_stream_t*)∈ + options.stdio[0].data.stream = (uv_stream_t*) ∈ options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio[1].data.stream = (uv_stream_t*) &out; options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, &options); @@ -605,7 +605,7 @@ TEST_IMPL(spawn_stdin) { buf.base = buffer; buf.len = sizeof(buffer); - r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb); + r = uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_cb); ASSERT(r == 0); r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); @@ -636,7 +636,7 @@ TEST_IMPL(spawn_stdio_greater_than_3) { options.stdio[1].flags = UV_IGNORE; options.stdio[2].flags = UV_IGNORE; options.stdio[3].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - options.stdio[3].data.stream = (uv_stream_t*)&pipe; + options.stdio[3].data.stream = (uv_stream_t*) &pipe; options.stdio_count = 4; r = uv_spawn(uv_default_loop(), &process, &options); @@ -677,7 +677,7 @@ int spawn_tcp_server_helper(void) { /* Make sure that we can listen on a socket that was * passed down from the parent process */ - r = uv_listen((uv_stream_t*)&tcp, SOMAXCONN, NULL); + r = uv_listen((uv_stream_t*) &tcp, SOMAXCONN, NULL); ASSERT(r == 0); return 1; @@ -703,10 +703,10 @@ TEST_IMPL(spawn_tcp_server) { r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); #ifdef _WIN32 - r = uv_fileno((uv_handle_t*)&tcp_server, &handle); + r = uv_fileno((uv_handle_t*) &tcp_server, &handle); fd = _open_osfhandle((intptr_t) handle, 0); #else - r = uv_fileno((uv_handle_t*)&tcp_server, &fd); + r = uv_fileno((uv_handle_t*) &tcp_server, &fd); #endif ASSERT(r == 0); ASSERT(fd > 0); @@ -833,7 +833,7 @@ TEST_IMPL(spawn_detached) { r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == 0); - uv_unref((uv_handle_t*)&process); + uv_unref((uv_handle_t*) &process); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); @@ -845,7 +845,7 @@ TEST_IMPL(spawn_detached) { r = uv_kill(process.pid, 0); ASSERT(r == 0); - r = uv_kill(process.pid, 15); + r = uv_kill(process.pid, SIGTERM); ASSERT(r == 0); MAKE_VALGRIND_HAPPY(); @@ -874,11 +874,11 @@ TEST_IMPL(spawn_and_kill_with_std) { options.stdio = stdio; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; - options.stdio[0].data.stream = (uv_stream_t*)∈ + options.stdio[0].data.stream = (uv_stream_t*) ∈ options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio[1].data.stream = (uv_stream_t*) &out; options.stdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - options.stdio[2].data.stream = (uv_stream_t*)&err; + options.stdio[2].data.stream = (uv_stream_t*) &err; options.stdio_count = 3; r = uv_spawn(uv_default_loop(), &process, &options); @@ -925,9 +925,9 @@ TEST_IMPL(spawn_and_ping) { uv_pipe_init(uv_default_loop(), &in, 0); options.stdio = stdio; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; - options.stdio[0].data.stream = (uv_stream_t*)∈ + options.stdio[0].data.stream = (uv_stream_t*) ∈ options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio[1].data.stream = (uv_stream_t*) &out; options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, &options); @@ -939,10 +939,10 @@ TEST_IMPL(spawn_and_ping) { r = uv_process_kill(&process, 0); ASSERT(r == 0); - r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb); + r = uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_cb); ASSERT(r == 0); - r = uv_read_start((uv_stream_t*)&out, on_alloc, on_read); + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); ASSERT(r == 0); ASSERT(exit_cb_called == 0); @@ -972,9 +972,9 @@ TEST_IMPL(spawn_same_stdout_stderr) { uv_pipe_init(uv_default_loop(), &in, 0); options.stdio = stdio; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; - options.stdio[0].data.stream = (uv_stream_t*)∈ + options.stdio[0].data.stream = (uv_stream_t*) ∈ options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio[1].data.stream = (uv_stream_t*) &out; options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, &options); @@ -986,10 +986,10 @@ TEST_IMPL(spawn_same_stdout_stderr) { r = uv_process_kill(&process, 0); ASSERT(r == 0); - r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb); + r = uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_cb); ASSERT(r == 0); - r = uv_read_start((uv_stream_t*)&out, on_alloc, on_read); + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); ASSERT(r == 0); ASSERT(exit_cb_called == 0); @@ -1077,7 +1077,7 @@ TEST_IMPL(kill) { ASSERT(r == 0); /* Kill the process. */ - r = uv_kill(process.pid, /* SIGTERM */ 15); + r = uv_kill(process.pid, SIGTERM); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); @@ -1105,7 +1105,7 @@ TEST_IMPL(spawn_detect_pipe_name_collisions_on_windows) { options.stdio = stdio; options.stdio[0].flags = UV_IGNORE; options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio[1].data.stream = (uv_stream_t*) &out; options.stdio_count = 2; /* Create a pipe that'll cause a collision. */ @@ -1821,32 +1821,32 @@ TEST_IMPL(spawn_inherit_streams) { ASSERT(uv_is_writable((uv_stream_t*) &pipe_stdout_parent) == bidir); child_stdio[0].flags = UV_INHERIT_STREAM; - child_stdio[0].data.stream = (uv_stream_t *)&pipe_stdin_child; + child_stdio[0].data.stream = (uv_stream_t *) &pipe_stdin_child; child_stdio[1].flags = UV_INHERIT_STREAM; - child_stdio[1].data.stream = (uv_stream_t *)&pipe_stdout_child; + child_stdio[1].data.stream = (uv_stream_t *) &pipe_stdout_child; options.stdio = child_stdio; options.stdio_count = 2; ASSERT(uv_spawn(loop, &child_req, &options) == 0); - uv_close((uv_handle_t*)&pipe_stdin_child, NULL); - uv_close((uv_handle_t*)&pipe_stdout_child, NULL); + uv_close((uv_handle_t*) &pipe_stdin_child, NULL); + uv_close((uv_handle_t*) &pipe_stdout_child, NULL); - buf = uv_buf_init((char*)ubuf, sizeof ubuf); + buf = uv_buf_init((char*) ubuf, sizeof ubuf); for (i = 0; i < sizeof ubuf; ++i) ubuf[i] = i & 255u; memset(output, 0, sizeof ubuf); r = uv_write(&write_req, - (uv_stream_t*)&pipe_stdin_parent, + (uv_stream_t*) &pipe_stdin_parent, &buf, 1, write_cb); ASSERT(r == 0); - r = uv_read_start((uv_stream_t*)&pipe_stdout_parent, on_alloc, on_read); + r = uv_read_start((uv_stream_t*) &pipe_stdout_parent, on_alloc, on_read); ASSERT(r == 0); r = uv_run(loop, UV_RUN_DEFAULT); diff --git a/deps/uv/test/test-tcp-connect-timeout.c b/deps/uv/test/test-tcp-connect-timeout.c index 081424b80023cf..6b455276c516b1 100644 --- a/deps/uv/test/test-tcp-connect-timeout.c +++ b/deps/uv/test/test-tcp-connect-timeout.c @@ -89,3 +89,108 @@ TEST_IMPL(tcp_connect_timeout) { MAKE_VALGRIND_HAPPY(); return 0; } + +/* Make sure connect fails instantly if the target is nonexisting + * local port. + */ + +static void connect_local_cb(uv_connect_t* req, int status) { + ASSERT_PTR_EQ(req, &connect_req); + ASSERT_NE(status, UV_ECANCELED); + connect_cb_called++; +} + +static int is_supported_system() { + int semver[3]; + int min_semver[3] = {10, 0, 16299}; + int cnt; + uv_utsname_t uname; + ASSERT_EQ(uv_os_uname(&uname), 0); + if (strcmp(uname.sysname, "Windows_NT") == 0) { + cnt = sscanf(uname.release, "%d.%d.%d", &semver[0], &semver[1], &semver[2]); + if (cnt != 3) { + return 0; + } + // relase >= 10.0.16299 + for (cnt = 0; cnt < 3; ++cnt) { + if (semver[cnt] > min_semver[cnt]) + return 1; + if (semver[cnt] < min_semver[cnt]) + return 0; + } + return 1; + } + return 1; +} + +TEST_IMPL(tcp_local_connect_timeout) { + struct sockaddr_in addr; + int r; + + if (!is_supported_system()) { + RETURN_SKIP("Unsupported system"); + } + ASSERT_EQ(0, uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_timer_init(uv_default_loop(), &timer); + ASSERT_EQ(r, 0); + + /* Give it 1s to timeout. */ + r = uv_timer_start(&timer, timer_cb, 1000, 0); + ASSERT_EQ(r, 0); + + r = uv_tcp_init(uv_default_loop(), &conn); + ASSERT_EQ(r, 0); + + r = uv_tcp_connect(&connect_req, + &conn, + (const struct sockaddr*) &addr, + connect_local_cb); + if (r == UV_ENETUNREACH) + RETURN_SKIP("Network unreachable."); + ASSERT_EQ(r, 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +TEST_IMPL(tcp6_local_connect_timeout) { + struct sockaddr_in6 addr; + int r; + + if (!is_supported_system()) { + RETURN_SKIP("Unsupported system"); + } + if (!can_ipv6()) { + RETURN_SKIP("IPv6 not supported"); + } + + ASSERT_EQ(0, uv_ip6_addr("::1", 9999, &addr)); + + r = uv_timer_init(uv_default_loop(), &timer); + ASSERT_EQ(r, 0); + + /* Give it 1s to timeout. */ + r = uv_timer_start(&timer, timer_cb, 1000, 0); + ASSERT_EQ(r, 0); + + r = uv_tcp_init(uv_default_loop(), &conn); + ASSERT_EQ(r, 0); + + r = uv_tcp_connect(&connect_req, + &conn, + (const struct sockaddr*) &addr, + connect_local_cb); + if (r == UV_ENETUNREACH) + RETURN_SKIP("Network unreachable."); + ASSERT_EQ(r, 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT_EQ(r, 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/deps/uv/test/test-tcp-read-stop-start.c b/deps/uv/test/test-tcp-read-stop-start.c new file mode 100644 index 00000000000000..9bccbc12fc58e6 --- /dev/null +++ b/deps/uv/test/test-tcp-read-stop-start.c @@ -0,0 +1,136 @@ +/* Copyright libuv project 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 "task.h" + +static uv_tcp_t server; +static uv_tcp_t connection; +static int read_cb_called = 0; + +static uv_tcp_t client; +static uv_connect_t connect_req; + + +static void on_read2(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf); + +static void on_write_close_immediately(uv_write_t* req, int status) { + ASSERT(0 == status); + + uv_close((uv_handle_t*)req->handle, NULL); /* Close immediately */ + free(req); +} + +static void on_write(uv_write_t* req, int status) { + ASSERT(0 == status); + + free(req); +} + +static void do_write(uv_stream_t* stream, uv_write_cb cb) { + uv_write_t* req = malloc(sizeof(*req)); + uv_buf_t buf; + buf.base = "1234578"; + buf.len = 8; + ASSERT(0 == uv_write(req, stream, &buf, 1, cb)); +} + +static void on_alloc(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[65536]; + buf->base = slab; + buf->len = sizeof(slab); +} + +static void on_read1(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { + ASSERT(nread >= 0); + + /* Do write on a half open connection to force WSAECONNABORTED (on Windows) + * in the subsequent uv_read_start() + */ + do_write(stream, on_write); + + ASSERT(0 == uv_read_stop(stream)); + + ASSERT(0 == uv_read_start(stream, on_alloc, on_read2)); + + read_cb_called++; +} + +static void on_read2(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { + ASSERT(nread < 0); + + uv_close((uv_handle_t*)stream, NULL); + uv_close((uv_handle_t*)&server, NULL); + + read_cb_called++; +} + +static void on_connection(uv_stream_t* server, int status) { + ASSERT(0 == status); + + ASSERT(0 == uv_tcp_init(server->loop, &connection)); + + ASSERT(0 == uv_accept(server, (uv_stream_t* )&connection)); + + ASSERT(0 == uv_read_start((uv_stream_t*)&connection, on_alloc, on_read1)); +} + +static void on_connect(uv_connect_t* req, int status) { + ASSERT(0 == status); + + do_write((uv_stream_t*)&client, on_write_close_immediately); +} + +TEST_IMPL(tcp_read_stop_start) { + uv_loop_t* loop = uv_default_loop(); + + { /* Server */ + struct sockaddr_in addr; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + ASSERT(0 == uv_tcp_init(loop, &server)); + + ASSERT(0 == uv_tcp_bind(&server, (struct sockaddr*) & addr, 0)); + + ASSERT(0 == uv_listen((uv_stream_t*)&server, 10, on_connection)); + } + + { /* Client */ + struct sockaddr_in addr; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + ASSERT(0 == uv_tcp_init(loop, &client)); + + ASSERT(0 == uv_tcp_connect(&connect_req, &client, + (const struct sockaddr*) & addr, on_connect)); + } + + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + + ASSERT(read_cb_called >= 2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/deps/uv/test/test-test-macros.c b/deps/uv/test/test-test-macros.c new file mode 100644 index 00000000000000..72a3992db5d508 --- /dev/null +++ b/deps/uv/test/test-test-macros.c @@ -0,0 +1,42 @@ +/* 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 "task.h" + +int test_macros_evil(void) { + static int x; + return x++; +} + + +TEST_IMPL(test_macros) { + char* a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + char* b = "ABCDEFGHIJKLMNOPQRSTUVWXYz"; + char* c = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + int i; + + i = test_macros_evil(); + ASSERT_STR_NE(a, b); + ASSERT_STR_EQ(a, c); + ASSERT_EQ(i + 1, test_macros_evil()); + ASSERT_EQ(i + 2, test_macros_evil()); + return 0; +} diff --git a/deps/uv/test/test-thread.c b/deps/uv/test/test-thread.c index f53bce0601cea9..432c243999fb15 100644 --- a/deps/uv/test/test-thread.c +++ b/deps/uv/test/test-thread.c @@ -167,6 +167,11 @@ TEST_IMPL(thread_create) { * that each "finished" callback is run in its originating thread. */ TEST_IMPL(threadpool_multiple_event_loops) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + struct test_thread threads[8]; size_t i; int r; diff --git a/deps/uv/test/test-tty.c b/deps/uv/test/test-tty.c index 0c6548f95f38c8..a9d38f22117afd 100644 --- a/deps/uv/test/test-tty.c +++ b/deps/uv/test/test-tty.c @@ -422,6 +422,11 @@ TEST_IMPL(tty_file) { } TEST_IMPL(tty_pty) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + #if defined(__APPLE__) || \ defined(__DragonFly__) || \ defined(__FreeBSD__) || \ diff --git a/deps/uv/test/test-udp-mmsg.c b/deps/uv/test/test-udp-mmsg.c new file mode 100644 index 00000000000000..94e8d5b82b9246 --- /dev/null +++ b/deps/uv/test/test-udp-mmsg.c @@ -0,0 +1,136 @@ +/* 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 "task.h" + +#include +#include +#include + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &recver || (uv_udp_t*)(handle) == &sender) + +#define BUFFER_MULTIPLIER 4 +#define MAX_DGRAM_SIZE (64 * 1024) +#define NUM_SENDS 8 +#define EXPECTED_MMSG_ALLOCS (NUM_SENDS / BUFFER_MULTIPLIER) + +static uv_udp_t recver; +static uv_udp_t sender; +static int recv_cb_called; +static int close_cb_called; +static int alloc_cb_called; + + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + size_t buffer_size; + CHECK_HANDLE(handle); + + /* Only allocate enough room for multiple dgrams if we can actually recv them */ + buffer_size = MAX_DGRAM_SIZE; + if (uv_udp_using_recvmmsg((uv_udp_t*)handle)) + buffer_size *= BUFFER_MULTIPLIER; + + /* Actually malloc to exercise free'ing the buffer later */ + buf->base = malloc(buffer_size); + ASSERT(buf->base != NULL); + buf->len = buffer_size; + alloc_cb_called++; +} + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + ASSERT(uv_is_closing(handle)); + close_cb_called++; +} + + +static void recv_cb(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* rcvbuf, + const struct sockaddr* addr, + unsigned flags) { + ASSERT_GE(nread, 0); + + if (nread > 0) { + ASSERT_EQ(nread, 4); + ASSERT(addr != NULL); + ASSERT_MEM_EQ("PING", rcvbuf->base, nread); + + recv_cb_called++; + if (recv_cb_called == NUM_SENDS) { + uv_close((uv_handle_t*)handle, close_cb); + uv_close((uv_handle_t*)&sender, close_cb); + } + } + + /* Don't free if the buffer could be reused via mmsg */ + if (rcvbuf && !(flags & UV_UDP_MMSG_CHUNK)) + free(rcvbuf->base); +} + + +TEST_IMPL(udp_mmsg) { + struct sockaddr_in addr; + uv_buf_t buf; + int i; + + ASSERT_EQ(0, uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + ASSERT_EQ(0, uv_udp_init_ex(uv_default_loop(), &recver, + AF_UNSPEC | UV_UDP_RECVMMSG)); + + ASSERT_EQ(0, uv_udp_bind(&recver, (const struct sockaddr*) &addr, 0)); + + ASSERT_EQ(0, uv_udp_recv_start(&recver, alloc_cb, recv_cb)); + + ASSERT_EQ(0, uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + ASSERT_EQ(0, uv_udp_init(uv_default_loop(), &sender)); + + buf = uv_buf_init("PING", 4); + for (i = 0; i < NUM_SENDS; i++) { + ASSERT_EQ(4, uv_udp_try_send(&sender, &buf, 1, (const struct sockaddr*) &addr)); + } + + ASSERT_EQ(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT_EQ(close_cb_called, 2); + ASSERT_EQ(recv_cb_called, NUM_SENDS); + + ASSERT_EQ(sender.send_queue_size, 0); + ASSERT_EQ(recver.send_queue_size, 0); + + printf("%d allocs for %d recvs\n", alloc_cb_called, recv_cb_called); + + /* On platforms that don't support mmsg, each recv gets its own alloc */ + if (uv_udp_using_recvmmsg(&recver)) + ASSERT_EQ(alloc_cb_called, EXPECTED_MMSG_ALLOCS); + else + ASSERT_EQ(alloc_cb_called, recv_cb_called); + + MAKE_VALGRIND_HAPPY(); + return 0; +}