deps: update libuv to 1.49.1

Refs: https://github.com/libuv/libuv/releases/tag/v1.49.1
PR-URL: https://github.com/nodejs/node/pull/55114
Refs: https://github.com/libuv/libuv/releases/tag/v1.49.0
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
This commit is contained in:
Santiago Gimeno 2024-10-11 09:30:43 +02:00 committed by Node.js GitHub Bot
parent b57977909a
commit ecedcba357
89 changed files with 3597 additions and 1302 deletions

7
deps/uv/.mailmap vendored
View File

@ -4,6 +4,7 @@ Aaron Bieber <qbit@deftly.net> <deftly@gmail.com>
Alan Gutierrez <alan@prettyrobots.com> <alan@blogometer.com>
Andrius Bentkus <andrius.bentkus@gmail.com> <toxedvirus@gmail.com>
Andy Fiddaman <andy@omniosce.org> <omnios@citrus-it.co.uk>
Andy Pan <panjf2000@gmail.com> <i@andypan.me>
Bert Belder <bertbelder@gmail.com> <i@bertbelder.com>
Bert Belder <bertbelder@gmail.com> <info@2bs.nl>
Bert Belder <bertbelder@gmail.com> <user@ChrUbuntu.(none)>
@ -18,6 +19,7 @@ David Carlier <devnexen@gmail.com>
Devchandra Meetei Leishangthem <dlmeetei@gmail.com>
Fedor Indutny <fedor.indutny@gmail.com> <fedor@indutny.com>
Frank Denis <github@pureftpd.org>
Hüseyin Açacak <110401522+huseyinacacak-janea@users.noreply.github.com> <huseyin@janeasystems.com>
Imran Iqbal <imrani@ca.ibm.com> <imran@imraniqbal.org>
Isaac Z. Schlueter <i@izs.me>
Jason Williams <necmon@yahoo.com>
@ -37,6 +39,7 @@ Michael Neumann <mneumann@think.localnet> <mneumann@ntecs.de>
Michael Penick <michael.penick@datastax.com> <mpenick@users.noreply.github.com>
Nicholas Vavilov <vvnicholas@gmail.com>
Nick Logan <ugexe@cpan.org> <nlogan@gmail.com>
Olivier Valentin <ovalenti@redhat.com> <valentio@free.fr>
Rasmus Christian Pedersen <zerhacken@yahoo.com>
Rasmus Christian Pedersen <zerhacken@yahoo.com> <ruysch@outlook.com>
Richard Lau <rlau@redhat.com> <riclau@uk.ibm.com>
@ -47,8 +50,8 @@ Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
Sam Roberts <vieuxtech@gmail.com> <sam@strongloop.com>
San-Tai Hsu <vanilla@fatpipi.com>
Santiago Gimeno <santiago.gimeno@quantion.es> <santiago.gimeno@gmail.com>
Saúl Ibarra Corretgé <saghul@gmail.com>
Saúl Ibarra Corretgé <saghul@gmail.com> <s@saghul.net>
Saúl Ibarra Corretgé <s@saghul.net>
Saúl Ibarra Corretgé <s@saghul.net> <saghul@gmail.com>
Shigeki Ohtsu <ohtsu@iij.ad.jp> <ohtsu@ohtsu.org>
Shuowang (Wayne) Zhang <shuowang.zhang@ibm.com>
TK-one <tk5641@naver.com>

21
deps/uv/AUTHORS vendored
View File

@ -567,3 +567,24 @@ Ardi Nugraha <33378542+ardi-nugraha@users.noreply.github.com>
Anton Bachin <antonbachin@yahoo.com>
Trevor Flynn <trevorflynn@liquidcrystalstudios.com>
Andy Pan <panjf2000@gmail.com>
Viacheslav Muravyev <slavamuravey@mail.ru>
Anthony Alayo <anthony.alayo@gmail.com>
Thomas Walter <31201229+waltoss@users.noreply.github.com>
hiiizxf <385122613@qq.com>
Geddy <guandichao@163.com>
Farzin Monsef <monseffarzin@gmail.com>
tgolang <154592711+tgolang@users.noreply.github.com>
josedelinux <josedelinux@hotmail.com>
Hüseyin Açacak <110401522+huseyinacacak-janea@users.noreply.github.com>
Uilian Ries <uilianries@gmail.com>
Olivier Valentin <ovalenti@redhat.com>
郑苏波 (Super Zheng) <superzheng@tencent.com>
zeertzjq <zeertzjq@outlook.com>
Ian Butterworth <i.r.butterworth@gmail.com>
握猫猫 <164346864@qq.com>
Zuohui Yang <274048862@qq.com>
Edigleysson Silva (Edy) <edigleyssonsilva@gmail.com>
Raihaan Shouhell <raihaanhimself@gmail.com>
Rialbat <miha-wead@mail.ru>
Adam <adam@NetBSD.org>
Poul T Lomholt <ptlomholt@users.noreply.github.com>

View File

@ -81,15 +81,20 @@ if(TSAN)
endif()
if(UBSAN)
cmake_minimum_required(VERSION 3.13)
list(APPEND uv_defines __UBSAN__=1)
if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|GNU|Clang")
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined")
set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined")
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined")
elseif(MSVC)
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=undefined")
add_compile_options("-fsanitize=undefined" "-fno-sanitize-recover=undefined")
if (NOT WIN32)
add_link_options("-fsanitize=undefined")
endif()
if(MSVC)
add_compile_options("/Oy-")
else()
add_compile_options("-fno-omit-frame-pointer")
endif()
else()
message(SEND_ERROR "UndefinedBehaviorSanitizer support requires clang, gcc, or msvc. Try again with -DCMAKE_C_COMPILER.")
message(SEND_ERROR "UndefinedBehaviorSanitizer support requires clang or gcc. Try again with -DCMAKE_C_COMPILER.")
endif()
endif()
@ -307,6 +312,7 @@ if(APPLE)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "GNU")
list(APPEND uv_defines _GNU_SOURCE _POSIX_C_SOURCE=200112 _XOPEN_SOURCE=500)
list(APPEND uv_libraries dl)
list(APPEND uv_sources
src/unix/bsd-ifaddrs.c
@ -566,6 +572,7 @@ if(LIBUV_BUILD_TESTS)
test/test-hrtime.c
test/test-idle.c
test/test-idna.c
test/test-iouring-pollhup.c
test/test-ip4-addr.c
test/test-ip6-addr.c
test/test-ip-name.c
@ -643,6 +650,7 @@ if(LIBUV_BUILD_TESTS)
test/test-tcp-oob.c
test/test-tcp-open.c
test/test-tcp-read-stop.c
test/test-tcp-reuseport.c
test/test-tcp-read-stop-start.c
test/test-tcp-rst.c
test/test-tcp-shutdown-after-write.c
@ -691,6 +699,7 @@ if(LIBUV_BUILD_TESTS)
test/test-udp-send-unreachable.c
test/test-udp-try-send.c
test/test-udp-recv-in-a-row.c
test/test-udp-reuseport.c
test/test-uname.c
test/test-walk-handles.c
test/test-watcher-cross-stop.c)
@ -787,6 +796,14 @@ if(MSVC)
endif()
endif()
if(BUILD_SHARED_LIBS)
set(LIB_SELECTED uv)
else()
set(LIB_SELECTED uv_a)
endif()
add_library(libuv::libuv ALIAS ${LIB_SELECTED})
message(STATUS "summary of build options:
Install prefix: ${CMAKE_INSTALL_PREFIX}
Target system: ${CMAKE_SYSTEM_NAME}

241
deps/uv/ChangeLog vendored
View File

@ -1,4 +1,237 @@
2024.02.07, Version 1.48.0 (Stable)
2024.10.11, Version 1.49.1 (Stable)
Changes since version 1.49.0:
* build: add darwin-syscalls.h to release tarball (Ben Noordhuis)
* linux: use IORING_SETUP_NO_SQARRAY when available (Ben Noordhuis)
* linux: use IORING_OP_FTRUNCATE when available (Ben Noordhuis)
* win: fix pNtQueryDirectoryFile check (Rialbat)
* win: fix WriteFile() error translation (Santiago Gimeno)
* win,fs: uv_fs_rmdir() to return ENOENT on file (Santiago Gimeno)
* win,pipe: ipc code does not support async read (Jameson Nash)
* netbsd: fix build (Adam)
* win,fs: fix bug in fs__readdir (Hüseyin Açacak)
* unix: workaround gcc bug on armv7 (Santiago Gimeno)
* unix: work around arm-linux-gnueabihf-gcc bug (Ben Noordhuis)
* unix: fix uv_tcp_keepalive in smartOS (Santiago Gimeno)
* unix: fix uv_getrusage ru_maxrss on solaris (Poul T Lomholt)
2024.09.25, Version 1.49.0 (Stable), d2e56a5e8d3e39947b78405ca6e4727c70f5568a
Changes since version 1.48.0:
* test: fix -Wpointer-to-int-cast on 32 bits systems (Ben Noordhuis)
* build: add alias for libuv to CMakeLists.txt (Anthony Alayo)
* linux: create io_uring sqpoll ring lazily (Ben Noordhuis)
* misc: run sample CI when code changes (Jameson Nash)
* linux: fix uv_available_parallelism using cgroup (Thomas Walter)
* doc: fix tty example segfault (hiiizxf)
* udp,unix: fix sendmsg use-after-free (Geddy)
* cygwin: implement uv_resident_set_memory (Farzin Monsef)
* win: almost fix race detecting ESRCH in uv_kill (Santiago Gimeno)
* test: disable env var test under win32+asan (Ben Noordhuis)
* unix,fs: fix realpath calls that use the system allocator (Saúl Ibarra
Corretgé)
* sunos: sync tcp keep-alive with other unices (Andy Pan)
* linux: fix /proc/self/stat executable name parsing (Farzin Monsef)
* test,ci: fix [AM]San, disable ASLR (Ben Noordhuis)
* win: remove _alloca usage (Ben Noordhuis)
* unix: reinstate preadv/pwritev fallback code (Ben Noordhuis)
* linux: don't delay EPOLL_CTL_DEL operations (Ben Noordhuis)
* doc: fix typos in ChangeLog (tgolang)
* unix,win: error on zero delay tcp keepalive (Saúl Ibarra Corretgé)
* win: simplify uv_once implementation (Saúl Ibarra Corretgé)
* doc: correct udp socket options documentation (Ben Noordhuis)
* linux: don't use sendmmsg() for single datagrams (Ben Noordhuis)
* unix: fix fd leaks in SCM_RIGHTS error path (Ben Noordhuis)
* win: robustify uv_os_getenv() error checking (Ben Noordhuis)
* test: use newer ASSERT_MEM_EQ macro (Ben Noordhuis)
* unix: de-duplicate conditions for using kqueue (Brad King)
* darwin: simplify uv_hrtime (Saúl Ibarra Corretgé)
* mailmap: update saghul's main email address (Saúl Ibarra Corretgé)
* win: remove no longer needed define (Saúl Ibarra Corretgé)
* doc: fix some typos (josedelinux)
* linux,darwin: make `uv_fs_copyfile` behaves like `cp -r` (Juan José Arboleda)
* dragonfly: disable SO_REUSEPORT for UDP socket bindings (Andy Pan)
* test: remove the obsolete HAVE_KQUEUE macro (Andy Pan)
* unix: use the presence of SOCK_* instead of OS macros for socketpair (Andy
Pan)
* bsd: support pipe2() on *BSD (Andy Pan)
* unix: support SO_REUSEPORT with load balancing for TCP (Andy Pan)
* doc: add entries for extended getpw (Juan José Arboleda)
* test: fix the flaky test-tcp-reuseport (Andy Pan)
* aix,ibmi: fix compilation errors in fs_copyfile (Jeffrey H. Johnson)
* unix: support SO_REUSEPORT with load balancing for UDP (Andy Pan)
* tcpkeepalive: distinguish OS versions and use proper time units (Andy Pan)
* win: map ERROR_BAD_EXE_FORMAT to UV_EFTYPE (Hüseyin Açacak)
* doc: add instruction how to install with Conan (Uilian Ries)
* unix,win: remove unused req parameter from macros (Viacheslav Muravyev)
* build: fix android ci build (Ben Noordhuis)
* unix,win: export wtf8 functions properly (Ben Noordhuis)
* hurd: add includes and macro prerequisites (Olivier Valentin)
* hurd: stub uv_thread_setpriority() (Olivier Valentin)
* ci: use macOS 12 for macOS and iOS builds (Saúl Ibarra Corretgé)
* darwin: fix crash on iOS(arm64) (郑苏波 (Super Zheng))
* Create dependabot.yml for updating github-actions (Jameson Nash)
* doc: correct names of Win32 APIs in fs.rst (zeertzjq)
* ci: bump upload and download-artifact versions (dependabot[bot])
* ci: bump actions/setup-python from 4 to 5 (dependabot[bot])
* ci: bump KyleMayes/install-llvm-action from 1 to 2 (dependabot[bot])
* win,error: remap ERROR_NO_DATA to EAGAIN (Jameson Nash)
* test: handle zero-length udp datagram (Ben Noordhuis)
* misc: remove splay trees macros (Viacheslav Muravyev)
* test,openbsd: remove superfluous ifdef guard (Ben Noordhuis)
* win,fs: use posix delete semantics, if supported (Ian Butterworth)
* win: fix env var in uv_os_homedir and uv_os_tmpdir (Hüseyin Açacak)
* fsevents: detect watched directory removal (Santiago Gimeno)
* ci: bump actions/checkout to 4 (dependabot[bot])
* linux: eliminate a read on eventfd per wakeup (Andy Pan)
* test: pipe_overlong_path handle ENAMETOOLONG (Abdirahim Musse)
* win,fs: use the new Windows fast stat API (Hüseyin Açacak)
* win,pipe: fix race with concurrent readers (Jameson Nash)
* win,signal: fix data race dispatching SIGWINCH (Jameson Nash)
* build: ubsan fixes (Matheus Izvekov)
* linux: disable SQPOLL io_uring by default (Santiago Gimeno)
* win: fix fs.c ubsan failure (Matheus Izvekov)
* test: rmdir can return `EEXIST` or `ENOTEMPTY` (Richard Lau)
* test: check for `UV_CHANGE` or `UV_RENAME` event (Richard Lau)
* unix,fs: silence -Wunused-result warning (Santiago Gimeno)
* linux: support abstract unix socket autobinding (Ben Noordhuis)
* kqueue: use EVFILT_USER for async if available (Andy Pan)
* win: remove deprecated GetVersionExW call (Shelley Vohr)
* doc: document uv_loop_option (握猫猫)
* doc: fix the `uv_*_set_data` series of functions (握猫猫)
* doc: properly label enumerations and types (握猫猫)
* doc: document specific macOS fs_event behavior (Santiago Gimeno)
* win,pipe: restore fallback handling for blocking pipes (Jameson Nash)
* unix,win: remove unused rb-tree macro parameters (Viacheslav Muravyev)
* win: compute parallelism from process cpu affinity (Ben Noordhuis)
* win: use NtQueryInformationProcess in uv_os_getppid (Zuohui Yang)
* win,pipe: fix missing assignment to success (Jameson Nash)
* win: fix uv_available_parallelism on win32 (Ben Noordhuis)
* win,pipe: fix another missing assignment to success (Jameson Nash)
* kqueue: disallow ill-suited file descriptor kinds (Andy Pan)
* unix: restore tty attributes on handle close (Ben Noordhuis)
* test: delete test with invalid assumption (Ben Noordhuis)
* dragonflybsd: fix compilation failure (Jeffrey H. Johnson)
* test: run android tests on ci (Edigleysson Silva (Edy))
* darwin: add udp mmsg support (Raihaan Shouhell)
* unix: work around arm-linux-gnueabihf-gcc bug (Ben Noordhuis)
* unix: expand uv_available_parallelism() to support more platforms (Ondřej
Surý)
* doc: add known issue in armv7 (Santiago Gimeno)
2024.02.07, Version 1.48.0 (Stable), e9f29cb984231524e3931aa0ae2c5dae1a32884e
Changes since version 1.47.0:
@ -911,7 +1144,7 @@ Changes since version 1.41.0:
* zos: treat __rfim_utok as binary (Shuowang (Wayne) Zhang)
* zos: use execvpe() to set environ explictly (Shuowang (Wayne) Zhang)
* zos: use execvpe() to set environ explicitly (Shuowang (Wayne) Zhang)
* zos: use custom proctitle implementation (Shuowang (Wayne) Zhang)
@ -3417,7 +3650,7 @@ Changes since version 1.9.1:
* zos: implement uv__io_check_fd (John Barboza)
* unix: unneccessary use const qualifier in container_of (John Barboza)
* unix: unnecessary use const qualifier in container_of (John Barboza)
* win,tty: add support for ANSI codes in win10 v1511 (Imran Iqbal)
@ -5520,7 +5753,7 @@ Changes since version 0.11.8:
is an int64_t, and no longer an int. (Bert Belder)
* process: make uv_spawn() return some types of errors immediately on windows,
instead of passing the error code the the exit callback. This brings it on
instead of passing the error code the exit callback. This brings it on
par with libuv's behavior on unix. (Bert Belder)

4
deps/uv/Makefile.am vendored
View File

@ -198,6 +198,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
test/test-hrtime.c \
test/test-idle.c \
test/test-idna.c \
test/test-iouring-pollhup.c \
test/test-ip4-addr.c \
test/test-ip6-addr.c \
test/test-ip-name.c \
@ -275,6 +276,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-reuseport.c \
test/test-tcp-read-stop-start.c \
test/test-tcp-rst.c \
test/test-tcp-shutdown-after-write.c \
@ -324,6 +326,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
test/test-udp-send-unreachable.c \
test/test-udp-try-send.c \
test/test-udp-recv-in-a-row.c \
test/test-udp-reuseport.c \
test/test-uname.c \
test/test-walk-handles.c \
test/test-watcher-cross-stop.c
@ -427,6 +430,7 @@ libuv_la_CFLAGS += -D_DARWIN_UNLIMITED_SELECT=1
libuv_la_SOURCES += src/unix/bsd-ifaddrs.c \
src/unix/darwin-proctitle.c \
src/unix/darwin-stub.h \
src/unix/darwin-syscalls.h \
src/unix/darwin.c \
src/unix/fsevents.c \
src/unix/kqueue.c \

12
deps/uv/README.md vendored
View File

@ -232,6 +232,18 @@ $ ./bootstrap-vcpkg.sh # for bash
$ ./vcpkg install libuv
```
### Install with Conan
You can install pre-built binaries for libuv or build it from source using [Conan](https://conan.io/). Use the following command:
```bash
conan install --requires="libuv/[*]" --build=missing
```
The libuv Conan recipe is kept up to date by Conan maintainers and community contributors.
If the version is out of date, please [create an issue or pull request](https://github.com/conan-io/conan-center-index) on the ConanCenterIndex repository.
### Running tests
Some tests are timing sensitive. Relaxing test timeouts may be necessary

View File

@ -13,7 +13,7 @@
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
AC_PREREQ(2.57)
AC_INIT([libuv], [1.48.0], [https://github.com/libuv/libuv/issues])
AC_INIT([libuv], [1.49.1], [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])

View File

@ -16,7 +16,10 @@ Starting with libuv v1.45.0, some file operations on Linux are handed off to
`io_uring <https://en.wikipedia.org/wiki/Io_uring>` when possible. Apart from
a (sometimes significant) increase in throughput there should be no change in
observable behavior. Libuv reverts to using its threadpool when the necessary
kernel features are unavailable or unsuitable.
kernel features are unavailable or unsuitable. Starting with libuv v1.49.0 this
behavior was reverted and Libuv on Linux by default will be using the threadpool
again. In order to enable io_uring the :c:type:`uv_loop_t` instance must be
configured with the :c:type:`UV_LOOP_ENABLE_IO_URING_SQPOLL` option.
.. note::
On Windows `uv_fs_*` functions use utf-8 encoding.
@ -129,10 +132,9 @@ Data types
uint64_t f_spare[4];
} uv_statfs_t;
.. c:enum:: uv_dirent_t
.. c:enum:: uv_dirent_type_t
Cross platform (reduced) equivalent of ``struct dirent``.
Used in :c:func:`uv_fs_scandir_next`.
Type of dirent.
::
@ -147,6 +149,14 @@ Data types
UV_DIRENT_BLOCK
} uv_dirent_type_t;
.. c:type:: uv_dirent_t
Cross platform (reduced) equivalent of ``struct dirent``.
Used in :c:func:`uv_fs_scandir_next`.
::
typedef struct uv_dirent_s {
const char* name;
uv_dirent_type_t type;
@ -454,7 +464,7 @@ API
.. c:function:: int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb)
Equivalent to :man:`realpath(3)` on Unix. Windows uses `GetFinalPathNameByHandle <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlea>`_.
Equivalent to :man:`realpath(3)` on Unix. Windows uses `GetFinalPathNameByHandleW <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew>`_.
The resulting string is stored in `req->ptr`.
.. warning::
@ -653,7 +663,7 @@ File open constants
.. note::
`UV_FS_O_RANDOM` is only supported on Windows via
`FILE_FLAG_RANDOM_ACCESS <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea>`_.
`FILE_FLAG_RANDOM_ACCESS <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew>`_.
.. c:macro:: UV_FS_O_RDONLY
@ -670,7 +680,7 @@ File open constants
.. note::
`UV_FS_O_SEQUENTIAL` is only supported on Windows via
`FILE_FLAG_SEQUENTIAL_SCAN <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea>`_.
`FILE_FLAG_SEQUENTIAL_SCAN <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew>`_.
.. c:macro:: UV_FS_O_SHORT_LIVED
@ -678,7 +688,7 @@ File open constants
.. note::
`UV_FS_O_SHORT_LIVED` is only supported on Windows via
`FILE_ATTRIBUTE_TEMPORARY <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea>`_.
`FILE_ATTRIBUTE_TEMPORARY <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew>`_.
.. c:macro:: UV_FS_O_SYMLINK
@ -699,7 +709,7 @@ File open constants
.. note::
`UV_FS_O_TEMPORARY` is only supported on Windows via
`FILE_ATTRIBUTE_TEMPORARY <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea>`_.
`FILE_ATTRIBUTE_TEMPORARY <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew>`_.
.. c:macro:: UV_FS_O_TRUNC

View File

@ -45,9 +45,9 @@ Data types
be a relative path to a file contained in the directory, or `NULL` if the
file name cannot be determined.
The `events` parameter is an ORed mask of :c:type:`uv_fs_event` elements.
The `events` parameter is an ORed mask of :c:enum:`uv_fs_event` elements.
.. c:type:: uv_fs_event
.. c:enum:: uv_fs_event
Event types that :c:type:`uv_fs_event_t` handles monitor.
@ -58,7 +58,7 @@ Data types
UV_CHANGE = 2
};
.. c:type:: uv_fs_event_flags
.. c:enum:: uv_fs_event_flags
Flags that can be passed to :c:func:`uv_fs_event_start` to control its
behavior.
@ -109,10 +109,13 @@ API
.. c:function:: int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, const char* path, unsigned int flags)
Start the handle with the given callback, which will watch the specified
`path` for changes. `flags` can be an ORed mask of :c:type:`uv_fs_event_flags`.
`path` for changes. `flags` can be an ORed mask of :c:enum:`uv_fs_event_flags`.
.. note:: Currently the only supported flag is ``UV_FS_EVENT_RECURSIVE`` and
only on OSX and Windows.
.. note:: On macOS, events collected by the OS immediately before calling
``uv_fs_event_start`` might be reported to the `uv_fs_event_cb`
callback.
.. c:function:: int uv_fs_event_stop(uv_fs_event_t* handle)

View File

@ -94,7 +94,7 @@ Public members
.. c:member:: uv_handle_type uv_handle_t.type
The :c:type:`uv_handle_type`, indicating the type of the underlying handle. Readonly.
The :c:enum:`uv_handle_type`, indicating the type of the underlying handle. Readonly.
.. c:member:: void* uv_handle_t.data
@ -248,7 +248,7 @@ just for some handle types.
.. versionadded:: 1.19.0
.. c:function:: void* uv_handle_set_data(uv_handle_t* handle, void* data)
.. c:function:: void uv_handle_set_data(uv_handle_t* handle, void* data)
Sets `handle->data` to `data`.

View File

@ -16,6 +16,19 @@ Data types
Loop data type.
.. c:enum:: uv_loop_option
Additional loop options.
See :c:func:`uv_loop_configure`.
::
typedef enum {
UV_LOOP_BLOCK_SIGNAL = 0,
UV_METRICS_IDLE_TIME,
UV_LOOP_USE_IO_URING_SQPOLL
} uv_loop_option;
.. c:enum:: uv_run_mode
Mode used to run the loop with :c:func:`uv_run`.
@ -73,8 +86,13 @@ API
This option is necessary to use :c:func:`uv_metrics_idle_time`.
- UV_LOOP_ENABLE_IO_URING_SQPOLL: Enable SQPOLL io_uring instance to handle
asynchronous file system operations.
.. versionchanged:: 1.39.0 added the UV_METRICS_IDLE_TIME option.
.. versionchanged:: 1.49.0 added the UV_LOOP_ENABLE_IO_URING_SQPOLL option.
.. c:function:: int uv_loop_close(uv_loop_t* loop)
Releases all internal loop resources. Call this function only when the loop
@ -238,7 +256,7 @@ API
.. versionadded:: 1.19.0
.. c:function:: void* uv_loop_set_data(uv_loop_t* loop, void* data)
.. c:function:: void uv_loop_set_data(uv_loop_t* loop, void* data)
Sets `loop->data` to `data`.

View File

@ -199,6 +199,18 @@ Data types
char* homedir;
} uv_passwd_t;
.. c:type:: uv_group_t
Data type for group file information.
::
typedef struct uv_group_s {
char* groupname;
unsigned long gid;
char** members;
} uv_group_t;
.. c:type:: uv_utsname_t
Data type for operating system name and version information.
@ -566,6 +578,35 @@ API
.. versionadded:: 1.9.0
.. c:function:: int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid)
Gets a subset of the password file entry for the provided uid.
The populated data includes the username, euid, gid, shell,
and home directory. On non-Windows systems, all data comes from
:man:`getpwuid_r(3)`. On Windows, uid and gid are set to -1 and have no
meaning, and shell is `NULL`. After successfully calling this function, the
memory allocated to `pwd` needs to be freed with
:c:func:`uv_os_free_passwd`.
.. versionadded:: 1.45.0
.. c:function:: int uv_os_get_group(uv_group_t* group, uv_uid_t gid)
Gets a subset of the group file entry for the provided uid.
The populated data includes the group name, gid, and members. On non-Windows
systems, all data comes from :man:`getgrgid_r(3)`. On Windows, uid and gid
are set to -1 and have no meaning. After successfully calling this function,
the memory allocated to `group` needs to be freed with
:c:func:`uv_os_free_group`.
.. versionadded:: 1.45.0
.. c:function:: void uv_os_free_group(uv_passwd_t* pwd)
Frees the memory previously allocated with :c:func:`uv_os_get_group`.
.. versionadded:: 1.45.0
.. c:function:: void uv_os_free_passwd(uv_passwd_t* pwd)
Frees the `pwd` memory previously allocated with :c:func:`uv_os_get_passwd`.

View File

@ -45,7 +45,7 @@ Data types
Type definition for callback passed to :c:func:`uv_poll_start`.
.. c:type:: uv_poll_event
.. c:enum:: uv_poll_event
Poll event types

View File

@ -40,7 +40,7 @@ Data types
will indicate the exit status and the signal that caused the process to
terminate, if any.
.. c:type:: uv_process_flags
.. c:enum:: uv_process_flags
Flags to be set on the flags field of :c:type:`uv_process_options_t`.
@ -190,7 +190,7 @@ Public members
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`.
``UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS`` flag on :c:enum:`uv_process_flags`.
.. c:member:: char** uv_process_options_t.env
@ -203,7 +203,7 @@ Public members
.. 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:enum:`uv_process_flags`.
.. c:member:: int uv_process_options_t.stdio_count
.. c:member:: uv_stdio_container_t* uv_process_options_t.stdio

View File

@ -21,17 +21,9 @@ Data types
Union of all request types.
.. c:enum:: uv_req_type
Public members
^^^^^^^^^^^^^^
.. c:member:: void* uv_req_t.data
Space for user-defined arbitrary data. libuv does not use this field.
.. c:member:: uv_req_type uv_req_t.type
Indicated the type of request. Readonly.
The kind of the libuv request.
::
@ -50,6 +42,18 @@ Public members
} uv_req_type;
Public members
^^^^^^^^^^^^^^
.. c:member:: void* uv_req_t.data
Space for user-defined arbitrary data. libuv does not use this field.
.. c:member:: uv_req_type uv_req_t.type
The :c:enum:`uv_req_type`, indicating the type of the request. Readonly.
API
---
@ -95,7 +99,7 @@ API
.. versionadded:: 1.19.0
.. c:function:: void* uv_req_set_data(uv_req_t* req, void* data)
.. c:function:: void uv_req_set_data(uv_req_t* req, void* data)
Sets `req->data` to `data`.

View File

@ -16,6 +16,28 @@ Data types
TCP handle type.
.. c:enum:: uv_tcp_flags
Flags used in :c:func:`uv_tcp_bind`.
::
enum uv_tcp_flags {
/* Used with uv_tcp_bind, when an IPv6 address is used. */
UV_TCP_IPV6ONLY = 1,
/* Enable SO_REUSEPORT socket option when binding the handle.
* This allows completely duplicate bindings by multiple processes
* or threads if they all set SO_REUSEPORT before binding the port.
* Incoming connections are distributed across the participating
* listener sockets.
*
* This flag is available only on Linux 3.9+, DragonFlyBSD 3.6+,
* FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ for now.
*/
UV_TCP_REUSEPORT = 2,
};
Public members
^^^^^^^^^^^^^^
@ -65,6 +87,10 @@ API
at the end of this procedure, then the handle is destroyed with a
``UV_ETIMEDOUT`` error passed to the corresponding callback.
If `delay` is less than 1 then ``UV_EINVAL`` is returned.
.. versionchanged:: 1.49.0 If `delay` is less than 1 then ``UV_EINVAL``` is returned.
.. c:function:: int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable)
Enable / disable simultaneous asynchronous accept requests that are
@ -77,16 +103,34 @@ API
.. c:function:: int uv_tcp_bind(uv_tcp_t* handle, const struct sockaddr* addr, unsigned int flags)
Bind the handle to an address and port. `addr` should point to an
initialized ``struct sockaddr_in`` or ``struct sockaddr_in6``.
Bind the handle to an address and port.
When the port is already taken, you can expect to see an ``UV_EADDRINUSE``
error from :c:func:`uv_listen` or :c:func:`uv_tcp_connect`. That is,
a successful call to this function does not guarantee that the call
to :c:func:`uv_listen` or :c:func:`uv_tcp_connect` will succeed as well.
error from :c:func:`uv_listen` or :c:func:`uv_tcp_connect` unless you specify
``UV_TCP_REUSEPORT`` in `flags` for all the binding sockets. That is, a successful
call to this function does not guarantee that the call to :c:func:`uv_listen` or
:c:func:`uv_tcp_connect` will succeed as well.
`flags` can contain ``UV_TCP_IPV6ONLY``, in which case dual-stack support
is disabled and only IPv6 is used.
:param handle: TCP handle. It should have been initialized with :c:func:`uv_tcp_init`.
:param addr: Address to bind to. It should point to an initialized ``struct sockaddr_in``
or ``struct sockaddr_in6``.
:param flags: Flags that control the behavior of binding the socket.
``UV_TCP_IPV6ONLY`` can be contained in `flags` to disable dual-stack
support and only use IPv6.
``UV_TCP_REUSEPORT`` can be contained in `flags` to enable the socket option
`SO_REUSEPORT` with the capability of load balancing that distribute incoming
connections across all listening sockets in multiple processes or threads.
:returns: 0 on success, or an error code < 0 on failure.
.. versionchanged:: 1.49.0 added the ``UV_TCP_REUSEPORT`` flag.
.. note::
``UV_TCP_REUSEPORT`` flag is available only on Linux 3.9+, DragonFlyBSD 3.6+,
FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ at the moment. On other platforms
this function will return an UV_ENOTSUP error.
.. c:function:: int uv_tcp_getsockname(const uv_tcp_t* handle, struct sockaddr* name, int* namelen)

View File

@ -98,7 +98,7 @@ API
.. c:function:: int uv_tty_set_mode(uv_tty_t* handle, uv_tty_mode_t mode)
.. versionchanged:: 1.2.0: the mode is specified as a
:c:type:`uv_tty_mode_t` value.
:c:enum:`uv_tty_mode_t` value.
Set the TTY using the specified terminal mode.

View File

@ -18,7 +18,7 @@ Data types
UDP send request type.
.. c:type:: uv_udp_flags
.. c:enum:: uv_udp_flags
Flags used in :c:func:`uv_udp_bind` and :c:type:`uv_udp_recv_cb`..
@ -28,19 +28,21 @@ Data types
/* Disables dual stack mode. */
UV_UDP_IPV6ONLY = 1,
/*
* Indicates message was truncated because read buffer was too small. The
* remainder was discarded by the OS. Used in uv_udp_recv_cb.
*/
* Indicates message was truncated because read buffer was too small. The
* remainder was discarded by the OS. Used in uv_udp_recv_cb.
*/
UV_UDP_PARTIAL = 2,
/*
* Indicates if SO_REUSEADDR will be set when binding the handle in
* uv_udp_bind.
* This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other
* Unix platforms, it sets the SO_REUSEADDR flag. What that means is that
* multiple threads or processes can bind to the same address without error
* (provided they all set the flag) but only the last one to bind will receive
* any traffic, in effect "stealing" the port from the previous listener.
*/
* Indicates if SO_REUSEADDR will be set when binding the handle.
* This sets the SO_REUSEPORT socket flag on the BSDs (except for
* DragonFlyBSD), OS X, and other platforms where SO_REUSEPORTs don't
* have the capability of load balancing, as the opposite of what
* UV_UDP_REUSEPORT would do. On other Unix platforms, it sets the
* SO_REUSEADDR flag. What that means is that multiple threads or
* processes can bind to the same address without error (provided
* they all set the flag) but only the last one to bind will receive
* any traffic, in effect "stealing" the port from the previous listener.
*/
UV_UDP_REUSEADDR = 4,
/*
* Indicates that the message was received by recvmmsg, so the buffer provided
@ -62,8 +64,20 @@ Data types
*/
UV_UDP_LINUX_RECVERR = 32,
/*
* Indicates that recvmmsg should be used, if available.
*/
* Indicates if SO_REUSEPORT will be set when binding the handle.
* This sets the SO_REUSEPORT socket option on supported platforms.
* Unlike UV_UDP_REUSEADDR, this flag will make multiple threads or
* processes that are binding to the same address and port "share"
* the port, which means incoming datagrams are distributed across
* the receiving sockets among threads or processes.
*
* This flag is available only on Linux 3.9+, DragonFlyBSD 3.6+,
* FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ for now.
*/
UV_UDP_REUSEPORT = 64,
/*
* Indicates that recvmmsg should be used, if available.
*/
UV_UDP_RECVMMSG = 256
};
@ -186,11 +200,24 @@ API
with the address and port to bind to.
:param flags: Indicate how the socket will be bound,
``UV_UDP_IPV6ONLY``, ``UV_UDP_REUSEADDR``, and ``UV_UDP_RECVERR``
are supported.
``UV_UDP_IPV6ONLY``, ``UV_UDP_REUSEADDR``, ``UV_UDP_REUSEPORT``,
and ``UV_UDP_RECVERR`` are supported.
:returns: 0 on success, or an error code < 0 on failure.
.. versionchanged:: 1.49.0 added the ``UV_UDP_REUSEPORT`` flag.
.. note::
``UV_UDP_REUSEPORT`` flag is available only on Linux 3.9+, DragonFlyBSD 3.6+,
FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ at the moment. On other platforms
this function will return an UV_ENOTSUP error.
For platforms where `SO_REUSEPORT`s have the capability of load balancing,
specifying both ``UV_UDP_REUSEADDR`` and ``UV_UDP_REUSEPORT`` in flags is allowed
and `SO_REUSEPORT` will always override the behavior of `SO_REUSEADDR`.
For platforms where `SO_REUSEPORT`s don't have the capability of load balancing,
specifying both ``UV_UDP_REUSEADDR`` and ``UV_UDP_REUSEPORT`` in flags will fail,
returning an UV_ENOTSUP error.
.. c:function:: int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr)
Associate the UDP handle to a remote address and port, so every
@ -285,7 +312,9 @@ API
local sockets.
:param handle: UDP handle. Should have been initialized with
:c:func:`uv_udp_init`.
:c:func:`uv_udp_init_ex` as either ``AF_INET`` or ``AF_INET6``, or have
been bound to an address explicitly with :c:func:`uv_udp_bind`, or
implicitly with :c:func:`uv_udp_send()` or :c:func:`uv_udp_recv_start`.
:param on: 1 for on, 0 for off.
@ -296,7 +325,9 @@ API
Set the multicast ttl.
:param handle: UDP handle. Should have been initialized with
:c:func:`uv_udp_init`.
:c:func:`uv_udp_init_ex` as either ``AF_INET`` or ``AF_INET6``, or have
been bound to an address explicitly with :c:func:`uv_udp_bind`, or
implicitly with :c:func:`uv_udp_send()` or :c:func:`uv_udp_recv_start`.
:param ttl: 1 through 255.
@ -307,7 +338,9 @@ API
Set the multicast interface to send or receive data on.
:param handle: UDP handle. Should have been initialized with
:c:func:`uv_udp_init`.
:c:func:`uv_udp_init_ex` as either ``AF_INET`` or ``AF_INET6``, or have
been bound to an address explicitly with :c:func:`uv_udp_bind`, or
implicitly with :c:func:`uv_udp_send()` or :c:func:`uv_udp_recv_start`.
:param interface_addr: interface address.
@ -318,7 +351,9 @@ API
Set broadcast on or off.
:param handle: UDP handle. Should have been initialized with
:c:func:`uv_udp_init`.
:c:func:`uv_udp_init_ex` as either ``AF_INET`` or ``AF_INET6``, or have
been bound to an address explicitly with :c:func:`uv_udp_bind`, or
implicitly with :c:func:`uv_udp_send()` or :c:func:`uv_udp_recv_start`.
:param on: 1 for on, 0 for off.
@ -329,7 +364,9 @@ API
Set the time to live.
:param handle: UDP handle. Should have been initialized with
:c:func:`uv_udp_init`.
:c:func:`uv_udp_init_ex` as either ``AF_INET`` or ``AF_INET6``, or have
been bound to an address explicitly with :c:func:`uv_udp_bind`, or
implicitly with :c:func:`uv_udp_send()` or :c:func:`uv_udp_recv_start`.
:param ttl: 1 through 255.

62
deps/uv/include/uv.h vendored
View File

@ -260,7 +260,9 @@ typedef struct uv_metrics_s uv_metrics_t;
typedef enum {
UV_LOOP_BLOCK_SIGNAL = 0,
UV_METRICS_IDLE_TIME
UV_METRICS_IDLE_TIME,
UV_LOOP_USE_IO_URING_SQPOLL
#define UV_LOOP_USE_IO_URING_SQPOLL UV_LOOP_USE_IO_URING_SQPOLL
} uv_loop_option;
typedef enum {
@ -604,7 +606,18 @@ UV_EXTERN int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable);
enum uv_tcp_flags {
/* Used with uv_tcp_bind, when an IPv6 address is used. */
UV_TCP_IPV6ONLY = 1
UV_TCP_IPV6ONLY = 1,
/* Enable SO_REUSEPORT socket option when binding the handle.
* This allows completely duplicate bindings by multiple processes
* or threads if they all set SO_REUSEPORT before binding the port.
* Incoming connections are distributed across the participating
* listener sockets.
*
* This flag is available only on Linux 3.9+, DragonFlyBSD 3.6+,
* FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ for now.
*/
UV_TCP_REUSEPORT = 2,
};
UV_EXTERN int uv_tcp_bind(uv_tcp_t* handle,
@ -645,10 +658,13 @@ enum uv_udp_flags {
UV_UDP_PARTIAL = 2,
/*
* Indicates if SO_REUSEADDR will be set when binding the handle.
* This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other
* Unix platforms, it sets the SO_REUSEADDR flag. What that means is that
* multiple threads or processes can bind to the same address without error
* (provided they all set the flag) but only the last one to bind will receive
* This sets the SO_REUSEPORT socket flag on the BSDs (except for
* DragonFlyBSD), OS X, and other platforms where SO_REUSEPORTs don't
* have the capability of load balancing, as the opposite of what
* UV_UDP_REUSEPORT would do. On other Unix platforms, it sets the
* SO_REUSEADDR flag. What that means is that multiple threads or
* processes can bind to the same address without error (provided
* they all set the flag) but only the last one to bind will receive
* any traffic, in effect "stealing" the port from the previous listener.
*/
UV_UDP_REUSEADDR = 4,
@ -671,6 +687,18 @@ enum uv_udp_flags {
* This flag is no-op on platforms other than Linux.
*/
UV_UDP_LINUX_RECVERR = 32,
/*
* Indicates if SO_REUSEPORT will be set when binding the handle.
* This sets the SO_REUSEPORT socket option on supported platforms.
* Unlike UV_UDP_REUSEADDR, this flag will make multiple threads or
* processes that are binding to the same address and port "share"
* the port, which means incoming datagrams are distributed across
* the receiving sockets among threads or processes.
*
* This flag is available only on Linux 3.9+, DragonFlyBSD 3.6+,
* FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ for now.
*/
UV_UDP_REUSEPORT = 64,
/*
* Indicates that recvmmsg should be used, if available.
*/
@ -1903,17 +1931,17 @@ struct uv_loop_s {
UV_EXTERN void* uv_loop_get_data(const uv_loop_t*);
UV_EXTERN void uv_loop_set_data(uv_loop_t*, void* data);
/* String utilities needed internally for dealing with Windows. */
size_t uv_utf16_length_as_wtf8(const uint16_t* utf16,
ssize_t utf16_len);
int uv_utf16_to_wtf8(const uint16_t* utf16,
ssize_t utf16_len,
char** wtf8_ptr,
size_t* wtf8_len_ptr);
ssize_t uv_wtf8_length_as_utf16(const char* wtf8);
void uv_wtf8_to_utf16(const char* wtf8,
uint16_t* utf16,
size_t utf16_len);
/* Unicode utilities needed for dealing with Windows. */
UV_EXTERN size_t uv_utf16_length_as_wtf8(const uint16_t* utf16,
ssize_t utf16_len);
UV_EXTERN int uv_utf16_to_wtf8(const uint16_t* utf16,
ssize_t utf16_len,
char** wtf8_ptr,
size_t* wtf8_len_ptr);
UV_EXTERN ssize_t uv_wtf8_length_as_utf16(const char* wtf8);
UV_EXTERN void uv_wtf8_to_utf16(const char* wtf8,
uint16_t* utf16,
size_t utf16_len);
/* Don't export the private CPP symbols. */
#undef UV_HANDLE_TYPE_PRIVATE

View File

@ -35,21 +35,7 @@
#endif
/*
* This file defines data structures for different types of trees:
* splay trees and red-black trees.
*
* A splay tree is a self-organizing data structure. Every operation
* on the tree causes a splay to happen. The splay moves the requested
* node to the root of the tree and partly rebalances it.
*
* This has the benefit that request locality causes faster lookups as
* the requested nodes move to the top of the tree. On the other hand,
* every lookup causes memory writes.
*
* The Balance Theorem bounds the total access time for m operations
* and n inserts on an initially empty tree as O((m + n)lg n). The
* amortized cost for a sequence of m accesses to a splay tree is O(lg n);
*
* This file defines data structures for red-black trees.
* A red-black tree is a binary search tree with the node color as an
* extra attribute. It fulfills a set of conditions:
* - every search path from the root to a leaf consists of the
@ -61,239 +47,6 @@
* The maximum height of a red-black tree is 2lg (n+1).
*/
#define SPLAY_HEAD(name, type) \
struct name { \
struct type *sph_root; /* root of the tree */ \
}
#define SPLAY_INITIALIZER(root) \
{ NULL }
#define SPLAY_INIT(root) do { \
(root)->sph_root = NULL; \
} while (/*CONSTCOND*/ 0)
#define SPLAY_ENTRY(type) \
struct { \
struct type *spe_left; /* left element */ \
struct type *spe_right; /* right element */ \
}
#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
#define SPLAY_ROOT(head) (head)->sph_root
#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
(head)->sph_root = tmp; \
} while (/*CONSTCOND*/ 0)
#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
(head)->sph_root = tmp; \
} while (/*CONSTCOND*/ 0)
#define SPLAY_LINKLEFT(head, tmp, field) do { \
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
tmp = (head)->sph_root; \
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
} while (/*CONSTCOND*/ 0)
#define SPLAY_LINKRIGHT(head, tmp, field) do { \
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
tmp = (head)->sph_root; \
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
} while (/*CONSTCOND*/ 0)
#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field); \
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
} while (/*CONSTCOND*/ 0)
/* Generates prototypes and inline functions */
#define SPLAY_PROTOTYPE(name, type, field, cmp) \
void name##_SPLAY(struct name *, struct type *); \
void name##_SPLAY_MINMAX(struct name *, int); \
struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
\
/* Finds the node with the same key as elm */ \
static __inline struct type * \
name##_SPLAY_FIND(struct name *head, struct type *elm) \
{ \
if (SPLAY_EMPTY(head)) \
return(NULL); \
name##_SPLAY(head, elm); \
if ((cmp)(elm, (head)->sph_root) == 0) \
return (head->sph_root); \
return (NULL); \
} \
\
static __inline struct type * \
name##_SPLAY_NEXT(struct name *head, struct type *elm) \
{ \
name##_SPLAY(head, elm); \
if (SPLAY_RIGHT(elm, field) != NULL) { \
elm = SPLAY_RIGHT(elm, field); \
while (SPLAY_LEFT(elm, field) != NULL) { \
elm = SPLAY_LEFT(elm, field); \
} \
} else \
elm = NULL; \
return (elm); \
} \
\
static __inline struct type * \
name##_SPLAY_MIN_MAX(struct name *head, int val) \
{ \
name##_SPLAY_MINMAX(head, val); \
return (SPLAY_ROOT(head)); \
}
/* Main splay operation.
* Moves node close to the key of elm to top
*/
#define SPLAY_GENERATE(name, type, field, cmp) \
struct type * \
name##_SPLAY_INSERT(struct name *head, struct type *elm) \
{ \
if (SPLAY_EMPTY(head)) { \
SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
} else { \
int __comp; \
name##_SPLAY(head, elm); \
__comp = (cmp)(elm, (head)->sph_root); \
if(__comp < 0) { \
SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field); \
SPLAY_RIGHT(elm, field) = (head)->sph_root; \
SPLAY_LEFT((head)->sph_root, field) = NULL; \
} else if (__comp > 0) { \
SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field); \
SPLAY_LEFT(elm, field) = (head)->sph_root; \
SPLAY_RIGHT((head)->sph_root, field) = NULL; \
} else \
return ((head)->sph_root); \
} \
(head)->sph_root = (elm); \
return (NULL); \
} \
\
struct type * \
name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
{ \
struct type *__tmp; \
if (SPLAY_EMPTY(head)) \
return (NULL); \
name##_SPLAY(head, elm); \
if ((cmp)(elm, (head)->sph_root) == 0) { \
if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
} else { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
name##_SPLAY(head, elm); \
SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
} \
return (elm); \
} \
return (NULL); \
} \
\
void \
name##_SPLAY(struct name *head, struct type *elm) \
{ \
struct type __node, *__left, *__right, *__tmp; \
int __comp; \
\
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \
__left = __right = &__node; \
\
while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \
if (__comp < 0) { \
__tmp = SPLAY_LEFT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if ((cmp)(elm, __tmp) < 0){ \
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
if (SPLAY_LEFT((head)->sph_root, field) == NULL) \
break; \
} \
SPLAY_LINKLEFT(head, __right, field); \
} else if (__comp > 0) { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if ((cmp)(elm, __tmp) > 0){ \
SPLAY_ROTATE_LEFT(head, __tmp, field); \
if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \
break; \
} \
SPLAY_LINKRIGHT(head, __left, field); \
} \
} \
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
} \
\
/* Splay with either the minimum or the maximum element \
* Used to find minimum or maximum element in tree. \
*/ \
void name##_SPLAY_MINMAX(struct name *head, int __comp) \
{ \
struct type __node, *__left, *__right, *__tmp; \
\
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \
__left = __right = &__node; \
\
for (;;) { \
if (__comp < 0) { \
__tmp = SPLAY_LEFT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if (__comp < 0){ \
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
if (SPLAY_LEFT((head)->sph_root, field) == NULL) \
break; \
} \
SPLAY_LINKLEFT(head, __right, field); \
} else if (__comp > 0) { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if (__comp > 0) { \
SPLAY_ROTATE_LEFT(head, __tmp, field); \
if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \
break; \
} \
SPLAY_LINKRIGHT(head, __left, field); \
} \
} \
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
}
#define SPLAY_NEGINF -1
#define SPLAY_INF 1
#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
#define SPLAY_FOREACH(x, name, head) \
for ((x) = SPLAY_MIN(name, head); \
(x) != NULL; \
(x) = SPLAY_NEXT(name, head, x))
/* Macros that define a red-black tree */
#define RB_HEAD(name, type) \
struct name { \
@ -730,8 +483,8 @@ name##_RB_MINMAX(struct name *head, int val) \
#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
#define RB_PREV(name, x, y) name##_RB_PREV(y)
#define RB_NEXT(name, x) name##_RB_NEXT(x)
#define RB_PREV(name, x) name##_RB_PREV(x)
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)

View File

@ -31,8 +31,8 @@
*/
#define UV_VERSION_MAJOR 1
#define UV_VERSION_MINOR 48
#define UV_VERSION_PATCH 0
#define UV_VERSION_MINOR 49
#define UV_VERSION_PATCH 1
#define UV_VERSION_IS_RELEASE 1
#define UV_VERSION_SUFFIX ""

View File

@ -290,8 +290,8 @@ typedef struct {
#define UV_ONCE_INIT { 0, NULL }
typedef struct uv_once_s {
unsigned char ran;
HANDLE event;
unsigned char unused;
INIT_ONCE init_once;
} uv_once_t;
/* Platform-specific definitions for uv_spawn support. */

View File

@ -82,7 +82,7 @@ static void uv__random_done(struct uv__work* w, int status) {
uv_random_t* req;
req = container_of(w, uv_random_t, work_req);
uv__req_unregister(req->loop, req);
uv__req_unregister(req->loop);
if (status == 0)
status = req->status;

View File

@ -356,7 +356,7 @@ static void uv__queue_done(struct uv__work* w, int err) {
uv_work_t* req;
req = container_of(w, uv_work_t, work_req);
uv__req_unregister(req->loop, req);
uv__req_unregister(req->loop);
if (req->after_work_cb == NULL)
return;

View File

@ -38,6 +38,34 @@
#include <sys/eventfd.h>
#endif
#if UV__KQUEUE_EVFILT_USER
static uv_once_t kqueue_runtime_detection_guard = UV_ONCE_INIT;
static int kqueue_evfilt_user_support = 1;
static void uv__kqueue_runtime_detection(void) {
int kq;
struct kevent ev[2];
struct timespec timeout = {0, 0};
/* Perform the runtime detection to ensure that kqueue with
* EVFILT_USER actually works. */
kq = kqueue();
EV_SET(ev, UV__KQUEUE_EVFILT_USER_IDENT, EVFILT_USER,
EV_ADD | EV_CLEAR, 0, 0, 0);
EV_SET(ev + 1, UV__KQUEUE_EVFILT_USER_IDENT, EVFILT_USER,
0, NOTE_TRIGGER, 0, 0);
if (kevent(kq, ev, 2, ev, 1, &timeout) < 1 ||
ev[0].filter != EVFILT_USER ||
ev[0].ident != UV__KQUEUE_EVFILT_USER_IDENT ||
ev[0].flags & EV_ERROR)
/* If we wind up here, we can assume that EVFILT_USER is defined but
* broken on the current system. */
kqueue_evfilt_user_support = 0;
uv__close(kq);
}
#endif
static void uv__async_send(uv_loop_t* loop);
static int uv__async_start(uv_loop_t* loop);
static void uv__cpu_relax(void);
@ -130,8 +158,10 @@ void uv__async_close(uv_async_t* handle) {
static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
#ifndef __linux__
char buf[1024];
ssize_t r;
#endif
struct uv__queue queue;
struct uv__queue* q;
uv_async_t* h;
@ -139,7 +169,12 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
assert(w == &loop->async_io_watcher);
#ifndef __linux__
#if UV__KQUEUE_EVFILT_USER
for (;!kqueue_evfilt_user_support;) {
#else
for (;;) {
#endif
r = read(w->fd, buf, sizeof(buf));
if (r == sizeof(buf))
@ -156,6 +191,7 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
abort();
}
#endif /* !__linux__ */
uv__queue_move(&loop->async_handles, &queue);
while (!uv__queue_empty(&queue)) {
@ -179,34 +215,58 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
static void uv__async_send(uv_loop_t* loop) {
const void* buf;
ssize_t len;
int fd;
int r;
ssize_t r;
#ifdef __linux__
uint64_t val;
buf = "";
len = 1;
fd = loop->async_wfd;
fd = loop->async_io_watcher.fd; /* eventfd */
for (val = 1; /* empty */; val = 1) {
r = write(fd, &val, sizeof(uint64_t));
if (r < 0) {
/* When EAGAIN occurs, the eventfd counter hits the maximum value of the unsigned 64-bit.
* We need to first drain the eventfd and then write again.
*
* Check out https://man7.org/linux/man-pages/man2/eventfd.2.html for details.
*/
if (errno == EAGAIN) {
/* It's ready to retry. */
if (read(fd, &val, sizeof(uint64_t)) > 0 || errno == EAGAIN) {
continue;
}
}
/* Unknown error occurs. */
break;
}
return;
}
#else
#if UV__KQUEUE_EVFILT_USER
struct kevent ev;
#if defined(__linux__)
if (fd == -1) {
static const uint64_t val = 1;
buf = &val;
len = sizeof(val);
fd = loop->async_io_watcher.fd; /* eventfd */
if (kqueue_evfilt_user_support) {
fd = loop->async_io_watcher.fd; /* magic number for EVFILT_USER */
EV_SET(&ev, fd, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0);
r = kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL);
if (r == 0)
return;
else
abort();
}
#endif
fd = loop->async_wfd; /* write end of the pipe */
do
r = write(fd, buf, len);
r = write(fd, "x", 1);
while (r == -1 && errno == EINTR);
if (r == len)
if (r == 1)
return;
if (r == -1)
if (errno == EAGAIN || errno == EWOULDBLOCK)
return;
#endif
abort();
}
@ -215,6 +275,9 @@ static void uv__async_send(uv_loop_t* loop) {
static int uv__async_start(uv_loop_t* loop) {
int pipefd[2];
int err;
#if UV__KQUEUE_EVFILT_USER
struct kevent ev;
#endif
if (loop->async_io_watcher.fd != -1)
return 0;
@ -226,6 +289,36 @@ static int uv__async_start(uv_loop_t* loop) {
pipefd[0] = err;
pipefd[1] = -1;
#elif UV__KQUEUE_EVFILT_USER
uv_once(&kqueue_runtime_detection_guard, uv__kqueue_runtime_detection);
if (kqueue_evfilt_user_support) {
/* In order not to break the generic pattern of I/O polling, a valid
* file descriptor is required to take up a room in loop->watchers,
* thus we create one for that, but this fd will not be actually used,
* it's just a placeholder and magic number which is going to be closed
* during the cleanup, as other FDs. */
err = uv__open_cloexec("/dev/null", O_RDONLY);
if (err < 0)
return err;
pipefd[0] = err;
pipefd[1] = -1;
/* When using EVFILT_USER event to wake up the kqueue, this event must be
* registered beforehand. Otherwise, calling kevent() to issue an
* unregistered EVFILT_USER event will get an ENOENT.
* Since uv__async_send() may happen before uv__io_poll() with multi-threads,
* we can't defer this registration of EVFILT_USER event as we did for other
* events, but must perform it right away. */
EV_SET(&ev, err, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, 0);
err = kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL);
if (err < 0)
return UV__ERR(errno);
} else {
err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE);
if (err < 0)
return err;
}
#else
err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE);
if (err < 0)
@ -236,6 +329,13 @@ static int uv__async_start(uv_loop_t* loop) {
uv__io_start(loop, &loop->async_io_watcher, POLLIN);
loop->async_wfd = pipefd[1];
#if UV__KQUEUE_EVFILT_USER
/* Prevent the EVFILT_USER event from being added to kqueue redundantly
* and mistakenly later in uv__io_poll(). */
if (kqueue_evfilt_user_support)
loop->async_io_watcher.events = loop->async_io_watcher.pevents;
#endif
return 0;
}

View File

@ -53,7 +53,8 @@
#if defined(__APPLE__)
# include <sys/filio.h>
# endif /* defined(__APPLE__) */
# include <sys/sysctl.h>
#endif /* defined(__APPLE__) */
#if defined(__APPLE__) && !TARGET_OS_IPHONE
@ -94,6 +95,15 @@ extern char** environ;
# define uv__accept4 accept4
#endif
#if defined(__FreeBSD__)
# include <sys/param.h>
# include <sys/cpuset.h>
#endif
#if defined(__NetBSD__)
# include <sched.h>
#endif
#if defined(__linux__) && defined(__SANITIZE_THREAD__) && defined(__clang__)
# include <sanitizer/linux_syscall_hooks.h>
#endif
@ -156,7 +166,7 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) {
break;
case UV_TTY:
uv__stream_close((uv_stream_t*)handle);
uv__tty_close((uv_tty_t*)handle);
break;
case UV_TCP:
@ -1024,7 +1034,7 @@ int uv_getrusage(uv_rusage_t* rusage) {
#if defined(__APPLE__)
rusage->ru_maxrss /= 1024; /* macOS and iOS report bytes. */
#elif defined(__sun)
rusage->ru_maxrss /= getpagesize() / 1024; /* Solaris reports pages. */
rusage->ru_maxrss *= getpagesize() / 1024; /* Solaris reports pages. */
#endif
return 0;
@ -1616,6 +1626,7 @@ static int set_nice_for_calling_thread(int priority) {
* If the function fails, the return value is non-zero.
*/
int uv_thread_setpriority(uv_thread_t tid, int priority) {
#if !defined(__GNU__)
int r;
int min;
int max;
@ -1677,10 +1688,14 @@ int uv_thread_setpriority(uv_thread_t tid, int priority) {
param.sched_priority = prio;
r = pthread_setschedparam(tid, policy, &param);
if (r != 0)
return UV__ERR(errno);
return UV__ERR(errno);
}
return 0;
#else /* !defined(__GNU__) */
/* Simulate success on systems where thread priority is not implemented. */
return 0;
#endif /* !defined(__GNU__) */
}
int uv_os_uname(uv_utsname_t* buffer) {
@ -1864,11 +1879,31 @@ int uv__search_path(const char* prog, char* buf, size_t* buflen) {
return UV_EINVAL;
}
#if defined(__linux__) || defined (__FreeBSD__)
# define uv__cpu_count(cpuset) CPU_COUNT(cpuset)
#elif defined(__NetBSD__)
static int uv__cpu_count(cpuset_t* set) {
int rc;
cpuid_t i;
rc = 0;
for (i = 0;; i++) {
int r = cpuset_isset(i, set);
if (r < 0)
break;
if (r)
rc++;
}
return rc;
}
#endif /* __NetBSD__ */
unsigned int uv_available_parallelism(void) {
long rc = -1;
#ifdef __linux__
cpu_set_t set;
long rc;
memset(&set, 0, sizeof(set));
@ -1877,29 +1912,127 @@ unsigned int uv_available_parallelism(void) {
* before falling back to sysconf(_SC_NPROCESSORS_ONLN).
*/
if (0 == sched_getaffinity(0, sizeof(set), &set))
rc = CPU_COUNT(&set);
else
rc = sysconf(_SC_NPROCESSORS_ONLN);
if (rc < 1)
rc = 1;
return (unsigned) rc;
rc = uv__cpu_count(&set);
#elif defined(__MVS__)
int rc;
rc = __get_num_online_cpus();
if (rc < 1)
rc = 1;
return (unsigned) rc;
#else /* __linux__ */
long rc;
#elif defined(__FreeBSD__)
cpuset_t set;
memset(&set, 0, sizeof(set));
if (0 == cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(set), &set))
rc = uv__cpu_count(&set);
#elif defined(__NetBSD__)
cpuset_t* set = cpuset_create();
if (set != NULL) {
if (0 == sched_getaffinity_np(getpid(), sizeof(set), &set))
rc = uv__cpu_count(&set);
cpuset_destroy(set);
}
#elif defined(__APPLE__)
int nprocs;
size_t i;
size_t len = sizeof(nprocs);
static const char *mib[] = {
"hw.activecpu",
"hw.logicalcpu",
"hw.ncpu"
};
for (i = 0; i < ARRAY_SIZE(mib); i++) {
if (0 == sysctlbyname(mib[i], &nprocs, &len, NULL, 0) &&
len == sizeof(nprocs) &&
nprocs > 0) {
rc = nprocs;
break;
}
}
#elif defined(__OpenBSD__)
int nprocs;
size_t i;
size_t len = sizeof(nprocs);
static int mib[][2] = {
# ifdef HW_NCPUONLINE
{ CTL_HW, HW_NCPUONLINE },
# endif
{ CTL_HW, HW_NCPU }
};
for (i = 0; i < ARRAY_SIZE(mib); i++) {
if (0 == sysctl(mib[i], ARRAY_SIZE(mib[i]), &nprocs, &len, NULL, 0) &&
len == sizeof(nprocs) &&
nprocs > 0) {
rc = nprocs;
break;
}
}
#endif /* __linux__ */
if (rc < 0)
rc = sysconf(_SC_NPROCESSORS_ONLN);
#ifdef __linux__
{
double rc_with_cgroup;
uv__cpu_constraint c = {0, 0, 0.0};
if (uv__get_constrained_cpu(&c) == 0 && c.period_length > 0) {
rc_with_cgroup = (double)c.quota_per_period / c.period_length * c.proportions;
if (rc_with_cgroup < rc)
rc = (long)rc_with_cgroup; /* Casting is safe since rc_with_cgroup < rc < LONG_MAX */
}
}
#endif /* __linux__ */
rc = sysconf(_SC_NPROCESSORS_ONLN);
if (rc < 1)
rc = 1;
return (unsigned) rc;
#endif /* __linux__ */
}
int uv__sock_reuseport(int fd) {
int on = 1;
#if defined(__FreeBSD__) && __FreeBSD__ >= 12 && defined(SO_REUSEPORT_LB)
/* FreeBSD 12 introduced a new socket option named SO_REUSEPORT_LB
* with the capability of load balancing, it's the substitution of
* the SO_REUSEPORTs on Linux and DragonFlyBSD. */
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT_LB, &on, sizeof(on)))
return UV__ERR(errno);
#elif (defined(__linux__) || \
defined(_AIX73) || \
(defined(__DragonFly__) && __DragonFly_version >= 300600) || \
(defined(UV__SOLARIS_11_4) && UV__SOLARIS_11_4)) && \
defined(SO_REUSEPORT)
/* On Linux 3.9+, the SO_REUSEPORT implementation distributes connections
* evenly across all of the threads (or processes) that are blocked in
* accept() on the same port. As with TCP, SO_REUSEPORT distributes datagrams
* evenly across all of the receiving threads (or process).
*
* DragonFlyBSD 3.6.0 extended SO_REUSEPORT to distribute workload to
* available sockets, which made it the equivalent of Linux's SO_REUSEPORT.
*
* AIX 7.2.5 added the feature that would add the capability to distribute
* incoming connections or datagrams across all listening ports for SO_REUSEPORT.
*
* Solaris 11 supported SO_REUSEPORT, but it's implemented only for
* binding to the same address and port, without load balancing.
* Solaris 11.4 extended SO_REUSEPORT with the capability of load balancing.
*/
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)))
return UV__ERR(errno);
#else
(void) (fd);
(void) (on);
/* SO_REUSEPORTs do not have the capability of load balancing on platforms
* other than those mentioned above. The semantics are completely different,
* therefore we shouldn't enable it, but fail this operation to indicate that
* UV_[TCP/UDP]_REUSEPORT is not supported on these platforms. */
return UV_ENOTSUP;
#endif
return 0;
}

View File

@ -36,9 +36,45 @@ int uv_uptime(double* uptime) {
}
int uv_resident_set_memory(size_t* rss) {
/* FIXME: read /proc/meminfo? */
*rss = 0;
char buf[1024];
const char* s;
long val;
int rc;
int i;
struct sysinfo si;
/* rss: 24th element */
rc = uv__slurp("/proc/self/stat", buf, sizeof(buf));
if (rc < 0)
return rc;
/* find the last ')' */
s = strrchr(buf, ')');
if (s == NULL)
goto err;
for (i = 1; i <= 22; i++) {
s = strchr(s + 1, ' ');
if (s == NULL)
goto err;
}
errno = 0;
val = strtol(s, NULL, 10);
if (val < 0 || errno != 0)
goto err;
do
rc = sysinfo(&si);
while (rc == -1 && errno == EINTR);
if (rc == -1)
return UV__ERR(errno);
*rss = val * si.mem_unit;
return 0;
err:
return UV_EINVAL;
}
int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {

17
deps/uv/src/unix/darwin-syscalls.h vendored Normal file
View File

@ -0,0 +1,17 @@
#ifndef UV_DARWIN_SYSCALLS_H_
#define UV_DARWIN_SYSCALLS_H_
#include <sys/types.h>
#include <sys/socket.h>
/* https://github.com/apple/darwin-xnu/blob/master/bsd/sys/socket.h */
struct mmsghdr {
struct msghdr msg_hdr;
size_t msg_len;
};
ssize_t recvmsg_x(int s, const struct mmsghdr* msgp, u_int cnt, int flags);
ssize_t sendmsg_x(int s, const struct mmsghdr* msgp, u_int cnt, int flags);
#endif /* UV_DARWIN_SYSCALLS_H_ */

View File

@ -25,7 +25,6 @@
#include <stdint.h>
#include <errno.h>
#include <dlfcn.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <mach-o/dyld.h> /* _NSGetExecutablePath */
@ -34,7 +33,6 @@
#include <unistd.h> /* sysconf */
static uv_once_t once = UV_ONCE_INIT;
static uint64_t (*time_func)(void);
static mach_timebase_info_data_t timebase;
@ -56,16 +54,12 @@ void uv__platform_loop_delete(uv_loop_t* loop) {
static void uv__hrtime_init_once(void) {
if (KERN_SUCCESS != mach_timebase_info(&timebase))
abort();
time_func = (uint64_t (*)(void)) dlsym(RTLD_DEFAULT, "mach_continuous_time");
if (time_func == NULL)
time_func = mach_absolute_time;
}
uint64_t uv__hrtime(uv_clocktype_t type) {
uv_once(&once, uv__hrtime_init_once);
return time_func() * timebase.numer / timebase.denom;
return mach_continuous_time() * timebase.numer / timebase.denom;
}

View File

@ -26,7 +26,12 @@
#include <errno.h>
#include <paths.h>
#include <sys/user.h>
#if defined(__DragonFly__)
# include <sys/event.h>
# include <sys/kinfo.h>
#else
# include <sys/user.h>
#endif
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/sysctl.h>

190
deps/uv/src/unix/fs.c vendored
View File

@ -31,6 +31,7 @@
#include <errno.h>
#include <dlfcn.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -82,17 +83,6 @@
# include <sys/statfs.h>
#endif
#if defined(__CYGWIN__) || \
(defined(__HAIKU__) && B_HAIKU_VERSION < B_HAIKU_VERSION_1_PRE_BETA_5) || \
(defined(__sun) && !defined(__illumos__)) || \
(defined(__APPLE__) && !TARGET_OS_IPHONE && \
MAC_OS_X_VERSION_MIN_REQUIRED < 110000)
#define preadv(fd, bufs, nbufs, off) \
pread(fd, (bufs)->iov_base, (bufs)->iov_len, off)
#define pwritev(fd, bufs, nbufs, off) \
pwrite(fd, (bufs)->iov_base, (bufs)->iov_len, off)
#endif
#if defined(_AIX) && _XOPEN_SOURCE <= 600
extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */
#endif
@ -149,7 +139,7 @@ extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */
#define POST \
do { \
if (cb != NULL) { \
uv__req_register(loop, req); \
uv__req_register(loop); \
uv__work_submit(loop, \
&req->work_req, \
UV__WORK_FAST_IO, \
@ -406,6 +396,123 @@ static ssize_t uv__fs_open(uv_fs_t* req) {
}
static ssize_t uv__preadv_or_pwritev_emul(int fd,
const struct iovec* bufs,
size_t nbufs,
off_t off,
int is_pread) {
ssize_t total;
ssize_t r;
size_t i;
size_t n;
void* p;
total = 0;
for (i = 0; i < (size_t) nbufs; i++) {
p = bufs[i].iov_base;
n = bufs[i].iov_len;
do
if (is_pread)
r = pread(fd, p, n, off);
else
r = pwrite(fd, p, n, off);
while (r == -1 && errno == EINTR);
if (r == -1) {
if (total > 0)
return total;
return -1;
}
off += r;
total += r;
if ((size_t) r < n)
return total;
}
return total;
}
#ifdef __linux__
typedef int uv__iovcnt;
#else
typedef size_t uv__iovcnt;
#endif
static ssize_t uv__preadv_emul(int fd,
const struct iovec* bufs,
uv__iovcnt nbufs,
off_t off) {
return uv__preadv_or_pwritev_emul(fd, bufs, nbufs, off, /*is_pread*/1);
}
static ssize_t uv__pwritev_emul(int fd,
const struct iovec* bufs,
uv__iovcnt nbufs,
off_t off) {
return uv__preadv_or_pwritev_emul(fd, bufs, nbufs, off, /*is_pread*/0);
}
/* The function pointer cache is an uintptr_t because _Atomic void*
* doesn't work on macos/ios/etc...
* Disable optimization on armv7 to work around the bug described in
* https://github.com/libuv/libuv/issues/4532
*/
#if defined(__arm__) && (__ARM_ARCH == 7)
__attribute__((optimize("O0")))
#endif
static ssize_t uv__preadv_or_pwritev(int fd,
const struct iovec* bufs,
size_t nbufs,
off_t off,
_Atomic uintptr_t* cache,
int is_pread) {
ssize_t (*f)(int, const struct iovec*, uv__iovcnt, off_t);
void* p;
p = (void*) atomic_load_explicit(cache, memory_order_relaxed);
if (p == NULL) {
#ifdef RTLD_DEFAULT
p = dlsym(RTLD_DEFAULT, is_pread ? "preadv" : "pwritev");
dlerror(); /* Clear errors. */
#endif /* RTLD_DEFAULT */
if (p == NULL)
p = is_pread ? uv__preadv_emul : uv__pwritev_emul;
atomic_store_explicit(cache, (uintptr_t) p, memory_order_relaxed);
}
/* Use memcpy instead of `f = p` to work around a compiler bug,
* see https://github.com/libuv/libuv/issues/4532
*/
memcpy(&f, &p, sizeof(p));
return f(fd, bufs, nbufs, off);
}
static ssize_t uv__preadv(int fd,
const struct iovec* bufs,
size_t nbufs,
off_t off) {
static _Atomic uintptr_t cache;
return uv__preadv_or_pwritev(fd, bufs, nbufs, off, &cache, /*is_pread*/1);
}
static ssize_t uv__pwritev(int fd,
const struct iovec* bufs,
size_t nbufs,
off_t off) {
static _Atomic uintptr_t cache;
return uv__preadv_or_pwritev(fd, bufs, nbufs, off, &cache, /*is_pread*/0);
}
static ssize_t uv__fs_read(uv_fs_t* req) {
const struct iovec* bufs;
unsigned int iovmax;
@ -433,7 +540,7 @@ static ssize_t uv__fs_read(uv_fs_t* req) {
if (nbufs == 1)
r = pread(fd, bufs->iov_base, bufs->iov_len, off);
else if (nbufs > 1)
r = preadv(fd, bufs, nbufs, off);
r = uv__preadv(fd, bufs, nbufs, off);
}
#ifdef __PASE__
@ -691,14 +798,23 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) {
static ssize_t uv__fs_realpath(uv_fs_t* req) {
char* buf;
char* tmp;
#if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809L
buf = realpath(req->path, NULL);
if (buf == NULL)
tmp = realpath(req->path, NULL);
if (tmp == NULL)
return -1;
buf = uv__strdup(tmp);
free(tmp); /* _Not_ uv__free. */
if (buf == NULL) {
errno = ENOMEM;
return -1;
}
#else
ssize_t len;
(void)tmp;
len = uv__fs_pathmax_size(req->path);
buf = uv__malloc(len + 1);
@ -962,7 +1078,10 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) {
return -1;
}
#elif defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__)
/* sendfile() on iOS(arm64) will throw SIGSYS signal cause crash. */
#elif (defined(__APPLE__) && !TARGET_OS_IPHONE) \
|| defined(__DragonFly__) \
|| defined(__FreeBSD__)
{
off_t len;
ssize_t r;
@ -1112,7 +1231,7 @@ static ssize_t uv__fs_write(uv_fs_t* req) {
if (nbufs == 1)
r = pwrite(fd, bufs->iov_base, bufs->iov_len, off);
else if (nbufs > 1)
r = pwritev(fd, bufs, nbufs, off);
r = uv__pwritev(fd, bufs, nbufs, off);
}
return r;
@ -1125,6 +1244,7 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) {
uv_file dstfd;
struct stat src_statsbuf;
struct stat dst_statsbuf;
struct timespec times[2];
int dst_flags;
int result;
int err;
@ -1202,6 +1322,35 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) {
}
}
/**
* Change the timestamps of the destination file to match the source file.
*/
#if defined(__APPLE__)
times[0] = src_statsbuf.st_atimespec;
times[1] = src_statsbuf.st_mtimespec;
#elif defined(_AIX)
times[0].tv_sec = src_statsbuf.st_atime;
times[0].tv_nsec = src_statsbuf.st_atime_n;
times[1].tv_sec = src_statsbuf.st_mtime;
times[1].tv_nsec = src_statsbuf.st_mtime_n;
#else
times[0] = src_statsbuf.st_atim;
times[1] = src_statsbuf.st_mtim;
#endif
if (futimens(dstfd, times) == -1) {
err = UV__ERR(errno);
goto out;
}
/*
* Change the ownership and permissions of the destination file to match the
* source file.
* `cp -p` does not care about errors here, so we don't either. Reuse the
* `result` variable to silence a -Wunused-result warning.
*/
result = fchown(dstfd, src_statsbuf.st_uid, src_statsbuf.st_gid);
if (fchmod(dstfd, src_statsbuf.st_mode) == -1) {
err = UV__ERR(errno);
#ifdef __linux__
@ -1619,7 +1768,7 @@ static void uv__fs_done(struct uv__work* w, int status) {
uv_fs_t* req;
req = container_of(w, uv_fs_t, work_req);
uv__req_unregister(req->loop, req);
uv__req_unregister(req->loop);
if (status == UV_ECANCELED) {
assert(req->result == 0);
@ -1631,7 +1780,7 @@ static void uv__fs_done(struct uv__work* w, int status) {
void uv__fs_post(uv_loop_t* loop, uv_fs_t* req) {
uv__req_register(loop, req);
uv__req_register(loop);
uv__work_submit(loop,
&req->work_req,
UV__WORK_FAST_IO,
@ -1766,6 +1915,9 @@ int uv_fs_ftruncate(uv_loop_t* loop,
INIT(FTRUNCATE);
req->file = file;
req->off = off;
if (cb != NULL)
if (uv__iou_fs_ftruncate(loop, req))
return 0;
POST;
}

View File

@ -276,10 +276,6 @@ static void uv__fsevents_event_cb(const FSEventStreamRef streamRef,
path += handle->realpath_len;
len -= handle->realpath_len;
/* Ignore events with path equal to directory itself */
if (len <= 1 && (flags & kFSEventStreamEventFlagItemIsDir))
continue;
if (len == 0) {
/* Since we're using fsevents to watch the file itself,
* realpath == path, and we now need to get the basename of the file back
@ -793,6 +789,7 @@ int uv__cf_loop_signal(uv_loop_t* loop,
/* Runs in UV loop to initialize handle */
int uv__fsevents_init(uv_fs_event_t* handle) {
char* buf;
int err;
uv__cf_loop_state_t* state;
@ -801,9 +798,13 @@ int uv__fsevents_init(uv_fs_event_t* handle) {
return err;
/* Get absolute path to file */
handle->realpath = realpath(handle->path, NULL);
if (handle->realpath == NULL)
buf = realpath(handle->path, NULL);
if (buf == NULL)
return UV__ERR(errno);
handle->realpath = uv__strdup(buf);
free(buf); /* _Not_ uv__free. */
if (handle->realpath == NULL)
return UV_ENOMEM;
handle->realpath_len = strlen(handle->realpath);
/* Initialize event queue */

View File

@ -109,7 +109,7 @@ static void uv__getaddrinfo_done(struct uv__work* w, int status) {
uv_getaddrinfo_t* req;
req = container_of(w, uv_getaddrinfo_t, work_req);
uv__req_unregister(req->loop, req);
uv__req_unregister(req->loop);
/* See initialization in uv_getaddrinfo(). */
if (req->hints)

View File

@ -58,7 +58,7 @@ static void uv__getnameinfo_done(struct uv__work* w, int status) {
char* service;
req = container_of(w, uv_getnameinfo_t, work_req);
uv__req_unregister(req->loop, req);
uv__req_unregister(req->loop);
host = service = NULL;
if (status == UV_ECANCELED) {

View File

@ -35,6 +35,10 @@
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#if defined(__APPLE__) || defined(__DragonFly__) || \
defined(__FreeBSD__) || defined(__NetBSD__)
#include <sys/event.h>
#endif
#define uv__msan_unpoison(p, n) \
do { \
@ -71,8 +75,11 @@
# include <poll.h>
#endif /* _AIX */
#if defined(__APPLE__) && !TARGET_OS_IPHONE
# include <AvailabilityMacros.h>
#if defined(__APPLE__)
# include "darwin-syscalls.h"
# if !TARGET_OS_IPHONE
# include <AvailabilityMacros.h>
# endif
#endif
/*
@ -157,7 +164,8 @@ typedef struct uv__stream_queued_fds_s uv__stream_queued_fds_t;
/* loop flags */
enum {
UV_LOOP_BLOCK_SIGPROF = 0x1,
UV_LOOP_REAP_CHILDREN = 0x2
UV_LOOP_REAP_CHILDREN = 0x2,
UV_LOOP_ENABLE_IO_URING_SQPOLL = 0x4
};
/* flags of excluding ifaddr */
@ -243,6 +251,7 @@ int uv__close(int fd); /* preserves errno */
int uv__close_nocheckstdio(int fd);
int uv__close_nocancel(int fd);
int uv__socket(int domain, int type, int protocol);
int uv__sock_reuseport(int fd);
ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags);
void uv__make_close_pending(uv_handle_t* handle);
int uv__getiovmax(void);
@ -287,6 +296,9 @@ int uv__tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb);
int uv__tcp_nodelay(int fd, int on);
int uv__tcp_keepalive(int fd, int on, unsigned int delay);
/* tty */
void uv__tty_close(uv_tty_t* handle);
/* pipe */
int uv__pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb);
@ -332,6 +344,7 @@ int uv__random_sysctl(void* buf, size_t buflen);
/* io_uring */
#ifdef __linux__
int uv__iou_fs_close(uv_loop_t* loop, uv_fs_t* req);
int uv__iou_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req);
int uv__iou_fs_fsync_or_fdatasync(uv_loop_t* loop,
uv_fs_t* req,
uint32_t fsync_flags);
@ -350,6 +363,7 @@ int uv__iou_fs_symlink(uv_loop_t* loop, uv_fs_t* req);
int uv__iou_fs_unlink(uv_loop_t* loop, uv_fs_t* req);
#else
#define uv__iou_fs_close(loop, req) 0
#define uv__iou_fs_ftruncate(loop, req) 0
#define uv__iou_fs_fsync_or_fdatasync(loop, req, fsync_flags) 0
#define uv__iou_fs_link(loop, req) 0
#define uv__iou_fs_mkdir(loop, req) 0
@ -472,4 +486,44 @@ uv__fs_copy_file_range(int fd_in,
#define UV__CPU_AFFINITY_SUPPORTED 0
#endif
#ifdef __linux__
typedef struct {
long long quota_per_period;
long long period_length;
double proportions;
} uv__cpu_constraint;
int uv__get_constrained_cpu(uv__cpu_constraint* constraint);
#endif
#if defined(__sun) && !defined(__illumos__)
#ifdef SO_FLOW_NAME
/* Since it's impossible to detect the Solaris 11.4 version via OS macros,
* so we check the presence of the socket option SO_FLOW_NAME that was first
* introduced to Solaris 11.4 and define a custom macro for determining 11.4.
*/
#define UV__SOLARIS_11_4 (1)
#else
#define UV__SOLARIS_11_4 (0)
#endif
#endif
#if defined(EVFILT_USER) && defined(NOTE_TRIGGER)
/* EVFILT_USER is available since OS X 10.6, DragonFlyBSD 4.0,
* FreeBSD 8.1, and NetBSD 10.0.
*
* Note that even though EVFILT_USER is defined on the current system,
* it may still fail to work at runtime somehow. In that case, we fall
* back to pipe-based signaling.
*/
#define UV__KQUEUE_EVFILT_USER 1
/* Magic number of identifier used for EVFILT_USER during runtime detection.
* There are no Google hits for this number when I create it. That way,
* people will be directed here if this number gets printed due to some
* kqueue error and they google for help. */
#define UV__KQUEUE_EVFILT_USER_IDENT 0x1e7e7711
#else
#define UV__KQUEUE_EVFILT_USER 0
#endif
#endif /* UV_UNIX_INTERNAL_H_ */

View File

@ -99,6 +99,39 @@ int uv__io_fork(uv_loop_t* loop) {
int uv__io_check_fd(uv_loop_t* loop, int fd) {
struct kevent ev;
int rc;
struct stat sb;
#ifdef __APPLE__
char path[MAXPATHLEN];
#endif
if (uv__fstat(fd, &sb))
return UV__ERR(errno);
/* On FreeBSD, kqueue only supports EVFILT_READ notification for regular files
* and always reports ready events for writing, resulting in busy-looping.
*
* On Darwin, DragonFlyBSD, NetBSD and OpenBSD, kqueue reports ready events for
* regular files as readable and writable only once, acting like an EV_ONESHOT.
*
* Neither of the above cases should be added to the kqueue.
*/
if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode))
return UV_EINVAL;
#ifdef __APPLE__
/* On Darwin (both macOS and iOS), in addition to regular files, FIFOs also don't
* work properly with kqueue: the disconnection from the last writer won't trigger
* an event for kqueue in spite of what the man pages say. Thus, we also disallow
* the case of S_IFIFO. */
if (S_ISFIFO(sb.st_mode)) {
/* File descriptors of FIFO, pipe and kqueue share the same type of file,
* therefore there is no way to tell them apart via stat.st_mode&S_IFMT.
* Fortunately, FIFO is the only one that has a persisted file on filesystem,
* from which we're able to make the distinction for it. */
if (!fcntl(fd, F_GETPATH, path))
return UV_EINVAL;
}
#endif
rc = 0;
EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
@ -334,6 +367,17 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
continue;
}
#if UV__KQUEUE_EVFILT_USER
if (ev->filter == EVFILT_USER) {
w = &loop->async_io_watcher;
assert(fd == w->fd);
uv__metrics_update_idle_time(loop);
w->cb(loop, w, w->events);
nevents++;
continue;
}
#endif
if (ev->filter == EVFILT_VNODE) {
assert(w->events == POLLIN);
assert(w->pevents == POLLIN);

View File

@ -126,6 +126,7 @@
enum {
UV__IORING_SETUP_SQPOLL = 2u,
UV__IORING_SETUP_NO_SQARRAY = 0x10000u,
};
enum {
@ -147,6 +148,7 @@ enum {
UV__IORING_OP_MKDIRAT = 37,
UV__IORING_OP_SYMLINKAT = 38,
UV__IORING_OP_LINKAT = 39,
UV__IORING_OP_FTRUNCATE = 55,
};
enum {
@ -159,10 +161,6 @@ enum {
UV__IORING_SQ_CQ_OVERFLOW = 2u,
};
enum {
UV__MKDIRAT_SYMLINKAT_LINKAT = 1u,
};
struct uv__io_cqring_offsets {
uint32_t head;
uint32_t tail;
@ -475,8 +473,16 @@ static int uv__use_io_uring(void) {
use = atomic_load_explicit(&use_io_uring, memory_order_relaxed);
if (use == 0) {
/* Disable io_uring by default due to CVE-2024-22017. */
use = -1;
use = uv__kernel_version() >=
#if defined(__hppa__)
/* io_uring first supported on parisc in 6.1, functional in .51 */
/* https://lore.kernel.org/all/cb912694-b1fe-dbb0-4d8c-d608f3526905@gmx.de/ */
/* 6.1.51 */ 0x060133
#else
/* Older kernels have a bug where the sqpoll thread uses 100% CPU. */
/* 5.10.186 */ 0x050ABA
#endif
? 1 : -1;
/* But users can still enable it if they so desire. */
val = getenv("UV_USE_IO_URING");
@ -491,14 +497,6 @@ static int uv__use_io_uring(void) {
}
UV_EXTERN int uv__node_patch_is_using_io_uring(void) {
// This function exists only in the modified copy of libuv in the Node.js
// repository. Node.js checks if this function exists and, if it does, uses it
// to determine whether libuv is using io_uring or not.
return uv__use_io_uring();
}
static void uv__iou_init(int epollfd,
struct uv__iou* iou,
uint32_t entries,
@ -509,10 +507,13 @@ static void uv__iou_init(int epollfd,
size_t sqlen;
size_t maxlen;
size_t sqelen;
unsigned kernel_version;
uint32_t* sqarray;
uint32_t i;
char* sq;
char* sqe;
int ringfd;
int no_sqarray;
sq = MAP_FAILED;
sqe = MAP_FAILED;
@ -520,11 +521,15 @@ static void uv__iou_init(int epollfd,
if (!uv__use_io_uring())
return;
kernel_version = uv__kernel_version();
no_sqarray =
UV__IORING_SETUP_NO_SQARRAY * (kernel_version >= /* 6.6 */0x060600);
/* SQPOLL required CAP_SYS_NICE until linux v5.12 relaxed that requirement.
* Mostly academic because we check for a v5.13 kernel afterwards anyway.
*/
memset(&params, 0, sizeof(params));
params.flags = flags;
params.flags = flags | no_sqarray;
if (flags & UV__IORING_SETUP_SQPOLL)
params.sq_thread_idle = 10; /* milliseconds */
@ -586,7 +591,6 @@ static void uv__iou_init(int epollfd,
iou->sqhead = (uint32_t*) (sq + params.sq_off.head);
iou->sqtail = (uint32_t*) (sq + params.sq_off.tail);
iou->sqmask = *(uint32_t*) (sq + params.sq_off.ring_mask);
iou->sqarray = (uint32_t*) (sq + params.sq_off.array);
iou->sqflags = (uint32_t*) (sq + params.sq_off.flags);
iou->cqhead = (uint32_t*) (sq + params.cq_off.head);
iou->cqtail = (uint32_t*) (sq + params.cq_off.tail);
@ -600,13 +604,13 @@ static void uv__iou_init(int epollfd,
iou->sqelen = sqelen;
iou->ringfd = ringfd;
iou->in_flight = 0;
iou->flags = 0;
if (uv__kernel_version() >= /* 5.15.0 */ 0x050F00)
iou->flags |= UV__MKDIRAT_SYMLINKAT_LINKAT;
if (no_sqarray)
return;
sqarray = (uint32_t*) (sq + params.sq_off.array);
for (i = 0; i <= iou->sqmask; i++)
iou->sqarray[i] = i; /* Slot -> sqe identity mapping. */
sqarray[i] = i; /* Slot -> sqe identity mapping. */
return;
@ -622,7 +626,7 @@ fail:
static void uv__iou_delete(struct uv__iou* iou) {
if (iou->ringfd != -1) {
if (iou->ringfd > -1) {
munmap(iou->sq, iou->maxlen);
munmap(iou->sqe, iou->sqelen);
uv__close(iou->ringfd);
@ -636,7 +640,7 @@ int uv__platform_loop_init(uv_loop_t* loop) {
lfields = uv__get_internal_fields(loop);
lfields->ctl.ringfd = -1;
lfields->iou.ringfd = -1;
lfields->iou.ringfd = -2; /* "uninitialized" */
loop->inotify_watchers = NULL;
loop->inotify_fd = -1;
@ -645,7 +649,6 @@ int uv__platform_loop_init(uv_loop_t* loop) {
if (loop->backend_fd == -1)
return UV__ERR(errno);
uv__iou_init(loop->backend_fd, &lfields->iou, 64, UV__IORING_SETUP_SQPOLL);
uv__iou_init(loop->backend_fd, &lfields->ctl, 256, 0);
return 0;
@ -713,23 +716,17 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
* This avoids a problem where the same file description remains open
* in another process, causing repeated junk epoll events.
*
* Perform EPOLL_CTL_DEL immediately instead of going through
* io_uring's submit queue, otherwise the file descriptor may
* be closed by the time the kernel starts the operation.
*
* We pass in a dummy epoll_event, to work around a bug in old kernels.
*
* Work around a bug in kernels 3.10 to 3.19 where passing a struct that
* has the EPOLLWAKEUP flag set generates spurious audit syslog warnings.
*/
memset(&dummy, 0, sizeof(dummy));
if (inv == NULL) {
epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &dummy);
} else {
uv__epoll_ctl_prep(loop->backend_fd,
&lfields->ctl,
inv->prep,
EPOLL_CTL_DEL,
fd,
&dummy);
}
epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &dummy);
}
@ -764,6 +761,23 @@ static struct uv__io_uring_sqe* uv__iou_get_sqe(struct uv__iou* iou,
uint32_t mask;
uint32_t slot;
/* Lazily create the ring. State machine: -2 means uninitialized, -1 means
* initialization failed. Anything else is a valid ring file descriptor.
*/
if (iou->ringfd == -2) {
/* By default, the SQPOLL is not created. Enable only if the loop is
* configured with UV_LOOP_USE_IO_URING_SQPOLL.
*/
if ((loop->flags & UV_LOOP_ENABLE_IO_URING_SQPOLL) == 0) {
iou->ringfd = -1;
return NULL;
}
uv__iou_init(loop->backend_fd, iou, 64, UV__IORING_SETUP_SQPOLL);
if (iou->ringfd == -2)
iou->ringfd = -1; /* "failed" */
}
if (iou->ringfd == -1)
return NULL;
@ -787,7 +801,7 @@ static struct uv__io_uring_sqe* uv__iou_get_sqe(struct uv__iou* iou,
req->work_req.done = NULL;
uv__queue_init(&req->work_req.wq);
uv__req_register(loop, req);
uv__req_register(loop);
iou->in_flight++;
return sqe;
@ -850,6 +864,26 @@ int uv__iou_fs_close(uv_loop_t* loop, uv_fs_t* req) {
}
int uv__iou_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req) {
struct uv__io_uring_sqe* sqe;
struct uv__iou* iou;
if (uv__kernel_version() < /* 6.9 */0x060900)
return 0;
iou = &uv__get_internal_fields(loop)->iou;
sqe = uv__iou_get_sqe(iou, loop, req);
if (sqe == NULL)
return 0;
sqe->fd = req->file;
sqe->len = req->off;
sqe->opcode = UV__IORING_OP_FTRUNCATE;
uv__iou_submit(iou);
return 1;
}
int uv__iou_fs_fsync_or_fdatasync(uv_loop_t* loop,
uv_fs_t* req,
uint32_t fsync_flags) {
@ -879,11 +913,10 @@ int uv__iou_fs_link(uv_loop_t* loop, uv_fs_t* req) {
struct uv__io_uring_sqe* sqe;
struct uv__iou* iou;
iou = &uv__get_internal_fields(loop)->iou;
if (!(iou->flags & UV__MKDIRAT_SYMLINKAT_LINKAT))
if (uv__kernel_version() < /* 5.15.0 */0x050F00)
return 0;
iou = &uv__get_internal_fields(loop)->iou;
sqe = uv__iou_get_sqe(iou, loop, req);
if (sqe == NULL)
return 0;
@ -904,11 +937,10 @@ int uv__iou_fs_mkdir(uv_loop_t* loop, uv_fs_t* req) {
struct uv__io_uring_sqe* sqe;
struct uv__iou* iou;
iou = &uv__get_internal_fields(loop)->iou;
if (!(iou->flags & UV__MKDIRAT_SYMLINKAT_LINKAT))
if (uv__kernel_version() < /* 5.15.0 */0x050F00)
return 0;
iou = &uv__get_internal_fields(loop)->iou;
sqe = uv__iou_get_sqe(iou, loop, req);
if (sqe == NULL)
return 0;
@ -972,11 +1004,10 @@ int uv__iou_fs_symlink(uv_loop_t* loop, uv_fs_t* req) {
struct uv__io_uring_sqe* sqe;
struct uv__iou* iou;
iou = &uv__get_internal_fields(loop)->iou;
if (!(iou->flags & UV__MKDIRAT_SYMLINKAT_LINKAT))
if (uv__kernel_version() < /* 5.15.0 */0x050F00)
return 0;
iou = &uv__get_internal_fields(loop)->iou;
sqe = uv__iou_get_sqe(iou, loop, req);
if (sqe == NULL)
return 0;
@ -1155,7 +1186,7 @@ static void uv__poll_io_uring(uv_loop_t* loop, struct uv__iou* iou) {
req = (uv_fs_t*) (uintptr_t) e->user_data;
assert(req->type == UV_FS);
uv__req_unregister(loop, req);
uv__req_unregister(loop);
iou->in_flight--;
/* If the op is not supported by the kernel retry using the thread pool */
@ -1207,6 +1238,10 @@ static void uv__poll_io_uring(uv_loop_t* loop, struct uv__iou* iou) {
}
/* Only for EPOLL_CTL_ADD and EPOLL_CTL_MOD. EPOLL_CTL_DEL should always be
* executed immediately, otherwise the file descriptor may have been closed
* by the time the kernel starts the operation.
*/
static void uv__epoll_ctl_prep(int epollfd,
struct uv__iou* ctl,
struct epoll_event (*events)[256],
@ -1218,45 +1253,28 @@ static void uv__epoll_ctl_prep(int epollfd,
uint32_t mask;
uint32_t slot;
if (ctl->ringfd == -1) {
if (!epoll_ctl(epollfd, op, fd, e))
return;
assert(op == EPOLL_CTL_ADD || op == EPOLL_CTL_MOD);
assert(ctl->ringfd != -1);
if (op == EPOLL_CTL_DEL)
return; /* Ignore errors, may be racing with another thread. */
mask = ctl->sqmask;
slot = (*ctl->sqtail)++ & mask;
if (op != EPOLL_CTL_ADD)
abort();
pe = &(*events)[slot];
*pe = *e;
if (errno != EEXIST)
abort();
sqe = ctl->sqe;
sqe = &sqe[slot];
/* File descriptor that's been watched before, update event mask. */
if (!epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, e))
return;
memset(sqe, 0, sizeof(*sqe));
sqe->addr = (uintptr_t) pe;
sqe->fd = epollfd;
sqe->len = op;
sqe->off = fd;
sqe->opcode = UV__IORING_OP_EPOLL_CTL;
sqe->user_data = op | slot << 2 | (int64_t) fd << 32;
abort();
} else {
mask = ctl->sqmask;
slot = (*ctl->sqtail)++ & mask;
pe = &(*events)[slot];
*pe = *e;
sqe = ctl->sqe;
sqe = &sqe[slot];
memset(sqe, 0, sizeof(*sqe));
sqe->addr = (uintptr_t) pe;
sqe->fd = epollfd;
sqe->len = op;
sqe->off = fd;
sqe->opcode = UV__IORING_OP_EPOLL_CTL;
sqe->user_data = op | slot << 2 | (int64_t) fd << 32;
if ((*ctl->sqhead & mask) == (*ctl->sqtail & mask))
uv__epoll_ctl_flush(epollfd, ctl, events);
}
if ((*ctl->sqhead & mask) == (*ctl->sqtail & mask))
uv__epoll_ctl_flush(epollfd, ctl, events);
}
@ -1396,9 +1414,29 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
w->events = w->pevents;
e.events = w->pevents;
if (w == &loop->async_io_watcher)
/* Enable edge-triggered mode on async_io_watcher(eventfd),
* so that we're able to eliminate the overhead of reading
* the eventfd via system call on each event loop wakeup.
*/
e.events |= EPOLLET;
e.data.fd = w->fd;
fd = w->fd;
uv__epoll_ctl_prep(epollfd, ctl, &prep, op, w->fd, &e);
if (ctl->ringfd != -1) {
uv__epoll_ctl_prep(epollfd, ctl, &prep, op, fd, &e);
continue;
}
if (!epoll_ctl(epollfd, op, fd, &e))
continue;
assert(op == EPOLL_CTL_ADD);
assert(errno == EEXIST);
/* File descriptor that's been watched before, update event mask. */
if (epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &e))
abort();
}
inv.events = events;
@ -1486,8 +1524,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
*
* Ignore all errors because we may be racing with another thread
* when the file descriptor is closed.
*
* Perform EPOLL_CTL_DEL immediately instead of going through
* io_uring's submit queue, otherwise the file descriptor may
* be closed by the time the kernel starts the operation.
*/
uv__epoll_ctl_prep(epollfd, ctl, &prep, EPOLL_CTL_DEL, fd, pe);
epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, pe);
continue;
}
@ -1622,36 +1664,17 @@ done:
int uv_resident_set_memory(size_t* rss) {
char buf[1024];
const char* s;
ssize_t n;
long val;
int fd;
int rc;
int i;
do
fd = open("/proc/self/stat", O_RDONLY);
while (fd == -1 && errno == EINTR);
/* rss: 24th element */
rc = uv__slurp("/proc/self/stat", buf, sizeof(buf));
if (rc < 0)
return rc;
if (fd == -1)
return UV__ERR(errno);
do
n = read(fd, buf, sizeof(buf) - 1);
while (n == -1 && errno == EINTR);
uv__close(fd);
if (n == -1)
return UV__ERR(errno);
buf[n] = '\0';
s = strchr(buf, ' ');
if (s == NULL)
goto err;
s += 1;
if (*s != '(')
goto err;
s = strchr(s, ')');
/* find the last ')' */
s = strrchr(buf, ')');
if (s == NULL)
goto err;
@ -1663,9 +1686,7 @@ int uv_resident_set_memory(size_t* rss) {
errno = 0;
val = strtol(s, NULL, 10);
if (errno != 0)
goto err;
if (val < 0)
if (val < 0 || errno != 0)
goto err;
*rss = val * getpagesize();
@ -2270,6 +2291,136 @@ uint64_t uv_get_available_memory(void) {
}
static int uv__get_cgroupv2_constrained_cpu(const char* cgroup,
uv__cpu_constraint* constraint) {
char path[256];
char buf[1024];
unsigned int weight;
int cgroup_size;
const char* cgroup_trimmed;
char quota_buf[16];
if (strncmp(cgroup, "0::/", 4) != 0)
return UV_EINVAL;
/* Trim ending \n by replacing it with a 0 */
cgroup_trimmed = cgroup + sizeof("0::/") - 1; /* Skip the prefix "0::/" */
cgroup_size = (int)strcspn(cgroup_trimmed, "\n"); /* Find the first slash */
/* Construct the path to the cpu.max file */
snprintf(path, sizeof(path), "/sys/fs/cgroup/%.*s/cpu.max", cgroup_size,
cgroup_trimmed);
/* Read cpu.max */
if (uv__slurp(path, buf, sizeof(buf)) < 0)
return UV_EIO;
if (sscanf(buf, "%15s %llu", quota_buf, &constraint->period_length) != 2)
return UV_EINVAL;
if (strncmp(quota_buf, "max", 3) == 0)
constraint->quota_per_period = LLONG_MAX;
else if (sscanf(quota_buf, "%lld", &constraint->quota_per_period) != 1)
return UV_EINVAL; // conversion failed
/* Construct the path to the cpu.weight file */
snprintf(path, sizeof(path), "/sys/fs/cgroup/%.*s/cpu.weight", cgroup_size,
cgroup_trimmed);
/* Read cpu.weight */
if (uv__slurp(path, buf, sizeof(buf)) < 0)
return UV_EIO;
if (sscanf(buf, "%u", &weight) != 1)
return UV_EINVAL;
constraint->proportions = (double)weight / 100.0;
return 0;
}
static char* uv__cgroup1_find_cpu_controller(const char* cgroup,
int* cgroup_size) {
/* Seek to the cpu controller line. */
char* cgroup_cpu = strstr(cgroup, ":cpu,");
if (cgroup_cpu != NULL) {
/* Skip the controller prefix to the start of the cgroup path. */
cgroup_cpu += sizeof(":cpu,") - 1;
/* Determine the length of the cgroup path, excluding the newline. */
*cgroup_size = (int)strcspn(cgroup_cpu, "\n");
}
return cgroup_cpu;
}
static int uv__get_cgroupv1_constrained_cpu(const char* cgroup,
uv__cpu_constraint* constraint) {
char path[256];
char buf[1024];
unsigned int shares;
int cgroup_size;
char* cgroup_cpu;
cgroup_cpu = uv__cgroup1_find_cpu_controller(cgroup, &cgroup_size);
if (cgroup_cpu == NULL)
return UV_EIO;
/* Construct the path to the cpu.cfs_quota_us file */
snprintf(path, sizeof(path), "/sys/fs/cgroup/%.*s/cpu.cfs_quota_us",
cgroup_size, cgroup_cpu);
if (uv__slurp(path, buf, sizeof(buf)) < 0)
return UV_EIO;
if (sscanf(buf, "%lld", &constraint->quota_per_period) != 1)
return UV_EINVAL;
/* Construct the path to the cpu.cfs_period_us file */
snprintf(path, sizeof(path), "/sys/fs/cgroup/%.*s/cpu.cfs_period_us",
cgroup_size, cgroup_cpu);
/* Read cpu.cfs_period_us */
if (uv__slurp(path, buf, sizeof(buf)) < 0)
return UV_EIO;
if (sscanf(buf, "%lld", &constraint->period_length) != 1)
return UV_EINVAL;
/* Construct the path to the cpu.shares file */
snprintf(path, sizeof(path), "/sys/fs/cgroup/%.*s/cpu.shares", cgroup_size,
cgroup_cpu);
/* Read cpu.shares */
if (uv__slurp(path, buf, sizeof(buf)) < 0)
return UV_EIO;
if (sscanf(buf, "%u", &shares) != 1)
return UV_EINVAL;
constraint->proportions = (double)shares / 1024.0;
return 0;
}
int uv__get_constrained_cpu(uv__cpu_constraint* constraint) {
char cgroup[1024];
/* Read the cgroup from /proc/self/cgroup */
if (uv__slurp("/proc/self/cgroup", cgroup, sizeof(cgroup)) < 0)
return UV_EIO;
/* Check if the system is using cgroup v2 by examining /proc/self/cgroup
* The entry for cgroup v2 is always in the format "0::$PATH"
* see https://docs.kernel.org/admin-guide/cgroup-v2.html */
if (strncmp(cgroup, "0::/", 4) == 0)
return uv__get_cgroupv2_constrained_cpu(cgroup, constraint);
else
return uv__get_cgroupv1_constrained_cpu(cgroup, constraint);
}
void uv_loadavg(double avg[3]) {
struct sysinfo info;
char buf[128]; /* Large enough to hold all of /proc/loadavg. */

View File

@ -217,6 +217,14 @@ int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) {
return 0;
}
#if defined(__linux__)
if (option == UV_LOOP_USE_IO_URING_SQPOLL) {
loop->flags |= UV_LOOP_ENABLE_IO_URING_SQPOLL;
return 0;
}
#endif
if (option != UV_LOOP_BLOCK_SIGNAL)
return UV_ENOSYS;

View File

@ -76,8 +76,13 @@ int uv_pipe_bind2(uv_pipe_t* handle,
if (name == NULL)
return UV_EINVAL;
/* namelen==0 on Linux means autobind the listen socket in the abstract
* socket namespace, see `man 7 unix` for details.
*/
#if !defined(__linux__)
if (namelen == 0)
return UV_EINVAL;
#endif
if (includes_nul(name, namelen))
return UV_EINVAL;
@ -344,8 +349,15 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
uv__peersockfunc func,
char* buffer,
size_t* size) {
#if defined(__linux__)
static const int is_linux = 1;
#else
static const int is_linux = 0;
#endif
struct sockaddr_un sa;
socklen_t addrlen;
size_t slop;
char* p;
int err;
addrlen = sizeof(sa);
@ -359,17 +371,20 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
return err;
}
#if defined(__linux__)
if (sa.sun_path[0] == 0)
/* Linux abstract namespace */
slop = 1;
if (is_linux && sa.sun_path[0] == '\0') {
/* Linux abstract namespace. Not zero-terminated. */
slop = 0;
addrlen -= offsetof(struct sockaddr_un, sun_path);
else
#endif
addrlen = strlen(sa.sun_path);
} else {
p = memchr(sa.sun_path, '\0', sizeof(sa.sun_path));
if (p == NULL)
p = ARRAY_END(sa.sun_path);
addrlen = p - sa.sun_path;
}
if ((size_t)addrlen >= *size) {
*size = addrlen + 1;
if ((size_t)addrlen + slop > *size) {
*size = addrlen + slop;
return UV_ENOBUFS;
}
@ -487,7 +502,11 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
int uv_pipe(uv_os_fd_t fds[2], int read_flags, int write_flags) {
uv_os_fd_t temp[2];
int err;
#if defined(__FreeBSD__) || defined(__linux__)
#if defined(__linux__) || \
defined(__FreeBSD__) || \
defined(__OpenBSD__) || \
defined(__DragonFly__) || \
defined(__NetBSD__)
int flags = O_CLOEXEC;
if ((read_flags & UV_NONBLOCK_PIPE) && (write_flags & UV_NONBLOCK_PIPE))

View File

@ -55,7 +55,8 @@
extern char **environ;
#endif
#if defined(__linux__)
#if defined(__linux__) || \
defined(__GNU__)
# include <grp.h>
#endif
@ -63,11 +64,7 @@ extern char **environ;
# include "zos-base.h"
#endif
#if defined(__APPLE__) || \
defined(__DragonFly__) || \
defined(__FreeBSD__) || \
defined(__NetBSD__) || \
defined(__OpenBSD__)
#ifdef UV_HAVE_KQUEUE
#include <sys/event.h>
#else
#define UV_USE_SIGCHLD

View File

@ -195,7 +195,7 @@ static void uv__signal_handler(int signum) {
for (handle = uv__signal_first_handle(signum);
handle != NULL && handle->signum == signum;
handle = RB_NEXT(uv__signal_tree_s, &uv__signal_tree, handle)) {
handle = RB_NEXT(uv__signal_tree_s, handle)) {
int r;
msg.signum = signum;

View File

@ -457,7 +457,7 @@ void uv__stream_destroy(uv_stream_t* stream) {
assert(stream->flags & UV_HANDLE_CLOSED);
if (stream->connect_req) {
uv__req_unregister(stream->loop, stream->connect_req);
uv__req_unregister(stream->loop);
stream->connect_req->cb(stream->connect_req, UV_ECANCELED);
stream->connect_req = NULL;
}
@ -642,7 +642,7 @@ static void uv__drain(uv_stream_t* stream) {
if ((stream->flags & UV_HANDLE_CLOSING) ||
!(stream->flags & UV_HANDLE_SHUT)) {
stream->shutdown_req = NULL;
uv__req_unregister(stream->loop, req);
uv__req_unregister(stream->loop);
err = 0;
if (stream->flags & UV_HANDLE_CLOSING)
@ -698,7 +698,8 @@ static int uv__write_req_update(uv_stream_t* stream,
do {
len = n < buf->len ? n : buf->len;
buf->base += len;
if (buf->len != 0)
buf->base += len;
buf->len -= len;
buf += (buf->len == 0); /* Advance to next buffer if this one is empty. */
n -= len;
@ -912,7 +913,7 @@ static void uv__write_callbacks(uv_stream_t* stream) {
q = uv__queue_head(&pq);
req = uv__queue_data(q, uv_write_t, queue);
uv__queue_remove(q);
uv__req_unregister(stream->loop, req);
uv__req_unregister(stream->loop);
if (req->bufs != NULL) {
stream->write_queue_size -= uv__write_req_size(req);
@ -979,11 +980,13 @@ static int uv__stream_queue_fd(uv_stream_t* stream, int fd) {
static int uv__stream_recv_cmsg(uv_stream_t* stream, struct msghdr* msg) {
struct cmsghdr* cmsg;
char* p;
char* pe;
int fd;
int err;
size_t i;
size_t count;
err = 0;
for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
if (cmsg->cmsg_type != SCM_RIGHTS) {
fprintf(stderr, "ignoring non-SCM_RIGHTS ancillary data: %d\n",
@ -996,24 +999,26 @@ static int uv__stream_recv_cmsg(uv_stream_t* stream, struct msghdr* msg) {
assert(count % sizeof(fd) == 0);
count /= sizeof(fd);
for (i = 0; i < count; i++) {
memcpy(&fd, (char*) CMSG_DATA(cmsg) + i * sizeof(fd), sizeof(fd));
/* Already has accepted fd, queue now */
if (stream->accepted_fd != -1) {
err = uv__stream_queue_fd(stream, fd);
if (err != 0) {
/* Close rest */
for (; i < count; i++)
uv__close(fd);
return err;
}
} else {
stream->accepted_fd = fd;
p = (void*) CMSG_DATA(cmsg);
pe = p + count * sizeof(fd);
while (p < pe) {
memcpy(&fd, p, sizeof(fd));
p += sizeof(fd);
if (err == 0) {
if (stream->accepted_fd == -1)
stream->accepted_fd = fd;
else
err = uv__stream_queue_fd(stream, fd);
}
if (err != 0)
uv__close(fd);
}
}
return 0;
return err;
}
@ -1268,7 +1273,7 @@ static void uv__stream_connect(uv_stream_t* stream) {
return;
stream->connect_req = NULL;
uv__req_unregister(stream->loop, req);
uv__req_unregister(stream->loop);
if (error < 0 || uv__queue_empty(&stream->write_queue)) {
uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT);

View File

@ -167,6 +167,12 @@ int uv__tcp_bind(uv_tcp_t* tcp,
if (setsockopt(tcp->io_watcher.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
return UV__ERR(errno);
if (flags & UV_TCP_REUSEPORT) {
err = uv__sock_reuseport(tcp->io_watcher.fd);
if (err)
return err;
}
#ifndef __OpenBSD__
#ifdef IPV6_V6ONLY
if (addr->sa_family == AF_INET6) {
@ -452,6 +458,14 @@ int uv__tcp_nodelay(int fd, int on) {
}
#if (defined(UV__SOLARIS_11_4) && !UV__SOLARIS_11_4) || \
(defined(__DragonFly__) && __DragonFly_version < 500702)
/* DragonFlyBSD <500702 and Solaris <11.4 require millisecond units
* for TCP keepalive options. */
#define UV_KEEPALIVE_FACTOR(x) (x *= 1000)
#else
#define UV_KEEPALIVE_FACTOR(x)
#endif
int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
int idle;
int intvl;
@ -467,8 +481,8 @@ int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
if (!on)
return 0;
if (delay == 0)
return -1;
if (delay < 1)
return UV_EINVAL;
#ifdef __sun
/* The implementation of TCP keep-alive on Solaris/SmartOS is a bit unusual
@ -501,49 +515,53 @@ int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
if (idle > 10*24*60*60)
idle = 10*24*60*60;
UV_KEEPALIVE_FACTOR(idle);
/* `TCP_KEEPIDLE`, `TCP_KEEPINTVL`, and `TCP_KEEPCNT` were not available on Solaris
* until version 11.4, but let's take a chance here. */
#if defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) && defined(TCP_KEEPCNT)
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle)))
return UV__ERR(errno);
intvl = idle/3;
intvl = 10; /* required at least 10 seconds */
UV_KEEPALIVE_FACTOR(intvl);
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl)))
return UV__ERR(errno);
cnt = 3;
cnt = 1; /* 1 retry, ensure (TCP_KEEPINTVL * TCP_KEEPCNT) is 10 seconds */
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt)))
return UV__ERR(errno);
#else
/* Fall back to the first implementation of tcp-alive mechanism for older Solaris,
* simulate the tcp-alive mechanism on other platforms via `TCP_KEEPALIVE_THRESHOLD` + `TCP_KEEPALIVE_ABORT_THRESHOLD`.
*/
idle *= 1000; /* kernel expects milliseconds */
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD, &idle, sizeof(idle)))
return UV__ERR(errno);
/* Note that the consequent probes will not be sent at equal intervals on Solaris,
* but will be sent using the exponential backoff algorithm. */
intvl = idle/3;
cnt = 3;
int time_to_abort = intvl * cnt;
int time_to_abort = 10; /* 10 seconds */
UV_KEEPALIVE_FACTOR(time_to_abort);
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD, &time_to_abort, sizeof(time_to_abort)))
return UV__ERR(errno);
#endif
#else /* !defined(__sun) */
idle = delay;
UV_KEEPALIVE_FACTOR(idle);
#ifdef TCP_KEEPIDLE
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay)))
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle)))
return UV__ERR(errno);
#elif defined(TCP_KEEPALIVE)
/* Darwin/macOS uses TCP_KEEPALIVE in place of TCP_KEEPIDLE. */
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay)))
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &idle, sizeof(idle)))
return UV__ERR(errno);
#endif
#ifdef TCP_KEEPINTVL
intvl = 1; /* 1 second; same as default on Win32 */
intvl = 1; /* 1 second; same as default on Win32 */
UV_KEEPALIVE_FACTOR(intvl);
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl)))
return UV__ERR(errno);
#endif
@ -612,7 +630,7 @@ void uv__tcp_close(uv_tcp_t* handle) {
int uv_socketpair(int type, int protocol, uv_os_sock_t fds[2], int flags0, int flags1) {
uv_os_sock_t temp[2];
int err;
#if defined(__FreeBSD__) || defined(__linux__)
#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC)
int flags;
flags = type | SOCK_CLOEXEC;

View File

@ -335,6 +335,37 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
}
void uv__tty_close(uv_tty_t* handle) {
int expected;
int fd;
fd = handle->io_watcher.fd;
if (fd == -1)
goto done;
/* This is used for uv_tty_reset_mode() */
do
expected = 0;
while (!atomic_compare_exchange_strong(&termios_spinlock, &expected, 1));
if (fd == orig_termios_fd) {
/* XXX(bnoordhuis) the tcsetattr is probably wrong when there are still
* other uv_tty_t handles active that refer to the same tty/pty but it's
* hard to recognize that particular situation without maintaining some
* kind of process-global data structure, and that still won't work in a
* multi-process setup.
*/
uv__tcsetattr(fd, TCSANOW, &orig_termios);
orig_termios_fd = -1;
}
atomic_store(&termios_spinlock, 0);
done:
uv__stream_close((uv_stream_t*) handle);
}
int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) {
struct winsize ws;
int err;
@ -452,7 +483,7 @@ int uv_tty_reset_mode(void) {
saved_errno = errno;
if (atomic_exchange(&termios_spinlock, 1))
return UV_EBUSY; /* In uv_tty_set_mode(). */
return UV_EBUSY; /* In uv_tty_set_mode() or uv__tty_close(). */
err = 0;
if (orig_termios_fd != -1)

193
deps/uv/src/unix/udp.c vendored
View File

@ -100,7 +100,7 @@ static void uv__udp_run_completed(uv_udp_t* handle) {
uv__queue_remove(q);
req = uv__queue_data(q, uv_udp_send_t, queue);
uv__req_unregister(handle->loop, req);
uv__req_unregister(handle->loop);
handle->send_queue_size -= uv__count_bufs(req->bufs, req->nbufs);
handle->send_queue_count--;
@ -141,14 +141,14 @@ static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents) {
if (revents & POLLIN)
uv__udp_recvmsg(handle);
if (revents & POLLOUT) {
if (revents & POLLOUT && !uv__is_closing(handle)) {
uv__udp_sendmsg(handle);
uv__udp_run_completed(handle);
}
}
static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) {
#if defined(__linux__) || defined(__FreeBSD__)
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
struct sockaddr_in6 peers[20];
struct iovec iov[ARRAY_SIZE(peers)];
struct mmsghdr msgs[ARRAY_SIZE(peers)];
@ -173,11 +173,18 @@ static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) {
msgs[k].msg_hdr.msg_control = NULL;
msgs[k].msg_hdr.msg_controllen = 0;
msgs[k].msg_hdr.msg_flags = 0;
msgs[k].msg_len = 0;
}
#if defined(__APPLE__)
do
nread = recvmsg_x(handle->io_watcher.fd, msgs, chunks, MSG_DONTWAIT);
while (nread == -1 && errno == EINTR);
#else
do
nread = recvmmsg(handle->io_watcher.fd, msgs, chunks, 0, NULL);
while (nread == -1 && errno == EINTR);
#endif
if (nread < 1) {
if (nread == 0 || errno == EAGAIN || errno == EWOULDBLOCK)
@ -204,9 +211,9 @@ static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) {
handle->recv_cb(handle, 0, buf, NULL, UV_UDP_MMSG_FREE);
}
return nread;
#else /* __linux__ || ____FreeBSD__ */
#else /* __linux__ || ____FreeBSD__ || __APPLE__ */
return UV_ENOSYS;
#endif /* __linux__ || ____FreeBSD__ */
#endif /* __linux__ || ____FreeBSD__ || __APPLE__ */
}
static void uv__udp_recvmsg(uv_udp_t* handle) {
@ -275,8 +282,61 @@ static void uv__udp_recvmsg(uv_udp_t* handle) {
&& handle->recv_cb != NULL);
}
static void uv__udp_sendmsg(uv_udp_t* handle) {
#if defined(__linux__) || defined(__FreeBSD__)
static void uv__udp_sendmsg_one(uv_udp_t* handle, uv_udp_send_t* req) {
struct uv__queue* q;
struct msghdr h;
ssize_t size;
for (;;) {
memset(&h, 0, sizeof h);
if (req->addr.ss_family == AF_UNSPEC) {
h.msg_name = NULL;
h.msg_namelen = 0;
} else {
h.msg_name = &req->addr;
if (req->addr.ss_family == AF_INET6)
h.msg_namelen = sizeof(struct sockaddr_in6);
else if (req->addr.ss_family == AF_INET)
h.msg_namelen = sizeof(struct sockaddr_in);
else if (req->addr.ss_family == AF_UNIX)
h.msg_namelen = sizeof(struct sockaddr_un);
else {
assert(0 && "unsupported address family");
abort();
}
}
h.msg_iov = (struct iovec*) req->bufs;
h.msg_iovlen = req->nbufs;
do
size = sendmsg(handle->io_watcher.fd, &h, 0);
while (size == -1 && errno == EINTR);
if (size == -1)
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
return;
req->status = (size == -1 ? UV__ERR(errno) : size);
/* Sending a datagram is an atomic operation: either all data
* is written or nothing is (and EMSGSIZE is raised). That is
* why we don't handle partial writes. Just pop the request
* off the write queue and onto the completed queue, done.
*/
uv__queue_remove(&req->queue);
uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
uv__io_feed(handle->loop, &handle->io_watcher);
if (uv__queue_empty(&handle->write_queue))
return;
q = uv__queue_head(&handle->write_queue);
req = uv__queue_data(q, uv_udp_send_t, queue);
}
}
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
static void uv__udp_sendmsg_many(uv_udp_t* handle) {
uv_udp_send_t* req;
struct mmsghdr h[20];
struct mmsghdr* p;
@ -285,16 +345,11 @@ static void uv__udp_sendmsg(uv_udp_t* handle) {
size_t pkts;
size_t i;
if (uv__queue_empty(&handle->write_queue))
return;
write_queue_drain:
for (pkts = 0, q = uv__queue_head(&handle->write_queue);
pkts < ARRAY_SIZE(h) && q != &handle->write_queue;
++pkts, q = uv__queue_head(q)) {
assert(q != NULL);
req = uv__queue_data(q, uv_udp_send_t, queue);
assert(req != NULL);
p = &h[pkts];
memset(p, 0, sizeof(*p));
@ -318,9 +373,15 @@ write_queue_drain:
h[pkts].msg_hdr.msg_iovlen = req->nbufs;
}
#if defined(__APPLE__)
do
npkts = sendmsg_x(handle->io_watcher.fd, h, pkts, MSG_DONTWAIT);
while (npkts == -1 && errno == EINTR);
#else
do
npkts = sendmmsg(handle->io_watcher.fd, h, pkts, 0);
while (npkts == -1 && errno == EINTR);
#endif
if (npkts < 1) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
@ -328,10 +389,7 @@ write_queue_drain:
for (i = 0, q = uv__queue_head(&handle->write_queue);
i < pkts && q != &handle->write_queue;
++i, q = uv__queue_head(&handle->write_queue)) {
assert(q != NULL);
req = uv__queue_data(q, uv_udp_send_t, queue);
assert(req != NULL);
req->status = UV__ERR(errno);
uv__queue_remove(&req->queue);
uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
@ -346,10 +404,7 @@ write_queue_drain:
for (i = 0, q = uv__queue_head(&handle->write_queue);
i < (size_t)npkts && q != &handle->write_queue;
++i, q = uv__queue_head(&handle->write_queue)) {
assert(q != NULL);
req = uv__queue_data(q, uv_udp_send_t, queue);
assert(req != NULL);
req->status = req->bufs[0].len;
/* Sending a datagram is an atomic operation: either all data
@ -364,75 +419,48 @@ write_queue_drain:
/* couldn't batch everything, continue sending (jump to avoid stack growth) */
if (!uv__queue_empty(&handle->write_queue))
goto write_queue_drain;
uv__io_feed(handle->loop, &handle->io_watcher);
#else /* __linux__ || ____FreeBSD__ */
uv_udp_send_t* req;
struct msghdr h;
}
#endif /* __linux__ || ____FreeBSD__ || __APPLE__ */
static void uv__udp_sendmsg(uv_udp_t* handle) {
struct uv__queue* q;
ssize_t size;
uv_udp_send_t* req;
while (!uv__queue_empty(&handle->write_queue)) {
q = uv__queue_head(&handle->write_queue);
assert(q != NULL);
if (uv__queue_empty(&handle->write_queue))
return;
req = uv__queue_data(q, uv_udp_send_t, queue);
assert(req != NULL);
q = uv__queue_head(&handle->write_queue);
req = uv__queue_data(q, uv_udp_send_t, queue);
memset(&h, 0, sizeof h);
if (req->addr.ss_family == AF_UNSPEC) {
h.msg_name = NULL;
h.msg_namelen = 0;
} else {
h.msg_name = &req->addr;
if (req->addr.ss_family == AF_INET6)
h.msg_namelen = sizeof(struct sockaddr_in6);
else if (req->addr.ss_family == AF_INET)
h.msg_namelen = sizeof(struct sockaddr_in);
else if (req->addr.ss_family == AF_UNIX)
h.msg_namelen = sizeof(struct sockaddr_un);
else {
assert(0 && "unsupported address family");
abort();
}
}
h.msg_iov = (struct iovec*) req->bufs;
h.msg_iovlen = req->nbufs;
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
/* Use sendmmsg() if this send request contains more than one datagram OR
* there is more than one send request (because that automatically implies
* there is more than one datagram.)
*/
if (req->nbufs != 1 || &handle->write_queue != uv__queue_next(&req->queue))
return uv__udp_sendmsg_many(handle);
#endif
do {
size = sendmsg(handle->io_watcher.fd, &h, 0);
} while (size == -1 && errno == EINTR);
if (size == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
break;
}
req->status = (size == -1 ? UV__ERR(errno) : size);
/* Sending a datagram is an atomic operation: either all data
* is written or nothing is (and EMSGSIZE is raised). That is
* why we don't handle partial writes. Just pop the request
* off the write queue and onto the completed queue, done.
*/
uv__queue_remove(&req->queue);
uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
uv__io_feed(handle->loop, &handle->io_watcher);
}
#endif /* __linux__ || ____FreeBSD__ */
return uv__udp_sendmsg_one(handle, req);
}
/* On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but with some additional
* refinements for programs that use multicast.
* refinements for programs that use multicast. Therefore we preferentially
* set SO_REUSEPORT over SO_REUSEADDR here, but we set SO_REUSEPORT only
* when that socket option doesn't have the capability of load balancing.
* Otherwise, we fall back to SO_REUSEADDR.
*
* Linux as of 3.9 has a SO_REUSEPORT socket option but with semantics that
* are different from the BSDs: it _shares_ the port rather than steal it
* from the current listener. While useful, it's not something we can emulate
* on other platforms so we don't enable it.
* Linux as of 3.9, DragonflyBSD 3.6, AIX 7.2.5 have the SO_REUSEPORT socket
* option but with semantics that are different from the BSDs: it _shares_
* the port rather than steals it from the current listener. While useful,
* it's not something we can emulate on other platforms so we don't enable it.
*
* zOS does not support getsockname with SO_REUSEPORT option when using
* AF_UNIX.
*/
static int uv__set_reuse(int fd) {
static int uv__sock_reuseaddr(int fd) {
int yes;
yes = 1;
@ -449,7 +477,7 @@ static int uv__set_reuse(int fd) {
return UV__ERR(errno);
}
#elif defined(SO_REUSEPORT) && !defined(__linux__) && !defined(__GNU__) && \
!defined(__sun__)
!defined(__sun__) && !defined(__DragonFly__) && !defined(_AIX73)
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
return UV__ERR(errno);
#else
@ -492,7 +520,8 @@ int uv__udp_bind(uv_udp_t* handle,
int fd;
/* Check for bad flags. */
if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR | UV_UDP_LINUX_RECVERR))
if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR |
UV_UDP_REUSEPORT | UV_UDP_LINUX_RECVERR))
return UV_EINVAL;
/* Cannot set IPv6-only mode on non-IPv6 socket. */
@ -515,7 +544,13 @@ int uv__udp_bind(uv_udp_t* handle,
}
if (flags & UV_UDP_REUSEADDR) {
err = uv__set_reuse(fd);
err = uv__sock_reuseaddr(fd);
if (err)
return err;
}
if (flags & UV_UDP_REUSEPORT) {
err = uv__sock_reuseport(fd);
if (err)
return err;
}
@ -722,7 +757,7 @@ int uv__udp_send(uv_udp_send_t* req,
req->bufs = uv__malloc(nbufs * sizeof(bufs[0]));
if (req->bufs == NULL) {
uv__req_unregister(handle->loop, req);
uv__req_unregister(handle->loop);
return UV_ENOMEM;
}
@ -1015,7 +1050,7 @@ int uv__udp_init_ex(uv_loop_t* loop,
int uv_udp_using_recvmmsg(const uv_udp_t* handle) {
#if defined(__linux__) || defined(__FreeBSD__)
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
if (handle->flags & UV_HANDLE_UDP_RECVMMSG)
return 1;
#endif
@ -1037,7 +1072,7 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
if (err)
return err;
err = uv__set_reuse(sock);
err = uv__sock_reuseaddr(sock);
if (err)
return err;

View File

@ -233,13 +233,13 @@ void uv__threadpool_cleanup(void);
#define uv__has_active_reqs(loop) \
((loop)->active_reqs.count > 0)
#define uv__req_register(loop, req) \
#define uv__req_register(loop) \
do { \
(loop)->active_reqs.count++; \
} \
while (0)
#define uv__req_unregister(loop, req) \
#define uv__req_unregister(loop) \
do { \
assert(uv__has_active_reqs(loop)); \
(loop)->active_reqs.count--; \
@ -349,7 +349,7 @@ void uv__threadpool_cleanup(void);
#define uv__req_init(loop, req, typ) \
do { \
UV_REQ_INIT(req, typ); \
uv__req_register(loop, req); \
uv__req_register(loop); \
} \
while (0)
@ -400,7 +400,6 @@ void uv__metrics_set_provider_entry_time(uv_loop_t* loop);
struct uv__iou {
uint32_t* sqhead;
uint32_t* sqtail;
uint32_t* sqarray;
uint32_t sqmask;
uint32_t* sqflags;
uint32_t* cqhead;
@ -415,7 +414,6 @@ struct uv__iou {
size_t sqelen;
int ringfd;
uint32_t in_flight;
uint32_t flags;
};
#endif /* __linux__ */

View File

@ -78,6 +78,7 @@ int uv_translate_sys_error(int sys_errno) {
case WSAEADDRNOTAVAIL: return UV_EADDRNOTAVAIL;
case WSAEAFNOSUPPORT: return UV_EAFNOSUPPORT;
case WSAEWOULDBLOCK: return UV_EAGAIN;
case ERROR_NO_DATA: return UV_EAGAIN;
case WSAEALREADY: return UV_EALREADY;
case ERROR_INVALID_FLAGS: return UV_EBADF;
case ERROR_INVALID_HANDLE: return UV_EBADF;
@ -157,7 +158,6 @@ int uv_translate_sys_error(int sys_errno) {
case ERROR_ACCESS_DENIED: return UV_EPERM;
case ERROR_PRIVILEGE_NOT_HELD: return UV_EPERM;
case ERROR_BAD_PIPE: return UV_EPIPE;
case ERROR_NO_DATA: return UV_EPIPE;
case ERROR_PIPE_NOT_CONNECTED: return UV_EPIPE;
case WSAESHUTDOWN: return UV_EPIPE;
case WSAEPROTONOSUPPORT: return UV_EPROTONOSUPPORT;
@ -168,6 +168,16 @@ int uv_translate_sys_error(int sys_errno) {
case ERROR_INVALID_FUNCTION: return UV_EISDIR;
case ERROR_META_EXPANSION_TOO_LONG: return UV_E2BIG;
case WSAESOCKTNOSUPPORT: return UV_ESOCKTNOSUPPORT;
case ERROR_BAD_EXE_FORMAT: return UV_EFTYPE;
default: return UV_UNKNOWN;
}
}
int uv_translate_write_sys_error(int sys_errno) {
switch (sys_errno) {
case ERROR_BROKEN_PIPE: return UV_EPIPE;
case ERROR_NO_DATA: return UV_EPIPE;
default:
return uv_translate_sys_error(sys_errno);
}
}

View File

@ -561,7 +561,25 @@ void uv__process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
}
} else {
err = GET_REQ_ERROR(req);
handle->cb(handle, NULL, 0, uv_translate_sys_error(err));
/*
* Check whether the ERROR_ACCESS_DENIED is caused by the watched directory
* being actually deleted (not an actual error) or a legit error. Retrieve
* FileStandardInfo to check whether the directory is pending deletion.
*/
FILE_STANDARD_INFO info;
if (err == ERROR_ACCESS_DENIED &&
handle->dirw != NULL &&
GetFileInformationByHandleEx(handle->dir_handle,
FileStandardInfo,
&info,
sizeof(info)) &&
info.Directory &&
info.DeletePending) {
uv__convert_utf16_to_utf8(handle->dirw, -1, &filename);
handle->cb(handle, filename, UV_RENAME, 0);
} else {
handle->cb(handle, NULL, 0, uv_translate_sys_error(err));
}
}
if (handle->flags & UV_HANDLE_CLOSING) {

309
deps/uv/src/win/fs.c vendored
View File

@ -46,6 +46,17 @@
#define UV_FS_FREE_PTR 0x0008
#define UV_FS_CLEANEDUP 0x0010
#ifndef FILE_DISPOSITION_DELETE
#define FILE_DISPOSITION_DELETE 0x0001
#endif /* FILE_DISPOSITION_DELETE */
#ifndef FILE_DISPOSITION_POSIX_SEMANTICS
#define FILE_DISPOSITION_POSIX_SEMANTICS 0x0002
#endif /* FILE_DISPOSITION_POSIX_SEMANTICS */
#ifndef FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE
#define FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE 0x0010
#endif /* FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE */
#define INIT(subtype) \
do { \
@ -58,7 +69,7 @@
#define POST \
do { \
if (cb != NULL) { \
uv__req_register(loop, req); \
uv__req_register(loop); \
uv__work_submit(loop, \
&req->work_req, \
UV__WORK_FAST_IO, \
@ -97,13 +108,14 @@
return; \
}
#define MILLION ((int64_t) 1000 * 1000)
#define BILLION ((int64_t) 1000 * 1000 * 1000)
#define NSEC_PER_TICK 100
#define TICKS_PER_SEC ((int64_t) 1e9 / NSEC_PER_TICK)
static const int64_t WIN_TO_UNIX_TICK_OFFSET = 11644473600 * TICKS_PER_SEC;
static void uv__filetime_to_timespec(uv_timespec_t *ts, int64_t filetime) {
filetime -= 116444736 * BILLION;
ts->tv_sec = (long) (filetime / (10 * MILLION));
ts->tv_nsec = (long) ((filetime - ts->tv_sec * 10 * MILLION) * 100U);
filetime -= WIN_TO_UNIX_TICK_OFFSET;
ts->tv_sec = filetime / TICKS_PER_SEC;
ts->tv_nsec = (filetime % TICKS_PER_SEC) * NSEC_PER_TICK;
if (ts->tv_nsec < 0) {
ts->tv_sec -= 1;
ts->tv_nsec += 1e9;
@ -112,7 +124,7 @@ static void uv__filetime_to_timespec(uv_timespec_t *ts, int64_t filetime) {
#define TIME_T_TO_FILETIME(time, filetime_ptr) \
do { \
int64_t bigtime = ((time) * 10 * MILLION + 116444736 * BILLION); \
int64_t bigtime = ((time) * TICKS_PER_SEC + WIN_TO_UNIX_TICK_OFFSET); \
(filetime_ptr)->dwLowDateTime = (uint64_t) bigtime & 0xFFFFFFFF; \
(filetime_ptr)->dwHighDateTime = (uint64_t) bigtime >> 32; \
} while(0)
@ -136,6 +148,16 @@ static int uv__file_symlink_usermode_flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGE
static DWORD uv__allocation_granularity;
typedef enum {
FS__STAT_PATH_SUCCESS,
FS__STAT_PATH_ERROR,
FS__STAT_PATH_TRY_SLOW
} fs__stat_path_return_t;
INLINE static void fs__stat_assign_statbuf_null(uv_stat_t* statbuf);
INLINE static void fs__stat_assign_statbuf(uv_stat_t* statbuf,
FILE_STAT_BASIC_INFORMATION stat_info, int do_lstat);
void uv__fs_init(void) {
SYSTEM_INFO system_info;
@ -1056,27 +1078,20 @@ void fs__write(uv_fs_t* req) {
error = ERROR_INVALID_FLAGS;
}
SET_REQ_WIN32_ERROR(req, error);
SET_REQ_UV_ERROR(req, uv_translate_write_sys_error(error), error);
}
}
void fs__rmdir(uv_fs_t* req) {
int result = _wrmdir(req->file.pathw);
if (result == -1)
SET_REQ_WIN32_ERROR(req, _doserrno);
else
SET_REQ_RESULT(req, 0);
}
void fs__unlink(uv_fs_t* req) {
static void fs__unlink_rmdir(uv_fs_t* req, BOOL isrmdir) {
const WCHAR* pathw = req->file.pathw;
HANDLE handle;
BY_HANDLE_FILE_INFORMATION info;
FILE_DISPOSITION_INFORMATION disposition;
FILE_DISPOSITION_INFORMATION_EX disposition_ex;
IO_STATUS_BLOCK iosb;
NTSTATUS status;
DWORD error;
handle = CreateFileW(pathw,
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | DELETE,
@ -1097,10 +1112,18 @@ void fs__unlink(uv_fs_t* req) {
return;
}
if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
/* Do not allow deletion of directories, unless it is a symlink. When the
* path refers to a non-symlink directory, report EPERM as mandated by
* POSIX.1. */
if (isrmdir && !(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
/* Error if we're in rmdir mode but it is not a dir.
* TODO: change it to UV_NOTDIR in v2. */
SET_REQ_UV_ERROR(req, UV_ENOENT, ERROR_DIRECTORY);
CloseHandle(handle);
return;
}
if (!isrmdir && (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
/* If not explicitly allowed, do not allow deletion of directories, unless
* it is a symlink. When the path refers to a non-symlink directory, report
* EPERM as mandated by POSIX.1. */
/* Check if it is a reparse point. If it's not, it's a normal directory. */
if (!(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
@ -1112,7 +1135,7 @@ void fs__unlink(uv_fs_t* req) {
/* Read the reparse point and check if it is a valid symlink. If not, don't
* unlink. */
if (fs__readlink_handle(handle, NULL, NULL) < 0) {
DWORD error = GetLastError();
error = GetLastError();
if (error == ERROR_SYMLINK_NOT_SUPPORTED)
error = ERROR_ACCESS_DENIED;
SET_REQ_WIN32_ERROR(req, error);
@ -1121,42 +1144,77 @@ void fs__unlink(uv_fs_t* req) {
}
}
if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
/* Remove read-only attribute */
FILE_BASIC_INFORMATION basic = { 0 };
/* Try posix delete first */
disposition_ex.Flags = FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS |
FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE;
basic.FileAttributes = (info.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY) |
FILE_ATTRIBUTE_ARCHIVE;
status = pNtSetInformationFile(handle,
&iosb,
&basic,
sizeof basic,
FileBasicInformation);
if (!NT_SUCCESS(status)) {
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
CloseHandle(handle);
return;
}
}
/* Try to set the delete flag. */
disposition.DeleteFile = TRUE;
status = pNtSetInformationFile(handle,
&iosb,
&disposition,
sizeof disposition,
FileDispositionInformation);
&disposition_ex,
sizeof disposition_ex,
FileDispositionInformationEx);
if (NT_SUCCESS(status)) {
SET_REQ_SUCCESS(req);
} else {
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
/* If status == STATUS_CANNOT_DELETE here, given we set
* FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE, STATUS_CANNOT_DELETE can only mean
* that there is an existing mapped view to the file, preventing delete.
* STATUS_CANNOT_DELETE maps to UV_EACCES so it's not specifically worth handling */
error = pRtlNtStatusToDosError(status);
if (error == ERROR_NOT_SUPPORTED /* filesystem does not support posix deletion */ ||
error == ERROR_INVALID_PARAMETER /* pre Windows 10 error */ ||
error == ERROR_INVALID_FUNCTION /* pre Windows 10 1607 error */) {
/* posix delete not supported so try fallback */
if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
/* Remove read-only attribute */
FILE_BASIC_INFORMATION basic = { 0 };
basic.FileAttributes = (info.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY) |
FILE_ATTRIBUTE_ARCHIVE;
status = pNtSetInformationFile(handle,
&iosb,
&basic,
sizeof basic,
FileBasicInformation);
if (!NT_SUCCESS(status)) {
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
CloseHandle(handle);
return;
}
}
/* Try to set the delete flag. */
disposition.DeleteFile = TRUE;
status = pNtSetInformationFile(handle,
&iosb,
&disposition,
sizeof disposition,
FileDispositionInformation);
if (NT_SUCCESS(status)) {
SET_REQ_SUCCESS(req);
} else {
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
}
} else {
SET_REQ_WIN32_ERROR(req, error);
}
}
CloseHandle(handle);
}
static void fs__rmdir(uv_fs_t* req) {
fs__unlink_rmdir(req, /*isrmdir*/1);
}
static void fs__unlink(uv_fs_t* req) {
fs__unlink_rmdir(req, /*isrmdir*/0);
}
void fs__mkdir(uv_fs_t* req) {
/* TODO: use req->mode. */
if (CreateDirectoryW(req->file.pathw, NULL)) {
@ -1182,7 +1240,7 @@ void fs__mktemp(uv_fs_t* req, uv__fs_mktemp_func func) {
size_t len;
uint64_t v;
char* path;
path = (char*)req->path;
len = wcslen(req->file.pathw);
ep = req->file.pathw + len;
@ -1593,12 +1651,12 @@ void fs__readdir(uv_fs_t* req) {
goto error;
/* Copy file type. */
if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
dent.d_type = UV__DT_DIR;
if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0)
dent.d_type = UV__DT_CHAR;
else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0)
dent.d_type = UV__DT_LINK;
else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0)
dent.d_type = UV__DT_CHAR;
else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
dent.d_type = UV__DT_DIR;
else
dent.d_type = UV__DT_FILE;
@ -1627,6 +1685,43 @@ void fs__closedir(uv_fs_t* req) {
SET_REQ_RESULT(req, 0);
}
INLINE static fs__stat_path_return_t fs__stat_path(WCHAR* path,
uv_stat_t* statbuf, int do_lstat) {
FILE_STAT_BASIC_INFORMATION stat_info;
// Check if the new fast API is available.
if (!pGetFileInformationByName) {
return FS__STAT_PATH_TRY_SLOW;
}
// Check if the API call fails.
if (!pGetFileInformationByName(path, FileStatBasicByNameInfo, &stat_info,
sizeof(stat_info))) {
switch(GetLastError()) {
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
case ERROR_NOT_READY:
case ERROR_BAD_NET_NAME:
/* These errors aren't worth retrying with the slow path. */
return FS__STAT_PATH_ERROR;
}
return FS__STAT_PATH_TRY_SLOW;
}
// A file handle is needed to get st_size for links.
if ((stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
return FS__STAT_PATH_TRY_SLOW;
}
if (stat_info.DeviceType == FILE_DEVICE_NULL) {
fs__stat_assign_statbuf_null(statbuf);
return FS__STAT_PATH_SUCCESS;
}
fs__stat_assign_statbuf(statbuf, stat_info, do_lstat);
return FS__STAT_PATH_SUCCESS;
}
INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
int do_lstat) {
size_t target_length = 0;
@ -1635,6 +1730,7 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
FILE_FS_VOLUME_INFORMATION volume_info;
NTSTATUS nt_status;
IO_STATUS_BLOCK io_status;
FILE_STAT_BASIC_INFORMATION stat_info;
nt_status = pNtQueryVolumeInformationFile(handle,
&io_status,
@ -1650,13 +1746,7 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
/* If it's NUL device set fields as reasonable as possible and return. */
if (device_info.DeviceType == FILE_DEVICE_NULL) {
memset(statbuf, 0, sizeof(uv_stat_t));
statbuf->st_mode = _S_IFCHR;
statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
((_S_IREAD | _S_IWRITE) >> 6);
statbuf->st_nlink = 1;
statbuf->st_blksize = 4096;
statbuf->st_rdev = FILE_DEVICE_NULL << 16;
fs__stat_assign_statbuf_null(statbuf);
return 0;
}
@ -1680,14 +1770,65 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
/* Buffer overflow (a warning status code) is expected here. */
if (io_status.Status == STATUS_NOT_IMPLEMENTED) {
statbuf->st_dev = 0;
stat_info.VolumeSerialNumber.QuadPart = 0;
} else if (NT_ERROR(nt_status)) {
SetLastError(pRtlNtStatusToDosError(nt_status));
return -1;
} else {
statbuf->st_dev = volume_info.VolumeSerialNumber;
stat_info.VolumeSerialNumber.QuadPart = volume_info.VolumeSerialNumber;
}
stat_info.DeviceType = device_info.DeviceType;
stat_info.FileAttributes = file_info.BasicInformation.FileAttributes;
stat_info.NumberOfLinks = file_info.StandardInformation.NumberOfLinks;
stat_info.FileId.QuadPart =
file_info.InternalInformation.IndexNumber.QuadPart;
stat_info.ChangeTime.QuadPart =
file_info.BasicInformation.ChangeTime.QuadPart;
stat_info.CreationTime.QuadPart =
file_info.BasicInformation.CreationTime.QuadPart;
stat_info.LastAccessTime.QuadPart =
file_info.BasicInformation.LastAccessTime.QuadPart;
stat_info.LastWriteTime.QuadPart =
file_info.BasicInformation.LastWriteTime.QuadPart;
stat_info.AllocationSize.QuadPart =
file_info.StandardInformation.AllocationSize.QuadPart;
if (do_lstat &&
(file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
/*
* If reading the link fails, the reparse point is not a symlink and needs
* to be treated as a regular file. The higher level lstat function will
* detect this failure and retry without do_lstat if appropriate.
*/
if (fs__readlink_handle(handle, NULL, &target_length) != 0) {
fs__stat_assign_statbuf(statbuf, stat_info, do_lstat);
return -1;
}
stat_info.EndOfFile.QuadPart = target_length;
} else {
stat_info.EndOfFile.QuadPart =
file_info.StandardInformation.EndOfFile.QuadPart;
}
fs__stat_assign_statbuf(statbuf, stat_info, do_lstat);
return 0;
}
INLINE static void fs__stat_assign_statbuf_null(uv_stat_t* statbuf) {
memset(statbuf, 0, sizeof(uv_stat_t));
statbuf->st_mode = _S_IFCHR;
statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
((_S_IREAD | _S_IWRITE) >> 6);
statbuf->st_nlink = 1;
statbuf->st_blksize = 4096;
statbuf->st_rdev = FILE_DEVICE_NULL << 16;
}
INLINE static void fs__stat_assign_statbuf(uv_stat_t* statbuf,
FILE_STAT_BASIC_INFORMATION stat_info, int do_lstat) {
statbuf->st_dev = stat_info.VolumeSerialNumber.QuadPart;
/* Todo: st_mode should probably always be 0666 for everyone. We might also
* want to report 0777 if the file is a .exe or a directory.
*
@ -1719,50 +1860,43 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
* target. Otherwise, reparse points must be treated as regular files.
*/
if (do_lstat &&
(file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
/*
* If reading the link fails, the reparse point is not a symlink and needs
* to be treated as a regular file. The higher level lstat function will
* detect this failure and retry without do_lstat if appropriate.
*/
if (fs__readlink_handle(handle, NULL, &target_length) != 0)
return -1;
(stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
statbuf->st_mode |= S_IFLNK;
statbuf->st_size = target_length;
statbuf->st_size = stat_info.EndOfFile.QuadPart;
}
if (statbuf->st_mode == 0) {
if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if (stat_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
statbuf->st_mode |= _S_IFDIR;
statbuf->st_size = 0;
} else {
statbuf->st_mode |= _S_IFREG;
statbuf->st_size = file_info.StandardInformation.EndOfFile.QuadPart;
statbuf->st_size = stat_info.EndOfFile.QuadPart;
}
}
if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_READONLY)
if (stat_info.FileAttributes & FILE_ATTRIBUTE_READONLY)
statbuf->st_mode |= _S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6);
else
statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
((_S_IREAD | _S_IWRITE) >> 6);
uv__filetime_to_timespec(&statbuf->st_atim,
file_info.BasicInformation.LastAccessTime.QuadPart);
stat_info.LastAccessTime.QuadPart);
uv__filetime_to_timespec(&statbuf->st_ctim,
file_info.BasicInformation.ChangeTime.QuadPart);
stat_info.ChangeTime.QuadPart);
uv__filetime_to_timespec(&statbuf->st_mtim,
file_info.BasicInformation.LastWriteTime.QuadPart);
stat_info.LastWriteTime.QuadPart);
uv__filetime_to_timespec(&statbuf->st_birthtim,
file_info.BasicInformation.CreationTime.QuadPart);
stat_info.CreationTime.QuadPart);
statbuf->st_ino = file_info.InternalInformation.IndexNumber.QuadPart;
statbuf->st_ino = stat_info.FileId.QuadPart;
/* st_blocks contains the on-disk allocation size in 512-byte units. */
statbuf->st_blocks =
(uint64_t) file_info.StandardInformation.AllocationSize.QuadPart >> 9;
(uint64_t) stat_info.AllocationSize.QuadPart >> 9;
statbuf->st_nlink = file_info.StandardInformation.NumberOfLinks;
statbuf->st_nlink = stat_info.NumberOfLinks;
/* The st_blksize is supposed to be the 'optimal' number of bytes for reading
* and writing to the disk. That is, for any definition of 'optimal' - it's
@ -1794,8 +1928,6 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
statbuf->st_uid = 0;
statbuf->st_rdev = 0;
statbuf->st_gen = 0;
return 0;
}
@ -1817,6 +1949,17 @@ INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
DWORD flags;
DWORD ret;
// If new API exists, try to use it.
switch (fs__stat_path(path, statbuf, do_lstat)) {
case FS__STAT_PATH_SUCCESS:
return 0;
case FS__STAT_PATH_ERROR:
return GetLastError();
case FS__STAT_PATH_TRY_SLOW:
break;
}
// If the new API does not exist, use the old API.
flags = FILE_FLAG_BACKUP_SEMANTICS;
if (do_lstat)
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
@ -2830,7 +2973,7 @@ static void uv__fs_done(struct uv__work* w, int status) {
uv_fs_t* req;
req = container_of(w, uv_fs_t, work_req);
uv__req_unregister(req->loop, req);
uv__req_unregister(req->loop);
if (status == UV_ECANCELED) {
assert(req->result == 0);

View File

@ -71,10 +71,9 @@ int uv__getaddrinfo_translate_error(int sys_err) {
DECLSPEC_IMPORT void WSAAPI FreeAddrInfoW(PADDRINFOW pAddrInfo);
#endif
/* Adjust size value to be multiple of 4. Use to keep pointer aligned.
* Do we need different versions of this for different architectures? */
#define ALIGNED_SIZE(X) ((((X) + 3) >> 2) << 2)
static size_t align_offset(size_t off, size_t alignment) {
return ((off + alignment - 1) / alignment) * alignment;
}
#ifndef NDIS_IF_MAX_STRING_SIZE
#define NDIS_IF_MAX_STRING_SIZE IF_MAX_STRING_SIZE
@ -103,17 +102,7 @@ static void uv__getaddrinfo_work(struct uv__work* w) {
* Each size calculation is adjusted to avoid unaligned pointers.
*/
static void uv__getaddrinfo_done(struct uv__work* w, int status) {
uv_getaddrinfo_t* req;
size_t addrinfo_len = 0;
ssize_t name_len = 0;
size_t addrinfo_struct_len = ALIGNED_SIZE(sizeof(struct addrinfo));
struct addrinfoW* addrinfow_ptr;
struct addrinfo* addrinfo_ptr;
char* alloc_ptr = NULL;
char* cur_ptr = NULL;
int r;
req = container_of(w, uv_getaddrinfo_t, work_req);
uv_getaddrinfo_t* req = container_of(w, uv_getaddrinfo_t, work_req);
/* release input parameter memory */
uv__free(req->alloc);
@ -126,34 +115,44 @@ static void uv__getaddrinfo_done(struct uv__work* w, int status) {
}
if (req->retcode == 0) {
char* alloc_ptr = NULL;
size_t cur_off = 0;
size_t addrinfo_len;
/* Convert addrinfoW to addrinfo. First calculate required length. */
addrinfow_ptr = req->addrinfow;
struct addrinfoW* addrinfow_ptr = req->addrinfow;
while (addrinfow_ptr != NULL) {
addrinfo_len += addrinfo_struct_len +
ALIGNED_SIZE(addrinfow_ptr->ai_addrlen);
cur_off = align_offset(cur_off, sizeof(void*));
cur_off += sizeof(struct addrinfo);
/* TODO: This alignment could be smaller, if we could
portably get the alignment for sockaddr. */
cur_off = align_offset(cur_off, sizeof(void*));
cur_off += addrinfow_ptr->ai_addrlen;
if (addrinfow_ptr->ai_canonname != NULL) {
name_len = uv_utf16_length_as_wtf8(addrinfow_ptr->ai_canonname, -1);
ssize_t name_len =
uv_utf16_length_as_wtf8(addrinfow_ptr->ai_canonname, -1);
if (name_len < 0) {
req->retcode = name_len;
goto complete;
}
addrinfo_len += ALIGNED_SIZE(name_len + 1);
cur_off += name_len + 1;
}
addrinfow_ptr = addrinfow_ptr->ai_next;
}
/* allocate memory for addrinfo results */
alloc_ptr = (char*)uv__malloc(addrinfo_len);
addrinfo_len = cur_off;
alloc_ptr = uv__malloc(addrinfo_len);
/* do conversions */
if (alloc_ptr != NULL) {
cur_ptr = alloc_ptr;
struct addrinfo *addrinfo_ptr = (struct addrinfo *)alloc_ptr;
cur_off = 0;
addrinfow_ptr = req->addrinfow;
while (addrinfow_ptr != NULL) {
for (;;) {
cur_off += sizeof(struct addrinfo);
assert(cur_off <= addrinfo_len);
/* copy addrinfo struct data */
assert(cur_ptr + addrinfo_struct_len <= alloc_ptr + addrinfo_len);
addrinfo_ptr = (struct addrinfo*)cur_ptr;
addrinfo_ptr->ai_family = addrinfow_ptr->ai_family;
addrinfo_ptr->ai_socktype = addrinfow_ptr->ai_socktype;
addrinfo_ptr->ai_protocol = addrinfow_ptr->ai_protocol;
@ -163,35 +162,37 @@ static void uv__getaddrinfo_done(struct uv__work* w, int status) {
addrinfo_ptr->ai_addr = NULL;
addrinfo_ptr->ai_next = NULL;
cur_ptr += addrinfo_struct_len;
/* copy sockaddr */
if (addrinfo_ptr->ai_addrlen > 0) {
assert(cur_ptr + addrinfo_ptr->ai_addrlen <=
alloc_ptr + addrinfo_len);
memcpy(cur_ptr, addrinfow_ptr->ai_addr, addrinfo_ptr->ai_addrlen);
addrinfo_ptr->ai_addr = (struct sockaddr*)cur_ptr;
cur_ptr += ALIGNED_SIZE(addrinfo_ptr->ai_addrlen);
cur_off = align_offset(cur_off, sizeof(void *));
addrinfo_ptr->ai_addr = (struct sockaddr *)(alloc_ptr + cur_off);
cur_off += addrinfo_ptr->ai_addrlen;
assert(cur_off <= addrinfo_len);
memcpy(addrinfo_ptr->ai_addr,
addrinfow_ptr->ai_addr,
addrinfo_ptr->ai_addrlen);
}
/* convert canonical name to UTF-8 */
if (addrinfow_ptr->ai_canonname != NULL) {
name_len = alloc_ptr + addrinfo_len - cur_ptr;
r = uv__copy_utf16_to_utf8(addrinfow_ptr->ai_canonname,
-1,
cur_ptr,
(size_t*)&name_len);
ssize_t name_len = addrinfo_len - cur_off;
addrinfo_ptr->ai_canonname = alloc_ptr + cur_off;
int r = uv__copy_utf16_to_utf8(addrinfow_ptr->ai_canonname,
-1,
addrinfo_ptr->ai_canonname,
(size_t*)&name_len);
assert(r == 0);
addrinfo_ptr->ai_canonname = cur_ptr;
cur_ptr += ALIGNED_SIZE(name_len + 1);
cur_off += name_len + 1;
assert(cur_off <= addrinfo_len);
}
assert(cur_ptr <= alloc_ptr + addrinfo_len);
/* set next ptr */
addrinfow_ptr = addrinfow_ptr->ai_next;
if (addrinfow_ptr != NULL) {
addrinfo_ptr->ai_next = (struct addrinfo*)cur_ptr;
}
if (addrinfow_ptr == NULL)
break;
cur_off = align_offset(cur_off, sizeof(void *));
addrinfo_ptr = (struct addrinfo *)(alloc_ptr + cur_off);
addrinfo_ptr->ai_next = addrinfo_ptr;
}
req->addrinfo = (struct addrinfo*)alloc_ptr;
} else {
@ -206,7 +207,7 @@ static void uv__getaddrinfo_done(struct uv__work* w, int status) {
}
complete:
uv__req_unregister(req->loop, req);
uv__req_unregister(req->loop);
/* finally do callback with converted result */
if (req->getaddrinfo_cb)
@ -242,10 +243,12 @@ int uv_getaddrinfo(uv_loop_t* loop,
const char* service,
const struct addrinfo* hints) {
char hostname_ascii[256];
size_t off = 0;
size_t nodesize = 0;
size_t servicesize = 0;
size_t serviceoff = 0;
size_t hintssize = 0;
char* alloc_ptr = NULL;
size_t hintoff = 0;
ssize_t rc;
if (req == NULL || (node == NULL && service == NULL)) {
@ -268,6 +271,7 @@ int uv_getaddrinfo(uv_loop_t* loop,
return rc;
nodesize = strlen(hostname_ascii) + 1;
node = hostname_ascii;
off += nodesize * sizeof(WCHAR);
}
if (service != NULL) {
@ -275,27 +279,28 @@ int uv_getaddrinfo(uv_loop_t* loop,
if (rc < 0)
return rc;
servicesize = rc;
off = align_offset(off, sizeof(WCHAR));
serviceoff = off;
off += servicesize * sizeof(WCHAR);
}
if (hints != NULL) {
hintssize = ALIGNED_SIZE(sizeof(struct addrinfoW));
off = align_offset(off, sizeof(void *));
hintoff = off;
hintssize = sizeof(struct addrinfoW);
off += hintssize;
}
/* allocate memory for inputs, and partition it as needed */
alloc_ptr = uv__malloc(ALIGNED_SIZE(nodesize * sizeof(WCHAR)) +
ALIGNED_SIZE(servicesize * sizeof(WCHAR)) +
hintssize);
if (!alloc_ptr)
req->alloc = uv__malloc(off);
if (!req->alloc)
return UV_ENOMEM;
/* save alloc_ptr now so we can free if error */
req->alloc = (void*) alloc_ptr;
/* Convert node string to UTF16 into allocated memory and save pointer in the
* request. The node here has been converted to ascii. */
if (node != NULL) {
req->node = (WCHAR*) alloc_ptr;
uv_wtf8_to_utf16(node, (WCHAR*) alloc_ptr, nodesize);
alloc_ptr += ALIGNED_SIZE(nodesize * sizeof(WCHAR));
req->node = (WCHAR*) req->alloc;
uv_wtf8_to_utf16(node, req->node, nodesize);
} else {
req->node = NULL;
}
@ -303,16 +308,15 @@ int uv_getaddrinfo(uv_loop_t* loop,
/* Convert service string to UTF16 into allocated memory and save pointer in
* the req. */
if (service != NULL) {
req->service = (WCHAR*) alloc_ptr;
uv_wtf8_to_utf16(service, (WCHAR*) alloc_ptr, servicesize);
alloc_ptr += ALIGNED_SIZE(servicesize * sizeof(WCHAR));
req->service = (WCHAR*) ((char*) req->alloc + serviceoff);
uv_wtf8_to_utf16(service, req->service, servicesize);
} else {
req->service = NULL;
}
/* copy hints to allocated memory and save pointer in req */
if (hints != NULL) {
req->addrinfow = (struct addrinfoW*) alloc_ptr;
req->addrinfow = (struct addrinfoW*) ((char*) req->alloc + hintoff);
req->addrinfow->ai_family = hints->ai_family;
req->addrinfow->ai_socktype = hints->ai_socktype;
req->addrinfow->ai_protocol = hints->ai_protocol;
@ -325,7 +329,7 @@ int uv_getaddrinfo(uv_loop_t* loop,
req->addrinfow = NULL;
}
uv__req_register(loop, req);
uv__req_register(loop);
if (getaddrinfo_cb) {
uv__work_submit(loop,

View File

@ -82,7 +82,7 @@ static void uv__getnameinfo_done(struct uv__work* w, int status) {
char* service;
req = container_of(w, uv_getnameinfo_t, work_req);
uv__req_unregister(req->loop, req);
uv__req_unregister(req->loop);
host = service = NULL;
if (status == UV_ECANCELED) {
@ -124,7 +124,7 @@ int uv_getnameinfo(uv_loop_t* loop,
}
UV_REQ_INIT(req, UV_GETNAMEINFO);
uv__req_register(loop, req);
uv__req_register(loop);
req->getnameinfo_cb = getnameinfo_cb;
req->flags = flags;

View File

@ -330,4 +330,6 @@ void uv__wake_all_loops(void);
*/
void uv__init_detect_system_wakeup(void);
int uv_translate_write_sys_error(int sys_errno);
#endif /* UV_WIN_INTERNAL_H_ */

267
deps/uv/src/win/pipe.c vendored
View File

@ -106,8 +106,8 @@ static int includes_nul(const char *s, size_t n) {
}
static void uv__unique_pipe_name(char* ptr, char* name, size_t size) {
snprintf(name, size, "\\\\?\\pipe\\uv\\%p-%lu", ptr, GetCurrentProcessId());
static void uv__unique_pipe_name(unsigned long long ptr, char* name, size_t size) {
snprintf(name, size, "\\\\?\\pipe\\uv\\%llu-%lu", ptr, GetCurrentProcessId());
}
@ -208,7 +208,7 @@ static void close_pipe(uv_pipe_t* pipe) {
static int uv__pipe_server(
HANDLE* pipeHandle_ptr, DWORD access,
char* name, size_t nameSize, char* random) {
char* name, size_t nameSize, unsigned long long random) {
HANDLE pipeHandle;
int err;
@ -249,7 +249,7 @@ static int uv__pipe_server(
static int uv__create_pipe_pair(
HANDLE* server_pipe_ptr, HANDLE* client_pipe_ptr,
unsigned int server_flags, unsigned int client_flags,
int inherit_client, char* random) {
int inherit_client, unsigned long long random) {
/* allowed flags are: UV_READABLE_PIPE | UV_WRITABLE_PIPE | UV_NONBLOCK_PIPE */
char pipe_name[64];
SECURITY_ATTRIBUTES sa;
@ -357,7 +357,12 @@ int uv_pipe(uv_file fds[2], int read_flags, int write_flags) {
/* TODO: better source of local randomness than &fds? */
read_flags |= UV_READABLE_PIPE;
write_flags |= UV_WRITABLE_PIPE;
err = uv__create_pipe_pair(&readh, &writeh, read_flags, write_flags, 0, (char*) &fds[0]);
err = uv__create_pipe_pair(&readh,
&writeh,
read_flags,
write_flags,
0,
(uintptr_t) &fds[0]);
if (err != 0)
return err;
temp[0] = _open_osfhandle((intptr_t) readh, 0);
@ -421,7 +426,7 @@ int uv__create_stdio_pipe_pair(uv_loop_t* loop,
}
err = uv__create_pipe_pair(&server_pipe, &client_pipe,
server_flags, client_flags, 1, (char*) server_pipe);
server_flags, client_flags, 1, (uintptr_t) server_pipe);
if (err)
goto error;
@ -667,15 +672,10 @@ void uv__pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) {
}
handle->pipe.conn.ipc_xfer_queue_length = 0;
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) {
UnregisterWait(handle->read_req.wait_handle);
handle->read_req.wait_handle = INVALID_HANDLE_VALUE;
}
if (handle->read_req.event_handle != NULL) {
CloseHandle(handle->read_req.event_handle);
handle->read_req.event_handle = NULL;
}
assert(handle->read_req.wait_handle == INVALID_HANDLE_VALUE);
if (handle->read_req.event_handle != NULL) {
CloseHandle(handle->read_req.event_handle);
handle->read_req.event_handle = NULL;
}
if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)
@ -868,7 +868,7 @@ void uv_pipe_connect(uv_connect_t* req,
SET_REQ_ERROR(req, err);
uv__insert_pending_req(loop, (uv_req_t*) req);
handle->reqs_pending++;
REGISTER_HANDLE_REQ(loop, handle, req);
REGISTER_HANDLE_REQ(loop, handle);
}
}
@ -959,7 +959,7 @@ int uv_pipe_connect2(uv_connect_t* req,
goto error;
}
REGISTER_HANDLE_REQ(loop, handle, req);
REGISTER_HANDLE_REQ(loop, handle);
handle->reqs_pending++;
return 0;
@ -974,7 +974,7 @@ int uv_pipe_connect2(uv_connect_t* req,
SET_REQ_SUCCESS(req);
uv__insert_pending_req(loop, (uv_req_t*) req);
handle->reqs_pending++;
REGISTER_HANDLE_REQ(loop, handle, req);
REGISTER_HANDLE_REQ(loop, handle);
return 0;
error:
@ -992,7 +992,7 @@ error:
SET_REQ_ERROR(req, err);
uv__insert_pending_req(loop, (uv_req_t*) req);
handle->reqs_pending++;
REGISTER_HANDLE_REQ(loop, handle, req);
REGISTER_HANDLE_REQ(loop, handle);
return 0;
}
@ -1417,13 +1417,12 @@ static void uv__pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) {
}
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
if (req->wait_handle == INVALID_HANDLE_VALUE) {
if (!RegisterWaitForSingleObject(&req->wait_handle,
req->event_handle, post_completion_read_wait, (void*) req,
INFINITE, WT_EXECUTEINWAITTHREAD)) {
SET_REQ_ERROR(req, GetLastError());
goto error;
}
assert(req->wait_handle == INVALID_HANDLE_VALUE);
if (!RegisterWaitForSingleObject(&req->wait_handle,
req->event_handle, post_completion_read_wait, (void*) req,
INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) {
SET_REQ_ERROR(req, GetLastError());
goto error;
}
}
}
@ -1451,16 +1450,16 @@ int uv__pipe_read_start(uv_pipe_t* handle,
handle->read_cb = read_cb;
handle->alloc_cb = alloc_cb;
if (handle->read_req.event_handle == NULL) {
handle->read_req.event_handle = CreateEvent(NULL, 0, 0, NULL);
if (handle->read_req.event_handle == NULL) {
uv_fatal_error(GetLastError(), "CreateEvent");
}
}
/* If reading was stopped and then started again, there could still be a read
* request pending. */
if (!(handle->flags & UV_HANDLE_READ_PENDING)) {
if (handle->flags & UV_HANDLE_EMULATE_IOCP &&
handle->read_req.event_handle == NULL) {
handle->read_req.event_handle = CreateEvent(NULL, 0, 0, NULL);
if (handle->read_req.event_handle == NULL) {
uv_fatal_error(GetLastError(), "CreateEvent");
}
}
uv__pipe_queue_read(loop, handle);
}
@ -1638,7 +1637,7 @@ static int uv__pipe_write_data(uv_loop_t* loop,
req->u.io.queued_bytes = 0;
}
REGISTER_HANDLE_REQ(loop, handle, req);
REGISTER_HANDLE_REQ(loop, handle);
handle->reqs_pending++;
handle->stream.conn.write_reqs_pending++;
POST_COMPLETION_FOR_REQ(loop, req);
@ -1686,7 +1685,7 @@ static int uv__pipe_write_data(uv_loop_t* loop,
CloseHandle(req->event_handle);
req->event_handle = NULL;
REGISTER_HANDLE_REQ(loop, handle, req);
REGISTER_HANDLE_REQ(loop, handle);
handle->reqs_pending++;
handle->stream.conn.write_reqs_pending++;
return 0;
@ -1713,13 +1712,13 @@ static int uv__pipe_write_data(uv_loop_t* loop,
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
if (!RegisterWaitForSingleObject(&req->wait_handle,
req->event_handle, post_completion_write_wait, (void*) req,
INFINITE, WT_EXECUTEINWAITTHREAD)) {
INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) {
return GetLastError();
}
}
}
REGISTER_HANDLE_REQ(loop, handle, req);
REGISTER_HANDLE_REQ(loop, handle);
handle->reqs_pending++;
handle->stream.conn.write_reqs_pending++;
@ -1889,7 +1888,7 @@ static void uv__pipe_read_error(uv_loop_t* loop, uv_pipe_t* handle, int error,
static void uv__pipe_read_error_or_eof(uv_loop_t* loop, uv_pipe_t* handle,
int error, uv_buf_t buf) {
DWORD error, uv_buf_t buf) {
if (error == ERROR_BROKEN_PIPE) {
uv__pipe_read_eof(loop, handle, buf);
} else {
@ -1919,17 +1918,25 @@ static void uv__pipe_queue_ipc_xfer_info(
/* Read an exact number of bytes from a pipe. If an error or end-of-file is
* encountered before the requested number of bytes are read, an error is
* returned. */
static int uv__pipe_read_exactly(HANDLE h, void* buffer, DWORD count) {
DWORD bytes_read, bytes_read_now;
static DWORD uv__pipe_read_exactly(uv_pipe_t* handle, void* buffer, DWORD count) {
uv_read_t* req;
DWORD bytes_read;
DWORD bytes_read_now;
bytes_read = 0;
while (bytes_read < count) {
if (!ReadFile(h,
req = &handle->read_req;
memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
req->u.io.overlapped.hEvent = (HANDLE) ((uintptr_t) req->event_handle | 1);
if (!ReadFile(handle->handle,
(char*) buffer + bytes_read,
count - bytes_read,
&bytes_read_now,
NULL)) {
return GetLastError();
&req->u.io.overlapped)) {
if (GetLastError() != ERROR_IO_PENDING)
return GetLastError();
if (!GetOverlappedResult(handle->handle, &req->u.io.overlapped, &bytes_read_now, TRUE))
return GetLastError();
}
bytes_read += bytes_read_now;
@ -1940,16 +1947,19 @@ static int uv__pipe_read_exactly(HANDLE h, void* buffer, DWORD count) {
}
static DWORD uv__pipe_read_data(uv_loop_t* loop,
uv_pipe_t* handle,
DWORD suggested_bytes,
DWORD max_bytes) {
DWORD bytes_read;
static int uv__pipe_read_data(uv_loop_t* loop,
uv_pipe_t* handle,
DWORD* bytes_read, /* inout argument */
DWORD max_bytes) {
uv_buf_t buf;
uv_read_t* req;
DWORD r;
DWORD bytes_available;
int more;
/* Ask the user for a buffer to read data into. */
buf = uv_buf_init(NULL, 0);
handle->alloc_cb((uv_handle_t*) handle, suggested_bytes, &buf);
handle->alloc_cb((uv_handle_t*) handle, *bytes_read, &buf);
if (buf.base == NULL || buf.len == 0) {
handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf);
return 0; /* Break out of read loop. */
@ -1958,33 +1968,77 @@ static DWORD uv__pipe_read_data(uv_loop_t* loop,
/* Ensure we read at most the smaller of:
* (a) the length of the user-allocated buffer.
* (b) the maximum data length as specified by the `max_bytes` argument.
* (c) the amount of data that can be read non-blocking
*/
if (max_bytes > buf.len)
max_bytes = buf.len;
/* Read into the user buffer. */
if (!ReadFile(handle->handle, buf.base, max_bytes, &bytes_read, NULL)) {
uv__pipe_read_error_or_eof(loop, handle, GetLastError(), buf);
return 0; /* Break out of read loop. */
if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) {
/* The user failed to supply a pipe that can be used non-blocking or with
* threads. Try to estimate the amount of data that is safe to read without
* blocking, in a race-y way however. */
bytes_available = 0;
if (!PeekNamedPipe(handle->handle, NULL, 0, NULL, &bytes_available, NULL)) {
r = GetLastError();
} else {
if (max_bytes > bytes_available)
max_bytes = bytes_available;
*bytes_read = 0;
if (max_bytes == 0 || ReadFile(handle->handle, buf.base, max_bytes, bytes_read, NULL))
r = ERROR_SUCCESS;
else
r = GetLastError();
}
more = max_bytes < bytes_available;
} else {
/* Read into the user buffer.
* Prepare an Event so that we can cancel if it doesn't complete immediately.
*/
req = &handle->read_req;
memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
req->u.io.overlapped.hEvent = (HANDLE) ((uintptr_t) req->event_handle | 1);
if (ReadFile(handle->handle, buf.base, max_bytes, bytes_read, &req->u.io.overlapped)) {
r = ERROR_SUCCESS;
} else {
r = GetLastError();
*bytes_read = 0;
if (r == ERROR_IO_PENDING) {
r = CancelIoEx(handle->handle, &req->u.io.overlapped);
assert(r || GetLastError() == ERROR_NOT_FOUND);
if (GetOverlappedResult(handle->handle, &req->u.io.overlapped, bytes_read, TRUE)) {
r = ERROR_SUCCESS;
} else {
r = GetLastError();
*bytes_read = 0;
}
}
}
more = *bytes_read == max_bytes;
}
/* Call the read callback. */
handle->read_cb((uv_stream_t*) handle, bytes_read, &buf);
if (r == ERROR_SUCCESS || r == ERROR_OPERATION_ABORTED)
handle->read_cb((uv_stream_t*) handle, *bytes_read, &buf);
else
uv__pipe_read_error_or_eof(loop, handle, r, buf);
return bytes_read;
return more;
}
static DWORD uv__pipe_read_ipc(uv_loop_t* loop, uv_pipe_t* handle) {
uint32_t* data_remaining = &handle->pipe.conn.ipc_data_frame.payload_remaining;
int err;
static int uv__pipe_read_ipc(uv_loop_t* loop, uv_pipe_t* handle) {
uint32_t* data_remaining;
DWORD err;
DWORD more;
DWORD bytes_read;
data_remaining = &handle->pipe.conn.ipc_data_frame.payload_remaining;
if (*data_remaining > 0) {
/* Read frame data payload. */
DWORD bytes_read =
uv__pipe_read_data(loop, handle, *data_remaining, *data_remaining);
bytes_read = *data_remaining;
more = uv__pipe_read_data(loop, handle, &bytes_read, bytes_read);
*data_remaining -= bytes_read;
return bytes_read;
} else {
/* Start of a new IPC frame. */
@ -1995,7 +2049,7 @@ static DWORD uv__pipe_read_ipc(uv_loop_t* loop, uv_pipe_t* handle) {
/* Read the IPC frame header. */
err = uv__pipe_read_exactly(
handle->handle, &frame_header, sizeof frame_header);
handle, &frame_header, sizeof frame_header);
if (err)
goto error;
@ -2031,21 +2085,24 @@ static DWORD uv__pipe_read_ipc(uv_loop_t* loop, uv_pipe_t* handle) {
/* If no socket xfer info follows, return here. Data will be read in a
* subsequent invocation of uv__pipe_read_ipc(). */
if (xfer_type == UV__IPC_SOCKET_XFER_NONE)
return sizeof frame_header; /* Number of bytes read. */
if (xfer_type != UV__IPC_SOCKET_XFER_NONE) {
/* Read transferred socket information. */
err = uv__pipe_read_exactly(handle, &xfer_info, sizeof xfer_info);
if (err)
goto error;
/* Read transferred socket information. */
err = uv__pipe_read_exactly(handle->handle, &xfer_info, sizeof xfer_info);
if (err)
goto error;
/* Store the pending socket info. */
uv__pipe_queue_ipc_xfer_info(handle, xfer_type, &xfer_info);
/* Return number of bytes read. */
return sizeof frame_header + sizeof xfer_info;
/* Store the pending socket info. */
uv__pipe_queue_ipc_xfer_info(handle, xfer_type, &xfer_info);
}
}
/* Return whether the caller should immediately try another read call to get
* more data. Calling uv__pipe_read_exactly will hang if there isn't data
* available, so we cannot do this unless we are guaranteed not to reach that.
*/
more = *data_remaining > 0;
return more;
invalid:
/* Invalid frame. */
err = WSAECONNABORTED; /* Maps to UV_ECONNABORTED. */
@ -2059,12 +2116,20 @@ error:
void uv__process_pipe_read_req(uv_loop_t* loop,
uv_pipe_t* handle,
uv_req_t* req) {
DWORD err;
DWORD more;
DWORD bytes_requested;
assert(handle->type == UV_NAMED_PIPE);
handle->flags &= ~(UV_HANDLE_READ_PENDING | UV_HANDLE_CANCELLATION_PENDING);
DECREASE_PENDING_REQ_COUNT(handle);
eof_timer_stop(handle);
if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) {
UnregisterWait(handle->read_req.wait_handle);
handle->read_req.wait_handle = INVALID_HANDLE_VALUE;
}
/* At this point, we're done with bookkeeping. If the user has stopped
* reading the pipe in the meantime, there is nothing left to do, since there
* is no callback that we can call. */
@ -2073,7 +2138,7 @@ void uv__process_pipe_read_req(uv_loop_t* loop,
if (!REQ_SUCCESS(req)) {
/* An error occurred doing the zero-read. */
DWORD err = GET_REQ_ERROR(req);
err = GET_REQ_ERROR(req);
/* If the read was cancelled by uv__pipe_interrupt_read(), the request may
* indicate an ERROR_OPERATION_ABORTED error. This error isn't relevant to
@ -2084,34 +2149,18 @@ void uv__process_pipe_read_req(uv_loop_t* loop,
} else {
/* The zero-read completed without error, indicating there is data
* available in the kernel buffer. */
DWORD avail;
/* Get the number of bytes available. */
avail = 0;
if (!PeekNamedPipe(handle->handle, NULL, 0, NULL, &avail, NULL))
uv__pipe_read_error_or_eof(loop, handle, GetLastError(), uv_null_buf_);
/* Read until we've either read all the bytes available, or the 'reading'
* flag is cleared. */
while (avail > 0 && handle->flags & UV_HANDLE_READING) {
while (handle->flags & UV_HANDLE_READING) {
bytes_requested = 65536;
/* Depending on the type of pipe, read either IPC frames or raw data. */
DWORD bytes_read =
handle->ipc ? uv__pipe_read_ipc(loop, handle)
: uv__pipe_read_data(loop, handle, avail, (DWORD) -1);
if (handle->ipc)
more = uv__pipe_read_ipc(loop, handle);
else
more = uv__pipe_read_data(loop, handle, &bytes_requested, INT32_MAX);
/* If no bytes were read, treat this as an indication that an error
* occurred, and break out of the read loop. */
if (bytes_read == 0)
if (more == 0)
break;
/* It is possible that more bytes were read than we thought were
* available. To prevent `avail` from underflowing, break out of the loop
* if this is the case. */
if (bytes_read > avail)
break;
/* Recompute the number of bytes available. */
avail -= bytes_read;
}
}
@ -2132,17 +2181,15 @@ void uv__process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle,
assert(handle->write_queue_size >= req->u.io.queued_bytes);
handle->write_queue_size -= req->u.io.queued_bytes;
UNREGISTER_HANDLE_REQ(loop, handle, req);
UNREGISTER_HANDLE_REQ(loop, handle);
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
if (req->wait_handle != INVALID_HANDLE_VALUE) {
UnregisterWait(req->wait_handle);
req->wait_handle = INVALID_HANDLE_VALUE;
}
if (req->event_handle) {
CloseHandle(req->event_handle);
req->event_handle = NULL;
}
if (req->wait_handle != INVALID_HANDLE_VALUE) {
UnregisterWait(req->wait_handle);
req->wait_handle = INVALID_HANDLE_VALUE;
}
if (req->event_handle) {
CloseHandle(req->event_handle);
req->event_handle = NULL;
}
err = GET_REQ_ERROR(req);
@ -2219,7 +2266,7 @@ void uv__process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle,
assert(handle->type == UV_NAMED_PIPE);
UNREGISTER_HANDLE_REQ(loop, handle, req);
UNREGISTER_HANDLE_REQ(loop, handle);
err = 0;
if (REQ_SUCCESS(req)) {
@ -2251,7 +2298,7 @@ void uv__process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle,
/* Clear the shutdown_req field so we don't go here again. */
handle->stream.conn.shutdown_req = NULL;
UNREGISTER_HANDLE_REQ(loop, handle, req);
UNREGISTER_HANDLE_REQ(loop, handle);
if (handle->flags & UV_HANDLE_CLOSING) {
/* Already closing. Cancel the shutdown. */

View File

@ -46,12 +46,12 @@
#define CHILD_STDIO_CRT_FLAGS(buffer, fd) \
*((unsigned char*) (buffer) + sizeof(int) + fd)
#define CHILD_STDIO_HANDLE(buffer, fd) \
*((HANDLE*) ((unsigned char*) (buffer) + \
sizeof(int) + \
sizeof(unsigned char) * \
CHILD_STDIO_COUNT((buffer)) + \
sizeof(HANDLE) * (fd)))
#define CHILD_STDIO_HANDLE(buffer, fd) \
((void*) ((unsigned char*) (buffer) + \
sizeof(int) + \
sizeof(unsigned char) * \
CHILD_STDIO_COUNT((buffer)) + \
sizeof(HANDLE) * (fd)))
/* CRT file descriptor mode flags */
@ -194,7 +194,7 @@ int uv__stdio_create(uv_loop_t* loop,
CHILD_STDIO_COUNT(buffer) = count;
for (i = 0; i < count; i++) {
CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE;
memset(CHILD_STDIO_HANDLE(buffer, i), 0xFF, sizeof(HANDLE));
}
for (i = 0; i < count; i++) {
@ -215,14 +215,15 @@ int uv__stdio_create(uv_loop_t* loop,
* handles in the stdio buffer are initialized with.
* INVALID_HANDLE_VALUE, which should be okay. */
if (i <= 2) {
HANDLE nul;
DWORD access = (i == 0) ? FILE_GENERIC_READ :
FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES;
err = uv__create_nul_handle(&CHILD_STDIO_HANDLE(buffer, i),
access);
err = uv__create_nul_handle(&nul, access);
if (err)
goto error;
memcpy(CHILD_STDIO_HANDLE(buffer, i), &nul, sizeof(HANDLE));
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
}
break;
@ -247,7 +248,7 @@ int uv__stdio_create(uv_loop_t* loop,
if (err)
goto error;
CHILD_STDIO_HANDLE(buffer, i) = child_pipe;
memcpy(CHILD_STDIO_HANDLE(buffer, i), &child_pipe, sizeof(HANDLE));
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE;
break;
}
@ -263,7 +264,7 @@ int uv__stdio_create(uv_loop_t* loop,
* error. */
if (fdopt.data.fd <= 2 && err == ERROR_INVALID_HANDLE) {
CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE;
memset(CHILD_STDIO_HANDLE(buffer, i), 0xFF, sizeof(HANDLE));
break;
}
goto error;
@ -298,7 +299,7 @@ int uv__stdio_create(uv_loop_t* loop,
return -1;
}
CHILD_STDIO_HANDLE(buffer, i) = child_handle;
memcpy(CHILD_STDIO_HANDLE(buffer, i), &child_handle, sizeof(HANDLE));
break;
}
@ -334,7 +335,7 @@ int uv__stdio_create(uv_loop_t* loop,
if (err)
goto error;
CHILD_STDIO_HANDLE(buffer, i) = child_handle;
memcpy(CHILD_STDIO_HANDLE(buffer, i), &child_handle, sizeof(HANDLE));
CHILD_STDIO_CRT_FLAGS(buffer, i) = crt_flags;
break;
}
@ -359,7 +360,7 @@ void uv__stdio_destroy(BYTE* buffer) {
count = CHILD_STDIO_COUNT(buffer);
for (i = 0; i < count; i++) {
HANDLE handle = CHILD_STDIO_HANDLE(buffer, i);
HANDLE handle = uv__stdio_handle(buffer, i);
if (handle != INVALID_HANDLE_VALUE) {
CloseHandle(handle);
}
@ -374,7 +375,7 @@ void uv__stdio_noinherit(BYTE* buffer) {
count = CHILD_STDIO_COUNT(buffer);
for (i = 0; i < count; i++) {
HANDLE handle = CHILD_STDIO_HANDLE(buffer, i);
HANDLE handle = uv__stdio_handle(buffer, i);
if (handle != INVALID_HANDLE_VALUE) {
SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
}
@ -412,5 +413,7 @@ WORD uv__stdio_size(BYTE* buffer) {
HANDLE uv__stdio_handle(BYTE* buffer, int fd) {
return CHILD_STDIO_HANDLE(buffer, fd);
HANDLE handle;
memcpy(&handle, CHILD_STDIO_HANDLE(buffer, fd), sizeof(HANDLE));
return handle;
}

View File

@ -26,7 +26,6 @@
#include <signal.h>
#include <limits.h>
#include <wchar.h>
#include <malloc.h> /* _alloca */
#include "uv.h"
#include "internal.h"
@ -598,11 +597,9 @@ error:
}
int env_strncmp(const wchar_t* a, int na, const wchar_t* b) {
static int env_strncmp(const wchar_t* a, int na, const wchar_t* b) {
wchar_t* a_eq;
wchar_t* b_eq;
wchar_t* A;
wchar_t* B;
int nb;
int r;
@ -617,27 +614,8 @@ int env_strncmp(const wchar_t* a, int na, const wchar_t* b) {
assert(b_eq);
nb = b_eq - b;
A = _alloca((na+1) * sizeof(wchar_t));
B = _alloca((nb+1) * sizeof(wchar_t));
r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, a, na, A, na);
assert(r==na);
A[na] = L'\0';
r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, b, nb, B, nb);
assert(r==nb);
B[nb] = L'\0';
for (;;) {
wchar_t AA = *A++;
wchar_t BB = *B++;
if (AA < BB) {
return -1;
} else if (AA > BB) {
return 1;
} else if (!AA && !BB) {
return 0;
}
}
r = CompareStringOrdinal(a, na, b, nb, /*case insensitive*/TRUE);
return r - CSTR_EQUAL;
}
@ -676,6 +654,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) {
WCHAR* dst_copy;
WCHAR** ptr_copy;
WCHAR** env_copy;
char* p;
size_t required_vars_value_len[ARRAY_SIZE(required_vars)];
/* first pass: determine size in UTF-16 */
@ -691,11 +670,13 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) {
}
/* second pass: copy to UTF-16 environment block */
dst_copy = uv__malloc(env_len * sizeof(WCHAR));
if (dst_copy == NULL && env_len > 0) {
len = env_block_count * sizeof(WCHAR*);
p = uv__malloc(len + env_len * sizeof(WCHAR));
if (p == NULL) {
return UV_ENOMEM;
}
env_copy = _alloca(env_block_count * sizeof(WCHAR*));
env_copy = (void*) &p[0];
dst_copy = (void*) &p[len];
ptr = dst_copy;
ptr_copy = env_copy;
@ -745,7 +726,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) {
/* final pass: copy, in sort order, and inserting required variables */
dst = uv__malloc((1+env_len) * sizeof(WCHAR));
if (!dst) {
uv__free(dst_copy);
uv__free(p);
return UV_ENOMEM;
}
@ -790,7 +771,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) {
assert(env_len == (size_t) (ptr - dst));
*ptr = L'\0';
uv__free(dst_copy);
uv__free(p);
*dst_ptr = dst;
return 0;
}
@ -1308,16 +1289,34 @@ static int uv__kill(HANDLE process_handle, int signum) {
/* Unconditionally terminate the process. On Windows, killed processes
* normally return 1. */
int err;
DWORD status;
if (TerminateProcess(process_handle, 1))
return 0;
/* If the process already exited before TerminateProcess was called,.
/* If the process already exited before TerminateProcess was called,
* TerminateProcess will fail with ERROR_ACCESS_DENIED. */
err = GetLastError();
if (err == ERROR_ACCESS_DENIED &&
WaitForSingleObject(process_handle, 0) == WAIT_OBJECT_0) {
return UV_ESRCH;
if (err == ERROR_ACCESS_DENIED) {
/* First check using GetExitCodeProcess() with status different from
* STILL_ACTIVE (259). This check can be set incorrectly by the process,
* though that is uncommon. */
if (GetExitCodeProcess(process_handle, &status) &&
status != STILL_ACTIVE) {
return UV_ESRCH;
}
/* But the process could have exited with code == STILL_ACTIVE, use then
* WaitForSingleObject with timeout zero. This is prone to a race
* condition as it could return WAIT_TIMEOUT because the handle might
* not have been signaled yet.That would result in returning the wrong
* error code here (UV_EACCES instead of UV_ESRCH), but we cannot fix
* the kernel synchronization issue that TerminateProcess is
* inconsistent with WaitForSingleObject with just the APIs available to
* us in user space. */
if (WaitForSingleObject(process_handle, 0) == WAIT_OBJECT_0) {
return UV_ESRCH;
}
}
return uv_translate_sys_error(err);
@ -1325,6 +1324,14 @@ static int uv__kill(HANDLE process_handle, int signum) {
case 0: {
/* Health check: is the process still alive? */
DWORD status;
if (!GetExitCodeProcess(process_handle, &status))
return uv_translate_sys_error(GetLastError());
if (status != STILL_ACTIVE)
return UV_ESRCH;
switch (WaitForSingleObject(process_handle, 0)) {
case WAIT_OBJECT_0:
return UV_ESRCH;

View File

@ -53,16 +53,16 @@
(uv__ntstatus_to_winsock_error(GET_REQ_STATUS((req))))
#define REGISTER_HANDLE_REQ(loop, handle, req) \
#define REGISTER_HANDLE_REQ(loop, handle) \
do { \
INCREASE_ACTIVE_COUNT((loop), (handle)); \
uv__req_register((loop), (req)); \
uv__req_register((loop)); \
} while (0)
#define UNREGISTER_HANDLE_REQ(loop, handle, req) \
#define UNREGISTER_HANDLE_REQ(loop, handle) \
do { \
DECREASE_ACTIVE_COUNT((loop), (handle)); \
uv__req_unregister((loop), (req)); \
uv__req_unregister((loop)); \
} while (0)
@ -83,7 +83,7 @@
INLINE static uv_req_t* uv__overlapped_to_req(OVERLAPPED* overlapped) {
return CONTAINING_RECORD(overlapped, uv_req_t, u.io.overlapped);
return container_of(overlapped, uv_req_t, u.io.overlapped);
}

View File

@ -91,7 +91,7 @@ int uv__signal_dispatch(int signum) {
for (handle = RB_NFIND(uv_signal_tree_s, &uv__signal_tree, &lookup);
handle != NULL && handle->signum == signum;
handle = RB_NEXT(uv_signal_tree_s, &uv__signal_tree, handle)) {
handle = RB_NEXT(uv_signal_tree_s, handle)) {
unsigned long previous = InterlockedExchange(
(volatile LONG*) &handle->pending_signum, signum);

View File

@ -131,7 +131,7 @@ int uv_write(uv_write_t* req,
case UV_NAMED_PIPE:
err = uv__pipe_write(
loop, req, (uv_pipe_t*) handle, bufs, nbufs, NULL, cb);
break;
return uv_translate_write_sys_error(err);
case UV_TTY:
err = uv__tty_write(loop, req, (uv_tty_t*) handle, bufs, nbufs, cb);
break;
@ -164,7 +164,7 @@ int uv_write2(uv_write_t* req,
err = uv__pipe_write(
loop, req, (uv_pipe_t*) handle, bufs, nbufs, send_handle, cb);
return uv_translate_sys_error(err);
return uv_translate_write_sys_error(err);
}
@ -216,7 +216,7 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) {
handle->flags &= ~UV_HANDLE_WRITABLE;
handle->stream.conn.shutdown_req = req;
handle->reqs_pending++;
REGISTER_HANDLE_REQ(loop, handle, req);
REGISTER_HANDLE_REQ(loop, handle);
if (handle->stream.conn.write_reqs_pending == 0) {
if (handle->type == UV_NAMED_PIPE)

45
deps/uv/src/win/tcp.c vendored
View File

@ -58,11 +58,17 @@ static int uv__tcp_keepalive(uv_tcp_t* handle, SOCKET socket, int enable, unsign
return WSAGetLastError();
}
if (enable && setsockopt(socket,
IPPROTO_TCP,
TCP_KEEPALIVE,
(const char*)&delay,
sizeof delay) == -1) {
if (!enable)
return 0;
if (delay < 1)
return UV_EINVAL;
if (setsockopt(socket,
IPPROTO_TCP,
TCP_KEEPALIVE,
(const char*)&delay,
sizeof delay) == -1) {
return WSAGetLastError();
}
@ -206,7 +212,7 @@ void uv__process_tcp_shutdown_req(uv_loop_t* loop, uv_tcp_t* stream, uv_shutdown
assert(stream->flags & UV_HANDLE_CONNECTION);
stream->stream.conn.shutdown_req = NULL;
UNREGISTER_HANDLE_REQ(loop, stream, req);
UNREGISTER_HANDLE_REQ(loop, stream);
err = 0;
if (stream->flags & UV_HANDLE_CLOSING)
@ -286,6 +292,12 @@ static int uv__tcp_try_bind(uv_tcp_t* handle,
DWORD err;
int r;
/* There is no SO_REUSEPORT on Windows, Windows only knows SO_REUSEADDR.
* so we just return an error directly when UV_TCP_REUSEPORT is requested
* for binding the socket. */
if (flags & UV_TCP_REUSEPORT)
return ERROR_NOT_SUPPORTED;
if (handle->socket == INVALID_SOCKET) {
SOCKET sock;
@ -822,7 +834,7 @@ out:
if (handle->delayed_error != 0) {
/* Process the req without IOCP. */
handle->reqs_pending++;
REGISTER_HANDLE_REQ(loop, handle, req);
REGISTER_HANDLE_REQ(loop, handle);
uv__insert_pending_req(loop, (uv_req_t*)req);
return 0;
}
@ -838,12 +850,12 @@ out:
if (UV_SUCCEEDED_WITHOUT_IOCP(success)) {
/* Process the req without IOCP. */
handle->reqs_pending++;
REGISTER_HANDLE_REQ(loop, handle, req);
REGISTER_HANDLE_REQ(loop, handle);
uv__insert_pending_req(loop, (uv_req_t*)req);
} else if (UV_SUCCEEDED_WITH_IOCP(success)) {
/* The req will be processed with IOCP. */
handle->reqs_pending++;
REGISTER_HANDLE_REQ(loop, handle, req);
REGISTER_HANDLE_REQ(loop, handle);
} else {
return WSAGetLastError();
}
@ -913,14 +925,14 @@ int uv__tcp_write(uv_loop_t* loop,
req->u.io.queued_bytes = 0;
handle->reqs_pending++;
handle->stream.conn.write_reqs_pending++;
REGISTER_HANDLE_REQ(loop, handle, req);
REGISTER_HANDLE_REQ(loop, handle);
uv__insert_pending_req(loop, (uv_req_t*) req);
} else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
/* Request queued by the kernel. */
req->u.io.queued_bytes = uv__count_bufs(bufs, nbufs);
handle->reqs_pending++;
handle->stream.conn.write_reqs_pending++;
REGISTER_HANDLE_REQ(loop, handle, req);
REGISTER_HANDLE_REQ(loop, handle);
handle->write_queue_size += req->u.io.queued_bytes;
if (handle->flags & UV_HANDLE_EMULATE_IOCP &&
!RegisterWaitForSingleObject(&req->wait_handle,
@ -934,7 +946,7 @@ int uv__tcp_write(uv_loop_t* loop,
req->u.io.queued_bytes = 0;
handle->reqs_pending++;
handle->stream.conn.write_reqs_pending++;
REGISTER_HANDLE_REQ(loop, handle, req);
REGISTER_HANDLE_REQ(loop, handle);
SET_REQ_ERROR(req, WSAGetLastError());
uv__insert_pending_req(loop, (uv_req_t*) req);
}
@ -1105,7 +1117,7 @@ void uv__process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle,
assert(handle->write_queue_size >= req->u.io.queued_bytes);
handle->write_queue_size -= req->u.io.queued_bytes;
UNREGISTER_HANDLE_REQ(loop, handle, req);
UNREGISTER_HANDLE_REQ(loop, handle);
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
if (req->wait_handle != INVALID_HANDLE_VALUE) {
@ -1197,7 +1209,7 @@ void uv__process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle,
assert(handle->type == UV_TCP);
UNREGISTER_HANDLE_REQ(loop, handle, req);
UNREGISTER_HANDLE_REQ(loop, handle);
err = 0;
if (handle->delayed_error) {
@ -1551,11 +1563,6 @@ int uv__tcp_connect(uv_connect_t* req,
return 0;
}
#ifndef WSA_FLAG_NO_HANDLE_INHERIT
/* Added in Windows 7 SP1. Specify this to avoid race conditions, */
/* but also manually clear the inherit flag in case this failed. */
#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
#endif
int uv_socketpair(int type, int protocol, uv_os_sock_t fds[2], int flags0, int flags1) {
SOCKET server = INVALID_SOCKET;

View File

@ -32,45 +32,23 @@
#include "uv.h"
#include "internal.h"
static void uv__once_inner(uv_once_t* guard, void (*callback)(void)) {
DWORD result;
HANDLE existing_event, created_event;
typedef void (*uv__once_cb)(void);
created_event = CreateEvent(NULL, 1, 0, NULL);
if (created_event == 0) {
/* Could fail in a low-memory situation? */
uv_fatal_error(GetLastError(), "CreateEvent");
}
typedef struct {
uv__once_cb callback;
} uv__once_data_t;
existing_event = InterlockedCompareExchangePointer(&guard->event,
created_event,
NULL);
static BOOL WINAPI uv__once_inner(INIT_ONCE *once, void* param, void** context) {
uv__once_data_t* data = param;
if (existing_event == NULL) {
/* We won the race */
callback();
data->callback();
result = SetEvent(created_event);
assert(result);
guard->ran = 1;
} else {
/* We lost the race. Destroy the event we created and wait for the existing
* one to become signaled. */
CloseHandle(created_event);
result = WaitForSingleObject(existing_event, INFINITE);
assert(result == WAIT_OBJECT_0);
}
return TRUE;
}
void uv_once(uv_once_t* guard, void (*callback)(void)) {
/* Fast case - avoid WaitForSingleObject. */
if (guard->ran) {
return;
}
uv__once_inner(guard, callback);
void uv_once(uv_once_t* guard, uv__once_cb callback) {
uv__once_data_t data = { .callback = callback };
InitOnceExecuteOnce(&guard->init_once, uv__once_inner, (void*) &data, NULL);
}

View File

@ -2183,7 +2183,7 @@ int uv__tty_write(uv_loop_t* loop,
handle->reqs_pending++;
handle->stream.conn.write_reqs_pending++;
REGISTER_HANDLE_REQ(loop, handle, req);
REGISTER_HANDLE_REQ(loop, handle);
req->u.io.queued_bytes = 0;
@ -2219,7 +2219,7 @@ void uv__process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
int err;
handle->write_queue_size -= req->u.io.queued_bytes;
UNREGISTER_HANDLE_REQ(loop, handle, req);
UNREGISTER_HANDLE_REQ(loop, handle);
if (req->cb) {
err = GET_REQ_ERROR(req);
@ -2263,7 +2263,7 @@ void uv__process_tty_shutdown_req(uv_loop_t* loop, uv_tty_t* stream, uv_shutdown
assert(req);
stream->stream.conn.shutdown_req = NULL;
UNREGISTER_HANDLE_REQ(loop, stream, req);
UNREGISTER_HANDLE_REQ(loop, stream);
/* TTY shutdown is really just a no-op */
if (req->cb) {
@ -2380,8 +2380,8 @@ static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param) {
/* Make sure to not overwhelm the system with resize events */
Sleep(33);
WaitForSingleObject(uv__tty_console_resized, INFINITE);
uv__tty_console_signal_resize();
ResetEvent(uv__tty_console_resized);
uv__tty_console_signal_resize();
}
return 0;
}

12
deps/uv/src/win/udp.c vendored
View File

@ -200,6 +200,12 @@ static int uv__udp_maybe_bind(uv_udp_t* handle,
if (handle->flags & UV_HANDLE_BOUND)
return 0;
/* There is no SO_REUSEPORT on Windows, Windows only knows SO_REUSEADDR.
* so we just return an error directly when UV_UDP_REUSEPORT is requested
* for binding the socket. */
if (flags & UV_UDP_REUSEPORT)
return ERROR_NOT_SUPPORTED;
if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6) {
/* UV_UDP_IPV6ONLY is supported only for IPV6 sockets */
return ERROR_INVALID_PARAMETER;
@ -376,7 +382,7 @@ static int uv__send(uv_udp_send_t* req,
handle->reqs_pending++;
handle->send_queue_size += req->u.io.queued_bytes;
handle->send_queue_count++;
REGISTER_HANDLE_REQ(loop, handle, req);
REGISTER_HANDLE_REQ(loop, handle);
uv__insert_pending_req(loop, (uv_req_t*)req);
} else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
/* Request queued by the kernel. */
@ -384,7 +390,7 @@ static int uv__send(uv_udp_send_t* req,
handle->reqs_pending++;
handle->send_queue_size += req->u.io.queued_bytes;
handle->send_queue_count++;
REGISTER_HANDLE_REQ(loop, handle, req);
REGISTER_HANDLE_REQ(loop, handle);
} else {
/* Send failed due to an error. */
return WSAGetLastError();
@ -527,7 +533,7 @@ void uv__process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle,
handle->send_queue_size -= req->u.io.queued_bytes;
handle->send_queue_count--;
UNREGISTER_HANDLE_REQ(loop, handle, req);
UNREGISTER_HANDLE_REQ(loop, handle);
if (req->cb) {
err = 0;

View File

@ -316,25 +316,19 @@ uv_pid_t uv_os_getpid(void) {
uv_pid_t uv_os_getppid(void) {
int parent_pid = -1;
HANDLE handle;
PROCESSENTRY32 pe;
DWORD current_pid = GetCurrentProcessId();
NTSTATUS nt_status;
PROCESS_BASIC_INFORMATION basic_info;
pe.dwSize = sizeof(PROCESSENTRY32);
handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (Process32First(handle, &pe)) {
do {
if (pe.th32ProcessID == current_pid) {
parent_pid = pe.th32ParentProcessID;
break;
}
} while( Process32Next(handle, &pe));
nt_status = pNtQueryInformationProcess(GetCurrentProcess(),
ProcessBasicInformation,
&basic_info,
sizeof(basic_info),
NULL);
if (NT_SUCCESS(nt_status)) {
return basic_info.InheritedFromUniqueProcessId;
} else {
return -1;
}
CloseHandle(handle);
return parent_pid;
}
@ -512,19 +506,23 @@ int uv_uptime(double* uptime) {
unsigned int uv_available_parallelism(void) {
SYSTEM_INFO info;
unsigned rc;
DWORD_PTR procmask;
DWORD_PTR sysmask;
int count;
int i;
/* TODO(bnoordhuis) Use GetLogicalProcessorInformationEx() to support systems
* with > 64 CPUs? See https://github.com/libuv/libuv/pull/3458
*/
GetSystemInfo(&info);
count = 0;
if (GetProcessAffinityMask(GetCurrentProcess(), &procmask, &sysmask))
for (i = 0; i < 8 * sizeof(procmask); i++)
count += 1 & (procmask >> i);
rc = info.dwNumberOfProcessors;
if (rc < 1)
rc = 1;
if (count > 0)
return count;
return rc;
return 1;
}
@ -942,8 +940,13 @@ int uv_os_homedir(char* buffer, size_t* size) {
r = uv_os_getenv("USERPROFILE", buffer, size);
/* Don't return an error if USERPROFILE was not found. */
if (r != UV_ENOENT)
if (r != UV_ENOENT) {
/* USERPROFILE is empty or invalid */
if (r == 0 && *size < 3) {
return UV_ENOENT;
}
return r;
}
/* USERPROFILE is not set, so call uv_os_get_passwd() */
r = uv_os_get_passwd(&pwd);
@ -980,6 +983,12 @@ int uv_os_tmpdir(char* buffer, size_t* size) {
if (len == 0) {
return uv_translate_sys_error(GetLastError());
}
/* tmp path is empty or invalid */
if (len < 3) {
return UV_ENOENT;
}
/* Include space for terminating null char. */
len += 1;
path = uv__malloc(len * sizeof(wchar_t));
@ -1259,6 +1268,9 @@ int uv_os_getenv(const char* name, char* buffer, size_t* size) {
SetLastError(ERROR_SUCCESS);
len = GetEnvironmentVariableW(name_w, var, varlen);
if (len == 0)
r = uv_translate_sys_error(GetLastError());
if (len < varlen)
break;
@ -1280,15 +1292,8 @@ int uv_os_getenv(const char* name, char* buffer, size_t* size) {
uv__free(name_w);
name_w = NULL;
if (len == 0) {
r = GetLastError();
if (r != ERROR_SUCCESS) {
r = uv_translate_sys_error(r);
goto fail;
}
}
r = uv__copy_utf16_to_utf8(var, len, buffer, size);
if (r == 0)
r = uv__copy_utf16_to_utf8(var, len, buffer, size);
fail:
@ -1528,20 +1533,7 @@ int uv_os_uname(uv_utsname_t* buffer) {
os_info.dwOSVersionInfoSize = sizeof(os_info);
os_info.szCSDVersion[0] = L'\0';
/* Try calling RtlGetVersion(), and fall back to the deprecated GetVersionEx()
if RtlGetVersion() is not available. */
if (pRtlGetVersion) {
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;
}
}
pRtlGetVersion(&os_info);
/* Populate the version field. */
version_size = 0;

View File

@ -48,12 +48,16 @@ sSetWinEventHook pSetWinEventHook;
/* ws2_32.dll function pointer */
uv_sGetHostNameW pGetHostNameW;
/* api-ms-win-core-file-l2-1-4.dll function pointer */
sGetFileInformationByName pGetFileInformationByName;
void uv__winapi_init(void) {
HMODULE ntdll_module;
HMODULE powrprof_module;
HMODULE user32_module;
HMODULE kernel32_module;
HMODULE ws2_32_module;
HMODULE api_win_core_file_module;
ntdll_module = GetModuleHandleA("ntdll.dll");
if (ntdll_module == NULL) {
@ -99,7 +103,7 @@ void uv__winapi_init(void) {
pNtQueryDirectoryFile = (sNtQueryDirectoryFile)
GetProcAddress(ntdll_module, "NtQueryDirectoryFile");
if (pNtQueryVolumeInformationFile == NULL) {
if (pNtQueryDirectoryFile == NULL) {
uv_fatal_error(GetLastError(), "GetProcAddress");
}
@ -144,4 +148,10 @@ void uv__winapi_init(void) {
ws2_32_module,
"GetHostNameW");
}
api_win_core_file_module = GetModuleHandleA("api-ms-win-core-file-l2-1-4.dll");
if (api_win_core_file_module != NULL) {
pGetFileInformationByName = (sGetFileInformationByName)GetProcAddress(
api_win_core_file_module, "GetFileInformationByName");
}
}

View File

@ -4125,6 +4125,24 @@ typedef const UNICODE_STRING *PCUNICODE_STRING;
# define DEVICE_TYPE DWORD
#endif
typedef struct _FILE_STAT_BASIC_INFORMATION {
LARGE_INTEGER FileId;
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
LARGE_INTEGER AllocationSize;
LARGE_INTEGER EndOfFile;
ULONG FileAttributes;
ULONG ReparseTag;
ULONG NumberOfLinks;
ULONG DeviceType;
ULONG DeviceCharacteristics;
ULONG Reserved;
FILE_ID_128 FileId128;
LARGE_INTEGER VolumeSerialNumber;
} FILE_STAT_BASIC_INFORMATION;
/* MinGW already has a definition for REPARSE_DATA_BUFFER, but mingw-w64 does
* not.
*/
@ -4224,6 +4242,15 @@ typedef enum _FILE_INFORMATION_CLASS {
FileNumaNodeInformation,
FileStandardLinkInformation,
FileRemoteProtocolInformation,
FileRenameInformationBypassAccessCheck,
FileLinkInformationBypassAccessCheck,
FileVolumeNameInformation,
FileIdInformation,
FileIdExtdDirectoryInformation,
FileReplaceCompletionInformation,
FileHardLinkFullIdInformation,
FileIdExtdBothDirectoryInformation,
FileDispositionInformationEx, /* based on https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ne-wdm-_file_information_class */
FileMaximumInformation
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
@ -4323,6 +4350,10 @@ typedef struct _FILE_DISPOSITION_INFORMATION {
BOOLEAN DeleteFile;
} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION;
typedef struct _FILE_DISPOSITION_INFORMATION_EX {
DWORD Flags;
} FILE_DISPOSITION_INFORMATION_EX, *PFILE_DISPOSITION_INFORMATION_EX;
typedef struct _FILE_PIPE_LOCAL_INFORMATION {
ULONG NamedPipeType;
ULONG NamedPipeConfiguration;
@ -4427,6 +4458,14 @@ typedef struct _FILE_FS_SECTOR_SIZE_INFORMATION {
ULONG ByteOffsetForPartitionAlignment;
} FILE_FS_SECTOR_SIZE_INFORMATION, *PFILE_FS_SECTOR_SIZE_INFORMATION;
typedef struct _PROCESS_BASIC_INFORMATION {
PVOID Reserved1;
PVOID PebBaseAddress;
PVOID Reserved2[2];
ULONG_PTR UniqueProcessId;
ULONG_PTR InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION;
typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION {
LARGE_INTEGER IdleTime;
LARGE_INTEGER KernelTime;
@ -4440,6 +4479,10 @@ typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION {
# define SystemProcessorPerformanceInformation 8
#endif
#ifndef ProcessBasicInformation
# define ProcessBasicInformation 0
#endif
#ifndef ProcessConsoleHostProcess
# define ProcessConsoleHostProcess 49
#endif
@ -4739,6 +4782,21 @@ typedef struct _TCP_INITIAL_RTO_PARAMETERS {
# define SIO_TCP_INITIAL_RTO _WSAIOW(IOC_VENDOR,17)
#endif
/* from winnt.h */
typedef enum _FILE_INFO_BY_NAME_CLASS {
FileStatByNameInfo,
FileStatLxByNameInfo,
FileCaseSensitiveByNameInfo,
FileStatBasicByNameInfo,
MaximumFileInfoByNameClass
} FILE_INFO_BY_NAME_CLASS;
typedef BOOL(WINAPI* sGetFileInformationByName)(
PCWSTR FileName,
FILE_INFO_BY_NAME_CLASS FileInformationClass,
PVOID FileInfoBuffer,
ULONG FileInfoBufferSize);
/* Ntdll function pointers */
extern sRtlGetVersion pRtlGetVersion;
extern sRtlNtStatusToDosError pRtlNtStatusToDosError;
@ -4759,6 +4817,9 @@ extern sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotifi
/* User32.dll function pointer */
extern sSetWinEventHook pSetWinEventHook;
/* api-ms-win-core-file-l2-1-4.dll function pointers */
extern sGetFileInformationByName pGetFileInformationByName;
/* ws2_32.dll function pointer */
/* mingw doesn't have this definition, so let's declare it here locally */
typedef int (WINAPI *uv_sGetHostNameW)

4
deps/uv/test/task.h vendored
View File

@ -53,6 +53,10 @@
# define TEST_PIPENAME "\\\\.\\pipe\\uv-test"
# define TEST_PIPENAME_2 "\\\\.\\pipe\\uv-test2"
# define TEST_PIPENAME_3 "\\\\.\\pipe\\uv-test3"
#elif __ANDROID__
# define TEST_PIPENAME "/data/local/tmp/uv-test-sock"
# define TEST_PIPENAME_2 "/data/local/tmp/uv-test-sock2"
# define TEST_PIPENAME_3 "/data/local/tmp/uv-test-sock3"
#else
# define TEST_PIPENAME "/tmp/uv-test-sock"
# define TEST_PIPENAME_2 "/tmp/uv-test-sock2"

View File

@ -75,6 +75,14 @@ TEST_IMPL(emfile) {
ASSERT_EQ(errno, EMFILE);
close(maxfd);
#if defined(__ANDROID__)
/* Android connect syscall requires an extra file descriptor
*
* It fails in uv__tcp_connect
* */
close(maxfd - 1);
#endif
/* Now connect and use up the last available file descriptor. The EMFILE
* handling logic in src/unix/stream.c should ensure that connect_cb() runs
* whereas connection_cb() should *not* run.

View File

@ -33,6 +33,11 @@ TEST_IMPL(env_vars) {
int i, r, envcount, found, found_win_special;
uv_env_item_t* envitems;
#if defined(_WIN32) && defined(__ASAN__)
/* See investigation in https://github.com/libuv/libuv/issues/4338 */
RETURN_SKIP("Test does not currently work on Windows under ASAN");
#endif
/* Reject invalid inputs when setting an environment variable */
r = uv_os_setenv(NULL, "foo");
ASSERT_EQ(r, UV_EINVAL);

View File

@ -46,6 +46,8 @@ static void handle_result(uv_fs_t* req) {
uv_fs_t stat_req;
uint64_t size;
uint64_t mode;
uint64_t uid;
uint64_t gid;
int r;
ASSERT_EQ(req->fs_type, UV_FS_COPYFILE);
@ -56,11 +58,15 @@ static void handle_result(uv_fs_t* req) {
ASSERT_OK(r);
size = stat_req.statbuf.st_size;
mode = stat_req.statbuf.st_mode;
uid = stat_req.statbuf.st_uid;
gid = stat_req.statbuf.st_gid;
uv_fs_req_cleanup(&stat_req);
r = uv_fs_stat(NULL, &stat_req, dst, NULL);
ASSERT_OK(r);
ASSERT_EQ(stat_req.statbuf.st_size, size);
ASSERT_EQ(stat_req.statbuf.st_mode, mode);
ASSERT_EQ(stat_req.statbuf.st_uid, uid);
ASSERT_EQ(stat_req.statbuf.st_gid, gid);
uv_fs_req_cleanup(&stat_req);
uv_fs_req_cleanup(req);
result_check_count++;

View File

@ -29,16 +29,6 @@
# include <AvailabilityMacros.h>
#endif
#ifndef HAVE_KQUEUE
# if defined(__APPLE__) || \
defined(__DragonFly__) || \
defined(__FreeBSD__) || \
defined(__OpenBSD__) || \
defined(__NetBSD__)
# define HAVE_KQUEUE 1
# endif
#endif
static uv_fs_event_t fs_event;
static const char file_prefix[] = "fsevent-";
static const int fs_event_file_count = 16;
@ -91,6 +81,22 @@ static void create_file(const char* name) {
uv_fs_req_cleanup(&req);
}
static int delete_dir(const char* name) {
int r;
uv_fs_t req;
r = uv_fs_rmdir(NULL, &req, name, NULL);
uv_fs_req_cleanup(&req);
return r;
}
static int delete_file(const char* name) {
int r;
uv_fs_t req;
r = uv_fs_unlink(NULL, &req, name, NULL);
uv_fs_req_cleanup(&req);
return r;
}
static void touch_file(const char* name) {
int r;
uv_file file;
@ -139,6 +145,19 @@ static void fs_event_cb_dir(uv_fs_event_t* handle, const char* filename,
uv_close((uv_handle_t*)handle, close_cb);
}
static void fs_event_cb_del_dir(uv_fs_event_t* handle,
const char* filename,
int events,
int status) {
++fs_event_cb_called;
ASSERT_PTR_EQ(handle, &fs_event);
ASSERT_OK(status);
ASSERT(events == UV_CHANGE || events == UV_RENAME);
ASSERT_OK(strcmp(filename, "watch_del_dir"));
ASSERT_OK(uv_fs_event_stop(handle));
uv_close((uv_handle_t*)handle, close_cb);
}
static const char* fs_event_get_filename(int i) {
snprintf(fs_event_filename,
sizeof(fs_event_filename),
@ -162,6 +181,15 @@ static void fs_event_create_files(uv_timer_t* handle) {
}
}
static void fs_event_del_dir(uv_timer_t* handle) {
int r;
r = delete_dir("watch_del_dir");
ASSERT_OK(r);
uv_close((uv_handle_t*)handle, close_cb);
}
static void fs_event_unlink_files(uv_timer_t* handle) {
int r;
int i;
@ -170,7 +198,7 @@ static void fs_event_unlink_files(uv_timer_t* handle) {
if (handle == NULL) {
/* Unlink all files */
for (i = 0; i < 16; i++) {
r = remove(fs_event_get_filename(i));
r = delete_file(fs_event_get_filename(i));
if (handle != NULL)
ASSERT_OK(r);
}
@ -179,7 +207,7 @@ static void fs_event_unlink_files(uv_timer_t* handle) {
ASSERT_LT(fs_event_removed, fs_event_file_count);
/* Remove the file */
ASSERT_OK(remove(fs_event_get_filename(fs_event_removed)));
ASSERT_OK(delete_file(fs_event_get_filename(fs_event_removed)));
if (++fs_event_removed < fs_event_file_count) {
/* Remove another file on a different event loop tick. We do it this way
@ -197,12 +225,13 @@ static void fs_event_cb_dir_multi_file(uv_fs_event_t* handle,
ASSERT_PTR_EQ(handle, &fs_event);
ASSERT_OK(status);
ASSERT(events == UV_CHANGE || events == UV_RENAME);
#if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
ASSERT_OK(strncmp(filename, file_prefix, sizeof(file_prefix) - 1));
#else
ASSERT_NE(filename == NULL ||
strncmp(filename, file_prefix, sizeof(file_prefix) - 1) == 0, 0);
#endif
#if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
ASSERT_NOT_NULL(filename);
ASSERT_MEM_EQ(filename, file_prefix, sizeof(file_prefix) - 1);
#else
if (filename != NULL)
ASSERT_MEM_EQ(filename, file_prefix, sizeof(file_prefix) - 1);
#endif
if (fs_event_created + fs_event_removed == fs_event_file_count) {
/* Once we've processed all create events, delete all files */
@ -246,7 +275,7 @@ static void fs_event_unlink_files_in_subdir(uv_timer_t* handle) {
if (handle == NULL) {
/* Unlink all files */
for (i = 0; i < 16; i++) {
r = remove(fs_event_get_filename_in_subdir(i));
r = delete_file(fs_event_get_filename_in_subdir(i));
if (handle != NULL)
ASSERT_OK(r);
}
@ -255,7 +284,7 @@ static void fs_event_unlink_files_in_subdir(uv_timer_t* handle) {
ASSERT_LT(fs_event_removed, fs_event_file_count);
/* Remove the file */
ASSERT_OK(remove(fs_event_get_filename_in_subdir(fs_event_removed)));
ASSERT_OK(delete_file(fs_event_get_filename_in_subdir(fs_event_removed)));
if (++fs_event_removed < fs_event_file_count) {
/* Remove another file on a different event loop tick. We do it this way
@ -416,9 +445,9 @@ TEST_IMPL(fs_event_watch_dir) {
/* Setup */
fs_event_unlink_files(NULL);
remove("watch_dir/file2");
remove("watch_dir/file1");
remove("watch_dir/");
delete_file("watch_dir/file2");
delete_file("watch_dir/file1");
delete_dir("watch_dir/");
create_dir("watch_dir");
r = uv_fs_event_init(loop, &fs_event);
@ -437,9 +466,47 @@ TEST_IMPL(fs_event_watch_dir) {
/* Cleanup */
fs_event_unlink_files(NULL);
remove("watch_dir/file2");
remove("watch_dir/file1");
remove("watch_dir/");
delete_file("watch_dir/file2");
delete_file("watch_dir/file1");
delete_dir("watch_dir/");
MAKE_VALGRIND_HAPPY(loop);
return 0;
}
TEST_IMPL(fs_event_watch_delete_dir) {
#if defined(NO_FS_EVENTS)
RETURN_SKIP(NO_FS_EVENTS);
#elif defined(__MVS__)
RETURN_SKIP("Directory watching not supported on this platform.");
#elif defined(__APPLE__) && defined(__TSAN__)
RETURN_SKIP("Times out under TSAN.");
#endif
uv_loop_t* loop = uv_default_loop();
int r;
/* Setup */
fs_event_unlink_files(NULL);
delete_dir("watch_del_dir/");
create_dir("watch_del_dir");
r = uv_fs_event_init(loop, &fs_event);
ASSERT_OK(r);
r = uv_fs_event_start(&fs_event, fs_event_cb_del_dir, "watch_del_dir", 0);
ASSERT_OK(r);
r = uv_timer_init(loop, &timer);
ASSERT_OK(r);
r = uv_timer_start(&timer, fs_event_del_dir, 100, 0);
ASSERT_OK(r);
uv_run(loop, UV_RUN_DEFAULT);
ASSERT_EQ(1, fs_event_cb_called);
ASSERT_EQ(2, close_cb_called);
/* Cleanup */
fs_event_unlink_files(NULL);
MAKE_VALGRIND_HAPPY(loop);
return 0;
@ -457,10 +524,10 @@ TEST_IMPL(fs_event_watch_dir_recursive) {
/* Setup */
loop = uv_default_loop();
fs_event_unlink_files(NULL);
remove("watch_dir/file2");
remove("watch_dir/file1");
remove("watch_dir/subdir");
remove("watch_dir/");
delete_file("watch_dir/file2");
delete_file("watch_dir/file1");
delete_dir("watch_dir/subdir");
delete_dir("watch_dir/");
create_dir("watch_dir");
create_dir("watch_dir/subdir");
@ -500,10 +567,10 @@ TEST_IMPL(fs_event_watch_dir_recursive) {
/* Cleanup */
fs_event_unlink_files_in_subdir(NULL);
remove("watch_dir/file2");
remove("watch_dir/file1");
remove("watch_dir/subdir");
remove("watch_dir/");
delete_file("watch_dir/file2");
delete_file("watch_dir/file1");
delete_dir("watch_dir/subdir");
delete_dir("watch_dir/");
MAKE_VALGRIND_HAPPY(loop);
return 0;
@ -521,8 +588,8 @@ TEST_IMPL(fs_event_watch_dir_short_path) {
/* Setup */
loop = uv_default_loop();
remove("watch_dir/file1");
remove("watch_dir/");
delete_file("watch_dir/file1");
delete_dir("watch_dir/");
create_dir("watch_dir");
create_file("watch_dir/file1");
@ -549,8 +616,8 @@ TEST_IMPL(fs_event_watch_dir_short_path) {
}
/* Cleanup */
remove("watch_dir/file1");
remove("watch_dir/");
delete_file("watch_dir/file1");
delete_dir("watch_dir/");
MAKE_VALGRIND_HAPPY(loop);
@ -571,9 +638,9 @@ TEST_IMPL(fs_event_watch_file) {
int r;
/* Setup */
remove("watch_dir/file2");
remove("watch_dir/file1");
remove("watch_dir/");
delete_file("watch_dir/file2");
delete_file("watch_dir/file1");
delete_dir("watch_dir/");
create_dir("watch_dir");
create_file("watch_dir/file1");
create_file("watch_dir/file2");
@ -594,9 +661,9 @@ TEST_IMPL(fs_event_watch_file) {
ASSERT_EQ(2, close_cb_called);
/* Cleanup */
remove("watch_dir/file2");
remove("watch_dir/file1");
remove("watch_dir/");
delete_file("watch_dir/file2");
delete_file("watch_dir/file1");
delete_dir("watch_dir/");
MAKE_VALGRIND_HAPPY(loop);
return 0;
@ -618,9 +685,9 @@ TEST_IMPL(fs_event_watch_file_exact_path) {
loop = uv_default_loop();
/* Setup */
remove("watch_dir/file.js");
remove("watch_dir/file.jsx");
remove("watch_dir/");
delete_file("watch_dir/file.js");
delete_file("watch_dir/file.jsx");
delete_dir("watch_dir/");
create_dir("watch_dir");
create_file("watch_dir/file.js");
create_file("watch_dir/file.jsx");
@ -647,9 +714,9 @@ TEST_IMPL(fs_event_watch_file_exact_path) {
ASSERT_EQ(2, timer_cb_exact_called);
/* Cleanup */
remove("watch_dir/file.js");
remove("watch_dir/file.jsx");
remove("watch_dir/");
delete_file("watch_dir/file.js");
delete_file("watch_dir/file.jsx");
delete_dir("watch_dir/");
MAKE_VALGRIND_HAPPY(loop);
return 0;
@ -690,7 +757,7 @@ TEST_IMPL(fs_event_watch_file_current_dir) {
loop = uv_default_loop();
/* Setup */
remove("watch_file");
delete_file("watch_file");
create_file("watch_file");
#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_12)
/* Empirically, kevent seems to (sometimes) report the preceding
@ -728,7 +795,7 @@ TEST_IMPL(fs_event_watch_file_current_dir) {
ASSERT_EQ(1, close_cb_called);
/* Cleanup */
remove("watch_file");
delete_file("watch_file");
MAKE_VALGRIND_HAPPY(loop);
return 0;
@ -770,8 +837,8 @@ TEST_IMPL(fs_event_no_callback_after_close) {
int r;
/* Setup */
remove("watch_dir/file1");
remove("watch_dir/");
delete_file("watch_dir/file1");
delete_dir("watch_dir/");
create_dir("watch_dir");
create_file("watch_dir/file1");
@ -792,8 +859,8 @@ TEST_IMPL(fs_event_no_callback_after_close) {
ASSERT_EQ(1, close_cb_called);
/* Cleanup */
remove("watch_dir/file1");
remove("watch_dir/");
delete_file("watch_dir/file1");
delete_dir("watch_dir/");
MAKE_VALGRIND_HAPPY(loop);
return 0;
@ -808,8 +875,8 @@ TEST_IMPL(fs_event_no_callback_on_close) {
int r;
/* Setup */
remove("watch_dir/file1");
remove("watch_dir/");
delete_file("watch_dir/file1");
delete_dir("watch_dir/");
create_dir("watch_dir");
create_file("watch_dir/file1");
@ -829,8 +896,8 @@ TEST_IMPL(fs_event_no_callback_on_close) {
ASSERT_EQ(1, close_cb_called);
/* Cleanup */
remove("watch_dir/file1");
remove("watch_dir/");
delete_file("watch_dir/file1");
delete_dir("watch_dir/");
MAKE_VALGRIND_HAPPY(loop);
return 0;
@ -902,8 +969,8 @@ TEST_IMPL(fs_event_close_with_pending_event) {
ASSERT_EQ(1, close_cb_called);
/* Clean up */
remove("watch_dir/file");
remove("watch_dir/");
delete_file("watch_dir/file");
delete_dir("watch_dir/");
MAKE_VALGRIND_HAPPY(loop);
return 0;
@ -927,7 +994,7 @@ TEST_IMPL(fs_event_close_with_pending_delete_event) {
ASSERT_OK(r);
/* Generate an fs event. */
remove("watch_dir/file");
delete_file("watch_dir/file");
/* Allow time for the remove event to propagate to the pending list. */
/* XXX - perhaps just for __sun? */
@ -941,7 +1008,7 @@ TEST_IMPL(fs_event_close_with_pending_delete_event) {
ASSERT_EQ(1, close_cb_called);
/* Clean up */
remove("watch_dir/");
delete_dir("watch_dir/");
MAKE_VALGRIND_HAPPY(loop);
return 0;
@ -984,7 +1051,7 @@ TEST_IMPL(fs_event_close_in_callback) {
/* Clean up */
fs_event_unlink_files(NULL);
remove("watch_dir/");
delete_dir("watch_dir/");
MAKE_VALGRIND_HAPPY(loop);
return 0;
@ -1020,7 +1087,7 @@ TEST_IMPL(fs_event_start_and_close) {
ASSERT_EQ(2, close_cb_called);
remove("watch_dir/");
delete_dir("watch_dir/");
MAKE_VALGRIND_HAPPY(loop);
return 0;
}
@ -1073,111 +1140,11 @@ TEST_IMPL(fs_event_getpath) {
close_cb_called = 0;
}
remove("watch_dir/");
delete_dir("watch_dir/");
MAKE_VALGRIND_HAPPY(loop);
return 0;
}
#if defined(__APPLE__)
static int fs_event_error_reported;
static void fs_event_error_report_cb(uv_fs_event_t* handle,
const char* filename,
int events,
int status) {
if (status != 0)
fs_event_error_reported = status;
}
static void timer_cb_nop(uv_timer_t* handle) {
++timer_cb_called;
uv_close((uv_handle_t*) handle, close_cb);
}
static void fs_event_error_report_close_cb(uv_handle_t* handle) {
ASSERT_NOT_NULL(handle);
close_cb_called++;
/* handle is allocated on-stack, no need to free it */
}
TEST_IMPL(fs_event_error_reporting) {
unsigned int i;
uv_loop_t loops[1024];
uv_fs_event_t events[ARRAY_SIZE(loops)];
uv_loop_t* loop;
uv_fs_event_t* event;
TEST_FILE_LIMIT(ARRAY_SIZE(loops) * 3);
remove("watch_dir/");
create_dir("watch_dir");
/* Create a lot of loops, and start FSEventStream in each of them.
* Eventually, this should create enough streams to make FSEventStreamStart()
* fail.
*/
for (i = 0; i < ARRAY_SIZE(loops); i++) {
loop = &loops[i];
ASSERT_OK(uv_loop_init(loop));
event = &events[i];
timer_cb_called = 0;
close_cb_called = 0;
ASSERT_OK(uv_fs_event_init(loop, event));
ASSERT_OK(uv_fs_event_start(event,
fs_event_error_report_cb,
"watch_dir",
0));
uv_unref((uv_handle_t*) event);
/* Let loop run for some time */
ASSERT_OK(uv_timer_init(loop, &timer));
ASSERT_OK(uv_timer_start(&timer, timer_cb_nop, 2, 0));
uv_run(loop, UV_RUN_DEFAULT);
ASSERT_EQ(1, timer_cb_called);
ASSERT_EQ(1, close_cb_called);
if (fs_event_error_reported != 0)
break;
}
/* At least one loop should fail */
ASSERT_EQ(fs_event_error_reported, UV_EMFILE);
/* Stop and close all events, and destroy loops */
do {
loop = &loops[i];
event = &events[i];
ASSERT_OK(uv_fs_event_stop(event));
uv_ref((uv_handle_t*) event);
uv_close((uv_handle_t*) event, fs_event_error_report_close_cb);
close_cb_called = 0;
uv_run(loop, UV_RUN_DEFAULT);
ASSERT_EQ(1, close_cb_called);
uv_loop_close(loop);
} while (i-- != 0);
remove("watch_dir/");
MAKE_VALGRIND_HAPPY(uv_default_loop());
return 0;
}
#else /* !defined(__APPLE__) */
TEST_IMPL(fs_event_error_reporting) {
/* No-op, needed only for FSEvents backend */
MAKE_VALGRIND_HAPPY(uv_default_loop());
return 0;
}
#endif /* defined(__APPLE__) */
TEST_IMPL(fs_event_watch_invalid_path) {
#if defined(NO_FS_EVENTS)
RETURN_SKIP(NO_FS_EVENTS);
@ -1216,7 +1183,7 @@ TEST_IMPL(fs_event_stop_in_cb) {
RETURN_SKIP(NO_FS_EVENTS);
#endif
remove(path);
delete_file(path);
create_file(path);
ASSERT_OK(uv_fs_event_init(uv_default_loop(), &fs));
@ -1239,7 +1206,7 @@ TEST_IMPL(fs_event_stop_in_cb) {
ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT));
ASSERT_EQ(1, fs_event_cb_stop_calls);
remove(path);
delete_file(path);
MAKE_VALGRIND_HAPPY(uv_default_loop());
return 0;

View File

@ -29,6 +29,7 @@ static uv_fs_t readdir_req;
static uv_fs_t closedir_req;
static uv_dirent_t dirents[1];
static uv_dirent_t symlink_dirents[2];
static int empty_opendir_cb_count;
static int empty_closedir_cb_count;
@ -460,3 +461,88 @@ TEST_IMPL(fs_readdir_non_empty_dir) {
MAKE_VALGRIND_HAPPY(uv_default_loop());
return 0;
}
static void readdir_symlink_readdir_cb(uv_fs_t* req) {
uv_dir_t* dir;
ASSERT_PTR_EQ(req, &readdir_req);
ASSERT_EQ(req->fs_type, UV_FS_READDIR);
dir = req->ptr;
if (req->result == 0) {
uv_fs_req_cleanup(req);
ASSERT_EQ(3, non_empty_readdir_cb_count);
uv_fs_closedir(uv_default_loop(),
&closedir_req,
dir,
non_empty_closedir_cb);
} else {
if (strcmp(symlink_dirents[0].name, "test_symlink") == 0) {
ASSERT_EQ(symlink_dirents[0].type, UV_DIRENT_LINK);
} else {
ASSERT_EQ(symlink_dirents[1].type, UV_DIRENT_LINK);
}
uv_fs_req_cleanup(req);
}
}
static void readdir_symlink_opendir_cb(uv_fs_t* req) {
uv_dir_t* dir;
int r;
ASSERT_PTR_EQ(req, &opendir_req);
ASSERT_EQ(req->fs_type, UV_FS_OPENDIR);
ASSERT_OK(req->result);
ASSERT_NOT_NULL(req->ptr);
dir = req->ptr;
dir->dirents = symlink_dirents;
dir->nentries = ARRAY_SIZE(symlink_dirents);
r = uv_fs_readdir(uv_default_loop(),
&readdir_req,
dir,
readdir_symlink_readdir_cb);
ASSERT_OK(r);
uv_fs_req_cleanup(req);
}
static void cleanup_symlink_test_files(void) {
uv_fs_t req;
uv_fs_rmdir(NULL, &req, "test_symlink_dir/test_subdir", NULL);
uv_fs_req_cleanup(&req);
uv_fs_unlink(NULL, &req, "test_symlink_dir/test_symlink", NULL);
uv_fs_req_cleanup(&req);
uv_fs_rmdir(NULL, &req, "test_symlink_dir", NULL);
uv_fs_req_cleanup(&req);
}
TEST_IMPL(fs_readdir_symlink) {
uv_fs_t mkdir_req;
uv_fs_t symlink_req;
int r;
cleanup_symlink_test_files();
r = uv_fs_mkdir(uv_default_loop(), &mkdir_req, "test_symlink_dir", 0755, NULL);
ASSERT_OK(r);
r = uv_fs_mkdir(uv_default_loop(), &mkdir_req, "test_symlink_dir/test_subdir", 0755, NULL);
ASSERT_OK(r);
r = uv_fs_symlink(uv_default_loop(), &symlink_req, "test_symlink_dir/test_subdir", "test_symlink_dir/test_symlink", UV_FS_SYMLINK_DIR, NULL);
ASSERT_OK(r);
r = uv_fs_opendir(uv_default_loop(), &opendir_req, "test_symlink_dir", readdir_symlink_opendir_cb);
ASSERT_OK(r);
r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
ASSERT_OK(r);
cleanup_symlink_test_files();
MAKE_VALGRIND_HAPPY(uv_default_loop());
return 0;
}

View File

@ -104,6 +104,7 @@ static uv_loop_t* loop;
static uv_fs_t open_req1;
static uv_fs_t open_req2;
static uv_fs_t open_req_noclose;
static uv_fs_t read_req;
static uv_fs_t write_req;
static uv_fs_t unlink_req;
@ -304,7 +305,7 @@ static void chown_root_cb(uv_fs_t* req) {
ASSERT_EQ(req->result, UV_EINVAL);
# elif defined(__PASE__)
/* On IBMi PASE, there is no root user. uid 0 is user qsecofr.
* User may grant qsecofr's privileges, including changing
* User may grant qsecofr's privileges, including changing
* the file's ownership to uid 0.
*/
ASSERT(req->result == 0 || req->result == UV_EPERM);
@ -1067,6 +1068,50 @@ TEST_IMPL(fs_file_sync) {
return 0;
}
TEST_IMPL(fs_posix_delete) {
int r;
/* Setup. */
unlink("test_dir/file");
rmdir("test_dir");
r = uv_fs_mkdir(NULL, &mkdir_req, "test_dir", 0755, NULL);
ASSERT_OK(r);
r = uv_fs_open(NULL, &open_req_noclose, "test_dir/file", UV_FS_O_WRONLY | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL);
ASSERT_GE(r, 0);
uv_fs_req_cleanup(&open_req_noclose);
/* should not be possible to delete the non-empty dir */
r = uv_fs_rmdir(NULL, &rmdir_req, "test_dir", NULL);
ASSERT((r == UV_ENOTEMPTY) || (r == UV_EEXIST));
ASSERT_EQ(r, rmdir_req.result);
uv_fs_req_cleanup(&rmdir_req);
r = uv_fs_rmdir(NULL, &rmdir_req, "test_dir/file", NULL);
ASSERT((r == UV_ENOTDIR) || (r == UV_ENOENT));
ASSERT_EQ(r, rmdir_req.result);
uv_fs_req_cleanup(&rmdir_req);
r = uv_fs_unlink(NULL, &unlink_req, "test_dir/file", NULL);
ASSERT_OK(r);
ASSERT_OK(unlink_req.result);
uv_fs_req_cleanup(&unlink_req);
/* delete the dir while the file is still open, which should succeed on posix */
r = uv_fs_rmdir(NULL, &rmdir_req, "test_dir", NULL);
ASSERT_OK(r);
ASSERT_OK(rmdir_req.result);
uv_fs_req_cleanup(&rmdir_req);
/* Cleanup */
r = uv_fs_close(NULL, &close_req, open_req_noclose.result, NULL);
ASSERT_OK(r);
uv_fs_req_cleanup(&close_req);
MAKE_VALGRIND_HAPPY(uv_default_loop());
return 0;
}
static void fs_file_write_null_buffer(int add_flags) {
int r;

View File

@ -68,5 +68,14 @@ TEST_IMPL(homedir) {
r = uv_os_homedir(homedir, &len);
ASSERT_EQ(r, UV_EINVAL);
#ifdef _WIN32
/* Test empty environment variable */
r = uv_os_setenv("USERPROFILE", "");
ASSERT_EQ(r, 0);
len = sizeof homedir;
r = uv_os_homedir(homedir, &len);
ASSERT_EQ(r, UV_ENOENT);
#endif
return 0;
}

111
deps/uv/test/test-iouring-pollhup.c vendored Normal file
View File

@ -0,0 +1,111 @@
/* 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"
#ifdef _WIN32
TEST_IMPL(iouring_pollhup) {
RETURN_SKIP("Not on Windows.");
}
#else /* !_WIN32 */
#include <unistd.h> /* close() */
static uv_pipe_t p1;
static uv_pipe_t p2;
static uv_idle_t idle_handle;
static int iters;
static int duped_fd;
static int newpipefds[2];
static void alloc_buffer(uv_handle_t* handle,
size_t suggested_size,
uv_buf_t* buf) {
static char slab[32];
*buf = uv_buf_init(slab, sizeof(slab));
}
static void read_data2(uv_stream_t* stream,
ssize_t nread,
const uv_buf_t* buf) {
if (nread < 0) {
ASSERT_EQ(nread, UV_EOF);
ASSERT_OK(close(duped_fd));
duped_fd = -1;
uv_close((uv_handle_t*) &p2, NULL);
uv_close((uv_handle_t*) &idle_handle, NULL);
} else {
/* If nread == 0 is because of POLLHUP received still from pipefds[0] file
* description which is still referenced in duped_fd. It should not happen
* if close(p1) was called after EPOLL_CTL_DEL.
*/
ASSERT_GT(nread, 0);
}
}
static void idle_cb(uv_idle_t* handle) {
if (++iters == 1) {
ASSERT_OK(uv_pipe_open(&p2, newpipefds[0]));
ASSERT_OK(uv_read_start((uv_stream_t*) &p2, alloc_buffer, read_data2));
} else {
ASSERT_OK(uv_idle_stop(handle));
ASSERT_OK(close(newpipefds[1]));
newpipefds[1] = -1;
}
}
static void read_data(uv_stream_t* stream,
ssize_t nread,
const uv_buf_t* buf) {
ASSERT_EQ(nread, UV_EOF);
uv_close((uv_handle_t*) stream, NULL);
ASSERT_OK(uv_idle_start(&idle_handle, idle_cb));
}
TEST_IMPL(iouring_pollhup) {
uv_loop_t* loop;
int pipefds[2];
loop = uv_default_loop();
ASSERT_OK(uv_pipe_init(loop, &p1, 0));
ASSERT_OK(uv_pipe_init(loop, &p2, 0));
ASSERT_OK(uv_idle_init(loop, &idle_handle));
ASSERT_OK(pipe(pipefds));
ASSERT_OK(pipe(newpipefds));
ASSERT_OK(uv_pipe_open(&p1, pipefds[0]));
duped_fd = dup(pipefds[0]);
ASSERT_NE(duped_fd, -1);
ASSERT_OK(uv_read_start((uv_stream_t*) &p1, alloc_buffer, read_data));
ASSERT_OK(close(pipefds[1])); /* Close write end, generate POLLHUP. */
pipefds[1] = -1;
ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT));
MAKE_VALGRIND_HAPPY(uv_default_loop());
return 0;
}
#endif /* !_WIN32 */

View File

@ -153,6 +153,7 @@ 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_reuseport)
TEST_DECLARE (tcp_rst)
TEST_DECLARE (tcp_bind6_error_addrinuse)
TEST_DECLARE (tcp_bind6_error_addrnotavail)
@ -189,6 +190,7 @@ TEST_DECLARE (udp_open_twice)
TEST_DECLARE (udp_open_bound)
TEST_DECLARE (udp_open_connect)
TEST_DECLARE (udp_recv_in_a_row)
TEST_DECLARE (udp_reuseport)
#ifndef _WIN32
TEST_DECLARE (udp_send_unix)
#endif
@ -207,6 +209,7 @@ TEST_DECLARE (pipe_connect_to_file)
TEST_DECLARE (pipe_connect_on_prepare)
TEST_DECLARE (pipe_getsockname)
TEST_DECLARE (pipe_getsockname_abstract)
TEST_DECLARE (pipe_getsockname_autobind)
TEST_DECLARE (pipe_getsockname_blocking)
TEST_DECLARE (pipe_pending_instances)
TEST_DECLARE (pipe_sendmsg)
@ -352,6 +355,7 @@ TEST_DECLARE (fs_file_nametoolong)
TEST_DECLARE (fs_file_loop)
TEST_DECLARE (fs_file_async)
TEST_DECLARE (fs_file_sync)
TEST_DECLARE (fs_posix_delete)
TEST_DECLARE (fs_file_write_null_buffer)
TEST_DECLARE (fs_async_dir)
TEST_DECLARE (fs_async_sendfile)
@ -393,6 +397,7 @@ TEST_DECLARE (fs_stat_missing_path)
TEST_DECLARE (fs_read_bufs)
TEST_DECLARE (fs_read_file_eof)
TEST_DECLARE (fs_event_watch_dir)
TEST_DECLARE (fs_event_watch_delete_dir)
TEST_DECLARE (fs_event_watch_dir_recursive)
#ifdef _WIN32
TEST_DECLARE (fs_event_watch_dir_short_path)
@ -412,7 +417,6 @@ TEST_DECLARE (fs_event_close_with_pending_event)
TEST_DECLARE (fs_event_close_with_pending_delete_event)
TEST_DECLARE (fs_event_close_in_callback)
TEST_DECLARE (fs_event_start_and_close)
TEST_DECLARE (fs_event_error_reporting)
TEST_DECLARE (fs_event_getpath)
TEST_DECLARE (fs_event_stop_in_cb)
TEST_DECLARE (fs_scandir_empty_dir)
@ -424,6 +428,9 @@ TEST_DECLARE (fs_readdir_empty_dir)
TEST_DECLARE (fs_readdir_file)
TEST_DECLARE (fs_readdir_non_empty_dir)
TEST_DECLARE (fs_readdir_non_existing_dir)
#ifdef _WIN32
TEST_DECLARE (fs_readdir_symlink)
#endif
TEST_DECLARE (fs_rename_to_existing_file)
TEST_DECLARE (fs_write_multiple_bufs)
TEST_DECLARE (fs_read_write_null_arguments)
@ -563,6 +570,8 @@ TEST_DECLARE (fork_threadpool_queue_work_simple)
#endif
#endif
TEST_DECLARE (iouring_pollhup)
TEST_DECLARE (idna_toascii)
TEST_DECLARE (utf8_decode1)
TEST_DECLARE (utf8_decode1_overrun)
@ -763,6 +772,8 @@ TASK_LIST_START
TEST_ENTRY (tcp_read_stop_start)
TEST_ENTRY (tcp_reuseport)
TEST_ENTRY (tcp_rst)
TEST_HELPER (tcp_rst, tcp4_echo_server)
@ -799,6 +810,7 @@ TASK_LIST_START
TEST_ENTRY (udp_sendmmsg_error)
TEST_ENTRY (udp_try_send)
TEST_ENTRY (udp_recv_in_a_row)
TEST_ENTRY (udp_reuseport)
TEST_ENTRY (udp_open)
TEST_ENTRY (udp_open_twice)
@ -818,6 +830,7 @@ TASK_LIST_START
TEST_ENTRY (pipe_overlong_path)
TEST_ENTRY (pipe_getsockname)
TEST_ENTRY (pipe_getsockname_abstract)
TEST_ENTRY (pipe_getsockname_autobind)
TEST_ENTRY (pipe_getsockname_blocking)
TEST_ENTRY (pipe_pending_instances)
TEST_ENTRY (pipe_sendmsg)
@ -1055,6 +1068,7 @@ TASK_LIST_START
TEST_ENTRY (fs_file_loop)
TEST_ENTRY (fs_file_async)
TEST_ENTRY (fs_file_sync)
TEST_ENTRY (fs_posix_delete)
TEST_ENTRY (fs_file_write_null_buffer)
TEST_ENTRY (fs_async_dir)
TEST_ENTRY (fs_async_sendfile)
@ -1096,6 +1110,7 @@ TASK_LIST_START
TEST_ENTRY (fs_read_file_eof)
TEST_ENTRY (fs_file_open_append)
TEST_ENTRY (fs_event_watch_dir)
TEST_ENTRY (fs_event_watch_delete_dir)
TEST_ENTRY (fs_event_watch_dir_recursive)
#ifdef _WIN32
TEST_ENTRY (fs_event_watch_dir_short_path)
@ -1115,7 +1130,6 @@ TASK_LIST_START
TEST_ENTRY (fs_event_close_with_pending_delete_event)
TEST_ENTRY (fs_event_close_in_callback)
TEST_ENTRY (fs_event_start_and_close)
TEST_ENTRY_CUSTOM (fs_event_error_reporting, 0, 0, 60000)
TEST_ENTRY (fs_event_getpath)
TEST_ENTRY (fs_event_stop_in_cb)
TEST_ENTRY (fs_scandir_empty_dir)
@ -1127,6 +1141,9 @@ TASK_LIST_START
TEST_ENTRY (fs_readdir_file)
TEST_ENTRY (fs_readdir_non_empty_dir)
TEST_ENTRY (fs_readdir_non_existing_dir)
#ifdef _WIN32
TEST_ENTRY (fs_readdir_symlink)
#endif
TEST_ENTRY (fs_rename_to_existing_file)
TEST_ENTRY (fs_write_multiple_bufs)
TEST_ENTRY (fs_write_alotof_bufs)
@ -1204,6 +1221,8 @@ TASK_LIST_START
#endif
#endif
TEST_ENTRY (iouring_pollhup)
TEST_ENTRY (utf8_decode1)
TEST_ENTRY (utf8_decode1_overrun)
TEST_ENTRY (uname)

View File

@ -172,19 +172,36 @@ TEST_IMPL(pipe_overlong_path) {
#ifndef _WIN32
char path[512];
memset(path, '@', sizeof(path));
ASSERT_EQ(UV_EINVAL,
uv_pipe_bind2(&pipe, path, sizeof(path), UV_PIPE_NO_TRUNCATE));
ASSERT_EQ(UV_EINVAL,
uv_pipe_connect2(&req,
&pipe,
path,
sizeof(path),
UV_PIPE_NO_TRUNCATE,
(uv_connect_cb) abort));
ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT));
#endif
ASSERT_EQ(UV_EINVAL, uv_pipe_bind(&pipe, ""));
/* On most platforms sun_path is smaller than the NAME_MAX
* Though there is nothing in the POSIX spec that says it needs to be.
* POSIX allows PATH_MAX length paths in saddr.sun_path BUT individual
* components of the path can only be NAME_MAX long.
* So in this case we end up with UV_ENAMETOOLONG error rather than
* UV_EINVAL.
* ref: https://github.com/libuv/libuv/issues/4231#issuecomment-2194612711
* On AIX the sun_path is larger than the NAME_MAX
*/
#if defined(_AIX) && !defined(__PASE__)
ASSERT_EQ(UV_ENAMETOOLONG,
uv_pipe_bind2(&pipe, path, sizeof(path), UV_PIPE_NO_TRUNCATE));
/* UV_ENAMETOOLONG is delayed in uv_pipe_connect2 and won't propagate until
* uv_run is called and causes timeouts, therefore in this case we skip calling
* uv_pipe_connect2
*/
#else
ASSERT_EQ(UV_EINVAL,
uv_pipe_bind2(&pipe, path, sizeof(path), UV_PIPE_NO_TRUNCATE));
ASSERT_EQ(UV_EINVAL,
uv_pipe_connect2(&req,
&pipe,
path,
sizeof(path),
UV_PIPE_NO_TRUNCATE,
(uv_connect_cb) abort));
ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT));
#endif /*if defined(_AIX) && !defined(__PASE__)*/
#endif /* ifndef _WIN32 */
uv_pipe_connect(&req,
&pipe,
"",
@ -195,5 +212,4 @@ TEST_IMPL(pipe_overlong_path) {
MAKE_VALGRIND_HAPPY(uv_default_loop());
return 0;
}

View File

@ -78,6 +78,36 @@ static void pipe_client_connect_cb(uv_connect_t* req, int status) {
}
#if defined(__linux__)
/* Socket name looks like \0[0-9a-f]{5}, e.g. "\0bad42" */
static void check_is_autobind_abstract_socket_name(const char *p, size_t len) {
ASSERT_EQ(len, 6);
ASSERT_EQ(*p, '\0');
while (*p != '\0') {
ASSERT((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f'));
p++;
}
}
static void pipe_client_autobind_connect_cb(uv_connect_t* req, int status) {
char buf[16];
size_t len;
ASSERT_OK(status);
len = 5;
ASSERT_EQ(UV_ENOBUFS, uv_pipe_getpeername(&pipe_client, buf, &len));
len = 6;
ASSERT_OK(uv_pipe_getpeername(&pipe_client, buf, &len));
check_is_autobind_abstract_socket_name(buf, len);
pipe_client_connect_cb_called++;
uv_close((uv_handle_t*) &pipe_client, pipe_close_cb);
uv_close((uv_handle_t*) &pipe_server, pipe_close_cb);
}
#endif /* defined(__linux__) */
static void pipe_server_connection_cb(uv_stream_t* handle, int status) {
/* This function *may* be called, depending on whether accept or the
* connection callback is called first.
@ -124,9 +154,11 @@ TEST_IMPL(pipe_getsockname) {
ASSERT_STR_EQ(pipe_server.pipe_fname, TEST_PIPENAME);
#endif
len = sizeof buf;
r = uv_pipe_getsockname(&pipe_server, buf, &len);
ASSERT_OK(r);
len = sizeof(TEST_PIPENAME) - 1;
ASSERT_EQ(UV_ENOBUFS, uv_pipe_getsockname(&pipe_server, buf, &len));
len = sizeof(TEST_PIPENAME);
ASSERT_OK(uv_pipe_getsockname(&pipe_server, buf, &len));
ASSERT_NE(0, buf[len - 1]);
ASSERT_EQ(buf[len], '\0');
@ -160,7 +192,8 @@ TEST_IMPL(pipe_getsockname) {
len = sizeof buf;
r = uv_pipe_getsockname(&pipe_client, buf, &len);
ASSERT(r == 0 && len == 0);
ASSERT_EQ(r, 0);
ASSERT_EQ(len, 0);
len = sizeof buf;
r = uv_pipe_getpeername(&pipe_client, buf, &len);
@ -228,6 +261,44 @@ TEST_IMPL(pipe_getsockname_abstract) {
#endif
}
TEST_IMPL(pipe_getsockname_autobind) {
#if defined(__linux__)
char buf[256];
size_t buflen;
buflen = sizeof(buf);
memset(buf, 0, sizeof(buf));
ASSERT_OK(uv_pipe_init(uv_default_loop(), &pipe_server, 0));
ASSERT_OK(uv_pipe_bind2(&pipe_server, "", 0, 0));
ASSERT_OK(uv_pipe_getsockname(&pipe_server, buf, &buflen));
check_is_autobind_abstract_socket_name(buf, buflen);
ASSERT_OK(uv_listen((uv_stream_t*) &pipe_server, 0,
pipe_server_connection_cb));
ASSERT_OK(uv_pipe_init(uv_default_loop(), &pipe_client, 0));
ASSERT_OK(uv_pipe_connect2(&connect_req, &pipe_client,
buf,
1 + strlen(&buf[1]),
0,
pipe_client_autobind_connect_cb));
ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT));
ASSERT_EQ(1, pipe_client_connect_cb_called);
ASSERT_EQ(2, pipe_close_cb_called);
MAKE_VALGRIND_HAPPY(uv_default_loop());
return 0;
#else
/* On other platforms it should simply fail with UV_EINVAL. */
ASSERT_OK(uv_pipe_init(uv_default_loop(), &pipe_server, 0));
ASSERT_EQ(UV_EINVAL, uv_pipe_bind2(&pipe_server, "", 0, 0));
uv_close((uv_handle_t*) &pipe_server, pipe_close_cb);
ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT));
ASSERT_EQ(1, pipe_close_cb_called);
MAKE_VALGRIND_HAPPY(uv_default_loop());
return 0;
#endif
}
TEST_IMPL(pipe_getsockname_blocking) {
#ifdef _WIN32
HANDLE readh, writeh;

View File

@ -90,6 +90,45 @@ TEST_IMPL(platform_output) {
ASSERT_GE(par, 1);
printf("uv_available_parallelism: %u\n", par);
#ifdef __linux__
FILE* file;
int cgroup_version = 0;
unsigned int cgroup_par = 0;
uint64_t quota, period;
// Attempt to parse cgroup v2 to deduce parallelism constraints
file = fopen("/sys/fs/cgroup/cpu.max", "r");
if (file) {
if (fscanf(file, "%lu %lu", &quota, &period) == 2 && quota > 0) {
cgroup_version = 2;
cgroup_par = (unsigned int)(quota / period);
}
fclose(file);
}
// If cgroup v2 wasn't present, try parsing cgroup v1
if (cgroup_version == 0) {
file = fopen("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us", "r");
if (file) {
if (fscanf(file, "%lu", &quota) == 1 && quota > 0 && quota < ~0ULL) {
fclose(file);
file = fopen("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us", "r");
if (file && fscanf(file, "%lu", &period) == 1) {
cgroup_version = 1;
cgroup_par = (unsigned int)(quota / period);
}
}
if (file) fclose(file);
}
}
// If we found cgroup parallelism constraints, assert and print them
if (cgroup_par > 0) {
ASSERT_GE(par, cgroup_par);
printf("cgroup v%d available parallelism: %u\n", cgroup_version, cgroup_par);
}
#endif
err = uv_cpu_info(&cpus, &count);
#if defined(__CYGWIN__) || defined(__MSYS__)
ASSERT_EQ(err, UV_ENOSYS);

View File

@ -626,26 +626,59 @@ TEST_IMPL(poll_unidirectional) {
/* Windows won't let you open a directory so we open a file instead.
* OS X lets you poll a file so open the $PWD instead. Both fail
* on Linux so it doesn't matter which one we pick. Both succeed
* on FreeBSD, Solaris and AIX so skip the test on those platforms.
* OS X lets you poll a file so open the $PWD instead. Both fail
* on Linux so it doesn't matter which one we pick. Both succeed
* on Solaris and AIX so skip the test on those platforms.
* On *BSD/Darwin, we disallow polling of regular files, directories.
* In addition to regular files, we also disallow FIFOs on Darwin.
*/
#ifdef __APPLE__
#define TEST_POLL_FIFO_PATH "/tmp/uv-test-poll-fifo"
#endif
TEST_IMPL(poll_bad_fdtype) {
#if !defined(__DragonFly__) && !defined(__FreeBSD__) && !defined(__sun) && \
#if !defined(__sun) && \
!defined(_AIX) && !defined(__MVS__) && \
!defined(__OpenBSD__) && !defined(__CYGWIN__) && !defined(__MSYS__) && \
!defined(__NetBSD__)
!defined(__CYGWIN__) && !defined(__MSYS__)
uv_poll_t poll_handle;
int fd;
int fd[2];
#if defined(_WIN32)
fd = _open("test/fixtures/empty_file", UV_FS_O_RDONLY);
fd[0] = _open("test/fixtures/empty_file", UV_FS_O_RDONLY);
#else
fd = open(".", UV_FS_O_RDONLY);
fd[0] = open(".", UV_FS_O_RDONLY);
#endif
ASSERT_NE(fd[0], -1);
ASSERT_NE(0, uv_poll_init(uv_default_loop(), &poll_handle, fd[0]));
ASSERT_OK(close(fd[0]));
#if defined(__APPLE__) || \
defined(__DragonFly__) || \
defined(__FreeBSD__) || \
defined(__OpenBSD__) || \
defined(__NetBSD__)
fd[0] = open("test/fixtures/empty_file", UV_FS_O_RDONLY);
ASSERT_NE(fd[0], -1);
/* Regular files should be banned from kqueue. */
ASSERT_NE(0, uv_poll_init(uv_default_loop(), &poll_handle, fd[0]));
ASSERT_OK(close(fd[0]));
#ifdef __APPLE__
ASSERT_OK(pipe(fd));
/* Pipes should be permitted in kqueue. */
ASSERT_EQ(0, uv_poll_init(uv_default_loop(), &poll_handle, fd[0]));
ASSERT_OK(close(fd[0]));
ASSERT_OK(close(fd[1]));
ASSERT_OK(mkfifo(TEST_POLL_FIFO_PATH, 0600));
fd[0] = open(TEST_POLL_FIFO_PATH, O_RDONLY | O_NONBLOCK);
ASSERT_NE(fd[0], -1);
fd[1] = open(TEST_POLL_FIFO_PATH, O_WRONLY | O_NONBLOCK);
ASSERT_NE(fd[1], -1);
/* FIFOs should be banned from kqueue. */
ASSERT_NE(0, uv_poll_init(uv_default_loop(), &poll_handle, fd[0]));
ASSERT_OK(close(fd[0]));
ASSERT_OK(close(fd[1]));
unlink(TEST_POLL_FIFO_PATH);
#endif
#endif
ASSERT_NE(fd, -1);
ASSERT_NE(0, uv_poll_init(uv_default_loop(), &poll_handle, fd));
ASSERT_OK(close(fd));
#endif
MAKE_VALGRIND_HAPPY(uv_default_loop());

View File

@ -63,7 +63,7 @@ static void fail_cb2(void) {
}
static void req_cb(uv_handle_t* req, int status) {
static void req_cb(uv_udp_send_t* req, int status) {
req_cb_called++;
}
@ -334,7 +334,7 @@ TEST_IMPL(udp_ref3) {
&buf,
1,
(const struct sockaddr*) &addr,
(uv_udp_send_cb) req_cb);
req_cb);
uv_unref((uv_handle_t*)&h);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
ASSERT_EQ(1, req_cb_called);

View File

@ -1054,7 +1054,7 @@ TEST_IMPL(kill) {
sigaddset(&set, SIGTERM);
ASSERT_OK(pthread_sigmask(SIG_BLOCK, &set, NULL));
}
ASSERT_NE(SIG_ERR, signal(SIGTERM, SIG_IGN));
ASSERT_PTR_NE(SIG_ERR, signal(SIGTERM, SIG_IGN));
#endif
r = uv_spawn(uv_default_loop(), &process, &options);
@ -1067,7 +1067,7 @@ TEST_IMPL(kill) {
sigaddset(&set, SIGTERM);
ASSERT_OK(pthread_sigmask(SIG_UNBLOCK, &set, NULL));
}
ASSERT_NE(SIG_ERR, signal(SIGTERM, SIG_DFL));
ASSERT_PTR_NE(SIG_ERR, signal(SIGTERM, SIG_DFL));
#endif
/* Sending signum == 0 should check if the

View File

@ -33,7 +33,8 @@ TEST_IMPL(tcp_flags) {
loop = uv_default_loop();
r = uv_tcp_init(loop, &handle);
/* Use _ex to make sure the socket is created. */
r = uv_tcp_init_ex(loop, &handle, AF_INET);
ASSERT_OK(r);
r = uv_tcp_nodelay(&handle, 1);
@ -42,6 +43,12 @@ TEST_IMPL(tcp_flags) {
r = uv_tcp_keepalive(&handle, 1, 60);
ASSERT_OK(r);
r = uv_tcp_keepalive(&handle, 0, 0);
ASSERT_OK(r);
r = uv_tcp_keepalive(&handle, 1, 0);
ASSERT_EQ(r, UV_EINVAL);
uv_close((uv_handle_t*)&handle, NULL);
r = uv_run(loop, UV_RUN_DEFAULT);

248
deps/uv/test/test-tcp-reuseport.c vendored Normal file
View File

@ -0,0 +1,248 @@
/* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#if !defined(__linux__) && !defined(__FreeBSD__) && \
!defined(__DragonFly__) && !defined(__sun) && !defined(_AIX73)
TEST_IMPL(tcp_reuseport) {
struct sockaddr_in addr;
uv_loop_t* loop;
uv_tcp_t handle;
int r;
ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
loop = uv_default_loop();
ASSERT_NOT_NULL(loop);
r = uv_tcp_init(loop, &handle);
ASSERT_OK(r);
r = uv_tcp_bind(&handle, (const struct sockaddr*) &addr, UV_TCP_REUSEPORT);
ASSERT_EQ(r, UV_ENOTSUP);
MAKE_VALGRIND_HAPPY(loop);
return 0;
}
#else
#define NUM_LISTENING_THREADS 2
#define MAX_TCP_CLIENTS 10
static uv_tcp_t tcp_connect_handles[MAX_TCP_CLIENTS];
static uv_connect_t tcp_connect_requests[MAX_TCP_CLIENTS];
static uv_sem_t semaphore;
static uv_mutex_t mutex;
static unsigned int accepted;
static unsigned int thread_loop1_accepted;
static unsigned int thread_loop2_accepted;
static unsigned int connected;
static uv_loop_t* main_loop;
static uv_loop_t thread_loop1;
static uv_loop_t thread_loop2;
static uv_tcp_t thread_handle1;
static uv_tcp_t thread_handle2;
static uv_timer_t thread_timer_handle1;
static uv_timer_t thread_timer_handle2;
static void on_close(uv_handle_t* handle) {
free(handle);
}
static void ticktack(uv_timer_t* timer) {
ASSERT(timer == &thread_timer_handle1 || timer == &thread_timer_handle2);
int done = 0;
uv_mutex_lock(&mutex);
if (accepted == MAX_TCP_CLIENTS) {
done = 1;
}
uv_mutex_unlock(&mutex);
if (done) {
uv_close((uv_handle_t*) timer, NULL);
if (timer->loop == &thread_loop1)
uv_close((uv_handle_t*) &thread_handle1, NULL);
if (timer->loop == &thread_loop2)
uv_close((uv_handle_t*) &thread_handle2, NULL);
}
}
static void on_connection(uv_stream_t* server, int status)
{
ASSERT_OK(status);
ASSERT(server == (uv_stream_t*) &thread_handle1 || \
server == (uv_stream_t*) &thread_handle2);
uv_tcp_t *client = malloc(sizeof(uv_tcp_t));
ASSERT_OK(uv_tcp_init(server->loop, client));
ASSERT_OK(uv_accept(server, (uv_stream_t*) client));
uv_close((uv_handle_t*) client, on_close);
if (server->loop == &thread_loop1)
thread_loop1_accepted++;
if (server->loop == &thread_loop2)
thread_loop2_accepted++;
uv_mutex_lock(&mutex);
accepted++;
uv_mutex_unlock(&mutex);
}
static void on_connect(uv_connect_t* req, int status) {
ASSERT_OK(status);
ASSERT_NOT_NULL(req->handle);
ASSERT_PTR_EQ(req->handle->loop, main_loop);
connected++;
uv_close((uv_handle_t*) req->handle, NULL);
}
static void create_listener(uv_loop_t* loop, uv_tcp_t* handle) {
struct sockaddr_in addr;
int r;
ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
r = uv_tcp_init(loop, handle);
ASSERT_OK(r);
r = uv_tcp_bind(handle, (const struct sockaddr*) &addr, UV_TCP_REUSEPORT);
ASSERT_OK(r);
r = uv_listen((uv_stream_t*) handle, 128, on_connection);
ASSERT_OK(r);
}
static void run_event_loop(void* arg) {
int r;
uv_tcp_t* handle;
uv_timer_t* timer;
uv_loop_t* loop = (uv_loop_t*) arg;
ASSERT(loop == &thread_loop1 || loop == &thread_loop2);
if (loop == &thread_loop1) {
handle = &thread_handle1;
timer = &thread_timer_handle1;
} else {
handle = &thread_handle2;
timer = &thread_timer_handle2;
}
create_listener(loop, handle);
r = uv_timer_init(loop, timer);
ASSERT_OK(r);
r = uv_timer_start(timer, ticktack, 0, 10);
ASSERT_OK(r);
/* Notify the main thread to start connecting. */
uv_sem_post(&semaphore);
r = uv_run(loop, UV_RUN_DEFAULT);
ASSERT_OK(r);
}
TEST_IMPL(tcp_reuseport) {
struct sockaddr_in addr;
int r;
int i;
r = uv_mutex_init(&mutex);
ASSERT_OK(r);
r = uv_sem_init(&semaphore, 0);
ASSERT_OK(r);
main_loop = uv_default_loop();
ASSERT_NOT_NULL(main_loop);
/* Run event loops of listeners in separate threads. */
uv_loop_init(&thread_loop1);
uv_loop_init(&thread_loop2);
uv_thread_t thread_loop_id1;
uv_thread_t thread_loop_id2;
uv_thread_create(&thread_loop_id1, run_event_loop, &thread_loop1);
uv_thread_create(&thread_loop_id2, run_event_loop, &thread_loop2);
/* Wait until all threads to poll for accepting connections
* before we start to connect. Otherwise the incoming connections
* might not be distributed across all listening threads. */
for (i = 0; i < NUM_LISTENING_THREADS; i++)
uv_sem_wait(&semaphore);
/* Now we know all threads are up and entering the uv_run(),
* but we still sleep a little bit just for dual fail-safe. */
uv_sleep(100);
/* Start connecting to the peers. */
ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
for (i = 0; i < MAX_TCP_CLIENTS; i++) {
r = uv_tcp_init(main_loop, &tcp_connect_handles[i]);
ASSERT_OK(r);
r = uv_tcp_connect(&tcp_connect_requests[i],
&tcp_connect_handles[i],
(const struct sockaddr*) &addr,
on_connect);
ASSERT_OK(r);
}
r = uv_run(main_loop, UV_RUN_DEFAULT);
ASSERT_OK(r);
/* Wait for all threads to exit. */
uv_thread_join(&thread_loop_id1);
uv_thread_join(&thread_loop_id2);
/* Verify if each listener per event loop accepted connections
* and the amount of accepted connections matches the one of
* connected connections.
*/
ASSERT_EQ(accepted, MAX_TCP_CLIENTS);
ASSERT_EQ(connected, MAX_TCP_CLIENTS);
ASSERT_GT(thread_loop1_accepted, 0);
ASSERT_GT(thread_loop2_accepted, 0);
ASSERT_EQ(thread_loop1_accepted + thread_loop2_accepted, connected);
/* Clean up. */
uv_mutex_destroy(&mutex);
uv_sem_destroy(&semaphore);
uv_loop_close(&thread_loop1);
uv_loop_close(&thread_loop2);
MAKE_VALGRIND_HAPPY(main_loop);
return 0;
}
#endif

View File

@ -76,6 +76,13 @@ TEST_IMPL(tmpdir) {
size_t lenx = sizeof tmpdirx;
r = uv_os_tmpdir(tmpdirx, &lenx);
ASSERT_OK(r);
/* Test empty environment variable */
r = uv_os_setenv("TMP", "");
ASSERT_EQ(r, 0);
len = sizeof tmpdir;
r = uv_os_tmpdir(tmpdir, &len);
ASSERT_EQ(r, UV_ENOENT);
#endif
return 0;

View File

@ -126,7 +126,7 @@ static void cl_recv_cb(uv_udp_t* handle,
r = uv_udp_set_membership(&server, MULTICAST_ADDR, NULL, UV_LEAVE_GROUP);
ASSERT_OK(r);
#if !defined(__OpenBSD__) && !defined(__NetBSD__)
#if !defined(__NetBSD__)
r = uv_udp_set_source_membership(&server, MULTICAST_ADDR, NULL, source_addr, UV_JOIN_GROUP);
ASSERT_OK(r);
#endif

View File

@ -192,6 +192,11 @@ TEST_IMPL(udp_multicast_join6) {
ASSERT_OK(r);
#if defined(__ANDROID__)
/* It returns an ENOSYS error */
RETURN_SKIP("Test does not currently work in ANDROID");
#endif
/* TODO(gengjiawen): Fix test on QEMU. */
#if defined(__QEMU__)
RETURN_SKIP("Test does not currently work in QEMU");

View File

@ -50,11 +50,16 @@ static void sv_recv_cb(uv_udp_t* handle,
const uv_buf_t* rcvbuf,
const struct sockaddr* addr,
unsigned flags) {
if (++ recv_cnt < N) {
ASSERT_EQ(sizeof(send_data), nread);
} else {
ASSERT_OK(nread);
}
/* |nread| can be zero when the kernel drops an incoming datagram after
* marking the file descriptor as readable but before libuv has a chance
* to receive it. Libuv still invokes the uv_udp_recv_cb callback to give
* back the memory from the uv_alloc_cb callback.
*
* See https://github.com/libuv/libuv/issues/4219.
*/
recv_cnt++;
if (nread > 0)
ASSERT_EQ(nread, sizeof(send_data));
}
static void check_cb(uv_check_t* handle) {
@ -63,9 +68,11 @@ static void check_cb(uv_check_t* handle) {
/**
* sv_recv_cb() is called with nread set to zero to indicate
* there is no more udp packet in the kernel, so the actual
* recv_cnt is one larger than N.
* recv_cnt is up to one larger than N. UDP being what it is,
* packets can get dropped so don't assume an exact count.
*/
ASSERT_EQ(N+1, recv_cnt);
ASSERT_GE(recv_cnt, 1);
ASSERT_LE(recv_cnt, N+1);
check_cb_called = 1;
/* we are done */

287
deps/uv/test/test-udp-reuseport.c vendored Normal file
View File

@ -0,0 +1,287 @@
/* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#if !defined(__linux__) && !defined(__FreeBSD__) && \
!defined(__DragonFly__) && !defined(__sun) && !defined(_AIX73)
TEST_IMPL(udp_reuseport) {
struct sockaddr_in addr1, addr2, addr3;
uv_loop_t* loop;
uv_udp_t handle1, handle2, handle3;
int r;
ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr1));
ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT_2, &addr2));
ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT_3, &addr3));
loop = uv_default_loop();
ASSERT_NOT_NULL(loop);
r = uv_udp_init(loop, &handle1);
ASSERT_OK(r);
r = uv_udp_bind(&handle1, (const struct sockaddr*) &addr1, UV_UDP_REUSEADDR);
ASSERT_OK(r);
r = uv_udp_init(loop, &handle2);
ASSERT_OK(r);
r = uv_udp_bind(&handle2, (const struct sockaddr*) &addr2, UV_UDP_REUSEPORT);
ASSERT_EQ(r, UV_ENOTSUP);
r = uv_udp_init(loop, &handle3);
ASSERT_OK(r);
/* For platforms where SO_REUSEPORTs don't have the capability of
* load balancing, specifying both UV_UDP_REUSEADDR and UV_UDP_REUSEPORT
* in flags will fail, returning an UV_ENOTSUP error. */
r = uv_udp_bind(&handle3, (const struct sockaddr*) &addr3,
UV_UDP_REUSEADDR | UV_UDP_REUSEPORT);
ASSERT_EQ(r, UV_ENOTSUP);
MAKE_VALGRIND_HAPPY(loop);
return 0;
}
#else
#define NUM_RECEIVING_THREADS 2
#define MAX_UDP_DATAGRAMS 10
static uv_udp_t udp_send_handles[MAX_UDP_DATAGRAMS];
static uv_udp_send_t udp_send_requests[MAX_UDP_DATAGRAMS];
static uv_sem_t semaphore;
static uv_mutex_t mutex;
static unsigned int received;
static unsigned int thread_loop1_recv;
static unsigned int thread_loop2_recv;
static unsigned int sent;
static uv_loop_t* main_loop;
static uv_loop_t thread_loop1;
static uv_loop_t thread_loop2;
static uv_udp_t thread_handle1;
static uv_udp_t thread_handle2;
static uv_timer_t thread_timer_handle1;
static uv_timer_t thread_timer_handle2;
static void alloc_cb(uv_handle_t* handle,
size_t suggested_size,
uv_buf_t* buf) {
buf->base = malloc(suggested_size);
buf->len = (int) suggested_size;
}
static void ticktack(uv_timer_t* timer) {
int done = 0;
ASSERT(timer == &thread_timer_handle1 || timer == &thread_timer_handle2);
uv_mutex_lock(&mutex);
if (received == MAX_UDP_DATAGRAMS) {
done = 1;
}
uv_mutex_unlock(&mutex);
if (done) {
uv_close((uv_handle_t*) timer, NULL);
if (timer->loop == &thread_loop1)
uv_close((uv_handle_t*) &thread_handle1, NULL);
if (timer->loop == &thread_loop2)
uv_close((uv_handle_t*) &thread_handle2, NULL);
}
}
static void on_recv(uv_udp_t* handle,
ssize_t nr,
const uv_buf_t* buf,
const struct sockaddr* addr,
unsigned flags) {
ASSERT_OK(flags);
ASSERT(handle == &thread_handle1 || handle == &thread_handle2);
ASSERT_GE(nr, 0);
if (nr == 0) {
ASSERT_NULL(addr);
free(buf->base);
return;
}
ASSERT_NOT_NULL(addr);
ASSERT_EQ(5, nr);
ASSERT(!memcmp("Hello", buf->base, nr));
free(buf->base);
if (handle->loop == &thread_loop1)
thread_loop1_recv++;
if (handle->loop == &thread_loop2)
thread_loop2_recv++;
uv_mutex_lock(&mutex);
received++;
uv_mutex_unlock(&mutex);
}
static void on_send(uv_udp_send_t* req, int status) {
ASSERT_OK(status);
ASSERT_PTR_EQ(req->handle->loop, main_loop);
if (++sent == MAX_UDP_DATAGRAMS)
uv_close((uv_handle_t*) req->handle, NULL);
}
static void bind_socket_and_prepare_recv(uv_loop_t* loop, uv_udp_t* handle) {
struct sockaddr_in addr;
int r;
ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
r = uv_udp_init(loop, handle);
ASSERT_OK(r);
/* For platforms where SO_REUSEPORTs have the capability of
* load balancing, specifying both UV_UDP_REUSEADDR and
* UV_UDP_REUSEPORT in flags is allowed and SO_REUSEPORT will
* always override the behavior of SO_REUSEADDR. */
r = uv_udp_bind(handle, (const struct sockaddr*) &addr,
UV_UDP_REUSEADDR | UV_UDP_REUSEPORT);
ASSERT_OK(r);
r = uv_udp_recv_start(handle, alloc_cb, on_recv);
ASSERT_OK(r);
}
static void run_event_loop(void* arg) {
int r;
uv_udp_t* handle;
uv_timer_t* timer;
uv_loop_t* loop = (uv_loop_t*) arg;
ASSERT(loop == &thread_loop1 || loop == &thread_loop2);
if (loop == &thread_loop1) {
handle = &thread_handle1;
timer = &thread_timer_handle1;
} else {
handle = &thread_handle2;
timer = &thread_timer_handle2;
}
bind_socket_and_prepare_recv(loop, handle);
r = uv_timer_init(loop, timer);
ASSERT_OK(r);
r = uv_timer_start(timer, ticktack, 0, 10);
ASSERT_OK(r);
/* Notify the main thread to start sending data. */
uv_sem_post(&semaphore);
r = uv_run(loop, UV_RUN_DEFAULT);
ASSERT_OK(r);
}
TEST_IMPL(udp_reuseport) {
struct sockaddr_in addr;
uv_buf_t buf;
int r;
int i;
r = uv_mutex_init(&mutex);
ASSERT_OK(r);
r = uv_sem_init(&semaphore, 0);
ASSERT_OK(r);
main_loop = uv_default_loop();
ASSERT_NOT_NULL(main_loop);
/* Run event loops of receiving sockets in separate threads. */
uv_loop_init(&thread_loop1);
uv_loop_init(&thread_loop2);
uv_thread_t thread_loop_id1;
uv_thread_t thread_loop_id2;
uv_thread_create(&thread_loop_id1, run_event_loop, &thread_loop1);
uv_thread_create(&thread_loop_id2, run_event_loop, &thread_loop2);
/* Wait until all threads to poll for receiving datagrams
* before we start to sending. Otherwise the incoming datagrams
* might not be distributed across all receiving threads. */
for (i = 0; i < NUM_RECEIVING_THREADS; i++)
uv_sem_wait(&semaphore);
/* Now we know all threads are up and entering the uv_run(),
* but we still sleep a little bit just for dual fail-safe. */
uv_sleep(100);
/* Start sending datagrams to the peers. */
buf = uv_buf_init("Hello", 5);
ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr));
for (i = 0; i < MAX_UDP_DATAGRAMS; i++) {
r = uv_udp_init(main_loop, &udp_send_handles[i]);
ASSERT_OK(r);
r = uv_udp_send(&udp_send_requests[i],
&udp_send_handles[i],
&buf,
1,
(const struct sockaddr*) &addr,
on_send);
ASSERT_OK(r);
}
r = uv_run(main_loop, UV_RUN_DEFAULT);
ASSERT_OK(r);
/* Wait for all threads to exit. */
uv_thread_join(&thread_loop_id1);
uv_thread_join(&thread_loop_id2);
/* Verify if each receiving socket per event loop received datagrams
* and the amount of received datagrams matches the one of sent datagrams.
*/
ASSERT_EQ(received, MAX_UDP_DATAGRAMS);
ASSERT_EQ(sent, MAX_UDP_DATAGRAMS);
ASSERT_GT(thread_loop1_recv, 0);
ASSERT_GT(thread_loop2_recv, 0);
ASSERT_EQ(thread_loop1_recv + thread_loop2_recv, sent);
/* Clean up. */
uv_mutex_destroy(&mutex);
uv_sem_destroy(&semaphore);
uv_loop_close(&thread_loop1);
uv_loop_close(&thread_loop2);
MAKE_VALGRIND_HAPPY(main_loop);
return 0;
}
#endif

1
deps/uv/uv.gyp vendored
View File

@ -124,6 +124,7 @@
'uv_sources_apple': [
'src/unix/darwin.c',
'src/unix/fsevents.c',
'src/unix/darwin-syscalls.h',
'src/unix/darwin-proctitle.c',
'src/unix/random-getentropy.c',
],