libuv 1.44.2

git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3934 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
Cory McWilliams 2022-07-24 21:25:38 +00:00
parent 41afc3bdd6
commit f06753b56e
67 changed files with 2148 additions and 764 deletions

View File

@ -0,0 +1,32 @@
name: ci-sample
on:
pull_request:
paths:
- '**'
- '!docs/**'
- '!.**'
- '.github/workflows/CI-sample.yml'
push:
branches:
- v[0-9].*
- master
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v2
- name: setup
run: cmake -E make_directory ${{runner.workspace}}/libuv/docs/code/build
- name: configure
# you may like use Ninja on unix-like OS, but for windows, the only easy way is to use Visual Studio if you want Ninja
run: cmake ..
working-directory: ${{runner.workspace}}/libuv/docs/code/build
- name: build
run: cmake --build .
working-directory: ${{runner.workspace}}/libuv/docs/code/build

129
deps/libuv/.github/workflows/CI-unix.yml vendored Normal file
View File

@ -0,0 +1,129 @@
name: CI-unix
on:
pull_request:
paths:
- '**'
- '!docs/**'
- '!src/win/**'
- '!.**'
- '.github/workflows/CI-unix.yml'
push:
branches:
- v[0-9].*
- master
jobs:
build-android:
runs-on: ubuntu-latest
container: reactnativecommunity/react-native-android:2020-5-20
steps:
- uses: actions/checkout@v2
- name: Envinfo
run: npx envinfo
- name: Configure android arm64
# see build options you can use in https://developer.android.com/ndk/guides/cmake
run: |
mkdir build
cd build
$ANDROID_HOME/cmake/3.10.2.4988404/bin/cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_HOME/ndk/20.0.5594570/build/cmake/android.toolchain.cmake -DCMAKE_BUILD_TYPE=Release -DANDROID_ABI="arm64-v8a" -DANDROID_PLATFORM=android-24 ..
- name: Build android arm64
run: |
$ANDROID_HOME/cmake/3.10.2.4988404/bin/cmake --build build
ls -lh build
build-macos:
runs-on: macos-10.15
steps:
- uses: actions/checkout@v2
- name: Envinfo
run: npx envinfo
- name: Setup
run: |
brew install ninja
- name: Configure
run: |
mkdir build
cd build
cmake .. -DBUILD_TESTING=ON -G Ninja
- name: Build
run: |
cmake --build build
ls -lh
- name: platform_output
run: |
./build/uv_run_tests platform_output
- name: platform_output_a
run: |
./build/uv_run_tests_a platform_output
- name: Test
run: |
cd build && ctest -V
build-ios:
runs-on: macos-10.15
steps:
- uses: actions/checkout@v2
- name: Configure
run: |
mkdir build-ios
cd build-ios
cmake .. -GXcode -DCMAKE_SYSTEM_NAME:STRING=iOS -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED:BOOL=NO -DCMAKE_CONFIGURATION_TYPES:STRING=Release
- name: Build
run: |
cmake --build build-ios
ls -lh build-ios
build-cross-qemu:
runs-on: ubuntu-latest
name: build-cross-qemu-${{ matrix.config.target }}
strategy:
fail-fast: false
matrix:
config:
- {target: arm, toolchain: gcc-arm-linux-gnueabi, cc: arm-linux-gnueabi-gcc, qemu: qemu-arm-static }
- {target: armhf, toolchain: gcc-arm-linux-gnueabihf, cc: arm-linux-gnueabihf-gcc, qemu: qemu-arm-static }
- {target: aarch64, toolchain: gcc-aarch64-linux-gnu, cc: aarch64-linux-gnu-gcc, qemu: qemu-aarch64-static }
- {target: riscv64, toolchain: gcc-riscv64-linux-gnu, cc: riscv64-linux-gnu-gcc, qemu: qemu-riscv64-static }
- {target: ppc, toolchain: gcc-powerpc-linux-gnu, cc: powerpc-linux-gnu-gcc, qemu: qemu-ppc-static }
- {target: ppc64, toolchain: gcc-powerpc64-linux-gnu, cc: powerpc64-linux-gnu-gcc, qemu: qemu-ppc64-static }
- {target: ppc64le, toolchain: gcc-powerpc64le-linux-gnu, cc: powerpc64le-linux-gnu-gcc, qemu: qemu-ppc64le-static }
- {target: s390x, toolchain: gcc-s390x-linux-gnu, cc: s390x-linux-gnu-gcc, qemu: qemu-s390x-static }
- {target: mips, toolchain: gcc-mips-linux-gnu, cc: mips-linux-gnu-gcc, qemu: qemu-mips-static }
- {target: mips64, toolchain: gcc-mips64-linux-gnuabi64, cc: mips64-linux-gnuabi64-gcc, qemu: qemu-mips64-static }
- {target: mipsel, toolchain: gcc-mipsel-linux-gnu, cc: mipsel-linux-gnu-gcc, qemu: qemu-mipsel-static }
- {target: mips64el,toolchain: gcc-mips64el-linux-gnuabi64, cc: mips64el-linux-gnuabi64-gcc,qemu: qemu-mips64el-static }
- {target: alpha, toolchain: gcc-alpha-linux-gnu, cc: alpha-linux-gnu-gcc, qemu: qemu-alpha-static }
- {target: arm (u64 slots), toolchain: gcc-arm-linux-gnueabi, cc: arm-linux-gnueabi-gcc, qemu: qemu-arm-static}
- {target: aarch64 (u64 slots), toolchain: gcc-aarch64-linux-gnu, cc: aarch64-linux-gnu-gcc, qemu: qemu-aarch64-static}
- {target: ppc (u64 slots), toolchain: gcc-powerpc-linux-gnu, cc: powerpc-linux-gnu-gcc, qemu: qemu-ppc-static}
- {target: ppc64 (u64 slots), toolchain: gcc-powerpc64-linux-gnu, cc: powerpc64-linux-gnu-gcc, qemu: qemu-ppc64-static}
steps:
- uses: actions/checkout@v2
- name: Install QEMU
# this ensure install latest qemu on ubuntu, apt get version is old
env:
QEMU_SRC: "http://archive.ubuntu.com/ubuntu/pool/universe/q/qemu"
QEMU_VER: "qemu-user-static_4\\.2-.*_amd64.deb$"
run: |
DEB=`curl -s $QEMU_SRC/ | grep -o -E 'href="([^"#]+)"' | cut -d'"' -f2 | grep $QEMU_VER | tail -1`
wget $QEMU_SRC/$DEB
sudo dpkg -i $DEB
- name: Install ${{ matrix.config.toolchain }}
run: |
sudo apt update
sudo apt install ${{ matrix.config.toolchain }} -y
- name: Configure with ${{ matrix.config.cc }}
run: |
mkdir build
cd build
cmake .. -DBUILD_TESTING=ON -DQEMU=ON -DCMAKE_C_COMPILER=${{ matrix.config.cc }}
- name: Build
run: |
cmake --build build
ls -lh build
- name: Test
run: |
${{ matrix.config.qemu }} build/uv_run_tests_a

51
deps/libuv/.github/workflows/CI-win.yml vendored Normal file
View File

@ -0,0 +1,51 @@
name: CI-win
on:
pull_request:
paths:
- '**'
- '!docs/**'
- '!src/unix/**'
- '!.**'
- '.github/workflows/CI-win.yml'
push:
branches:
- v[0-9].*
- master
jobs:
build-windows:
runs-on: windows-${{ matrix.config.server }}
name: build-${{ matrix.config.toolchain}}-${{ matrix.config.arch}}
strategy:
fail-fast: false
matrix:
config:
- {toolchain: Visual Studio 16 2019, arch: Win32, server: 2019}
- {toolchain: Visual Studio 16 2019, arch: x64, server: 2019}
- {toolchain: Visual Studio 17 2022, arch: Win32, server: 2022}
- {toolchain: Visual Studio 17 2022, arch: x64, server: 2022}
steps:
- uses: actions/checkout@v2
- name: Envinfo
run: npx envinfo
- name: Build
shell: cmd
run: |
mkdir -p build
cd build
cmake .. -DBUILD_TESTING=ON -G "${{ matrix.config.toolchain }}" -A ${{ matrix.config.arch }}
cmake --build .
- name: platform_output
shell: cmd
run: |
build\\Debug\\uv_run_tests.exe platform_output
- name: platform_output_a
shell: cmd
run: |
build\\Debug\\uv_run_tests_a.exe platform_output
- name: Test
shell: cmd
run: |
cd build
ctest -C Debug -V

View File

@ -1,6 +1,16 @@
name: Sanitizer checks name: Sanitizer checks
on: [push, pull_request] on:
pull_request:
paths:
- '**'
- '!docs/**'
- '!.**'
- '.github/workflows/sanitizer.yml'
push:
branches:
- v[0-9].*
- master
jobs: jobs:
sanitizers: sanitizers:
@ -12,15 +22,20 @@ jobs:
sudo apt-get install ninja-build sudo apt-get install ninja-build
- name: Envinfo - name: Envinfo
run: npx envinfo run: npx envinfo
- name: TSAN - name: TSAN Build
run: | run: |
mkdir build-tsan mkdir build-tsan
(cd build-tsan && cmake .. -G Ninja -DBUILD_TESTING=ON -DTSAN=ON -DCMAKE_BUILD_TYPE=Release) (cd build-tsan && cmake .. -G Ninja -DBUILD_TESTING=ON -DTSAN=ON -DCMAKE_BUILD_TYPE=Release)
cmake --build build-tsan cmake --build build-tsan
./build-tsan/uv_run_tests_a || true # currently permit failures - name: TSAN Test
- name: ASAN continue-on-error: true # currently permit failures
run: |
./build-tsan/uv_run_tests_a
- name: ASAN Build
run: | run: |
mkdir build-asan mkdir build-asan
(cd build-asan && cmake .. -G Ninja -DBUILD_TESTING=ON -DASAN=ON -DCMAKE_BUILD_TYPE=Debug) (cd build-asan && cmake .. -G Ninja -DBUILD_TESTING=ON -DASAN=ON -DCMAKE_BUILD_TYPE=Debug)
cmake --build build-asan cmake --build build-asan
- name: ASAN Test
run: |
./build-asan/uv_run_tests_a ./build-asan/uv_run_tests_a

9
deps/libuv/AUTHORS vendored
View File

@ -508,3 +508,12 @@ Paul Evans <leonerd@leonerd.org.uk>
wyckster <wyckster@hotmail.com> wyckster <wyckster@hotmail.com>
Vittore F. Scolari <vittore.scolari@gmail.com> Vittore F. Scolari <vittore.scolari@gmail.com>
roflcopter4 <15476346+roflcopter4@users.noreply.github.com> roflcopter4 <15476346+roflcopter4@users.noreply.github.com>
V-for-Vasili <vasili.skurydzin@protonmail.com>
Denny C. Dai <dennycd@me.com>
Hannah Shi <hannahshisfb@gmail.com>
tuftedocelot <tuftedocelot@fastmail.fm>
blogdaren <blogdaren@163.com>
chucksilvers <chuq@chuq.com>
Sergey Fedorov <vital.had@gmail.com>
theanarkh <2923878201@qq.com>
Samuel Cabrero <samuelcabrero@gmail.com>

View File

@ -125,6 +125,7 @@ set(uv_sources
src/inet.c src/inet.c
src/random.c src/random.c
src/strscpy.c src/strscpy.c
src/strtok.c
src/threadpool.c src/threadpool.c
src/timer.c src/timer.c
src/uv-common.c src/uv-common.c
@ -269,6 +270,11 @@ if(CMAKE_SYSTEM_NAME STREQUAL "GNU")
src/unix/hurd.c) src/unix/hurd.c)
endif() endif()
if(CMAKE_SYSTEM_NAME STREQUAL "kFreeBSD")
list(APPEND uv_defines _GNU_SOURCE)
list(APPEND uv_libraries dl freebsd-glue)
endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Linux") if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
list(APPEND uv_defines _GNU_SOURCE _POSIX_C_SOURCE=200112) list(APPEND uv_defines _GNU_SOURCE _POSIX_C_SOURCE=200112)
list(APPEND uv_libraries dl rt) list(APPEND uv_libraries dl rt)
@ -458,7 +464,6 @@ if(LIBUV_BUILD_TESTS)
test/test-async-null-cb.c test/test-async-null-cb.c
test/test-async.c test/test-async.c
test/test-barrier.c test/test-barrier.c
test/test-callback-order.c
test/test-callback-stack.c test/test-callback-stack.c
test/test-close-fd.c test/test-close-fd.c
test/test-close-order.c test/test-close-order.c
@ -557,10 +562,12 @@ if(LIBUV_BUILD_TESTS)
test/test-spawn.c test/test-spawn.c
test/test-stdio-over-pipes.c test/test-stdio-over-pipes.c
test/test-strscpy.c test/test-strscpy.c
test/test-strtok.c
test/test-tcp-alloc-cb-fail.c test/test-tcp-alloc-cb-fail.c
test/test-tcp-bind-error.c test/test-tcp-bind-error.c
test/test-tcp-bind6-error.c test/test-tcp-bind6-error.c
test/test-tcp-close-accept.c test/test-tcp-close-accept.c
test/test-tcp-close-after-read-timeout.c
test/test-tcp-close-while-connecting.c test/test-tcp-close-while-connecting.c
test/test-tcp-close.c test/test-tcp-close.c
test/test-tcp-close-reset.c test/test-tcp-close-reset.c
@ -574,6 +581,7 @@ if(LIBUV_BUILD_TESTS)
test/test-tcp-open.c test/test-tcp-open.c
test/test-tcp-read-stop.c test/test-tcp-read-stop.c
test/test-tcp-read-stop-start.c test/test-tcp-read-stop-start.c
test/test-tcp-rst.c
test/test-tcp-shutdown-after-write.c test/test-tcp-shutdown-after-write.c
test/test-tcp-try-write.c test/test-tcp-try-write.c
test/test-tcp-try-write-error.c test/test-tcp-try-write-error.c

130
deps/libuv/ChangeLog vendored
View File

@ -1,4 +1,132 @@
2022.03.09, Version 1.44.1 (Stable) 2022.07.12, Version 1.44.2 (Stable)
Changes since version 1.44.1:
* Add SHA to ChangeLog (Jameson Nash)
* aix, ibmi: handle server hang when remote sends TCP RST (V-for-Vasili)
* build: make CI a bit noisier (Jameson Nash)
* process: reset the signal mask if the fork fails (Jameson Nash)
* zos: implement cmpxchgi() using assembly (Shuowang (Wayne) Zhang)
* build: AC_SUBST for AM_CFLAGS (Claes Nästén)
* ibmi: Implement UDP disconnect (V-for-Vasili)
* doc: update active maintainers list (Ben Noordhuis)
* build: fix kFreeBSD build (James McCoy)
* build: remove Windows 2016 workflows (Darshan Sen)
* Revert "win,errors: remap ERROR_ACCESS_DENIED to UV_EACCES" (Darshan Sen)
* unix: simplify getpwuid call (Jameson Nash)
* build: filter CI by paths and branches (Jameson Nash)
* build: add iOS to macos CI (Jameson Nash)
* build: re-enable CI for windows changes (Jameson Nash)
* process,iOS: fix build breakage in process.c (Denny C. Dai)
* test: remove unused declarations in tcp_rst test (V-for-Vasili)
* core: add thread-safe strtok implementation (Guilherme Íscaro)
* win: fix incompatible-types warning (twosee)
* test: fix flaky file watcher test (Ben Noordhuis)
* build: fix AIX xlc autotools build (V-for-Vasili)
* unix,win: fix UV_RUN_ONCE + uv_idle_stop loop hang (Ben Noordhuis)
* win: fix unexpected ECONNRESET error on TCP socket (twosee)
* doc: make sample cross-platform build (gengjiawen)
* test: separate some static variables by test cases (Hannah Shi)
* sunos: fs-event callback can be called after uv_close() (Andy Fiddaman)
* uv: re-register interest in a file after change (Shuowang (Wayne) Zhang)
* uv: register UV_RENAME event for _RFIM_UNLINK (Shuowang (Wayne) Zhang)
* uv: register __rfim_event 156 as UV_RENAME (Shuowang (Wayne) Zhang)
* doc: remove smartos from supported platforms (Ben Noordhuis)
* macos: avoid posix_spawnp() cwd bug (Jameson Nash)
* release: check versions of autogen scripts are newer (Jameson Nash)
* test: rewrite embed test (Ben Noordhuis)
* openbsd: use utimensat instead of lutimes (tuftedocelot)
* doc: fix link to uvwget example main() function (blogdaren)
* unix: use MSG_CMSG_CLOEXEC where supported (Ben Noordhuis)
* test: remove disabled callback_order test (Ben Noordhuis)
* win,pipe: fix bugs with pipe resource lifetime management (Jameson Nash)
* loop: better align order-of-events behavior between platforms (Jameson Nash)
* aix,test: uv_backend_fd is not supported by poll (V-for-Vasili)
* kqueue: skip EVFILT_PROC when invalidating fds (chucksilvers)
* darwin: fix atomic-ops.h ppc64 build (Sergey Fedorov)
* zos: don't err when killing a zombie process (Shuowang (Wayne) Zhang)
* zos: avoid fs event callbacks after uv_close() (Shuowang (Wayne) Zhang)
* zos: correctly format interface addresses names (Shuowang (Wayne) Zhang)
* zos: add uv_interface_addresses() netmask support (Shuowang (Wayne) Zhang)
* zos: improve memory management of ip addresses (Shuowang (Wayne) Zhang)
* tcp,pipe: fail `bind` or `listen` after `close` (theanarkh)
* zos: implement uv_available_parallelism() (Shuowang (Wayne) Zhang)
* udp,win: fix UDP compiler warning (Jameson Nash)
* zos: fix early exit of epoll_wait() (Shuowang (Wayne) Zhang)
* unix,tcp: fix errno handling in uv__tcp_bind() (Samuel Cabrero)
* shutdown,unix: reduce code duplication (Jameson Nash)
* unix: fix c99 comments (Ben Noordhuis)
* unix: retry tcgetattr/tcsetattr() on EINTR (Ben Noordhuis)
* docs: update introduction.rst (Ikko Ashimine)
* unix,stream: optimize uv_shutdown() codepath (Jameson Nash)
* zos: delay signal handling until after normal i/o (Shuowang (Wayne) Zhang)
* stream: uv__drain() always needs to stop POLLOUT (Jameson Nash)
* unix,tcp: allow EINVAL errno from setsockopt in uv_tcp_close_reset() (Stacey
Marshall)
* win,shutdown: improve how shutdown is dispatched (Jameson Nash)
2022.03.09, Version 1.44.1 (Stable), e8b7eb6908a847ffbe6ab2eec7428e43a0aa53a2
Changes since version 1.44.0: Changes since version 1.44.0:

View File

@ -1,10 +1,7 @@
# Project Maintainers # Project Maintainers
libuv is currently managed by the following individuals: libuv is currently managed by the following individuals:
* **Anna Henningsen** ([@addaleax](https://github.com/addaleax))
* **Bartosz Sosnowski** ([@bzoz](https://github.com/bzoz))
* **Ben Noordhuis** ([@bnoordhuis](https://github.com/bnoordhuis)) * **Ben Noordhuis** ([@bnoordhuis](https://github.com/bnoordhuis))
- GPG key: D77B 1E34 243F BAF0 5F8E 9CC3 4F55 C8C8 46AB 89B9 (pubkey-bnoordhuis) - GPG key: D77B 1E34 243F BAF0 5F8E 9CC3 4F55 C8C8 46AB 89B9 (pubkey-bnoordhuis)
* **Bert Belder** ([@piscisaureus](https://github.com/piscisaureus)) * **Bert Belder** ([@piscisaureus](https://github.com/piscisaureus))
@ -13,13 +10,10 @@ libuv is currently managed by the following individuals:
- GPG key: 5735 3E0D BDAA A7E8 39B6 6A1A FF47 D5E4 AD8B 4FDC (pubkey-cjihrig-kb) - GPG key: 5735 3E0D BDAA A7E8 39B6 6A1A FF47 D5E4 AD8B 4FDC (pubkey-cjihrig-kb)
* **Fedor Indutny** ([@indutny](https://github.com/indutny)) * **Fedor Indutny** ([@indutny](https://github.com/indutny))
- GPG key: AF2E EA41 EC34 47BF DD86 FED9 D706 3CCE 19B7 E890 (pubkey-indutny) - GPG key: AF2E EA41 EC34 47BF DD86 FED9 D706 3CCE 19B7 E890 (pubkey-indutny)
* **Imran Iqbal** ([@imran-iq](https://github.com/imran-iq))
- GPG key: 9DFE AA5F 481B BF77 2D90 03CE D592 4925 2F8E C41A (pubkey-iwuzhere)
* **Jameson Nash** ([@vtjnash](https://github.com/vtjnash)) * **Jameson Nash** ([@vtjnash](https://github.com/vtjnash))
- GPG key: AEAD 0A4B 6867 6775 1A0E 4AEF 34A2 5FB1 2824 6514 (pubkey-vtjnash) - GPG key: AEAD 0A4B 6867 6775 1A0E 4AEF 34A2 5FB1 2824 6514 (pubkey-vtjnash)
- GPG key: CFBB 9CA9 A5BE AFD7 0E2B 3C5A 79A6 7C55 A367 9C8B (pubkey2022-vtjnash) - GPG key: CFBB 9CA9 A5BE AFD7 0E2B 3C5A 79A6 7C55 A367 9C8B (pubkey2022-vtjnash)
* **Jiawen Geng** ([@gengjiawen](https://github.com/gengjiawen)) * **Jiawen Geng** ([@gengjiawen](https://github.com/gengjiawen))
* **John Barboza** ([@jbarz](https://github.com/jbarz))
* **Kaoru Takanashi** ([@erw7](https://github.com/erw7)) * **Kaoru Takanashi** ([@erw7](https://github.com/erw7))
- GPG Key: 5804 F999 8A92 2AFB A398 47A0 7183 5090 6134 887F (pubkey-erw7) - GPG Key: 5804 F999 8A92 2AFB A398 47A0 7183 5090 6134 887F (pubkey-erw7)
* **Richard Lau** ([@richardlau](https://github.com/richardlau)) * **Richard Lau** ([@richardlau](https://github.com/richardlau))
@ -29,6 +23,13 @@ libuv is currently managed by the following individuals:
* **Saúl Ibarra Corretgé** ([@saghul](https://github.com/saghul)) * **Saúl Ibarra Corretgé** ([@saghul](https://github.com/saghul))
- GPG key: FDF5 1936 4458 319F A823 3DC9 410E 5553 AE9B C059 (pubkey-saghul) - GPG key: FDF5 1936 4458 319F A823 3DC9 410E 5553 AE9B C059 (pubkey-saghul)
## Project Maintainers emeriti
* **Anna Henningsen** ([@addaleax](https://github.com/addaleax))
* **Bartosz Sosnowski** ([@bzoz](https://github.com/bzoz))
* **Imran Iqbal** ([@imran-iq](https://github.com/imran-iq))
* **John Barboza** ([@jbarz](https://github.com/jbarz))
## Storing a maintainer key in Git ## Storing a maintainer key in Git
It's quite handy to store a maintainer's signature as a git blob, and have It's quite handy to store a maintainer's signature as a git blob, and have

View File

@ -43,7 +43,9 @@ libuv_la_SOURCES = src/fs-poll.c \
src/uv-data-getter-setters.c \ src/uv-data-getter-setters.c \
src/uv-common.c \ src/uv-common.c \
src/uv-common.h \ src/uv-common.h \
src/version.c src/version.c \
src/strtok.c \
src/strtok.h
if SUNOS if SUNOS
# Can't be turned into a CC_CHECK_CFLAGS in configure.ac, it makes compilers # Can't be turned into a CC_CHECK_CFLAGS in configure.ac, it makes compilers
@ -150,7 +152,6 @@ test_run_tests_SOURCES = test/blackhole-server.c \
test/test-async.c \ test/test-async.c \
test/test-async-null-cb.c \ test/test-async-null-cb.c \
test/test-barrier.c \ test/test-barrier.c \
test/test-callback-order.c \
test/test-callback-stack.c \ test/test-callback-stack.c \
test/test-close-fd.c \ test/test-close-fd.c \
test/test-close-order.c \ test/test-close-order.c \
@ -250,11 +251,13 @@ test_run_tests_SOURCES = test/blackhole-server.c \
test/test-spawn.c \ test/test-spawn.c \
test/test-stdio-over-pipes.c \ test/test-stdio-over-pipes.c \
test/test-strscpy.c \ test/test-strscpy.c \
test/test-strtok.c \
test/test-tcp-alloc-cb-fail.c \ test/test-tcp-alloc-cb-fail.c \
test/test-tcp-bind-error.c \ test/test-tcp-bind-error.c \
test/test-tcp-bind6-error.c \ test/test-tcp-bind6-error.c \
test/test-tcp-close-accept.c \ test/test-tcp-close-accept.c \
test/test-tcp-close-while-connecting.c \ test/test-tcp-close-while-connecting.c \
test/test-tcp-close-after-read-timeout.c \
test/test-tcp-close.c \ test/test-tcp-close.c \
test/test-tcp-close-reset.c \ test/test-tcp-close-reset.c \
test/test-tcp-create-socket-early.c \ test/test-tcp-create-socket-early.c \
@ -266,6 +269,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
test/test-tcp-open.c \ test/test-tcp-open.c \
test/test-tcp-read-stop.c \ test/test-tcp-read-stop.c \
test/test-tcp-read-stop-start.c \ test/test-tcp-read-stop-start.c \
test/test-tcp-rst.c \
test/test-tcp-shutdown-after-write.c \ test/test-tcp-shutdown-after-write.c \
test/test-tcp-unexpected-read.c \ test/test-tcp-unexpected-read.c \
test/test-tcp-oob.c \ test/test-tcp-oob.c \
@ -463,6 +467,10 @@ libuv_la_SOURCES += src/unix/bsd-ifaddrs.c \
src/unix/hurd.c src/unix/hurd.c
endif endif
if KFREEBSD
libuv_la_CFLAGS += -D_GNU_SOURCE
endif
if LINUX if LINUX
uvinclude_HEADERS += include/uv/linux.h uvinclude_HEADERS += include/uv/linux.h
libuv_la_CFLAGS += -D_GNU_SOURCE libuv_la_CFLAGS += -D_GNU_SOURCE

View File

@ -10,7 +10,6 @@
| IBM i | Tier 2 | >= IBM i 7.2 | Maintainers: @libuv/ibmi | | IBM i | Tier 2 | >= IBM i 7.2 | Maintainers: @libuv/ibmi |
| z/OS | Tier 2 | >= V2R2 | Maintainers: @libuv/zos | | z/OS | Tier 2 | >= V2R2 | Maintainers: @libuv/zos |
| Linux with musl | Tier 2 | musl >= 1.0 | | | Linux with musl | Tier 2 | musl >= 1.0 | |
| SmartOS | Tier 3 | >= 14.4 | |
| Android | Tier 3 | NDK >= r15b | Android 7.0, `-DANDROID_PLATFORM=android-24` | | Android | Tier 3 | NDK >= r15b | Android 7.0, `-DANDROID_PLATFORM=android-24` |
| MinGW | Tier 3 | MinGW32 and MinGW-w64 | | | MinGW | Tier 3 | MinGW32 and MinGW-w64 | |
| SunOS | Tier 3 | Solaris 121 and later | | | SunOS | Tier 3 | Solaris 121 and later | |

35
deps/libuv/autogen.sh vendored
View File

@ -14,9 +14,16 @@
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
set -eu
cd `dirname "$0"` cd `dirname "$0"`
if [ "$LIBTOOLIZE" = "" ] && [ "`uname`" = "Darwin" ]; then if [ "${1:-dev}" == "release" ]; then
export LIBUV_RELEASE=true
else
export LIBUV_RELEASE=false
fi
if [ "${LIBTOOLIZE:-}" = "" ] && [ "`uname`" = "Darwin" ]; then
LIBTOOLIZE=glibtoolize LIBTOOLIZE=glibtoolize
fi fi
@ -25,9 +32,17 @@ AUTOCONF=${AUTOCONF:-autoconf}
AUTOMAKE=${AUTOMAKE:-automake} AUTOMAKE=${AUTOMAKE:-automake}
LIBTOOLIZE=${LIBTOOLIZE:-libtoolize} LIBTOOLIZE=${LIBTOOLIZE:-libtoolize}
aclocal_version=`"$ACLOCAL" --version | head -n 1 | sed 's/[^.0-9]//g'`
autoconf_version=`"$AUTOCONF" --version | head -n 1 | sed 's/[^.0-9]//g'`
automake_version=`"$AUTOMAKE" --version | head -n 1 | sed 's/[^.0-9]//g'` automake_version=`"$AUTOMAKE" --version | head -n 1 | sed 's/[^.0-9]//g'`
automake_version_major=`echo "$automake_version" | cut -d. -f1` automake_version_major=`echo "$automake_version" | cut -d. -f1`
automake_version_minor=`echo "$automake_version" | cut -d. -f2` automake_version_minor=`echo "$automake_version" | cut -d. -f2`
libtoolize_version=`"$LIBTOOLIZE" --version | head -n 1 | sed 's/[^.0-9]//g'`
if [ $aclocal_version != $automake_version ]; then
echo "FATAL: aclocal version appears not to be from the same as automake"
exit 1
fi
UV_EXTRA_AUTOMAKE_FLAGS= UV_EXTRA_AUTOMAKE_FLAGS=
if test "$automake_version_major" -gt 1 || \ if test "$automake_version_major" -gt 1 || \
@ -39,8 +54,22 @@ fi
echo "m4_define([UV_EXTRA_AUTOMAKE_FLAGS], [$UV_EXTRA_AUTOMAKE_FLAGS])" \ echo "m4_define([UV_EXTRA_AUTOMAKE_FLAGS], [$UV_EXTRA_AUTOMAKE_FLAGS])" \
> m4/libuv-extra-automake-flags.m4 > m4/libuv-extra-automake-flags.m4
set -ex (set -x
"$LIBTOOLIZE" --copy "$LIBTOOLIZE" --copy --force
"$ACLOCAL" -I m4 "$ACLOCAL" -I m4
)
if $LIBUV_RELEASE; then
"$AUTOCONF" -o /dev/null m4/libuv-check-versions.m4
echo "
AC_PREREQ($autoconf_version)
AC_INIT([libuv-release-check], [0.0])
AM_INIT_AUTOMAKE([$automake_version])
LT_PREREQ($libtoolize_version)
AC_OUTPUT
" > m4/libuv-check-versions.m4
fi
(
set -x
"$AUTOCONF" "$AUTOCONF"
"$AUTOMAKE" --add-missing --copy "$AUTOMAKE" --add-missing --copy
)

View File

@ -13,7 +13,7 @@
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
AC_PREREQ(2.57) AC_PREREQ(2.57)
AC_INIT([libuv], [1.44.1], [https://github.com/libuv/libuv/issues]) AC_INIT([libuv], [1.44.2], [https://github.com/libuv/libuv/issues])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
m4_include([m4/libuv-extra-automake-flags.m4]) m4_include([m4/libuv-extra-automake-flags.m4])
m4_include([m4/as_case.m4]) m4_include([m4/as_case.m4])
@ -28,7 +28,9 @@ AM_PROG_CC_C_O
CC_ATTRIBUTE_VISIBILITY([default], [ CC_ATTRIBUTE_VISIBILITY([default], [
CC_FLAG_VISIBILITY([CFLAGS="${CFLAGS} -fvisibility=hidden"]) CC_FLAG_VISIBILITY([CFLAGS="${CFLAGS} -fvisibility=hidden"])
]) ])
CC_CHECK_CFLAGS_APPEND([-fno-strict-aliasing]) # Xlc has a flag "-f<filename>". Need to use CC_CHECK_FLAG_SUPPORTED_APPEND so
# we exclude -fno-strict-aliasing for xlc
CC_CHECK_FLAG_SUPPORTED_APPEND([-fno-strict-aliasing])
CC_CHECK_CFLAGS_APPEND([-g]) CC_CHECK_CFLAGS_APPEND([-g])
CC_CHECK_CFLAGS_APPEND([-std=gnu89]) CC_CHECK_CFLAGS_APPEND([-std=gnu89])
CC_CHECK_CFLAGS_APPEND([-Wall]) CC_CHECK_CFLAGS_APPEND([-Wall])
@ -60,6 +62,7 @@ AM_CONDITIONAL([CYGWIN], [AS_CASE([$host_os],[cygwin*], [true], [false])
AM_CONDITIONAL([DARWIN], [AS_CASE([$host_os],[darwin*], [true], [false])]) AM_CONDITIONAL([DARWIN], [AS_CASE([$host_os],[darwin*], [true], [false])])
AM_CONDITIONAL([DRAGONFLY],[AS_CASE([$host_os],[dragonfly*], [true], [false])]) AM_CONDITIONAL([DRAGONFLY],[AS_CASE([$host_os],[dragonfly*], [true], [false])])
AM_CONDITIONAL([FREEBSD], [AS_CASE([$host_os],[*freebsd*], [true], [false])]) AM_CONDITIONAL([FREEBSD], [AS_CASE([$host_os],[*freebsd*], [true], [false])])
AM_CONDITIONAL([KFREEBSD], [AS_CASE([$host_os],[kfreebsd*], [true], [false])])
AM_CONDITIONAL([HAIKU], [AS_CASE([$host_os],[haiku], [true], [false])]) AM_CONDITIONAL([HAIKU], [AS_CASE([$host_os],[haiku], [true], [false])])
AM_CONDITIONAL([HURD], [AS_CASE([$host_os],[gnu*], [true], [false])]) AM_CONDITIONAL([HURD], [AS_CASE([$host_os],[gnu*], [true], [false])])
AM_CONDITIONAL([LINUX], [AS_CASE([$host_os],[linux*], [true], [false])]) AM_CONDITIONAL([LINUX], [AS_CASE([$host_os],[linux*], [true], [false])])

51
deps/libuv/docs/code/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,51 @@
cmake_minimum_required(VERSION 3.5)
project(libuv_sample)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_subdirectory("../../" build)
set(SIMPLE_SAMPLES
cgi
helloworld
dns
detach
default-loop
idle-basic
idle-compute
interfaces
locks
onchange
pipe-echo-server
ref-timer
spawn
tcp-echo-server
thread-create
udp-dhcp
uvcat
uvstop
uvtee
)
IF (NOT WIN32)
list(APPEND SIMPLE_SAMPLES
signal
progress
queue-cancel
queue-work
tty
tty-gravity
)
ENDIF()
foreach (X IN LISTS SIMPLE_SAMPLES)
add_executable(${X} ${X}/main.c)
target_link_libraries(${X} uv_a)
endforeach ()
FIND_PACKAGE(CURL)
IF(CURL_FOUND)
add_executable(uvwget uvwget/main.c)
target_link_libraries(uvwget uv_a ${CURL_LIBRARIES})
ENDIF(CURL_FOUND)

View File

@ -15,8 +15,8 @@ void cleanup_handles(uv_process_t *req, int64_t exit_status, int term_signal) {
} }
void invoke_cgi_script(uv_tcp_t *client) { void invoke_cgi_script(uv_tcp_t *client) {
size_t size = 500; char path[500];
char path[size]; size_t size = sizeof(path);
uv_exepath(path, &size); uv_exepath(path, &size);
strcpy(path + (strlen(path) - strlen("cgi")), "tick"); strcpy(path + (strlen(path) - strlen("cgi")), "tick");

View File

@ -11,17 +11,17 @@ int main() {
printf("Number of interfaces: %d\n", count); printf("Number of interfaces: %d\n", count);
while (i--) { while (i--) {
uv_interface_address_t interface = info[i]; uv_interface_address_t interface_a = info[i];
printf("Name: %s\n", interface.name); printf("Name: %s\n", interface_a.name);
printf("Internal? %s\n", interface.is_internal ? "Yes" : "No"); printf("Internal? %s\n", interface_a.is_internal ? "Yes" : "No");
if (interface.address.address4.sin_family == AF_INET) { if (interface_a.address.address4.sin_family == AF_INET) {
uv_ip4_name(&interface.address.address4, buf, sizeof(buf)); uv_ip4_name(&interface_a.address.address4, buf, sizeof(buf));
printf("IPv4 address: %s\n", buf); printf("IPv4 address: %s\n", buf);
} }
else if (interface.address.address4.sin_family == AF_INET6) { else if (interface_a.address.address4.sin_family == AF_INET6) {
uv_ip6_name(&interface.address.address6, buf, sizeof(buf)); uv_ip6_name(&interface_a.address.address6, buf, sizeof(buf));
printf("IPv6 address: %s\n", buf); printf("IPv6 address: %s\n", buf);
} }

View File

@ -1,5 +1,4 @@
#include <stdio.h> #include <stdio.h>
#include <unistd.h>
#include <uv.h> #include <uv.h>
@ -7,7 +6,7 @@ void hare(void *arg) {
int tracklen = *((int *) arg); int tracklen = *((int *) arg);
while (tracklen) { while (tracklen) {
tracklen--; tracklen--;
sleep(1); uv_sleep(1000);
fprintf(stderr, "Hare ran another step\n"); fprintf(stderr, "Hare ran another step\n");
} }
fprintf(stderr, "Hare done running!\n"); fprintf(stderr, "Hare done running!\n");
@ -18,7 +17,7 @@ void tortoise(void *arg) {
while (tracklen) { while (tracklen) {
tracklen--; tracklen--;
fprintf(stderr, "Tortoise ran another step\n"); fprintf(stderr, "Tortoise ran another step\n");
sleep(3); uv_sleep(3000);
} }
fprintf(stderr, "Tortoise done running!\n"); fprintf(stderr, "Tortoise done running!\n");
} }

View File

@ -53,7 +53,8 @@ uv_buf_t make_discover_msg() {
// HOPS // HOPS
buffer.base[3] = 0x0; buffer.base[3] = 0x0;
// XID 4 bytes // XID 4 bytes
buffer.base[4] = (unsigned int) random(); if (uv_random(NULL, NULL, &buffer.base[4], 4, 0, NULL))
abort();
// SECS // SECS
buffer.base[8] = 0x0; buffer.base[8] = 0x0;
// FLAGS // FLAGS

View File

@ -1,7 +1,6 @@
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h>
#include <uv.h> #include <uv.h>
void on_read(uv_fs_t *req); void on_read(uv_fs_t *req);

View File

@ -1,6 +1,5 @@
#include <stdio.h> #include <stdio.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>

View File

@ -53,7 +53,7 @@ Code
---- ----
All the example code and the source of the book is included as part of All the example code and the source of the book is included as part of
the libuv_ project on Github. the libuv_ project on GitHub.
Clone or Download libuv_, then build it:: Clone or Download libuv_, then build it::
sh autogen.sh sh autogen.sh

View File

@ -220,7 +220,7 @@ progress with the download whenever libuv notifies of I/O readiness.
.. literalinclude:: ../../code/uvwget/main.c .. literalinclude:: ../../code/uvwget/main.c
:language: c :language: c
:linenos: :linenos:
:lines: 1-9,140- :lines: 1-9,142-
:emphasize-lines: 7,21,24-25 :emphasize-lines: 7,21,24-25
The way each library is integrated with libuv will vary. In the case of The way each library is integrated with libuv will vary. In the case of

View File

@ -32,7 +32,7 @@
#define UV_VERSION_MAJOR 1 #define UV_VERSION_MAJOR 1
#define UV_VERSION_MINOR 44 #define UV_VERSION_MINOR 44
#define UV_VERSION_PATCH 1 #define UV_VERSION_PATCH 2
#define UV_VERSION_IS_RELEASE 1 #define UV_VERSION_IS_RELEASE 1
#define UV_VERSION_SUFFIX "" #define UV_VERSION_SUFFIX ""

View File

@ -377,6 +377,12 @@ typedef struct {
OVERLAPPED overlapped; \ OVERLAPPED overlapped; \
size_t queued_bytes; \ size_t queued_bytes; \
} io; \ } io; \
/* in v2, we can move these to the UV_CONNECT_PRIVATE_FIELDS */ \
struct { \
ULONG_PTR result; /* overlapped.Internal is reused to hold the result */\
HANDLE pipeHandle; \
DWORD duplex_flags; \
} connect; \
} u; \ } u; \
struct uv_req_s* next_req; struct uv_req_s* next_req;

View File

@ -73,6 +73,8 @@ AC_DEFUN([CC_CHECK_CFLAG_APPEND], [
AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes], AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes],
[AM_CFLAGS="$AM_CFLAGS $1"; DEBUG_CFLAGS="$DEBUG_CFLAGS $1"; $2], [$3]) [AM_CFLAGS="$AM_CFLAGS $1"; DEBUG_CFLAGS="$DEBUG_CFLAGS $1"; $2], [$3])
AC_SUBST([AM_CFLAGS])
]) ])
dnl CC_CHECK_CFLAGS_APPEND([FLAG1 FLAG2], [action-if-found], [action-if-not]) dnl CC_CHECK_CFLAGS_APPEND([FLAG1 FLAG2], [action-if-found], [action-if-not])
@ -102,6 +104,20 @@ AC_DEFUN([CC_CHECK_LDFLAGS], [
[$2], [$3]) [$2], [$3])
]) ])
dnl Check if flag is supported by both compiler and linker
dnl If so, append it to AM_CFLAGS
dnl CC_CHECK_FLAG_SUPPORTED_APPEND([FLAG])
AC_DEFUN([CC_CHECK_FLAG_SUPPORTED_APPEND], [
CC_CHECK_CFLAGS([$1],
[CC_CHECK_LDFLAGS([$1],
[AM_CFLAGS="$AM_CFLAGS $1";
DEBUG_CFLAGS="$DEBUG_CFLAGS $1";
AC_SUBST([AM_CFLAGS])
])
])
])
dnl define the LDFLAGS_NOUNDEFINED variable with the correct value for dnl define the LDFLAGS_NOUNDEFINED variable with the correct value for
dnl the current linker to avoid undefined references in a shared object. dnl the current linker to avoid undefined references in a shared object.
AC_DEFUN([CC_NOUNDEFINED], [ AC_DEFUN([CC_NOUNDEFINED], [

7
deps/libuv/m4/libuv-check-versions.m4 vendored Normal file
View File

@ -0,0 +1,7 @@
AC_PREREQ(2.71)
AC_INIT([libuv-release-check], [0.0])
AM_INIT_AUTOMAKE([1.16.5])
LT_PREREQ(2.4.7)
AC_OUTPUT

52
deps/libuv/src/strtok.c vendored Normal file
View File

@ -0,0 +1,52 @@
/* 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 <stdlib.h>
#include "strtok.h"
char* uv__strtok(char* str, const char* sep, char** itr) {
const char* sep_itr;
char* tmp;
char* start;
if (str == NULL)
start = tmp = *itr;
else
start = tmp = str;
if (tmp == NULL)
return NULL;
while (*tmp != '\0') {
sep_itr = sep;
while (*sep_itr != '\0') {
if (*tmp == *sep_itr) {
*itr = tmp + 1;
*tmp = '\0';
return start;
}
sep_itr++;
}
tmp++;
}
*itr = NULL;
return start;
}

27
deps/libuv/src/strtok.h vendored Normal file
View File

@ -0,0 +1,27 @@
/* Copyright libuv project contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef UV_STRTOK_H_
#define UV_STRTOK_H_
char* uv__strtok(char* str, const char* sep, char** itr);
#endif /* UV_STRTOK_H_ */

View File

@ -37,12 +37,11 @@ UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) {
: "memory"); : "memory");
return out; return out;
#elif defined(__MVS__) #elif defined(__MVS__)
unsigned int op4; /* Use hand-rolled assembly because codegen from builtin __plo_CSST results in
if (__plo_CSST(ptr, (unsigned int*) &oldval, newval, * a runtime bug.
(unsigned int*) ptr, *ptr, &op4)) */
return oldval; __asm(" cs %0,%2,%1 \n " : "+r"(oldval), "+m"(*ptr) : "r"(newval) :);
else return oldval;
return op4;
#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) #elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
return atomic_cas_uint((uint_t *)ptr, (uint_t)oldval, (uint_t)newval); return atomic_cas_uint((uint_t *)ptr, (uint_t)oldval, (uint_t)newval);
#else #else
@ -55,7 +54,9 @@ UV_UNUSED(static void cpu_relax(void)) {
__asm__ __volatile__ ("rep; nop" ::: "memory"); /* a.k.a. PAUSE */ __asm__ __volatile__ ("rep; nop" ::: "memory"); /* a.k.a. PAUSE */
#elif (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__) #elif (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__)
__asm__ __volatile__ ("yield" ::: "memory"); __asm__ __volatile__ ("yield" ::: "memory");
#elif defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) #elif (defined(__ppc__) || defined(__ppc64__)) && defined(__APPLE__)
__asm volatile ("" : : : "memory");
#elif !defined(__APPLE__) && (defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__))
__asm__ __volatile__ ("or 1,1,1; or 2,2,2" ::: "memory"); __asm__ __volatile__ ("or 1,1,1; or 2,2,2" ::: "memory");
#endif #endif
} }

View File

@ -20,6 +20,7 @@
#include "uv.h" #include "uv.h"
#include "internal.h" #include "internal.h"
#include "strtok.h"
#include <stddef.h> /* NULL */ #include <stddef.h> /* NULL */
#include <stdio.h> /* printf */ #include <stdio.h> /* printf */
@ -80,7 +81,8 @@ extern char** environ;
#endif #endif
#if defined(__MVS__) #if defined(__MVS__)
#include <sys/ioctl.h> # include <sys/ioctl.h>
# include "zos-sys-info.h"
#endif #endif
#if defined(__linux__) #if defined(__linux__)
@ -93,7 +95,7 @@ extern char** environ;
# include <sanitizer/linux_syscall_hooks.h> # include <sanitizer/linux_syscall_hooks.h>
#endif #endif
static int uv__run_pending(uv_loop_t* loop); static void uv__run_pending(uv_loop_t* loop);
/* Verify that uv_buf_t is ABI-compatible with struct iovec. */ /* Verify that uv_buf_t is ABI-compatible with struct iovec. */
STATIC_ASSERT(sizeof(uv_buf_t) == sizeof(struct iovec)); STATIC_ASSERT(sizeof(uv_buf_t) == sizeof(struct iovec));
@ -159,6 +161,15 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) {
case UV_FS_EVENT: case UV_FS_EVENT:
uv__fs_event_close((uv_fs_event_t*)handle); uv__fs_event_close((uv_fs_event_t*)handle);
#if defined(__sun) || defined(__MVS__)
/*
* On Solaris, illumos, and z/OS we will not be able to dissociate the
* watcher for an event which is pending delivery, so we cannot always call
* uv__make_close_pending() straight away. The backend will call the
* function once the event has cleared.
*/
return;
#endif
break; break;
case UV_POLL: case UV_POLL:
@ -371,7 +382,7 @@ int uv_loop_alive(const uv_loop_t* loop) {
int uv_run(uv_loop_t* loop, uv_run_mode mode) { int uv_run(uv_loop_t* loop, uv_run_mode mode) {
int timeout; int timeout;
int r; int r;
int ran_pending; int can_sleep;
r = uv__loop_alive(loop); r = uv__loop_alive(loop);
if (!r) if (!r)
@ -380,16 +391,25 @@ int uv_run(uv_loop_t* loop, uv_run_mode mode) {
while (r != 0 && loop->stop_flag == 0) { while (r != 0 && loop->stop_flag == 0) {
uv__update_time(loop); uv__update_time(loop);
uv__run_timers(loop); uv__run_timers(loop);
ran_pending = uv__run_pending(loop);
can_sleep =
QUEUE_EMPTY(&loop->pending_queue) && QUEUE_EMPTY(&loop->idle_handles);
uv__run_pending(loop);
uv__run_idle(loop); uv__run_idle(loop);
uv__run_prepare(loop); uv__run_prepare(loop);
timeout = 0; timeout = 0;
if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT) if ((mode == UV_RUN_ONCE && can_sleep) || mode == UV_RUN_DEFAULT)
timeout = uv__backend_timeout(loop); timeout = uv__backend_timeout(loop);
uv__io_poll(loop, timeout); uv__io_poll(loop, timeout);
/* Process immediate callbacks (e.g. write_cb) a small fixed number of
* times to avoid loop starvation.*/
for (r = 0; r < 8 && !QUEUE_EMPTY(&loop->pending_queue); r++)
uv__run_pending(loop);
/* Run one final update on the provider_idle_time in case uv__io_poll /* Run one final update on the provider_idle_time in case uv__io_poll
* returned because the timeout expired, but no events were received. This * returned because the timeout expired, but no events were received. This
* call will be ignored if the provider_entry_time was either never set (if * call will be ignored if the provider_entry_time was either never set (if
@ -653,28 +673,23 @@ int uv__cloexec(int fd, int set) {
ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) { ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) {
struct cmsghdr* cmsg; #if defined(__ANDROID__) || \
defined(__DragonFly__) || \
defined(__FreeBSD__) || \
defined(__NetBSD__) || \
defined(__OpenBSD__) || \
defined(__linux__)
ssize_t rc; ssize_t rc;
rc = recvmsg(fd, msg, flags | MSG_CMSG_CLOEXEC);
if (rc == -1)
return UV__ERR(errno);
return rc;
#else
struct cmsghdr* cmsg;
int* pfd; int* pfd;
int* end; int* end;
#if defined(__linux__) ssize_t rc;
static int no_msg_cmsg_cloexec;
if (0 == uv__load_relaxed(&no_msg_cmsg_cloexec)) {
rc = recvmsg(fd, msg, flags | 0x40000000); /* MSG_CMSG_CLOEXEC */
if (rc != -1)
return rc;
if (errno != EINVAL)
return UV__ERR(errno);
rc = recvmsg(fd, msg, flags);
if (rc == -1)
return UV__ERR(errno);
uv__store_relaxed(&no_msg_cmsg_cloexec, 1);
} else {
rc = recvmsg(fd, msg, flags);
}
#else
rc = recvmsg(fd, msg, flags); rc = recvmsg(fd, msg, flags);
#endif
if (rc == -1) if (rc == -1)
return UV__ERR(errno); return UV__ERR(errno);
if (msg->msg_controllen == 0) if (msg->msg_controllen == 0)
@ -687,6 +702,7 @@ ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) {
pfd += 1) pfd += 1)
uv__cloexec(*pfd, 1); uv__cloexec(*pfd, 1);
return rc; return rc;
#endif
} }
@ -779,14 +795,11 @@ int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd) {
} }
static int uv__run_pending(uv_loop_t* loop) { static void uv__run_pending(uv_loop_t* loop) {
QUEUE* q; QUEUE* q;
QUEUE pq; QUEUE pq;
uv__io_t* w; uv__io_t* w;
if (QUEUE_EMPTY(&loop->pending_queue))
return 0;
QUEUE_MOVE(&loop->pending_queue, &pq); QUEUE_MOVE(&loop->pending_queue, &pq);
while (!QUEUE_EMPTY(&pq)) { while (!QUEUE_EMPTY(&pq)) {
@ -796,8 +809,6 @@ static int uv__run_pending(uv_loop_t* loop) {
w = QUEUE_DATA(q, uv__io_t, pending_queue); w = QUEUE_DATA(q, uv__io_t, pending_queue);
w->cb(loop, w, POLLOUT); w->cb(loop, w, POLLOUT);
} }
return 1;
} }
@ -1162,24 +1173,17 @@ int uv__getpwuid_r(uv_passwd_t* pwd) {
size_t name_size; size_t name_size;
size_t homedir_size; size_t homedir_size;
size_t shell_size; size_t shell_size;
long initsize;
int r; int r;
if (pwd == NULL) if (pwd == NULL)
return UV_EINVAL; return UV_EINVAL;
initsize = sysconf(_SC_GETPW_R_SIZE_MAX);
if (initsize <= 0)
bufsize = 4096;
else
bufsize = (size_t) initsize;
uid = geteuid(); uid = geteuid();
buf = NULL;
for (;;) { /* Calling sysconf(_SC_GETPW_R_SIZE_MAX) would get the suggested size, but it
uv__free(buf); * is frequently 1024 or 4096, so we can just use that directly. The pwent
* will not usually be large. */
for (bufsize = 2000;; bufsize *= 2) {
buf = uv__malloc(bufsize); buf = uv__malloc(bufsize);
if (buf == NULL) if (buf == NULL)
@ -1189,21 +1193,18 @@ int uv__getpwuid_r(uv_passwd_t* pwd) {
r = getpwuid_r(uid, &pw, buf, bufsize, &result); r = getpwuid_r(uid, &pw, buf, bufsize, &result);
while (r == EINTR); while (r == EINTR);
if (r != 0 || result == NULL)
uv__free(buf);
if (r != ERANGE) if (r != ERANGE)
break; break;
bufsize *= 2;
} }
if (r != 0) { if (r != 0)
uv__free(buf);
return UV__ERR(r); return UV__ERR(r);
}
if (result == NULL) { if (result == NULL)
uv__free(buf);
return UV_ENOENT; return UV_ENOENT;
}
/* Allocate memory for the username, shell, and home directory */ /* Allocate memory for the username, shell, and home directory */
name_size = strlen(pw.pw_name) + 1; name_size = strlen(pw.pw_name) + 1;
@ -1556,6 +1557,7 @@ int uv__search_path(const char* prog, char* buf, size_t* buflen) {
char* cloned_path; char* cloned_path;
char* path_env; char* path_env;
char* token; char* token;
char* itr;
if (buf == NULL || buflen == NULL || *buflen == 0) if (buf == NULL || buflen == NULL || *buflen == 0)
return UV_EINVAL; return UV_EINVAL;
@ -1597,7 +1599,7 @@ int uv__search_path(const char* prog, char* buf, size_t* buflen) {
if (cloned_path == NULL) if (cloned_path == NULL)
return UV_ENOMEM; return UV_ENOMEM;
token = strtok(cloned_path, ":"); token = uv__strtok(cloned_path, ":", &itr);
while (token != NULL) { while (token != NULL) {
snprintf(trypath, sizeof(trypath) - 1, "%s/%s", token, prog); snprintf(trypath, sizeof(trypath) - 1, "%s/%s", token, prog);
if (realpath(trypath, abspath) == abspath) { if (realpath(trypath, abspath) == abspath) {
@ -1616,7 +1618,7 @@ int uv__search_path(const char* prog, char* buf, size_t* buflen) {
return 0; return 0;
} }
} }
token = strtok(NULL, ":"); token = uv__strtok(NULL, ":", &itr);
} }
uv__free(cloned_path); uv__free(cloned_path);
@ -1646,7 +1648,13 @@ unsigned int uv_available_parallelism(void) {
return (unsigned) rc; return (unsigned) rc;
#elif defined(__MVS__) #elif defined(__MVS__)
return 1; /* TODO(bnoordhuis) Read from CSD_NUMBER_ONLINE_CPUS? */ int rc;
rc = __get_num_online_cpus();
if (rc < 1)
rc = 1;
return (unsigned) rc;
#else /* __linux__ */ #else /* __linux__ */
long rc; long rc;

View File

@ -1181,7 +1181,8 @@ static ssize_t uv__fs_lutime(uv_fs_t* req) {
defined(_AIX71) || \ defined(_AIX71) || \
defined(__sun) || \ defined(__sun) || \
defined(__HAIKU__) || \ defined(__HAIKU__) || \
defined(__GNU__) defined(__GNU__) || \
defined(__OpenBSD__)
struct timespec ts[2]; struct timespec ts[2];
ts[0] = uv__fs_to_timespec(req->atime); ts[0] = uv__fs_to_timespec(req->atime);
ts[1] = uv__fs_to_timespec(req->mtime); ts[1] = uv__fs_to_timespec(req->mtime);

View File

@ -277,7 +277,6 @@ void uv__tcp_close(uv_tcp_t* handle);
size_t uv__thread_stack_size(void); size_t uv__thread_stack_size(void);
void uv__udp_close(uv_udp_t* handle); void uv__udp_close(uv_udp_t* handle);
void uv__udp_finish_close(uv_udp_t* handle); void uv__udp_finish_close(uv_udp_t* handle);
uv_handle_type uv__handle_type(int fd);
FILE* uv__open_file(const char* path); FILE* uv__open_file(const char* path);
int uv__getpwuid_r(uv_passwd_t* pwd); int uv__getpwuid_r(uv_passwd_t* pwd);
int uv__search_path(const char* prog, char* buf, size_t* buflen); int uv__search_path(const char* prog, char* buf, size_t* buflen);

View File

@ -456,7 +456,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
/* Invalidate events with same file descriptor */ /* Invalidate events with same file descriptor */
for (i = 0; i < nfds; i++) for (i = 0; i < nfds; i++)
if ((int) events[i].ident == fd) if ((int) events[i].ident == fd && events[i].filter != EVFILT_PROC)
events[i].ident = -1; events[i].ident = -1;
} }

View File

@ -284,6 +284,8 @@ int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events,
nmsgsfds_t size; nmsgsfds_t size;
struct pollfd* pfds; struct pollfd* pfds;
int pollret; int pollret;
int pollfdret;
int pollmsgret;
int reventcount; int reventcount;
int nevents; int nevents;
struct pollfd msg_fd; struct pollfd msg_fd;
@ -304,24 +306,24 @@ int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events,
return -1; return -1;
} }
if (lst->size > 0) assert(lst->size > 0);
_SET_FDS_MSGS(size, 1, lst->size - 1); _SET_FDS_MSGS(size, 1, lst->size - 1);
else
_SET_FDS_MSGS(size, 0, 0);
pfds = lst->items; pfds = lst->items;
pollret = poll(pfds, size, timeout); pollret = poll(pfds, size, timeout);
if (pollret <= 0) if (pollret <= 0)
return pollret; return pollret;
assert(lst->size > 0); pollfdret = _NFDS(pollret);
pollmsgret = _NMSGS(pollret);
pollret = _NFDS(pollret) + _NMSGS(pollret);
reventcount = 0; reventcount = 0;
nevents = 0; nevents = 0;
msg_fd = pfds[lst->size - 1]; msg_fd = pfds[lst->size - 1]; /* message queue is always last entry */
maxevents = maxevents - pollmsgret; /* allow spot for message queue */
for (i = 0; for (i = 0;
i < lst->size && i < maxevents && reventcount < pollret; ++i) { i < lst->size - 1 &&
nevents < maxevents &&
reventcount < pollfdret; ++i) {
struct epoll_event ev; struct epoll_event ev;
struct pollfd* pfd; struct pollfd* pfd;
@ -332,18 +334,18 @@ int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events,
ev.fd = pfd->fd; ev.fd = pfd->fd;
ev.events = pfd->revents; ev.events = pfd->revents;
ev.is_msg = 0; ev.is_msg = 0;
if (pfd->revents & POLLIN && pfd->revents & POLLOUT)
reventcount += 2;
else if (pfd->revents & (POLLIN | POLLOUT))
++reventcount;
pfd->revents = 0; reventcount++;
events[nevents++] = ev; events[nevents++] = ev;
} }
if (msg_fd.revents != 0 && msg_fd.fd != -1) if (pollmsgret > 0 && msg_fd.revents != 0 && msg_fd.fd != -1) {
if (i == lst->size) struct epoll_event ev;
events[nevents - 1].is_msg = 1; ev.fd = msg_fd.fd;
ev.events = msg_fd.revents;
ev.is_msg = 1;
events[nevents++] = ev;
}
return nevents; return nevents;
} }

View File

@ -278,7 +278,9 @@ static int uv__interface_addresses_v6(uv_interface_address_t** addresses,
__net_ifconf6header_t ifc; __net_ifconf6header_t ifc;
__net_ifconf6entry_t* ifr; __net_ifconf6entry_t* ifr;
__net_ifconf6entry_t* p; __net_ifconf6entry_t* p;
__net_ifconf6entry_t flg; unsigned int i;
int count_names;
unsigned char netmask[16] = {0};
*count = 0; *count = 0;
/* Assume maximum buffer size allowable */ /* Assume maximum buffer size allowable */
@ -287,24 +289,33 @@ static int uv__interface_addresses_v6(uv_interface_address_t** addresses,
if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)))
return UV__ERR(errno); return UV__ERR(errno);
ifc.__nif6h_version = 1; ifc.__nif6h_buffer = uv__calloc(1, maxsize);
ifc.__nif6h_buflen = maxsize;
ifc.__nif6h_buffer = uv__calloc(1, maxsize);;
if (ioctl(sockfd, SIOCGIFCONF6, &ifc) == -1) { if (ifc.__nif6h_buffer == NULL) {
uv__close(sockfd); uv__close(sockfd);
return UV__ERR(errno); return UV_ENOMEM;
} }
ifc.__nif6h_version = 1;
ifc.__nif6h_buflen = maxsize;
if (ioctl(sockfd, SIOCGIFCONF6, &ifc) == -1) {
/* This will error on a system that does not support IPv6. However, we want
* to treat this as there being 0 interfaces so we can continue to get IPv4
* interfaces in uv_interface_addresses(). So return 0 instead of the error.
*/
uv__free(ifc.__nif6h_buffer);
uv__close(sockfd);
errno = 0;
return 0;
}
*count = 0;
ifr = (__net_ifconf6entry_t*)(ifc.__nif6h_buffer); ifr = (__net_ifconf6entry_t*)(ifc.__nif6h_buffer);
while ((char*)ifr < (char*)ifc.__nif6h_buffer + ifc.__nif6h_buflen) { while ((char*)ifr < (char*)ifc.__nif6h_buffer + ifc.__nif6h_buflen) {
p = ifr; p = ifr;
ifr = (__net_ifconf6entry_t*)((char*)ifr + ifc.__nif6h_entrylen); ifr = (__net_ifconf6entry_t*)((char*)ifr + ifc.__nif6h_entrylen);
if (!(p->__nif6e_addr.sin6_family == AF_INET6 || if (!(p->__nif6e_addr.sin6_family == AF_INET6))
p->__nif6e_addr.sin6_family == AF_INET))
continue; continue;
if (!(p->__nif6e_flags & _NIF6E_FLAGS_ON_LINK_ACTIVE)) if (!(p->__nif6e_flags & _NIF6E_FLAGS_ON_LINK_ACTIVE))
@ -313,21 +324,28 @@ static int uv__interface_addresses_v6(uv_interface_address_t** addresses,
++(*count); ++(*count);
} }
if ((*count) == 0) {
uv__free(ifc.__nif6h_buffer);
uv__close(sockfd);
return 0;
}
/* Alloc the return interface structs */ /* Alloc the return interface structs */
*addresses = uv__malloc(*count * sizeof(uv_interface_address_t)); *addresses = uv__calloc(1, *count * sizeof(uv_interface_address_t));
if (!(*addresses)) { if (!(*addresses)) {
uv__free(ifc.__nif6h_buffer);
uv__close(sockfd); uv__close(sockfd);
return UV_ENOMEM; return UV_ENOMEM;
} }
address = *addresses; address = *addresses;
count_names = 0;
ifr = (__net_ifconf6entry_t*)(ifc.__nif6h_buffer); ifr = (__net_ifconf6entry_t*)(ifc.__nif6h_buffer);
while ((char*)ifr < (char*)ifc.__nif6h_buffer + ifc.__nif6h_buflen) { while ((char*)ifr < (char*)ifc.__nif6h_buffer + ifc.__nif6h_buflen) {
p = ifr; p = ifr;
ifr = (__net_ifconf6entry_t*)((char*)ifr + ifc.__nif6h_entrylen); ifr = (__net_ifconf6entry_t*)((char*)ifr + ifc.__nif6h_entrylen);
if (!(p->__nif6e_addr.sin6_family == AF_INET6 || if (!(p->__nif6e_addr.sin6_family == AF_INET6))
p->__nif6e_addr.sin6_family == AF_INET))
continue; continue;
if (!(p->__nif6e_flags & _NIF6E_FLAGS_ON_LINK_ACTIVE)) if (!(p->__nif6e_flags & _NIF6E_FLAGS_ON_LINK_ACTIVE))
@ -335,20 +353,41 @@ static int uv__interface_addresses_v6(uv_interface_address_t** addresses,
/* All conditions above must match count loop */ /* All conditions above must match count loop */
address->name = uv__strdup(p->__nif6e_name); i = 0;
/* Ignore EBCDIC space (0x40) padding in name */
while (i < ARRAY_SIZE(p->__nif6e_name) &&
p->__nif6e_name[i] != 0x40 &&
p->__nif6e_name[i] != 0)
++i;
address->name = uv__malloc(i + 1);
if (address->name == NULL) {
uv_free_interface_addresses(*addresses, count_names);
uv__free(ifc.__nif6h_buffer);
uv__close(sockfd);
return UV_ENOMEM;
}
memcpy(address->name, p->__nif6e_name, i);
address->name[i] = '\0';
__e2a_s(address->name);
count_names++;
if (p->__nif6e_addr.sin6_family == AF_INET6) address->address.address6 = *((struct sockaddr_in6*) &p->__nif6e_addr);
address->address.address6 = *((struct sockaddr_in6*) &p->__nif6e_addr);
else
address->address.address4 = *((struct sockaddr_in*) &p->__nif6e_addr);
/* TODO: Retrieve netmask using SIOCGIFNETMASK ioctl */ for (i = 0; i < (p->__nif6e_prefixlen / 8); i++)
netmask[i] = 0xFF;
address->is_internal = flg.__nif6e_flags & _NIF6E_FLAGS_LOOPBACK ? 1 : 0; if (p->__nif6e_prefixlen % 8)
memset(address->phys_addr, 0, sizeof(address->phys_addr)); netmask[i] = 0xFF << (8 - (p->__nif6e_prefixlen % 8));
address->netmask.netmask6.sin6_len = p->__nif6e_prefixlen;
memcpy(&(address->netmask.netmask6.sin6_addr), netmask, 16);
address->netmask.netmask6.sin6_family = AF_INET6;
address->is_internal = p->__nif6e_flags & _NIF6E_FLAGS_LOOPBACK ? 1 : 0;
address++; address++;
} }
uv__free(ifc.__nif6h_buffer);
uv__close(sockfd); uv__close(sockfd);
return 0; return 0;
} }
@ -362,14 +401,18 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
struct ifreq flg; struct ifreq flg;
struct ifreq* ifr; struct ifreq* ifr;
struct ifreq* p; struct ifreq* p;
uv_interface_address_t* addresses_v6;
int count_v6; int count_v6;
unsigned int i;
int rc;
int count_names;
*count = 0; *count = 0;
*addresses = NULL; *addresses = NULL;
/* get the ipv6 addresses first */ /* get the ipv6 addresses first */
uv_interface_address_t* addresses_v6; if ((rc = uv__interface_addresses_v6(&addresses_v6, &count_v6)) != 0)
uv__interface_addresses_v6(&addresses_v6, &count_v6); return rc;
/* now get the ipv4 addresses */ /* now get the ipv4 addresses */
@ -377,12 +420,27 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
maxsize = 16384; maxsize = 16384;
sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (0 > sockfd) if (0 > sockfd) {
if (count_v6)
uv_free_interface_addresses(addresses_v6, count_v6);
return UV__ERR(errno); return UV__ERR(errno);
}
ifc.ifc_req = uv__calloc(1, maxsize); ifc.ifc_req = uv__calloc(1, maxsize);
if (ifc.ifc_req == NULL) {
if (count_v6)
uv_free_interface_addresses(addresses_v6, count_v6);
uv__close(sockfd);
return UV_ENOMEM;
}
ifc.ifc_len = maxsize; ifc.ifc_len = maxsize;
if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) { if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) {
if (count_v6)
uv_free_interface_addresses(addresses_v6, count_v6);
uv__free(ifc.ifc_req);
uv__close(sockfd); uv__close(sockfd);
return UV__ERR(errno); return UV__ERR(errno);
} }
@ -403,6 +461,9 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name));
if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) {
if (count_v6)
uv_free_interface_addresses(addresses_v6, count_v6);
uv__free(ifc.ifc_req);
uv__close(sockfd); uv__close(sockfd);
return UV__ERR(errno); return UV__ERR(errno);
} }
@ -413,27 +474,35 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
(*count)++; (*count)++;
} }
if (*count == 0) { if (*count == 0 && count_v6 == 0) {
uv__free(ifc.ifc_req);
uv__close(sockfd); uv__close(sockfd);
return 0; return 0;
} }
/* Alloc the return interface structs */ /* Alloc the return interface structs */
*addresses = uv__malloc((*count + count_v6) * *addresses = uv__calloc(1, (*count + count_v6) *
sizeof(uv_interface_address_t)); sizeof(uv_interface_address_t));
if (!(*addresses)) { if (!(*addresses)) {
if (count_v6)
uv_free_interface_addresses(addresses_v6, count_v6);
uv__free(ifc.ifc_req);
uv__close(sockfd); uv__close(sockfd);
return UV_ENOMEM; return UV_ENOMEM;
} }
address = *addresses; address = *addresses;
/* copy over the ipv6 addresses */ /* copy over the ipv6 addresses if any are found */
memcpy(address, addresses_v6, count_v6 * sizeof(uv_interface_address_t)); if (count_v6) {
address += count_v6; memcpy(address, addresses_v6, count_v6 * sizeof(uv_interface_address_t));
*count += count_v6; address += count_v6;
uv__free(addresses_v6); *count += count_v6;
/* free ipv6 addresses, but keep address names */
uv__free(addresses_v6);
}
count_names = *count;
ifr = ifc.ifc_req; ifr = ifc.ifc_req;
while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) {
p = ifr; p = ifr;
@ -446,6 +515,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name));
if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) {
uv_free_interface_addresses(*addresses, count_names);
uv__free(ifc.ifc_req);
uv__close(sockfd); uv__close(sockfd);
return UV_ENOSYS; return UV_ENOSYS;
} }
@ -455,22 +526,43 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
/* All conditions above must match count loop */ /* All conditions above must match count loop */
address->name = uv__strdup(p->ifr_name); i = 0;
/* Ignore EBCDIC space (0x40) padding in name */
while (i < ARRAY_SIZE(p->ifr_name) &&
p->ifr_name[i] != 0x40 &&
p->ifr_name[i] != 0)
++i;
address->name = uv__malloc(i + 1);
if (address->name == NULL) {
uv_free_interface_addresses(*addresses, count_names);
uv__free(ifc.ifc_req);
uv__close(sockfd);
return UV_ENOMEM;
}
memcpy(address->name, p->ifr_name, i);
address->name[i] = '\0';
__e2a_s(address->name);
count_names++;
if (p->ifr_addr.sa_family == AF_INET6) { address->address.address4 = *((struct sockaddr_in*) &p->ifr_addr);
address->address.address6 = *((struct sockaddr_in6*) &p->ifr_addr);
} else { if (ioctl(sockfd, SIOCGIFNETMASK, p) == -1) {
address->address.address4 = *((struct sockaddr_in*) &p->ifr_addr); uv_free_interface_addresses(*addresses, count_names);
uv__free(ifc.ifc_req);
uv__close(sockfd);
return UV__ERR(errno);
} }
address->netmask.netmask4 = *((struct sockaddr_in*) &p->ifr_addr);
address->netmask.netmask4.sin_family = AF_INET;
address->is_internal = flg.ifr_flags & IFF_LOOPBACK ? 1 : 0; address->is_internal = flg.ifr_flags & IFF_LOOPBACK ? 1 : 0;
memset(address->phys_addr, 0, sizeof(address->phys_addr));
address++; address++;
} }
#undef ADDR_SIZE #undef ADDR_SIZE
#undef MAX #undef MAX
uv__free(ifc.ifc_req);
uv__close(sockfd); uv__close(sockfd);
return 0; return 0;
} }
@ -529,27 +621,17 @@ int uv__io_check_fd(uv_loop_t* loop, int fd) {
} }
void uv__fs_event_close(uv_fs_event_t* handle) {
uv_fs_event_stop(handle);
}
int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) {
uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT);
return 0; return 0;
} }
int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, static int os390_regfileint(uv_fs_event_t* handle, char* path) {
const char* filename, unsigned int flags) {
uv__os390_epoll* ep; uv__os390_epoll* ep;
_RFIS reg_struct; _RFIS reg_struct;
char* path;
int rc; int rc;
if (uv__is_active(handle))
return UV_EINVAL;
ep = handle->loop->ep; ep = handle->loop->ep;
assert(ep->msg_queue != -1); assert(ep->msg_queue != -1);
@ -558,17 +640,10 @@ int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb,
reg_struct.__rfis_type = 1; reg_struct.__rfis_type = 1;
memcpy(reg_struct.__rfis_utok, &handle, sizeof(handle)); memcpy(reg_struct.__rfis_utok, &handle, sizeof(handle));
path = uv__strdup(filename);
if (path == NULL)
return UV_ENOMEM;
rc = __w_pioctl(path, _IOCC_REGFILEINT, sizeof(reg_struct), &reg_struct); rc = __w_pioctl(path, _IOCC_REGFILEINT, sizeof(reg_struct), &reg_struct);
if (rc != 0) if (rc != 0)
return UV__ERR(errno); return UV__ERR(errno);
uv__handle_start(handle);
handle->path = path;
handle->cb = cb;
memcpy(handle->rfis_rftok, reg_struct.__rfis_rftok, memcpy(handle->rfis_rftok, reg_struct.__rfis_rftok,
sizeof(handle->rfis_rftok)); sizeof(handle->rfis_rftok));
@ -576,7 +651,33 @@ int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb,
} }
int uv_fs_event_stop(uv_fs_event_t* handle) { int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb,
const char* filename, unsigned int flags) {
char* path;
int rc;
if (uv__is_active(handle))
return UV_EINVAL;
path = uv__strdup(filename);
if (path == NULL)
return UV_ENOMEM;
rc = os390_regfileint(handle, path);
if (rc != 0) {
uv__free(path);
return rc;
}
uv__handle_start(handle);
handle->path = path;
handle->cb = cb;
return 0;
}
int uv__fs_event_stop(uv_fs_event_t* handle) {
uv__os390_epoll* ep; uv__os390_epoll* ep;
_RFIS reg_struct; _RFIS reg_struct;
int rc; int rc;
@ -602,12 +703,40 @@ int uv_fs_event_stop(uv_fs_event_t* handle) {
if (rc != 0 && errno != EALREADY && errno != ENOENT) if (rc != 0 && errno != EALREADY && errno != ENOENT)
abort(); abort();
if (handle->path != NULL) {
uv__free(handle->path);
handle->path = NULL;
}
if (rc != 0 && errno == EALREADY)
return -1;
uv__handle_stop(handle); uv__handle_stop(handle);
return 0; return 0;
} }
int uv_fs_event_stop(uv_fs_event_t* handle) {
uv__fs_event_stop(handle);
return 0;
}
void uv__fs_event_close(uv_fs_event_t* handle) {
/*
* If we were unable to unregister file interest here, then it is most likely
* that there is a pending queued change notification. When this happens, we
* don't want to complete the close as it will free the underlying memory for
* the handle, causing a use-after-free problem when the event is processed.
* We defer the final cleanup until after the event is consumed in
* os390_message_queue_handler().
*/
if (uv__fs_event_stop(handle) == 0)
uv__make_close_pending((uv_handle_t*) handle);
}
static int os390_message_queue_handler(uv__os390_epoll* ep) { static int os390_message_queue_handler(uv__os390_epoll* ep) {
uv_fs_event_t* handle; uv_fs_event_t* handle;
int msglen; int msglen;
@ -628,7 +757,15 @@ static int os390_message_queue_handler(uv__os390_epoll* ep) {
events = 0; events = 0;
if (msg.__rfim_event == _RFIM_ATTR || msg.__rfim_event == _RFIM_WRITE) if (msg.__rfim_event == _RFIM_ATTR || msg.__rfim_event == _RFIM_WRITE)
events = UV_CHANGE; events = UV_CHANGE;
else if (msg.__rfim_event == _RFIM_RENAME) else if (msg.__rfim_event == _RFIM_RENAME || msg.__rfim_event == _RFIM_UNLINK)
events = UV_RENAME;
else if (msg.__rfim_event == 156)
/* TODO(gabylb): zos - this event should not happen, need to investigate.
*
* This event seems to occur when the watched file is [re]moved, or an
* editor (like vim) renames then creates the file on save (for vim, that's
* when backupcopy=no|auto).
*/
events = UV_RENAME; events = UV_RENAME;
else else
/* Some event that we are not interested in. */ /* Some event that we are not interested in. */
@ -639,6 +776,26 @@ static int os390_message_queue_handler(uv__os390_epoll* ep) {
*/ */
__a2e_l(msg.__rfim_utok, sizeof(msg.__rfim_utok)); __a2e_l(msg.__rfim_utok, sizeof(msg.__rfim_utok));
handle = *(uv_fs_event_t**)(msg.__rfim_utok); handle = *(uv_fs_event_t**)(msg.__rfim_utok);
assert(handle != NULL);
assert((handle->flags & UV_HANDLE_CLOSED) == 0);
if (uv__is_closing(handle)) {
uv__handle_stop(handle);
uv__make_close_pending((uv_handle_t*) handle);
return 0;
} else if (handle->path == NULL) {
/* _RFIS_UNREG returned EALREADY. */
uv__handle_stop(handle);
return 0;
}
/* The file is implicitly unregistered when the change notification is
* sent, only one notification is sent per registration. So we need to
* re-register interest in a file after each change notification we
* receive.
*/
assert(handle->path != NULL);
os390_regfileint(handle, handle->path);
handle->cb(handle, uv__basename_r(handle->path), events, 0); handle->cb(handle, uv__basename_r(handle->path), events, 0);
return 1; return 1;
} }
@ -650,6 +807,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
struct epoll_event* pe; struct epoll_event* pe;
struct epoll_event e; struct epoll_event e;
uv__os390_epoll* ep; uv__os390_epoll* ep;
int have_signals;
int real_timeout; int real_timeout;
QUEUE* q; QUEUE* q;
uv__io_t* w; uv__io_t* w;
@ -712,6 +870,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
count = 48; /* Benchmarks suggest this gives the best throughput. */ count = 48; /* Benchmarks suggest this gives the best throughput. */
real_timeout = timeout; real_timeout = timeout;
int nevents = 0; int nevents = 0;
have_signals = 0;
if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) {
reset_timeout = 1; reset_timeout = 1;
@ -796,6 +955,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
ep = loop->ep; ep = loop->ep;
if (pe->is_msg) { if (pe->is_msg) {
os390_message_queue_handler(ep); os390_message_queue_handler(ep);
nevents++;
continue; continue;
} }
@ -825,19 +985,35 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
pe->events |= w->pevents & (POLLIN | POLLOUT); pe->events |= w->pevents & (POLLIN | POLLOUT);
if (pe->events != 0) { if (pe->events != 0) {
uv__metrics_update_idle_time(loop); /* Run signal watchers last. This also affects child process watchers
w->cb(loop, w, pe->events); * because those are implemented in terms of signal watchers.
*/
if (w == &loop->signal_io_watcher) {
have_signals = 1;
} else {
uv__metrics_update_idle_time(loop);
w->cb(loop, w, pe->events);
}
nevents++; nevents++;
} }
} }
loop->watchers[loop->nwatchers] = NULL;
loop->watchers[loop->nwatchers + 1] = NULL;
if (reset_timeout != 0) { if (reset_timeout != 0) {
timeout = user_timeout; timeout = user_timeout;
reset_timeout = 0; reset_timeout = 0;
} }
if (have_signals != 0) {
uv__metrics_update_idle_time(loop);
loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN);
}
loop->watchers[loop->nwatchers] = NULL;
loop->watchers[loop->nwatchers + 1] = NULL;
if (have_signals != 0)
return; /* Event loop should cycle now so don't poll again. */
if (nevents != 0) { if (nevents != 0) {
if (nfds == ARRAY_SIZE(events) && --count != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) {
/* Poll for more events but don't block this time. */ /* Poll for more events but don't block this time. */
@ -872,6 +1048,5 @@ int uv__io_fork(uv_loop_t* loop) {
*/ */
loop->ep = NULL; loop->ep = NULL;
uv__platform_loop_delete(loop);
return uv__platform_loop_init(loop); return uv__platform_loop_init(loop);
} }

View File

@ -51,7 +51,9 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) {
/* Already bound? */ /* Already bound? */
if (uv__stream_fd(handle) >= 0) if (uv__stream_fd(handle) >= 0)
return UV_EINVAL; return UV_EINVAL;
if (uv__is_closing(handle)) {
return UV_EINVAL;
}
/* Make a copy of the file name, it outlives this function's scope. */ /* Make a copy of the file name, it outlives this function's scope. */
pipe_fname = uv__strdup(name); pipe_fname = uv__strdup(name);
if (pipe_fname == NULL) if (pipe_fname == NULL)
@ -319,7 +321,7 @@ uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) {
if (handle->accepted_fd == -1) if (handle->accepted_fd == -1)
return UV_UNKNOWN_HANDLE; return UV_UNKNOWN_HANDLE;
else else
return uv__handle_type(handle->accepted_fd); return uv_guess_handle(handle->accepted_fd);
} }

View File

@ -35,7 +35,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <poll.h> #include <poll.h>
#if defined(__APPLE__) && !TARGET_OS_IPHONE #if defined(__APPLE__)
# include <spawn.h> # include <spawn.h>
# include <paths.h> # include <paths.h>
# include <sys/kauth.h> # include <sys/kauth.h>
@ -671,27 +671,25 @@ static int uv__spawn_resolve_and_spawn(const uv_process_options_t* options,
if (options->env != NULL) if (options->env != NULL)
env = options->env; env = options->env;
/* If options->file contains a slash, posix_spawn/posix_spawnp behave /* If options->file contains a slash, posix_spawn/posix_spawnp should behave
* the same, and don't involve PATH resolution at all. Otherwise, if * the same, and do not involve PATH resolution at all. The libc
* options->file does not include a slash, but no custom environment is * `posix_spawnp` provided by Apple is buggy (since 10.15), so we now emulate it
* to be used, the environment used for path resolution as well for the * here, per https://github.com/libuv/libuv/pull/3583. */
* child process is that of the parent process, so posix_spawnp is the if (strchr(options->file, '/') != NULL) {
* way to go. */
if (strchr(options->file, '/') != NULL || options->env == NULL) {
do do
err = posix_spawnp(pid, options->file, actions, attrs, options->args, env); err = posix_spawn(pid, options->file, actions, attrs, options->args, env);
while (err == EINTR); while (err == EINTR);
return err; return err;
} }
/* Look for the definition of PATH in the provided env */ /* Look for the definition of PATH in the provided env */
path = uv__spawn_find_path_in_env(options->env); path = uv__spawn_find_path_in_env(env);
/* The following resolution logic (execvpe emulation) is copied from /* The following resolution logic (execvpe emulation) is copied from
* https://git.musl-libc.org/cgit/musl/tree/src/process/execvp.c * https://git.musl-libc.org/cgit/musl/tree/src/process/execvp.c
* and adapted to work for our specific usage */ * and adapted to work for our specific usage */
/* If no path was provided in options->env, use the default value /* If no path was provided in env, use the default value
* to look for the executable */ * to look for the executable */
if (path == NULL) if (path == NULL)
path = _PATH_DEFPATH; path = _PATH_DEFPATH;
@ -812,11 +810,6 @@ static int uv__spawn_and_init_child_fork(const uv_process_options_t* options,
*pid = fork(); *pid = fork();
if (*pid == -1) {
/* Failed to fork */
return UV__ERR(errno);
}
if (*pid == 0) { if (*pid == 0) {
/* Fork succeeded, in the child process */ /* Fork succeeded, in the child process */
uv__process_child_init(options, stdio_count, pipes, error_fd); uv__process_child_init(options, stdio_count, pipes, error_fd);
@ -826,6 +819,10 @@ static int uv__spawn_and_init_child_fork(const uv_process_options_t* options,
if (pthread_sigmask(SIG_SETMASK, &sigoldset, NULL) != 0) if (pthread_sigmask(SIG_SETMASK, &sigoldset, NULL) != 0)
abort(); abort();
if (*pid == -1)
/* Failed to fork */
return UV__ERR(errno);
/* Fork succeeded, in the parent process */ /* Fork succeeded, in the parent process */
return 0; return 0;
} }
@ -1066,9 +1063,16 @@ int uv_process_kill(uv_process_t* process, int signum) {
int uv_kill(int pid, int signum) { int uv_kill(int pid, int signum) {
if (kill(pid, signum)) if (kill(pid, signum)) {
#if defined(__MVS__)
/* EPERM is returned if the process is a zombie. */
siginfo_t infop;
if (errno == EPERM &&
waitid(P_PID, pid, &infop, WNOHANG | WNOWAIT | WEXITED) == 0)
return 0;
#endif
return UV__ERR(errno); return UV__ERR(errno);
else } else
return 0; return 0;
} }

View File

@ -66,6 +66,7 @@ static void uv__read(uv_stream_t* stream);
static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events); static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events);
static void uv__write_callbacks(uv_stream_t* stream); static void uv__write_callbacks(uv_stream_t* stream);
static size_t uv__write_req_size(uv_write_t* req); static size_t uv__write_req_size(uv_write_t* req);
static void uv__drain(uv_stream_t* stream);
void uv__stream_init(uv_loop_t* loop, void uv__stream_init(uv_loop_t* loop,
@ -453,17 +454,7 @@ void uv__stream_destroy(uv_stream_t* stream) {
uv__stream_flush_write_queue(stream, UV_ECANCELED); uv__stream_flush_write_queue(stream, UV_ECANCELED);
uv__write_callbacks(stream); uv__write_callbacks(stream);
uv__drain(stream);
if (stream->shutdown_req) {
/* The ECANCELED error code is a lie, the shutdown(2) syscall is a
* fait accompli at this point. Maybe we should revisit this in v0.11.
* A possible reason for leaving it unchanged is that it informs the
* callee that the handle has been destroyed.
*/
uv__req_unregister(stream->loop, stream->shutdown_req);
stream->shutdown_req->cb(stream->shutdown_req, UV_ECANCELED);
stream->shutdown_req = NULL;
}
assert(stream->write_queue_size == 0); assert(stream->write_queue_size == 0);
} }
@ -641,7 +632,9 @@ done:
int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) { int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) {
int err; int err;
if (uv__is_closing(stream)) {
return UV_EINVAL;
}
switch (stream->type) { switch (stream->type) {
case UV_TCP: case UV_TCP:
err = uv__tcp_listen((uv_tcp_t*)stream, backlog, cb); err = uv__tcp_listen((uv_tcp_t*)stream, backlog, cb);
@ -667,25 +660,30 @@ static void uv__drain(uv_stream_t* stream) {
int err; int err;
assert(QUEUE_EMPTY(&stream->write_queue)); assert(QUEUE_EMPTY(&stream->write_queue));
uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT); if (!(stream->flags & UV_HANDLE_CLOSING)) {
uv__stream_osx_interrupt_select(stream); uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT);
uv__stream_osx_interrupt_select(stream);
}
/* Shutdown? */ if (!(stream->flags & UV_HANDLE_SHUTTING))
if ((stream->flags & UV_HANDLE_SHUTTING) && return;
!(stream->flags & UV_HANDLE_CLOSING) &&
req = stream->shutdown_req;
assert(req);
if ((stream->flags & UV_HANDLE_CLOSING) ||
!(stream->flags & UV_HANDLE_SHUT)) { !(stream->flags & UV_HANDLE_SHUT)) {
assert(stream->shutdown_req);
req = stream->shutdown_req;
stream->shutdown_req = NULL; stream->shutdown_req = NULL;
stream->flags &= ~UV_HANDLE_SHUTTING; stream->flags &= ~UV_HANDLE_SHUTTING;
uv__req_unregister(stream->loop, req); uv__req_unregister(stream->loop, req);
err = 0; err = 0;
if (shutdown(uv__stream_fd(stream), SHUT_WR)) if (stream->flags & UV_HANDLE_CLOSING)
/* The user destroyed the stream before we got to do the shutdown. */
err = UV_ECANCELED;
else if (shutdown(uv__stream_fd(stream), SHUT_WR))
err = UV__ERR(errno); err = UV__ERR(errno);
else /* Success. */
if (err == 0)
stream->flags |= UV_HANDLE_SHUT; stream->flags |= UV_HANDLE_SHUT;
if (req->cb != NULL) if (req->cb != NULL)
@ -926,7 +924,6 @@ static void uv__write(uv_stream_t* stream) {
} }
req->error = n; req->error = n;
// XXX(jwn): this must call uv__stream_flush_write_queue(stream, n) here, since we won't generate any more events
uv__write_req_finish(req); uv__write_req_finish(req);
uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT); uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT);
uv__stream_osx_interrupt_select(stream); uv__stream_osx_interrupt_select(stream);
@ -964,49 +961,6 @@ static void uv__write_callbacks(uv_stream_t* stream) {
} }
uv_handle_type uv__handle_type(int fd) {
struct sockaddr_storage ss;
socklen_t sslen;
socklen_t len;
int type;
memset(&ss, 0, sizeof(ss));
sslen = sizeof(ss);
if (getsockname(fd, (struct sockaddr*)&ss, &sslen))
return UV_UNKNOWN_HANDLE;
len = sizeof type;
if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &len))
return UV_UNKNOWN_HANDLE;
if (type == SOCK_STREAM) {
#if defined(_AIX) || defined(__DragonFly__)
/* on AIX/DragonFly the getsockname call returns an empty sa structure
* for sockets of type AF_UNIX. For all other types it will
* return a properly filled in structure.
*/
if (sslen == 0)
return UV_NAMED_PIPE;
#endif
switch (ss.ss_family) {
case AF_UNIX:
return UV_NAMED_PIPE;
case AF_INET:
case AF_INET6:
return UV_TCP;
}
}
if (type == SOCK_DGRAM &&
(ss.ss_family == AF_INET || ss.ss_family == AF_INET6))
return UV_UDP;
return UV_UNKNOWN_HANDLE;
}
static void uv__stream_eof(uv_stream_t* stream, const uv_buf_t* buf) { static void uv__stream_eof(uv_stream_t* stream, const uv_buf_t* buf) {
stream->flags |= UV_HANDLE_READ_EOF; stream->flags |= UV_HANDLE_READ_EOF;
stream->flags &= ~UV_HANDLE_READING; stream->flags &= ~UV_HANDLE_READING;
@ -1278,7 +1232,8 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) {
assert(uv__stream_fd(stream) >= 0); assert(uv__stream_fd(stream) >= 0);
/* Initialize request */ /* Initialize request. The `shutdown(2)` call will always be deferred until
* `uv__drain`, just before the callback is run. */
uv__req_init(stream->loop, req, UV_SHUTDOWN); uv__req_init(stream->loop, req, UV_SHUTDOWN);
req->handle = stream; req->handle = stream;
req->cb = cb; req->cb = cb;
@ -1286,8 +1241,8 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) {
stream->flags |= UV_HANDLE_SHUTTING; stream->flags |= UV_HANDLE_SHUTTING;
stream->flags &= ~UV_HANDLE_WRITABLE; stream->flags &= ~UV_HANDLE_WRITABLE;
uv__io_start(stream->loop, &stream->io_watcher, POLLOUT); if (QUEUE_EMPTY(&stream->write_queue))
uv__stream_osx_interrupt_select(stream); uv__io_feed(stream->loop, &stream->io_watcher);
return 0; return 0;
} }

View File

@ -154,7 +154,6 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
sigset_t set; sigset_t set;
uint64_t base; uint64_t base;
uint64_t diff; uint64_t diff;
uint64_t idle_poll;
unsigned int nfds; unsigned int nfds;
unsigned int i; unsigned int i;
int saved_errno; int saved_errno;
@ -424,7 +423,7 @@ void uv_loadavg(double avg[3]) {
#if defined(PORT_SOURCE_FILE) #if defined(PORT_SOURCE_FILE)
static int uv__fs_event_rearm(uv_fs_event_t *handle) { static int uv__fs_event_rearm(uv_fs_event_t *handle) {
if (handle->fd == -1) if (handle->fd == PORT_DELETED)
return UV_EBADF; return UV_EBADF;
if (port_associate(handle->loop->fs_fd, if (port_associate(handle->loop->fs_fd,
@ -475,6 +474,12 @@ static void uv__fs_event_read(uv_loop_t* loop,
handle = (uv_fs_event_t*) pe.portev_user; handle = (uv_fs_event_t*) pe.portev_user;
assert((r == 0) && "unexpected port_get() error"); assert((r == 0) && "unexpected port_get() error");
if (uv__is_closing(handle)) {
uv__handle_stop(handle);
uv__make_close_pending((uv_handle_t*) handle);
break;
}
events = 0; events = 0;
if (pe.portev_events & (FILE_ATTRIB | FILE_MODIFIED)) if (pe.portev_events & (FILE_ATTRIB | FILE_MODIFIED))
events |= UV_CHANGE; events |= UV_CHANGE;
@ -542,12 +547,14 @@ int uv_fs_event_start(uv_fs_event_t* handle,
} }
int uv_fs_event_stop(uv_fs_event_t* handle) { static int uv__fs_event_stop(uv_fs_event_t* handle) {
int ret = 0;
if (!uv__is_active(handle)) if (!uv__is_active(handle))
return 0; return 0;
if (handle->fd == PORT_FIRED || handle->fd == PORT_LOADED) { if (handle->fd == PORT_LOADED) {
port_dissociate(handle->loop->fs_fd, ret = port_dissociate(handle->loop->fs_fd,
PORT_SOURCE_FILE, PORT_SOURCE_FILE,
(uintptr_t) &handle->fo); (uintptr_t) &handle->fo);
} }
@ -556,13 +563,28 @@ int uv_fs_event_stop(uv_fs_event_t* handle) {
uv__free(handle->path); uv__free(handle->path);
handle->path = NULL; handle->path = NULL;
handle->fo.fo_name = NULL; handle->fo.fo_name = NULL;
uv__handle_stop(handle); if (ret == 0)
uv__handle_stop(handle);
return ret;
}
int uv_fs_event_stop(uv_fs_event_t* handle) {
(void) uv__fs_event_stop(handle);
return 0; return 0;
} }
void uv__fs_event_close(uv_fs_event_t* handle) { void uv__fs_event_close(uv_fs_event_t* handle) {
uv_fs_event_stop(handle); /*
* If we were unable to dissociate the port here, then it is most likely
* that there is a pending queued event. When this happens, we don't want
* to complete the close as it will free the underlying memory for the
* handle, causing a use-after-free problem when the event is processed.
* We defer the final cleanup until after the event is consumed in
* uv__fs_event_read().
*/
if (uv__fs_event_stop(handle) == 0)
uv__make_close_pending((uv_handle_t*) handle);
} }
#else /* !defined(PORT_SOURCE_FILE) */ #else /* !defined(PORT_SOURCE_FILE) */

View File

@ -184,14 +184,15 @@ int uv__tcp_bind(uv_tcp_t* tcp,
#endif #endif
errno = 0; errno = 0;
if (bind(tcp->io_watcher.fd, addr, addrlen) && errno != EADDRINUSE) { err = bind(tcp->io_watcher.fd, addr, addrlen);
if (err == -1 && errno != EADDRINUSE) {
if (errno == EAFNOSUPPORT) if (errno == EAFNOSUPPORT)
/* OSX, other BSDs and SunoS fail with EAFNOSUPPORT when binding a /* OSX, other BSDs and SunoS fail with EAFNOSUPPORT when binding a
* socket created with AF_INET to an AF_INET6 address or vice versa. */ * socket created with AF_INET to an AF_INET6 address or vice versa. */
return UV_EINVAL; return UV_EINVAL;
return UV__ERR(errno); return UV__ERR(errno);
} }
tcp->delayed_error = UV__ERR(errno); tcp->delayed_error = (err == -1) ? UV__ERR(errno) : 0;
tcp->flags |= UV_HANDLE_BOUND; tcp->flags |= UV_HANDLE_BOUND;
if (addr->sa_family == AF_INET6) if (addr->sa_family == AF_INET6)
@ -320,8 +321,16 @@ int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb) {
return UV_EINVAL; return UV_EINVAL;
fd = uv__stream_fd(handle); fd = uv__stream_fd(handle);
if (0 != setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l))) if (0 != setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l))) {
return UV__ERR(errno); if (errno == EINVAL) {
/* Open Group Specifications Issue 7, 2018 edition states that
* EINVAL may mean the socket has been shut down already.
* Behavior observed on Solaris, illumos and macOS. */
errno = 0;
} else {
return UV__ERR(errno);
}
}
uv_close((uv_handle_t*) handle, close_cb); uv_close((uv_handle_t*) handle, close_cb);
return 0; return 0;

View File

@ -66,6 +66,19 @@ static int orig_termios_fd = -1;
static struct termios orig_termios; static struct termios orig_termios;
static uv_spinlock_t termios_spinlock = UV_SPINLOCK_INITIALIZER; static uv_spinlock_t termios_spinlock = UV_SPINLOCK_INITIALIZER;
int uv__tcsetattr(int fd, int how, const struct termios *term) {
int rc;
do
rc = tcsetattr(fd, how, term);
while (rc == -1 && errno == EINTR);
if (rc == -1)
return UV__ERR(errno);
return 0;
}
static int uv__tty_is_slave(const int fd) { static int uv__tty_is_slave(const int fd) {
int result; int result;
#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
@ -268,13 +281,18 @@ static void uv__tty_make_raw(struct termios* tio) {
int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) { int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
struct termios tmp; struct termios tmp;
int fd; int fd;
int rc;
if (tty->mode == (int) mode) if (tty->mode == (int) mode)
return 0; return 0;
fd = uv__stream_fd(tty); fd = uv__stream_fd(tty);
if (tty->mode == UV_TTY_MODE_NORMAL && mode != UV_TTY_MODE_NORMAL) { if (tty->mode == UV_TTY_MODE_NORMAL && mode != UV_TTY_MODE_NORMAL) {
if (tcgetattr(fd, &tty->orig_termios)) do
rc = tcgetattr(fd, &tty->orig_termios);
while (rc == -1 && errno == EINTR);
if (rc == -1)
return UV__ERR(errno); return UV__ERR(errno);
/* This is used for uv_tty_reset_mode() */ /* This is used for uv_tty_reset_mode() */
@ -304,11 +322,11 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
} }
/* Apply changes after draining */ /* Apply changes after draining */
if (tcsetattr(fd, TCSADRAIN, &tmp)) rc = uv__tcsetattr(fd, TCSADRAIN, &tmp);
return UV__ERR(errno); if (rc == 0)
tty->mode = mode;
tty->mode = mode; return rc;
return 0;
} }
@ -331,7 +349,7 @@ int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) {
uv_handle_type uv_guess_handle(uv_file file) { uv_handle_type uv_guess_handle(uv_file file) {
struct sockaddr sa; struct sockaddr_storage ss;
struct stat s; struct stat s;
socklen_t len; socklen_t len;
int type; int type;
@ -342,8 +360,24 @@ uv_handle_type uv_guess_handle(uv_file file) {
if (isatty(file)) if (isatty(file))
return UV_TTY; return UV_TTY;
if (fstat(file, &s)) if (fstat(file, &s)) {
#if defined(__PASE__)
/* On ibmi receiving RST from TCP instead of FIN immediately puts fd into
* an error state. fstat will return EINVAL, getsockname will also return
* EINVAL, even if sockaddr_storage is valid. (If file does not refer to a
* socket, ENOTSOCK is returned instead.)
* In such cases, we will permit the user to open the connection as uv_tcp
* still, so that the user can get immediately notified of the error in
* their read callback and close this fd.
*/
len = sizeof(ss);
if (getsockname(file, (struct sockaddr*) &ss, &len)) {
if (errno == EINVAL)
return UV_TCP;
}
#endif
return UV_UNKNOWN_HANDLE; return UV_UNKNOWN_HANDLE;
}
if (S_ISREG(s.st_mode)) if (S_ISREG(s.st_mode))
return UV_FILE; return UV_FILE;
@ -357,16 +391,29 @@ uv_handle_type uv_guess_handle(uv_file file) {
if (!S_ISSOCK(s.st_mode)) if (!S_ISSOCK(s.st_mode))
return UV_UNKNOWN_HANDLE; return UV_UNKNOWN_HANDLE;
len = sizeof(ss);
if (getsockname(file, (struct sockaddr*) &ss, &len)) {
#if defined(_AIX)
/* On aix receiving RST from TCP instead of FIN immediately puts fd into
* an error state. In such case getsockname will return EINVAL, even if
* sockaddr_storage is valid.
* In such cases, we will permit the user to open the connection as uv_tcp
* still, so that the user can get immediately notified of the error in
* their read callback and close this fd.
*/
if (errno == EINVAL) {
return UV_TCP;
}
#endif
return UV_UNKNOWN_HANDLE;
}
len = sizeof(type); len = sizeof(type);
if (getsockopt(file, SOL_SOCKET, SO_TYPE, &type, &len)) if (getsockopt(file, SOL_SOCKET, SO_TYPE, &type, &len))
return UV_UNKNOWN_HANDLE; return UV_UNKNOWN_HANDLE;
len = sizeof(sa);
if (getsockname(file, &sa, &len))
return UV_UNKNOWN_HANDLE;
if (type == SOCK_DGRAM) if (type == SOCK_DGRAM)
if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6) if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6)
return UV_UDP; return UV_UDP;
if (type == SOCK_STREAM) { if (type == SOCK_STREAM) {
@ -379,9 +426,9 @@ uv_handle_type uv_guess_handle(uv_file file) {
return UV_NAMED_PIPE; return UV_NAMED_PIPE;
#endif /* defined(_AIX) || defined(__DragonFly__) */ #endif /* defined(_AIX) || defined(__DragonFly__) */
if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6) if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6)
return UV_TCP; return UV_TCP;
if (sa.sa_family == AF_UNIX) if (ss.ss_family == AF_UNIX)
return UV_NAMED_PIPE; return UV_NAMED_PIPE;
} }
@ -403,8 +450,7 @@ int uv_tty_reset_mode(void) {
err = 0; err = 0;
if (orig_termios_fd != -1) if (orig_termios_fd != -1)
if (tcsetattr(orig_termios_fd, TCSANOW, &orig_termios)) err = uv__tcsetattr(orig_termios_fd, TCSANOW, &orig_termios);
err = UV__ERR(errno);
uv_spinlock_unlock(&termios_spinlock); uv_spinlock_unlock(&termios_spinlock);
errno = saved_errno; errno = saved_errno;

View File

@ -704,7 +704,16 @@ int uv__udp_disconnect(uv_udp_t* handle) {
do { do {
errno = 0; errno = 0;
#ifdef __PASE__
/* On IBMi a connectionless transport socket can be disconnected by
* either setting the addr parameter to NULL or setting the
* addr_length parameter to zero, and issuing another connect().
* https://www.ibm.com/docs/en/i/7.4?topic=ssw_ibm_i_74/apis/connec.htm
*/
r = connect(handle->io_watcher.fd, (struct sockaddr*) NULL, 0);
#else
r = connect(handle->io_watcher.fd, (struct sockaddr*) &addr, sizeof(addr)); r = connect(handle->io_watcher.fd, (struct sockaddr*) &addr, sizeof(addr));
#endif
} while (r == -1 && errno == EINTR); } while (r == -1 && errno == EINTR);
if (r == -1) { if (r == -1) {

View File

@ -295,7 +295,9 @@ int uv_tcp_bind(uv_tcp_t* handle,
if (handle->type != UV_TCP) if (handle->type != UV_TCP)
return UV_EINVAL; return UV_EINVAL;
if (uv__is_closing(handle)) {
return UV_EINVAL;
}
if (addr->sa_family == AF_INET) if (addr->sa_family == AF_INET)
addrlen = sizeof(struct sockaddr_in); addrlen = sizeof(struct sockaddr_in);
else if (addr->sa_family == AF_INET6) else if (addr->sa_family == AF_INET6)

View File

@ -592,7 +592,7 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) {
int uv_run(uv_loop_t *loop, uv_run_mode mode) { int uv_run(uv_loop_t *loop, uv_run_mode mode) {
DWORD timeout; DWORD timeout;
int r; int r;
int ran_pending; int can_sleep;
r = uv__loop_alive(loop); r = uv__loop_alive(loop);
if (!r) if (!r)
@ -602,12 +602,14 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) {
uv_update_time(loop); uv_update_time(loop);
uv__run_timers(loop); uv__run_timers(loop);
ran_pending = uv__process_reqs(loop); can_sleep = loop->pending_reqs_tail == NULL && loop->idle_handles == NULL;
uv__process_reqs(loop);
uv__idle_invoke(loop); uv__idle_invoke(loop);
uv__prepare_invoke(loop); uv__prepare_invoke(loop);
timeout = 0; timeout = 0;
if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT) if ((mode == UV_RUN_ONCE && can_sleep) || mode == UV_RUN_DEFAULT)
timeout = uv_backend_timeout(loop); timeout = uv_backend_timeout(loop);
if (pGetQueuedCompletionStatusEx) if (pGetQueuedCompletionStatusEx)
@ -615,6 +617,11 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) {
else else
uv__poll_wine(loop, timeout); uv__poll_wine(loop, timeout);
/* Process immediate callbacks (e.g. write_cb) a small fixed number of
* times to avoid loop starvation.*/
for (r = 0; r < 8 && loop->pending_reqs_tail != NULL; r++)
uv__process_reqs(loop);
/* Run one final update on the provider_idle_time in case uv__poll* /* Run one final update on the provider_idle_time in case uv__poll*
* returned because the timeout expired, but no events were received. This * returned because the timeout expired, but no events were received. This
* call will be ignored if the provider_entry_time was either never set (if * call will be ignored if the provider_entry_time was either never set (if

View File

@ -73,7 +73,6 @@ int uv_translate_sys_error(int sys_errno) {
case WSAEACCES: return UV_EACCES; case WSAEACCES: return UV_EACCES;
case ERROR_ELEVATION_REQUIRED: return UV_EACCES; case ERROR_ELEVATION_REQUIRED: return UV_EACCES;
case ERROR_CANT_ACCESS_FILE: return UV_EACCES; case ERROR_CANT_ACCESS_FILE: return UV_EACCES;
case ERROR_ACCESS_DENIED: return UV_EACCES;
case ERROR_ADDRESS_ALREADY_ASSOCIATED: return UV_EADDRINUSE; case ERROR_ADDRESS_ALREADY_ASSOCIATED: return UV_EADDRINUSE;
case WSAEADDRINUSE: return UV_EADDRINUSE; case WSAEADDRINUSE: return UV_EADDRINUSE;
case WSAEADDRNOTAVAIL: return UV_EADDRNOTAVAIL; case WSAEADDRNOTAVAIL: return UV_EADDRNOTAVAIL;
@ -155,6 +154,7 @@ int uv_translate_sys_error(int sys_errno) {
case WSAENOTSOCK: return UV_ENOTSOCK; case WSAENOTSOCK: return UV_ENOTSOCK;
case ERROR_NOT_SUPPORTED: return UV_ENOTSUP; case ERROR_NOT_SUPPORTED: return UV_ENOTSUP;
case ERROR_BROKEN_PIPE: return UV_EOF; case ERROR_BROKEN_PIPE: return UV_EOF;
case ERROR_ACCESS_DENIED: return UV_EPERM;
case ERROR_PRIVILEGE_NOT_HELD: return UV_EPERM; case ERROR_PRIVILEGE_NOT_HELD: return UV_EPERM;
case ERROR_BAD_PIPE: return UV_EPIPE; case ERROR_BAD_PIPE: return UV_EPIPE;
case ERROR_NO_DATA: return UV_EPIPE; case ERROR_NO_DATA: return UV_EPIPE;

View File

@ -88,6 +88,9 @@ void uv__process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle,
uv_req_t* req); uv_req_t* req);
void uv__process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, void uv__process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle,
uv_connect_t* req); uv_connect_t* req);
void uv__process_tcp_shutdown_req(uv_loop_t* loop,
uv_tcp_t* stream,
uv_shutdown_t* req);
void uv__tcp_close(uv_loop_t* loop, uv_tcp_t* tcp); void uv__tcp_close(uv_loop_t* loop, uv_tcp_t* tcp);
void uv__tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle); void uv__tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle);
@ -130,6 +133,7 @@ int uv__pipe_write(uv_loop_t* loop,
size_t nbufs, size_t nbufs,
uv_stream_t* send_handle, uv_stream_t* send_handle,
uv_write_cb cb); uv_write_cb cb);
void uv__pipe_shutdown(uv_loop_t* loop, uv_pipe_t* handle, uv_shutdown_t* req);
void uv__process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle, void uv__process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle,
uv_req_t* req); uv_req_t* req);
@ -143,7 +147,6 @@ void uv__process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle,
uv_shutdown_t* req); uv_shutdown_t* req);
void uv__pipe_close(uv_loop_t* loop, uv_pipe_t* handle); void uv__pipe_close(uv_loop_t* loop, uv_pipe_t* handle);
void uv__pipe_cleanup(uv_loop_t* loop, uv_pipe_t* handle);
void uv__pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle); void uv__pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle);
@ -177,7 +180,9 @@ void uv__process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle,
*/ */
void uv__process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle, void uv__process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle,
uv_connect_t* req); uv_connect_t* req);
void uv__process_tty_shutdown_req(uv_loop_t* loop,
uv_tty_t* stream,
uv_shutdown_t* req);
void uv__tty_endgame(uv_loop_t* loop, uv_tty_t* handle); void uv__tty_endgame(uv_loop_t* loop, uv_tty_t* handle);

View File

@ -121,14 +121,10 @@ int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) {
static void uv__pipe_connection_init(uv_pipe_t* handle) { static void uv__pipe_connection_init(uv_pipe_t* handle) {
assert(!(handle->flags & UV_HANDLE_PIPESERVER));
uv__connection_init((uv_stream_t*) handle); uv__connection_init((uv_stream_t*) handle);
handle->read_req.data = handle; handle->read_req.data = handle;
handle->pipe.conn.eof_timer = NULL; handle->pipe.conn.eof_timer = NULL;
assert(!(handle->flags & UV_HANDLE_PIPESERVER));
if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) {
handle->pipe.conn.readfile_thread_handle = NULL;
InitializeCriticalSection(&handle->pipe.conn.readfile_thread_lock);
}
} }
@ -393,6 +389,8 @@ int uv__create_stdio_pipe_pair(uv_loop_t* loop,
unsigned int client_flags; unsigned int client_flags;
int err; int err;
uv__pipe_connection_init(parent_pipe);
server_pipe = INVALID_HANDLE_VALUE; server_pipe = INVALID_HANDLE_VALUE;
client_pipe = INVALID_HANDLE_VALUE; client_pipe = INVALID_HANDLE_VALUE;
@ -427,7 +425,6 @@ int uv__create_stdio_pipe_pair(uv_loop_t* loop,
goto error; goto error;
} }
uv__pipe_connection_init(parent_pipe);
parent_pipe->handle = server_pipe; parent_pipe->handle = server_pipe;
*child_pipe_ptr = client_pipe; *child_pipe_ptr = client_pipe;
@ -462,7 +459,9 @@ static int uv__set_pipe_handle(uv_loop_t* loop,
DWORD current_mode = 0; DWORD current_mode = 0;
DWORD err = 0; DWORD err = 0;
if (handle->flags & UV_HANDLE_PIPESERVER) assert(handle->flags & UV_HANDLE_CONNECTION);
assert(!(handle->flags & UV_HANDLE_PIPESERVER));
if (handle->flags & UV_HANDLE_CLOSING)
return UV_EINVAL; return UV_EINVAL;
if (handle->handle != INVALID_HANDLE_VALUE) if (handle->handle != INVALID_HANDLE_VALUE)
return UV_EBUSY; return UV_EBUSY;
@ -478,18 +477,17 @@ static int uv__set_pipe_handle(uv_loop_t* loop,
*/ */
if (!GetNamedPipeHandleState(pipeHandle, &current_mode, NULL, NULL, if (!GetNamedPipeHandleState(pipeHandle, &current_mode, NULL, NULL,
NULL, NULL, 0)) { NULL, NULL, 0)) {
return -1; return uv_translate_sys_error(GetLastError());
} else if (current_mode & PIPE_NOWAIT) { } else if (current_mode & PIPE_NOWAIT) {
SetLastError(ERROR_ACCESS_DENIED); return UV_EACCES;
return -1;
} }
} else { } else {
/* If this returns ERROR_INVALID_PARAMETER we probably opened /* If this returns ERROR_INVALID_PARAMETER we probably opened
* something that is not a pipe. */ * something that is not a pipe. */
if (err == ERROR_INVALID_PARAMETER) { if (err == ERROR_INVALID_PARAMETER) {
SetLastError(WSAENOTSOCK); return UV_ENOTSOCK;
} }
return -1; return uv_translate_sys_error(err);
} }
} }
@ -500,13 +498,15 @@ static int uv__set_pipe_handle(uv_loop_t* loop,
sizeof(mode_info), sizeof(mode_info),
FileModeInformation); FileModeInformation);
if (nt_status != STATUS_SUCCESS) { if (nt_status != STATUS_SUCCESS) {
return -1; return uv_translate_sys_error(err);
} }
if (mode_info.Mode & FILE_SYNCHRONOUS_IO_ALERT || if (mode_info.Mode & FILE_SYNCHRONOUS_IO_ALERT ||
mode_info.Mode & FILE_SYNCHRONOUS_IO_NONALERT) { mode_info.Mode & FILE_SYNCHRONOUS_IO_NONALERT) {
/* Non-overlapped pipe. */ /* Non-overlapped pipe. */
handle->flags |= UV_HANDLE_NON_OVERLAPPED_PIPE; handle->flags |= UV_HANDLE_NON_OVERLAPPED_PIPE;
handle->pipe.conn.readfile_thread_handle = NULL;
InitializeCriticalSection(&handle->pipe.conn.readfile_thread_lock);
} else { } else {
/* Overlapped pipe. Try to associate with IOCP. */ /* Overlapped pipe. Try to associate with IOCP. */
if (CreateIoCompletionPort(pipeHandle, if (CreateIoCompletionPort(pipeHandle,
@ -578,135 +578,109 @@ static DWORD WINAPI pipe_shutdown_thread_proc(void* parameter) {
} }
void uv__pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { void uv__pipe_shutdown(uv_loop_t* loop, uv_pipe_t* handle, uv_shutdown_t *req) {
int err;
DWORD result; DWORD result;
uv_shutdown_t* req;
NTSTATUS nt_status; NTSTATUS nt_status;
IO_STATUS_BLOCK io_status; IO_STATUS_BLOCK io_status;
FILE_PIPE_LOCAL_INFORMATION pipe_info; FILE_PIPE_LOCAL_INFORMATION pipe_info;
assert(handle->flags & UV_HANDLE_CONNECTION);
assert(req != NULL);
assert(handle->stream.conn.write_reqs_pending == 0);
SET_REQ_SUCCESS(req);
if (handle->flags & UV_HANDLE_CLOSING) {
uv__insert_pending_req(loop, (uv_req_t*) req);
return;
}
/* Try to avoid flushing the pipe buffer in the thread pool. */
nt_status = pNtQueryInformationFile(handle->handle,
&io_status,
&pipe_info,
sizeof pipe_info,
FilePipeLocalInformation);
if (nt_status != STATUS_SUCCESS) {
SET_REQ_ERROR(req, pRtlNtStatusToDosError(nt_status));
handle->flags |= UV_HANDLE_WRITABLE; /* Questionable. */
uv__insert_pending_req(loop, (uv_req_t*) req);
return;
}
if (pipe_info.OutboundQuota == pipe_info.WriteQuotaAvailable) {
/* Short-circuit, no need to call FlushFileBuffers:
* all writes have been read. */
uv__insert_pending_req(loop, (uv_req_t*) req);
return;
}
/* Run FlushFileBuffers in the thread pool. */
result = QueueUserWorkItem(pipe_shutdown_thread_proc,
req,
WT_EXECUTELONGFUNCTION);
if (!result) {
SET_REQ_ERROR(req, GetLastError());
handle->flags |= UV_HANDLE_WRITABLE; /* Questionable. */
uv__insert_pending_req(loop, (uv_req_t*) req);
return;
}
}
void uv__pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) {
uv__ipc_xfer_queue_item_t* xfer_queue_item; uv__ipc_xfer_queue_item_t* xfer_queue_item;
if ((handle->flags & UV_HANDLE_CONNECTION) && assert(handle->reqs_pending == 0);
handle->stream.conn.shutdown_req != NULL && assert(handle->flags & UV_HANDLE_CLOSING);
handle->stream.conn.write_reqs_pending == 0) { assert(!(handle->flags & UV_HANDLE_CLOSED));
req = handle->stream.conn.shutdown_req;
/* Clear the shutdown_req field so we don't go here again. */ if (handle->flags & UV_HANDLE_CONNECTION) {
handle->stream.conn.shutdown_req = NULL; /* Free pending sockets */
while (!QUEUE_EMPTY(&handle->pipe.conn.ipc_xfer_queue)) {
QUEUE* q;
SOCKET socket;
if (handle->flags & UV_HANDLE_CLOSING) { q = QUEUE_HEAD(&handle->pipe.conn.ipc_xfer_queue);
UNREGISTER_HANDLE_REQ(loop, handle, req); QUEUE_REMOVE(q);
xfer_queue_item = QUEUE_DATA(q, uv__ipc_xfer_queue_item_t, member);
/* Already closing. Cancel the shutdown. */ /* Materialize socket and close it */
if (req->cb) { socket = WSASocketW(FROM_PROTOCOL_INFO,
req->cb(req, UV_ECANCELED); FROM_PROTOCOL_INFO,
FROM_PROTOCOL_INFO,
&xfer_queue_item->xfer_info.socket_info,
0,
WSA_FLAG_OVERLAPPED);
uv__free(xfer_queue_item);
if (socket != INVALID_SOCKET)
closesocket(socket);
}
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) {
DECREASE_PENDING_REQ_COUNT(handle); CloseHandle(handle->read_req.event_handle);
return; handle->read_req.event_handle = NULL;
}
/* Try to avoid flushing the pipe buffer in the thread pool. */
nt_status = pNtQueryInformationFile(handle->handle,
&io_status,
&pipe_info,
sizeof pipe_info,
FilePipeLocalInformation);
if (nt_status != STATUS_SUCCESS) {
/* Failure */
UNREGISTER_HANDLE_REQ(loop, handle, req);
handle->flags |= UV_HANDLE_WRITABLE; /* Questionable */
if (req->cb) {
err = pRtlNtStatusToDosError(nt_status);
req->cb(req, uv_translate_sys_error(err));
} }
DECREASE_PENDING_REQ_COUNT(handle);
return;
} }
if (pipe_info.OutboundQuota == pipe_info.WriteQuotaAvailable) { if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)
/* Short-circuit, no need to call FlushFileBuffers. */ DeleteCriticalSection(&handle->pipe.conn.readfile_thread_lock);
uv__insert_pending_req(loop, (uv_req_t*) req);
return;
}
/* Run FlushFileBuffers in the thread pool. */
result = QueueUserWorkItem(pipe_shutdown_thread_proc,
req,
WT_EXECUTELONGFUNCTION);
if (result) {
return;
} else {
/* Failure. */
UNREGISTER_HANDLE_REQ(loop, handle, req);
handle->flags |= UV_HANDLE_WRITABLE; /* Questionable */
if (req->cb) {
err = GetLastError();
req->cb(req, uv_translate_sys_error(err));
}
DECREASE_PENDING_REQ_COUNT(handle);
return;
}
} }
if (handle->flags & UV_HANDLE_CLOSING && if (handle->flags & UV_HANDLE_PIPESERVER) {
handle->reqs_pending == 0) { assert(handle->pipe.serv.accept_reqs);
assert(!(handle->flags & UV_HANDLE_CLOSED)); uv__free(handle->pipe.serv.accept_reqs);
handle->pipe.serv.accept_reqs = NULL;
if (handle->flags & UV_HANDLE_CONNECTION) {
/* Free pending sockets */
while (!QUEUE_EMPTY(&handle->pipe.conn.ipc_xfer_queue)) {
QUEUE* q;
SOCKET socket;
q = QUEUE_HEAD(&handle->pipe.conn.ipc_xfer_queue);
QUEUE_REMOVE(q);
xfer_queue_item = QUEUE_DATA(q, uv__ipc_xfer_queue_item_t, member);
/* Materialize socket and close it */
socket = WSASocketW(FROM_PROTOCOL_INFO,
FROM_PROTOCOL_INFO,
FROM_PROTOCOL_INFO,
&xfer_queue_item->xfer_info.socket_info,
0,
WSA_FLAG_OVERLAPPED);
uv__free(xfer_queue_item);
if (socket != INVALID_SOCKET)
closesocket(socket);
}
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;
}
}
if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)
DeleteCriticalSection(&handle->pipe.conn.readfile_thread_lock);
}
if (handle->flags & UV_HANDLE_PIPESERVER) {
assert(handle->pipe.serv.accept_reqs);
uv__free(handle->pipe.serv.accept_reqs);
handle->pipe.serv.accept_reqs = NULL;
}
uv__handle_close(handle);
} }
uv__handle_close(handle);
} }
@ -731,7 +705,9 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) {
if (!name) { if (!name) {
return UV_EINVAL; return UV_EINVAL;
} }
if (uv__is_closing(handle)) {
return UV_EINVAL;
}
if (!(handle->flags & UV_HANDLE_PIPESERVER)) { if (!(handle->flags & UV_HANDLE_PIPESERVER)) {
handle->pipe.serv.pending_instances = default_pending_pipe_instances; handle->pipe.serv.pending_instances = default_pending_pipe_instances;
} }
@ -815,7 +791,7 @@ static DWORD WINAPI pipe_connect_thread_proc(void* parameter) {
assert(loop); assert(loop);
/* We're here because CreateFile on a pipe returned ERROR_PIPE_BUSY. We wait /* We're here because CreateFile on a pipe returned ERROR_PIPE_BUSY. We wait
* for the pipe to become available with WaitNamedPipe. */ * up to 30 seconds for the pipe to become available with WaitNamedPipe. */
while (WaitNamedPipeW(handle->name, 30000)) { while (WaitNamedPipeW(handle->name, 30000)) {
/* The pipe is now available, try to connect. */ /* The pipe is now available, try to connect. */
pipeHandle = open_named_pipe(handle->name, &duplex_flags); pipeHandle = open_named_pipe(handle->name, &duplex_flags);
@ -825,9 +801,10 @@ static DWORD WINAPI pipe_connect_thread_proc(void* parameter) {
SwitchToThread(); SwitchToThread();
} }
if (pipeHandle != INVALID_HANDLE_VALUE && if (pipeHandle != INVALID_HANDLE_VALUE) {
!uv__set_pipe_handle(loop, handle, pipeHandle, -1, duplex_flags)) {
SET_REQ_SUCCESS(req); SET_REQ_SUCCESS(req);
req->u.connect.pipeHandle = pipeHandle;
req->u.connect.duplex_flags = duplex_flags;
} else { } else {
SET_REQ_ERROR(req, GetLastError()); SET_REQ_ERROR(req, GetLastError());
} }
@ -849,6 +826,18 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle,
UV_REQ_INIT(req, UV_CONNECT); UV_REQ_INIT(req, UV_CONNECT);
req->handle = (uv_stream_t*) handle; req->handle = (uv_stream_t*) handle;
req->cb = cb; req->cb = cb;
req->u.connect.pipeHandle = INVALID_HANDLE_VALUE;
req->u.connect.duplex_flags = 0;
if (handle->flags & UV_HANDLE_PIPESERVER) {
err = ERROR_INVALID_PARAMETER;
goto error;
}
if (handle->flags & UV_HANDLE_CONNECTION) {
err = ERROR_PIPE_BUSY;
goto error;
}
uv__pipe_connection_init(handle);
/* Convert name to UTF16. */ /* Convert name to UTF16. */
nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR); nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR);
@ -888,17 +877,8 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle,
goto error; goto error;
} }
assert(pipeHandle != INVALID_HANDLE_VALUE); req->u.connect.pipeHandle = pipeHandle;
req->u.connect.duplex_flags = duplex_flags;
if (uv__set_pipe_handle(loop,
(uv_pipe_t*) req->handle,
pipeHandle,
-1,
duplex_flags)) {
err = GetLastError();
goto error;
}
SET_REQ_SUCCESS(req); SET_REQ_SUCCESS(req);
uv__insert_pending_req(loop, (uv_req_t*) req); uv__insert_pending_req(loop, (uv_req_t*) req);
handle->reqs_pending++; handle->reqs_pending++;
@ -937,7 +917,7 @@ void uv__pipe_interrupt_read(uv_pipe_t* handle) {
/* Cancel asynchronous read. */ /* Cancel asynchronous read. */
r = CancelIoEx(handle->handle, &handle->read_req.u.io.overlapped); r = CancelIoEx(handle->handle, &handle->read_req.u.io.overlapped);
assert(r || GetLastError() == ERROR_NOT_FOUND); assert(r || GetLastError() == ERROR_NOT_FOUND);
(void) r;
} else { } else {
/* Cancel synchronous read (which is happening in the thread pool). */ /* Cancel synchronous read (which is happening in the thread pool). */
HANDLE thread; HANDLE thread;
@ -973,17 +953,30 @@ void uv__pipe_interrupt_read(uv_pipe_t* handle) {
void uv__pipe_read_stop(uv_pipe_t* handle) { void uv__pipe_read_stop(uv_pipe_t* handle) {
handle->flags &= ~UV_HANDLE_READING; handle->flags &= ~UV_HANDLE_READING;
DECREASE_ACTIVE_COUNT(handle->loop, handle); DECREASE_ACTIVE_COUNT(handle->loop, handle);
uv__pipe_interrupt_read(handle); uv__pipe_interrupt_read(handle);
} }
/* Cleans up uv_pipe_t (server or connection) and all resources associated with /* Cleans up uv_pipe_t (server or connection) and all resources associated with
* it. */ * it. */
void uv__pipe_cleanup(uv_loop_t* loop, uv_pipe_t* handle) { void uv__pipe_close(uv_loop_t* loop, uv_pipe_t* handle) {
int i; int i;
HANDLE pipeHandle; HANDLE pipeHandle;
if (handle->flags & UV_HANDLE_READING) {
handle->flags &= ~UV_HANDLE_READING;
DECREASE_ACTIVE_COUNT(loop, handle);
}
if (handle->flags & UV_HANDLE_LISTENING) {
handle->flags &= ~UV_HANDLE_LISTENING;
DECREASE_ACTIVE_COUNT(loop, handle);
}
handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
uv__handle_closing(handle);
uv__pipe_interrupt_read(handle); uv__pipe_interrupt_read(handle);
if (handle->name) { if (handle->name) {
@ -1003,35 +996,17 @@ void uv__pipe_cleanup(uv_loop_t* loop, uv_pipe_t* handle) {
} }
if (handle->flags & UV_HANDLE_CONNECTION) { if (handle->flags & UV_HANDLE_CONNECTION) {
handle->flags &= ~UV_HANDLE_WRITABLE;
eof_timer_destroy(handle); eof_timer_destroy(handle);
} }
if ((handle->flags & UV_HANDLE_CONNECTION) if ((handle->flags & UV_HANDLE_CONNECTION)
&& handle->handle != INVALID_HANDLE_VALUE) && handle->handle != INVALID_HANDLE_VALUE) {
/* This will eventually destroy the write queue for us too. */
close_pipe(handle); close_pipe(handle);
}
void uv__pipe_close(uv_loop_t* loop, uv_pipe_t* handle) {
if (handle->flags & UV_HANDLE_READING) {
handle->flags &= ~UV_HANDLE_READING;
DECREASE_ACTIVE_COUNT(loop, handle);
} }
if (handle->flags & UV_HANDLE_LISTENING) { if (handle->reqs_pending == 0)
handle->flags &= ~UV_HANDLE_LISTENING;
DECREASE_ACTIVE_COUNT(loop, handle);
}
uv__pipe_cleanup(loop, handle);
if (handle->reqs_pending == 0) {
uv__want_endgame(loop, (uv_handle_t*) handle); uv__want_endgame(loop, (uv_handle_t*) handle);
}
handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
uv__handle_closing(handle);
} }
@ -1099,6 +1074,7 @@ int uv__pipe_accept(uv_pipe_t* server, uv_stream_t* client) {
} else { } else {
pipe_client = (uv_pipe_t*) client; pipe_client = (uv_pipe_t*) client;
uv__pipe_connection_init(pipe_client);
/* Find a connection instance that has been connected, but not yet /* Find a connection instance that has been connected, but not yet
* accepted. */ * accepted. */
@ -1110,7 +1086,6 @@ int uv__pipe_accept(uv_pipe_t* server, uv_stream_t* client) {
} }
/* Initialize the client handle and copy the pipeHandle to the client */ /* Initialize the client handle and copy the pipeHandle to the client */
uv__pipe_connection_init(pipe_client);
pipe_client->handle = req->pipeHandle; pipe_client->handle = req->pipeHandle;
pipe_client->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; pipe_client->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
@ -2094,10 +2069,9 @@ void uv__process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle,
uv__queue_non_overlapped_write(handle); uv__queue_non_overlapped_write(handle);
} }
if (handle->stream.conn.shutdown_req != NULL && if (handle->stream.conn.write_reqs_pending == 0)
handle->stream.conn.write_reqs_pending == 0) { if (handle->flags & UV_HANDLE_SHUTTING)
uv__want_endgame(loop, (uv_handle_t*)handle); uv__pipe_shutdown(loop, handle, handle->stream.conn.shutdown_req);
}
DECREASE_PENDING_REQ_COUNT(handle); DECREASE_PENDING_REQ_COUNT(handle);
} }
@ -2110,7 +2084,7 @@ void uv__process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle,
assert(handle->type == UV_NAMED_PIPE); assert(handle->type == UV_NAMED_PIPE);
if (handle->flags & UV_HANDLE_CLOSING) { if (handle->flags & UV_HANDLE_CLOSING) {
/* The req->pipeHandle should be freed already in uv__pipe_cleanup(). */ /* The req->pipeHandle should be freed already in uv__pipe_close(). */
assert(req->pipeHandle == INVALID_HANDLE_VALUE); assert(req->pipeHandle == INVALID_HANDLE_VALUE);
DECREASE_PENDING_REQ_COUNT(handle); DECREASE_PENDING_REQ_COUNT(handle);
return; return;
@ -2140,52 +2114,72 @@ void uv__process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle,
void uv__process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle, void uv__process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle,
uv_connect_t* req) { uv_connect_t* req) {
HANDLE pipeHandle;
DWORD duplex_flags;
int err; int err;
assert(handle->type == UV_NAMED_PIPE); assert(handle->type == UV_NAMED_PIPE);
UNREGISTER_HANDLE_REQ(loop, handle, req); UNREGISTER_HANDLE_REQ(loop, handle, req);
if (req->cb) { err = 0;
err = 0; if (REQ_SUCCESS(req)) {
if (REQ_SUCCESS(req)) { pipeHandle = req->u.connect.pipeHandle;
uv__pipe_connection_init(handle); duplex_flags = req->u.connect.duplex_flags;
} else { err = uv__set_pipe_handle(loop, handle, pipeHandle, -1, duplex_flags);
err = GET_REQ_ERROR(req); if (err)
} CloseHandle(pipeHandle);
req->cb(req, uv_translate_sys_error(err)); } else {
err = uv_translate_sys_error(GET_REQ_ERROR(req));
} }
if (req->cb)
req->cb(req, err);
DECREASE_PENDING_REQ_COUNT(handle); DECREASE_PENDING_REQ_COUNT(handle);
} }
void uv__process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle, void uv__process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle,
uv_shutdown_t* req) { uv_shutdown_t* req) {
int err;
assert(handle->type == UV_NAMED_PIPE); assert(handle->type == UV_NAMED_PIPE);
/* Clear the shutdown_req field so we don't go here again. */
handle->stream.conn.shutdown_req = NULL;
handle->flags &= ~UV_HANDLE_SHUTTING;
UNREGISTER_HANDLE_REQ(loop, handle, req); UNREGISTER_HANDLE_REQ(loop, handle, req);
if (handle->flags & UV_HANDLE_READABLE) { if (handle->flags & UV_HANDLE_CLOSING) {
/* Initialize and optionally start the eof timer. Only do this if the pipe /* Already closing. Cancel the shutdown. */
* is readable and we haven't seen EOF come in ourselves. */ err = UV_ECANCELED;
eof_timer_init(handle); } else if (!REQ_SUCCESS(req)) {
/* An error occurred in trying to shutdown gracefully. */
/* If reading start the timer right now. Otherwise uv__pipe_queue_read will err = uv_translate_sys_error(GET_REQ_ERROR(req));
* start it. */
if (handle->flags & UV_HANDLE_READ_PENDING) {
eof_timer_start(handle);
}
} else { } else {
/* This pipe is not readable. We can just close it to let the other end if (handle->flags & UV_HANDLE_READABLE) {
* know that we're done writing. */ /* Initialize and optionally start the eof timer. Only do this if the pipe
close_pipe(handle); * is readable and we haven't seen EOF come in ourselves. */
eof_timer_init(handle);
/* If reading start the timer right now. Otherwise uv__pipe_queue_read will
* start it. */
if (handle->flags & UV_HANDLE_READ_PENDING) {
eof_timer_start(handle);
}
} else {
/* This pipe is not readable. We can just close it to let the other end
* know that we're done writing. */
close_pipe(handle);
}
err = 0;
} }
if (req->cb) { if (req->cb)
req->cb(req, 0); req->cb(req, err);
}
DECREASE_PENDING_REQ_COUNT(handle); DECREASE_PENDING_REQ_COUNT(handle);
} }
@ -2200,7 +2194,8 @@ static void eof_timer_init(uv_pipe_t* pipe) {
pipe->pipe.conn.eof_timer = (uv_timer_t*) uv__malloc(sizeof *pipe->pipe.conn.eof_timer); pipe->pipe.conn.eof_timer = (uv_timer_t*) uv__malloc(sizeof *pipe->pipe.conn.eof_timer);
r = uv_timer_init(pipe->loop, pipe->pipe.conn.eof_timer); r = uv_timer_init(pipe->loop, pipe->pipe.conn.eof_timer);
assert(r == 0); /* timers can't fail */ assert(r == 0); /* timers can't fail */
(void) r;
pipe->pipe.conn.eof_timer->data = pipe; pipe->pipe.conn.eof_timer->data = pipe;
uv_unref((uv_handle_t*) pipe->pipe.conn.eof_timer); uv_unref((uv_handle_t*) pipe->pipe.conn.eof_timer);
} }
@ -2280,10 +2275,16 @@ int uv_pipe_open(uv_pipe_t* pipe, uv_file file) {
IO_STATUS_BLOCK io_status; IO_STATUS_BLOCK io_status;
FILE_ACCESS_INFORMATION access; FILE_ACCESS_INFORMATION access;
DWORD duplex_flags = 0; DWORD duplex_flags = 0;
int err;
if (os_handle == INVALID_HANDLE_VALUE) if (os_handle == INVALID_HANDLE_VALUE)
return UV_EBADF; return UV_EBADF;
if (pipe->flags & UV_HANDLE_PIPESERVER)
return UV_EINVAL;
if (pipe->flags & UV_HANDLE_CONNECTION)
return UV_EBUSY;
uv__pipe_connection_init(pipe);
uv__once_init(); uv__once_init();
/* In order to avoid closing a stdio file descriptor 0-2, duplicate the /* In order to avoid closing a stdio file descriptor 0-2, duplicate the
* underlying OS handle and forget about the original fd. * underlying OS handle and forget about the original fd.
@ -2300,6 +2301,7 @@ int uv_pipe_open(uv_pipe_t* pipe, uv_file file) {
FALSE, FALSE,
DUPLICATE_SAME_ACCESS)) DUPLICATE_SAME_ACCESS))
return uv_translate_sys_error(GetLastError()); return uv_translate_sys_error(GetLastError());
assert(os_handle != INVALID_HANDLE_VALUE);
file = -1; file = -1;
} }
@ -2327,17 +2329,17 @@ int uv_pipe_open(uv_pipe_t* pipe, uv_file file) {
if (access.AccessFlags & FILE_READ_DATA) if (access.AccessFlags & FILE_READ_DATA)
duplex_flags |= UV_HANDLE_READABLE; duplex_flags |= UV_HANDLE_READABLE;
if (os_handle == INVALID_HANDLE_VALUE || err = uv__set_pipe_handle(pipe->loop,
uv__set_pipe_handle(pipe->loop, pipe,
pipe, os_handle,
os_handle, file,
file, duplex_flags);
duplex_flags) == -1) { if (err) {
return UV_EINVAL; if (file == -1)
CloseHandle(os_handle);
return err;
} }
uv__pipe_connection_init(pipe);
if (pipe->ipc) { if (pipe->ipc) {
assert(!(pipe->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)); assert(!(pipe->flags & UV_HANDLE_NON_OVERLAPPED_PIPE));
pipe->pipe.conn.ipc_remote_pid = uv_os_getppid(); pipe->pipe.conn.ipc_remote_pid = uv_os_getppid();
@ -2361,6 +2363,51 @@ static int uv__pipe_getname(const uv_pipe_t* handle, char* buffer, size_t* size)
uv__once_init(); uv__once_init();
name_info = NULL; name_info = NULL;
if (handle->name != NULL) {
/* The user might try to query the name before we are connected,
* and this is just easier to return the cached value if we have it. */
name_buf = handle->name;
name_len = wcslen(name_buf);
/* check how much space we need */
addrlen = WideCharToMultiByte(CP_UTF8,
0,
name_buf,
name_len,
NULL,
0,
NULL,
NULL);
if (!addrlen) {
*size = 0;
err = uv_translate_sys_error(GetLastError());
return err;
} else if (addrlen >= *size) {
*size = addrlen + 1;
err = UV_ENOBUFS;
goto error;
}
addrlen = WideCharToMultiByte(CP_UTF8,
0,
name_buf,
name_len,
buffer,
addrlen,
NULL,
NULL);
if (!addrlen) {
*size = 0;
err = uv_translate_sys_error(GetLastError());
return err;
}
*size = addrlen;
buffer[addrlen] = '\0';
return 0;
}
if (handle->handle == INVALID_HANDLE_VALUE) { if (handle->handle == INVALID_HANDLE_VALUE) {
*size = 0; *size = 0;
return UV_EINVAL; return UV_EINVAL;
@ -2498,6 +2545,11 @@ int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size) {
if (handle->handle != INVALID_HANDLE_VALUE) if (handle->handle != INVALID_HANDLE_VALUE)
return uv__pipe_getname(handle, buffer, size); return uv__pipe_getname(handle, buffer, size);
if (handle->flags & UV_HANDLE_CONNECTION) {
if (handle->name != NULL)
return uv__pipe_getname(handle, buffer, size);
}
return UV_EBADF; return UV_EBADF;
} }

View File

@ -138,13 +138,13 @@ INLINE static void uv__insert_pending_req(uv_loop_t* loop, uv_req_t* req) {
} while (0) } while (0)
INLINE static int uv__process_reqs(uv_loop_t* loop) { INLINE static void uv__process_reqs(uv_loop_t* loop) {
uv_req_t* req; uv_req_t* req;
uv_req_t* first; uv_req_t* first;
uv_req_t* next; uv_req_t* next;
if (loop->pending_reqs_tail == NULL) if (loop->pending_reqs_tail == NULL)
return 0; return;
first = loop->pending_reqs_tail->next_req; first = loop->pending_reqs_tail->next_req;
next = first; next = first;
@ -172,12 +172,7 @@ INLINE static int uv__process_reqs(uv_loop_t* loop) {
break; break;
case UV_SHUTDOWN: case UV_SHUTDOWN:
/* Tcp shutdown requests don't come here. */ DELEGATE_STREAM_REQ(loop, (uv_shutdown_t*) req, shutdown, handle);
assert(((uv_shutdown_t*) req)->handle->type == UV_NAMED_PIPE);
uv__process_pipe_shutdown_req(
loop,
(uv_pipe_t*) ((uv_shutdown_t*) req)->handle,
(uv_shutdown_t*) req);
break; break;
case UV_UDP_RECV: case UV_UDP_RECV:
@ -214,8 +209,6 @@ INLINE static int uv__process_reqs(uv_loop_t* loop) {
assert(0); assert(0);
} }
} }
return 1;
} }
#endif /* UV_WIN_REQ_INL_H_ */ #endif /* UV_WIN_REQ_INL_H_ */

View File

@ -29,7 +29,9 @@
int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) { int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) {
int err; int err;
if (uv__is_closing(stream)) {
return UV_EINVAL;
}
err = ERROR_INVALID_PARAMETER; err = ERROR_INVALID_PARAMETER;
switch (stream->type) { switch (stream->type) {
case UV_TCP: case UV_TCP:
@ -217,7 +219,12 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) {
handle->reqs_pending++; handle->reqs_pending++;
REGISTER_HANDLE_REQ(loop, handle, req); REGISTER_HANDLE_REQ(loop, handle, req);
uv__want_endgame(loop, (uv_handle_t*)handle); if (handle->stream.conn.write_reqs_pending == 0) {
if (handle->type == UV_NAMED_PIPE)
uv__pipe_shutdown(loop, (uv_pipe_t*) handle, req);
else
uv__insert_pending_req(loop, (uv_req_t*) req);
}
return 0; return 0;
} }

View File

@ -205,73 +205,76 @@ int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* handle) {
} }
void uv__tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { void uv__process_tcp_shutdown_req(uv_loop_t* loop, uv_tcp_t* stream, uv_shutdown_t *req) {
int err; int err;
assert(req);
assert(stream->stream.conn.write_reqs_pending == 0);
assert(!(stream->flags & UV_HANDLE_SHUT));
assert(stream->flags & UV_HANDLE_CONNECTION);
stream->stream.conn.shutdown_req = NULL;
stream->flags &= ~UV_HANDLE_SHUTTING;
UNREGISTER_HANDLE_REQ(loop, stream, req);
err = 0;
if (stream->flags & UV_HANDLE_CLOSING)
/* The user destroyed the stream before we got to do the shutdown. */
err = UV_ECANCELED;
else if (shutdown(stream->socket, SD_SEND) == SOCKET_ERROR)
err = uv_translate_sys_error(WSAGetLastError());
else /* Success. */
stream->flags |= UV_HANDLE_SHUT;
if (req->cb)
req->cb(req, err);
DECREASE_PENDING_REQ_COUNT(stream);
}
void uv__tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) {
unsigned int i; unsigned int i;
uv_tcp_accept_t* req; uv_tcp_accept_t* req;
if (handle->flags & UV_HANDLE_CONNECTION && assert(handle->flags & UV_HANDLE_CLOSING);
handle->stream.conn.shutdown_req != NULL && assert(handle->reqs_pending == 0);
handle->stream.conn.write_reqs_pending == 0) { assert(!(handle->flags & UV_HANDLE_CLOSED));
assert(handle->socket == INVALID_SOCKET);
UNREGISTER_HANDLE_REQ(loop, handle, handle->stream.conn.shutdown_req); if (!(handle->flags & UV_HANDLE_CONNECTION) && handle->tcp.serv.accept_reqs) {
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
err = 0; for (i = 0; i < uv_simultaneous_server_accepts; i++) {
if (handle->flags & UV_HANDLE_CLOSING) { req = &handle->tcp.serv.accept_reqs[i];
err = ERROR_OPERATION_ABORTED; if (req->wait_handle != INVALID_HANDLE_VALUE) {
} else if (shutdown(handle->socket, SD_SEND) == SOCKET_ERROR) { UnregisterWait(req->wait_handle);
err = WSAGetLastError(); req->wait_handle = INVALID_HANDLE_VALUE;
} }
if (req->event_handle != NULL) {
if (handle->stream.conn.shutdown_req->cb) { CloseHandle(req->event_handle);
handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req, req->event_handle = NULL;
uv_translate_sys_error(err));
}
handle->stream.conn.shutdown_req = NULL;
DECREASE_PENDING_REQ_COUNT(handle);
return;
}
if (handle->flags & UV_HANDLE_CLOSING &&
handle->reqs_pending == 0) {
assert(!(handle->flags & UV_HANDLE_CLOSED));
assert(handle->socket == INVALID_SOCKET);
if (!(handle->flags & UV_HANDLE_CONNECTION) && handle->tcp.serv.accept_reqs) {
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
for (i = 0; i < uv_simultaneous_server_accepts; i++) {
req = &handle->tcp.serv.accept_reqs[i];
if (req->wait_handle != INVALID_HANDLE_VALUE) {
UnregisterWait(req->wait_handle);
req->wait_handle = INVALID_HANDLE_VALUE;
}
if (req->event_handle != NULL) {
CloseHandle(req->event_handle);
req->event_handle = NULL;
}
} }
} }
uv__free(handle->tcp.serv.accept_reqs);
handle->tcp.serv.accept_reqs = NULL;
} }
if (handle->flags & UV_HANDLE_CONNECTION && uv__free(handle->tcp.serv.accept_reqs);
handle->flags & UV_HANDLE_EMULATE_IOCP) { handle->tcp.serv.accept_reqs = NULL;
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;
}
}
uv__handle_close(handle);
loop->active_tcp_streams--;
} }
if (handle->flags & UV_HANDLE_CONNECTION &&
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;
}
}
uv__handle_close(handle);
loop->active_tcp_streams--;
} }
@ -1160,9 +1163,10 @@ void uv__process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle,
closesocket(handle->socket); closesocket(handle->socket);
handle->socket = INVALID_SOCKET; handle->socket = INVALID_SOCKET;
} }
if (handle->stream.conn.shutdown_req != NULL) { if (handle->flags & UV_HANDLE_SHUTTING)
uv__want_endgame(loop, (uv_handle_t*)handle); uv__process_tcp_shutdown_req(loop,
} handle,
handle->stream.conn.shutdown_req);
} }
DECREASE_PENDING_REQ_COUNT(handle); DECREASE_PENDING_REQ_COUNT(handle);
@ -1411,7 +1415,7 @@ static void uv__tcp_try_cancel_reqs(uv_tcp_t* tcp) {
int writing; int writing;
socket = tcp->socket; socket = tcp->socket;
reading = tcp->flags & UV_HANDLE_READING; reading = tcp->flags & UV_HANDLE_READ_PENDING;
writing = tcp->stream.conn.write_reqs_pending > 0; writing = tcp->stream.conn.write_reqs_pending > 0;
if (!reading && !writing) if (!reading && !writing)
return; return;
@ -1458,10 +1462,10 @@ static void uv__tcp_try_cancel_reqs(uv_tcp_t* tcp) {
void uv__tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) { void uv__tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) {
if (tcp->flags & UV_HANDLE_CONNECTION) { if (tcp->flags & UV_HANDLE_CONNECTION) {
uv__tcp_try_cancel_reqs(tcp);
if (tcp->flags & UV_HANDLE_READING) { if (tcp->flags & UV_HANDLE_READING) {
uv_read_stop((uv_stream_t*) tcp); uv_read_stop((uv_stream_t*) tcp);
} }
uv__tcp_try_cancel_reqs(tcp);
} else { } else {
if (tcp->tcp.serv.accept_reqs != NULL) { if (tcp->tcp.serv.accept_reqs != NULL) {
/* First close the incoming sockets to cancel the accept operations before /* First close the incoming sockets to cancel the accept operations before
@ -1483,6 +1487,9 @@ void uv__tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) {
DECREASE_ACTIVE_COUNT(loop, tcp); DECREASE_ACTIVE_COUNT(loop, tcp);
} }
tcp->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
uv__handle_closing(tcp);
/* If any overlapped req failed to cancel, calling `closesocket` now would /* If any overlapped req failed to cancel, calling `closesocket` now would
* cause Win32 to send an RST packet. Try to avoid that for writes, if * cause Win32 to send an RST packet. Try to avoid that for writes, if
* possibly applicable, by waiting to process the completion notifications * possibly applicable, by waiting to process the completion notifications
@ -1494,12 +1501,8 @@ void uv__tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) {
tcp->socket = INVALID_SOCKET; tcp->socket = INVALID_SOCKET;
} }
tcp->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); if (tcp->reqs_pending == 0)
uv__handle_closing(tcp); uv__want_endgame(loop, (uv_handle_t*) tcp);
if (tcp->reqs_pending == 0) {
uv__want_endgame(tcp->loop, (uv_handle_t*)tcp);
}
} }

View File

@ -2237,11 +2237,13 @@ void uv__process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
req->cb(req, uv_translate_sys_error(err)); req->cb(req, uv_translate_sys_error(err));
} }
handle->stream.conn.write_reqs_pending--; handle->stream.conn.write_reqs_pending--;
if (handle->stream.conn.shutdown_req != NULL && if (handle->stream.conn.write_reqs_pending == 0)
handle->stream.conn.write_reqs_pending == 0) { if (handle->flags & UV_HANDLE_SHUTTING)
uv__want_endgame(loop, (uv_handle_t*)handle); uv__process_tty_shutdown_req(loop,
} handle,
handle->stream.conn.shutdown_req);
DECREASE_PENDING_REQ_COUNT(handle); DECREASE_PENDING_REQ_COUNT(handle);
} }
@ -2262,43 +2264,43 @@ void uv__tty_close(uv_tty_t* handle) {
handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
uv__handle_closing(handle); uv__handle_closing(handle);
if (handle->reqs_pending == 0) { if (handle->reqs_pending == 0)
uv__want_endgame(handle->loop, (uv_handle_t*) handle); uv__want_endgame(handle->loop, (uv_handle_t*) handle);
}
void uv__process_tty_shutdown_req(uv_loop_t* loop, uv_tty_t* stream, uv_shutdown_t* req) {
assert(stream->stream.conn.write_reqs_pending == 0);
assert(req);
stream->stream.conn.shutdown_req = NULL;
stream->flags &= ~UV_HANDLE_SHUTTING;
UNREGISTER_HANDLE_REQ(loop, stream, req);
/* TTY shutdown is really just a no-op */
if (req->cb) {
if (stream->flags & UV_HANDLE_CLOSING) {
req->cb(req, UV_ECANCELED);
} else {
req->cb(req, 0);
}
} }
DECREASE_PENDING_REQ_COUNT(stream);
} }
void uv__tty_endgame(uv_loop_t* loop, uv_tty_t* handle) { void uv__tty_endgame(uv_loop_t* loop, uv_tty_t* handle) {
if (!(handle->flags & UV_HANDLE_TTY_READABLE) && assert(handle->flags & UV_HANDLE_CLOSING);
handle->stream.conn.shutdown_req != NULL && assert(handle->reqs_pending == 0);
handle->stream.conn.write_reqs_pending == 0) {
UNREGISTER_HANDLE_REQ(loop, handle, handle->stream.conn.shutdown_req);
/* TTY shutdown is really just a no-op */ /* The wait handle used for raw reading should be unregistered when the
if (handle->stream.conn.shutdown_req->cb) { * wait callback runs. */
if (handle->flags & UV_HANDLE_CLOSING) { assert(!(handle->flags & UV_HANDLE_TTY_READABLE) ||
handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req, UV_ECANCELED); handle->tty.rd.read_raw_wait == NULL);
} else {
handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req, 0);
}
}
handle->stream.conn.shutdown_req = NULL; assert(!(handle->flags & UV_HANDLE_CLOSED));
uv__handle_close(handle);
DECREASE_PENDING_REQ_COUNT(handle);
return;
}
if (handle->flags & UV_HANDLE_CLOSING &&
handle->reqs_pending == 0) {
/* The wait handle used for raw reading should be unregistered when the
* wait callback runs. */
assert(!(handle->flags & UV_HANDLE_TTY_READABLE) ||
handle->tty.rd.read_raw_wait == NULL);
assert(!(handle->flags & UV_HANDLE_CLOSED));
uv__handle_close(handle);
}
} }

View File

@ -1087,7 +1087,7 @@ int uv__udp_disconnect(uv_udp_t* handle) {
memset(&addr, 0, sizeof(addr)); memset(&addr, 0, sizeof(addr));
err = connect(handle->socket, &addr, sizeof(addr)); err = connect(handle->socket, (struct sockaddr*) &addr, sizeof(addr));
if (err) if (err)
return uv_translate_sys_error(WSAGetLastError()); return uv_translate_sys_error(WSAGetLastError());
@ -1146,6 +1146,7 @@ int uv__udp_try_send(uv_udp_t* handle,
err = uv__convert_to_localhost_if_unspecified(addr, &converted); err = uv__convert_to_localhost_if_unspecified(addr, &converted);
if (err) if (err)
return err; return err;
addr = (const struct sockaddr*) &converted;
} }
/* Already sending a message.*/ /* Already sending a message.*/
@ -1169,7 +1170,7 @@ int uv__udp_try_send(uv_udp_t* handle,
nbufs, nbufs,
&bytes, &bytes,
0, 0,
(const struct sockaddr*) &converted, addr,
addrlen, addrlen,
NULL, NULL,
NULL); NULL);

View File

@ -1,4 +1,4 @@
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. /* Copyright libuv project contributors. All rights reserved.
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to * of this software and associated documentation files (the "Software"), to
@ -25,115 +25,55 @@
#include <stdlib.h> #include <stdlib.h>
#include <errno.h> #include <errno.h>
#ifndef HAVE_KQUEUE #if !defined(_WIN32) && !defined(_AIX)
# if defined(__APPLE__) || \ #include <poll.h>
defined(__DragonFly__) || \
defined(__FreeBSD__) || \
defined(__FreeBSD_kernel__) || \
defined(__OpenBSD__) || \
defined(__NetBSD__)
# define HAVE_KQUEUE 1
# endif
#endif #endif
#ifndef HAVE_EPOLL static uv_async_t async;
# if defined(__linux__) static uv_barrier_t barrier;
# define HAVE_EPOLL 1
# endif
#endif
#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL)
#if defined(HAVE_KQUEUE)
# include <sys/types.h>
# include <sys/event.h>
# include <sys/time.h>
#endif
#if defined(HAVE_EPOLL)
# include <sys/epoll.h>
#endif
static uv_thread_t embed_thread;
static uv_sem_t embed_sem;
static uv_timer_t embed_timer;
static uv_async_t embed_async;
static volatile int embed_closed;
static int embed_timer_called;
static void embed_thread_runner(void* arg) { static void thread_main(void* arg) {
int r; ASSERT_LE(0, uv_barrier_wait(&barrier));
int fd; uv_sleep(250);
int timeout; ASSERT_EQ(0, uv_async_send(&async));
while (!embed_closed) {
fd = uv_backend_fd(uv_default_loop());
timeout = uv_backend_timeout(uv_default_loop());
do {
#if defined(HAVE_KQUEUE)
struct timespec ts;
ts.tv_sec = timeout / 1000;
ts.tv_nsec = (timeout % 1000) * 1000000;
r = kevent(fd, NULL, 0, NULL, 0, &ts);
#elif defined(HAVE_EPOLL)
{
struct epoll_event ev;
r = epoll_wait(fd, &ev, 1, timeout);
}
#endif
} while (r == -1 && errno == EINTR);
uv_async_send(&embed_async);
uv_sem_wait(&embed_sem);
}
} }
static void embed_cb(uv_async_t* async) { static void async_cb(uv_async_t* handle) {
uv_run(uv_default_loop(), UV_RUN_ONCE); uv_close((uv_handle_t*) handle, NULL);
uv_sem_post(&embed_sem);
} }
static void embed_timer_cb(uv_timer_t* timer) {
embed_timer_called++;
embed_closed = 1;
uv_close((uv_handle_t*) &embed_async, NULL);
}
#endif
TEST_IMPL(embed) { TEST_IMPL(embed) {
#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL) uv_thread_t thread;
uv_loop_t external; uv_loop_t* loop;
ASSERT(0 == uv_loop_init(&external)); loop = uv_default_loop();
ASSERT_EQ(0, uv_async_init(loop, &async, async_cb));
ASSERT_EQ(0, uv_barrier_init(&barrier, 2));
ASSERT_EQ(0, uv_thread_create(&thread, thread_main, NULL));
ASSERT_LE(0, uv_barrier_wait(&barrier));
embed_timer_called = 0; while (uv_loop_alive(loop)) {
embed_closed = 0; #if defined(_WIN32) || defined(_AIX)
ASSERT_LE(0, uv_run(loop, UV_RUN_ONCE));
uv_async_init(&external, &embed_async, embed_cb); #else
int rc;
/* Start timer in default loop */ do {
uv_timer_init(uv_default_loop(), &embed_timer); struct pollfd p;
uv_timer_start(&embed_timer, embed_timer_cb, 250, 0); p.fd = uv_backend_fd(loop);
p.events = POLLIN;
/* Start worker that will interrupt external loop */ p.revents = 0;
uv_sem_init(&embed_sem, 0); rc = poll(&p, 1, uv_backend_timeout(loop));
uv_thread_create(&embed_thread, embed_thread_runner, NULL); } while (rc == -1 && errno == EINTR);
ASSERT_LE(0, uv_run(loop, UV_RUN_NOWAIT));
/* But run external loop */
uv_run(&external, UV_RUN_DEFAULT);
uv_thread_join(&embed_thread);
uv_loop_close(&external);
ASSERT(embed_timer_called == 1);
#endif #endif
}
ASSERT_EQ(0, uv_thread_join(&thread));
uv_barrier_destroy(&barrier);
MAKE_VALGRIND_HAPPY();
return 0; return 0;
} }

View File

@ -334,19 +334,8 @@ static void fs_event_cb_file(uv_fs_event_t* handle, const char* filename,
uv_close((uv_handle_t*)handle, close_cb); uv_close((uv_handle_t*)handle, close_cb);
} }
static void timer_cb_close_handle(uv_timer_t* timer) {
uv_handle_t* handle;
ASSERT_NOT_NULL(timer);
handle = timer->data;
uv_close((uv_handle_t*)timer, NULL);
uv_close((uv_handle_t*)handle, close_cb);
}
static void fs_event_cb_file_current_dir(uv_fs_event_t* handle, static void fs_event_cb_file_current_dir(uv_fs_event_t* handle,
const char* filename, int events, int status) { const char* filename, int events, int status) {
ASSERT(fs_event_cb_called == 0);
++fs_event_cb_called; ++fs_event_cb_called;
ASSERT(handle == &fs_event); ASSERT(handle == &fs_event);
@ -358,13 +347,7 @@ static void fs_event_cb_file_current_dir(uv_fs_event_t* handle,
ASSERT(filename == NULL || strcmp(filename, "watch_file") == 0); ASSERT(filename == NULL || strcmp(filename, "watch_file") == 0);
#endif #endif
/* Regression test for SunOS: touch should generate just one event. */ uv_close((uv_handle_t*)handle, close_cb);
{
static uv_timer_t timer;
uv_timer_init(handle->loop, &timer);
timer.data = handle;
uv_timer_start(&timer, timer_cb_close_handle, 250, 0);
}
} }
static void timer_cb_file(uv_timer_t* handle) { static void timer_cb_file(uv_timer_t* handle) {
@ -738,7 +721,8 @@ TEST_IMPL(fs_event_watch_file_current_dir) {
uv_run(loop, UV_RUN_DEFAULT); uv_run(loop, UV_RUN_DEFAULT);
ASSERT(timer_cb_touch_called == 1); ASSERT(timer_cb_touch_called == 1);
ASSERT(fs_event_cb_called == 1); /* FSEvents on macOS sometimes sends one change event, sometimes two. */
ASSERT_NE(0, fs_event_cb_called);
ASSERT(close_cb_called == 1); ASSERT(close_cb_called == 1);
/* Cleanup */ /* Cleanup */
@ -923,6 +907,44 @@ TEST_IMPL(fs_event_close_with_pending_event) {
return 0; return 0;
} }
TEST_IMPL(fs_event_close_with_pending_delete_event) {
#if defined(NO_FS_EVENTS)
RETURN_SKIP(NO_FS_EVENTS);
#endif
uv_loop_t* loop;
int r;
loop = uv_default_loop();
create_dir("watch_dir");
create_file("watch_dir/file");
r = uv_fs_event_init(loop, &fs_event);
ASSERT(r == 0);
r = uv_fs_event_start(&fs_event, fs_event_fail, "watch_dir/file", 0);
ASSERT(r == 0);
/* Generate an fs event. */
remove("watch_dir/file");
/* Allow time for the remove event to propagate to the pending list. */
/* XXX - perhaps just for __sun? */
uv_sleep(1100);
uv_update_time(loop);
uv_close((uv_handle_t*)&fs_event, close_cb);
uv_run(loop, UV_RUN_DEFAULT);
ASSERT(close_cb_called == 1);
/* Clean up */
remove("watch_dir/");
MAKE_VALGRIND_HAPPY();
return 0;
}
TEST_IMPL(fs_event_close_in_callback) { TEST_IMPL(fs_event_close_in_callback) {
#if defined(NO_FS_EVENTS) #if defined(NO_FS_EVENTS)
RETURN_SKIP(NO_FS_EVENTS); RETURN_SKIP(NO_FS_EVENTS);

View File

@ -30,8 +30,9 @@ static const int server_port = TEST_PORT;
/* Will be updated right after making the uv_connect_call */ /* Will be updated right after making the uv_connect_call */
static int connect_port = -1; static int connect_port = -1;
static int getsocknamecount = 0; static int getsocknamecount_tcp = 0;
static int getpeernamecount = 0; static int getpeernamecount = 0;
static int getsocknamecount_udp = 0;
static uv_loop_t* loop; static uv_loop_t* loop;
static uv_tcp_t tcp; static uv_tcp_t tcp;
@ -131,7 +132,7 @@ static void on_connection(uv_stream_t* server, int status) {
r = uv_tcp_getsockname(handle, &sockname, &namelen); r = uv_tcp_getsockname(handle, &sockname, &namelen);
ASSERT(r == 0); ASSERT(r == 0);
check_sockname(&sockname, "127.0.0.1", server_port, "accepted socket"); check_sockname(&sockname, "127.0.0.1", server_port, "accepted socket");
getsocknamecount++; getsocknamecount_tcp++;
namelen = sizeof peername; namelen = sizeof peername;
r = uv_tcp_getpeername(handle, &peername, &namelen); r = uv_tcp_getpeername(handle, &peername, &namelen);
@ -154,7 +155,7 @@ static void on_connect(uv_connect_t* req, int status) {
r = uv_tcp_getsockname((uv_tcp_t*) req->handle, &sockname, &namelen); r = uv_tcp_getsockname((uv_tcp_t*) req->handle, &sockname, &namelen);
ASSERT(r == 0); ASSERT(r == 0);
check_sockname(&sockname, "127.0.0.1", 0, "connected socket"); check_sockname(&sockname, "127.0.0.1", 0, "connected socket");
getsocknamecount++; getsocknamecount_tcp++;
namelen = sizeof peername; namelen = sizeof peername;
r = uv_tcp_getpeername((uv_tcp_t*) req->handle, &peername, &namelen); r = uv_tcp_getpeername((uv_tcp_t*) req->handle, &peername, &namelen);
@ -197,7 +198,7 @@ static int tcp_listener(void) {
r = uv_tcp_getsockname(&tcpServer, &sockname, &namelen); r = uv_tcp_getsockname(&tcpServer, &sockname, &namelen);
ASSERT(r == 0); ASSERT(r == 0);
check_sockname(&sockname, "0.0.0.0", server_port, "server socket"); check_sockname(&sockname, "0.0.0.0", server_port, "server socket");
getsocknamecount++; getsocknamecount_tcp++;
namelen = sizeof sockname; namelen = sizeof sockname;
r = uv_tcp_getpeername(&tcpServer, &peername, &namelen); r = uv_tcp_getpeername(&tcpServer, &peername, &namelen);
@ -256,7 +257,7 @@ static void udp_recv(uv_udp_t* handle,
r = uv_udp_getsockname(&udp, &sockname, &namelen); r = uv_udp_getsockname(&udp, &sockname, &namelen);
ASSERT(r == 0); ASSERT(r == 0);
check_sockname(&sockname, "0.0.0.0", 0, "udp receiving socket"); check_sockname(&sockname, "0.0.0.0", 0, "udp receiving socket");
getsocknamecount++; getsocknamecount_udp++;
uv_close((uv_handle_t*) &udp, NULL); uv_close((uv_handle_t*) &udp, NULL);
uv_close((uv_handle_t*) handle, NULL); uv_close((uv_handle_t*) handle, NULL);
@ -293,7 +294,7 @@ static int udp_listener(void) {
r = uv_udp_getsockname(&udpServer, &sockname, &namelen); r = uv_udp_getsockname(&udpServer, &sockname, &namelen);
ASSERT(r == 0); ASSERT(r == 0);
check_sockname(&sockname, "0.0.0.0", server_port, "udp listener socket"); check_sockname(&sockname, "0.0.0.0", server_port, "udp listener socket");
getsocknamecount++; getsocknamecount_udp++;
r = uv_udp_recv_start(&udpServer, alloc, udp_recv); r = uv_udp_recv_start(&udpServer, alloc, udp_recv);
ASSERT(r == 0); ASSERT(r == 0);
@ -333,7 +334,7 @@ TEST_IMPL(getsockname_tcp) {
uv_run(loop, UV_RUN_DEFAULT); uv_run(loop, UV_RUN_DEFAULT);
ASSERT(getsocknamecount == 3); ASSERT(getsocknamecount_tcp == 3);
ASSERT(getpeernamecount == 3); ASSERT(getpeernamecount == 3);
MAKE_VALGRIND_HAPPY(); MAKE_VALGRIND_HAPPY();
@ -351,7 +352,7 @@ TEST_IMPL(getsockname_udp) {
uv_run(loop, UV_RUN_DEFAULT); uv_run(loop, UV_RUN_DEFAULT);
ASSERT(getsocknamecount == 2); ASSERT(getsocknamecount_udp == 2);
ASSERT(udp.send_queue_size == 0); ASSERT(udp.send_queue_size == 0);
ASSERT(udpServer.send_queue_size == 0); ASSERT(udpServer.send_queue_size == 0);

View File

@ -97,3 +97,29 @@ TEST_IMPL(idle_starvation) {
MAKE_VALGRIND_HAPPY(); MAKE_VALGRIND_HAPPY();
return 0; return 0;
} }
static void idle_stop(uv_idle_t* handle) {
uv_idle_stop(handle);
}
TEST_IMPL(idle_check) {
ASSERT_EQ(0, uv_idle_init(uv_default_loop(), &idle_handle));
ASSERT_EQ(0, uv_idle_start(&idle_handle, idle_stop));
ASSERT_EQ(0, uv_check_init(uv_default_loop(), &check_handle));
ASSERT_EQ(0, uv_check_start(&check_handle, check_cb));
ASSERT_EQ(1, uv_run(uv_default_loop(), UV_RUN_ONCE));
ASSERT_EQ(1, check_cb_called);
ASSERT_EQ(0, close_cb_called);
uv_close((uv_handle_t*) &idle_handle, close_cb);
uv_close((uv_handle_t*) &check_handle, close_cb);
ASSERT_EQ(0, uv_run(uv_default_loop(), UV_RUN_ONCE));
ASSERT_EQ(2, close_cb_called);
MAKE_VALGRIND_HAPPY();
return 0;
}

View File

@ -22,7 +22,6 @@
#include "uv.h" #include "uv.h"
TEST_DECLARE (platform_output) TEST_DECLARE (platform_output)
TEST_DECLARE (callback_order)
TEST_DECLARE (close_order) TEST_DECLARE (close_order)
TEST_DECLARE (run_once) TEST_DECLARE (run_once)
TEST_DECLARE (run_nowait) TEST_DECLARE (run_nowait)
@ -123,15 +122,18 @@ TEST_DECLARE (tcp_bind_error_inval)
TEST_DECLARE (tcp_bind_localhost_ok) TEST_DECLARE (tcp_bind_localhost_ok)
TEST_DECLARE (tcp_bind_invalid_flags) TEST_DECLARE (tcp_bind_invalid_flags)
TEST_DECLARE (tcp_bind_writable_flags) TEST_DECLARE (tcp_bind_writable_flags)
TEST_DECLARE (tcp_bind_or_listen_error_after_close)
TEST_DECLARE (tcp_listen_without_bind) TEST_DECLARE (tcp_listen_without_bind)
TEST_DECLARE (tcp_connect_error_fault) TEST_DECLARE (tcp_connect_error_fault)
TEST_DECLARE (tcp_connect_timeout) TEST_DECLARE (tcp_connect_timeout)
TEST_DECLARE (tcp_local_connect_timeout) TEST_DECLARE (tcp_local_connect_timeout)
TEST_DECLARE (tcp6_local_connect_timeout) TEST_DECLARE (tcp6_local_connect_timeout)
TEST_DECLARE (tcp_close_while_connecting) TEST_DECLARE (tcp_close_while_connecting)
TEST_DECLARE (tcp_close_after_read_timeout)
TEST_DECLARE (tcp_close) TEST_DECLARE (tcp_close)
TEST_DECLARE (tcp_close_reset_accepted) TEST_DECLARE (tcp_close_reset_accepted)
TEST_DECLARE (tcp_close_reset_accepted_after_shutdown) TEST_DECLARE (tcp_close_reset_accepted_after_shutdown)
TEST_DECLARE (tcp_close_reset_accepted_after_socket_shutdown)
TEST_DECLARE (tcp_close_reset_client) TEST_DECLARE (tcp_close_reset_client)
TEST_DECLARE (tcp_close_reset_client_after_shutdown) TEST_DECLARE (tcp_close_reset_client_after_shutdown)
TEST_DECLARE (tcp_create_early) TEST_DECLARE (tcp_create_early)
@ -147,6 +149,7 @@ TEST_DECLARE (tcp_write_to_half_open_connection)
TEST_DECLARE (tcp_unexpected_read) TEST_DECLARE (tcp_unexpected_read)
TEST_DECLARE (tcp_read_stop) TEST_DECLARE (tcp_read_stop)
TEST_DECLARE (tcp_read_stop_start) TEST_DECLARE (tcp_read_stop_start)
TEST_DECLARE (tcp_rst)
TEST_DECLARE (tcp_bind6_error_addrinuse) TEST_DECLARE (tcp_bind6_error_addrinuse)
TEST_DECLARE (tcp_bind6_error_addrnotavail) TEST_DECLARE (tcp_bind6_error_addrnotavail)
TEST_DECLARE (tcp_bind6_error_fault) TEST_DECLARE (tcp_bind6_error_fault)
@ -191,6 +194,7 @@ TEST_DECLARE (pipe_bind_error_addrnotavail)
TEST_DECLARE (pipe_bind_error_inval) TEST_DECLARE (pipe_bind_error_inval)
TEST_DECLARE (pipe_connect_multiple) TEST_DECLARE (pipe_connect_multiple)
TEST_DECLARE (pipe_listen_without_bind) TEST_DECLARE (pipe_listen_without_bind)
TEST_DECLARE (pipe_bind_or_listen_error_after_close)
TEST_DECLARE (pipe_connect_bad_name) TEST_DECLARE (pipe_connect_bad_name)
TEST_DECLARE (pipe_connect_to_file) TEST_DECLARE (pipe_connect_to_file)
TEST_DECLARE (pipe_connect_on_prepare) TEST_DECLARE (pipe_connect_on_prepare)
@ -224,6 +228,7 @@ TEST_DECLARE (timer_is_closing)
TEST_DECLARE (timer_null_callback) TEST_DECLARE (timer_null_callback)
TEST_DECLARE (timer_early_check) TEST_DECLARE (timer_early_check)
TEST_DECLARE (idle_starvation) TEST_DECLARE (idle_starvation)
TEST_DECLARE (idle_check)
TEST_DECLARE (loop_handles) TEST_DECLARE (loop_handles)
TEST_DECLARE (get_loadavg) TEST_DECLARE (get_loadavg)
TEST_DECLARE (walk_handles) TEST_DECLARE (walk_handles)
@ -318,6 +323,7 @@ TEST_DECLARE (spawn_inherit_streams)
TEST_DECLARE (spawn_quoted_path) TEST_DECLARE (spawn_quoted_path)
TEST_DECLARE (spawn_tcp_server) TEST_DECLARE (spawn_tcp_server)
TEST_DECLARE (spawn_exercise_sigchld_issue) TEST_DECLARE (spawn_exercise_sigchld_issue)
TEST_DECLARE (spawn_relative_path)
TEST_DECLARE (fs_poll) TEST_DECLARE (fs_poll)
TEST_DECLARE (fs_poll_getpath) TEST_DECLARE (fs_poll_getpath)
TEST_DECLARE (fs_poll_close_request) TEST_DECLARE (fs_poll_close_request)
@ -386,6 +392,7 @@ TEST_DECLARE (fs_event_no_callback_after_close)
TEST_DECLARE (fs_event_no_callback_on_close) TEST_DECLARE (fs_event_no_callback_on_close)
TEST_DECLARE (fs_event_immediate_close) TEST_DECLARE (fs_event_immediate_close)
TEST_DECLARE (fs_event_close_with_pending_event) 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_close_in_callback)
TEST_DECLARE (fs_event_start_and_close) TEST_DECLARE (fs_event_start_and_close)
TEST_DECLARE (fs_event_error_reporting) TEST_DECLARE (fs_event_error_reporting)
@ -422,6 +429,7 @@ TEST_DECLARE (fs_invalid_mkdir_name)
#endif #endif
TEST_DECLARE (fs_get_system_error) TEST_DECLARE (fs_get_system_error)
TEST_DECLARE (strscpy) TEST_DECLARE (strscpy)
TEST_DECLARE (strtok)
TEST_DECLARE (threadpool_queue_work_simple) TEST_DECLARE (threadpool_queue_work_simple)
TEST_DECLARE (threadpool_queue_work_einval) TEST_DECLARE (threadpool_queue_work_einval)
TEST_DECLARE (threadpool_multiple_event_loops) TEST_DECLARE (threadpool_multiple_event_loops)
@ -541,9 +549,6 @@ TEST_DECLARE (metrics_idle_time_zero)
TASK_LIST_START TASK_LIST_START
TEST_ENTRY_CUSTOM (platform_output, 0, 1, 5000) TEST_ENTRY_CUSTOM (platform_output, 0, 1, 5000)
#if 0
TEST_ENTRY (callback_order)
#endif
TEST_ENTRY (test_macros) TEST_ENTRY (test_macros)
TEST_ENTRY (close_order) TEST_ENTRY (close_order)
TEST_ENTRY (run_once) TEST_ENTRY (run_once)
@ -695,15 +700,18 @@ TASK_LIST_START
TEST_ENTRY (tcp_bind_localhost_ok) TEST_ENTRY (tcp_bind_localhost_ok)
TEST_ENTRY (tcp_bind_invalid_flags) TEST_ENTRY (tcp_bind_invalid_flags)
TEST_ENTRY (tcp_bind_writable_flags) TEST_ENTRY (tcp_bind_writable_flags)
TEST_ENTRY (tcp_bind_or_listen_error_after_close)
TEST_ENTRY (tcp_listen_without_bind) TEST_ENTRY (tcp_listen_without_bind)
TEST_ENTRY (tcp_connect_error_fault) TEST_ENTRY (tcp_connect_error_fault)
TEST_ENTRY (tcp_connect_timeout) TEST_ENTRY (tcp_connect_timeout)
TEST_ENTRY (tcp_local_connect_timeout) TEST_ENTRY (tcp_local_connect_timeout)
TEST_ENTRY (tcp6_local_connect_timeout) TEST_ENTRY (tcp6_local_connect_timeout)
TEST_ENTRY (tcp_close_while_connecting) TEST_ENTRY (tcp_close_while_connecting)
TEST_ENTRY (tcp_close_after_read_timeout)
TEST_ENTRY (tcp_close) TEST_ENTRY (tcp_close)
TEST_ENTRY (tcp_close_reset_accepted) TEST_ENTRY (tcp_close_reset_accepted)
TEST_ENTRY (tcp_close_reset_accepted_after_shutdown) TEST_ENTRY (tcp_close_reset_accepted_after_shutdown)
TEST_ENTRY (tcp_close_reset_accepted_after_socket_shutdown)
TEST_ENTRY (tcp_close_reset_client) TEST_ENTRY (tcp_close_reset_client)
TEST_ENTRY (tcp_close_reset_client_after_shutdown) TEST_ENTRY (tcp_close_reset_client_after_shutdown)
TEST_ENTRY (tcp_create_early) TEST_ENTRY (tcp_create_early)
@ -723,6 +731,9 @@ TASK_LIST_START
TEST_ENTRY (tcp_read_stop_start) TEST_ENTRY (tcp_read_stop_start)
TEST_ENTRY (tcp_rst)
TEST_HELPER (tcp_rst, tcp4_echo_server)
TEST_ENTRY (tcp_bind6_error_addrinuse) TEST_ENTRY (tcp_bind6_error_addrinuse)
TEST_ENTRY (tcp_bind6_error_addrnotavail) TEST_ENTRY (tcp_bind6_error_addrnotavail)
TEST_ENTRY (tcp_bind6_error_fault) TEST_ENTRY (tcp_bind6_error_fault)
@ -769,6 +780,7 @@ TASK_LIST_START
TEST_ENTRY (pipe_bind_error_inval) TEST_ENTRY (pipe_bind_error_inval)
TEST_ENTRY (pipe_connect_multiple) TEST_ENTRY (pipe_connect_multiple)
TEST_ENTRY (pipe_listen_without_bind) TEST_ENTRY (pipe_listen_without_bind)
TEST_ENTRY (pipe_bind_or_listen_error_after_close)
TEST_ENTRY (pipe_getsockname) TEST_ENTRY (pipe_getsockname)
TEST_ENTRY (pipe_getsockname_abstract) TEST_ENTRY (pipe_getsockname_abstract)
TEST_ENTRY (pipe_getsockname_blocking) TEST_ENTRY (pipe_getsockname_blocking)
@ -814,6 +826,7 @@ TASK_LIST_START
TEST_ENTRY (timer_early_check) TEST_ENTRY (timer_early_check)
TEST_ENTRY (idle_starvation) TEST_ENTRY (idle_starvation)
TEST_ENTRY (idle_check)
TEST_ENTRY (ref) TEST_ENTRY (ref)
TEST_ENTRY (idle_ref) TEST_ENTRY (idle_ref)
@ -946,6 +959,7 @@ TASK_LIST_START
TEST_ENTRY (spawn_quoted_path) TEST_ENTRY (spawn_quoted_path)
TEST_ENTRY (spawn_tcp_server) TEST_ENTRY (spawn_tcp_server)
TEST_ENTRY (spawn_exercise_sigchld_issue) TEST_ENTRY (spawn_exercise_sigchld_issue)
TEST_ENTRY (spawn_relative_path)
TEST_ENTRY (fs_poll) TEST_ENTRY (fs_poll)
TEST_ENTRY (fs_poll_getpath) TEST_ENTRY (fs_poll_getpath)
TEST_ENTRY (fs_poll_close_request) TEST_ENTRY (fs_poll_close_request)
@ -1048,6 +1062,7 @@ TASK_LIST_START
TEST_ENTRY (fs_event_no_callback_on_close) TEST_ENTRY (fs_event_no_callback_on_close)
TEST_ENTRY (fs_event_immediate_close) TEST_ENTRY (fs_event_immediate_close)
TEST_ENTRY (fs_event_close_with_pending_event) TEST_ENTRY (fs_event_close_with_pending_event)
TEST_ENTRY (fs_event_close_with_pending_delete_event)
TEST_ENTRY (fs_event_close_in_callback) TEST_ENTRY (fs_event_close_in_callback)
TEST_ENTRY (fs_event_start_and_close) TEST_ENTRY (fs_event_start_and_close)
TEST_ENTRY_CUSTOM (fs_event_error_reporting, 0, 0, 60000) TEST_ENTRY_CUSTOM (fs_event_error_reporting, 0, 0, 60000)
@ -1084,6 +1099,7 @@ TASK_LIST_START
TEST_ENTRY (get_osfhandle_valid_handle) TEST_ENTRY (get_osfhandle_valid_handle)
TEST_ENTRY (open_osfhandle_valid_handle) TEST_ENTRY (open_osfhandle_valid_handle)
TEST_ENTRY (strscpy) TEST_ENTRY (strscpy)
TEST_ENTRY (strtok)
TEST_ENTRY (threadpool_queue_work_simple) TEST_ENTRY (threadpool_queue_work_simple)
TEST_ENTRY (threadpool_queue_work_einval) TEST_ENTRY (threadpool_queue_work_einval)
TEST_ENTRY_CUSTOM (threadpool_multiple_event_loops, 0, 0, 60000) TEST_ENTRY_CUSTOM (threadpool_multiple_event_loops, 0, 0, 60000)

View File

@ -137,3 +137,19 @@ TEST_IMPL(pipe_listen_without_bind) {
MAKE_VALGRIND_HAPPY(); MAKE_VALGRIND_HAPPY();
return 0; return 0;
} }
TEST_IMPL(pipe_bind_or_listen_error_after_close) {
uv_pipe_t server;
ASSERT_EQ(uv_pipe_init(uv_default_loop(), &server, 0), 0);
uv_close((uv_handle_t*) &server, NULL);
ASSERT_EQ(uv_pipe_bind(&server, TEST_PIPENAME), UV_EINVAL);
ASSERT_EQ(uv_listen((uv_stream_t*) &server, SOMAXCONN, NULL), UV_EINVAL);
ASSERT_EQ(uv_run(uv_default_loop(), UV_RUN_DEFAULT), 0);
MAKE_VALGRIND_HAPPY();
return 0;
}

View File

@ -102,8 +102,7 @@ TEST_IMPL(pipe_set_non_blocking) {
ASSERT(n == UV_EAGAIN); /* E_NOTIMPL */ ASSERT(n == UV_EAGAIN); /* E_NOTIMPL */
ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &pipe_handle, &buf, 1, write_cb)); ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &pipe_handle, &buf, 1, write_cb));
ASSERT_NOT_NULL(write_req.handle); ASSERT_NOT_NULL(write_req.handle);
ASSERT(1 == uv_run(uv_default_loop(), UV_RUN_ONCE)); /* queue write_cb */ ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE));
ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); /* process write_cb */
ASSERT_NULL(write_req.handle); /* check for signaled completion of write_cb */ ASSERT_NULL(write_req.handle); /* check for signaled completion of write_cb */
n = buf.len; n = buf.len;
#endif #endif

View File

@ -1675,9 +1675,6 @@ TEST_IMPL(closed_fd_events) {
ASSERT(req.result == 1); ASSERT(req.result == 1);
uv_fs_req_cleanup(&req); uv_fs_req_cleanup(&req);
#ifdef _WIN32
ASSERT(1 == uv_run(uv_default_loop(), UV_RUN_ONCE));
#endif
ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE));
/* should have received just one byte */ /* should have received just one byte */
@ -1981,3 +1978,37 @@ void spawn_stdin_stdout(void) {
} }
} }
#endif /* !_WIN32 */ #endif /* !_WIN32 */
TEST_IMPL(spawn_relative_path) {
char* sep;
init_process_options("spawn_helper1", exit_cb);
exepath_size = sizeof(exepath) - 2;
ASSERT_EQ(0, uv_exepath(exepath, &exepath_size));
exepath[exepath_size] = '\0';
/* Poor man's basename(3). */
sep = strrchr(exepath, '/');
if (sep == NULL)
sep = strrchr(exepath, '\\');
ASSERT_NOT_NULL(sep);
/* Split into dirname and basename and make basename relative. */
memmove(sep + 2, sep, 1 + strlen(sep));
sep[0] = '\0';
sep[1] = '.';
sep[2] = '/';
options.cwd = exepath;
options.file = options.args[0] = sep + 1;
ASSERT_EQ(0, uv_spawn(uv_default_loop(), &process, &options));
ASSERT_EQ(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT));
ASSERT_EQ(1, exit_cb_called);
ASSERT_EQ(1, close_cb_called);
MAKE_VALGRIND_HAPPY();
return 0;
}

90
deps/libuv/test/test-strtok.c vendored Normal file
View File

@ -0,0 +1,90 @@
/* 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 <string.h>
#include "../src/strtok.h"
#include "../src/strtok.c"
struct strtok_test_case {
const char* str;
const char* sep;
};
const char* tokens[] = {
"abc",
NULL,
"abc",
"abf",
NULL,
"This",
"is.a",
"test",
"of",
"the",
"string",
"tokenizer",
"function.",
NULL,
"Hello",
"This-is-a-nice",
"-string",
NULL
};
#define ASSERT_STRCMP(x, y) \
ASSERT((x != NULL && y != NULL && strcmp(x, y) == 0) || (x == y && x == NULL))
TEST_IMPL(strtok) {
struct strtok_test_case tests[] = {
{ "abc", "" },
{ "abc.abf", "." },
{ "This;is.a:test:of=the/string\\tokenizer-function.", "\\/:;=-" },
{ "Hello This-is-a-nice.-string", " ." },
};
size_t tokens_len = ARRAY_SIZE(tokens);
size_t tests_len = ARRAY_SIZE(tests);
size_t i;
size_t j;
char* itr;
char* tok_r;
char current_test[2048];
for (i = 0, j = 0; i < tests_len; i += 1) {
ASSERT(j < tokens_len);
snprintf(current_test, sizeof(current_test), "%s", tests[i].str);
tok_r = uv__strtok(current_test, tests[i].sep, &itr);
ASSERT_STRCMP(tok_r, tokens[j]);
j++;
while (tok_r) {
ASSERT(j < tokens_len);
tok_r = uv__strtok(NULL, tests[i].sep, &itr);
ASSERT_STRCMP(tok_r, tokens[j]);
j++;
}
}
return 0;
}

View File

@ -297,3 +297,21 @@ TEST_IMPL(tcp_bind_writable_flags) {
MAKE_VALGRIND_HAPPY(); MAKE_VALGRIND_HAPPY();
return 0; return 0;
} }
TEST_IMPL(tcp_bind_or_listen_error_after_close) {
uv_tcp_t tcp;
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(9999);
addr.sin_family = AF_INET;
ASSERT_EQ(uv_tcp_init(uv_default_loop(), &tcp), 0);
uv_close((uv_handle_t*) &tcp, NULL);
ASSERT_EQ(uv_tcp_bind(&tcp, (struct sockaddr*) &addr, 0), UV_EINVAL);
ASSERT_EQ(uv_listen((uv_stream_t*) &tcp, 5, NULL), UV_EINVAL);
ASSERT_EQ(uv_run(uv_default_loop(), UV_RUN_DEFAULT), 0);
MAKE_VALGRIND_HAPPY();
return 0;
}

View File

@ -0,0 +1,183 @@
/* Copyright libuv project and contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "uv.h"
#include "task.h"
static uv_tcp_t client;
static uv_tcp_t connection;
static uv_connect_t connect_req;
static uv_timer_t timer;
static int read_cb_called;
static int on_close_called;
static void on_connection(uv_stream_t* server, int status);
static void on_client_connect(uv_connect_t* req, int status);
static void on_client_alloc(uv_handle_t* handle,
size_t suggested_size,
uv_buf_t* buf);
static void on_client_read(uv_stream_t* stream,
ssize_t nread,
const uv_buf_t* buf);
static void on_client_timeout(uv_timer_t* handle);
static void on_close(uv_handle_t* handle);
static void on_client_connect(uv_connect_t* conn_req, int status) {
int r;
r = uv_read_start((uv_stream_t*) &client, on_client_alloc, on_client_read);
ASSERT_EQ(r, 0);
r = uv_timer_start(&timer, on_client_timeout, 1000, 0);
ASSERT_EQ(r, 0);
}
static void on_client_alloc(uv_handle_t* handle,
size_t suggested_size,
uv_buf_t* buf) {
static char slab[8];
buf->base = slab;
buf->len = sizeof(slab);
}
static void on_client_read(uv_stream_t* stream, ssize_t nread,
const uv_buf_t* buf) {
ASSERT_LT(nread, 0);
read_cb_called++;
}
static void on_client_timeout(uv_timer_t* handle) {
ASSERT_EQ(handle, &timer);
ASSERT_EQ(read_cb_called, 0);
uv_read_stop((uv_stream_t*) &client);
uv_close((uv_handle_t*) &client, on_close);
uv_close((uv_handle_t*) &timer, on_close);
}
static void on_connection_alloc(uv_handle_t* handle,
size_t suggested_size,
uv_buf_t* buf) {
static char slab[8];
buf->base = slab;
buf->len = sizeof(slab);
}
static void on_connection_read(uv_stream_t* stream,
ssize_t nread,
const uv_buf_t* buf) {
ASSERT_EQ(nread, UV_EOF);
read_cb_called++;
uv_close((uv_handle_t*) stream, on_close);
}
static void on_connection(uv_stream_t* server, int status) {
int r;
ASSERT_EQ(status, 0);
ASSERT_EQ(uv_accept(server, (uv_stream_t*) &connection), 0);
r = uv_read_start((uv_stream_t*) &connection,
on_connection_alloc,
on_connection_read);
ASSERT_EQ(r, 0);
}
static void on_close(uv_handle_t* handle) {
ASSERT(handle == (uv_handle_t*) &client ||
handle == (uv_handle_t*) &connection ||
handle == (uv_handle_t*) &timer);
on_close_called++;
}
static void start_server(uv_loop_t* loop, uv_tcp_t* handle) {
struct sockaddr_in addr;
int r;
ASSERT_EQ(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr), 0);
r = uv_tcp_init(loop, handle);
ASSERT_EQ(r, 0);
r = uv_tcp_bind(handle, (const struct sockaddr*) &addr, 0);
ASSERT_EQ(r, 0);
r = uv_listen((uv_stream_t*) handle, 128, on_connection);
ASSERT_EQ(r, 0);
uv_unref((uv_handle_t*) handle);
}
/* Check that pending write requests have their callbacks
* invoked when the handle is closed.
*/
TEST_IMPL(tcp_close_after_read_timeout) {
struct sockaddr_in addr;
uv_tcp_t tcp_server;
uv_loop_t* loop;
int r;
ASSERT_EQ(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr), 0);
loop = uv_default_loop();
/* We can't use the echo server, it doesn't handle ECONNRESET. */
start_server(loop, &tcp_server);
r = uv_tcp_init(loop, &client);
ASSERT_EQ(r, 0);
r = uv_tcp_connect(&connect_req,
&client,
(const struct sockaddr*) &addr,
on_client_connect);
ASSERT_EQ(r, 0);
r = uv_tcp_init(loop, &connection);
ASSERT_EQ(r, 0);
r = uv_timer_init(loop, &timer);
ASSERT_EQ(r, 0);
ASSERT_EQ(read_cb_called, 0);
ASSERT_EQ(on_close_called, 0);
r = uv_run(loop, UV_RUN_DEFAULT);
ASSERT_EQ(r, 0);
ASSERT_EQ(read_cb_called, 1);
ASSERT_EQ(on_close_called, 3);
MAKE_VALGRIND_HAPPY();
return 0;
}

View File

@ -25,6 +25,12 @@
#include <errno.h> #include <errno.h>
#include <string.h> /* memset */ #include <string.h> /* memset */
#ifdef _WIN32
# define INVALID_FD (INVALID_HANDLE_VALUE)
#else
# define INVALID_FD (-1)
#endif
static uv_loop_t* loop; static uv_loop_t* loop;
static uv_tcp_t tcp_server; static uv_tcp_t tcp_server;
static uv_tcp_t tcp_client; static uv_tcp_t tcp_client;
@ -62,9 +68,22 @@ static void do_write(uv_tcp_t* handle) {
static void do_close(uv_tcp_t* handle) { static void do_close(uv_tcp_t* handle) {
uv_os_fd_t fd;
int r;
if (shutdown_before_close == 1) { if (shutdown_before_close == 1) {
ASSERT(0 == uv_shutdown(&shutdown_req, (uv_stream_t*) handle, shutdown_cb)); ASSERT(0 == uv_shutdown(&shutdown_req, (uv_stream_t*) handle, shutdown_cb));
ASSERT(UV_EINVAL == uv_tcp_close_reset(handle, close_cb)); ASSERT(UV_EINVAL == uv_tcp_close_reset(handle, close_cb));
} else if (shutdown_before_close == 2) {
r = uv_fileno((const uv_handle_t*) handle, &fd);
ASSERT_EQ(r, 0);
ASSERT_NE(fd, INVALID_FD);
#ifdef _WIN32
ASSERT_EQ(0, shutdown(fd, SD_BOTH));
#else
ASSERT_EQ(0, shutdown(fd, SHUT_RDWR));
#endif
ASSERT_EQ(0, uv_tcp_close_reset(handle, close_cb));
} else { } else {
ASSERT(0 == uv_tcp_close_reset(handle, close_cb)); ASSERT(0 == uv_tcp_close_reset(handle, close_cb));
ASSERT(UV_ENOTCONN == uv_shutdown(&shutdown_req, (uv_stream_t*) handle, shutdown_cb)); ASSERT(UV_ENOTCONN == uv_shutdown(&shutdown_req, (uv_stream_t*) handle, shutdown_cb));
@ -288,3 +307,30 @@ TEST_IMPL(tcp_close_reset_accepted_after_shutdown) {
MAKE_VALGRIND_HAPPY(); MAKE_VALGRIND_HAPPY();
return 0; return 0;
} }
TEST_IMPL(tcp_close_reset_accepted_after_socket_shutdown) {
int r;
loop = uv_default_loop();
start_server(loop, &tcp_server);
client_close = 0;
shutdown_before_close = 2;
do_connect(loop, &tcp_client);
ASSERT_EQ(write_cb_called, 0);
ASSERT_EQ(close_cb_called, 0);
ASSERT_EQ(shutdown_cb_called, 0);
r = uv_run(loop, UV_RUN_DEFAULT);
ASSERT_EQ(r, 0);
ASSERT_EQ(write_cb_called, 4);
ASSERT_EQ(close_cb_called, 1);
ASSERT_EQ(shutdown_cb_called, 0);
MAKE_VALGRIND_HAPPY();
return 0;
}

107
deps/libuv/test/test-tcp-rst.c vendored Normal file
View File

@ -0,0 +1,107 @@
/* Copyright libuv project and contributors. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "uv.h"
#include "task.h"
static uv_tcp_t tcp;
static uv_connect_t connect_req;
static uv_buf_t qbuf;
static int called_alloc_cb;
static int called_connect_cb;
static int called_close_cb;
static void close_cb(uv_handle_t* handle) {
ASSERT(handle == (uv_handle_t*) &tcp);
called_close_cb++;
}
static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) {
buf->base = malloc(size);
buf->len = size;
called_alloc_cb++;
}
static void read_cb(uv_stream_t* t, ssize_t nread, const uv_buf_t* buf) {
ASSERT_PTR_EQ((uv_tcp_t*) t, &tcp);
ASSERT_EQ(nread, UV_ECONNRESET);
int fd;
ASSERT_EQ(0, uv_fileno((uv_handle_t*) t, &fd));
uv_handle_type type = uv_guess_handle(fd);
ASSERT_EQ(type, UV_TCP);
uv_close((uv_handle_t *) t, close_cb);
free(buf->base);
}
static void connect_cb(uv_connect_t *req, int status) {
ASSERT_EQ(status, 0);
ASSERT_PTR_EQ(req, &connect_req);
/* Start reading from the connection so we receive the RST in uv__read. */
ASSERT_EQ(0, uv_read_start((uv_stream_t*) &tcp, alloc_cb, read_cb));
/* Write 'QSH' to receive RST from the echo server. */
ASSERT_EQ(qbuf.len, uv_try_write((uv_stream_t*) &tcp, &qbuf, 1));
called_connect_cb++;
}
/*
* This test has a client which connects to the echo_server and receives TCP
* RST. Test checks that uv_guess_handle still works on a reset TCP handle.
*/
TEST_IMPL(tcp_rst) {
#ifndef _WIN32
struct sockaddr_in server_addr;
int r;
qbuf.base = "QSH";
qbuf.len = 3;
ASSERT_EQ(0, uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr));
r = uv_tcp_init(uv_default_loop(), &tcp);
ASSERT_EQ(r, 0);
r = uv_tcp_connect(&connect_req,
&tcp,
(const struct sockaddr*) &server_addr,
connect_cb);
ASSERT_EQ(r, 0);
uv_run(uv_default_loop(), UV_RUN_DEFAULT);
ASSERT_EQ(called_alloc_cb, 1);
ASSERT_EQ(called_connect_cb, 1);
ASSERT_EQ(called_close_cb, 1);
MAKE_VALGRIND_HAPPY();
return 0;
#else
RETURN_SKIP("Unix only test");
#endif
}

View File

@ -25,6 +25,8 @@
static int once_cb_called = 0; static int once_cb_called = 0;
static int once_close_cb_called = 0; static int once_close_cb_called = 0;
static int twice_cb_called = 0;
static int twice_close_cb_called = 0;
static int repeat_cb_called = 0; static int repeat_cb_called = 0;
static int repeat_close_cb_called = 0; static int repeat_close_cb_called = 0;
static int order_cb_called = 0; static int order_cb_called = 0;
@ -58,6 +60,27 @@ static void once_cb(uv_timer_t* handle) {
uv_update_time(uv_default_loop()); uv_update_time(uv_default_loop());
} }
static void twice_close_cb(uv_handle_t* handle) {
printf("TWICE_CLOSE_CB\n");
ASSERT_NOT_NULL(handle);
ASSERT(0 == uv_is_active(handle));
twice_close_cb_called++;
}
static void twice_cb(uv_timer_t* handle) {
printf("TWICE_CB %d\n", twice_cb_called);
ASSERT_NOT_NULL(handle);
ASSERT(0 == uv_is_active((uv_handle_t*) handle));
twice_cb_called++;
uv_close((uv_handle_t*)handle, twice_close_cb);
}
static void repeat_close_cb(uv_handle_t* handle) { static void repeat_close_cb(uv_handle_t* handle) {
printf("REPEAT_CLOSE_CB\n"); printf("REPEAT_CLOSE_CB\n");
@ -144,12 +167,12 @@ TEST_IMPL(timer_start_twice) {
ASSERT(r == 0); ASSERT(r == 0);
r = uv_timer_start(&once, never_cb, 86400 * 1000, 0); r = uv_timer_start(&once, never_cb, 86400 * 1000, 0);
ASSERT(r == 0); ASSERT(r == 0);
r = uv_timer_start(&once, once_cb, 10, 0); r = uv_timer_start(&once, twice_cb, 10, 0);
ASSERT(r == 0); ASSERT(r == 0);
r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
ASSERT(r == 0); ASSERT(r == 0);
ASSERT(once_cb_called == 1); ASSERT(twice_cb_called == 1);
MAKE_VALGRIND_HAPPY(); MAKE_VALGRIND_HAPPY();
return 0; return 0;

View File

@ -98,10 +98,6 @@ static void sv_recv_cb(uv_udp_t* handle,
TEST_IMPL(udp_connect) { TEST_IMPL(udp_connect) {
#if defined(__PASE__)
RETURN_SKIP(
"IBMi PASE's UDP connection can not be disconnected with AF_UNSPEC.");
#endif
uv_udp_send_t req; uv_udp_send_t req;
struct sockaddr_in ext_addr; struct sockaddr_in ext_addr;
struct sockaddr_in tmp_addr; struct sockaddr_in tmp_addr;

View File

@ -98,10 +98,6 @@ static void sv_recv_cb(uv_udp_t* handle,
TEST_IMPL(udp_connect6) { TEST_IMPL(udp_connect6) {
#if defined(__PASE__)
RETURN_SKIP(
"IBMi PASE's UDP connection can not be disconnected with AF_UNSPEC.");
#endif
uv_udp_send_t req; uv_udp_send_t req;
struct sockaddr_in6 ext_addr; struct sockaddr_in6 ext_addr;
struct sockaddr_in6 tmp_addr; struct sockaddr_in6 tmp_addr;