Compare commits
178 Commits
tcp-keepal
...
v1.x
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16d6a0b49d | ||
|
|
436c04048e | ||
|
|
feddddb56b | ||
|
|
f61f9c29d8 | ||
|
|
843b64faf5 | ||
|
|
85b526f56a | ||
|
|
8a94b7b2ec | ||
|
|
b807450e98 | ||
|
|
82cdfb75ff | ||
|
|
dcace2a393 | ||
|
|
378edb28f4 | ||
|
|
7894072528 | ||
|
|
abe59d6319 | ||
|
|
e399e00e78 | ||
|
|
51477bc711 | ||
|
|
23632e9104 | ||
|
|
a6ddf41edf | ||
|
|
82351168b3 | ||
|
|
bc19beadbd | ||
|
|
f15c602bd0 | ||
|
|
0f31978c30 | ||
|
|
a2ba04f83f | ||
|
|
8fb9cb9194 | ||
|
|
be8eec8c5a | ||
|
|
e59e2a9e49 | ||
|
|
ec5a4b54f7 | ||
|
|
beebf02cf6 | ||
|
|
a94f2ad2b7 | ||
|
|
3d78d121f4 | ||
|
|
e8969bff6c | ||
|
|
7b4cf04a91 | ||
|
|
acebb97490 | ||
|
|
88201044ed | ||
|
|
72d9abccd7 | ||
|
|
16e6e84dcc | ||
|
|
7752218db2 | ||
|
|
88baee1a35 | ||
|
|
264bb335af | ||
|
|
6af08fb527 | ||
|
|
2494c088f0 | ||
|
|
467859c2ba | ||
|
|
69bad8201b | ||
|
|
c431bc39c3 | ||
|
|
14644080c8 | ||
|
|
3d0578e6eb | ||
|
|
61c966cf0b | ||
|
|
b7d07d78e9 | ||
|
|
556a0f1f0f | ||
|
|
b1d30f9489 | ||
|
|
c6d43bea09 | ||
|
|
31ea3411cc | ||
|
|
d05744e3ed | ||
|
|
1b084f7bbe | ||
|
|
5dcef22c62 | ||
|
|
15e3f84678 | ||
|
|
2907f6d69e | ||
|
|
c6b67af390 | ||
|
|
7b75935b00 | ||
|
|
2d8371a06e | ||
|
|
d4ab6fbba4 | ||
|
|
e129cd7fda | ||
|
|
a3abfbcb08 | ||
|
|
64f4502b9b | ||
|
|
0caf5bb876 | ||
|
|
94e467ad93 | ||
|
|
078180e13d | ||
|
|
e1095c7a43 | ||
|
|
058c49b7ba | ||
|
|
7e6590f31d | ||
|
|
9cf0710d71 | ||
|
|
52a9243317 | ||
|
|
18d48bc13c | ||
|
|
fbe2d85bd5 | ||
|
|
be0b00a80d | ||
|
|
bfbd6db0d6 | ||
|
|
8be336f4ee | ||
|
|
7c3abfbf1e | ||
|
|
1f36b01ed0 | ||
|
|
8d957c56b3 | ||
|
|
0be52c8251 | ||
|
|
1cbffcbd5d | ||
|
|
670e75ee7e | ||
|
|
f55efb2f38 | ||
|
|
88b874e63c | ||
|
|
473dafc593 | ||
|
|
65e3735320 | ||
|
|
f806be87d3 | ||
|
|
bcc6d1c1fc | ||
|
|
675a5a5396 | ||
|
|
5467ec969a | ||
|
|
511e202e13 | ||
|
|
d2e56a5e8d | ||
|
|
cc2e0aa3cf | ||
|
|
e1a5465255 | ||
|
|
32603fd5ff | ||
|
|
1c778bd001 | ||
|
|
5bb19f35ea | ||
|
|
88af4a87d2 | ||
|
|
9dddebab0d | ||
|
|
a49f264dff | ||
|
|
44e61dab7e | ||
|
|
0a00e80c36 | ||
|
|
5ff1fc724f | ||
|
|
f00d4b6775 | ||
|
|
5cbc82e369 | ||
|
|
58dfb6c89b | ||
|
|
b5eb41d882 | ||
|
|
c869cd1d8a | ||
|
|
5cc7175514 | ||
|
|
3e1733a053 | ||
|
|
8809d1df8d | ||
|
|
1790abb3b2 | ||
|
|
31d9165999 | ||
|
|
27134547ff | ||
|
|
1eac3310ad | ||
|
|
a53e7877e4 | ||
|
|
c84a2dbe03 | ||
|
|
88ab6e78da | ||
|
|
5537d6a689 | ||
|
|
e78e29c231 | ||
|
|
9b3b61f606 | ||
|
|
a6a987c0de | ||
|
|
727ee7237e | ||
|
|
4e310d0f90 | ||
|
|
f23037fe21 | ||
|
|
e5cb1d3d3d | ||
|
|
63b22be083 | ||
|
|
badecdca14 | ||
|
|
83306585ff | ||
|
|
18266a6969 | ||
|
|
0c36b16d1b | ||
|
|
e4d47c5357 | ||
|
|
593aa3b2f6 | ||
|
|
47c833675b | ||
|
|
372e4c645e | ||
|
|
6ab153cf8e | ||
|
|
9678211c24 | ||
|
|
5d1ccc12c4 | ||
|
|
f56f21d7da | ||
|
|
ecc11611d3 | ||
|
|
f279d9e6c6 | ||
|
|
ae6e146775 | ||
|
|
90648ea3e5 | ||
|
|
2780b87d56 | ||
|
|
e37539a46c | ||
|
|
7c491bde32 | ||
|
|
6621fe045a | ||
|
|
36f0789d83 | ||
|
|
fedfa9893e | ||
|
|
ba24986f8d | ||
|
|
eb5af8e3c0 | ||
|
|
1ee1063402 | ||
|
|
541329d51f | ||
|
|
d2d92b74a8 | ||
|
|
c8d4a87f49 | ||
|
|
ab3ecf6565 | ||
|
|
287987b37c | ||
|
|
10ccd08471 | ||
|
|
bf61390769 | ||
|
|
520eb622f0 | ||
|
|
497f3168d1 | ||
|
|
8083ab26e0 | ||
|
|
c0a61c3bb3 | ||
|
|
f50ae53c42 | ||
|
|
17219b8f39 | ||
|
|
46c0e1769b | ||
|
|
4fce06ec96 | ||
|
|
77e4cd5b18 | ||
|
|
d05ed869bb | ||
|
|
abc9767034 | ||
|
|
6adeeacee7 | ||
|
|
f55628eed0 | ||
|
|
3ecce91410 | ||
|
|
cc23e204d7 | ||
|
|
e0c5fc8714 | ||
|
|
91ba13054a | ||
|
|
b0816180e3 | ||
|
|
fa6745b4f2 |
7
.github/dependabot.yml
vendored
Normal file
7
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
4
.github/workflows/CI-docs.yml
vendored
4
.github/workflows/CI-docs.yml
vendored
@ -11,8 +11,8 @@ jobs:
|
||||
docs-src:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.9'
|
||||
cache: 'pip' # caching pip dependencies
|
||||
|
||||
2
.github/workflows/CI-sample.yml
vendored
2
.github/workflows/CI-sample.yml
vendored
@ -21,7 +21,7 @@ jobs:
|
||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||
runs-on: ${{matrix.os}}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: setup
|
||||
run: cmake -E make_directory ${{runner.workspace}}/libuv/docs/code/build
|
||||
- name: configure
|
||||
|
||||
124
.github/workflows/CI-unix.yml
vendored
124
.github/workflows/CI-unix.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: configure
|
||||
run: |
|
||||
./autogen.sh
|
||||
@ -29,28 +29,67 @@ jobs:
|
||||
|
||||
build-android:
|
||||
runs-on: ubuntu-latest
|
||||
container: reactnativecommunity/react-native-android:2020-5-20
|
||||
env:
|
||||
ANDROID_AVD_HOME: /root/.android/avd
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Envinfo
|
||||
run: npx envinfo
|
||||
- name: Configure android arm64
|
||||
# see build options you can use in https://developer.android.com/ndk/guides/cmake
|
||||
- name: Enable KVM
|
||||
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
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
- name: Build and Test
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: 30
|
||||
arch: x86_64
|
||||
target: google_apis
|
||||
ram-size: 2048M
|
||||
emulator-options: -no-audio -no-window -gpu off -no-boot-anim -netdelay none -netspeed full -writable-system -no-snapshot-save -no-snapshot-load -no-snapshot
|
||||
disable-animations: true
|
||||
script: |
|
||||
echo "::group::Configure"
|
||||
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DCMAKE_BUILD_TYPE=Release -DANDROID_ABI="x86_64" -DANDROID_PLATFORM=android-30
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Build"
|
||||
cmake --build build
|
||||
|
||||
## Correct some ld bugs that cause problems with libuv tests
|
||||
wget "https://github.com/termux/termux-elf-cleaner/releases/download/v2.2.1/termux-elf-cleaner" -P build
|
||||
chmod a+x build/termux-elf-cleaner
|
||||
build/termux-elf-cleaner --api-level 30 ./build/uv_run_tests
|
||||
build/termux-elf-cleaner --api-level 30 ./build/uv_run_tests_a
|
||||
|
||||
adb shell "su 0 setenforce 0" # to allow some syscalls like link, chmod, etc.
|
||||
|
||||
## Push the build and test fixtures to the device
|
||||
adb push build /data/local/tmp
|
||||
adb shell mkdir /data/local/tmp/build/test
|
||||
adb push test/fixtures /data/local/tmp/build/test
|
||||
echo "::endgroup::"
|
||||
|
||||
## Run the tests
|
||||
file build/uv_run_tests_a
|
||||
adb shell "cd /data/local/tmp/build && env UV_TEST_TIMEOUT_MULTIPLIER=5 ./uv_run_tests_a"
|
||||
|
||||
build-macos:
|
||||
runs-on: macos-11
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-13, macos-14]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Envinfo
|
||||
run: npx envinfo
|
||||
- name: Disable Firewall
|
||||
run: |
|
||||
/usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
|
||||
sudo defaults write /Library/Preferences/com.apple.alf globalstate -int 0
|
||||
/usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
|
||||
- name: Setup
|
||||
run: |
|
||||
brew install ninja automake libtool
|
||||
@ -81,9 +120,13 @@ jobs:
|
||||
make -C build-auto -j4
|
||||
|
||||
build-ios:
|
||||
runs-on: macos-11
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-13, macos-14]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Configure
|
||||
run: |
|
||||
mkdir build-ios
|
||||
@ -95,45 +138,36 @@ jobs:
|
||||
ls -lh build-ios
|
||||
|
||||
build-cross-qemu:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
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: 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}
|
||||
- {target: arm, toolchain: gcc-arm-linux-gnueabi, cc: arm-linux-gnueabi-gcc, qemu: qemu-arm }
|
||||
- {target: armhf, toolchain: gcc-arm-linux-gnueabihf, cc: arm-linux-gnueabihf-gcc, qemu: qemu-arm }
|
||||
- {target: aarch64, toolchain: gcc-aarch64-linux-gnu, cc: aarch64-linux-gnu-gcc, qemu: qemu-aarch64 }
|
||||
- {target: riscv64, toolchain: gcc-riscv64-linux-gnu, cc: riscv64-linux-gnu-gcc, qemu: qemu-riscv64 }
|
||||
- {target: ppc, toolchain: gcc-powerpc-linux-gnu, cc: powerpc-linux-gnu-gcc, qemu: qemu-ppc }
|
||||
- {target: ppc64, toolchain: gcc-powerpc64-linux-gnu, cc: powerpc64-linux-gnu-gcc, qemu: qemu-ppc64 }
|
||||
- {target: ppc64le, toolchain: gcc-powerpc64le-linux-gnu, cc: powerpc64le-linux-gnu-gcc, qemu: qemu-ppc64le }
|
||||
- {target: s390x, toolchain: gcc-s390x-linux-gnu, cc: s390x-linux-gnu-gcc, qemu: qemu-s390x }
|
||||
- {target: mips, toolchain: gcc-mips-linux-gnu, cc: mips-linux-gnu-gcc, qemu: qemu-mips }
|
||||
- {target: mips64, toolchain: gcc-mips64-linux-gnuabi64, cc: mips64-linux-gnuabi64-gcc, qemu: qemu-mips64 }
|
||||
- {target: mipsel, toolchain: gcc-mipsel-linux-gnu, cc: mipsel-linux-gnu-gcc, qemu: qemu-mipsel }
|
||||
- {target: mips64el, toolchain: gcc-mips64el-linux-gnuabi64, cc: mips64el-linux-gnuabi64-gcc,qemu: qemu-mips64el }
|
||||
- {target: arm (u64 slots), toolchain: gcc-arm-linux-gnueabi, cc: arm-linux-gnueabi-gcc, qemu: qemu-arm }
|
||||
- {target: aarch64 (u64 slots), toolchain: gcc-aarch64-linux-gnu, cc: aarch64-linux-gnu-gcc, qemu: qemu-aarch64 }
|
||||
- {target: ppc (u64 slots), toolchain: gcc-powerpc-linux-gnu, cc: powerpc-linux-gnu-gcc, qemu: qemu-ppc }
|
||||
- {target: ppc64 (u64 slots), toolchain: gcc-powerpc64-linux-gnu, cc: powerpc64-linux-gnu-gcc, qemu: qemu-ppc64 }
|
||||
|
||||
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_7\\.2+dfsg-.*_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 }}
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install qemu and ${{ matrix.config.toolchain }}
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install ${{ matrix.config.toolchain }} -y
|
||||
sudo apt install qemu-user qemu-user-binfmt ${{ matrix.config.toolchain }} -y
|
||||
- name: Configure with ${{ matrix.config.cc }}
|
||||
run: |
|
||||
mkdir build
|
||||
|
||||
11
.github/workflows/CI-win.yml
vendored
11
.github/workflows/CI-win.yml
vendored
@ -29,13 +29,12 @@ jobs:
|
||||
- {toolchain: Visual Studio 17 2022, arch: x64, server: 2022, config: UBSAN}
|
||||
- {toolchain: Visual Studio 17 2022, arch: arm64, server: 2022}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run:
|
||||
cmake -S . -B build -DBUILD_TESTING=ON
|
||||
-G "${{ matrix.config.toolchain }}" -A ${{ matrix.config.arch }}
|
||||
${{ matrix.config.config == 'ASAN' && '-DASAN=on -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded' ||
|
||||
matrix.config.config == 'UBSAN' && '-DUBSAN=on' || '' }}
|
||||
${{ matrix.config.config == 'ASAN' && '-DASAN=on -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded' || '' }}
|
||||
|
||||
cmake --build build --config RelWithDebInfo
|
||||
|
||||
@ -81,7 +80,7 @@ jobs:
|
||||
- {arch: i686, server: 2022, libgcc: dw2 }
|
||||
- {arch: x86_64, server: 2022, libgcc: seh }
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install mingw32 environment
|
||||
run: |
|
||||
sudo apt update
|
||||
@ -99,7 +98,7 @@ jobs:
|
||||
`${{ matrix.config.arch }}-w64-mingw32-gcc -print-file-name=libatomic-1.dll` \
|
||||
build/usr/bin
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: mingw-${{ matrix.config.arch }}
|
||||
path: build/usr/**/*
|
||||
@ -117,7 +116,7 @@ jobs:
|
||||
- {arch: x86_64, server: 2022}
|
||||
steps:
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: mingw-${{ matrix.config.arch }}
|
||||
- name: Test
|
||||
|
||||
15
.github/workflows/sanitizer.yml
vendored
15
.github/workflows/sanitizer.yml
vendored
@ -16,13 +16,18 @@ jobs:
|
||||
sanitizers-linux:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup
|
||||
run: |
|
||||
sudo apt-get install ninja-build
|
||||
- name: Envinfo
|
||||
run: npx envinfo
|
||||
|
||||
# [AM]SAN fail on newer kernels due to a bigger PIE slide
|
||||
- name: Disable ASLR
|
||||
run: |
|
||||
sudo sysctl -w kernel.randomize_va_space=0
|
||||
|
||||
- name: ASAN Build
|
||||
run: |
|
||||
mkdir build-asan
|
||||
@ -62,9 +67,9 @@ jobs:
|
||||
./build-ubsan/uv_run_tests_a
|
||||
|
||||
sanitizers-macos:
|
||||
runs-on: macos-11
|
||||
runs-on: macos-13
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Envinfo
|
||||
run: npx envinfo
|
||||
@ -99,14 +104,14 @@ jobs:
|
||||
sanitizers-windows:
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup
|
||||
run: |
|
||||
choco install ninja
|
||||
|
||||
# Note: clang shipped with VS2022 has an issue where the UBSAN runtime doesn't link.
|
||||
- name: Install LLVM and Clang
|
||||
uses: KyleMayes/install-llvm-action@v1
|
||||
uses: KyleMayes/install-llvm-action@v2
|
||||
with:
|
||||
version: "17"
|
||||
|
||||
|
||||
6
.mailmap
6
.mailmap
@ -4,6 +4,7 @@ Aaron Bieber <qbit@deftly.net> <deftly@gmail.com>
|
||||
Alan Gutierrez <alan@prettyrobots.com> <alan@blogometer.com>
|
||||
Andrius Bentkus <andrius.bentkus@gmail.com> <toxedvirus@gmail.com>
|
||||
Andy Fiddaman <andy@omniosce.org> <omnios@citrus-it.co.uk>
|
||||
Andy Pan <panjf2000@gmail.com> <i@andypan.me>
|
||||
Bert Belder <bertbelder@gmail.com> <i@bertbelder.com>
|
||||
Bert Belder <bertbelder@gmail.com> <info@2bs.nl>
|
||||
Bert Belder <bertbelder@gmail.com> <user@ChrUbuntu.(none)>
|
||||
@ -18,6 +19,7 @@ David Carlier <devnexen@gmail.com>
|
||||
Devchandra Meetei Leishangthem <dlmeetei@gmail.com>
|
||||
Fedor Indutny <fedor.indutny@gmail.com> <fedor@indutny.com>
|
||||
Frank Denis <github@pureftpd.org>
|
||||
Hüseyin Açacak <110401522+huseyinacacak-janea@users.noreply.github.com> <huseyin@janeasystems.com>
|
||||
Imran Iqbal <imrani@ca.ibm.com> <imran@imraniqbal.org>
|
||||
Isaac Z. Schlueter <i@izs.me>
|
||||
Jason Williams <necmon@yahoo.com>
|
||||
@ -37,6 +39,7 @@ Michael Neumann <mneumann@think.localnet> <mneumann@ntecs.de>
|
||||
Michael Penick <michael.penick@datastax.com> <mpenick@users.noreply.github.com>
|
||||
Nicholas Vavilov <vvnicholas@gmail.com>
|
||||
Nick Logan <ugexe@cpan.org> <nlogan@gmail.com>
|
||||
Olivier Valentin <ovalenti@redhat.com> <valentio@free.fr>
|
||||
Rasmus Christian Pedersen <zerhacken@yahoo.com>
|
||||
Rasmus Christian Pedersen <zerhacken@yahoo.com> <ruysch@outlook.com>
|
||||
Richard Lau <rlau@redhat.com> <riclau@uk.ibm.com>
|
||||
@ -47,7 +50,8 @@ Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
|
||||
Sam Roberts <vieuxtech@gmail.com> <sam@strongloop.com>
|
||||
San-Tai Hsu <vanilla@fatpipi.com>
|
||||
Santiago Gimeno <santiago.gimeno@quantion.es> <santiago.gimeno@gmail.com>
|
||||
Saúl Ibarra Corretgé <saghul@gmail.com>
|
||||
Saúl Ibarra Corretgé <s@saghul.net>
|
||||
Saúl Ibarra Corretgé <s@saghul.net> <saghul@gmail.com>
|
||||
Saúl Ibarra Corretgé <saghul@gmail.com> <s@saghul.net>
|
||||
Shigeki Ohtsu <ohtsu@iij.ad.jp> <ohtsu@ohtsu.org>
|
||||
Shuowang (Wayne) Zhang <shuowang.zhang@ibm.com>
|
||||
|
||||
@ -2,7 +2,7 @@ version: 2
|
||||
|
||||
sphinx:
|
||||
builder: html
|
||||
configuration: null
|
||||
configuration: docs/src/conf.py
|
||||
fail_on_warning: false
|
||||
|
||||
build:
|
||||
|
||||
25
AUTHORS
25
AUTHORS
@ -567,3 +567,28 @@ Ardi Nugraha <33378542+ardi-nugraha@users.noreply.github.com>
|
||||
Anton Bachin <antonbachin@yahoo.com>
|
||||
Trevor Flynn <trevorflynn@liquidcrystalstudios.com>
|
||||
Andy Pan <panjf2000@gmail.com>
|
||||
Viacheslav Muravyev <slavamuravey@mail.ru>
|
||||
Anthony Alayo <anthony.alayo@gmail.com>
|
||||
Thomas Walter <31201229+waltoss@users.noreply.github.com>
|
||||
hiiizxf <385122613@qq.com>
|
||||
Geddy <guandichao@163.com>
|
||||
Farzin Monsef <monseffarzin@gmail.com>
|
||||
tgolang <154592711+tgolang@users.noreply.github.com>
|
||||
josedelinux <josedelinux@hotmail.com>
|
||||
Hüseyin Açacak <110401522+huseyinacacak-janea@users.noreply.github.com>
|
||||
Uilian Ries <uilianries@gmail.com>
|
||||
Olivier Valentin <ovalenti@redhat.com>
|
||||
郑苏波 (Super Zheng) <superzheng@tencent.com>
|
||||
zeertzjq <zeertzjq@outlook.com>
|
||||
Ian Butterworth <i.r.butterworth@gmail.com>
|
||||
握猫猫 <164346864@qq.com>
|
||||
Zuohui Yang <274048862@qq.com>
|
||||
Edigleysson Silva (Edy) <edigleyssonsilva@gmail.com>
|
||||
Raihaan Shouhell <raihaanhimself@gmail.com>
|
||||
Rialbat <miha-wead@mail.ru>
|
||||
Adam <adam@NetBSD.org>
|
||||
Poul T Lomholt <ptlomholt@users.noreply.github.com>
|
||||
Thad House <ThadHouse@users.noreply.github.com>
|
||||
Julian A Avar C <28635807+julian-a-avar-c@users.noreply.github.com>
|
||||
amcgoogan <105525867+amcgoogan@users.noreply.github.com>
|
||||
Rafael Gonzaga <rafael.nunu@hotmail.com>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.9)
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
if(POLICY CMP0091)
|
||||
cmake_policy(SET CMP0091 NEW) # Enable MSVC_RUNTIME_LIBRARY setting
|
||||
@ -81,15 +81,20 @@ if(TSAN)
|
||||
endif()
|
||||
|
||||
if(UBSAN)
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
list(APPEND uv_defines __UBSAN__=1)
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|GNU|Clang")
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined")
|
||||
set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined")
|
||||
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined")
|
||||
elseif(MSVC)
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=undefined")
|
||||
add_compile_options("-fsanitize=undefined" "-fno-sanitize-recover=undefined")
|
||||
if (NOT WIN32)
|
||||
add_link_options("-fsanitize=undefined")
|
||||
endif()
|
||||
if(MSVC)
|
||||
add_compile_options("/Oy-")
|
||||
else()
|
||||
add_compile_options("-fno-omit-frame-pointer")
|
||||
endif()
|
||||
else()
|
||||
message(SEND_ERROR "UndefinedBehaviorSanitizer support requires clang, gcc, or msvc. Try again with -DCMAKE_C_COMPILER.")
|
||||
message(SEND_ERROR "UndefinedBehaviorSanitizer support requires clang or gcc. Try again with -DCMAKE_C_COMPILER.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@ -181,7 +186,7 @@ set(uv_sources
|
||||
src/version.c)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND uv_defines WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0602 _CRT_DECLARE_NONSTDC_NAMES=0)
|
||||
list(APPEND uv_defines WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0A00 _CRT_DECLARE_NONSTDC_NAMES=0)
|
||||
list(APPEND uv_libraries
|
||||
psapi
|
||||
user32
|
||||
@ -307,6 +312,7 @@ if(APPLE)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "GNU")
|
||||
list(APPEND uv_defines _GNU_SOURCE _POSIX_C_SOURCE=200112 _XOPEN_SOURCE=500)
|
||||
list(APPEND uv_libraries dl)
|
||||
list(APPEND uv_sources
|
||||
src/unix/bsd-ifaddrs.c
|
||||
@ -566,6 +572,7 @@ if(LIBUV_BUILD_TESTS)
|
||||
test/test-hrtime.c
|
||||
test/test-idle.c
|
||||
test/test-idna.c
|
||||
test/test-iouring-pollhup.c
|
||||
test/test-ip4-addr.c
|
||||
test/test-ip6-addr.c
|
||||
test/test-ip-name.c
|
||||
@ -643,6 +650,7 @@ if(LIBUV_BUILD_TESTS)
|
||||
test/test-tcp-oob.c
|
||||
test/test-tcp-open.c
|
||||
test/test-tcp-read-stop.c
|
||||
test/test-tcp-reuseport.c
|
||||
test/test-tcp-read-stop-start.c
|
||||
test/test-tcp-rst.c
|
||||
test/test-tcp-shutdown-after-write.c
|
||||
@ -659,6 +667,7 @@ if(LIBUV_BUILD_TESTS)
|
||||
test/test-thread-affinity.c
|
||||
test/test-thread-equal.c
|
||||
test/test-thread.c
|
||||
test/test-thread-name.c
|
||||
test/test-thread-priority.c
|
||||
test/test-threadpool-cancel.c
|
||||
test/test-threadpool.c
|
||||
@ -691,6 +700,7 @@ if(LIBUV_BUILD_TESTS)
|
||||
test/test-udp-send-unreachable.c
|
||||
test/test-udp-try-send.c
|
||||
test/test-udp-recv-in-a-row.c
|
||||
test/test-udp-reuseport.c
|
||||
test/test-uname.c
|
||||
test/test-walk-handles.c
|
||||
test/test-watcher-cross-stop.c)
|
||||
|
||||
338
ChangeLog
338
ChangeLog
@ -1,3 +1,335 @@
|
||||
2025.01.15, Version 1.50.0 (Stable), 8fb9cb919489a48880680a56efecff6a7dfb4504
|
||||
|
||||
Changes since version 1.49.2:
|
||||
|
||||
* ci: run macOS and iOS tests also on macOS 14 (Saúl Ibarra Corretgé)
|
||||
|
||||
* unix,win: map ENOEXEC errno (Saúl Ibarra Corretgé)
|
||||
|
||||
* test: skip multicast join test on ENOEXEC (Saúl Ibarra Corretgé)
|
||||
|
||||
* ci: make sure the macOS firewall is disabled (Saúl Ibarra Corretgé)
|
||||
|
||||
* darwin,test: squelch EBUSY error on multicast join (Saúl Ibarra Corretgé)
|
||||
|
||||
* build: update minimum cmake to 3.10 (Ben Noordhuis)
|
||||
|
||||
* kqueue: use EVFILT_USER for async if available (Jameson Nash)
|
||||
|
||||
* unix,win: fix off-by-one in uv_wtf8_to_utf16() (Ben Noordhuis)
|
||||
|
||||
* doc: add scala-native-loop to LINKS.md (Julian A Avar C)
|
||||
|
||||
* unix: fix build breakage on haiku, openbsd, etc (Jeffrey H. Johnson)
|
||||
|
||||
* kqueue: lower overhead in uv__io_check_fd (Andy Pan)
|
||||
|
||||
* doc: move cjihrig back to active maintainers (cjihrig)
|
||||
|
||||
* build(deps): bump actions/checkout from 3 to 4 (dependabot[bot])
|
||||
|
||||
* unix,pipe: fix handling null buffer in uv_pipe_get{sock,peer}name (Saúl
|
||||
Ibarra Corretgé)
|
||||
|
||||
* unix,win: harmonize buffer checking (Saúl Ibarra Corretgé)
|
||||
|
||||
* unix,win: add support for detached threads (Juan José Arboleda)
|
||||
|
||||
* src: add uv_thread_set/getname() methods (Santiago Gimeno)
|
||||
|
||||
* build: fix qemu builds (Ben Noordhuis)
|
||||
|
||||
* win: drop support for windows 8 (Ben Noordhuis)
|
||||
|
||||
* linux: fix uv_cpu_info() arm cpu model detection (Ben Noordhuis)
|
||||
|
||||
* linux: always use io_uring for epoll batching (Ben Noordhuis)
|
||||
|
||||
* doc: clarify repeating timer behavior more (Ben Noordhuis)
|
||||
|
||||
* unix,win: handle nbufs=0 in uv_udp_try_send (Ben Noordhuis)
|
||||
|
||||
* win: use GetQueuedCompletionStatusEx directly (Saúl Ibarra Corretgé)
|
||||
|
||||
* win: enable uv_thread_{get,set}name on MinGW (Saúl Ibarra Corretgé)
|
||||
|
||||
* win: drop support for the legacy MinGW (Saúl Ibarra Corretgé)
|
||||
|
||||
* win,fs: get (most) fstat when no permission (Jameson Nash)
|
||||
|
||||
* win: plug uv_fs_event_start memory leak (amcgoogan)
|
||||
|
||||
* test: address FreeBSD kernel bug causing NULL path in fsevents (Juan José
|
||||
Arboleda)
|
||||
|
||||
* unix: refactor udp sendmsg code (Ben Noordhuis)
|
||||
|
||||
* unix,win: add uv_udp_try_send2 (Ben Noordhuis)
|
||||
|
||||
* test: fix flaky flaky udp_mmsg test (Juan José Arboleda)
|
||||
|
||||
* build: enable fdsan in Android (Juan José Arboleda)
|
||||
|
||||
* test: fix udp-multicast-join for FreeBSD (Juan José Arboleda)
|
||||
|
||||
* win: fix leak processing fs event (Saúl Ibarra Corretgé)
|
||||
|
||||
* src: set a default thread name for workers (Rafael Gonzaga)
|
||||
|
||||
* misc: implement uv_getrusage_thread (Juan José Arboleda)
|
||||
|
||||
|
||||
2024.10.18, Version 1.49.2 (Stable), e1095c7a4373ce00cd8874d8e820de5afb25776e
|
||||
|
||||
Changes since version 1.49.1:
|
||||
|
||||
* win,fs: remove trailing slash in junctions (Hüseyin Açacak)
|
||||
|
||||
* Revert "linux: eliminate a read on eventfd per wakeup" (Ben Noordhuis)
|
||||
|
||||
* win: Fix linked list logic in getaddrinfo (Thad House)
|
||||
|
||||
* win: fix compilation against Windows 24H2 SDK (Thad House)
|
||||
|
||||
* win: remap ERROR_NOACCESS and ERROR_BUFFER_OVERFLOW (Jameson Nash)
|
||||
|
||||
* win,fs: match trailing slash presence in junctions to user input (Jameson
|
||||
Nash)
|
||||
|
||||
|
||||
2024.10.11, Version 1.49.1 (Stable), 8be336f4ee296d20e1c071a44d6adf279e202236
|
||||
|
||||
Changes since version 1.49.0:
|
||||
|
||||
* build: add darwin-syscalls.h to release tarball (Ben Noordhuis)
|
||||
|
||||
* linux: use IORING_SETUP_NO_SQARRAY when available (Ben Noordhuis)
|
||||
|
||||
* linux: use IORING_OP_FTRUNCATE when available (Ben Noordhuis)
|
||||
|
||||
* win: fix pNtQueryDirectoryFile check (Rialbat)
|
||||
|
||||
* win: fix WriteFile() error translation (Santiago Gimeno)
|
||||
|
||||
* win,fs: uv_fs_rmdir() to return ENOENT on file (Santiago Gimeno)
|
||||
|
||||
* win,pipe: ipc code does not support async read (Jameson Nash)
|
||||
|
||||
* netbsd: fix build (Adam)
|
||||
|
||||
* win,fs: fix bug in fs__readdir (Hüseyin Açacak)
|
||||
|
||||
* unix: workaround gcc bug on armv7 (Santiago Gimeno)
|
||||
|
||||
* unix: work around arm-linux-gnueabihf-gcc bug (Ben Noordhuis)
|
||||
|
||||
* unix: fix uv_tcp_keepalive in smartOS (Santiago Gimeno)
|
||||
|
||||
* unix: fix uv_getrusage ru_maxrss on solaris (Poul T Lomholt)
|
||||
|
||||
|
||||
2024.09.25, Version 1.49.0 (Stable), d2e56a5e8d3e39947b78405ca6e4727c70f5568a
|
||||
|
||||
Changes since version 1.48.0:
|
||||
|
||||
* test: fix -Wpointer-to-int-cast on 32 bits systems (Ben Noordhuis)
|
||||
|
||||
* build: add alias for libuv to CMakeLists.txt (Anthony Alayo)
|
||||
|
||||
* linux: create io_uring sqpoll ring lazily (Ben Noordhuis)
|
||||
|
||||
* misc: run sample CI when code changes (Jameson Nash)
|
||||
|
||||
* linux: fix uv_available_parallelism using cgroup (Thomas Walter)
|
||||
|
||||
* doc: fix tty example segfault (hiiizxf)
|
||||
|
||||
* udp,unix: fix sendmsg use-after-free (Geddy)
|
||||
|
||||
* cygwin: implement uv_resident_set_memory (Farzin Monsef)
|
||||
|
||||
* win: almost fix race detecting ESRCH in uv_kill (Santiago Gimeno)
|
||||
|
||||
* test: disable env var test under win32+asan (Ben Noordhuis)
|
||||
|
||||
* unix,fs: fix realpath calls that use the system allocator (Saúl Ibarra
|
||||
Corretgé)
|
||||
|
||||
* sunos: sync tcp keep-alive with other unices (Andy Pan)
|
||||
|
||||
* linux: fix /proc/self/stat executable name parsing (Farzin Monsef)
|
||||
|
||||
* test,ci: fix [AM]San, disable ASLR (Ben Noordhuis)
|
||||
|
||||
* win: remove _alloca usage (Ben Noordhuis)
|
||||
|
||||
* unix: reinstate preadv/pwritev fallback code (Ben Noordhuis)
|
||||
|
||||
* linux: don't delay EPOLL_CTL_DEL operations (Ben Noordhuis)
|
||||
|
||||
* doc: fix typos in ChangeLog (tgolang)
|
||||
|
||||
* unix,win: error on zero delay tcp keepalive (Saúl Ibarra Corretgé)
|
||||
|
||||
* win: simplify uv_once implementation (Saúl Ibarra Corretgé)
|
||||
|
||||
* doc: correct udp socket options documentation (Ben Noordhuis)
|
||||
|
||||
* linux: don't use sendmmsg() for single datagrams (Ben Noordhuis)
|
||||
|
||||
* unix: fix fd leaks in SCM_RIGHTS error path (Ben Noordhuis)
|
||||
|
||||
* win: robustify uv_os_getenv() error checking (Ben Noordhuis)
|
||||
|
||||
* test: use newer ASSERT_MEM_EQ macro (Ben Noordhuis)
|
||||
|
||||
* unix: de-duplicate conditions for using kqueue (Brad King)
|
||||
|
||||
* darwin: simplify uv_hrtime (Saúl Ibarra Corretgé)
|
||||
|
||||
* mailmap: update saghul's main email address (Saúl Ibarra Corretgé)
|
||||
|
||||
* win: remove no longer needed define (Saúl Ibarra Corretgé)
|
||||
|
||||
* doc: fix some typos (josedelinux)
|
||||
|
||||
* linux,darwin: make `uv_fs_copyfile` behaves like `cp -r` (Juan José Arboleda)
|
||||
|
||||
* dragonfly: disable SO_REUSEPORT for UDP socket bindings (Andy Pan)
|
||||
|
||||
* test: remove the obsolete HAVE_KQUEUE macro (Andy Pan)
|
||||
|
||||
* unix: use the presence of SOCK_* instead of OS macros for socketpair (Andy
|
||||
Pan)
|
||||
|
||||
* bsd: support pipe2() on *BSD (Andy Pan)
|
||||
|
||||
* unix: support SO_REUSEPORT with load balancing for TCP (Andy Pan)
|
||||
|
||||
* doc: add entries for extended getpw (Juan José Arboleda)
|
||||
|
||||
* test: fix the flaky test-tcp-reuseport (Andy Pan)
|
||||
|
||||
* aix,ibmi: fix compilation errors in fs_copyfile (Jeffrey H. Johnson)
|
||||
|
||||
* unix: support SO_REUSEPORT with load balancing for UDP (Andy Pan)
|
||||
|
||||
* tcpkeepalive: distinguish OS versions and use proper time units (Andy Pan)
|
||||
|
||||
* win: map ERROR_BAD_EXE_FORMAT to UV_EFTYPE (Hüseyin Açacak)
|
||||
|
||||
* doc: add instruction how to install with Conan (Uilian Ries)
|
||||
|
||||
* unix,win: remove unused req parameter from macros (Viacheslav Muravyev)
|
||||
|
||||
* build: fix android ci build (Ben Noordhuis)
|
||||
|
||||
* unix,win: export wtf8 functions properly (Ben Noordhuis)
|
||||
|
||||
* hurd: add includes and macro prerequisites (Olivier Valentin)
|
||||
|
||||
* hurd: stub uv_thread_setpriority() (Olivier Valentin)
|
||||
|
||||
* ci: use macOS 12 for macOS and iOS builds (Saúl Ibarra Corretgé)
|
||||
|
||||
* darwin: fix crash on iOS(arm64) (郑苏波 (Super Zheng))
|
||||
|
||||
* Create dependabot.yml for updating github-actions (Jameson Nash)
|
||||
|
||||
* doc: correct names of Win32 APIs in fs.rst (zeertzjq)
|
||||
|
||||
* ci: bump upload and download-artifact versions (dependabot[bot])
|
||||
|
||||
* ci: bump actions/setup-python from 4 to 5 (dependabot[bot])
|
||||
|
||||
* ci: bump KyleMayes/install-llvm-action from 1 to 2 (dependabot[bot])
|
||||
|
||||
* win,error: remap ERROR_NO_DATA to EAGAIN (Jameson Nash)
|
||||
|
||||
* test: handle zero-length udp datagram (Ben Noordhuis)
|
||||
|
||||
* misc: remove splay trees macros (Viacheslav Muravyev)
|
||||
|
||||
* test,openbsd: remove superfluous ifdef guard (Ben Noordhuis)
|
||||
|
||||
* win,fs: use posix delete semantics, if supported (Ian Butterworth)
|
||||
|
||||
* win: fix env var in uv_os_homedir and uv_os_tmpdir (Hüseyin Açacak)
|
||||
|
||||
* fsevents: detect watched directory removal (Santiago Gimeno)
|
||||
|
||||
* ci: bump actions/checkout to 4 (dependabot[bot])
|
||||
|
||||
* linux: eliminate a read on eventfd per wakeup (Andy Pan)
|
||||
|
||||
* test: pipe_overlong_path handle ENAMETOOLONG (Abdirahim Musse)
|
||||
|
||||
* win,fs: use the new Windows fast stat API (Hüseyin Açacak)
|
||||
|
||||
* win,pipe: fix race with concurrent readers (Jameson Nash)
|
||||
|
||||
* win,signal: fix data race dispatching SIGWINCH (Jameson Nash)
|
||||
|
||||
* build: ubsan fixes (Matheus Izvekov)
|
||||
|
||||
* linux: disable SQPOLL io_uring by default (Santiago Gimeno)
|
||||
|
||||
* win: fix fs.c ubsan failure (Matheus Izvekov)
|
||||
|
||||
* test: rmdir can return `EEXIST` or `ENOTEMPTY` (Richard Lau)
|
||||
|
||||
* test: check for `UV_CHANGE` or `UV_RENAME` event (Richard Lau)
|
||||
|
||||
* unix,fs: silence -Wunused-result warning (Santiago Gimeno)
|
||||
|
||||
* linux: support abstract unix socket autobinding (Ben Noordhuis)
|
||||
|
||||
* kqueue: use EVFILT_USER for async if available (Andy Pan)
|
||||
|
||||
* win: remove deprecated GetVersionExW call (Shelley Vohr)
|
||||
|
||||
* doc: document uv_loop_option (握猫猫)
|
||||
|
||||
* doc: fix the `uv_*_set_data` series of functions (握猫猫)
|
||||
|
||||
* doc: properly label enumerations and types (握猫猫)
|
||||
|
||||
* doc: document specific macOS fs_event behavior (Santiago Gimeno)
|
||||
|
||||
* win,pipe: restore fallback handling for blocking pipes (Jameson Nash)
|
||||
|
||||
* unix,win: remove unused rb-tree macro parameters (Viacheslav Muravyev)
|
||||
|
||||
* win: compute parallelism from process cpu affinity (Ben Noordhuis)
|
||||
|
||||
* win: use NtQueryInformationProcess in uv_os_getppid (Zuohui Yang)
|
||||
|
||||
* win,pipe: fix missing assignment to success (Jameson Nash)
|
||||
|
||||
* win: fix uv_available_parallelism on win32 (Ben Noordhuis)
|
||||
|
||||
* win,pipe: fix another missing assignment to success (Jameson Nash)
|
||||
|
||||
* kqueue: disallow ill-suited file descriptor kinds (Andy Pan)
|
||||
|
||||
* unix: restore tty attributes on handle close (Ben Noordhuis)
|
||||
|
||||
* test: delete test with invalid assumption (Ben Noordhuis)
|
||||
|
||||
* dragonflybsd: fix compilation failure (Jeffrey H. Johnson)
|
||||
|
||||
* test: run android tests on ci (Edigleysson Silva (Edy))
|
||||
|
||||
* darwin: add udp mmsg support (Raihaan Shouhell)
|
||||
|
||||
* unix: work around arm-linux-gnueabihf-gcc bug (Ben Noordhuis)
|
||||
|
||||
* unix: expand uv_available_parallelism() to support more platforms (Ondřej
|
||||
Surý)
|
||||
|
||||
* doc: add known issue in armv7 (Santiago Gimeno)
|
||||
|
||||
|
||||
2024.02.07, Version 1.48.0 (Stable), e9f29cb984231524e3931aa0ae2c5dae1a32884e
|
||||
|
||||
Changes since version 1.47.0:
|
||||
@ -911,7 +1243,7 @@ Changes since version 1.41.0:
|
||||
|
||||
* zos: treat __rfim_utok as binary (Shuowang (Wayne) Zhang)
|
||||
|
||||
* zos: use execvpe() to set environ explictly (Shuowang (Wayne) Zhang)
|
||||
* zos: use execvpe() to set environ explicitly (Shuowang (Wayne) Zhang)
|
||||
|
||||
* zos: use custom proctitle implementation (Shuowang (Wayne) Zhang)
|
||||
|
||||
@ -3417,7 +3749,7 @@ Changes since version 1.9.1:
|
||||
|
||||
* zos: implement uv__io_check_fd (John Barboza)
|
||||
|
||||
* unix: unneccessary use const qualifier in container_of (John Barboza)
|
||||
* unix: unnecessary use const qualifier in container_of (John Barboza)
|
||||
|
||||
* win,tty: add support for ANSI codes in win10 v1511 (Imran Iqbal)
|
||||
|
||||
@ -5520,7 +5852,7 @@ Changes since version 0.11.8:
|
||||
is an int64_t, and no longer an int. (Bert Belder)
|
||||
|
||||
* process: make uv_spawn() return some types of errors immediately on windows,
|
||||
instead of passing the error code the the exit callback. This brings it on
|
||||
instead of passing the error code the exit callback. This brings it on
|
||||
par with libuv's behavior on unix. (Bert Belder)
|
||||
|
||||
|
||||
|
||||
3
LINKS.md
3
LINKS.md
@ -37,6 +37,7 @@
|
||||
* [Pixie-io](https://github.com/pixie-io/pixie): Open-source observability tool for Kubernetes applications.
|
||||
* [potion](https://github.com/perl11/potion)/[p2](https://github.com/perl11/p2): runtime
|
||||
* [racer](https://libraries.io/rubygems/racer): Ruby web server written as an C extension
|
||||
* [scala-native-loop](https://github.com/scala-native/scala-native-loop): Extensible event loop and async-oriented IO for Scala Native; powered by libuv
|
||||
* [Socket Runtime](https://sockets.sh): A runtime for creating native cross-platform software on mobile and desktop using HTML, CSS, and JavaScript
|
||||
* [spider-gazelle](https://github.com/cotag/spider-gazelle): Ruby web server using libuv bindings
|
||||
* [Suave](http://suave.io/): A simple web development F# library providing a lightweight web server and a set of combinators to manipulate route flow and task composition
|
||||
@ -107,3 +108,5 @@
|
||||
* [node.pas](https://github.com/vovach777/node.pas) NodeJS-like ecosystem
|
||||
* Haskell
|
||||
* [Z.Haskell](https://z.haskell.world)
|
||||
* C3
|
||||
* [libuv.c3l](https://github.com/velikoss/libuv.c3l)
|
||||
|
||||
@ -4,6 +4,9 @@ libuv is currently managed by the following individuals:
|
||||
|
||||
* **Ben Noordhuis** ([@bnoordhuis](https://github.com/bnoordhuis))
|
||||
- GPG key: D77B 1E34 243F BAF0 5F8E 9CC3 4F55 C8C8 46AB 89B9 (pubkey-bnoordhuis)
|
||||
* **Colin Ihrig** ([@cjihrig](https://github.com/cjihrig))
|
||||
- GPG key: 94AE 3667 5C46 4D64 BAFA 68DD 7434 390B DBE9 B9C5 (pubkey-cjihrig)
|
||||
- GPG key: 5735 3E0D BDAA A7E8 39B6 6A1A FF47 D5E4 AD8B 4FDC (pubkey-cjihrig-kb)
|
||||
* **Jameson Nash** ([@vtjnash](https://github.com/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)
|
||||
@ -24,9 +27,6 @@ libuv is currently managed by the following individuals:
|
||||
* **Anna Henningsen** ([@addaleax](https://github.com/addaleax))
|
||||
* **Bartosz Sosnowski** ([@bzoz](https://github.com/bzoz))
|
||||
* **Bert Belder** ([@piscisaureus](https://github.com/piscisaureus))
|
||||
* **Colin Ihrig** ([@cjihrig](https://github.com/cjihrig))
|
||||
- GPG key: 94AE 3667 5C46 4D64 BAFA 68DD 7434 390B DBE9 B9C5 (pubkey-cjihrig)
|
||||
- GPG key: 5735 3E0D BDAA A7E8 39B6 6A1A FF47 D5E4 AD8B 4FDC (pubkey-cjihrig-kb)
|
||||
* **Fedor Indutny** ([@indutny](https://github.com/indutny))
|
||||
- GPG key: AF2E EA41 EC34 47BF DD86 FED9 D706 3CCE 19B7 E890 (pubkey-indutny)
|
||||
* **Imran Iqbal** ([@imran-iq](https://github.com/imran-iq))
|
||||
|
||||
@ -59,7 +59,7 @@ if WINNT
|
||||
uvinclude_HEADERS += include/uv/win.h include/uv/tree.h
|
||||
AM_CPPFLAGS += -I$(top_srcdir)/src/win \
|
||||
-DWIN32_LEAN_AND_MEAN \
|
||||
-D_WIN32_WINNT=0x0602
|
||||
-D_WIN32_WINNT=0x0A00
|
||||
libuv_la_SOURCES += src/win/async.c \
|
||||
src/win/atomicops-inl.h \
|
||||
src/win/core.c \
|
||||
@ -198,6 +198,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
|
||||
test/test-hrtime.c \
|
||||
test/test-idle.c \
|
||||
test/test-idna.c \
|
||||
test/test-iouring-pollhup.c \
|
||||
test/test-ip4-addr.c \
|
||||
test/test-ip6-addr.c \
|
||||
test/test-ip-name.c \
|
||||
@ -275,6 +276,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
|
||||
test/test-tcp-flags.c \
|
||||
test/test-tcp-open.c \
|
||||
test/test-tcp-read-stop.c \
|
||||
test/test-tcp-reuseport.c \
|
||||
test/test-tcp-read-stop-start.c \
|
||||
test/test-tcp-rst.c \
|
||||
test/test-tcp-shutdown-after-write.c \
|
||||
@ -292,6 +294,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
|
||||
test/test-thread-equal.c \
|
||||
test/test-thread.c \
|
||||
test/test-thread-affinity.c \
|
||||
test/test-thread-name.c \
|
||||
test/test-thread-priority.c \
|
||||
test/test-threadpool-cancel.c \
|
||||
test/test-threadpool.c \
|
||||
@ -324,6 +327,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
|
||||
test/test-udp-send-unreachable.c \
|
||||
test/test-udp-try-send.c \
|
||||
test/test-udp-recv-in-a-row.c \
|
||||
test/test-udp-reuseport.c \
|
||||
test/test-uname.c \
|
||||
test/test-walk-handles.c \
|
||||
test/test-watcher-cross-stop.c
|
||||
@ -427,6 +431,7 @@ libuv_la_CFLAGS += -D_DARWIN_UNLIMITED_SELECT=1
|
||||
libuv_la_SOURCES += src/unix/bsd-ifaddrs.c \
|
||||
src/unix/darwin-proctitle.c \
|
||||
src/unix/darwin-stub.h \
|
||||
src/unix/darwin-syscalls.h \
|
||||
src/unix/darwin.c \
|
||||
src/unix/fsevents.c \
|
||||
src/unix/kqueue.c \
|
||||
|
||||
12
README.md
12
README.md
@ -232,6 +232,18 @@ $ ./bootstrap-vcpkg.sh # for bash
|
||||
$ ./vcpkg install libuv
|
||||
```
|
||||
|
||||
### Install with Conan
|
||||
|
||||
You can install pre-built binaries for libuv or build it from source using [Conan](https://conan.io/). Use the following command:
|
||||
|
||||
```bash
|
||||
conan install --requires="libuv/[*]" --build=missing
|
||||
```
|
||||
|
||||
The libuv Conan recipe is kept up to date by Conan maintainers and community contributors.
|
||||
If the version is out of date, please [create an issue or pull request](https://github.com/conan-io/conan-center-index) on the ConanCenterIndex repository.
|
||||
|
||||
|
||||
### Running tests
|
||||
|
||||
Some tests are timing sensitive. Relaxing test timeouts may be necessary
|
||||
|
||||
@ -4,14 +4,14 @@
|
||||
|---|---|---|---|
|
||||
| GNU/Linux | Tier 1 | Linux >= 3.10 with glibc >= 2.17 | |
|
||||
| macOS | Tier 1 | macOS >= 11 | Currently supported macOS releases |
|
||||
| Windows | Tier 1 | >= Windows 8 | VS 2015 and later are supported |
|
||||
| Windows | Tier 1 | >= Windows 10 | VS 2015 and later are supported |
|
||||
| FreeBSD | Tier 2 | >= 12 | |
|
||||
| AIX | Tier 2 | >= 6 | Maintainers: @libuv/aix |
|
||||
| IBM i | Tier 2 | >= IBM i 7.2 | Maintainers: @libuv/ibmi |
|
||||
| z/OS | Tier 2 | >= V2R2 | Maintainers: @libuv/zos |
|
||||
| Linux with musl | Tier 2 | musl >= 1.0 | |
|
||||
| Android | Tier 3 | NDK >= r15b | Android 7.0, `-DANDROID_PLATFORM=android-24` |
|
||||
| MinGW | Tier 3 | MinGW32 and MinGW-w64 | |
|
||||
| MinGW | Tier 3 | MinGW-w64 | |
|
||||
| SunOS | Tier 3 | Solaris 121 and later | |
|
||||
| Other | Tier 3 | N/A | |
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
AC_PREREQ(2.57)
|
||||
AC_INIT([libuv], [1.48.1-dev], [https://github.com/libuv/libuv/issues])
|
||||
AC_INIT([libuv], [1.50.1-dev], [https://github.com/libuv/libuv/issues])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
m4_include([m4/libuv-extra-automake-flags.m4])
|
||||
m4_include([m4/as_case.m4])
|
||||
|
||||
@ -16,7 +16,10 @@ Starting with libuv v1.45.0, some file operations on Linux are handed off to
|
||||
`io_uring <https://en.wikipedia.org/wiki/Io_uring>` when possible. Apart from
|
||||
a (sometimes significant) increase in throughput there should be no change in
|
||||
observable behavior. Libuv reverts to using its threadpool when the necessary
|
||||
kernel features are unavailable or unsuitable.
|
||||
kernel features are unavailable or unsuitable. Starting with libuv v1.49.0 this
|
||||
behavior was reverted and Libuv on Linux by default will be using the threadpool
|
||||
again. In order to enable io_uring the :c:type:`uv_loop_t` instance must be
|
||||
configured with the :c:type:`UV_LOOP_ENABLE_IO_URING_SQPOLL` option.
|
||||
|
||||
.. note::
|
||||
On Windows `uv_fs_*` functions use utf-8 encoding.
|
||||
@ -129,10 +132,9 @@ Data types
|
||||
uint64_t f_spare[4];
|
||||
} uv_statfs_t;
|
||||
|
||||
.. c:enum:: uv_dirent_t
|
||||
.. c:enum:: uv_dirent_type_t
|
||||
|
||||
Cross platform (reduced) equivalent of ``struct dirent``.
|
||||
Used in :c:func:`uv_fs_scandir_next`.
|
||||
Type of dirent.
|
||||
|
||||
::
|
||||
|
||||
@ -147,6 +149,14 @@ Data types
|
||||
UV_DIRENT_BLOCK
|
||||
} uv_dirent_type_t;
|
||||
|
||||
|
||||
.. c:type:: uv_dirent_t
|
||||
|
||||
Cross platform (reduced) equivalent of ``struct dirent``.
|
||||
Used in :c:func:`uv_fs_scandir_next`.
|
||||
|
||||
::
|
||||
|
||||
typedef struct uv_dirent_s {
|
||||
const char* name;
|
||||
uv_dirent_type_t type;
|
||||
@ -420,6 +430,12 @@ API
|
||||
|
||||
Equivalent to :man:`utime(2)`, :man:`futimes(3)` and :man:`lutimes(3)` respectively.
|
||||
|
||||
Passing `UV_FS_UTIME_NOW` as the atime or mtime sets the timestamp to the
|
||||
current time.
|
||||
|
||||
Passing `UV_FS_UTIME_OMIT` as the atime or mtime leaves the timestamp
|
||||
untouched.
|
||||
|
||||
.. note::
|
||||
z/OS: `uv_fs_lutime()` is not implemented for z/OS. It can still be called but will return
|
||||
``UV_ENOSYS``.
|
||||
@ -454,7 +470,7 @@ API
|
||||
|
||||
.. c:function:: int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb)
|
||||
|
||||
Equivalent to :man:`realpath(3)` on Unix. Windows uses `GetFinalPathNameByHandle <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlea>`_.
|
||||
Equivalent to :man:`realpath(3)` on Unix. Windows uses `GetFinalPathNameByHandleW <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew>`_.
|
||||
The resulting string is stored in `req->ptr`.
|
||||
|
||||
.. warning::
|
||||
@ -653,7 +669,7 @@ File open constants
|
||||
|
||||
.. note::
|
||||
`UV_FS_O_RANDOM` is only supported on Windows via
|
||||
`FILE_FLAG_RANDOM_ACCESS <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea>`_.
|
||||
`FILE_FLAG_RANDOM_ACCESS <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew>`_.
|
||||
|
||||
.. c:macro:: UV_FS_O_RDONLY
|
||||
|
||||
@ -670,7 +686,7 @@ File open constants
|
||||
|
||||
.. note::
|
||||
`UV_FS_O_SEQUENTIAL` is only supported on Windows via
|
||||
`FILE_FLAG_SEQUENTIAL_SCAN <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea>`_.
|
||||
`FILE_FLAG_SEQUENTIAL_SCAN <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew>`_.
|
||||
|
||||
.. c:macro:: UV_FS_O_SHORT_LIVED
|
||||
|
||||
@ -678,7 +694,7 @@ File open constants
|
||||
|
||||
.. note::
|
||||
`UV_FS_O_SHORT_LIVED` is only supported on Windows via
|
||||
`FILE_ATTRIBUTE_TEMPORARY <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea>`_.
|
||||
`FILE_ATTRIBUTE_TEMPORARY <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew>`_.
|
||||
|
||||
.. c:macro:: UV_FS_O_SYMLINK
|
||||
|
||||
@ -699,7 +715,7 @@ File open constants
|
||||
|
||||
.. note::
|
||||
`UV_FS_O_TEMPORARY` is only supported on Windows via
|
||||
`FILE_ATTRIBUTE_TEMPORARY <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea>`_.
|
||||
`FILE_ATTRIBUTE_TEMPORARY <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew>`_.
|
||||
|
||||
.. c:macro:: UV_FS_O_TRUNC
|
||||
|
||||
|
||||
@ -45,9 +45,14 @@ Data types
|
||||
be a relative path to a file contained in the directory, or `NULL` if the
|
||||
file name cannot be determined.
|
||||
|
||||
The `events` parameter is an ORed mask of :c:type:`uv_fs_event` elements.
|
||||
The `events` parameter is an ORed mask of :c:enum:`uv_fs_event` elements.
|
||||
|
||||
.. c:type:: uv_fs_event
|
||||
.. note::
|
||||
For FreeBSD path could sometimes be `NULL` due to a kernel bug.
|
||||
|
||||
.. _Reference: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=197695
|
||||
|
||||
.. c:enum:: uv_fs_event
|
||||
|
||||
Event types that :c:type:`uv_fs_event_t` handles monitor.
|
||||
|
||||
@ -58,7 +63,7 @@ Data types
|
||||
UV_CHANGE = 2
|
||||
};
|
||||
|
||||
.. c:type:: uv_fs_event_flags
|
||||
.. c:enum:: uv_fs_event_flags
|
||||
|
||||
Flags that can be passed to :c:func:`uv_fs_event_start` to control its
|
||||
behavior.
|
||||
@ -109,10 +114,13 @@ API
|
||||
.. c:function:: int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, const char* path, unsigned int flags)
|
||||
|
||||
Start the handle with the given callback, which will watch the specified
|
||||
`path` for changes. `flags` can be an ORed mask of :c:type:`uv_fs_event_flags`.
|
||||
`path` for changes. `flags` can be an ORed mask of :c:enum:`uv_fs_event_flags`.
|
||||
|
||||
.. note:: Currently the only supported flag is ``UV_FS_EVENT_RECURSIVE`` and
|
||||
only on OSX and Windows.
|
||||
.. note:: On macOS, events collected by the OS immediately before calling
|
||||
``uv_fs_event_start`` might be reported to the `uv_fs_event_cb`
|
||||
callback.
|
||||
|
||||
.. c:function:: int uv_fs_event_stop(uv_fs_event_t* handle)
|
||||
|
||||
|
||||
@ -333,7 +333,7 @@ to hand off their I/O to other processes. Applications include load-balancing
|
||||
servers, worker processes and other ways to make optimum use of CPU. libuv only
|
||||
supports sending **TCP sockets or other pipes** over pipes for now.
|
||||
|
||||
To demonstrate, we will look at a echo server implementation that hands of
|
||||
To demonstrate, we will look at an echo server implementation that hands off
|
||||
clients to worker processes in a round-robin fashion. This program is a bit
|
||||
involved, and while only snippets are included in the book, it is recommended
|
||||
to read the full code to really understand it.
|
||||
|
||||
@ -94,7 +94,7 @@ Public members
|
||||
|
||||
.. c:member:: uv_handle_type uv_handle_t.type
|
||||
|
||||
The :c:type:`uv_handle_type`, indicating the type of the underlying handle. Readonly.
|
||||
The :c:enum:`uv_handle_type`, indicating the type of the underlying handle. Readonly.
|
||||
|
||||
.. c:member:: void* uv_handle_t.data
|
||||
|
||||
@ -248,7 +248,7 @@ just for some handle types.
|
||||
|
||||
.. versionadded:: 1.19.0
|
||||
|
||||
.. c:function:: void* uv_handle_set_data(uv_handle_t* handle, void* data)
|
||||
.. c:function:: void uv_handle_set_data(uv_handle_t* handle, void* data)
|
||||
|
||||
Sets `handle->data` to `data`.
|
||||
|
||||
|
||||
@ -58,5 +58,5 @@ libuv can be downloaded from `here <https://dist.libuv.org/dist/>`_.
|
||||
Installation
|
||||
------------
|
||||
|
||||
Installation instructions can be found in `the README <https://github.com/libuv/libuv/blob/master/README.md>`_.
|
||||
Installation instructions can be found in the `README <https://github.com/libuv/libuv/blob/master/README.md>`_.
|
||||
|
||||
|
||||
@ -16,6 +16,19 @@ Data types
|
||||
|
||||
Loop data type.
|
||||
|
||||
.. c:enum:: uv_loop_option
|
||||
|
||||
Additional loop options.
|
||||
See :c:func:`uv_loop_configure`.
|
||||
|
||||
::
|
||||
|
||||
typedef enum {
|
||||
UV_LOOP_BLOCK_SIGNAL = 0,
|
||||
UV_METRICS_IDLE_TIME,
|
||||
UV_LOOP_USE_IO_URING_SQPOLL
|
||||
} uv_loop_option;
|
||||
|
||||
.. c:enum:: uv_run_mode
|
||||
|
||||
Mode used to run the loop with :c:func:`uv_run`.
|
||||
@ -73,8 +86,13 @@ API
|
||||
|
||||
This option is necessary to use :c:func:`uv_metrics_idle_time`.
|
||||
|
||||
- UV_LOOP_ENABLE_IO_URING_SQPOLL: Enable SQPOLL io_uring instance to handle
|
||||
asynchronous file system operations.
|
||||
|
||||
.. versionchanged:: 1.39.0 added the UV_METRICS_IDLE_TIME option.
|
||||
|
||||
.. versionchanged:: 1.49.0 added the UV_LOOP_ENABLE_IO_URING_SQPOLL option.
|
||||
|
||||
.. c:function:: int uv_loop_close(uv_loop_t* loop)
|
||||
|
||||
Releases all internal loop resources. Call this function only when the loop
|
||||
@ -238,7 +256,7 @@ API
|
||||
|
||||
.. versionadded:: 1.19.0
|
||||
|
||||
.. c:function:: void* uv_loop_set_data(uv_loop_t* loop, void* data)
|
||||
.. c:function:: void uv_loop_set_data(uv_loop_t* loop, void* data)
|
||||
|
||||
Sets `loop->data` to `data`.
|
||||
|
||||
|
||||
@ -199,6 +199,18 @@ Data types
|
||||
char* homedir;
|
||||
} uv_passwd_t;
|
||||
|
||||
.. c:type:: uv_group_t
|
||||
|
||||
Data type for group file information.
|
||||
|
||||
::
|
||||
|
||||
typedef struct uv_group_s {
|
||||
char* groupname;
|
||||
unsigned long gid;
|
||||
char** members;
|
||||
} uv_group_t;
|
||||
|
||||
.. c:type:: uv_utsname_t
|
||||
|
||||
Data type for operating system name and version information.
|
||||
@ -348,6 +360,17 @@ API
|
||||
On Windows not all fields are set, the unsupported fields are filled with zeroes.
|
||||
See :c:type:`uv_rusage_t` for more details.
|
||||
|
||||
.. c:function:: int uv_getrusage_thread(uv_rusage_t* rusage)
|
||||
|
||||
Gets the resource usage measures for the calling thread.
|
||||
|
||||
.. versionadded:: 1.50.0
|
||||
|
||||
.. note::
|
||||
Not supported on all platforms. May return `UV_ENOTSUP`.
|
||||
On macOS and Windows not all fields are set, the unsupported fields are filled with zeroes.
|
||||
See :c:type:`uv_rusage_t` for more details.
|
||||
|
||||
.. c:function:: uv_pid_t uv_os_getpid(void)
|
||||
|
||||
Returns the current process ID.
|
||||
@ -566,6 +589,35 @@ API
|
||||
|
||||
.. versionadded:: 1.9.0
|
||||
|
||||
.. c:function:: int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid)
|
||||
|
||||
Gets a subset of the password file entry for the provided uid.
|
||||
The populated data includes the username, euid, gid, shell,
|
||||
and home directory. On non-Windows systems, all data comes from
|
||||
:man:`getpwuid_r(3)`. On Windows, uid and gid are set to -1 and have no
|
||||
meaning, and shell is `NULL`. After successfully calling this function, the
|
||||
memory allocated to `pwd` needs to be freed with
|
||||
:c:func:`uv_os_free_passwd`.
|
||||
|
||||
.. versionadded:: 1.45.0
|
||||
|
||||
.. c:function:: int uv_os_get_group(uv_group_t* group, uv_uid_t gid)
|
||||
|
||||
Gets a subset of the group file entry for the provided uid.
|
||||
The populated data includes the group name, gid, and members. On non-Windows
|
||||
systems, all data comes from :man:`getgrgid_r(3)`. On Windows, uid and gid
|
||||
are set to -1 and have no meaning. After successfully calling this function,
|
||||
the memory allocated to `group` needs to be freed with
|
||||
:c:func:`uv_os_free_group`.
|
||||
|
||||
.. versionadded:: 1.45.0
|
||||
|
||||
.. c:function:: void uv_os_free_group(uv_passwd_t* pwd)
|
||||
|
||||
Frees the memory previously allocated with :c:func:`uv_os_get_group`.
|
||||
|
||||
.. versionadded:: 1.45.0
|
||||
|
||||
.. c:function:: void uv_os_free_passwd(uv_passwd_t* pwd)
|
||||
|
||||
Frees the `pwd` memory previously allocated with :c:func:`uv_os_get_passwd`.
|
||||
|
||||
@ -45,7 +45,7 @@ Data types
|
||||
|
||||
Type definition for callback passed to :c:func:`uv_poll_start`.
|
||||
|
||||
.. c:type:: uv_poll_event
|
||||
.. c:enum:: uv_poll_event
|
||||
|
||||
Poll event types
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@ Data types
|
||||
will indicate the exit status and the signal that caused the process to
|
||||
terminate, if any.
|
||||
|
||||
.. c:type:: uv_process_flags
|
||||
.. c:enum:: uv_process_flags
|
||||
|
||||
Flags to be set on the flags field of :c:type:`uv_process_options_t`.
|
||||
|
||||
@ -190,7 +190,7 @@ Public members
|
||||
Command line arguments. args[0] should be the path to the program. On
|
||||
Windows this uses `CreateProcess` which concatenates the arguments into a
|
||||
string this can cause some strange errors. See the
|
||||
``UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS`` flag on :c:type:`uv_process_flags`.
|
||||
``UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS`` flag on :c:enum:`uv_process_flags`.
|
||||
|
||||
.. c:member:: char** uv_process_options_t.env
|
||||
|
||||
@ -203,7 +203,7 @@ Public members
|
||||
.. c:member:: unsigned int uv_process_options_t.flags
|
||||
|
||||
Various flags that control how :c:func:`uv_spawn` behaves. See
|
||||
:c:type:`uv_process_flags`.
|
||||
:c:enum:`uv_process_flags`.
|
||||
|
||||
.. c:member:: int uv_process_options_t.stdio_count
|
||||
.. c:member:: uv_stdio_container_t* uv_process_options_t.stdio
|
||||
|
||||
@ -21,17 +21,9 @@ Data types
|
||||
|
||||
Union of all request types.
|
||||
|
||||
.. c:enum:: uv_req_type
|
||||
|
||||
Public members
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
.. c:member:: void* uv_req_t.data
|
||||
|
||||
Space for user-defined arbitrary data. libuv does not use this field.
|
||||
|
||||
.. c:member:: uv_req_type uv_req_t.type
|
||||
|
||||
Indicated the type of request. Readonly.
|
||||
The kind of the libuv request.
|
||||
|
||||
::
|
||||
|
||||
@ -50,6 +42,18 @@ Public members
|
||||
} uv_req_type;
|
||||
|
||||
|
||||
Public members
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
.. c:member:: void* uv_req_t.data
|
||||
|
||||
Space for user-defined arbitrary data. libuv does not use this field.
|
||||
|
||||
.. c:member:: uv_req_type uv_req_t.type
|
||||
|
||||
The :c:enum:`uv_req_type`, indicating the type of the request. Readonly.
|
||||
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
@ -95,7 +99,7 @@ API
|
||||
|
||||
.. versionadded:: 1.19.0
|
||||
|
||||
.. c:function:: void* uv_req_set_data(uv_req_t* req, void* data)
|
||||
.. c:function:: void uv_req_set_data(uv_req_t* req, void* data)
|
||||
|
||||
Sets `req->data` to `data`.
|
||||
|
||||
|
||||
@ -16,6 +16,28 @@ Data types
|
||||
|
||||
TCP handle type.
|
||||
|
||||
.. c:enum:: uv_tcp_flags
|
||||
|
||||
Flags used in :c:func:`uv_tcp_bind`.
|
||||
|
||||
::
|
||||
|
||||
enum uv_tcp_flags {
|
||||
/* Used with uv_tcp_bind, when an IPv6 address is used. */
|
||||
UV_TCP_IPV6ONLY = 1,
|
||||
|
||||
/* Enable SO_REUSEPORT socket option when binding the handle.
|
||||
* This allows completely duplicate bindings by multiple processes
|
||||
* or threads if they all set SO_REUSEPORT before binding the port.
|
||||
* Incoming connections are distributed across the participating
|
||||
* listener sockets.
|
||||
*
|
||||
* This flag is available only on Linux 3.9+, DragonFlyBSD 3.6+,
|
||||
* FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ for now.
|
||||
*/
|
||||
UV_TCP_REUSEPORT = 2,
|
||||
};
|
||||
|
||||
|
||||
Public members
|
||||
^^^^^^^^^^^^^^
|
||||
@ -65,6 +87,10 @@ API
|
||||
at the end of this procedure, then the handle is destroyed with a
|
||||
``UV_ETIMEDOUT`` error passed to the corresponding callback.
|
||||
|
||||
If `delay` is less than 1 then ``UV_EINVAL`` is returned.
|
||||
|
||||
.. versionchanged:: 1.49.0 If `delay` is less than 1 then ``UV_EINVAL``` is returned.
|
||||
|
||||
.. c:function:: int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable)
|
||||
|
||||
Enable / disable simultaneous asynchronous accept requests that are
|
||||
@ -77,16 +103,34 @@ API
|
||||
|
||||
.. c:function:: int uv_tcp_bind(uv_tcp_t* handle, const struct sockaddr* addr, unsigned int flags)
|
||||
|
||||
Bind the handle to an address and port. `addr` should point to an
|
||||
initialized ``struct sockaddr_in`` or ``struct sockaddr_in6``.
|
||||
Bind the handle to an address and port.
|
||||
|
||||
When the port is already taken, you can expect to see an ``UV_EADDRINUSE``
|
||||
error from :c:func:`uv_listen` or :c:func:`uv_tcp_connect`. That is,
|
||||
a successful call to this function does not guarantee that the call
|
||||
to :c:func:`uv_listen` or :c:func:`uv_tcp_connect` will succeed as well.
|
||||
error from :c:func:`uv_listen` or :c:func:`uv_tcp_connect` unless you specify
|
||||
``UV_TCP_REUSEPORT`` in `flags` for all the binding sockets. That is, a successful
|
||||
call to this function does not guarantee that the call to :c:func:`uv_listen` or
|
||||
:c:func:`uv_tcp_connect` will succeed as well.
|
||||
|
||||
`flags` can contain ``UV_TCP_IPV6ONLY``, in which case dual-stack support
|
||||
is disabled and only IPv6 is used.
|
||||
:param handle: TCP handle. It should have been initialized with :c:func:`uv_tcp_init`.
|
||||
|
||||
:param addr: Address to bind to. It should point to an initialized ``struct sockaddr_in``
|
||||
or ``struct sockaddr_in6``.
|
||||
|
||||
:param flags: Flags that control the behavior of binding the socket.
|
||||
``UV_TCP_IPV6ONLY`` can be contained in `flags` to disable dual-stack
|
||||
support and only use IPv6.
|
||||
``UV_TCP_REUSEPORT`` can be contained in `flags` to enable the socket option
|
||||
`SO_REUSEPORT` with the capability of load balancing that distribute incoming
|
||||
connections across all listening sockets in multiple processes or threads.
|
||||
|
||||
:returns: 0 on success, or an error code < 0 on failure.
|
||||
|
||||
.. versionchanged:: 1.49.0 added the ``UV_TCP_REUSEPORT`` flag.
|
||||
|
||||
.. note::
|
||||
``UV_TCP_REUSEPORT`` flag is available only on Linux 3.9+, DragonFlyBSD 3.6+,
|
||||
FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ at the moment. On other platforms
|
||||
this function will return an UV_ENOTSUP error.
|
||||
|
||||
.. c:function:: int uv_tcp_getsockname(const uv_tcp_t* handle, struct sockaddr* name, int* namelen)
|
||||
|
||||
|
||||
@ -78,6 +78,14 @@ Threads
|
||||
|
||||
.. versionchanged:: 1.4.1 returns a UV_E* error code on failure
|
||||
|
||||
.. c:function:: int uv_thread_detach(uv_thread_t* tid)
|
||||
|
||||
Detaches a thread. Detached threads automatically release their
|
||||
resources upon termination, eliminating the need for the application to
|
||||
call `uv_thread_join`.
|
||||
|
||||
.. versionadded:: 1.50.0
|
||||
|
||||
.. c:function:: int uv_thread_create_ex(uv_thread_t* tid, const uv_thread_options_t* params, uv_thread_cb entry, void* arg)
|
||||
|
||||
Like :c:func:`uv_thread_create`, but additionally specifies options for creating a new thread.
|
||||
@ -132,7 +140,29 @@ Threads
|
||||
.. c:function:: int uv_thread_join(uv_thread_t *tid)
|
||||
.. c:function:: int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2)
|
||||
|
||||
.. c:function:: int uv_thread_setname(const char* name)
|
||||
|
||||
Sets the name of the current thread. Different platforms define different limits on the max number of characters
|
||||
a thread name can be: Linux, IBM i (16), macOS (64), Windows (32767), and NetBSD (32), etc. `uv_thread_setname()`
|
||||
will truncate it in case `name` is larger than the limit of the platform.
|
||||
|
||||
Not supported on Windows Server 2016, returns `UV_ENOSYS`.
|
||||
|
||||
.. versionadded:: 1.50.0
|
||||
|
||||
.. c:function:: int uv_thread_getname(uv_thread_t* tid, char* name, size_t* size)
|
||||
|
||||
Gets the name of the thread specified by `tid`. The thread name is copied, with the trailing NUL, into the buffer
|
||||
pointed to by `name`. The `size` parameter specifies the size of the buffer pointed to by `name`.
|
||||
The buffer should be large enough to hold the name of the thread plus the trailing NUL, or it will be truncated to fit
|
||||
with the trailing NUL.
|
||||
|
||||
Not supported on Windows Server 2016, returns `UV_ENOSYS`.
|
||||
|
||||
.. versionadded:: 1.50.0
|
||||
|
||||
.. c:function:: int uv_thread_setpriority(uv_thread_t tid, int priority)
|
||||
|
||||
If the function succeeds, the return value is 0.
|
||||
If the function fails, the return value is less than zero.
|
||||
Sets the scheduling priority of the thread specified by tid. It requires elevated
|
||||
@ -140,7 +170,9 @@ Threads
|
||||
The priority can be set to the following constants. UV_THREAD_PRIORITY_HIGHEST,
|
||||
UV_THREAD_PRIORITY_ABOVE_NORMAL, UV_THREAD_PRIORITY_NORMAL,
|
||||
UV_THREAD_PRIORITY_BELOW_NORMAL, UV_THREAD_PRIORITY_LOWEST.
|
||||
|
||||
.. c:function:: int uv_thread_getpriority(uv_thread_t tid, int* priority)
|
||||
|
||||
If the function succeeds, the return value is 0.
|
||||
If the function fails, the return value is less than zero.
|
||||
Retrieves the scheduling priority of the thread specified by tid. The value in the
|
||||
|
||||
@ -17,6 +17,8 @@ is 1024).
|
||||
.. versionchanged:: 1.45.0 threads now have an 8 MB stack instead of the
|
||||
(sometimes too low) platform default.
|
||||
|
||||
.. versionchanged:: 1.50.0 threads now have a default name of libuv-worker.
|
||||
|
||||
The threadpool is global and shared across all event loops. When a particular
|
||||
function makes use of the threadpool (i.e. when using :c:func:`uv_queue_work`)
|
||||
libuv preallocates and initializes the maximum number of threads allowed by
|
||||
|
||||
@ -6,6 +6,15 @@
|
||||
|
||||
Timer handles are used to schedule callbacks to be called in the future.
|
||||
|
||||
Timers are either single-shot or repeating. Repeating timers do not adjust
|
||||
for overhead but are rearmed relative to the event loop's idea of "now".
|
||||
|
||||
Libuv updates its idea of "now" right before executing timer callbacks, and
|
||||
right after waking up from waiting for I/O. See also :c:func:`uv_update_time`.
|
||||
|
||||
Example: a repeating timer with a 50 ms interval whose callback takes 17 ms
|
||||
to complete, runs again 33 ms later. If other tasks take longer than 33 ms,
|
||||
the timer callback runs as soon as possible.
|
||||
|
||||
Data types
|
||||
----------
|
||||
@ -64,11 +73,6 @@ API
|
||||
duration, and will follow normal timer semantics in the case of a
|
||||
time-slice overrun.
|
||||
|
||||
For example, if a 50ms repeating timer first runs for 17ms, it will be
|
||||
scheduled to run again 33ms later. If other tasks consume more than the
|
||||
33ms following the first timer callback, then the callback will run as soon
|
||||
as possible.
|
||||
|
||||
.. note::
|
||||
If the repeat value is set from a timer callback it does not immediately take effect.
|
||||
If the timer was non-repeating before, it will have been stopped. If it was repeating,
|
||||
|
||||
@ -27,10 +27,15 @@ Data types
|
||||
typedef enum {
|
||||
/* Initial/normal terminal mode */
|
||||
UV_TTY_MODE_NORMAL,
|
||||
/* Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled) */
|
||||
/*
|
||||
* Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled).
|
||||
* May become equivalent to UV_TTY_MODE_RAW_VT in future libuv versions.
|
||||
*/
|
||||
UV_TTY_MODE_RAW,
|
||||
/* Binary-safe I/O mode for IPC (Unix-only) */
|
||||
UV_TTY_MODE_IO
|
||||
UV_TTY_MODE_IO,
|
||||
/* Raw input mode. On Windows ENABLE_VIRTUAL_TERMINAL_INPUT is also set. */
|
||||
UV_TTY_MODE_RAW_VT
|
||||
} uv_tty_mode_t;
|
||||
|
||||
.. c:enum:: uv_tty_vtermstate_t
|
||||
@ -98,7 +103,7 @@ API
|
||||
.. c:function:: int uv_tty_set_mode(uv_tty_t* handle, uv_tty_mode_t mode)
|
||||
|
||||
.. versionchanged:: 1.2.0: the mode is specified as a
|
||||
:c:type:`uv_tty_mode_t` value.
|
||||
:c:enum:`uv_tty_mode_t` value.
|
||||
|
||||
Set the TTY using the specified terminal mode.
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ Data types
|
||||
|
||||
UDP send request type.
|
||||
|
||||
.. c:type:: uv_udp_flags
|
||||
.. c:enum:: uv_udp_flags
|
||||
|
||||
Flags used in :c:func:`uv_udp_bind` and :c:type:`uv_udp_recv_cb`..
|
||||
|
||||
@ -28,19 +28,21 @@ Data types
|
||||
/* Disables dual stack mode. */
|
||||
UV_UDP_IPV6ONLY = 1,
|
||||
/*
|
||||
* Indicates message was truncated because read buffer was too small. The
|
||||
* remainder was discarded by the OS. Used in uv_udp_recv_cb.
|
||||
*/
|
||||
* Indicates message was truncated because read buffer was too small. The
|
||||
* remainder was discarded by the OS. Used in uv_udp_recv_cb.
|
||||
*/
|
||||
UV_UDP_PARTIAL = 2,
|
||||
/*
|
||||
* Indicates if SO_REUSEADDR will be set when binding the handle in
|
||||
* uv_udp_bind.
|
||||
* This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other
|
||||
* Unix platforms, it sets the SO_REUSEADDR flag. What that means is that
|
||||
* multiple threads or processes can bind to the same address without error
|
||||
* (provided they all set the flag) but only the last one to bind will receive
|
||||
* any traffic, in effect "stealing" the port from the previous listener.
|
||||
*/
|
||||
* Indicates if SO_REUSEADDR will be set when binding the handle.
|
||||
* This sets the SO_REUSEPORT socket flag on the BSDs (except for
|
||||
* DragonFlyBSD), OS X, and other platforms where SO_REUSEPORTs don't
|
||||
* have the capability of load balancing, as the opposite of what
|
||||
* UV_UDP_REUSEPORT would do. On other Unix platforms, it sets the
|
||||
* SO_REUSEADDR flag. What that means is that multiple threads or
|
||||
* processes can bind to the same address without error (provided
|
||||
* they all set the flag) but only the last one to bind will receive
|
||||
* any traffic, in effect "stealing" the port from the previous listener.
|
||||
*/
|
||||
UV_UDP_REUSEADDR = 4,
|
||||
/*
|
||||
* Indicates that the message was received by recvmmsg, so the buffer provided
|
||||
@ -62,8 +64,20 @@ Data types
|
||||
*/
|
||||
UV_UDP_LINUX_RECVERR = 32,
|
||||
/*
|
||||
* Indicates that recvmmsg should be used, if available.
|
||||
*/
|
||||
* Indicates if SO_REUSEPORT will be set when binding the handle.
|
||||
* This sets the SO_REUSEPORT socket option on supported platforms.
|
||||
* Unlike UV_UDP_REUSEADDR, this flag will make multiple threads or
|
||||
* processes that are binding to the same address and port "share"
|
||||
* the port, which means incoming datagrams are distributed across
|
||||
* the receiving sockets among threads or processes.
|
||||
*
|
||||
* This flag is available only on Linux 3.9+, DragonFlyBSD 3.6+,
|
||||
* FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ for now.
|
||||
*/
|
||||
UV_UDP_REUSEPORT = 64,
|
||||
/*
|
||||
* Indicates that recvmmsg should be used, if available.
|
||||
*/
|
||||
UV_UDP_RECVMMSG = 256
|
||||
};
|
||||
|
||||
@ -186,11 +200,24 @@ API
|
||||
with the address and port to bind to.
|
||||
|
||||
:param flags: Indicate how the socket will be bound,
|
||||
``UV_UDP_IPV6ONLY``, ``UV_UDP_REUSEADDR``, and ``UV_UDP_RECVERR``
|
||||
are supported.
|
||||
``UV_UDP_IPV6ONLY``, ``UV_UDP_REUSEADDR``, ``UV_UDP_REUSEPORT``,
|
||||
and ``UV_UDP_RECVERR`` are supported.
|
||||
|
||||
:returns: 0 on success, or an error code < 0 on failure.
|
||||
|
||||
.. versionchanged:: 1.49.0 added the ``UV_UDP_REUSEPORT`` flag.
|
||||
|
||||
.. note::
|
||||
``UV_UDP_REUSEPORT`` flag is available only on Linux 3.9+, DragonFlyBSD 3.6+,
|
||||
FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ at the moment. On other platforms
|
||||
this function will return an UV_ENOTSUP error.
|
||||
For platforms where `SO_REUSEPORT`s have the capability of load balancing,
|
||||
specifying both ``UV_UDP_REUSEADDR`` and ``UV_UDP_REUSEPORT`` in flags is allowed
|
||||
and `SO_REUSEPORT` will always override the behavior of `SO_REUSEADDR`.
|
||||
For platforms where `SO_REUSEPORT`s don't have the capability of load balancing,
|
||||
specifying both ``UV_UDP_REUSEADDR`` and ``UV_UDP_REUSEPORT`` in flags will fail,
|
||||
returning an UV_ENOTSUP error.
|
||||
|
||||
.. c:function:: int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr)
|
||||
|
||||
Associate the UDP handle to a remote address and port, so every
|
||||
@ -285,7 +312,9 @@ API
|
||||
local sockets.
|
||||
|
||||
:param handle: UDP handle. Should have been initialized with
|
||||
:c:func:`uv_udp_init`.
|
||||
:c:func:`uv_udp_init_ex` as either ``AF_INET`` or ``AF_INET6``, or have
|
||||
been bound to an address explicitly with :c:func:`uv_udp_bind`, or
|
||||
implicitly with :c:func:`uv_udp_send()` or :c:func:`uv_udp_recv_start`.
|
||||
|
||||
:param on: 1 for on, 0 for off.
|
||||
|
||||
@ -296,7 +325,9 @@ API
|
||||
Set the multicast ttl.
|
||||
|
||||
:param handle: UDP handle. Should have been initialized with
|
||||
:c:func:`uv_udp_init`.
|
||||
:c:func:`uv_udp_init_ex` as either ``AF_INET`` or ``AF_INET6``, or have
|
||||
been bound to an address explicitly with :c:func:`uv_udp_bind`, or
|
||||
implicitly with :c:func:`uv_udp_send()` or :c:func:`uv_udp_recv_start`.
|
||||
|
||||
:param ttl: 1 through 255.
|
||||
|
||||
@ -307,7 +338,9 @@ API
|
||||
Set the multicast interface to send or receive data on.
|
||||
|
||||
:param handle: UDP handle. Should have been initialized with
|
||||
:c:func:`uv_udp_init`.
|
||||
:c:func:`uv_udp_init_ex` as either ``AF_INET`` or ``AF_INET6``, or have
|
||||
been bound to an address explicitly with :c:func:`uv_udp_bind`, or
|
||||
implicitly with :c:func:`uv_udp_send()` or :c:func:`uv_udp_recv_start`.
|
||||
|
||||
:param interface_addr: interface address.
|
||||
|
||||
@ -318,7 +351,9 @@ API
|
||||
Set broadcast on or off.
|
||||
|
||||
:param handle: UDP handle. Should have been initialized with
|
||||
:c:func:`uv_udp_init`.
|
||||
:c:func:`uv_udp_init_ex` as either ``AF_INET`` or ``AF_INET6``, or have
|
||||
been bound to an address explicitly with :c:func:`uv_udp_bind`, or
|
||||
implicitly with :c:func:`uv_udp_send()` or :c:func:`uv_udp_recv_start`.
|
||||
|
||||
:param on: 1 for on, 0 for off.
|
||||
|
||||
@ -329,7 +364,9 @@ API
|
||||
Set the time to live.
|
||||
|
||||
:param handle: UDP handle. Should have been initialized with
|
||||
:c:func:`uv_udp_init`.
|
||||
:c:func:`uv_udp_init_ex` as either ``AF_INET`` or ``AF_INET6``, or have
|
||||
been bound to an address explicitly with :c:func:`uv_udp_bind`, or
|
||||
implicitly with :c:func:`uv_udp_send()` or :c:func:`uv_udp_recv_start`.
|
||||
|
||||
:param ttl: 1 through 255.
|
||||
|
||||
@ -389,6 +426,20 @@ API
|
||||
|
||||
.. versionchanged:: 1.27.0 added support for connected sockets
|
||||
|
||||
.. c:function:: int uv_udp_try_send2(uv_udp_t* handle, unsigned int count, uv_buf_t* bufs[/*count*/], unsigned int nbufs[/*count*/], struct sockaddr* addrs[/*count*/], unsigned int flags)
|
||||
|
||||
Like :c:func:`uv_udp_try_send`, but can send multiple datagrams.
|
||||
Lightweight abstraction around :man:`sendmmsg(2)`, with a :man:`sendmsg(2)`
|
||||
fallback loop for platforms that do not support the former. The handle must
|
||||
be fully initialized; call c:func:`uv_udp_bind` first.
|
||||
|
||||
:returns: >= 0: number of datagrams sent. Zero only if `count` was zero.
|
||||
< 0: negative error code. Only if sending the first datagram fails,
|
||||
otherwise returns a positive send count. ``UV_EAGAIN`` when datagrams
|
||||
cannot be sent right now; fall back to :c:func:`uv_udp_send`.
|
||||
|
||||
.. versionadded:: 1.50.0
|
||||
|
||||
.. c:function:: int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb)
|
||||
|
||||
Prepare for receiving data. If the socket has not previously been bound
|
||||
|
||||
86
include/uv.h
86
include/uv.h
@ -58,6 +58,7 @@ extern "C" {
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
|
||||
/* Internal type, do not use. */
|
||||
struct uv__queue {
|
||||
@ -157,6 +158,7 @@ struct uv__queue {
|
||||
XX(ESOCKTNOSUPPORT, "socket type not supported") \
|
||||
XX(ENODATA, "no data available") \
|
||||
XX(EUNATCH, "protocol driver not attached") \
|
||||
XX(ENOEXEC, "exec format error") \
|
||||
|
||||
#define UV_HANDLE_TYPE_MAP(XX) \
|
||||
XX(ASYNC, async) \
|
||||
@ -260,7 +262,9 @@ typedef struct uv_metrics_s uv_metrics_t;
|
||||
|
||||
typedef enum {
|
||||
UV_LOOP_BLOCK_SIGNAL = 0,
|
||||
UV_METRICS_IDLE_TIME
|
||||
UV_METRICS_IDLE_TIME,
|
||||
UV_LOOP_USE_IO_URING_SQPOLL
|
||||
#define UV_LOOP_USE_IO_URING_SQPOLL UV_LOOP_USE_IO_URING_SQPOLL
|
||||
} uv_loop_option;
|
||||
|
||||
typedef enum {
|
||||
@ -604,7 +608,18 @@ UV_EXTERN int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable);
|
||||
|
||||
enum uv_tcp_flags {
|
||||
/* Used with uv_tcp_bind, when an IPv6 address is used. */
|
||||
UV_TCP_IPV6ONLY = 1
|
||||
UV_TCP_IPV6ONLY = 1,
|
||||
|
||||
/* Enable SO_REUSEPORT socket option when binding the handle.
|
||||
* This allows completely duplicate bindings by multiple processes
|
||||
* or threads if they all set SO_REUSEPORT before binding the port.
|
||||
* Incoming connections are distributed across the participating
|
||||
* listener sockets.
|
||||
*
|
||||
* This flag is available only on Linux 3.9+, DragonFlyBSD 3.6+,
|
||||
* FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ for now.
|
||||
*/
|
||||
UV_TCP_REUSEPORT = 2,
|
||||
};
|
||||
|
||||
UV_EXTERN int uv_tcp_bind(uv_tcp_t* handle,
|
||||
@ -645,10 +660,13 @@ enum uv_udp_flags {
|
||||
UV_UDP_PARTIAL = 2,
|
||||
/*
|
||||
* Indicates if SO_REUSEADDR will be set when binding the handle.
|
||||
* This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other
|
||||
* Unix platforms, it sets the SO_REUSEADDR flag. What that means is that
|
||||
* multiple threads or processes can bind to the same address without error
|
||||
* (provided they all set the flag) but only the last one to bind will receive
|
||||
* This sets the SO_REUSEPORT socket flag on the BSDs (except for
|
||||
* DragonFlyBSD), OS X, and other platforms where SO_REUSEPORTs don't
|
||||
* have the capability of load balancing, as the opposite of what
|
||||
* UV_UDP_REUSEPORT would do. On other Unix platforms, it sets the
|
||||
* SO_REUSEADDR flag. What that means is that multiple threads or
|
||||
* processes can bind to the same address without error (provided
|
||||
* they all set the flag) but only the last one to bind will receive
|
||||
* any traffic, in effect "stealing" the port from the previous listener.
|
||||
*/
|
||||
UV_UDP_REUSEADDR = 4,
|
||||
@ -671,6 +689,18 @@ enum uv_udp_flags {
|
||||
* This flag is no-op on platforms other than Linux.
|
||||
*/
|
||||
UV_UDP_LINUX_RECVERR = 32,
|
||||
/*
|
||||
* Indicates if SO_REUSEPORT will be set when binding the handle.
|
||||
* This sets the SO_REUSEPORT socket option on supported platforms.
|
||||
* Unlike UV_UDP_REUSEADDR, this flag will make multiple threads or
|
||||
* processes that are binding to the same address and port "share"
|
||||
* the port, which means incoming datagrams are distributed across
|
||||
* the receiving sockets among threads or processes.
|
||||
*
|
||||
* This flag is available only on Linux 3.9+, DragonFlyBSD 3.6+,
|
||||
* FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ for now.
|
||||
*/
|
||||
UV_UDP_REUSEPORT = 64,
|
||||
/*
|
||||
* Indicates that recvmmsg should be used, if available.
|
||||
*/
|
||||
@ -747,6 +777,12 @@ UV_EXTERN int uv_udp_try_send(uv_udp_t* handle,
|
||||
const uv_buf_t bufs[],
|
||||
unsigned int nbufs,
|
||||
const struct sockaddr* addr);
|
||||
UV_EXTERN int uv_udp_try_send2(uv_udp_t* handle,
|
||||
unsigned int count,
|
||||
uv_buf_t* bufs[/*count*/],
|
||||
unsigned int nbufs[/*count*/],
|
||||
struct sockaddr* addrs[/*count*/],
|
||||
unsigned int flags);
|
||||
UV_EXTERN int uv_udp_recv_start(uv_udp_t* handle,
|
||||
uv_alloc_cb alloc_cb,
|
||||
uv_udp_recv_cb recv_cb);
|
||||
@ -770,10 +806,15 @@ struct uv_tty_s {
|
||||
typedef enum {
|
||||
/* Initial/normal terminal mode */
|
||||
UV_TTY_MODE_NORMAL,
|
||||
/* Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled) */
|
||||
/*
|
||||
* Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled).
|
||||
* May become equivalent to UV_TTY_MODE_RAW_VT in future libuv versions.
|
||||
*/
|
||||
UV_TTY_MODE_RAW,
|
||||
/* Binary-safe I/O mode for IPC (Unix-only) */
|
||||
UV_TTY_MODE_IO
|
||||
UV_TTY_MODE_IO,
|
||||
/* Raw input mode. On Windows ENABLE_VIRTUAL_TERMINAL_INPUT is also set. */
|
||||
UV_TTY_MODE_RAW_VT
|
||||
} uv_tty_mode_t;
|
||||
|
||||
typedef enum {
|
||||
@ -1260,6 +1301,7 @@ typedef struct {
|
||||
} uv_rusage_t;
|
||||
|
||||
UV_EXTERN int uv_getrusage(uv_rusage_t* rusage);
|
||||
UV_EXTERN int uv_getrusage_thread(uv_rusage_t* rusage);
|
||||
|
||||
UV_EXTERN int uv_os_homedir(char* buffer, size_t* size);
|
||||
UV_EXTERN int uv_os_tmpdir(char* buffer, size_t* size);
|
||||
@ -1549,6 +1591,8 @@ UV_EXTERN int uv_fs_chmod(uv_loop_t* loop,
|
||||
const char* path,
|
||||
int mode,
|
||||
uv_fs_cb cb);
|
||||
#define UV_FS_UTIME_NOW (INFINITY)
|
||||
#define UV_FS_UTIME_OMIT (NAN)
|
||||
UV_EXTERN int uv_fs_utime(uv_loop_t* loop,
|
||||
uv_fs_t* req,
|
||||
const char* path,
|
||||
@ -1841,6 +1885,7 @@ UV_EXTERN int uv_gettimeofday(uv_timeval64_t* tv);
|
||||
typedef void (*uv_thread_cb)(void* arg);
|
||||
|
||||
UV_EXTERN int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg);
|
||||
UV_EXTERN int uv_thread_detach(uv_thread_t* tid);
|
||||
|
||||
typedef enum {
|
||||
UV_THREAD_NO_FLAGS = 0x00,
|
||||
@ -1870,6 +1915,9 @@ UV_EXTERN int uv_thread_getcpu(void);
|
||||
UV_EXTERN uv_thread_t uv_thread_self(void);
|
||||
UV_EXTERN int uv_thread_join(uv_thread_t *tid);
|
||||
UV_EXTERN int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2);
|
||||
UV_EXTERN int uv_thread_setname(const char* name);
|
||||
UV_EXTERN int uv_thread_getname(uv_thread_t* tid, char* name, size_t size);
|
||||
|
||||
|
||||
/* The presence of these unions force similar struct layout. */
|
||||
#define XX(_, name) uv_ ## name ## _t name;
|
||||
@ -1903,17 +1951,17 @@ struct uv_loop_s {
|
||||
UV_EXTERN void* uv_loop_get_data(const uv_loop_t*);
|
||||
UV_EXTERN void uv_loop_set_data(uv_loop_t*, void* data);
|
||||
|
||||
/* String utilities needed internally for dealing with Windows. */
|
||||
size_t uv_utf16_length_as_wtf8(const uint16_t* utf16,
|
||||
ssize_t utf16_len);
|
||||
int uv_utf16_to_wtf8(const uint16_t* utf16,
|
||||
ssize_t utf16_len,
|
||||
char** wtf8_ptr,
|
||||
size_t* wtf8_len_ptr);
|
||||
ssize_t uv_wtf8_length_as_utf16(const char* wtf8);
|
||||
void uv_wtf8_to_utf16(const char* wtf8,
|
||||
uint16_t* utf16,
|
||||
size_t utf16_len);
|
||||
/* Unicode utilities needed for dealing with Windows. */
|
||||
UV_EXTERN size_t uv_utf16_length_as_wtf8(const uint16_t* utf16,
|
||||
ssize_t utf16_len);
|
||||
UV_EXTERN int uv_utf16_to_wtf8(const uint16_t* utf16,
|
||||
ssize_t utf16_len,
|
||||
char** wtf8_ptr,
|
||||
size_t* wtf8_len_ptr);
|
||||
UV_EXTERN ssize_t uv_wtf8_length_as_utf16(const char* wtf8);
|
||||
UV_EXTERN void uv_wtf8_to_utf16(const char* wtf8,
|
||||
uint16_t* utf16,
|
||||
size_t utf16_len);
|
||||
|
||||
/* Don't export the private CPP symbols. */
|
||||
#undef UV_HANDLE_TYPE_PRIVATE
|
||||
|
||||
@ -474,4 +474,10 @@
|
||||
# define UV__EUNATCH (-4023)
|
||||
#endif
|
||||
|
||||
#if defined(ENOEXEC) && !defined(_WIN32)
|
||||
# define UV__ENOEXEC UV__ERR(ENOEXEC)
|
||||
#else
|
||||
# define UV__ENOEXEC (-4022)
|
||||
#endif
|
||||
|
||||
#endif /* UV_ERRNO_H_ */
|
||||
|
||||
@ -35,21 +35,7 @@
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This file defines data structures for different types of trees:
|
||||
* splay trees and red-black trees.
|
||||
*
|
||||
* A splay tree is a self-organizing data structure. Every operation
|
||||
* on the tree causes a splay to happen. The splay moves the requested
|
||||
* node to the root of the tree and partly rebalances it.
|
||||
*
|
||||
* This has the benefit that request locality causes faster lookups as
|
||||
* the requested nodes move to the top of the tree. On the other hand,
|
||||
* every lookup causes memory writes.
|
||||
*
|
||||
* The Balance Theorem bounds the total access time for m operations
|
||||
* and n inserts on an initially empty tree as O((m + n)lg n). The
|
||||
* amortized cost for a sequence of m accesses to a splay tree is O(lg n);
|
||||
*
|
||||
* This file defines data structures for red-black trees.
|
||||
* A red-black tree is a binary search tree with the node color as an
|
||||
* extra attribute. It fulfills a set of conditions:
|
||||
* - every search path from the root to a leaf consists of the
|
||||
@ -61,239 +47,6 @@
|
||||
* The maximum height of a red-black tree is 2lg (n+1).
|
||||
*/
|
||||
|
||||
#define SPLAY_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sph_root; /* root of the tree */ \
|
||||
}
|
||||
|
||||
#define SPLAY_INITIALIZER(root) \
|
||||
{ NULL }
|
||||
|
||||
#define SPLAY_INIT(root) do { \
|
||||
(root)->sph_root = NULL; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *spe_left; /* left element */ \
|
||||
struct type *spe_right; /* right element */ \
|
||||
}
|
||||
|
||||
#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
|
||||
#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
|
||||
#define SPLAY_ROOT(head) (head)->sph_root
|
||||
#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
|
||||
|
||||
/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
|
||||
#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
|
||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
|
||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
||||
(head)->sph_root = tmp; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
|
||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
||||
(head)->sph_root = tmp; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_LINKLEFT(head, tmp, field) do { \
|
||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
||||
tmp = (head)->sph_root; \
|
||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_LINKRIGHT(head, tmp, field) do { \
|
||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
||||
tmp = (head)->sph_root; \
|
||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
|
||||
SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
|
||||
SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
/* Generates prototypes and inline functions */
|
||||
|
||||
#define SPLAY_PROTOTYPE(name, type, field, cmp) \
|
||||
void name##_SPLAY(struct name *, struct type *); \
|
||||
void name##_SPLAY_MINMAX(struct name *, int); \
|
||||
struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
|
||||
struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
|
||||
\
|
||||
/* Finds the node with the same key as elm */ \
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_FIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
if (SPLAY_EMPTY(head)) \
|
||||
return(NULL); \
|
||||
name##_SPLAY(head, elm); \
|
||||
if ((cmp)(elm, (head)->sph_root) == 0) \
|
||||
return (head->sph_root); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_NEXT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
name##_SPLAY(head, elm); \
|
||||
if (SPLAY_RIGHT(elm, field) != NULL) { \
|
||||
elm = SPLAY_RIGHT(elm, field); \
|
||||
while (SPLAY_LEFT(elm, field) != NULL) { \
|
||||
elm = SPLAY_LEFT(elm, field); \
|
||||
} \
|
||||
} else \
|
||||
elm = NULL; \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_MIN_MAX(struct name *head, int val) \
|
||||
{ \
|
||||
name##_SPLAY_MINMAX(head, val); \
|
||||
return (SPLAY_ROOT(head)); \
|
||||
}
|
||||
|
||||
/* Main splay operation.
|
||||
* Moves node close to the key of elm to top
|
||||
*/
|
||||
#define SPLAY_GENERATE(name, type, field, cmp) \
|
||||
struct type * \
|
||||
name##_SPLAY_INSERT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
if (SPLAY_EMPTY(head)) { \
|
||||
SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
|
||||
} else { \
|
||||
int __comp; \
|
||||
name##_SPLAY(head, elm); \
|
||||
__comp = (cmp)(elm, (head)->sph_root); \
|
||||
if(__comp < 0) { \
|
||||
SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field); \
|
||||
SPLAY_RIGHT(elm, field) = (head)->sph_root; \
|
||||
SPLAY_LEFT((head)->sph_root, field) = NULL; \
|
||||
} else if (__comp > 0) { \
|
||||
SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
SPLAY_LEFT(elm, field) = (head)->sph_root; \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = NULL; \
|
||||
} else \
|
||||
return ((head)->sph_root); \
|
||||
} \
|
||||
(head)->sph_root = (elm); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
struct type * \
|
||||
name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *__tmp; \
|
||||
if (SPLAY_EMPTY(head)) \
|
||||
return (NULL); \
|
||||
name##_SPLAY(head, elm); \
|
||||
if ((cmp)(elm, (head)->sph_root) == 0) { \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
|
||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
} else { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
|
||||
name##_SPLAY(head, elm); \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
|
||||
} \
|
||||
return (elm); \
|
||||
} \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
void \
|
||||
name##_SPLAY(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type __node, *__left, *__right, *__tmp; \
|
||||
int __comp; \
|
||||
\
|
||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \
|
||||
__left = __right = &__node; \
|
||||
\
|
||||
while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \
|
||||
if (__comp < 0) { \
|
||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if ((cmp)(elm, __tmp) < 0){ \
|
||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL) \
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKLEFT(head, __right, field); \
|
||||
} else if (__comp > 0) { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if ((cmp)(elm, __tmp) > 0){ \
|
||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKRIGHT(head, __left, field); \
|
||||
} \
|
||||
} \
|
||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
||||
} \
|
||||
\
|
||||
/* Splay with either the minimum or the maximum element \
|
||||
* Used to find minimum or maximum element in tree. \
|
||||
*/ \
|
||||
void name##_SPLAY_MINMAX(struct name *head, int __comp) \
|
||||
{ \
|
||||
struct type __node, *__left, *__right, *__tmp; \
|
||||
\
|
||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \
|
||||
__left = __right = &__node; \
|
||||
\
|
||||
for (;;) { \
|
||||
if (__comp < 0) { \
|
||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if (__comp < 0){ \
|
||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL) \
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKLEFT(head, __right, field); \
|
||||
} else if (__comp > 0) { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if (__comp > 0) { \
|
||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKRIGHT(head, __left, field); \
|
||||
} \
|
||||
} \
|
||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
||||
}
|
||||
|
||||
#define SPLAY_NEGINF -1
|
||||
#define SPLAY_INF 1
|
||||
|
||||
#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
|
||||
#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
|
||||
#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
|
||||
#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
|
||||
#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
|
||||
: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
|
||||
#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
|
||||
: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
|
||||
|
||||
#define SPLAY_FOREACH(x, name, head) \
|
||||
for ((x) = SPLAY_MIN(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = SPLAY_NEXT(name, head, x))
|
||||
|
||||
/* Macros that define a red-black tree */
|
||||
#define RB_HEAD(name, type) \
|
||||
struct name { \
|
||||
@ -730,8 +483,8 @@ name##_RB_MINMAX(struct name *head, int val) \
|
||||
#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
|
||||
#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
|
||||
#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
|
||||
#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
|
||||
#define RB_PREV(name, x, y) name##_RB_PREV(y)
|
||||
#define RB_NEXT(name, x) name##_RB_NEXT(x)
|
||||
#define RB_PREV(name, x) name##_RB_PREV(x)
|
||||
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
|
||||
#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
|
||||
|
||||
|
||||
@ -271,7 +271,10 @@ typedef struct {
|
||||
|
||||
#define UV_UDP_SEND_PRIVATE_FIELDS \
|
||||
struct uv__queue queue; \
|
||||
struct sockaddr_storage addr; \
|
||||
union { \
|
||||
struct sockaddr addr; \
|
||||
struct sockaddr_storage storage; \
|
||||
} u; \
|
||||
unsigned int nbufs; \
|
||||
uv_buf_t* bufs; \
|
||||
ssize_t status; \
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
*/
|
||||
|
||||
#define UV_VERSION_MAJOR 1
|
||||
#define UV_VERSION_MINOR 48
|
||||
#define UV_VERSION_MINOR 50
|
||||
#define UV_VERSION_PATCH 1
|
||||
#define UV_VERSION_IS_RELEASE 0
|
||||
#define UV_VERSION_SUFFIX "dev"
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
#ifndef _WIN32_WINNT
|
||||
# define _WIN32_WINNT 0x0600
|
||||
# define _WIN32_WINNT 0x0A00
|
||||
#endif
|
||||
|
||||
#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED)
|
||||
@ -32,14 +32,6 @@ typedef intptr_t ssize_t;
|
||||
|
||||
#include <winsock2.h>
|
||||
|
||||
#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
|
||||
typedef struct pollfd {
|
||||
SOCKET fd;
|
||||
short events;
|
||||
short revents;
|
||||
} WSAPOLLFD, *PWSAPOLLFD, *LPWSAPOLLFD;
|
||||
#endif
|
||||
|
||||
#ifndef LOCALE_INVARIANT
|
||||
# define LOCALE_INVARIANT 0x007f
|
||||
#endif
|
||||
@ -290,8 +282,8 @@ typedef struct {
|
||||
#define UV_ONCE_INIT { 0, NULL }
|
||||
|
||||
typedef struct uv_once_s {
|
||||
unsigned char ran;
|
||||
HANDLE event;
|
||||
unsigned char unused;
|
||||
INIT_ONCE init_once;
|
||||
} uv_once_t;
|
||||
|
||||
/* Platform-specific definitions for uv_spawn support. */
|
||||
@ -507,8 +499,11 @@ typedef struct {
|
||||
union { \
|
||||
struct { \
|
||||
/* Used for readable TTY handles */ \
|
||||
/* TODO: remove me in v2.x. */ \
|
||||
HANDLE unused_; \
|
||||
union { \
|
||||
/* TODO: remove me in v2.x. */ \
|
||||
HANDLE unused_; \
|
||||
int mode; \
|
||||
} mode; \
|
||||
uv_buf_t read_line_buffer; \
|
||||
HANDLE read_raw_wait; \
|
||||
/* Fields used for translating win keystrokes into vt100 characters */ \
|
||||
|
||||
@ -139,6 +139,9 @@ int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buffer, size_t* size) {
|
||||
struct poll_ctx* ctx;
|
||||
size_t required_len;
|
||||
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (!uv_is_active((uv_handle_t*)handle)) {
|
||||
*size = 0;
|
||||
return UV_EINVAL;
|
||||
|
||||
@ -393,7 +393,7 @@ void uv_wtf8_to_utf16(const char* source_ptr,
|
||||
code_point = uv__wtf8_decode1(&source_ptr);
|
||||
/* uv_wtf8_length_as_utf16 should have been called and checked first. */
|
||||
assert(code_point >= 0);
|
||||
if (code_point > 0x10000) {
|
||||
if (code_point > 0xFFFF) {
|
||||
assert(code_point < 0x10FFFF);
|
||||
*w_target++ = (((code_point - 0x10000) >> 10) + 0xD800);
|
||||
*w_target++ = ((code_point - 0x10000) & 0x3FF) + 0xDC00;
|
||||
|
||||
@ -82,7 +82,7 @@ static void uv__random_done(struct uv__work* w, int status) {
|
||||
uv_random_t* req;
|
||||
|
||||
req = container_of(w, uv_random_t, work_req);
|
||||
uv__req_unregister(req->loop, req);
|
||||
uv__req_unregister(req->loop);
|
||||
|
||||
if (status == 0)
|
||||
status = req->status;
|
||||
|
||||
@ -59,6 +59,7 @@ static void worker(void* arg) {
|
||||
struct uv__queue* q;
|
||||
int is_slow_work;
|
||||
|
||||
uv_thread_setname("libuv-worker");
|
||||
uv_sem_post((uv_sem_t*) arg);
|
||||
arg = NULL;
|
||||
|
||||
@ -356,7 +357,7 @@ static void uv__queue_done(struct uv__work* w, int err) {
|
||||
uv_work_t* req;
|
||||
|
||||
req = container_of(w, uv_work_t, work_req);
|
||||
uv__req_unregister(req->loop, req);
|
||||
uv__req_unregister(req->loop);
|
||||
|
||||
if (req->after_work_cb == NULL)
|
||||
return;
|
||||
|
||||
@ -1120,6 +1120,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
struct ifreq *ifr, *p, flg;
|
||||
struct in6_ifreq if6;
|
||||
struct sockaddr_dl* sa_addr;
|
||||
size_t namelen;
|
||||
char* name;
|
||||
|
||||
ifc.ifc_req = NULL;
|
||||
sock6fd = -1;
|
||||
@ -1156,6 +1158,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
#define ADDR_SIZE(p) MAX((p).sa_len, sizeof(p))
|
||||
|
||||
/* Count all up and running ipv4/ipv6 addresses */
|
||||
namelen = 0;
|
||||
ifr = ifc.ifc_req;
|
||||
while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) {
|
||||
p = ifr;
|
||||
@ -1175,6 +1178,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING))
|
||||
continue;
|
||||
|
||||
namelen += strlen(ent->ifa_name) + 1;
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
@ -1182,8 +1186,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
goto cleanup;
|
||||
|
||||
/* Alloc the return interface structs */
|
||||
*addresses = uv__calloc(*count, sizeof(**addresses));
|
||||
if (!(*addresses)) {
|
||||
*addresses = uv__calloc(1, *count * sizeof(**addresses) + namelen);
|
||||
if (*addresses == NULL) {
|
||||
r = UV_ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
@ -1210,7 +1214,9 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
|
||||
/* All conditions above must match count loop */
|
||||
|
||||
address->name = uv__strdup(p->ifr_name);
|
||||
namelen = strlen(p->ifr_name) + 1;
|
||||
address->name = memcpy(name, p->ifr_name, namelen);
|
||||
name += namelen;
|
||||
|
||||
if (inet6)
|
||||
address->address.address6 = *((struct sockaddr_in6*) &p->ifr_addr);
|
||||
@ -1282,13 +1288,7 @@ cleanup:
|
||||
|
||||
|
||||
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||
int count) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
uv__free(addresses[i].name);
|
||||
}
|
||||
|
||||
int count) {
|
||||
uv__free(addresses);
|
||||
}
|
||||
|
||||
|
||||
@ -38,6 +38,34 @@
|
||||
#include <sys/eventfd.h>
|
||||
#endif
|
||||
|
||||
#if UV__KQUEUE_EVFILT_USER
|
||||
static uv_once_t kqueue_runtime_detection_guard = UV_ONCE_INIT;
|
||||
static int kqueue_evfilt_user_support = 1;
|
||||
|
||||
|
||||
static void uv__kqueue_runtime_detection(void) {
|
||||
int kq;
|
||||
struct kevent ev[2];
|
||||
struct timespec timeout = {0, 0};
|
||||
|
||||
/* Perform the runtime detection to ensure that kqueue with
|
||||
* EVFILT_USER actually works. */
|
||||
kq = kqueue();
|
||||
EV_SET(ev, UV__KQUEUE_EVFILT_USER_IDENT, EVFILT_USER,
|
||||
EV_ADD | EV_CLEAR, 0, 0, 0);
|
||||
EV_SET(ev + 1, UV__KQUEUE_EVFILT_USER_IDENT, EVFILT_USER,
|
||||
0, NOTE_TRIGGER, 0, 0);
|
||||
if (kevent(kq, ev, 2, ev, 1, &timeout) < 1 ||
|
||||
ev[0].filter != EVFILT_USER ||
|
||||
ev[0].ident != UV__KQUEUE_EVFILT_USER_IDENT ||
|
||||
ev[0].flags & EV_ERROR)
|
||||
/* If we wind up here, we can assume that EVFILT_USER is defined but
|
||||
* broken on the current system. */
|
||||
kqueue_evfilt_user_support = 0;
|
||||
uv__close(kq);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void uv__async_send(uv_loop_t* loop);
|
||||
static int uv__async_start(uv_loop_t* loop);
|
||||
static void uv__cpu_relax(void);
|
||||
@ -139,7 +167,11 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
|
||||
|
||||
assert(w == &loop->async_io_watcher);
|
||||
|
||||
#if UV__KQUEUE_EVFILT_USER
|
||||
for (;!kqueue_evfilt_user_support;) {
|
||||
#else
|
||||
for (;;) {
|
||||
#endif
|
||||
r = read(w->fd, buf, sizeof(buf));
|
||||
|
||||
if (r == sizeof(buf))
|
||||
@ -195,6 +227,17 @@ static void uv__async_send(uv_loop_t* loop) {
|
||||
len = sizeof(val);
|
||||
fd = loop->async_io_watcher.fd; /* eventfd */
|
||||
}
|
||||
#elif UV__KQUEUE_EVFILT_USER
|
||||
struct kevent ev;
|
||||
|
||||
if (kqueue_evfilt_user_support) {
|
||||
fd = loop->async_io_watcher.fd; /* magic number for EVFILT_USER */
|
||||
EV_SET(&ev, fd, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0);
|
||||
r = kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL);
|
||||
if (r == 0)
|
||||
return;
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
|
||||
do
|
||||
@ -215,6 +258,9 @@ static void uv__async_send(uv_loop_t* loop) {
|
||||
static int uv__async_start(uv_loop_t* loop) {
|
||||
int pipefd[2];
|
||||
int err;
|
||||
#if UV__KQUEUE_EVFILT_USER
|
||||
struct kevent ev;
|
||||
#endif
|
||||
|
||||
if (loop->async_io_watcher.fd != -1)
|
||||
return 0;
|
||||
@ -226,6 +272,36 @@ static int uv__async_start(uv_loop_t* loop) {
|
||||
|
||||
pipefd[0] = err;
|
||||
pipefd[1] = -1;
|
||||
#elif UV__KQUEUE_EVFILT_USER
|
||||
uv_once(&kqueue_runtime_detection_guard, uv__kqueue_runtime_detection);
|
||||
if (kqueue_evfilt_user_support) {
|
||||
/* In order not to break the generic pattern of I/O polling, a valid
|
||||
* file descriptor is required to take up a room in loop->watchers,
|
||||
* thus we create one for that, but this fd will not be actually used,
|
||||
* it's just a placeholder and magic number which is going to be closed
|
||||
* during the cleanup, as other FDs. */
|
||||
err = uv__open_cloexec("/", O_RDONLY);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
pipefd[0] = err;
|
||||
pipefd[1] = -1;
|
||||
|
||||
/* When using EVFILT_USER event to wake up the kqueue, this event must be
|
||||
* registered beforehand. Otherwise, calling kevent() to issue an
|
||||
* unregistered EVFILT_USER event will get an ENOENT.
|
||||
* Since uv__async_send() may happen before uv__io_poll() with multi-threads,
|
||||
* we can't defer this registration of EVFILT_USER event as we did for other
|
||||
* events, but must perform it right away. */
|
||||
EV_SET(&ev, err, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, 0);
|
||||
err = kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL);
|
||||
if (err < 0)
|
||||
return UV__ERR(errno);
|
||||
} else {
|
||||
err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
#else
|
||||
err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE);
|
||||
if (err < 0)
|
||||
@ -236,6 +312,13 @@ static int uv__async_start(uv_loop_t* loop) {
|
||||
uv__io_start(loop, &loop->async_io_watcher, POLLIN);
|
||||
loop->async_wfd = pipefd[1];
|
||||
|
||||
#if UV__KQUEUE_EVFILT_USER
|
||||
/* Prevent the EVFILT_USER event from being added to kqueue redundantly
|
||||
* and mistakenly later in uv__io_poll(). */
|
||||
if (kqueue_evfilt_user_support)
|
||||
loop->async_io_watcher.events = loop->async_io_watcher.pevents;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -65,13 +65,13 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO(bnoordhuis) share with linux.c */
|
||||
int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
uv_interface_address_t* address;
|
||||
struct ifaddrs* addrs;
|
||||
struct ifaddrs* ent;
|
||||
uv_interface_address_t* address;
|
||||
#if !(defined(__CYGWIN__) || defined(__MSYS__)) && !defined(__GNU__)
|
||||
int i;
|
||||
#endif
|
||||
size_t namelen;
|
||||
char* name;
|
||||
|
||||
*count = 0;
|
||||
*addresses = NULL;
|
||||
@ -80,9 +80,11 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
return UV__ERR(errno);
|
||||
|
||||
/* Count the number of interfaces */
|
||||
namelen = 0;
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
|
||||
continue;
|
||||
namelen += strlen(ent->ifa_name) + 1;
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
@ -92,20 +94,22 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
}
|
||||
|
||||
/* Make sure the memory is initiallized to zero using calloc() */
|
||||
*addresses = uv__calloc(*count, sizeof(**addresses));
|
||||
|
||||
*addresses = uv__calloc(1, *count * sizeof(**addresses) + namelen);
|
||||
if (*addresses == NULL) {
|
||||
freeifaddrs(addrs);
|
||||
return UV_ENOMEM;
|
||||
}
|
||||
|
||||
name = (char*) &(*addresses)[*count];
|
||||
address = *addresses;
|
||||
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
|
||||
continue;
|
||||
|
||||
address->name = uv__strdup(ent->ifa_name);
|
||||
namelen = strlen(ent->ifa_name) + 1;
|
||||
address->name = memcpy(name, ent->ifa_name, namelen);
|
||||
name += namelen;
|
||||
|
||||
if (ent->ifa_addr->sa_family == AF_INET6) {
|
||||
address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
|
||||
@ -129,6 +133,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
#if !(defined(__CYGWIN__) || defined(__MSYS__)) && !defined(__GNU__)
|
||||
/* Fill in physical addresses for each interface */
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
int i;
|
||||
|
||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))
|
||||
continue;
|
||||
|
||||
@ -151,13 +157,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
}
|
||||
|
||||
|
||||
/* TODO(bnoordhuis) share with linux.c */
|
||||
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||
int count) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
uv__free(addresses[i].name);
|
||||
}
|
||||
|
||||
uv__free(addresses);
|
||||
}
|
||||
|
||||
230
src/unix/core.c
230
src/unix/core.c
@ -52,8 +52,11 @@
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# include <mach/mach.h>
|
||||
# include <mach/thread_info.h>
|
||||
# include <sys/filio.h>
|
||||
# endif /* defined(__APPLE__) */
|
||||
# include <sys/sysctl.h>
|
||||
#endif /* defined(__APPLE__) */
|
||||
|
||||
|
||||
#if defined(__APPLE__) && !TARGET_OS_IPHONE
|
||||
@ -94,6 +97,15 @@ extern char** environ;
|
||||
# define uv__accept4 accept4
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
# include <sys/param.h>
|
||||
# include <sys/cpuset.h>
|
||||
#endif
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
# include <sched.h>
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) && defined(__SANITIZE_THREAD__) && defined(__clang__)
|
||||
# include <sanitizer/linux_syscall_hooks.h>
|
||||
#endif
|
||||
@ -156,7 +168,7 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) {
|
||||
break;
|
||||
|
||||
case UV_TTY:
|
||||
uv__stream_close((uv_stream_t*)handle);
|
||||
uv__tty_close((uv_tty_t*)handle);
|
||||
break;
|
||||
|
||||
case UV_TCP:
|
||||
@ -741,7 +753,7 @@ ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) {
|
||||
int uv_cwd(char* buffer, size_t* size) {
|
||||
char scratch[1 + UV__PATH_MAX];
|
||||
|
||||
if (buffer == NULL || size == NULL)
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
/* Try to read directly into the user's buffer first... */
|
||||
@ -989,10 +1001,10 @@ int uv__fd_exists(uv_loop_t* loop, int fd) {
|
||||
}
|
||||
|
||||
|
||||
int uv_getrusage(uv_rusage_t* rusage) {
|
||||
static int uv__getrusage(int who, uv_rusage_t* rusage) {
|
||||
struct rusage usage;
|
||||
|
||||
if (getrusage(RUSAGE_SELF, &usage))
|
||||
if (getrusage(who, &usage))
|
||||
return UV__ERR(errno);
|
||||
|
||||
rusage->ru_utime.tv_sec = usage.ru_utime.tv_sec;
|
||||
@ -1024,13 +1036,57 @@ int uv_getrusage(uv_rusage_t* rusage) {
|
||||
#if defined(__APPLE__)
|
||||
rusage->ru_maxrss /= 1024; /* macOS and iOS report bytes. */
|
||||
#elif defined(__sun)
|
||||
rusage->ru_maxrss /= getpagesize() / 1024; /* Solaris reports pages. */
|
||||
rusage->ru_maxrss *= getpagesize() / 1024; /* Solaris reports pages. */
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_getrusage(uv_rusage_t* rusage) {
|
||||
return uv__getrusage(RUSAGE_SELF, rusage);
|
||||
}
|
||||
|
||||
|
||||
int uv_getrusage_thread(uv_rusage_t* rusage) {
|
||||
#if defined(__APPLE__)
|
||||
mach_msg_type_number_t count;
|
||||
thread_basic_info_data_t info;
|
||||
kern_return_t kr;
|
||||
thread_t thread;
|
||||
|
||||
thread = mach_thread_self();
|
||||
count = THREAD_BASIC_INFO_COUNT;
|
||||
kr = thread_info(thread,
|
||||
THREAD_BASIC_INFO,
|
||||
(thread_info_t)&info,
|
||||
&count);
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
mach_port_deallocate(mach_task_self(), thread);
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
memset(rusage, 0, sizeof(*rusage));
|
||||
|
||||
rusage->ru_utime.tv_sec = info.user_time.seconds;
|
||||
rusage->ru_utime.tv_usec = info.user_time.microseconds;
|
||||
rusage->ru_stime.tv_sec = info.system_time.seconds;
|
||||
rusage->ru_stime.tv_usec = info.system_time.microseconds;
|
||||
|
||||
mach_port_deallocate(mach_task_self(), thread);
|
||||
|
||||
return 0;
|
||||
|
||||
#elif defined(RUSAGE_LWP)
|
||||
return uv__getrusage(RUSAGE_LWP, rusage);
|
||||
#elif defined(RUSAGE_THREAD)
|
||||
return uv__getrusage(RUSAGE_THREAD, rusage);
|
||||
#endif /* defined(__APPLE__) */
|
||||
return UV_ENOTSUP;
|
||||
}
|
||||
|
||||
|
||||
int uv__open_cloexec(const char* path, int flags) {
|
||||
#if defined(O_CLOEXEC)
|
||||
int fd;
|
||||
@ -1616,6 +1672,7 @@ static int set_nice_for_calling_thread(int priority) {
|
||||
* If the function fails, the return value is non-zero.
|
||||
*/
|
||||
int uv_thread_setpriority(uv_thread_t tid, int priority) {
|
||||
#if !defined(__GNU__)
|
||||
int r;
|
||||
int min;
|
||||
int max;
|
||||
@ -1677,10 +1734,14 @@ int uv_thread_setpriority(uv_thread_t tid, int priority) {
|
||||
param.sched_priority = prio;
|
||||
r = pthread_setschedparam(tid, policy, ¶m);
|
||||
if (r != 0)
|
||||
return UV__ERR(errno);
|
||||
return UV__ERR(errno);
|
||||
}
|
||||
|
||||
return 0;
|
||||
#else /* !defined(__GNU__) */
|
||||
/* Simulate success on systems where thread priority is not implemented. */
|
||||
return 0;
|
||||
#endif /* !defined(__GNU__) */
|
||||
}
|
||||
|
||||
int uv_os_uname(uv_utsname_t* buffer) {
|
||||
@ -1864,13 +1925,31 @@ int uv__search_path(const char* prog, char* buf, size_t* buflen) {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
#if defined(__linux__) || defined (__FreeBSD__)
|
||||
# define uv__cpu_count(cpuset) CPU_COUNT(cpuset)
|
||||
#elif defined(__NetBSD__)
|
||||
static int uv__cpu_count(cpuset_t* set) {
|
||||
int rc;
|
||||
cpuid_t i;
|
||||
|
||||
rc = 0;
|
||||
for (i = 0;; i++) {
|
||||
int r = cpuset_isset(i, set);
|
||||
if (r < 0)
|
||||
break;
|
||||
if (r)
|
||||
rc++;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif /* __NetBSD__ */
|
||||
|
||||
unsigned int uv_available_parallelism(void) {
|
||||
long rc = -1;
|
||||
|
||||
#ifdef __linux__
|
||||
cpu_set_t set;
|
||||
long rc;
|
||||
double rc_with_cgroup;
|
||||
uv__cpu_constraint c = {0, 0, 0.0};
|
||||
|
||||
memset(&set, 0, sizeof(set));
|
||||
|
||||
@ -1879,34 +1958,127 @@ unsigned int uv_available_parallelism(void) {
|
||||
* before falling back to sysconf(_SC_NPROCESSORS_ONLN).
|
||||
*/
|
||||
if (0 == sched_getaffinity(0, sizeof(set), &set))
|
||||
rc = CPU_COUNT(&set);
|
||||
else
|
||||
rc = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
|
||||
if (uv__get_constrained_cpu(&c) == 0 && c.period_length > 0) {
|
||||
rc_with_cgroup = (double)c.quota_per_period / c.period_length * c.proportions;
|
||||
if (rc_with_cgroup < rc)
|
||||
rc = (long)rc_with_cgroup; /* Casting is safe since rc_with_cgroup < rc < LONG_MAX */
|
||||
}
|
||||
if (rc < 1)
|
||||
rc = 1;
|
||||
|
||||
return (unsigned) rc;
|
||||
rc = uv__cpu_count(&set);
|
||||
#elif defined(__MVS__)
|
||||
int rc;
|
||||
|
||||
rc = __get_num_online_cpus();
|
||||
if (rc < 1)
|
||||
rc = 1;
|
||||
|
||||
return (unsigned) rc;
|
||||
#else /* __linux__ */
|
||||
long rc;
|
||||
#elif defined(__FreeBSD__)
|
||||
cpuset_t set;
|
||||
|
||||
memset(&set, 0, sizeof(set));
|
||||
|
||||
if (0 == cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(set), &set))
|
||||
rc = uv__cpu_count(&set);
|
||||
#elif defined(__NetBSD__)
|
||||
cpuset_t* set = cpuset_create();
|
||||
if (set != NULL) {
|
||||
if (0 == sched_getaffinity_np(getpid(), sizeof(set), &set))
|
||||
rc = uv__cpu_count(&set);
|
||||
cpuset_destroy(set);
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
int nprocs;
|
||||
size_t i;
|
||||
size_t len = sizeof(nprocs);
|
||||
static const char *mib[] = {
|
||||
"hw.activecpu",
|
||||
"hw.logicalcpu",
|
||||
"hw.ncpu"
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mib); i++) {
|
||||
if (0 == sysctlbyname(mib[i], &nprocs, &len, NULL, 0) &&
|
||||
len == sizeof(nprocs) &&
|
||||
nprocs > 0) {
|
||||
rc = nprocs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#elif defined(__OpenBSD__)
|
||||
int nprocs;
|
||||
size_t i;
|
||||
size_t len = sizeof(nprocs);
|
||||
static int mib[][2] = {
|
||||
# ifdef HW_NCPUONLINE
|
||||
{ CTL_HW, HW_NCPUONLINE },
|
||||
# endif
|
||||
{ CTL_HW, HW_NCPU }
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mib); i++) {
|
||||
if (0 == sysctl(mib[i], ARRAY_SIZE(mib[i]), &nprocs, &len, NULL, 0) &&
|
||||
len == sizeof(nprocs) &&
|
||||
nprocs > 0) {
|
||||
rc = nprocs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* __linux__ */
|
||||
|
||||
if (rc < 0)
|
||||
rc = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
|
||||
#ifdef __linux__
|
||||
{
|
||||
double rc_with_cgroup;
|
||||
uv__cpu_constraint c = {0, 0, 0.0};
|
||||
|
||||
if (uv__get_constrained_cpu(&c) == 0 && c.period_length > 0) {
|
||||
rc_with_cgroup = (double)c.quota_per_period / c.period_length * c.proportions;
|
||||
if (rc_with_cgroup < rc)
|
||||
rc = (long)rc_with_cgroup; /* Casting is safe since rc_with_cgroup < rc < LONG_MAX */
|
||||
}
|
||||
}
|
||||
#endif /* __linux__ */
|
||||
|
||||
rc = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
if (rc < 1)
|
||||
rc = 1;
|
||||
|
||||
return (unsigned) rc;
|
||||
#endif /* __linux__ */
|
||||
}
|
||||
|
||||
int uv__sock_reuseport(int fd) {
|
||||
int on = 1;
|
||||
#if defined(__FreeBSD__) && __FreeBSD__ >= 12 && defined(SO_REUSEPORT_LB)
|
||||
/* FreeBSD 12 introduced a new socket option named SO_REUSEPORT_LB
|
||||
* with the capability of load balancing, it's the substitution of
|
||||
* the SO_REUSEPORTs on Linux and DragonFlyBSD. */
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT_LB, &on, sizeof(on)))
|
||||
return UV__ERR(errno);
|
||||
#elif (defined(__linux__) || \
|
||||
defined(_AIX73) || \
|
||||
(defined(__DragonFly__) && __DragonFly_version >= 300600) || \
|
||||
(defined(UV__SOLARIS_11_4) && UV__SOLARIS_11_4)) && \
|
||||
defined(SO_REUSEPORT)
|
||||
/* On Linux 3.9+, the SO_REUSEPORT implementation distributes connections
|
||||
* evenly across all of the threads (or processes) that are blocked in
|
||||
* accept() on the same port. As with TCP, SO_REUSEPORT distributes datagrams
|
||||
* evenly across all of the receiving threads (or process).
|
||||
*
|
||||
* DragonFlyBSD 3.6.0 extended SO_REUSEPORT to distribute workload to
|
||||
* available sockets, which made it the equivalent of Linux's SO_REUSEPORT.
|
||||
*
|
||||
* AIX 7.2.5 added the feature that would add the capability to distribute
|
||||
* incoming connections or datagrams across all listening ports for SO_REUSEPORT.
|
||||
*
|
||||
* Solaris 11 supported SO_REUSEPORT, but it's implemented only for
|
||||
* binding to the same address and port, without load balancing.
|
||||
* Solaris 11.4 extended SO_REUSEPORT with the capability of load balancing.
|
||||
*/
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)))
|
||||
return UV__ERR(errno);
|
||||
#else
|
||||
(void) (fd);
|
||||
(void) (on);
|
||||
/* SO_REUSEPORTs do not have the capability of load balancing on platforms
|
||||
* other than those mentioned above. The semantics are completely different,
|
||||
* therefore we shouldn't enable it, but fail this operation to indicate that
|
||||
* UV_[TCP/UDP]_REUSEPORT is not supported on these platforms. */
|
||||
return UV_ENOTSUP;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -33,25 +33,9 @@
|
||||
#include "darwin-stub.h"
|
||||
#endif
|
||||
|
||||
|
||||
static int uv__pthread_setname_np(const char* name) {
|
||||
char namebuf[64]; /* MAXTHREADNAMESIZE */
|
||||
int err;
|
||||
|
||||
strncpy(namebuf, name, sizeof(namebuf) - 1);
|
||||
namebuf[sizeof(namebuf) - 1] = '\0';
|
||||
|
||||
err = pthread_setname_np(namebuf);
|
||||
if (err)
|
||||
return UV__ERR(err);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__set_process_title(const char* title) {
|
||||
#if TARGET_OS_IPHONE
|
||||
return uv__pthread_setname_np(title);
|
||||
return uv__thread_setname(title);
|
||||
#else
|
||||
CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef,
|
||||
const char*,
|
||||
@ -177,7 +161,7 @@ int uv__set_process_title(const char* title) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
uv__pthread_setname_np(title); /* Don't care if it fails. */
|
||||
uv__thread_setname(title); /* Don't care if it fails. */
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
|
||||
17
src/unix/darwin-syscalls.h
Normal file
17
src/unix/darwin-syscalls.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef UV_DARWIN_SYSCALLS_H_
|
||||
#define UV_DARWIN_SYSCALLS_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
/* https://github.com/apple/darwin-xnu/blob/master/bsd/sys/socket.h */
|
||||
|
||||
struct mmsghdr {
|
||||
struct msghdr msg_hdr;
|
||||
size_t msg_len;
|
||||
};
|
||||
|
||||
ssize_t recvmsg_x(int s, const struct mmsghdr* msgp, u_int cnt, int flags);
|
||||
ssize_t sendmsg_x(int s, const struct mmsghdr* msgp, u_int cnt, int flags);
|
||||
|
||||
#endif /* UV_DARWIN_SYSCALLS_H_ */
|
||||
@ -25,7 +25,6 @@
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_time.h>
|
||||
#include <mach-o/dyld.h> /* _NSGetExecutablePath */
|
||||
@ -34,7 +33,6 @@
|
||||
#include <unistd.h> /* sysconf */
|
||||
|
||||
static uv_once_t once = UV_ONCE_INIT;
|
||||
static uint64_t (*time_func)(void);
|
||||
static mach_timebase_info_data_t timebase;
|
||||
|
||||
|
||||
@ -56,16 +54,12 @@ void uv__platform_loop_delete(uv_loop_t* loop) {
|
||||
static void uv__hrtime_init_once(void) {
|
||||
if (KERN_SUCCESS != mach_timebase_info(&timebase))
|
||||
abort();
|
||||
|
||||
time_func = (uint64_t (*)(void)) dlsym(RTLD_DEFAULT, "mach_continuous_time");
|
||||
if (time_func == NULL)
|
||||
time_func = mach_absolute_time;
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv__hrtime(uv_clocktype_t type) {
|
||||
uv_once(&once, uv__hrtime_init_once);
|
||||
return time_func() * timebase.numer / timebase.denom;
|
||||
return mach_continuous_time() * timebase.numer / timebase.denom;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -26,7 +26,12 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include <paths.h>
|
||||
#include <sys/user.h>
|
||||
#if defined(__DragonFly__)
|
||||
# include <sys/event.h>
|
||||
# include <sys/kinfo.h>
|
||||
#else
|
||||
# include <sys/user.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
273
src/unix/fs.c
273
src/unix/fs.c
@ -31,6 +31,7 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -82,17 +83,6 @@
|
||||
# include <sys/statfs.h>
|
||||
#endif
|
||||
|
||||
#if defined(__CYGWIN__) || \
|
||||
(defined(__HAIKU__) && B_HAIKU_VERSION < B_HAIKU_VERSION_1_PRE_BETA_5) || \
|
||||
(defined(__sun) && !defined(__illumos__)) || \
|
||||
(defined(__APPLE__) && !TARGET_OS_IPHONE && \
|
||||
MAC_OS_X_VERSION_MIN_REQUIRED < 110000)
|
||||
#define preadv(fd, bufs, nbufs, off) \
|
||||
pread(fd, (bufs)->iov_base, (bufs)->iov_len, off)
|
||||
#define pwritev(fd, bufs, nbufs, off) \
|
||||
pwrite(fd, (bufs)->iov_base, (bufs)->iov_len, off)
|
||||
#endif
|
||||
|
||||
#if defined(_AIX) && _XOPEN_SOURCE <= 600
|
||||
extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */
|
||||
#endif
|
||||
@ -149,7 +139,7 @@ extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */
|
||||
#define POST \
|
||||
do { \
|
||||
if (cb != NULL) { \
|
||||
uv__req_register(loop, req); \
|
||||
uv__req_register(loop); \
|
||||
uv__work_submit(loop, \
|
||||
&req->work_req, \
|
||||
UV__WORK_FAST_IO, \
|
||||
@ -213,8 +203,23 @@ static ssize_t uv__fs_fdatasync(uv_fs_t* req) {
|
||||
}
|
||||
|
||||
|
||||
UV_UNUSED(static struct timespec uv__fs_to_timespec(double time)) {
|
||||
#if defined(__APPLE__) \
|
||||
|| defined(_AIX71) \
|
||||
|| defined(__DragonFly__) \
|
||||
|| defined(__FreeBSD__) \
|
||||
|| defined(__HAIKU__) \
|
||||
|| defined(__NetBSD__) \
|
||||
|| defined(__OpenBSD__) \
|
||||
|| defined(__linux__) \
|
||||
|| defined(__sun)
|
||||
static struct timespec uv__fs_to_timespec(double time) {
|
||||
struct timespec ts;
|
||||
|
||||
if (uv__isinf(time))
|
||||
return (struct timespec){UTIME_NOW, UTIME_NOW};
|
||||
if (uv__isnan(time))
|
||||
return (struct timespec){UTIME_OMIT, UTIME_OMIT};
|
||||
|
||||
ts.tv_sec = time;
|
||||
ts.tv_nsec = (time - ts.tv_sec) * 1e9;
|
||||
|
||||
@ -231,41 +236,23 @@ UV_UNUSED(static struct timespec uv__fs_to_timespec(double time)) {
|
||||
}
|
||||
return ts;
|
||||
}
|
||||
#endif
|
||||
|
||||
UV_UNUSED(static struct timeval uv__fs_to_timeval(double time)) {
|
||||
struct timeval tv;
|
||||
tv.tv_sec = time;
|
||||
tv.tv_usec = (time - tv.tv_sec) * 1e6;
|
||||
if (tv.tv_usec < 0) {
|
||||
tv.tv_usec += 1e6;
|
||||
tv.tv_sec -= 1;
|
||||
}
|
||||
return tv;
|
||||
}
|
||||
|
||||
static ssize_t uv__fs_futime(uv_fs_t* req) {
|
||||
#if defined(__linux__) \
|
||||
#if defined(__APPLE__) \
|
||||
|| defined(_AIX71) \
|
||||
|| defined(__DragonFly__) \
|
||||
|| defined(__FreeBSD__) \
|
||||
|| defined(__HAIKU__) \
|
||||
|| defined(__GNU__)
|
||||
|| defined(__NetBSD__) \
|
||||
|| defined(__OpenBSD__) \
|
||||
|| defined(__linux__) \
|
||||
|| defined(__sun)
|
||||
struct timespec ts[2];
|
||||
ts[0] = uv__fs_to_timespec(req->atime);
|
||||
ts[1] = uv__fs_to_timespec(req->mtime);
|
||||
return futimens(req->file, ts);
|
||||
#elif defined(__APPLE__) \
|
||||
|| defined(__DragonFly__) \
|
||||
|| defined(__FreeBSD__) \
|
||||
|| defined(__NetBSD__) \
|
||||
|| defined(__OpenBSD__) \
|
||||
|| defined(__sun)
|
||||
struct timeval tv[2];
|
||||
tv[0] = uv__fs_to_timeval(req->atime);
|
||||
tv[1] = uv__fs_to_timeval(req->mtime);
|
||||
# if defined(__sun)
|
||||
return futimesat(req->file, NULL, tv);
|
||||
# else
|
||||
return futimes(req->file, tv);
|
||||
# endif
|
||||
#elif defined(__MVS__)
|
||||
attrib_t atr;
|
||||
memset(&atr, 0, sizeof(atr));
|
||||
@ -406,6 +393,120 @@ static ssize_t uv__fs_open(uv_fs_t* req) {
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uv__preadv_or_pwritev_emul(int fd,
|
||||
const struct iovec* bufs,
|
||||
size_t nbufs,
|
||||
off_t off,
|
||||
int is_pread) {
|
||||
ssize_t total;
|
||||
ssize_t r;
|
||||
size_t i;
|
||||
size_t n;
|
||||
void* p;
|
||||
|
||||
total = 0;
|
||||
for (i = 0; i < (size_t) nbufs; i++) {
|
||||
p = bufs[i].iov_base;
|
||||
n = bufs[i].iov_len;
|
||||
|
||||
do
|
||||
if (is_pread)
|
||||
r = pread(fd, p, n, off);
|
||||
else
|
||||
r = pwrite(fd, p, n, off);
|
||||
while (r == -1 && errno == EINTR);
|
||||
|
||||
if (r == -1) {
|
||||
if (total > 0)
|
||||
return total;
|
||||
return -1;
|
||||
}
|
||||
|
||||
off += r;
|
||||
total += r;
|
||||
|
||||
if ((size_t) r < n)
|
||||
return total;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __linux__
|
||||
typedef int uv__iovcnt;
|
||||
#else
|
||||
typedef size_t uv__iovcnt;
|
||||
#endif
|
||||
|
||||
|
||||
static ssize_t uv__preadv_emul(int fd,
|
||||
const struct iovec* bufs,
|
||||
uv__iovcnt nbufs,
|
||||
off_t off) {
|
||||
return uv__preadv_or_pwritev_emul(fd, bufs, nbufs, off, /*is_pread*/1);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uv__pwritev_emul(int fd,
|
||||
const struct iovec* bufs,
|
||||
uv__iovcnt nbufs,
|
||||
off_t off) {
|
||||
return uv__preadv_or_pwritev_emul(fd, bufs, nbufs, off, /*is_pread*/0);
|
||||
}
|
||||
|
||||
|
||||
/* The function pointer cache is an uintptr_t because _Atomic void*
|
||||
* doesn't work on macos/ios/etc...
|
||||
*/
|
||||
static ssize_t uv__preadv_or_pwritev(int fd,
|
||||
const struct iovec* bufs,
|
||||
size_t nbufs,
|
||||
off_t off,
|
||||
_Atomic uintptr_t* cache,
|
||||
int is_pread) {
|
||||
ssize_t (*f)(int, const struct iovec*, uv__iovcnt, off_t);
|
||||
void* p;
|
||||
|
||||
p = (void*) atomic_load_explicit(cache, memory_order_relaxed);
|
||||
if (p == NULL) {
|
||||
#ifdef RTLD_DEFAULT
|
||||
/* Try _LARGEFILE_SOURCE version of preadv/pwritev first,
|
||||
* then fall back to the plain version, for libcs like musl.
|
||||
*/
|
||||
p = dlsym(RTLD_DEFAULT, is_pread ? "preadv64" : "pwritev64");
|
||||
if (p == NULL)
|
||||
p = dlsym(RTLD_DEFAULT, is_pread ? "preadv" : "pwritev");
|
||||
dlerror(); /* Clear errors. */
|
||||
#endif /* RTLD_DEFAULT */
|
||||
if (p == NULL)
|
||||
p = is_pread ? uv__preadv_emul : uv__pwritev_emul;
|
||||
atomic_store_explicit(cache, (uintptr_t) p, memory_order_relaxed);
|
||||
}
|
||||
|
||||
f = p;
|
||||
return f(fd, bufs, nbufs, off);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uv__preadv(int fd,
|
||||
const struct iovec* bufs,
|
||||
size_t nbufs,
|
||||
off_t off) {
|
||||
static _Atomic uintptr_t cache;
|
||||
return uv__preadv_or_pwritev(fd, bufs, nbufs, off, &cache, /*is_pread*/1);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uv__pwritev(int fd,
|
||||
const struct iovec* bufs,
|
||||
size_t nbufs,
|
||||
off_t off) {
|
||||
static _Atomic uintptr_t cache;
|
||||
return uv__preadv_or_pwritev(fd, bufs, nbufs, off, &cache, /*is_pread*/0);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t uv__fs_read(uv_fs_t* req) {
|
||||
const struct iovec* bufs;
|
||||
unsigned int iovmax;
|
||||
@ -433,7 +534,7 @@ static ssize_t uv__fs_read(uv_fs_t* req) {
|
||||
if (nbufs == 1)
|
||||
r = pread(fd, bufs->iov_base, bufs->iov_len, off);
|
||||
else if (nbufs > 1)
|
||||
r = preadv(fd, bufs, nbufs, off);
|
||||
r = uv__preadv(fd, bufs, nbufs, off);
|
||||
}
|
||||
|
||||
#ifdef __PASE__
|
||||
@ -971,7 +1072,10 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) {
|
||||
|
||||
return -1;
|
||||
}
|
||||
#elif defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
/* sendfile() on iOS(arm64) will throw SIGSYS signal cause crash. */
|
||||
#elif (defined(__APPLE__) && !TARGET_OS_IPHONE) \
|
||||
|| defined(__DragonFly__) \
|
||||
|| defined(__FreeBSD__)
|
||||
{
|
||||
off_t len;
|
||||
ssize_t r;
|
||||
@ -1035,25 +1139,20 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) {
|
||||
|
||||
|
||||
static ssize_t uv__fs_utime(uv_fs_t* req) {
|
||||
#if defined(__linux__) \
|
||||
|| defined(_AIX71) \
|
||||
|| defined(__sun) \
|
||||
|| defined(__HAIKU__)
|
||||
#if defined(__APPLE__) \
|
||||
|| defined(_AIX71) \
|
||||
|| defined(__DragonFly__) \
|
||||
|| defined(__FreeBSD__) \
|
||||
|| defined(__HAIKU__) \
|
||||
|| defined(__NetBSD__) \
|
||||
|| defined(__OpenBSD__) \
|
||||
|| defined(__linux__) \
|
||||
|| defined(__sun)
|
||||
struct timespec ts[2];
|
||||
ts[0] = uv__fs_to_timespec(req->atime);
|
||||
ts[1] = uv__fs_to_timespec(req->mtime);
|
||||
return utimensat(AT_FDCWD, req->path, ts, 0);
|
||||
#elif defined(__APPLE__) \
|
||||
|| defined(__DragonFly__) \
|
||||
|| defined(__FreeBSD__) \
|
||||
|| defined(__NetBSD__) \
|
||||
|| defined(__OpenBSD__)
|
||||
struct timeval tv[2];
|
||||
tv[0] = uv__fs_to_timeval(req->atime);
|
||||
tv[1] = uv__fs_to_timeval(req->mtime);
|
||||
return utimes(req->path, tv);
|
||||
#elif defined(_AIX) \
|
||||
&& !defined(_AIX71)
|
||||
#elif defined(_AIX) && !defined(_AIX71)
|
||||
struct utimbuf buf;
|
||||
buf.actime = req->atime;
|
||||
buf.modtime = req->mtime;
|
||||
@ -1074,24 +1173,19 @@ static ssize_t uv__fs_utime(uv_fs_t* req) {
|
||||
|
||||
|
||||
static ssize_t uv__fs_lutime(uv_fs_t* req) {
|
||||
#if defined(__linux__) || \
|
||||
defined(_AIX71) || \
|
||||
defined(__sun) || \
|
||||
defined(__HAIKU__) || \
|
||||
defined(__GNU__) || \
|
||||
defined(__OpenBSD__)
|
||||
#if defined(__APPLE__) \
|
||||
|| defined(_AIX71) \
|
||||
|| defined(__DragonFly__) \
|
||||
|| defined(__FreeBSD__) \
|
||||
|| defined(__HAIKU__) \
|
||||
|| defined(__NetBSD__) \
|
||||
|| defined(__OpenBSD__) \
|
||||
|| defined(__linux__) \
|
||||
|| defined(__sun)
|
||||
struct timespec ts[2];
|
||||
ts[0] = uv__fs_to_timespec(req->atime);
|
||||
ts[1] = uv__fs_to_timespec(req->mtime);
|
||||
return utimensat(AT_FDCWD, req->path, ts, AT_SYMLINK_NOFOLLOW);
|
||||
#elif defined(__APPLE__) || \
|
||||
defined(__DragonFly__) || \
|
||||
defined(__FreeBSD__) || \
|
||||
defined(__NetBSD__)
|
||||
struct timeval tv[2];
|
||||
tv[0] = uv__fs_to_timeval(req->atime);
|
||||
tv[1] = uv__fs_to_timeval(req->mtime);
|
||||
return lutimes(req->path, tv);
|
||||
#else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
@ -1121,7 +1215,7 @@ static ssize_t uv__fs_write(uv_fs_t* req) {
|
||||
if (nbufs == 1)
|
||||
r = pwrite(fd, bufs->iov_base, bufs->iov_len, off);
|
||||
else if (nbufs > 1)
|
||||
r = pwritev(fd, bufs, nbufs, off);
|
||||
r = uv__pwritev(fd, bufs, nbufs, off);
|
||||
}
|
||||
|
||||
return r;
|
||||
@ -1134,6 +1228,7 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) {
|
||||
uv_file dstfd;
|
||||
struct stat src_statsbuf;
|
||||
struct stat dst_statsbuf;
|
||||
struct timespec times[2];
|
||||
int dst_flags;
|
||||
int result;
|
||||
int err;
|
||||
@ -1211,6 +1306,35 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the timestamps of the destination file to match the source file.
|
||||
*/
|
||||
#if defined(__APPLE__)
|
||||
times[0] = src_statsbuf.st_atimespec;
|
||||
times[1] = src_statsbuf.st_mtimespec;
|
||||
#elif defined(_AIX)
|
||||
times[0].tv_sec = src_statsbuf.st_atime;
|
||||
times[0].tv_nsec = src_statsbuf.st_atime_n;
|
||||
times[1].tv_sec = src_statsbuf.st_mtime;
|
||||
times[1].tv_nsec = src_statsbuf.st_mtime_n;
|
||||
#else
|
||||
times[0] = src_statsbuf.st_atim;
|
||||
times[1] = src_statsbuf.st_mtim;
|
||||
#endif
|
||||
|
||||
if (futimens(dstfd, times) == -1) {
|
||||
err = UV__ERR(errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the ownership and permissions of the destination file to match the
|
||||
* source file.
|
||||
* `cp -p` does not care about errors here, so we don't either. Reuse the
|
||||
* `result` variable to silence a -Wunused-result warning.
|
||||
*/
|
||||
result = fchown(dstfd, src_statsbuf.st_uid, src_statsbuf.st_gid);
|
||||
|
||||
if (fchmod(dstfd, src_statsbuf.st_mode) == -1) {
|
||||
err = UV__ERR(errno);
|
||||
#ifdef __linux__
|
||||
@ -1628,7 +1752,7 @@ static void uv__fs_done(struct uv__work* w, int status) {
|
||||
uv_fs_t* req;
|
||||
|
||||
req = container_of(w, uv_fs_t, work_req);
|
||||
uv__req_unregister(req->loop, req);
|
||||
uv__req_unregister(req->loop);
|
||||
|
||||
if (status == UV_ECANCELED) {
|
||||
assert(req->result == 0);
|
||||
@ -1640,7 +1764,7 @@ static void uv__fs_done(struct uv__work* w, int status) {
|
||||
|
||||
|
||||
void uv__fs_post(uv_loop_t* loop, uv_fs_t* req) {
|
||||
uv__req_register(loop, req);
|
||||
uv__req_register(loop);
|
||||
uv__work_submit(loop,
|
||||
&req->work_req,
|
||||
UV__WORK_FAST_IO,
|
||||
@ -1775,6 +1899,9 @@ int uv_fs_ftruncate(uv_loop_t* loop,
|
||||
INIT(FTRUNCATE);
|
||||
req->file = file;
|
||||
req->off = off;
|
||||
if (cb != NULL)
|
||||
if (uv__iou_fs_ftruncate(loop, req))
|
||||
return 0;
|
||||
POST;
|
||||
}
|
||||
|
||||
|
||||
@ -276,10 +276,6 @@ static void uv__fsevents_event_cb(const FSEventStreamRef streamRef,
|
||||
path += handle->realpath_len;
|
||||
len -= handle->realpath_len;
|
||||
|
||||
/* Ignore events with path equal to directory itself */
|
||||
if (len <= 1 && (flags & kFSEventStreamEventFlagItemIsDir))
|
||||
continue;
|
||||
|
||||
if (len == 0) {
|
||||
/* Since we're using fsevents to watch the file itself,
|
||||
* realpath == path, and we now need to get the basename of the file back
|
||||
|
||||
@ -109,7 +109,7 @@ static void uv__getaddrinfo_done(struct uv__work* w, int status) {
|
||||
uv_getaddrinfo_t* req;
|
||||
|
||||
req = container_of(w, uv_getaddrinfo_t, work_req);
|
||||
uv__req_unregister(req->loop, req);
|
||||
uv__req_unregister(req->loop);
|
||||
|
||||
/* See initialization in uv_getaddrinfo(). */
|
||||
if (req->hints)
|
||||
|
||||
@ -58,7 +58,7 @@ static void uv__getnameinfo_done(struct uv__work* w, int status) {
|
||||
char* service;
|
||||
|
||||
req = container_of(w, uv_getnameinfo_t, work_req);
|
||||
uv__req_unregister(req->loop, req);
|
||||
uv__req_unregister(req->loop);
|
||||
host = service = NULL;
|
||||
|
||||
if (status == UV_ECANCELED) {
|
||||
|
||||
@ -403,6 +403,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
return UV_ENOSYS;
|
||||
|
||||
/* The first loop to get the size of the array to be allocated */
|
||||
namelen = 0;
|
||||
for (cur = ifap; cur; cur = cur->ifa_next) {
|
||||
if (!(cur->ifa_addr->sa_family == AF_INET6 ||
|
||||
cur->ifa_addr->sa_family == AF_INET))
|
||||
@ -411,6 +412,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
if (!(cur->ifa_flags & IFF_UP && cur->ifa_flags & IFF_RUNNING))
|
||||
continue;
|
||||
|
||||
namelen += strlen(cur->ifa_name) + 1;
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
@ -420,11 +422,13 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
}
|
||||
|
||||
/* Alloc the return interface structs */
|
||||
*addresses = uv__calloc(*count, sizeof(**addresses));
|
||||
*addresses = uv__calloc(1, *count * sizeof(**addresses) + namelen);
|
||||
if (*addresses == NULL) {
|
||||
Qp2freeifaddrs(ifap);
|
||||
return UV_ENOMEM;
|
||||
}
|
||||
|
||||
name = (char*) &(*addresses)[*count];
|
||||
address = *addresses;
|
||||
|
||||
/* The second loop to fill in the array */
|
||||
@ -436,7 +440,9 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
if (!(cur->ifa_flags & IFF_UP && cur->ifa_flags & IFF_RUNNING))
|
||||
continue;
|
||||
|
||||
address->name = uv__strdup(cur->ifa_name);
|
||||
namelen = strlen(cur->ifa_name) + 1;
|
||||
address->name = memcpy(name, cur->ifa_name, namelen);
|
||||
name += namelen;
|
||||
|
||||
inet6 = (cur->ifa_addr->sa_family == AF_INET6);
|
||||
|
||||
@ -497,13 +503,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
}
|
||||
|
||||
|
||||
void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
uv__free(addresses[i].name);
|
||||
}
|
||||
|
||||
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||
int count) {
|
||||
uv__free(addresses);
|
||||
}
|
||||
|
||||
|
||||
@ -35,6 +35,10 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#if defined(__APPLE__) || defined(__DragonFly__) || \
|
||||
defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
#include <sys/event.h>
|
||||
#endif
|
||||
|
||||
#define uv__msan_unpoison(p, n) \
|
||||
do { \
|
||||
@ -71,8 +75,11 @@
|
||||
# include <poll.h>
|
||||
#endif /* _AIX */
|
||||
|
||||
#if defined(__APPLE__) && !TARGET_OS_IPHONE
|
||||
# include <AvailabilityMacros.h>
|
||||
#if defined(__APPLE__)
|
||||
# include "darwin-syscalls.h"
|
||||
# if !TARGET_OS_IPHONE
|
||||
# include <AvailabilityMacros.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -157,7 +164,8 @@ typedef struct uv__stream_queued_fds_s uv__stream_queued_fds_t;
|
||||
/* loop flags */
|
||||
enum {
|
||||
UV_LOOP_BLOCK_SIGPROF = 0x1,
|
||||
UV_LOOP_REAP_CHILDREN = 0x2
|
||||
UV_LOOP_REAP_CHILDREN = 0x2,
|
||||
UV_LOOP_ENABLE_IO_URING_SQPOLL = 0x4
|
||||
};
|
||||
|
||||
/* flags of excluding ifaddr */
|
||||
@ -243,6 +251,7 @@ int uv__close(int fd); /* preserves errno */
|
||||
int uv__close_nocheckstdio(int fd);
|
||||
int uv__close_nocancel(int fd);
|
||||
int uv__socket(int domain, int type, int protocol);
|
||||
int uv__sock_reuseport(int fd);
|
||||
ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags);
|
||||
void uv__make_close_pending(uv_handle_t* handle);
|
||||
int uv__getiovmax(void);
|
||||
@ -287,6 +296,9 @@ int uv__tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb);
|
||||
int uv__tcp_nodelay(int fd, int on);
|
||||
int uv__tcp_keepalive(int fd, int on, unsigned int delay);
|
||||
|
||||
/* tty */
|
||||
void uv__tty_close(uv_tty_t* handle);
|
||||
|
||||
/* pipe */
|
||||
int uv__pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb);
|
||||
|
||||
@ -315,6 +327,8 @@ void uv__prepare_close(uv_prepare_t* handle);
|
||||
void uv__process_close(uv_process_t* handle);
|
||||
void uv__stream_close(uv_stream_t* handle);
|
||||
void uv__tcp_close(uv_tcp_t* handle);
|
||||
int uv__thread_setname(const char* name);
|
||||
int uv__thread_getname(uv_thread_t* tid, char* name, size_t size);
|
||||
size_t uv__thread_stack_size(void);
|
||||
void uv__udp_close(uv_udp_t* handle);
|
||||
void uv__udp_finish_close(uv_udp_t* handle);
|
||||
@ -332,6 +346,7 @@ int uv__random_sysctl(void* buf, size_t buflen);
|
||||
/* io_uring */
|
||||
#ifdef __linux__
|
||||
int uv__iou_fs_close(uv_loop_t* loop, uv_fs_t* req);
|
||||
int uv__iou_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req);
|
||||
int uv__iou_fs_fsync_or_fdatasync(uv_loop_t* loop,
|
||||
uv_fs_t* req,
|
||||
uint32_t fsync_flags);
|
||||
@ -350,6 +365,7 @@ int uv__iou_fs_symlink(uv_loop_t* loop, uv_fs_t* req);
|
||||
int uv__iou_fs_unlink(uv_loop_t* loop, uv_fs_t* req);
|
||||
#else
|
||||
#define uv__iou_fs_close(loop, req) 0
|
||||
#define uv__iou_fs_ftruncate(loop, req) 0
|
||||
#define uv__iou_fs_fsync_or_fdatasync(loop, req, fsync_flags) 0
|
||||
#define uv__iou_fs_link(loop, req) 0
|
||||
#define uv__iou_fs_mkdir(loop, req) 0
|
||||
@ -482,4 +498,34 @@ typedef struct {
|
||||
int uv__get_constrained_cpu(uv__cpu_constraint* constraint);
|
||||
#endif
|
||||
|
||||
#if defined(__sun) && !defined(__illumos__)
|
||||
#ifdef SO_FLOW_NAME
|
||||
/* Since it's impossible to detect the Solaris 11.4 version via OS macros,
|
||||
* so we check the presence of the socket option SO_FLOW_NAME that was first
|
||||
* introduced to Solaris 11.4 and define a custom macro for determining 11.4.
|
||||
*/
|
||||
#define UV__SOLARIS_11_4 (1)
|
||||
#else
|
||||
#define UV__SOLARIS_11_4 (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(EVFILT_USER) && defined(NOTE_TRIGGER)
|
||||
/* EVFILT_USER is available since OS X 10.6, DragonFlyBSD 4.0,
|
||||
* FreeBSD 8.1, and NetBSD 10.0.
|
||||
*
|
||||
* Note that even though EVFILT_USER is defined on the current system,
|
||||
* it may still fail to work at runtime somehow. In that case, we fall
|
||||
* back to pipe-based signaling.
|
||||
*/
|
||||
#define UV__KQUEUE_EVFILT_USER 1
|
||||
/* Magic number of identifier used for EVFILT_USER during runtime detection.
|
||||
* There are no Google hits for this number when I create it. That way,
|
||||
* people will be directed here if this number gets printed due to some
|
||||
* kqueue error and they google for help. */
|
||||
#define UV__KQUEUE_EVFILT_USER_IDENT 0x1e7e7711
|
||||
#else
|
||||
#define UV__KQUEUE_EVFILT_USER 0
|
||||
#endif
|
||||
|
||||
#endif /* UV_UNIX_INTERNAL_H_ */
|
||||
|
||||
@ -97,20 +97,47 @@ int uv__io_fork(uv_loop_t* loop) {
|
||||
|
||||
|
||||
int uv__io_check_fd(uv_loop_t* loop, int fd) {
|
||||
struct kevent ev;
|
||||
int rc;
|
||||
struct kevent ev[2];
|
||||
struct stat sb;
|
||||
#ifdef __APPLE__
|
||||
char path[MAXPATHLEN];
|
||||
#endif
|
||||
|
||||
rc = 0;
|
||||
EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
|
||||
if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
|
||||
rc = UV__ERR(errno);
|
||||
if (uv__fstat(fd, &sb))
|
||||
return UV__ERR(errno);
|
||||
|
||||
EV_SET(&ev, fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
|
||||
if (rc == 0)
|
||||
if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
|
||||
abort();
|
||||
/* On FreeBSD, kqueue only supports EVFILT_READ notification for regular files
|
||||
* and always reports ready events for writing, resulting in busy-looping.
|
||||
*
|
||||
* On Darwin, DragonFlyBSD, NetBSD and OpenBSD, kqueue reports ready events for
|
||||
* regular files as readable and writable only once, acting like an EV_ONESHOT.
|
||||
*
|
||||
* Neither of the above cases should be added to the kqueue.
|
||||
*/
|
||||
if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode))
|
||||
return UV_EINVAL;
|
||||
|
||||
return rc;
|
||||
#ifdef __APPLE__
|
||||
/* On Darwin (both macOS and iOS), in addition to regular files, FIFOs also don't
|
||||
* work properly with kqueue: the disconnection from the last writer won't trigger
|
||||
* an event for kqueue in spite of what the man pages say. Thus, we also disallow
|
||||
* the case of S_IFIFO. */
|
||||
if (S_ISFIFO(sb.st_mode)) {
|
||||
/* File descriptors of FIFO, pipe and kqueue share the same type of file,
|
||||
* therefore there is no way to tell them apart via stat.st_mode&S_IFMT.
|
||||
* Fortunately, FIFO is the only one that has a persisted file on filesystem,
|
||||
* from which we're able to make the distinction for it. */
|
||||
if (!fcntl(fd, F_GETPATH, path))
|
||||
return UV_EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
EV_SET(ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
|
||||
EV_SET(ev + 1, fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
|
||||
if (kevent(loop->backend_fd, ev, 2, NULL, 0, NULL))
|
||||
return UV__ERR(errno);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -334,6 +361,17 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#if UV__KQUEUE_EVFILT_USER
|
||||
if (ev->filter == EVFILT_USER) {
|
||||
w = &loop->async_io_watcher;
|
||||
assert(fd == w->fd);
|
||||
uv__metrics_update_idle_time(loop);
|
||||
w->cb(loop, w, w->events);
|
||||
nevents++;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ev->filter == EVFILT_VNODE) {
|
||||
assert(w->events == POLLIN);
|
||||
assert(w->pevents == POLLIN);
|
||||
|
||||
306
src/unix/linux.c
306
src/unix/linux.c
@ -126,6 +126,7 @@
|
||||
|
||||
enum {
|
||||
UV__IORING_SETUP_SQPOLL = 2u,
|
||||
UV__IORING_SETUP_NO_SQARRAY = 0x10000u,
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -147,6 +148,7 @@ enum {
|
||||
UV__IORING_OP_MKDIRAT = 37,
|
||||
UV__IORING_OP_SYMLINKAT = 38,
|
||||
UV__IORING_OP_LINKAT = 39,
|
||||
UV__IORING_OP_FTRUNCATE = 55,
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -159,10 +161,6 @@ enum {
|
||||
UV__IORING_SQ_CQ_OVERFLOW = 2u,
|
||||
};
|
||||
|
||||
enum {
|
||||
UV__MKDIRAT_SYMLINKAT_LINKAT = 1u,
|
||||
};
|
||||
|
||||
struct uv__io_cqring_offsets {
|
||||
uint32_t head;
|
||||
uint32_t tail;
|
||||
@ -457,7 +455,7 @@ int uv__io_uring_register(int fd, unsigned opcode, void* arg, unsigned nargs) {
|
||||
}
|
||||
|
||||
|
||||
static int uv__use_io_uring(void) {
|
||||
static int uv__use_io_uring(uint32_t flags) {
|
||||
#if defined(__ANDROID_API__)
|
||||
return 0; /* Possibly available but blocked by seccomp. */
|
||||
#elif defined(__arm__) && __SIZEOF_POINTER__ == 4
|
||||
@ -472,25 +470,27 @@ static int uv__use_io_uring(void) {
|
||||
char* val;
|
||||
int use;
|
||||
|
||||
#if defined(__hppa__)
|
||||
/* io_uring first supported on parisc in 6.1, functional in .51
|
||||
* https://lore.kernel.org/all/cb912694-b1fe-dbb0-4d8c-d608f3526905@gmx.de/
|
||||
*/
|
||||
if (uv__kernel_version() < /*6.1.51*/0x060133)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
/* SQPOLL is all kinds of buggy but epoll batching should work fine. */
|
||||
if (0 == (flags & UV__IORING_SETUP_SQPOLL))
|
||||
return 1;
|
||||
|
||||
/* Older kernels have a bug where the sqpoll thread uses 100% CPU. */
|
||||
if (uv__kernel_version() < /*5.10.186*/0x050ABA)
|
||||
return 0;
|
||||
|
||||
use = atomic_load_explicit(&use_io_uring, memory_order_relaxed);
|
||||
|
||||
if (use == 0) {
|
||||
use = uv__kernel_version() >=
|
||||
#if defined(__hppa__)
|
||||
/* io_uring first supported on parisc in 6.1, functional in .51 */
|
||||
/* https://lore.kernel.org/all/cb912694-b1fe-dbb0-4d8c-d608f3526905@gmx.de/ */
|
||||
/* 6.1.51 */ 0x060133
|
||||
#else
|
||||
/* Older kernels have a bug where the sqpoll thread uses 100% CPU. */
|
||||
/* 5.10.186 */ 0x050ABA
|
||||
#endif
|
||||
? 1 : -1;
|
||||
|
||||
/* But users can still enable it if they so desire. */
|
||||
val = getenv("UV_USE_IO_URING");
|
||||
if (val != NULL)
|
||||
use = atoi(val) ? 1 : -1;
|
||||
|
||||
use = val != NULL && atoi(val) > 0 ? 1 : -1;
|
||||
atomic_store_explicit(&use_io_uring, use, memory_order_relaxed);
|
||||
}
|
||||
|
||||
@ -509,22 +509,29 @@ static void uv__iou_init(int epollfd,
|
||||
size_t sqlen;
|
||||
size_t maxlen;
|
||||
size_t sqelen;
|
||||
unsigned kernel_version;
|
||||
uint32_t* sqarray;
|
||||
uint32_t i;
|
||||
char* sq;
|
||||
char* sqe;
|
||||
int ringfd;
|
||||
int no_sqarray;
|
||||
|
||||
sq = MAP_FAILED;
|
||||
sqe = MAP_FAILED;
|
||||
|
||||
if (!uv__use_io_uring())
|
||||
if (!uv__use_io_uring(flags))
|
||||
return;
|
||||
|
||||
kernel_version = uv__kernel_version();
|
||||
no_sqarray =
|
||||
UV__IORING_SETUP_NO_SQARRAY * (kernel_version >= /* 6.6 */0x060600);
|
||||
|
||||
/* SQPOLL required CAP_SYS_NICE until linux v5.12 relaxed that requirement.
|
||||
* Mostly academic because we check for a v5.13 kernel afterwards anyway.
|
||||
*/
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.flags = flags;
|
||||
params.flags = flags | no_sqarray;
|
||||
|
||||
if (flags & UV__IORING_SETUP_SQPOLL)
|
||||
params.sq_thread_idle = 10; /* milliseconds */
|
||||
@ -586,7 +593,6 @@ static void uv__iou_init(int epollfd,
|
||||
iou->sqhead = (uint32_t*) (sq + params.sq_off.head);
|
||||
iou->sqtail = (uint32_t*) (sq + params.sq_off.tail);
|
||||
iou->sqmask = *(uint32_t*) (sq + params.sq_off.ring_mask);
|
||||
iou->sqarray = (uint32_t*) (sq + params.sq_off.array);
|
||||
iou->sqflags = (uint32_t*) (sq + params.sq_off.flags);
|
||||
iou->cqhead = (uint32_t*) (sq + params.cq_off.head);
|
||||
iou->cqtail = (uint32_t*) (sq + params.cq_off.tail);
|
||||
@ -600,13 +606,13 @@ static void uv__iou_init(int epollfd,
|
||||
iou->sqelen = sqelen;
|
||||
iou->ringfd = ringfd;
|
||||
iou->in_flight = 0;
|
||||
iou->flags = 0;
|
||||
|
||||
if (uv__kernel_version() >= /* 5.15.0 */ 0x050F00)
|
||||
iou->flags |= UV__MKDIRAT_SYMLINKAT_LINKAT;
|
||||
if (no_sqarray)
|
||||
return;
|
||||
|
||||
sqarray = (uint32_t*) (sq + params.sq_off.array);
|
||||
for (i = 0; i <= iou->sqmask; i++)
|
||||
iou->sqarray[i] = i; /* Slot -> sqe identity mapping. */
|
||||
sqarray[i] = i; /* Slot -> sqe identity mapping. */
|
||||
|
||||
return;
|
||||
|
||||
@ -712,23 +718,17 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
|
||||
* This avoids a problem where the same file description remains open
|
||||
* in another process, causing repeated junk epoll events.
|
||||
*
|
||||
* Perform EPOLL_CTL_DEL immediately instead of going through
|
||||
* io_uring's submit queue, otherwise the file descriptor may
|
||||
* be closed by the time the kernel starts the operation.
|
||||
*
|
||||
* We pass in a dummy epoll_event, to work around a bug in old kernels.
|
||||
*
|
||||
* Work around a bug in kernels 3.10 to 3.19 where passing a struct that
|
||||
* has the EPOLLWAKEUP flag set generates spurious audit syslog warnings.
|
||||
*/
|
||||
memset(&dummy, 0, sizeof(dummy));
|
||||
|
||||
if (inv == NULL) {
|
||||
epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &dummy);
|
||||
} else {
|
||||
uv__epoll_ctl_prep(loop->backend_fd,
|
||||
&lfields->ctl,
|
||||
inv->prep,
|
||||
EPOLL_CTL_DEL,
|
||||
fd,
|
||||
&dummy);
|
||||
}
|
||||
epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &dummy);
|
||||
}
|
||||
|
||||
|
||||
@ -767,7 +767,14 @@ static struct uv__io_uring_sqe* uv__iou_get_sqe(struct uv__iou* iou,
|
||||
* initialization failed. Anything else is a valid ring file descriptor.
|
||||
*/
|
||||
if (iou->ringfd == -2) {
|
||||
uv__iou_init(loop->backend_fd, iou, 64, UV__IORING_SETUP_SQPOLL);
|
||||
/* By default, the SQPOLL is not created. Enable only if the loop is
|
||||
* configured with UV_LOOP_USE_IO_URING_SQPOLL and the UV_USE_IO_URING
|
||||
* environment variable is unset or a positive number.
|
||||
*/
|
||||
if (loop->flags & UV_LOOP_ENABLE_IO_URING_SQPOLL)
|
||||
if (uv__use_io_uring(UV__IORING_SETUP_SQPOLL))
|
||||
uv__iou_init(loop->backend_fd, iou, 64, UV__IORING_SETUP_SQPOLL);
|
||||
|
||||
if (iou->ringfd == -2)
|
||||
iou->ringfd = -1; /* "failed" */
|
||||
}
|
||||
@ -795,7 +802,7 @@ static struct uv__io_uring_sqe* uv__iou_get_sqe(struct uv__iou* iou,
|
||||
req->work_req.done = NULL;
|
||||
uv__queue_init(&req->work_req.wq);
|
||||
|
||||
uv__req_register(loop, req);
|
||||
uv__req_register(loop);
|
||||
iou->in_flight++;
|
||||
|
||||
return sqe;
|
||||
@ -858,6 +865,26 @@ int uv__iou_fs_close(uv_loop_t* loop, uv_fs_t* req) {
|
||||
}
|
||||
|
||||
|
||||
int uv__iou_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req) {
|
||||
struct uv__io_uring_sqe* sqe;
|
||||
struct uv__iou* iou;
|
||||
|
||||
if (uv__kernel_version() < /* 6.9 */0x060900)
|
||||
return 0;
|
||||
|
||||
iou = &uv__get_internal_fields(loop)->iou;
|
||||
sqe = uv__iou_get_sqe(iou, loop, req);
|
||||
if (sqe == NULL)
|
||||
return 0;
|
||||
|
||||
sqe->fd = req->file;
|
||||
sqe->len = req->off;
|
||||
sqe->opcode = UV__IORING_OP_FTRUNCATE;
|
||||
uv__iou_submit(iou);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int uv__iou_fs_fsync_or_fdatasync(uv_loop_t* loop,
|
||||
uv_fs_t* req,
|
||||
uint32_t fsync_flags) {
|
||||
@ -887,11 +914,10 @@ int uv__iou_fs_link(uv_loop_t* loop, uv_fs_t* req) {
|
||||
struct uv__io_uring_sqe* sqe;
|
||||
struct uv__iou* iou;
|
||||
|
||||
iou = &uv__get_internal_fields(loop)->iou;
|
||||
|
||||
if (!(iou->flags & UV__MKDIRAT_SYMLINKAT_LINKAT))
|
||||
if (uv__kernel_version() < /* 5.15.0 */0x050F00)
|
||||
return 0;
|
||||
|
||||
iou = &uv__get_internal_fields(loop)->iou;
|
||||
sqe = uv__iou_get_sqe(iou, loop, req);
|
||||
if (sqe == NULL)
|
||||
return 0;
|
||||
@ -912,11 +938,10 @@ int uv__iou_fs_mkdir(uv_loop_t* loop, uv_fs_t* req) {
|
||||
struct uv__io_uring_sqe* sqe;
|
||||
struct uv__iou* iou;
|
||||
|
||||
iou = &uv__get_internal_fields(loop)->iou;
|
||||
|
||||
if (!(iou->flags & UV__MKDIRAT_SYMLINKAT_LINKAT))
|
||||
if (uv__kernel_version() < /* 5.15.0 */0x050F00)
|
||||
return 0;
|
||||
|
||||
iou = &uv__get_internal_fields(loop)->iou;
|
||||
sqe = uv__iou_get_sqe(iou, loop, req);
|
||||
if (sqe == NULL)
|
||||
return 0;
|
||||
@ -980,11 +1005,10 @@ int uv__iou_fs_symlink(uv_loop_t* loop, uv_fs_t* req) {
|
||||
struct uv__io_uring_sqe* sqe;
|
||||
struct uv__iou* iou;
|
||||
|
||||
iou = &uv__get_internal_fields(loop)->iou;
|
||||
|
||||
if (!(iou->flags & UV__MKDIRAT_SYMLINKAT_LINKAT))
|
||||
if (uv__kernel_version() < /* 5.15.0 */0x050F00)
|
||||
return 0;
|
||||
|
||||
iou = &uv__get_internal_fields(loop)->iou;
|
||||
sqe = uv__iou_get_sqe(iou, loop, req);
|
||||
if (sqe == NULL)
|
||||
return 0;
|
||||
@ -1163,7 +1187,7 @@ static void uv__poll_io_uring(uv_loop_t* loop, struct uv__iou* iou) {
|
||||
req = (uv_fs_t*) (uintptr_t) e->user_data;
|
||||
assert(req->type == UV_FS);
|
||||
|
||||
uv__req_unregister(loop, req);
|
||||
uv__req_unregister(loop);
|
||||
iou->in_flight--;
|
||||
|
||||
/* If the op is not supported by the kernel retry using the thread pool */
|
||||
@ -1215,6 +1239,10 @@ static void uv__poll_io_uring(uv_loop_t* loop, struct uv__iou* iou) {
|
||||
}
|
||||
|
||||
|
||||
/* Only for EPOLL_CTL_ADD and EPOLL_CTL_MOD. EPOLL_CTL_DEL should always be
|
||||
* executed immediately, otherwise the file descriptor may have been closed
|
||||
* by the time the kernel starts the operation.
|
||||
*/
|
||||
static void uv__epoll_ctl_prep(int epollfd,
|
||||
struct uv__iou* ctl,
|
||||
struct epoll_event (*events)[256],
|
||||
@ -1226,45 +1254,28 @@ static void uv__epoll_ctl_prep(int epollfd,
|
||||
uint32_t mask;
|
||||
uint32_t slot;
|
||||
|
||||
if (ctl->ringfd == -1) {
|
||||
if (!epoll_ctl(epollfd, op, fd, e))
|
||||
return;
|
||||
assert(op == EPOLL_CTL_ADD || op == EPOLL_CTL_MOD);
|
||||
assert(ctl->ringfd != -1);
|
||||
|
||||
if (op == EPOLL_CTL_DEL)
|
||||
return; /* Ignore errors, may be racing with another thread. */
|
||||
mask = ctl->sqmask;
|
||||
slot = (*ctl->sqtail)++ & mask;
|
||||
|
||||
if (op != EPOLL_CTL_ADD)
|
||||
abort();
|
||||
pe = &(*events)[slot];
|
||||
*pe = *e;
|
||||
|
||||
if (errno != EEXIST)
|
||||
abort();
|
||||
sqe = ctl->sqe;
|
||||
sqe = &sqe[slot];
|
||||
|
||||
/* File descriptor that's been watched before, update event mask. */
|
||||
if (!epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, e))
|
||||
return;
|
||||
memset(sqe, 0, sizeof(*sqe));
|
||||
sqe->addr = (uintptr_t) pe;
|
||||
sqe->fd = epollfd;
|
||||
sqe->len = op;
|
||||
sqe->off = fd;
|
||||
sqe->opcode = UV__IORING_OP_EPOLL_CTL;
|
||||
sqe->user_data = op | slot << 2 | (int64_t) fd << 32;
|
||||
|
||||
abort();
|
||||
} else {
|
||||
mask = ctl->sqmask;
|
||||
slot = (*ctl->sqtail)++ & mask;
|
||||
|
||||
pe = &(*events)[slot];
|
||||
*pe = *e;
|
||||
|
||||
sqe = ctl->sqe;
|
||||
sqe = &sqe[slot];
|
||||
|
||||
memset(sqe, 0, sizeof(*sqe));
|
||||
sqe->addr = (uintptr_t) pe;
|
||||
sqe->fd = epollfd;
|
||||
sqe->len = op;
|
||||
sqe->off = fd;
|
||||
sqe->opcode = UV__IORING_OP_EPOLL_CTL;
|
||||
sqe->user_data = op | slot << 2 | (int64_t) fd << 32;
|
||||
|
||||
if ((*ctl->sqhead & mask) == (*ctl->sqtail & mask))
|
||||
uv__epoll_ctl_flush(epollfd, ctl, events);
|
||||
}
|
||||
if ((*ctl->sqhead & mask) == (*ctl->sqtail & mask))
|
||||
uv__epoll_ctl_flush(epollfd, ctl, events);
|
||||
}
|
||||
|
||||
|
||||
@ -1405,8 +1416,22 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
w->events = w->pevents;
|
||||
e.events = w->pevents;
|
||||
e.data.fd = w->fd;
|
||||
fd = w->fd;
|
||||
|
||||
uv__epoll_ctl_prep(epollfd, ctl, &prep, op, w->fd, &e);
|
||||
if (ctl->ringfd != -1) {
|
||||
uv__epoll_ctl_prep(epollfd, ctl, &prep, op, fd, &e);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!epoll_ctl(epollfd, op, fd, &e))
|
||||
continue;
|
||||
|
||||
assert(op == EPOLL_CTL_ADD);
|
||||
assert(errno == EEXIST);
|
||||
|
||||
/* File descriptor that's been watched before, update event mask. */
|
||||
if (epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &e))
|
||||
abort();
|
||||
}
|
||||
|
||||
inv.events = events;
|
||||
@ -1494,8 +1519,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
*
|
||||
* Ignore all errors because we may be racing with another thread
|
||||
* when the file descriptor is closed.
|
||||
*
|
||||
* Perform EPOLL_CTL_DEL immediately instead of going through
|
||||
* io_uring's submit queue, otherwise the file descriptor may
|
||||
* be closed by the time the kernel starts the operation.
|
||||
*/
|
||||
uv__epoll_ctl_prep(epollfd, ctl, &prep, EPOLL_CTL_DEL, fd, pe);
|
||||
epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, pe);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1630,36 +1659,17 @@ done:
|
||||
int uv_resident_set_memory(size_t* rss) {
|
||||
char buf[1024];
|
||||
const char* s;
|
||||
ssize_t n;
|
||||
long val;
|
||||
int fd;
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
do
|
||||
fd = open("/proc/self/stat", O_RDONLY);
|
||||
while (fd == -1 && errno == EINTR);
|
||||
/* rss: 24th element */
|
||||
rc = uv__slurp("/proc/self/stat", buf, sizeof(buf));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (fd == -1)
|
||||
return UV__ERR(errno);
|
||||
|
||||
do
|
||||
n = read(fd, buf, sizeof(buf) - 1);
|
||||
while (n == -1 && errno == EINTR);
|
||||
|
||||
uv__close(fd);
|
||||
if (n == -1)
|
||||
return UV__ERR(errno);
|
||||
buf[n] = '\0';
|
||||
|
||||
s = strchr(buf, ' ');
|
||||
if (s == NULL)
|
||||
goto err;
|
||||
|
||||
s += 1;
|
||||
if (*s != '(')
|
||||
goto err;
|
||||
|
||||
s = strchr(s, ')');
|
||||
/* find the last ')' */
|
||||
s = strrchr(buf, ')');
|
||||
if (s == NULL)
|
||||
goto err;
|
||||
|
||||
@ -1671,9 +1681,7 @@ int uv_resident_set_memory(size_t* rss) {
|
||||
|
||||
errno = 0;
|
||||
val = strtol(s, NULL, 10);
|
||||
if (errno != 0)
|
||||
goto err;
|
||||
if (val < 0)
|
||||
if (val < 0 || errno != 0)
|
||||
goto err;
|
||||
|
||||
*rss = val * getpagesize();
|
||||
@ -1706,16 +1714,22 @@ int uv_uptime(double* uptime) {
|
||||
int uv_cpu_info(uv_cpu_info_t** ci, int* count) {
|
||||
#if defined(__PPC__)
|
||||
static const char model_marker[] = "cpu\t\t: ";
|
||||
static const char model_marker2[] = "";
|
||||
#elif defined(__arm__)
|
||||
static const char model_marker[] = "Processor\t: ";
|
||||
static const char model_marker[] = "model name\t: ";
|
||||
static const char model_marker2[] = "Processor\t: ";
|
||||
#elif defined(__aarch64__)
|
||||
static const char model_marker[] = "CPU part\t: ";
|
||||
static const char model_marker2[] = "";
|
||||
#elif defined(__mips__)
|
||||
static const char model_marker[] = "cpu model\t\t: ";
|
||||
static const char model_marker2[] = "";
|
||||
#elif defined(__loongarch__)
|
||||
static const char model_marker[] = "cpu family\t\t: ";
|
||||
static const char model_marker2[] = "";
|
||||
#else
|
||||
static const char model_marker[] = "model name\t: ";
|
||||
static const char model_marker2[] = "";
|
||||
#endif
|
||||
static const char parts[] =
|
||||
#ifdef __aarch64__
|
||||
@ -1814,14 +1828,22 @@ int uv_cpu_info(uv_cpu_info_t** ci, int* count) {
|
||||
if (1 != fscanf(fp, "processor\t: %u\n", &cpu))
|
||||
break; /* Parse error. */
|
||||
|
||||
found = 0;
|
||||
while (!found && fgets(buf, sizeof(buf), fp))
|
||||
found = !strncmp(buf, model_marker, sizeof(model_marker) - 1);
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
if (!strncmp(buf, model_marker, sizeof(model_marker) - 1)) {
|
||||
p = buf + sizeof(model_marker) - 1;
|
||||
goto parts;
|
||||
}
|
||||
if (!*model_marker2)
|
||||
continue;
|
||||
if (!strncmp(buf, model_marker2, sizeof(model_marker2) - 1)) {
|
||||
p = buf + sizeof(model_marker2) - 1;
|
||||
goto parts;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
goto next;
|
||||
goto next; /* Not found. */
|
||||
|
||||
p = buf + sizeof(model_marker) - 1;
|
||||
parts:
|
||||
n = (int) strcspn(p, "\n");
|
||||
|
||||
/* arm64: translate CPU part code to model name. */
|
||||
@ -1932,11 +1954,15 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
|
||||
return !exclude_type;
|
||||
}
|
||||
|
||||
/* TODO(bnoordhuis) share with bsd-ifaddrs.c */
|
||||
int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
struct ifaddrs *addrs, *ent;
|
||||
uv_interface_address_t* address;
|
||||
struct sockaddr_ll* sll;
|
||||
struct ifaddrs* addrs;
|
||||
struct ifaddrs* ent;
|
||||
size_t namelen;
|
||||
char* name;
|
||||
int i;
|
||||
struct sockaddr_ll *sll;
|
||||
|
||||
*count = 0;
|
||||
*addresses = NULL;
|
||||
@ -1945,10 +1971,12 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
return UV__ERR(errno);
|
||||
|
||||
/* Count the number of interfaces */
|
||||
namelen = 0;
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
|
||||
continue;
|
||||
|
||||
namelen += strlen(ent->ifa_name) + 1;
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
@ -1958,19 +1986,22 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
}
|
||||
|
||||
/* Make sure the memory is initiallized to zero using calloc() */
|
||||
*addresses = uv__calloc(*count, sizeof(**addresses));
|
||||
if (!(*addresses)) {
|
||||
*addresses = uv__calloc(1, *count * sizeof(**addresses) + namelen);
|
||||
if (*addresses == NULL) {
|
||||
freeifaddrs(addrs);
|
||||
return UV_ENOMEM;
|
||||
}
|
||||
|
||||
name = (char*) &(*addresses)[*count];
|
||||
address = *addresses;
|
||||
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
|
||||
continue;
|
||||
|
||||
address->name = uv__strdup(ent->ifa_name);
|
||||
namelen = strlen(ent->ifa_name) + 1;
|
||||
address->name = memcpy(name, ent->ifa_name, namelen);
|
||||
name += namelen;
|
||||
|
||||
if (ent->ifa_addr->sa_family == AF_INET6) {
|
||||
address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
|
||||
@ -2014,14 +2045,9 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
}
|
||||
|
||||
|
||||
/* TODO(bnoordhuis) share with bsd-ifaddrs.c */
|
||||
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||
int count) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
uv__free(addresses[i].name);
|
||||
}
|
||||
|
||||
int count) {
|
||||
uv__free(addresses);
|
||||
}
|
||||
|
||||
@ -2278,7 +2304,7 @@ uint64_t uv_get_available_memory(void) {
|
||||
}
|
||||
|
||||
|
||||
static int uv__get_cgroupv2_constrained_cpu(const char* cgroup,
|
||||
static int uv__get_cgroupv2_constrained_cpu(const char* cgroup,
|
||||
uv__cpu_constraint* constraint) {
|
||||
char path[256];
|
||||
char buf[1024];
|
||||
@ -2289,7 +2315,7 @@ static int uv__get_cgroupv2_constrained_cpu(const char* cgroup,
|
||||
|
||||
if (strncmp(cgroup, "0::/", 4) != 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
|
||||
/* Trim ending \n by replacing it with a 0 */
|
||||
cgroup_trimmed = cgroup + sizeof("0::/") - 1; /* Skip the prefix "0::/" */
|
||||
cgroup_size = (int)strcspn(cgroup_trimmed, "\n"); /* Find the first slash */
|
||||
@ -2341,7 +2367,7 @@ static char* uv__cgroup1_find_cpu_controller(const char* cgroup,
|
||||
return cgroup_cpu;
|
||||
}
|
||||
|
||||
static int uv__get_cgroupv1_constrained_cpu(const char* cgroup,
|
||||
static int uv__get_cgroupv1_constrained_cpu(const char* cgroup,
|
||||
uv__cpu_constraint* constraint) {
|
||||
char path[256];
|
||||
char buf[1024];
|
||||
@ -2359,8 +2385,8 @@ static int uv__get_cgroupv1_constrained_cpu(const char* cgroup,
|
||||
cgroup_size, cgroup_cpu);
|
||||
|
||||
if (uv__slurp(path, buf, sizeof(buf)) < 0)
|
||||
return UV_EIO;
|
||||
|
||||
return UV_EIO;
|
||||
|
||||
if (sscanf(buf, "%lld", &constraint->quota_per_period) != 1)
|
||||
return UV_EINVAL;
|
||||
|
||||
@ -2382,7 +2408,7 @@ static int uv__get_cgroupv1_constrained_cpu(const char* cgroup,
|
||||
/* Read cpu.shares */
|
||||
if (uv__slurp(path, buf, sizeof(buf)) < 0)
|
||||
return UV_EIO;
|
||||
|
||||
|
||||
if (sscanf(buf, "%u", &shares) != 1)
|
||||
return UV_EINVAL;
|
||||
|
||||
|
||||
@ -217,6 +217,14 @@ int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
if (option == UV_LOOP_USE_IO_URING_SQPOLL) {
|
||||
loop->flags |= UV_LOOP_ENABLE_IO_URING_SQPOLL;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if (option != UV_LOOP_BLOCK_SIGNAL)
|
||||
return UV_ENOSYS;
|
||||
|
||||
|
||||
@ -76,8 +76,13 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
||||
if (name == NULL)
|
||||
return UV_EINVAL;
|
||||
|
||||
/* namelen==0 on Linux means autobind the listen socket in the abstract
|
||||
* socket namespace, see `man 7 unix` for details.
|
||||
*/
|
||||
#if !defined(__linux__)
|
||||
if (namelen == 0)
|
||||
return UV_EINVAL;
|
||||
#endif
|
||||
|
||||
if (includes_nul(name, namelen))
|
||||
return UV_EINVAL;
|
||||
@ -344,10 +349,20 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
|
||||
uv__peersockfunc func,
|
||||
char* buffer,
|
||||
size_t* size) {
|
||||
#if defined(__linux__)
|
||||
static const int is_linux = 1;
|
||||
#else
|
||||
static const int is_linux = 0;
|
||||
#endif
|
||||
struct sockaddr_un sa;
|
||||
socklen_t addrlen;
|
||||
size_t slop;
|
||||
char* p;
|
||||
int err;
|
||||
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
addrlen = sizeof(sa);
|
||||
memset(&sa, 0, addrlen);
|
||||
err = uv__getsockpeername((const uv_handle_t*) handle,
|
||||
@ -359,17 +374,20 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
|
||||
return err;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
if (sa.sun_path[0] == 0)
|
||||
/* Linux abstract namespace */
|
||||
slop = 1;
|
||||
if (is_linux && sa.sun_path[0] == '\0') {
|
||||
/* Linux abstract namespace. Not zero-terminated. */
|
||||
slop = 0;
|
||||
addrlen -= offsetof(struct sockaddr_un, sun_path);
|
||||
else
|
||||
#endif
|
||||
addrlen = strlen(sa.sun_path);
|
||||
} else {
|
||||
p = memchr(sa.sun_path, '\0', sizeof(sa.sun_path));
|
||||
if (p == NULL)
|
||||
p = ARRAY_END(sa.sun_path);
|
||||
addrlen = p - sa.sun_path;
|
||||
}
|
||||
|
||||
|
||||
if ((size_t)addrlen >= *size) {
|
||||
*size = addrlen + 1;
|
||||
if ((size_t)addrlen + slop > *size) {
|
||||
*size = addrlen + slop;
|
||||
return UV_ENOBUFS;
|
||||
}
|
||||
|
||||
@ -429,7 +447,7 @@ uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) {
|
||||
int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
|
||||
unsigned desired_mode;
|
||||
struct stat pipe_stat;
|
||||
char* name_buffer;
|
||||
char name_buffer[1 + UV__PATH_MAX];
|
||||
size_t name_len;
|
||||
int r;
|
||||
|
||||
@ -442,26 +460,14 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
|
||||
return UV_EINVAL;
|
||||
|
||||
/* Unfortunately fchmod does not work on all platforms, we will use chmod. */
|
||||
name_len = 0;
|
||||
r = uv_pipe_getsockname(handle, NULL, &name_len);
|
||||
if (r != UV_ENOBUFS)
|
||||
return r;
|
||||
|
||||
name_buffer = uv__malloc(name_len);
|
||||
if (name_buffer == NULL)
|
||||
return UV_ENOMEM;
|
||||
|
||||
name_len = sizeof(name_buffer);
|
||||
r = uv_pipe_getsockname(handle, name_buffer, &name_len);
|
||||
if (r != 0) {
|
||||
uv__free(name_buffer);
|
||||
if (r != 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* stat must be used as fstat has a bug on Darwin */
|
||||
if (uv__stat(name_buffer, &pipe_stat) == -1) {
|
||||
uv__free(name_buffer);
|
||||
return -errno;
|
||||
}
|
||||
if (uv__stat(name_buffer, &pipe_stat) == -1)
|
||||
return UV__ERR(errno);
|
||||
|
||||
desired_mode = 0;
|
||||
if (mode & UV_READABLE)
|
||||
@ -470,15 +476,12 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
|
||||
desired_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
|
||||
|
||||
/* Exit early if pipe already has desired mode. */
|
||||
if ((pipe_stat.st_mode & desired_mode) == desired_mode) {
|
||||
uv__free(name_buffer);
|
||||
if ((pipe_stat.st_mode & desired_mode) == desired_mode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
pipe_stat.st_mode |= desired_mode;
|
||||
|
||||
r = chmod(name_buffer, pipe_stat.st_mode);
|
||||
uv__free(name_buffer);
|
||||
|
||||
return r != -1 ? 0 : UV__ERR(errno);
|
||||
}
|
||||
@ -487,7 +490,11 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
|
||||
int uv_pipe(uv_os_fd_t fds[2], int read_flags, int write_flags) {
|
||||
uv_os_fd_t temp[2];
|
||||
int err;
|
||||
#if defined(__FreeBSD__) || defined(__linux__)
|
||||
#if defined(__linux__) || \
|
||||
defined(__FreeBSD__) || \
|
||||
defined(__OpenBSD__) || \
|
||||
defined(__DragonFly__) || \
|
||||
defined(__NetBSD__)
|
||||
int flags = O_CLOEXEC;
|
||||
|
||||
if ((read_flags & UV_NONBLOCK_PIPE) && (write_flags & UV_NONBLOCK_PIPE))
|
||||
|
||||
@ -24,7 +24,6 @@
|
||||
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
static void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
|
||||
|
||||
@ -55,7 +55,8 @@
|
||||
extern char **environ;
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
#if defined(__linux__) || \
|
||||
defined(__GNU__)
|
||||
# include <grp.h>
|
||||
#endif
|
||||
|
||||
@ -63,11 +64,7 @@ extern char **environ;
|
||||
# include "zos-base.h"
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) || \
|
||||
defined(__DragonFly__) || \
|
||||
defined(__FreeBSD__) || \
|
||||
defined(__NetBSD__) || \
|
||||
defined(__OpenBSD__)
|
||||
#ifdef UV_HAVE_KQUEUE
|
||||
#include <sys/event.h>
|
||||
#else
|
||||
#define UV_USE_SIGCHLD
|
||||
@ -191,8 +188,12 @@ void uv__wait_children(uv_loop_t* loop) {
|
||||
static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) {
|
||||
int mask;
|
||||
int fd;
|
||||
int ret;
|
||||
int size;
|
||||
int i;
|
||||
|
||||
mask = UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD | UV_INHERIT_STREAM;
|
||||
size = 64 * 1024;
|
||||
|
||||
switch (container->flags & mask) {
|
||||
case UV_IGNORE:
|
||||
@ -202,8 +203,17 @@ static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) {
|
||||
assert(container->data.stream != NULL);
|
||||
if (container->data.stream->type != UV_NAMED_PIPE)
|
||||
return UV_EINVAL;
|
||||
else
|
||||
return uv_socketpair(SOCK_STREAM, 0, fds, 0, 0);
|
||||
else {
|
||||
ret = uv_socketpair(SOCK_STREAM, 0, fds, 0, 0);
|
||||
|
||||
if (ret == 0)
|
||||
for (i = 0; i < 2; i++) {
|
||||
setsockopt(fds[i], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
|
||||
setsockopt(fds[i], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
case UV_INHERIT_FD:
|
||||
case UV_INHERIT_STREAM:
|
||||
|
||||
@ -195,7 +195,7 @@ static void uv__signal_handler(int signum) {
|
||||
|
||||
for (handle = uv__signal_first_handle(signum);
|
||||
handle != NULL && handle->signum == signum;
|
||||
handle = RB_NEXT(uv__signal_tree_s, &uv__signal_tree, handle)) {
|
||||
handle = RB_NEXT(uv__signal_tree_s, handle)) {
|
||||
int r;
|
||||
|
||||
msg.signum = signum;
|
||||
|
||||
@ -457,7 +457,7 @@ void uv__stream_destroy(uv_stream_t* stream) {
|
||||
assert(stream->flags & UV_HANDLE_CLOSED);
|
||||
|
||||
if (stream->connect_req) {
|
||||
uv__req_unregister(stream->loop, stream->connect_req);
|
||||
uv__req_unregister(stream->loop);
|
||||
stream->connect_req->cb(stream->connect_req, UV_ECANCELED);
|
||||
stream->connect_req = NULL;
|
||||
}
|
||||
@ -642,7 +642,7 @@ static void uv__drain(uv_stream_t* stream) {
|
||||
if ((stream->flags & UV_HANDLE_CLOSING) ||
|
||||
!(stream->flags & UV_HANDLE_SHUT)) {
|
||||
stream->shutdown_req = NULL;
|
||||
uv__req_unregister(stream->loop, req);
|
||||
uv__req_unregister(stream->loop);
|
||||
|
||||
err = 0;
|
||||
if (stream->flags & UV_HANDLE_CLOSING)
|
||||
@ -698,7 +698,8 @@ static int uv__write_req_update(uv_stream_t* stream,
|
||||
|
||||
do {
|
||||
len = n < buf->len ? n : buf->len;
|
||||
buf->base += len;
|
||||
if (buf->len != 0)
|
||||
buf->base += len;
|
||||
buf->len -= len;
|
||||
buf += (buf->len == 0); /* Advance to next buffer if this one is empty. */
|
||||
n -= len;
|
||||
@ -912,7 +913,7 @@ static void uv__write_callbacks(uv_stream_t* stream) {
|
||||
q = uv__queue_head(&pq);
|
||||
req = uv__queue_data(q, uv_write_t, queue);
|
||||
uv__queue_remove(q);
|
||||
uv__req_unregister(stream->loop, req);
|
||||
uv__req_unregister(stream->loop);
|
||||
|
||||
if (req->bufs != NULL) {
|
||||
stream->write_queue_size -= uv__write_req_size(req);
|
||||
@ -979,11 +980,13 @@ static int uv__stream_queue_fd(uv_stream_t* stream, int fd) {
|
||||
|
||||
static int uv__stream_recv_cmsg(uv_stream_t* stream, struct msghdr* msg) {
|
||||
struct cmsghdr* cmsg;
|
||||
char* p;
|
||||
char* pe;
|
||||
int fd;
|
||||
int err;
|
||||
size_t i;
|
||||
size_t count;
|
||||
|
||||
err = 0;
|
||||
for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
|
||||
if (cmsg->cmsg_type != SCM_RIGHTS) {
|
||||
fprintf(stderr, "ignoring non-SCM_RIGHTS ancillary data: %d\n",
|
||||
@ -996,24 +999,26 @@ static int uv__stream_recv_cmsg(uv_stream_t* stream, struct msghdr* msg) {
|
||||
assert(count % sizeof(fd) == 0);
|
||||
count /= sizeof(fd);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
memcpy(&fd, (char*) CMSG_DATA(cmsg) + i * sizeof(fd), sizeof(fd));
|
||||
/* Already has accepted fd, queue now */
|
||||
if (stream->accepted_fd != -1) {
|
||||
err = uv__stream_queue_fd(stream, fd);
|
||||
if (err != 0) {
|
||||
/* Close rest */
|
||||
for (; i < count; i++)
|
||||
uv__close(fd);
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
stream->accepted_fd = fd;
|
||||
p = (void*) CMSG_DATA(cmsg);
|
||||
pe = p + count * sizeof(fd);
|
||||
|
||||
while (p < pe) {
|
||||
memcpy(&fd, p, sizeof(fd));
|
||||
p += sizeof(fd);
|
||||
|
||||
if (err == 0) {
|
||||
if (stream->accepted_fd == -1)
|
||||
stream->accepted_fd = fd;
|
||||
else
|
||||
err = uv__stream_queue_fd(stream, fd);
|
||||
}
|
||||
|
||||
if (err != 0)
|
||||
uv__close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@ -1268,7 +1273,7 @@ static void uv__stream_connect(uv_stream_t* stream) {
|
||||
return;
|
||||
|
||||
stream->connect_req = NULL;
|
||||
uv__req_unregister(stream->loop, req);
|
||||
uv__req_unregister(stream->loop);
|
||||
|
||||
if (error < 0 || uv__queue_empty(&stream->write_queue)) {
|
||||
uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT);
|
||||
|
||||
@ -826,6 +826,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
uv_interface_address_t* address;
|
||||
struct ifaddrs* addrs;
|
||||
struct ifaddrs* ent;
|
||||
size_t namelen;
|
||||
char* name;
|
||||
|
||||
*count = 0;
|
||||
*addresses = NULL;
|
||||
@ -834,9 +836,11 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
return UV__ERR(errno);
|
||||
|
||||
/* Count the number of interfaces */
|
||||
namelen = 0;
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
if (uv__ifaddr_exclude(ent))
|
||||
continue;
|
||||
namelen += strlen(ent->ifa_name) + 1;
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
@ -845,19 +849,22 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*addresses = uv__malloc(*count * sizeof(**addresses));
|
||||
if (!(*addresses)) {
|
||||
*addresses = uv__calloc(1, *count * sizeof(**addresses) + namelen);
|
||||
if (*addresses == NULL) {
|
||||
freeifaddrs(addrs);
|
||||
return UV_ENOMEM;
|
||||
}
|
||||
|
||||
name = (char*) &(*addresses)[*count];
|
||||
address = *addresses;
|
||||
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
if (uv__ifaddr_exclude(ent))
|
||||
continue;
|
||||
|
||||
address->name = uv__strdup(ent->ifa_name);
|
||||
namelen = strlen(ent->ifa_name) + 1;
|
||||
address->name = memcpy(name, ent->ifa_name, namelen);
|
||||
name += namelen;
|
||||
|
||||
if (ent->ifa_addr->sa_family == AF_INET6) {
|
||||
address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
|
||||
@ -885,13 +892,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
#endif /* SUNOS_NO_IFADDRS */
|
||||
|
||||
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||
int count) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
uv__free(addresses[i].name);
|
||||
}
|
||||
|
||||
int count) {
|
||||
uv__free(addresses);
|
||||
}
|
||||
|
||||
|
||||
@ -167,6 +167,12 @@ int uv__tcp_bind(uv_tcp_t* tcp,
|
||||
if (setsockopt(tcp->io_watcher.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
|
||||
return UV__ERR(errno);
|
||||
|
||||
if (flags & UV_TCP_REUSEPORT) {
|
||||
err = uv__sock_reuseport(tcp->io_watcher.fd);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifndef __OpenBSD__
|
||||
#ifdef IPV6_V6ONLY
|
||||
if (addr->sa_family == AF_INET6) {
|
||||
@ -452,6 +458,14 @@ int uv__tcp_nodelay(int fd, int on) {
|
||||
}
|
||||
|
||||
|
||||
#if (defined(UV__SOLARIS_11_4) && !UV__SOLARIS_11_4) || \
|
||||
(defined(__DragonFly__) && __DragonFly_version < 500702)
|
||||
/* DragonFlyBSD <500702 and Solaris <11.4 require millisecond units
|
||||
* for TCP keepalive options. */
|
||||
#define UV_KEEPALIVE_FACTOR(x) (x *= 1000)
|
||||
#else
|
||||
#define UV_KEEPALIVE_FACTOR(x)
|
||||
#endif
|
||||
int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
|
||||
int idle;
|
||||
int intvl;
|
||||
@ -467,8 +481,8 @@ int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
|
||||
if (!on)
|
||||
return 0;
|
||||
|
||||
if (delay == 0)
|
||||
return -1;
|
||||
if (delay < 1)
|
||||
return UV_EINVAL;
|
||||
|
||||
#ifdef __sun
|
||||
/* The implementation of TCP keep-alive on Solaris/SmartOS is a bit unusual
|
||||
@ -501,49 +515,53 @@ int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
|
||||
if (idle > 10*24*60*60)
|
||||
idle = 10*24*60*60;
|
||||
|
||||
UV_KEEPALIVE_FACTOR(idle);
|
||||
|
||||
/* `TCP_KEEPIDLE`, `TCP_KEEPINTVL`, and `TCP_KEEPCNT` were not available on Solaris
|
||||
* until version 11.4, but let's take a chance here. */
|
||||
#if defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) && defined(TCP_KEEPCNT)
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle)))
|
||||
return UV__ERR(errno);
|
||||
|
||||
intvl = idle/3;
|
||||
intvl = 10; /* required at least 10 seconds */
|
||||
UV_KEEPALIVE_FACTOR(intvl);
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl)))
|
||||
return UV__ERR(errno);
|
||||
|
||||
cnt = 3;
|
||||
cnt = 1; /* 1 retry, ensure (TCP_KEEPINTVL * TCP_KEEPCNT) is 10 seconds */
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt)))
|
||||
return UV__ERR(errno);
|
||||
#else
|
||||
/* Fall back to the first implementation of tcp-alive mechanism for older Solaris,
|
||||
* simulate the tcp-alive mechanism on other platforms via `TCP_KEEPALIVE_THRESHOLD` + `TCP_KEEPALIVE_ABORT_THRESHOLD`.
|
||||
*/
|
||||
idle *= 1000; /* kernel expects milliseconds */
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD, &idle, sizeof(idle)))
|
||||
return UV__ERR(errno);
|
||||
|
||||
/* Note that the consequent probes will not be sent at equal intervals on Solaris,
|
||||
* but will be sent using the exponential backoff algorithm. */
|
||||
intvl = idle/3;
|
||||
cnt = 3;
|
||||
int time_to_abort = intvl * cnt;
|
||||
int time_to_abort = 10; /* 10 seconds */
|
||||
UV_KEEPALIVE_FACTOR(time_to_abort);
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD, &time_to_abort, sizeof(time_to_abort)))
|
||||
return UV__ERR(errno);
|
||||
#endif
|
||||
|
||||
#else /* !defined(__sun) */
|
||||
|
||||
idle = delay;
|
||||
UV_KEEPALIVE_FACTOR(idle);
|
||||
#ifdef TCP_KEEPIDLE
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay)))
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(idle)))
|
||||
return UV__ERR(errno);
|
||||
#elif defined(TCP_KEEPALIVE)
|
||||
/* Darwin/macOS uses TCP_KEEPALIVE in place of TCP_KEEPIDLE. */
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay)))
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &idle, sizeof(idle)))
|
||||
return UV__ERR(errno);
|
||||
#endif
|
||||
|
||||
#ifdef TCP_KEEPINTVL
|
||||
intvl = 1; /* 1 second; same as default on Win32 */
|
||||
intvl = 1; /* 1 second; same as default on Win32 */
|
||||
UV_KEEPALIVE_FACTOR(intvl);
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl)))
|
||||
return UV__ERR(errno);
|
||||
#endif
|
||||
@ -612,7 +630,7 @@ void uv__tcp_close(uv_tcp_t* handle) {
|
||||
int uv_socketpair(int type, int protocol, uv_os_sock_t fds[2], int flags0, int flags1) {
|
||||
uv_os_sock_t temp[2];
|
||||
int err;
|
||||
#if defined(__FreeBSD__) || defined(__linux__)
|
||||
#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC)
|
||||
int flags;
|
||||
|
||||
flags = type | SOCK_CLOEXEC;
|
||||
|
||||
@ -23,6 +23,9 @@
|
||||
#include "internal.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#ifdef __OpenBSD__
|
||||
#include <pthread_np.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
@ -126,6 +129,12 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
|
||||
return uv_thread_create_ex(tid, ¶ms, entry, arg);
|
||||
}
|
||||
|
||||
|
||||
int uv_thread_detach(uv_thread_t *tid) {
|
||||
return UV__ERR(pthread_detach(*tid));
|
||||
}
|
||||
|
||||
|
||||
int uv_thread_create_ex(uv_thread_t* tid,
|
||||
const uv_thread_options_t* params,
|
||||
void (*entry)(void *arg),
|
||||
@ -205,7 +214,7 @@ int uv_thread_setaffinity(uv_thread_t* tid,
|
||||
if (cpumask[i])
|
||||
CPU_SET(i, &cpuset);
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#if defined(__ANDROID__) || defined(__OHOS__)
|
||||
if (sched_setaffinity(pthread_gettid_np(*tid), sizeof(cpuset), &cpuset))
|
||||
r = errno;
|
||||
else
|
||||
@ -233,7 +242,7 @@ int uv_thread_getaffinity(uv_thread_t* tid,
|
||||
return UV_EINVAL;
|
||||
|
||||
CPU_ZERO(&cpuset);
|
||||
#if defined(__ANDROID__)
|
||||
#if defined(__ANDROID__) || defined(__OHOS__)
|
||||
if (sched_getaffinity(pthread_gettid_np(*tid), sizeof(cpuset), &cpuset))
|
||||
r = errno;
|
||||
else
|
||||
@ -291,6 +300,18 @@ int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) {
|
||||
return pthread_equal(*t1, *t2);
|
||||
}
|
||||
|
||||
int uv_thread_setname(const char* name) {
|
||||
if (name == NULL)
|
||||
return UV_EINVAL;
|
||||
return uv__thread_setname(name);
|
||||
}
|
||||
|
||||
int uv_thread_getname(uv_thread_t* tid, char* name, size_t size) {
|
||||
if (name == NULL || size == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
return uv__thread_getname(tid, name, size);
|
||||
}
|
||||
|
||||
int uv_mutex_init(uv_mutex_t* mutex) {
|
||||
#if defined(NDEBUG) || !defined(PTHREAD_MUTEX_ERRORCHECK)
|
||||
@ -875,3 +896,80 @@ void uv_key_set(uv_key_t* key, void* value) {
|
||||
if (pthread_setspecific(*key, value))
|
||||
abort();
|
||||
}
|
||||
|
||||
#if defined(_AIX) || defined(__MVS__) || defined(__PASE__)
|
||||
int uv__thread_setname(const char* name) {
|
||||
return UV_ENOSYS;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
int uv__thread_setname(const char* name) {
|
||||
char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
strncpy(namebuf, name, sizeof(namebuf) - 1);
|
||||
namebuf[sizeof(namebuf) - 1] = '\0';
|
||||
int err = pthread_setname_np(namebuf);
|
||||
if (err)
|
||||
return UV__ERR(errno);
|
||||
return 0;
|
||||
}
|
||||
#elif defined(__NetBSD__)
|
||||
int uv__thread_setname(const char* name) {
|
||||
char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
strncpy(namebuf, name, sizeof(namebuf) - 1);
|
||||
namebuf[sizeof(namebuf) - 1] = '\0';
|
||||
return UV__ERR(pthread_setname_np(pthread_self(), "%s", namebuf));
|
||||
}
|
||||
#elif defined(__OpenBSD__)
|
||||
int uv__thread_setname(const char* name) {
|
||||
char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
strncpy(namebuf, name, sizeof(namebuf) - 1);
|
||||
namebuf[sizeof(namebuf) - 1] = '\0';
|
||||
pthread_set_name_np(pthread_self(), namebuf);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int uv__thread_setname(const char* name) {
|
||||
char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
strncpy(namebuf, name, sizeof(namebuf) - 1);
|
||||
namebuf[sizeof(namebuf) - 1] = '\0';
|
||||
return UV__ERR(pthread_setname_np(pthread_self(), namebuf));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (defined(__ANDROID_API__) && __ANDROID_API__ < 26) || \
|
||||
defined(_AIX) || \
|
||||
defined(__MVS__) || \
|
||||
defined(__PASE__)
|
||||
int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) {
|
||||
return UV_ENOSYS;
|
||||
}
|
||||
#elif defined(__OpenBSD__)
|
||||
int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) {
|
||||
char thread_name[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
pthread_get_name_np(*tid, thread_name, sizeof(thread_name));
|
||||
strncpy(name, thread_name, size - 1);
|
||||
name[size - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) {
|
||||
char thread_name[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
if (pthread_getname_np(*tid, thread_name, sizeof(thread_name)) != 0)
|
||||
return UV__ERR(errno);
|
||||
|
||||
strncpy(name, thread_name, size - 1);
|
||||
name[size - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) {
|
||||
int r;
|
||||
char thread_name[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
r = pthread_getname_np(*tid, thread_name, sizeof(thread_name));
|
||||
if (r != 0)
|
||||
return UV__ERR(r);
|
||||
|
||||
strncpy(name, thread_name, size - 1);
|
||||
name[size - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -284,6 +284,11 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
|
||||
int fd;
|
||||
int rc;
|
||||
|
||||
if (uv__is_raw_tty_mode(mode)) {
|
||||
/* There is only a single raw TTY mode on UNIX. */
|
||||
mode = UV_TTY_MODE_RAW;
|
||||
}
|
||||
|
||||
if (tty->mode == (int) mode)
|
||||
return 0;
|
||||
|
||||
@ -324,6 +329,8 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
|
||||
case UV_TTY_MODE_IO:
|
||||
uv__tty_make_raw(&tmp);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
/* Apply changes after draining */
|
||||
@ -335,6 +342,37 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
|
||||
}
|
||||
|
||||
|
||||
void uv__tty_close(uv_tty_t* handle) {
|
||||
int expected;
|
||||
int fd;
|
||||
|
||||
fd = handle->io_watcher.fd;
|
||||
if (fd == -1)
|
||||
goto done;
|
||||
|
||||
/* This is used for uv_tty_reset_mode() */
|
||||
do
|
||||
expected = 0;
|
||||
while (!atomic_compare_exchange_strong(&termios_spinlock, &expected, 1));
|
||||
|
||||
if (fd == orig_termios_fd) {
|
||||
/* XXX(bnoordhuis) the tcsetattr is probably wrong when there are still
|
||||
* other uv_tty_t handles active that refer to the same tty/pty but it's
|
||||
* hard to recognize that particular situation without maintaining some
|
||||
* kind of process-global data structure, and that still won't work in a
|
||||
* multi-process setup.
|
||||
*/
|
||||
uv__tcsetattr(fd, TCSANOW, &orig_termios);
|
||||
orig_termios_fd = -1;
|
||||
}
|
||||
|
||||
atomic_store(&termios_spinlock, 0);
|
||||
|
||||
done:
|
||||
uv__stream_close((uv_stream_t*) handle);
|
||||
}
|
||||
|
||||
|
||||
int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) {
|
||||
struct winsize ws;
|
||||
int err;
|
||||
@ -452,7 +490,7 @@ int uv_tty_reset_mode(void) {
|
||||
saved_errno = errno;
|
||||
|
||||
if (atomic_exchange(&termios_spinlock, 1))
|
||||
return UV_EBUSY; /* In uv_tty_set_mode(). */
|
||||
return UV_EBUSY; /* In uv_tty_set_mode() or uv__tty_close(). */
|
||||
|
||||
err = 0;
|
||||
if (orig_termios_fd != -1)
|
||||
|
||||
421
src/unix/udp.c
421
src/unix/udp.c
@ -47,6 +47,10 @@ static void uv__udp_sendmsg(uv_udp_t* handle);
|
||||
static int uv__udp_maybe_deferred_bind(uv_udp_t* handle,
|
||||
int domain,
|
||||
unsigned int flags);
|
||||
static int uv__udp_sendmsg1(int fd,
|
||||
const uv_buf_t* bufs,
|
||||
unsigned int nbufs,
|
||||
const struct sockaddr* addr);
|
||||
|
||||
|
||||
void uv__udp_close(uv_udp_t* handle) {
|
||||
@ -100,7 +104,7 @@ static void uv__udp_run_completed(uv_udp_t* handle) {
|
||||
uv__queue_remove(q);
|
||||
|
||||
req = uv__queue_data(q, uv_udp_send_t, queue);
|
||||
uv__req_unregister(handle->loop, req);
|
||||
uv__req_unregister(handle->loop);
|
||||
|
||||
handle->send_queue_size -= uv__count_bufs(req->bufs, req->nbufs);
|
||||
handle->send_queue_count--;
|
||||
@ -148,7 +152,7 @@ static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents) {
|
||||
}
|
||||
|
||||
static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) {
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
|
||||
struct sockaddr_in6 peers[20];
|
||||
struct iovec iov[ARRAY_SIZE(peers)];
|
||||
struct mmsghdr msgs[ARRAY_SIZE(peers)];
|
||||
@ -173,11 +177,18 @@ static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) {
|
||||
msgs[k].msg_hdr.msg_control = NULL;
|
||||
msgs[k].msg_hdr.msg_controllen = 0;
|
||||
msgs[k].msg_hdr.msg_flags = 0;
|
||||
msgs[k].msg_len = 0;
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
do
|
||||
nread = recvmsg_x(handle->io_watcher.fd, msgs, chunks, MSG_DONTWAIT);
|
||||
while (nread == -1 && errno == EINTR);
|
||||
#else
|
||||
do
|
||||
nread = recvmmsg(handle->io_watcher.fd, msgs, chunks, 0, NULL);
|
||||
while (nread == -1 && errno == EINTR);
|
||||
#endif
|
||||
|
||||
if (nread < 1) {
|
||||
if (nread == 0 || errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
@ -204,9 +215,9 @@ static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) {
|
||||
handle->recv_cb(handle, 0, buf, NULL, UV_UDP_MMSG_FREE);
|
||||
}
|
||||
return nread;
|
||||
#else /* __linux__ || ____FreeBSD__ */
|
||||
#else /* __linux__ || ____FreeBSD__ || __APPLE__ */
|
||||
return UV_ENOSYS;
|
||||
#endif /* __linux__ || ____FreeBSD__ */
|
||||
#endif /* __linux__ || ____FreeBSD__ || __APPLE__ */
|
||||
}
|
||||
|
||||
static void uv__udp_recvmsg(uv_udp_t* handle) {
|
||||
@ -275,164 +286,22 @@ static void uv__udp_recvmsg(uv_udp_t* handle) {
|
||||
&& handle->recv_cb != NULL);
|
||||
}
|
||||
|
||||
static void uv__udp_sendmsg(uv_udp_t* handle) {
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
uv_udp_send_t* req;
|
||||
struct mmsghdr h[20];
|
||||
struct mmsghdr* p;
|
||||
struct uv__queue* q;
|
||||
ssize_t npkts;
|
||||
size_t pkts;
|
||||
size_t i;
|
||||
|
||||
if (uv__queue_empty(&handle->write_queue))
|
||||
return;
|
||||
|
||||
write_queue_drain:
|
||||
for (pkts = 0, q = uv__queue_head(&handle->write_queue);
|
||||
pkts < ARRAY_SIZE(h) && q != &handle->write_queue;
|
||||
++pkts, q = uv__queue_head(q)) {
|
||||
assert(q != NULL);
|
||||
req = uv__queue_data(q, uv_udp_send_t, queue);
|
||||
assert(req != NULL);
|
||||
|
||||
p = &h[pkts];
|
||||
memset(p, 0, sizeof(*p));
|
||||
if (req->addr.ss_family == AF_UNSPEC) {
|
||||
p->msg_hdr.msg_name = NULL;
|
||||
p->msg_hdr.msg_namelen = 0;
|
||||
} else {
|
||||
p->msg_hdr.msg_name = &req->addr;
|
||||
if (req->addr.ss_family == AF_INET6)
|
||||
p->msg_hdr.msg_namelen = sizeof(struct sockaddr_in6);
|
||||
else if (req->addr.ss_family == AF_INET)
|
||||
p->msg_hdr.msg_namelen = sizeof(struct sockaddr_in);
|
||||
else if (req->addr.ss_family == AF_UNIX)
|
||||
p->msg_hdr.msg_namelen = sizeof(struct sockaddr_un);
|
||||
else {
|
||||
assert(0 && "unsupported address family");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
h[pkts].msg_hdr.msg_iov = (struct iovec*) req->bufs;
|
||||
h[pkts].msg_hdr.msg_iovlen = req->nbufs;
|
||||
}
|
||||
|
||||
do
|
||||
npkts = sendmmsg(handle->io_watcher.fd, h, pkts, 0);
|
||||
while (npkts == -1 && errno == EINTR);
|
||||
|
||||
if (npkts < 1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
|
||||
return;
|
||||
for (i = 0, q = uv__queue_head(&handle->write_queue);
|
||||
i < pkts && q != &handle->write_queue;
|
||||
++i, q = uv__queue_head(&handle->write_queue)) {
|
||||
assert(q != NULL);
|
||||
req = uv__queue_data(q, uv_udp_send_t, queue);
|
||||
assert(req != NULL);
|
||||
|
||||
req->status = UV__ERR(errno);
|
||||
uv__queue_remove(&req->queue);
|
||||
uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
|
||||
}
|
||||
uv__io_feed(handle->loop, &handle->io_watcher);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Safety: npkts known to be >0 below. Hence cast from ssize_t
|
||||
* to size_t safe.
|
||||
*/
|
||||
for (i = 0, q = uv__queue_head(&handle->write_queue);
|
||||
i < (size_t)npkts && q != &handle->write_queue;
|
||||
++i, q = uv__queue_head(&handle->write_queue)) {
|
||||
assert(q != NULL);
|
||||
req = uv__queue_data(q, uv_udp_send_t, queue);
|
||||
assert(req != NULL);
|
||||
|
||||
req->status = req->bufs[0].len;
|
||||
|
||||
/* Sending a datagram is an atomic operation: either all data
|
||||
* is written or nothing is (and EMSGSIZE is raised). That is
|
||||
* why we don't handle partial writes. Just pop the request
|
||||
* off the write queue and onto the completed queue, done.
|
||||
*/
|
||||
uv__queue_remove(&req->queue);
|
||||
uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
|
||||
}
|
||||
|
||||
/* couldn't batch everything, continue sending (jump to avoid stack growth) */
|
||||
if (!uv__queue_empty(&handle->write_queue))
|
||||
goto write_queue_drain;
|
||||
uv__io_feed(handle->loop, &handle->io_watcher);
|
||||
#else /* __linux__ || ____FreeBSD__ */
|
||||
uv_udp_send_t* req;
|
||||
struct msghdr h;
|
||||
struct uv__queue* q;
|
||||
ssize_t size;
|
||||
|
||||
while (!uv__queue_empty(&handle->write_queue)) {
|
||||
q = uv__queue_head(&handle->write_queue);
|
||||
assert(q != NULL);
|
||||
|
||||
req = uv__queue_data(q, uv_udp_send_t, queue);
|
||||
assert(req != NULL);
|
||||
|
||||
memset(&h, 0, sizeof h);
|
||||
if (req->addr.ss_family == AF_UNSPEC) {
|
||||
h.msg_name = NULL;
|
||||
h.msg_namelen = 0;
|
||||
} else {
|
||||
h.msg_name = &req->addr;
|
||||
if (req->addr.ss_family == AF_INET6)
|
||||
h.msg_namelen = sizeof(struct sockaddr_in6);
|
||||
else if (req->addr.ss_family == AF_INET)
|
||||
h.msg_namelen = sizeof(struct sockaddr_in);
|
||||
else if (req->addr.ss_family == AF_UNIX)
|
||||
h.msg_namelen = sizeof(struct sockaddr_un);
|
||||
else {
|
||||
assert(0 && "unsupported address family");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
h.msg_iov = (struct iovec*) req->bufs;
|
||||
h.msg_iovlen = req->nbufs;
|
||||
|
||||
do {
|
||||
size = sendmsg(handle->io_watcher.fd, &h, 0);
|
||||
} while (size == -1 && errno == EINTR);
|
||||
|
||||
if (size == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
|
||||
break;
|
||||
}
|
||||
|
||||
req->status = (size == -1 ? UV__ERR(errno) : size);
|
||||
|
||||
/* Sending a datagram is an atomic operation: either all data
|
||||
* is written or nothing is (and EMSGSIZE is raised). That is
|
||||
* why we don't handle partial writes. Just pop the request
|
||||
* off the write queue and onto the completed queue, done.
|
||||
*/
|
||||
uv__queue_remove(&req->queue);
|
||||
uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
|
||||
uv__io_feed(handle->loop, &handle->io_watcher);
|
||||
}
|
||||
#endif /* __linux__ || ____FreeBSD__ */
|
||||
}
|
||||
|
||||
/* On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but with some additional
|
||||
* refinements for programs that use multicast.
|
||||
* refinements for programs that use multicast. Therefore we preferentially
|
||||
* set SO_REUSEPORT over SO_REUSEADDR here, but we set SO_REUSEPORT only
|
||||
* when that socket option doesn't have the capability of load balancing.
|
||||
* Otherwise, we fall back to SO_REUSEADDR.
|
||||
*
|
||||
* Linux as of 3.9 has a SO_REUSEPORT socket option but with semantics that
|
||||
* are different from the BSDs: it _shares_ the port rather than steal it
|
||||
* from the current listener. While useful, it's not something we can emulate
|
||||
* on other platforms so we don't enable it.
|
||||
* Linux as of 3.9, DragonflyBSD 3.6, AIX 7.2.5 have the SO_REUSEPORT socket
|
||||
* option but with semantics that are different from the BSDs: it _shares_
|
||||
* the port rather than steals it from the current listener. While useful,
|
||||
* it's not something we can emulate on other platforms so we don't enable it.
|
||||
*
|
||||
* zOS does not support getsockname with SO_REUSEPORT option when using
|
||||
* AF_UNIX.
|
||||
*/
|
||||
static int uv__set_reuse(int fd) {
|
||||
static int uv__sock_reuseaddr(int fd) {
|
||||
int yes;
|
||||
yes = 1;
|
||||
|
||||
@ -449,7 +318,7 @@ static int uv__set_reuse(int fd) {
|
||||
return UV__ERR(errno);
|
||||
}
|
||||
#elif defined(SO_REUSEPORT) && !defined(__linux__) && !defined(__GNU__) && \
|
||||
!defined(__sun__)
|
||||
!defined(__sun__) && !defined(__DragonFly__) && !defined(_AIX73)
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
|
||||
return UV__ERR(errno);
|
||||
#else
|
||||
@ -492,7 +361,8 @@ int uv__udp_bind(uv_udp_t* handle,
|
||||
int fd;
|
||||
|
||||
/* Check for bad flags. */
|
||||
if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR | UV_UDP_LINUX_RECVERR))
|
||||
if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR |
|
||||
UV_UDP_REUSEPORT | UV_UDP_LINUX_RECVERR))
|
||||
return UV_EINVAL;
|
||||
|
||||
/* Cannot set IPv6-only mode on non-IPv6 socket. */
|
||||
@ -515,7 +385,13 @@ int uv__udp_bind(uv_udp_t* handle,
|
||||
}
|
||||
|
||||
if (flags & UV_UDP_REUSEADDR) {
|
||||
err = uv__set_reuse(fd);
|
||||
err = uv__sock_reuseaddr(fd);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (flags & UV_UDP_REUSEPORT) {
|
||||
err = uv__sock_reuseport(fd);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
@ -708,11 +584,11 @@ int uv__udp_send(uv_udp_send_t* req,
|
||||
empty_queue = (handle->send_queue_count == 0);
|
||||
|
||||
uv__req_init(handle->loop, req, UV_UDP_SEND);
|
||||
assert(addrlen <= sizeof(req->addr));
|
||||
assert(addrlen <= sizeof(req->u.storage));
|
||||
if (addr == NULL)
|
||||
req->addr.ss_family = AF_UNSPEC;
|
||||
req->u.storage.ss_family = AF_UNSPEC;
|
||||
else
|
||||
memcpy(&req->addr, addr, addrlen);
|
||||
memcpy(&req->u.storage, addr, addrlen);
|
||||
req->send_cb = send_cb;
|
||||
req->handle = handle;
|
||||
req->nbufs = nbufs;
|
||||
@ -722,7 +598,7 @@ int uv__udp_send(uv_udp_send_t* req,
|
||||
req->bufs = uv__malloc(nbufs * sizeof(bufs[0]));
|
||||
|
||||
if (req->bufs == NULL) {
|
||||
uv__req_unregister(handle->loop, req);
|
||||
uv__req_unregister(handle->loop);
|
||||
return UV_ENOMEM;
|
||||
}
|
||||
|
||||
@ -755,10 +631,9 @@ int uv__udp_try_send(uv_udp_t* handle,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen) {
|
||||
int err;
|
||||
struct msghdr h;
|
||||
ssize_t size;
|
||||
|
||||
assert(nbufs > 0);
|
||||
if (nbufs < 1)
|
||||
return UV_EINVAL;
|
||||
|
||||
/* already sending a message */
|
||||
if (handle->send_queue_count != 0)
|
||||
@ -772,24 +647,11 @@ int uv__udp_try_send(uv_udp_t* handle,
|
||||
assert(handle->flags & UV_HANDLE_UDP_CONNECTED);
|
||||
}
|
||||
|
||||
memset(&h, 0, sizeof h);
|
||||
h.msg_name = (struct sockaddr*) addr;
|
||||
h.msg_namelen = addrlen;
|
||||
h.msg_iov = (struct iovec*) bufs;
|
||||
h.msg_iovlen = nbufs;
|
||||
err = uv__udp_sendmsg1(handle->io_watcher.fd, bufs, nbufs, addr);
|
||||
if (err > 0)
|
||||
return uv__count_bufs(bufs, nbufs);
|
||||
|
||||
do {
|
||||
size = sendmsg(handle->io_watcher.fd, &h, 0);
|
||||
} while (size == -1 && errno == EINTR);
|
||||
|
||||
if (size == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
|
||||
return UV_EAGAIN;
|
||||
else
|
||||
return UV__ERR(errno);
|
||||
}
|
||||
|
||||
return size;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@ -1015,7 +877,7 @@ int uv__udp_init_ex(uv_loop_t* loop,
|
||||
|
||||
|
||||
int uv_udp_using_recvmmsg(const uv_udp_t* handle) {
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
|
||||
if (handle->flags & UV_HANDLE_UDP_RECVMMSG)
|
||||
return 1;
|
||||
#endif
|
||||
@ -1037,7 +899,7 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = uv__set_reuse(sock);
|
||||
err = uv__sock_reuseaddr(sock);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -1366,3 +1228,194 @@ int uv__udp_recv_stop(uv_udp_t* handle) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int uv__udp_prep_pkt(struct msghdr* h,
|
||||
const uv_buf_t* bufs,
|
||||
const unsigned int nbufs,
|
||||
const struct sockaddr* addr) {
|
||||
memset(h, 0, sizeof(*h));
|
||||
h->msg_name = (void*) addr;
|
||||
h->msg_iov = (void*) bufs;
|
||||
h->msg_iovlen = nbufs;
|
||||
if (addr == NULL)
|
||||
return 0;
|
||||
switch (addr->sa_family) {
|
||||
case AF_INET:
|
||||
h->msg_namelen = sizeof(struct sockaddr_in);
|
||||
return 0;
|
||||
case AF_INET6:
|
||||
h->msg_namelen = sizeof(struct sockaddr_in6);
|
||||
return 0;
|
||||
case AF_UNIX:
|
||||
h->msg_namelen = sizeof(struct sockaddr_un);
|
||||
return 0;
|
||||
case AF_UNSPEC:
|
||||
h->msg_name = NULL;
|
||||
return 0;
|
||||
}
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
|
||||
static int uv__udp_sendmsg1(int fd,
|
||||
const uv_buf_t* bufs,
|
||||
unsigned int nbufs,
|
||||
const struct sockaddr* addr) {
|
||||
struct msghdr h;
|
||||
int r;
|
||||
|
||||
if ((r = uv__udp_prep_pkt(&h, bufs, nbufs, addr)))
|
||||
return r;
|
||||
|
||||
do
|
||||
r = sendmsg(fd, &h, 0);
|
||||
while (r == -1 && errno == EINTR);
|
||||
|
||||
if (r < 0) {
|
||||
r = UV__ERR(errno);
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
|
||||
r = UV_EAGAIN;
|
||||
return r;
|
||||
}
|
||||
|
||||
/* UDP sockets don't EOF so we don't have to handle r=0 specially,
|
||||
* that only happens when the input was a zero-sized buffer.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int uv__udp_sendmsgv(int fd,
|
||||
unsigned int count,
|
||||
uv_buf_t* bufs[/*count*/],
|
||||
unsigned int nbufs[/*count*/],
|
||||
struct sockaddr* addrs[/*count*/]) {
|
||||
unsigned int i;
|
||||
int nsent;
|
||||
int r;
|
||||
|
||||
r = 0;
|
||||
nsent = 0;
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || \
|
||||
(defined(__sun__) && defined(MSG_WAITFORONE))
|
||||
if (count > 1) {
|
||||
for (i = 0; i < count; /*empty*/) {
|
||||
struct mmsghdr m[20];
|
||||
unsigned int n;
|
||||
|
||||
for (n = 0; i < count && n < ARRAY_SIZE(m); i++, n++)
|
||||
if ((r = uv__udp_prep_pkt(&m[n].msg_hdr, bufs[i], nbufs[i], addrs[i])))
|
||||
goto exit;
|
||||
|
||||
do
|
||||
#if defined(__APPLE__)
|
||||
r = sendmsg_x(fd, m, n, MSG_DONTWAIT);
|
||||
#else
|
||||
r = sendmmsg(fd, m, n, 0);
|
||||
#endif
|
||||
while (r == -1 && errno == EINTR);
|
||||
|
||||
if (r < 1)
|
||||
goto exit;
|
||||
|
||||
nsent += r;
|
||||
i += r;
|
||||
}
|
||||
|
||||
goto exit;
|
||||
}
|
||||
#endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) ||
|
||||
* (defined(__sun__) && defined(MSG_WAITFORONE))
|
||||
*/
|
||||
|
||||
for (i = 0; i < count; i++, nsent++)
|
||||
if ((r = uv__udp_sendmsg1(fd, bufs[i], nbufs[i], addrs[i])))
|
||||
goto exit; /* goto to avoid unused label warning. */
|
||||
|
||||
exit:
|
||||
|
||||
if (nsent > 0)
|
||||
return nsent;
|
||||
|
||||
if (r < 0) {
|
||||
r = UV__ERR(errno);
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
|
||||
r = UV_EAGAIN;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static void uv__udp_sendmsg(uv_udp_t* handle) {
|
||||
static const int N = 20;
|
||||
struct sockaddr* addrs[N];
|
||||
unsigned int nbufs[N];
|
||||
uv_buf_t* bufs[N];
|
||||
struct uv__queue* q;
|
||||
uv_udp_send_t* req;
|
||||
int n;
|
||||
|
||||
if (uv__queue_empty(&handle->write_queue))
|
||||
return;
|
||||
|
||||
again:
|
||||
n = 0;
|
||||
q = uv__queue_head(&handle->write_queue);
|
||||
do {
|
||||
req = uv__queue_data(q, uv_udp_send_t, queue);
|
||||
addrs[n] = &req->u.addr;
|
||||
nbufs[n] = req->nbufs;
|
||||
bufs[n] = req->bufs;
|
||||
q = uv__queue_next(q);
|
||||
n++;
|
||||
} while (n < N && q != &handle->write_queue);
|
||||
|
||||
n = uv__udp_sendmsgv(handle->io_watcher.fd, n, bufs, nbufs, addrs);
|
||||
while (n > 0) {
|
||||
q = uv__queue_head(&handle->write_queue);
|
||||
req = uv__queue_data(q, uv_udp_send_t, queue);
|
||||
req->status = uv__count_bufs(req->bufs, req->nbufs);
|
||||
uv__queue_remove(&req->queue);
|
||||
uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
|
||||
n--;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
if (uv__queue_empty(&handle->write_queue))
|
||||
goto feed;
|
||||
goto again;
|
||||
}
|
||||
|
||||
if (n == UV_EAGAIN)
|
||||
return;
|
||||
|
||||
/* Register the error against first request in queue because that
|
||||
* is the request that uv__udp_sendmsgv tried but failed to send,
|
||||
* because if it did send any requests, it won't return an error.
|
||||
*/
|
||||
q = uv__queue_head(&handle->write_queue);
|
||||
req = uv__queue_data(q, uv_udp_send_t, queue);
|
||||
req->status = n;
|
||||
uv__queue_remove(&req->queue);
|
||||
uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
|
||||
feed:
|
||||
uv__io_feed(handle->loop, &handle->io_watcher);
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_try_send2(uv_udp_t* handle,
|
||||
unsigned int count,
|
||||
uv_buf_t* bufs[/*count*/],
|
||||
unsigned int nbufs[/*count*/],
|
||||
struct sockaddr* addrs[/*count*/]) {
|
||||
int fd;
|
||||
|
||||
fd = handle->io_watcher.fd;
|
||||
if (fd == -1)
|
||||
return UV_EINVAL;
|
||||
|
||||
return uv__udp_sendmsgv(fd, count, bufs, nbufs, addrs);
|
||||
}
|
||||
|
||||
@ -514,6 +514,25 @@ int uv_udp_try_send(uv_udp_t* handle,
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_try_send2(uv_udp_t* handle,
|
||||
unsigned int count,
|
||||
uv_buf_t* bufs[/*count*/],
|
||||
unsigned int nbufs[/*count*/],
|
||||
struct sockaddr* addrs[/*count*/],
|
||||
unsigned int flags) {
|
||||
if (count < 1)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (flags != 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (handle->send_queue_count > 0)
|
||||
return UV_EAGAIN;
|
||||
|
||||
return uv__udp_try_send2(handle, count, bufs, nbufs, addrs);
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_recv_start(uv_udp_t* handle,
|
||||
uv_alloc_cb alloc_cb,
|
||||
uv_udp_recv_cb recv_cb) {
|
||||
@ -644,6 +663,9 @@ int uv_send_buffer_size(uv_handle_t* handle, int *value) {
|
||||
int uv_fs_event_getpath(uv_fs_event_t* handle, char* buffer, size_t* size) {
|
||||
size_t required_len;
|
||||
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (!uv__is_active(handle)) {
|
||||
*size = 0;
|
||||
return UV_EINVAL;
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "uv/tree.h"
|
||||
@ -125,7 +126,7 @@ enum {
|
||||
|
||||
/* Only used by uv_tty_t handles. */
|
||||
UV_HANDLE_TTY_READABLE = 0x01000000,
|
||||
UV_HANDLE_TTY_RAW = 0x02000000,
|
||||
UV_HANDLE_UNUSED0 = 0x02000000,
|
||||
UV_HANDLE_TTY_SAVED_POSITION = 0x04000000,
|
||||
UV_HANDLE_TTY_SAVED_ATTRIBUTES = 0x08000000,
|
||||
|
||||
@ -140,6 +141,10 @@ enum {
|
||||
UV_HANDLE_REAP = 0x10000000
|
||||
};
|
||||
|
||||
static inline int uv__is_raw_tty_mode(uv_tty_mode_t m) {
|
||||
return m == UV_TTY_MODE_RAW || m == UV_TTY_MODE_RAW_VT;
|
||||
}
|
||||
|
||||
int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap);
|
||||
|
||||
void uv__loop_close(uv_loop_t* loop);
|
||||
@ -191,6 +196,12 @@ int uv__udp_try_send(uv_udp_t* handle,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen);
|
||||
|
||||
int uv__udp_try_send2(uv_udp_t* handle,
|
||||
unsigned int count,
|
||||
uv_buf_t* bufs[/*count*/],
|
||||
unsigned int nbufs[/*count*/],
|
||||
struct sockaddr* addrs[/*count*/]);
|
||||
|
||||
int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloccb,
|
||||
uv_udp_recv_cb recv_cb);
|
||||
|
||||
@ -233,13 +244,13 @@ void uv__threadpool_cleanup(void);
|
||||
#define uv__has_active_reqs(loop) \
|
||||
((loop)->active_reqs.count > 0)
|
||||
|
||||
#define uv__req_register(loop, req) \
|
||||
#define uv__req_register(loop) \
|
||||
do { \
|
||||
(loop)->active_reqs.count++; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define uv__req_unregister(loop, req) \
|
||||
#define uv__req_unregister(loop) \
|
||||
do { \
|
||||
assert(uv__has_active_reqs(loop)); \
|
||||
(loop)->active_reqs.count--; \
|
||||
@ -349,7 +360,7 @@ void uv__threadpool_cleanup(void);
|
||||
#define uv__req_init(loop, req, typ) \
|
||||
do { \
|
||||
UV_REQ_INIT(req, typ); \
|
||||
uv__req_register(loop, req); \
|
||||
uv__req_register(loop); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
@ -400,7 +411,6 @@ void uv__metrics_set_provider_entry_time(uv_loop_t* loop);
|
||||
struct uv__iou {
|
||||
uint32_t* sqhead;
|
||||
uint32_t* sqtail;
|
||||
uint32_t* sqarray;
|
||||
uint32_t sqmask;
|
||||
uint32_t* sqflags;
|
||||
uint32_t* cqhead;
|
||||
@ -415,7 +425,6 @@ struct uv__iou {
|
||||
size_t sqelen;
|
||||
int ringfd;
|
||||
uint32_t in_flight;
|
||||
uint32_t flags;
|
||||
};
|
||||
#endif /* __linux__ */
|
||||
|
||||
@ -430,4 +439,36 @@ struct uv__loop_internal_fields_s {
|
||||
#endif /* __linux__ */
|
||||
};
|
||||
|
||||
#if defined(_WIN32)
|
||||
# define UV_PTHREAD_MAX_NAMELEN_NP 32767
|
||||
#elif defined(__APPLE__)
|
||||
# define UV_PTHREAD_MAX_NAMELEN_NP 64
|
||||
#elif defined(__NetBSD__) || defined(__illumos__)
|
||||
# define UV_PTHREAD_MAX_NAMELEN_NP PTHREAD_MAX_NAMELEN_NP
|
||||
#elif defined (__linux__)
|
||||
# define UV_PTHREAD_MAX_NAMELEN_NP 16
|
||||
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
||||
# define UV_PTHREAD_MAX_NAMELEN_NP (MAXCOMLEN + 1)
|
||||
#else
|
||||
# define UV_PTHREAD_MAX_NAMELEN_NP 16
|
||||
#endif
|
||||
|
||||
/* Open-coded so downstream users don't have to link libm. */
|
||||
static inline int uv__isinf(double d) {
|
||||
uint64_t v;
|
||||
|
||||
STATIC_ASSERT(sizeof(v) == sizeof(d));
|
||||
memcpy(&v, &d, sizeof(v));
|
||||
return (v << 1 >> 53) == 2047 && !(v << 12);
|
||||
}
|
||||
|
||||
/* Open-coded so downstream users don't have to link libm. */
|
||||
static inline int uv__isnan(double d) {
|
||||
uint64_t v;
|
||||
|
||||
STATIC_ASSERT(sizeof(v) == sizeof(d));
|
||||
memcpy(&v, &d, sizeof(v));
|
||||
return (v << 1 >> 53) == 2047 && !!(v << 12);
|
||||
}
|
||||
|
||||
#endif /* UV_COMMON_H_ */
|
||||
|
||||
110
src/win/core.c
110
src/win/core.c
@ -423,97 +423,6 @@ int uv_backend_timeout(const uv_loop_t* loop) {
|
||||
}
|
||||
|
||||
|
||||
static void uv__poll_wine(uv_loop_t* loop, DWORD timeout) {
|
||||
uv__loop_internal_fields_t* lfields;
|
||||
DWORD bytes;
|
||||
ULONG_PTR key;
|
||||
OVERLAPPED* overlapped;
|
||||
uv_req_t* req;
|
||||
int repeat;
|
||||
uint64_t timeout_time;
|
||||
uint64_t user_timeout;
|
||||
int reset_timeout;
|
||||
|
||||
lfields = uv__get_internal_fields(loop);
|
||||
timeout_time = loop->time + timeout;
|
||||
|
||||
if (lfields->flags & UV_METRICS_IDLE_TIME) {
|
||||
reset_timeout = 1;
|
||||
user_timeout = timeout;
|
||||
timeout = 0;
|
||||
} else {
|
||||
reset_timeout = 0;
|
||||
}
|
||||
|
||||
for (repeat = 0; ; repeat++) {
|
||||
/* Only need to set the provider_entry_time if timeout != 0. The function
|
||||
* will return early if the loop isn't configured with UV_METRICS_IDLE_TIME.
|
||||
*/
|
||||
if (timeout != 0)
|
||||
uv__metrics_set_provider_entry_time(loop);
|
||||
|
||||
/* Store the current timeout in a location that's globally accessible so
|
||||
* other locations like uv__work_done() can determine whether the queue
|
||||
* of events in the callback were waiting when poll was called.
|
||||
*/
|
||||
lfields->current_timeout = timeout;
|
||||
|
||||
GetQueuedCompletionStatus(loop->iocp,
|
||||
&bytes,
|
||||
&key,
|
||||
&overlapped,
|
||||
timeout);
|
||||
|
||||
if (reset_timeout != 0) {
|
||||
if (overlapped && timeout == 0)
|
||||
uv__metrics_inc_events_waiting(loop, 1);
|
||||
timeout = user_timeout;
|
||||
reset_timeout = 0;
|
||||
}
|
||||
|
||||
/* Placed here because on success the loop will break whether there is an
|
||||
* empty package or not, or if GetQueuedCompletionStatus returned early then
|
||||
* the timeout will be updated and the loop will run again. In either case
|
||||
* the idle time will need to be updated.
|
||||
*/
|
||||
uv__metrics_update_idle_time(loop);
|
||||
|
||||
if (overlapped) {
|
||||
uv__metrics_inc_events(loop, 1);
|
||||
|
||||
/* Package was dequeued */
|
||||
req = uv__overlapped_to_req(overlapped);
|
||||
uv__insert_pending_req(loop, req);
|
||||
|
||||
/* Some time might have passed waiting for I/O,
|
||||
* so update the loop time here.
|
||||
*/
|
||||
uv_update_time(loop);
|
||||
} else if (GetLastError() != WAIT_TIMEOUT) {
|
||||
/* Serious error */
|
||||
uv_fatal_error(GetLastError(), "GetQueuedCompletionStatus");
|
||||
} else if (timeout > 0) {
|
||||
/* GetQueuedCompletionStatus can occasionally return a little early.
|
||||
* Make sure that the desired timeout target time is reached.
|
||||
*/
|
||||
uv_update_time(loop);
|
||||
if (timeout_time > loop->time) {
|
||||
timeout = (DWORD)(timeout_time - loop->time);
|
||||
/* The first call to GetQueuedCompletionStatus should return very
|
||||
* close to the target time and the second should reach it, but
|
||||
* this is not stated in the documentation. To make sure a busy
|
||||
* loop cannot happen, the timeout is increased exponentially
|
||||
* starting on the third round.
|
||||
*/
|
||||
timeout += repeat ? (1 << (repeat - 1)) : 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void uv__poll(uv_loop_t* loop, DWORD timeout) {
|
||||
uv__loop_internal_fields_t* lfields;
|
||||
BOOL success;
|
||||
@ -553,12 +462,12 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) {
|
||||
*/
|
||||
lfields->current_timeout = timeout;
|
||||
|
||||
success = pGetQueuedCompletionStatusEx(loop->iocp,
|
||||
overlappeds,
|
||||
ARRAY_SIZE(overlappeds),
|
||||
&count,
|
||||
timeout,
|
||||
FALSE);
|
||||
success = GetQueuedCompletionStatusEx(loop->iocp,
|
||||
overlappeds,
|
||||
ARRAY_SIZE(overlappeds),
|
||||
&count,
|
||||
timeout,
|
||||
FALSE);
|
||||
|
||||
if (reset_timeout != 0) {
|
||||
timeout = user_timeout;
|
||||
@ -566,7 +475,7 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) {
|
||||
}
|
||||
|
||||
/* Placed here because on success the loop will break whether there is an
|
||||
* empty package or not, or if pGetQueuedCompletionStatusEx returned early
|
||||
* empty package or not, or if GetQueuedCompletionStatusEx returned early
|
||||
* then the timeout will be updated and the loop will run again. In either
|
||||
* case the idle time will need to be updated.
|
||||
*/
|
||||
@ -647,10 +556,7 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) {
|
||||
|
||||
uv__metrics_inc_loop_count(loop);
|
||||
|
||||
if (pGetQueuedCompletionStatusEx)
|
||||
uv__poll(loop, timeout);
|
||||
else
|
||||
uv__poll_wine(loop, timeout);
|
||||
uv__poll(loop, timeout);
|
||||
|
||||
/* Process immediate callbacks (e.g. write_cb) a small fixed number of
|
||||
* times to avoid loop starvation.*/
|
||||
|
||||
@ -69,7 +69,6 @@ int uv_translate_sys_error(int sys_errno) {
|
||||
}
|
||||
|
||||
switch (sys_errno) {
|
||||
case ERROR_NOACCESS: return UV_EACCES;
|
||||
case WSAEACCES: return UV_EACCES;
|
||||
case ERROR_ELEVATION_REQUIRED: return UV_EACCES;
|
||||
case ERROR_CANT_ACCESS_FILE: return UV_EACCES;
|
||||
@ -78,6 +77,7 @@ int uv_translate_sys_error(int sys_errno) {
|
||||
case WSAEADDRNOTAVAIL: return UV_EADDRNOTAVAIL;
|
||||
case WSAEAFNOSUPPORT: return UV_EAFNOSUPPORT;
|
||||
case WSAEWOULDBLOCK: return UV_EAGAIN;
|
||||
case ERROR_NO_DATA: return UV_EAGAIN;
|
||||
case WSAEALREADY: return UV_EALREADY;
|
||||
case ERROR_INVALID_FLAGS: return UV_EBADF;
|
||||
case ERROR_INVALID_HANDLE: return UV_EBADF;
|
||||
@ -95,7 +95,7 @@ int uv_translate_sys_error(int sys_errno) {
|
||||
case WSAECONNRESET: return UV_ECONNRESET;
|
||||
case ERROR_ALREADY_EXISTS: return UV_EEXIST;
|
||||
case ERROR_FILE_EXISTS: return UV_EEXIST;
|
||||
case ERROR_BUFFER_OVERFLOW: return UV_EFAULT;
|
||||
case ERROR_NOACCESS: return UV_EFAULT;
|
||||
case WSAEFAULT: return UV_EFAULT;
|
||||
case ERROR_HOST_UNREACHABLE: return UV_EHOSTUNREACH;
|
||||
case WSAEHOSTUNREACH: return UV_EHOSTUNREACH;
|
||||
@ -126,6 +126,7 @@ int uv_translate_sys_error(int sys_errno) {
|
||||
case ERROR_TOO_MANY_OPEN_FILES: return UV_EMFILE;
|
||||
case WSAEMFILE: return UV_EMFILE;
|
||||
case WSAEMSGSIZE: return UV_EMSGSIZE;
|
||||
case ERROR_BUFFER_OVERFLOW: return UV_ENAMETOOLONG;
|
||||
case ERROR_FILENAME_EXCED_RANGE: return UV_ENAMETOOLONG;
|
||||
case ERROR_NETWORK_UNREACHABLE: return UV_ENETUNREACH;
|
||||
case WSAENETUNREACH: return UV_ENETUNREACH;
|
||||
@ -157,7 +158,6 @@ int uv_translate_sys_error(int sys_errno) {
|
||||
case ERROR_ACCESS_DENIED: return UV_EPERM;
|
||||
case ERROR_PRIVILEGE_NOT_HELD: return UV_EPERM;
|
||||
case ERROR_BAD_PIPE: return UV_EPIPE;
|
||||
case ERROR_NO_DATA: return UV_EPIPE;
|
||||
case ERROR_PIPE_NOT_CONNECTED: return UV_EPIPE;
|
||||
case WSAESHUTDOWN: return UV_EPIPE;
|
||||
case WSAEPROTONOSUPPORT: return UV_EPROTONOSUPPORT;
|
||||
@ -168,6 +168,16 @@ int uv_translate_sys_error(int sys_errno) {
|
||||
case ERROR_INVALID_FUNCTION: return UV_EISDIR;
|
||||
case ERROR_META_EXPANSION_TOO_LONG: return UV_E2BIG;
|
||||
case WSAESOCKTNOSUPPORT: return UV_ESOCKTNOSUPPORT;
|
||||
case ERROR_BAD_EXE_FORMAT: return UV_EFTYPE;
|
||||
default: return UV_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
int uv_translate_write_sys_error(int sys_errno) {
|
||||
switch (sys_errno) {
|
||||
case ERROR_BROKEN_PIPE: return UV_EPIPE;
|
||||
case ERROR_NO_DATA: return UV_EPIPE;
|
||||
default:
|
||||
return uv_translate_sys_error(sys_errno);
|
||||
}
|
||||
}
|
||||
|
||||
@ -253,6 +253,8 @@ short_path_done:
|
||||
}
|
||||
|
||||
dir_to_watch = dir;
|
||||
uv__free(short_path);
|
||||
short_path = NULL;
|
||||
uv__free(pathw);
|
||||
pathw = NULL;
|
||||
}
|
||||
@ -561,7 +563,27 @@ void uv__process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
|
||||
}
|
||||
} else {
|
||||
err = GET_REQ_ERROR(req);
|
||||
handle->cb(handle, NULL, 0, uv_translate_sys_error(err));
|
||||
/*
|
||||
* Check whether the ERROR_ACCESS_DENIED is caused by the watched directory
|
||||
* being actually deleted (not an actual error) or a legit error. Retrieve
|
||||
* FileStandardInfo to check whether the directory is pending deletion.
|
||||
*/
|
||||
FILE_STANDARD_INFO info;
|
||||
if (err == ERROR_ACCESS_DENIED &&
|
||||
handle->dirw != NULL &&
|
||||
GetFileInformationByHandleEx(handle->dir_handle,
|
||||
FileStandardInfo,
|
||||
&info,
|
||||
sizeof(info)) &&
|
||||
info.Directory &&
|
||||
info.DeletePending) {
|
||||
uv__convert_utf16_to_utf8(handle->dirw, -1, &filename);
|
||||
handle->cb(handle, filename, UV_RENAME, 0);
|
||||
uv__free(filename);
|
||||
filename = NULL;
|
||||
} else {
|
||||
handle->cb(handle, NULL, 0, uv_translate_sys_error(err));
|
||||
}
|
||||
}
|
||||
|
||||
if (handle->flags & UV_HANDLE_CLOSING) {
|
||||
|
||||
544
src/win/fs.c
544
src/win/fs.c
@ -46,6 +46,30 @@
|
||||
#define UV_FS_FREE_PTR 0x0008
|
||||
#define UV_FS_CLEANEDUP 0x0010
|
||||
|
||||
#ifndef FILE_DISPOSITION_DELETE
|
||||
#define FILE_DISPOSITION_DELETE 0x0001
|
||||
#endif /* FILE_DISPOSITION_DELETE */
|
||||
|
||||
#ifndef FILE_DISPOSITION_POSIX_SEMANTICS
|
||||
#define FILE_DISPOSITION_POSIX_SEMANTICS 0x0002
|
||||
#endif /* FILE_DISPOSITION_POSIX_SEMANTICS */
|
||||
|
||||
#ifndef FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE
|
||||
#define FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE 0x0010
|
||||
#endif /* FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE */
|
||||
|
||||
NTSTATUS uv__RtlUnicodeStringInit(
|
||||
PUNICODE_STRING DestinationString,
|
||||
PWSTR SourceString,
|
||||
size_t SourceStringLen
|
||||
) {
|
||||
if (SourceStringLen > 0x7FFF)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
DestinationString->MaximumLength = DestinationString->Length =
|
||||
SourceStringLen * sizeof(SourceString[0]);
|
||||
DestinationString->Buffer = SourceString;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
#define INIT(subtype) \
|
||||
do { \
|
||||
@ -58,7 +82,7 @@
|
||||
#define POST \
|
||||
do { \
|
||||
if (cb != NULL) { \
|
||||
uv__req_register(loop, req); \
|
||||
uv__req_register(loop); \
|
||||
uv__work_submit(loop, \
|
||||
&req->work_req, \
|
||||
UV__WORK_FAST_IO, \
|
||||
@ -97,13 +121,14 @@
|
||||
return; \
|
||||
}
|
||||
|
||||
#define MILLION ((int64_t) 1000 * 1000)
|
||||
#define BILLION ((int64_t) 1000 * 1000 * 1000)
|
||||
#define NSEC_PER_TICK 100
|
||||
#define TICKS_PER_SEC ((int64_t) 1e9 / NSEC_PER_TICK)
|
||||
static const int64_t WIN_TO_UNIX_TICK_OFFSET = 11644473600 * TICKS_PER_SEC;
|
||||
|
||||
static void uv__filetime_to_timespec(uv_timespec_t *ts, int64_t filetime) {
|
||||
filetime -= 116444736 * BILLION;
|
||||
ts->tv_sec = (long) (filetime / (10 * MILLION));
|
||||
ts->tv_nsec = (long) ((filetime - ts->tv_sec * 10 * MILLION) * 100U);
|
||||
filetime -= WIN_TO_UNIX_TICK_OFFSET;
|
||||
ts->tv_sec = filetime / TICKS_PER_SEC;
|
||||
ts->tv_nsec = (filetime % TICKS_PER_SEC) * NSEC_PER_TICK;
|
||||
if (ts->tv_nsec < 0) {
|
||||
ts->tv_sec -= 1;
|
||||
ts->tv_nsec += 1e9;
|
||||
@ -112,7 +137,7 @@ static void uv__filetime_to_timespec(uv_timespec_t *ts, int64_t filetime) {
|
||||
|
||||
#define TIME_T_TO_FILETIME(time, filetime_ptr) \
|
||||
do { \
|
||||
int64_t bigtime = ((time) * 10 * MILLION + 116444736 * BILLION); \
|
||||
int64_t bigtime = ((time) * TICKS_PER_SEC + WIN_TO_UNIX_TICK_OFFSET); \
|
||||
(filetime_ptr)->dwLowDateTime = (uint64_t) bigtime & 0xFFFFFFFF; \
|
||||
(filetime_ptr)->dwHighDateTime = (uint64_t) bigtime >> 32; \
|
||||
} while(0)
|
||||
@ -136,6 +161,16 @@ static int uv__file_symlink_usermode_flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGE
|
||||
|
||||
static DWORD uv__allocation_granularity;
|
||||
|
||||
typedef enum {
|
||||
FS__STAT_PATH_SUCCESS,
|
||||
FS__STAT_PATH_ERROR,
|
||||
FS__STAT_PATH_TRY_SLOW
|
||||
} fs__stat_path_return_t;
|
||||
|
||||
INLINE static void fs__stat_assign_statbuf_null(uv_stat_t* statbuf);
|
||||
INLINE static void fs__stat_assign_statbuf(uv_stat_t* statbuf,
|
||||
FILE_STAT_BASIC_INFORMATION stat_info, int do_lstat);
|
||||
|
||||
|
||||
void uv__fs_init(void) {
|
||||
SYSTEM_INFO system_info;
|
||||
@ -1056,27 +1091,20 @@ void fs__write(uv_fs_t* req) {
|
||||
error = ERROR_INVALID_FLAGS;
|
||||
}
|
||||
|
||||
SET_REQ_WIN32_ERROR(req, error);
|
||||
SET_REQ_UV_ERROR(req, uv_translate_write_sys_error(error), error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fs__rmdir(uv_fs_t* req) {
|
||||
int result = _wrmdir(req->file.pathw);
|
||||
if (result == -1)
|
||||
SET_REQ_WIN32_ERROR(req, _doserrno);
|
||||
else
|
||||
SET_REQ_RESULT(req, 0);
|
||||
}
|
||||
|
||||
|
||||
void fs__unlink(uv_fs_t* req) {
|
||||
static void fs__unlink_rmdir(uv_fs_t* req, BOOL isrmdir) {
|
||||
const WCHAR* pathw = req->file.pathw;
|
||||
HANDLE handle;
|
||||
BY_HANDLE_FILE_INFORMATION info;
|
||||
FILE_DISPOSITION_INFORMATION disposition;
|
||||
FILE_DISPOSITION_INFORMATION_EX disposition_ex;
|
||||
IO_STATUS_BLOCK iosb;
|
||||
NTSTATUS status;
|
||||
DWORD error;
|
||||
|
||||
handle = CreateFileW(pathw,
|
||||
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | DELETE,
|
||||
@ -1097,10 +1125,18 @@ void fs__unlink(uv_fs_t* req) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
/* Do not allow deletion of directories, unless it is a symlink. When the
|
||||
* path refers to a non-symlink directory, report EPERM as mandated by
|
||||
* POSIX.1. */
|
||||
if (isrmdir && !(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
/* Error if we're in rmdir mode but it is not a dir.
|
||||
* TODO: change it to UV_NOTDIR in v2. */
|
||||
SET_REQ_UV_ERROR(req, UV_ENOENT, ERROR_DIRECTORY);
|
||||
CloseHandle(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isrmdir && (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
/* If not explicitly allowed, do not allow deletion of directories, unless
|
||||
* it is a symlink. When the path refers to a non-symlink directory, report
|
||||
* EPERM as mandated by POSIX.1. */
|
||||
|
||||
/* Check if it is a reparse point. If it's not, it's a normal directory. */
|
||||
if (!(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
|
||||
@ -1112,7 +1148,7 @@ void fs__unlink(uv_fs_t* req) {
|
||||
/* Read the reparse point and check if it is a valid symlink. If not, don't
|
||||
* unlink. */
|
||||
if (fs__readlink_handle(handle, NULL, NULL) < 0) {
|
||||
DWORD error = GetLastError();
|
||||
error = GetLastError();
|
||||
if (error == ERROR_SYMLINK_NOT_SUPPORTED)
|
||||
error = ERROR_ACCESS_DENIED;
|
||||
SET_REQ_WIN32_ERROR(req, error);
|
||||
@ -1121,42 +1157,77 @@ void fs__unlink(uv_fs_t* req) {
|
||||
}
|
||||
}
|
||||
|
||||
if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
|
||||
/* Remove read-only attribute */
|
||||
FILE_BASIC_INFORMATION basic = { 0 };
|
||||
/* Try posix delete first */
|
||||
disposition_ex.Flags = FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS |
|
||||
FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE;
|
||||
|
||||
basic.FileAttributes = (info.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY) |
|
||||
FILE_ATTRIBUTE_ARCHIVE;
|
||||
|
||||
status = pNtSetInformationFile(handle,
|
||||
&iosb,
|
||||
&basic,
|
||||
sizeof basic,
|
||||
FileBasicInformation);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
|
||||
CloseHandle(handle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to set the delete flag. */
|
||||
disposition.DeleteFile = TRUE;
|
||||
status = pNtSetInformationFile(handle,
|
||||
&iosb,
|
||||
&disposition,
|
||||
sizeof disposition,
|
||||
FileDispositionInformation);
|
||||
&disposition_ex,
|
||||
sizeof disposition_ex,
|
||||
FileDispositionInformationEx);
|
||||
if (NT_SUCCESS(status)) {
|
||||
SET_REQ_SUCCESS(req);
|
||||
} else {
|
||||
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
|
||||
/* If status == STATUS_CANNOT_DELETE here, given we set
|
||||
* FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE, STATUS_CANNOT_DELETE can only mean
|
||||
* that there is an existing mapped view to the file, preventing delete.
|
||||
* STATUS_CANNOT_DELETE maps to UV_EACCES so it's not specifically worth handling */
|
||||
error = pRtlNtStatusToDosError(status);
|
||||
if (error == ERROR_NOT_SUPPORTED /* filesystem does not support posix deletion */ ||
|
||||
error == ERROR_INVALID_PARAMETER /* pre Windows 10 error */ ||
|
||||
error == ERROR_INVALID_FUNCTION /* pre Windows 10 1607 error */) {
|
||||
/* posix delete not supported so try fallback */
|
||||
if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
|
||||
/* Remove read-only attribute */
|
||||
FILE_BASIC_INFORMATION basic = { 0 };
|
||||
|
||||
basic.FileAttributes = (info.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY) |
|
||||
FILE_ATTRIBUTE_ARCHIVE;
|
||||
|
||||
status = pNtSetInformationFile(handle,
|
||||
&iosb,
|
||||
&basic,
|
||||
sizeof basic,
|
||||
FileBasicInformation);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
|
||||
CloseHandle(handle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to set the delete flag. */
|
||||
disposition.DeleteFile = TRUE;
|
||||
status = pNtSetInformationFile(handle,
|
||||
&iosb,
|
||||
&disposition,
|
||||
sizeof disposition,
|
||||
FileDispositionInformation);
|
||||
if (NT_SUCCESS(status)) {
|
||||
SET_REQ_SUCCESS(req);
|
||||
} else {
|
||||
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
|
||||
}
|
||||
} else {
|
||||
SET_REQ_WIN32_ERROR(req, error);
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(handle);
|
||||
}
|
||||
|
||||
|
||||
static void fs__rmdir(uv_fs_t* req) {
|
||||
fs__unlink_rmdir(req, /*isrmdir*/1);
|
||||
}
|
||||
|
||||
|
||||
static void fs__unlink(uv_fs_t* req) {
|
||||
fs__unlink_rmdir(req, /*isrmdir*/0);
|
||||
}
|
||||
|
||||
|
||||
void fs__mkdir(uv_fs_t* req) {
|
||||
/* TODO: use req->mode. */
|
||||
if (CreateDirectoryW(req->file.pathw, NULL)) {
|
||||
@ -1182,7 +1253,7 @@ void fs__mktemp(uv_fs_t* req, uv__fs_mktemp_func func) {
|
||||
size_t len;
|
||||
uint64_t v;
|
||||
char* path;
|
||||
|
||||
|
||||
path = (char*)req->path;
|
||||
len = wcslen(req->file.pathw);
|
||||
ep = req->file.pathw + len;
|
||||
@ -1593,12 +1664,12 @@ void fs__readdir(uv_fs_t* req) {
|
||||
goto error;
|
||||
|
||||
/* Copy file type. */
|
||||
if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
|
||||
dent.d_type = UV__DT_DIR;
|
||||
if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0)
|
||||
dent.d_type = UV__DT_CHAR;
|
||||
else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0)
|
||||
dent.d_type = UV__DT_LINK;
|
||||
else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0)
|
||||
dent.d_type = UV__DT_CHAR;
|
||||
else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
|
||||
dent.d_type = UV__DT_DIR;
|
||||
else
|
||||
dent.d_type = UV__DT_FILE;
|
||||
|
||||
@ -1627,6 +1698,43 @@ void fs__closedir(uv_fs_t* req) {
|
||||
SET_REQ_RESULT(req, 0);
|
||||
}
|
||||
|
||||
INLINE static fs__stat_path_return_t fs__stat_path(WCHAR* path,
|
||||
uv_stat_t* statbuf, int do_lstat) {
|
||||
FILE_STAT_BASIC_INFORMATION stat_info;
|
||||
|
||||
/* Check if the new fast API is available. */
|
||||
if (!pGetFileInformationByName) {
|
||||
return FS__STAT_PATH_TRY_SLOW;
|
||||
}
|
||||
|
||||
/* Check if the API call fails. */
|
||||
if (!pGetFileInformationByName(path, FileStatBasicByNameInfo, &stat_info,
|
||||
sizeof(stat_info))) {
|
||||
switch(GetLastError()) {
|
||||
case ERROR_FILE_NOT_FOUND:
|
||||
case ERROR_PATH_NOT_FOUND:
|
||||
case ERROR_NOT_READY:
|
||||
case ERROR_BAD_NET_NAME:
|
||||
/* These errors aren't worth retrying with the slow path. */
|
||||
return FS__STAT_PATH_ERROR;
|
||||
}
|
||||
return FS__STAT_PATH_TRY_SLOW;
|
||||
}
|
||||
|
||||
/* A file handle is needed to get st_size for links. */
|
||||
if ((stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
|
||||
return FS__STAT_PATH_TRY_SLOW;
|
||||
}
|
||||
|
||||
if (stat_info.DeviceType == FILE_DEVICE_NULL) {
|
||||
fs__stat_assign_statbuf_null(statbuf);
|
||||
return FS__STAT_PATH_SUCCESS;
|
||||
}
|
||||
|
||||
fs__stat_assign_statbuf(statbuf, stat_info, do_lstat);
|
||||
return FS__STAT_PATH_SUCCESS;
|
||||
}
|
||||
|
||||
INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
|
||||
int do_lstat) {
|
||||
size_t target_length = 0;
|
||||
@ -1635,6 +1743,7 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
|
||||
FILE_FS_VOLUME_INFORMATION volume_info;
|
||||
NTSTATUS nt_status;
|
||||
IO_STATUS_BLOCK io_status;
|
||||
FILE_STAT_BASIC_INFORMATION stat_info;
|
||||
|
||||
nt_status = pNtQueryVolumeInformationFile(handle,
|
||||
&io_status,
|
||||
@ -1650,13 +1759,7 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
|
||||
|
||||
/* If it's NUL device set fields as reasonable as possible and return. */
|
||||
if (device_info.DeviceType == FILE_DEVICE_NULL) {
|
||||
memset(statbuf, 0, sizeof(uv_stat_t));
|
||||
statbuf->st_mode = _S_IFCHR;
|
||||
statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
|
||||
((_S_IREAD | _S_IWRITE) >> 6);
|
||||
statbuf->st_nlink = 1;
|
||||
statbuf->st_blksize = 4096;
|
||||
statbuf->st_rdev = FILE_DEVICE_NULL << 16;
|
||||
fs__stat_assign_statbuf_null(statbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1680,14 +1783,64 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
|
||||
|
||||
/* Buffer overflow (a warning status code) is expected here. */
|
||||
if (io_status.Status == STATUS_NOT_IMPLEMENTED) {
|
||||
statbuf->st_dev = 0;
|
||||
stat_info.VolumeSerialNumber.QuadPart = 0;
|
||||
} else if (NT_ERROR(nt_status)) {
|
||||
SetLastError(pRtlNtStatusToDosError(nt_status));
|
||||
return -1;
|
||||
} else {
|
||||
statbuf->st_dev = volume_info.VolumeSerialNumber;
|
||||
stat_info.VolumeSerialNumber.LowPart = volume_info.VolumeSerialNumber;
|
||||
}
|
||||
|
||||
stat_info.DeviceType = device_info.DeviceType;
|
||||
stat_info.FileAttributes = file_info.BasicInformation.FileAttributes;
|
||||
stat_info.NumberOfLinks = file_info.StandardInformation.NumberOfLinks;
|
||||
stat_info.FileId.QuadPart =
|
||||
file_info.InternalInformation.IndexNumber.QuadPart;
|
||||
stat_info.ChangeTime.QuadPart =
|
||||
file_info.BasicInformation.ChangeTime.QuadPart;
|
||||
stat_info.CreationTime.QuadPart =
|
||||
file_info.BasicInformation.CreationTime.QuadPart;
|
||||
stat_info.LastAccessTime.QuadPart =
|
||||
file_info.BasicInformation.LastAccessTime.QuadPart;
|
||||
stat_info.LastWriteTime.QuadPart =
|
||||
file_info.BasicInformation.LastWriteTime.QuadPart;
|
||||
stat_info.AllocationSize.QuadPart =
|
||||
file_info.StandardInformation.AllocationSize.QuadPart;
|
||||
|
||||
if (do_lstat &&
|
||||
(file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
|
||||
/*
|
||||
* If reading the link fails, the reparse point is not a symlink and needs
|
||||
* to be treated as a regular file. The higher level lstat function will
|
||||
* detect this failure and retry without do_lstat if appropriate.
|
||||
*/
|
||||
if (fs__readlink_handle(handle, NULL, &target_length) != 0) {
|
||||
return -1;
|
||||
}
|
||||
stat_info.EndOfFile.QuadPart = target_length;
|
||||
} else {
|
||||
stat_info.EndOfFile.QuadPart =
|
||||
file_info.StandardInformation.EndOfFile.QuadPart;
|
||||
}
|
||||
|
||||
fs__stat_assign_statbuf(statbuf, stat_info, do_lstat);
|
||||
return 0;
|
||||
}
|
||||
|
||||
INLINE static void fs__stat_assign_statbuf_null(uv_stat_t* statbuf) {
|
||||
memset(statbuf, 0, sizeof(uv_stat_t));
|
||||
statbuf->st_mode = _S_IFCHR;
|
||||
statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
|
||||
((_S_IREAD | _S_IWRITE) >> 6);
|
||||
statbuf->st_nlink = 1;
|
||||
statbuf->st_blksize = 4096;
|
||||
statbuf->st_rdev = FILE_DEVICE_NULL << 16;
|
||||
}
|
||||
|
||||
INLINE static void fs__stat_assign_statbuf(uv_stat_t* statbuf,
|
||||
FILE_STAT_BASIC_INFORMATION stat_info, int do_lstat) {
|
||||
statbuf->st_dev = stat_info.VolumeSerialNumber.LowPart;
|
||||
|
||||
/* Todo: st_mode should probably always be 0666 for everyone. We might also
|
||||
* want to report 0777 if the file is a .exe or a directory.
|
||||
*
|
||||
@ -1719,50 +1872,43 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
|
||||
* target. Otherwise, reparse points must be treated as regular files.
|
||||
*/
|
||||
if (do_lstat &&
|
||||
(file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
|
||||
/*
|
||||
* If reading the link fails, the reparse point is not a symlink and needs
|
||||
* to be treated as a regular file. The higher level lstat function will
|
||||
* detect this failure and retry without do_lstat if appropriate.
|
||||
*/
|
||||
if (fs__readlink_handle(handle, NULL, &target_length) != 0)
|
||||
return -1;
|
||||
(stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
|
||||
statbuf->st_mode |= S_IFLNK;
|
||||
statbuf->st_size = target_length;
|
||||
statbuf->st_size = stat_info.EndOfFile.QuadPart;
|
||||
}
|
||||
|
||||
if (statbuf->st_mode == 0) {
|
||||
if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
if (stat_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
statbuf->st_mode |= _S_IFDIR;
|
||||
statbuf->st_size = 0;
|
||||
} else {
|
||||
statbuf->st_mode |= _S_IFREG;
|
||||
statbuf->st_size = file_info.StandardInformation.EndOfFile.QuadPart;
|
||||
statbuf->st_size = stat_info.EndOfFile.QuadPart;
|
||||
}
|
||||
}
|
||||
|
||||
if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_READONLY)
|
||||
if (stat_info.FileAttributes & FILE_ATTRIBUTE_READONLY)
|
||||
statbuf->st_mode |= _S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6);
|
||||
else
|
||||
statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
|
||||
((_S_IREAD | _S_IWRITE) >> 6);
|
||||
|
||||
uv__filetime_to_timespec(&statbuf->st_atim,
|
||||
file_info.BasicInformation.LastAccessTime.QuadPart);
|
||||
stat_info.LastAccessTime.QuadPart);
|
||||
uv__filetime_to_timespec(&statbuf->st_ctim,
|
||||
file_info.BasicInformation.ChangeTime.QuadPart);
|
||||
stat_info.ChangeTime.QuadPart);
|
||||
uv__filetime_to_timespec(&statbuf->st_mtim,
|
||||
file_info.BasicInformation.LastWriteTime.QuadPart);
|
||||
stat_info.LastWriteTime.QuadPart);
|
||||
uv__filetime_to_timespec(&statbuf->st_birthtim,
|
||||
file_info.BasicInformation.CreationTime.QuadPart);
|
||||
stat_info.CreationTime.QuadPart);
|
||||
|
||||
statbuf->st_ino = file_info.InternalInformation.IndexNumber.QuadPart;
|
||||
statbuf->st_ino = stat_info.FileId.QuadPart;
|
||||
|
||||
/* st_blocks contains the on-disk allocation size in 512-byte units. */
|
||||
statbuf->st_blocks =
|
||||
(uint64_t) file_info.StandardInformation.AllocationSize.QuadPart >> 9;
|
||||
(uint64_t) stat_info.AllocationSize.QuadPart >> 9;
|
||||
|
||||
statbuf->st_nlink = file_info.StandardInformation.NumberOfLinks;
|
||||
statbuf->st_nlink = stat_info.NumberOfLinks;
|
||||
|
||||
/* The st_blksize is supposed to be the 'optimal' number of bytes for reading
|
||||
* and writing to the disk. That is, for any definition of 'optimal' - it's
|
||||
@ -1794,8 +1940,6 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
|
||||
statbuf->st_uid = 0;
|
||||
statbuf->st_rdev = 0;
|
||||
statbuf->st_gen = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -1809,6 +1953,179 @@ INLINE static void fs__stat_prepare_path(WCHAR* pathw) {
|
||||
}
|
||||
}
|
||||
|
||||
INLINE static DWORD fs__stat_directory(WCHAR* path, uv_stat_t* statbuf,
|
||||
int do_lstat, DWORD ret_error) {
|
||||
HANDLE handle = INVALID_HANDLE_VALUE;
|
||||
FILE_STAT_BASIC_INFORMATION stat_info;
|
||||
FILE_ID_FULL_DIR_INFORMATION dir_info;
|
||||
FILE_FS_VOLUME_INFORMATION volume_info;
|
||||
FILE_FS_DEVICE_INFORMATION device_info;
|
||||
IO_STATUS_BLOCK io_status;
|
||||
NTSTATUS nt_status;
|
||||
WCHAR* path_dirpath = NULL;
|
||||
WCHAR* path_filename = NULL;
|
||||
UNICODE_STRING FileMask;
|
||||
size_t len;
|
||||
size_t split;
|
||||
WCHAR splitchar;
|
||||
int includes_name;
|
||||
|
||||
/* AKA strtok or wcscspn, in reverse. */
|
||||
len = wcslen(path);
|
||||
split = len;
|
||||
|
||||
includes_name = 0;
|
||||
while (split > 0 && path[split - 1] != L'\\' && path[split - 1] != L'/' &&
|
||||
path[split - 1] != L':') {
|
||||
/* check if the path contains a character other than /,\,:,. */
|
||||
if (path[split-1] != '.') {
|
||||
includes_name = 1;
|
||||
}
|
||||
split--;
|
||||
}
|
||||
/* If the path is a relative path with a file name or a folder name */
|
||||
if (split == 0 && includes_name) {
|
||||
path_dirpath = L".";
|
||||
/* If there is a slash or a backslash */
|
||||
} else if (path[split - 1] == L'\\' || path[split - 1] == L'/') {
|
||||
path_dirpath = path;
|
||||
/* If there is no filename, consider it as a relative folder path */
|
||||
if (!includes_name) {
|
||||
split = len;
|
||||
/* Else, split it */
|
||||
} else {
|
||||
splitchar = path[split - 1];
|
||||
path[split - 1] = L'\0';
|
||||
}
|
||||
/* e.g. "..", "c:" */
|
||||
} else {
|
||||
path_dirpath = path;
|
||||
split = len;
|
||||
}
|
||||
path_filename = &path[split];
|
||||
|
||||
len = 0;
|
||||
while (1) {
|
||||
if (path_filename[len] == L'\0')
|
||||
break;
|
||||
if (path_filename[len] == L'*' || path_filename[len] == L'?' ||
|
||||
path_filename[len] == L'>' || path_filename[len] == L'<' ||
|
||||
path_filename[len] == L'"') {
|
||||
ret_error = ERROR_INVALID_NAME;
|
||||
goto cleanup;
|
||||
}
|
||||
len++;
|
||||
}
|
||||
|
||||
/* Get directory handle */
|
||||
handle = CreateFileW(path_dirpath,
|
||||
FILE_LIST_DIRECTORY,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
ret_error = GetLastError();
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Get files in the directory */
|
||||
nt_status = uv__RtlUnicodeStringInit(&FileMask, path_filename, len);
|
||||
if (!NT_SUCCESS(nt_status)) {
|
||||
ret_error = pRtlNtStatusToDosError(nt_status);
|
||||
goto cleanup;
|
||||
}
|
||||
nt_status = pNtQueryDirectoryFile(handle,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&io_status,
|
||||
&dir_info,
|
||||
sizeof(dir_info),
|
||||
FileIdFullDirectoryInformation,
|
||||
TRUE,
|
||||
&FileMask,
|
||||
TRUE);
|
||||
|
||||
/* Buffer overflow (a warning status code) is expected here since there isn't
|
||||
* enough space to store the FileName, and actually indicates success. */
|
||||
if (!NT_SUCCESS(nt_status) && nt_status != STATUS_BUFFER_OVERFLOW) {
|
||||
if (nt_status == STATUS_NO_MORE_FILES)
|
||||
ret_error = ERROR_PATH_NOT_FOUND;
|
||||
else
|
||||
ret_error = pRtlNtStatusToDosError(nt_status);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Assign values to stat_info */
|
||||
memset(&stat_info, 0, sizeof(FILE_STAT_BASIC_INFORMATION));
|
||||
stat_info.FileAttributes = dir_info.FileAttributes;
|
||||
stat_info.CreationTime.QuadPart = dir_info.CreationTime.QuadPart;
|
||||
stat_info.LastAccessTime.QuadPart = dir_info.LastAccessTime.QuadPart;
|
||||
stat_info.LastWriteTime.QuadPart = dir_info.LastWriteTime.QuadPart;
|
||||
if (stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
||||
/* A file handle is needed to get st_size for the link (from
|
||||
* FSCTL_GET_REPARSE_POINT), which is required by posix, but we are here
|
||||
* because getting the file handle failed. We could get just the
|
||||
* ReparsePointTag by querying FILE_ID_EXTD_DIR_INFORMATION instead to make
|
||||
* sure this really is a link before giving up here on the uv_fs_stat call,
|
||||
* but that doesn't seem essential. */
|
||||
if (!do_lstat)
|
||||
goto cleanup;
|
||||
stat_info.EndOfFile.QuadPart = 0;
|
||||
stat_info.AllocationSize.QuadPart = 0;
|
||||
} else {
|
||||
stat_info.EndOfFile.QuadPart = dir_info.EndOfFile.QuadPart;
|
||||
stat_info.AllocationSize.QuadPart = dir_info.AllocationSize.QuadPart;
|
||||
}
|
||||
stat_info.ChangeTime.QuadPart = dir_info.ChangeTime.QuadPart;
|
||||
stat_info.FileId.QuadPart = dir_info.FileId.QuadPart;
|
||||
|
||||
/* Finish up by getting device info from the directory handle,
|
||||
* since files presumably must live on their device. */
|
||||
nt_status = pNtQueryVolumeInformationFile(handle,
|
||||
&io_status,
|
||||
&volume_info,
|
||||
sizeof volume_info,
|
||||
FileFsVolumeInformation);
|
||||
|
||||
/* Buffer overflow (a warning status code) is expected here. */
|
||||
if (io_status.Status == STATUS_NOT_IMPLEMENTED) {
|
||||
stat_info.VolumeSerialNumber.QuadPart = 0;
|
||||
} else if (NT_ERROR(nt_status)) {
|
||||
ret_error = pRtlNtStatusToDosError(nt_status);
|
||||
goto cleanup;
|
||||
} else {
|
||||
stat_info.VolumeSerialNumber.QuadPart = volume_info.VolumeSerialNumber;
|
||||
}
|
||||
|
||||
nt_status = pNtQueryVolumeInformationFile(handle,
|
||||
&io_status,
|
||||
&device_info,
|
||||
sizeof device_info,
|
||||
FileFsDeviceInformation);
|
||||
|
||||
/* Buffer overflow (a warning status code) is expected here. */
|
||||
if (NT_ERROR(nt_status)) {
|
||||
ret_error = pRtlNtStatusToDosError(nt_status);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
stat_info.DeviceType = device_info.DeviceType;
|
||||
stat_info.NumberOfLinks = 1; /* No way to recover this info. */
|
||||
|
||||
fs__stat_assign_statbuf(statbuf, stat_info, do_lstat);
|
||||
ret_error = 0;
|
||||
|
||||
cleanup:
|
||||
if (split != 0)
|
||||
path[split - 1] = splitchar;
|
||||
if (handle != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(handle);
|
||||
return ret_error;
|
||||
}
|
||||
|
||||
INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
|
||||
int do_lstat,
|
||||
@ -1817,6 +2134,17 @@ INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
|
||||
DWORD flags;
|
||||
DWORD ret;
|
||||
|
||||
/* If new API exists, try to use it. */
|
||||
switch (fs__stat_path(path, statbuf, do_lstat)) {
|
||||
case FS__STAT_PATH_SUCCESS:
|
||||
return 0;
|
||||
case FS__STAT_PATH_ERROR:
|
||||
return GetLastError();
|
||||
case FS__STAT_PATH_TRY_SLOW:
|
||||
break;
|
||||
}
|
||||
|
||||
/* If the new API does not exist, use the old API. */
|
||||
flags = FILE_FLAG_BACKUP_SEMANTICS;
|
||||
if (do_lstat)
|
||||
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
|
||||
@ -1829,8 +2157,12 @@ INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
|
||||
flags,
|
||||
NULL);
|
||||
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
return GetLastError();
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
ret = GetLastError();
|
||||
if (ret != ERROR_ACCESS_DENIED && ret != ERROR_SHARING_VIOLATION)
|
||||
return ret;
|
||||
return fs__stat_directory(path, statbuf, do_lstat, ret);
|
||||
}
|
||||
|
||||
if (fs__stat_handle(handle, statbuf, do_lstat) != 0)
|
||||
ret = GetLastError();
|
||||
@ -2248,14 +2580,29 @@ fchmod_cleanup:
|
||||
|
||||
|
||||
INLINE static int fs__utime_handle(HANDLE handle, double atime, double mtime) {
|
||||
FILETIME filetime_a, filetime_m;
|
||||
FILETIME filetime_as, *filetime_a = &filetime_as;
|
||||
FILETIME filetime_ms, *filetime_m = &filetime_ms;
|
||||
FILETIME now;
|
||||
|
||||
TIME_T_TO_FILETIME(atime, &filetime_a);
|
||||
TIME_T_TO_FILETIME(mtime, &filetime_m);
|
||||
if (uv__isinf(atime) || uv__isinf(mtime))
|
||||
GetSystemTimeAsFileTime(&now);
|
||||
|
||||
if (!SetFileTime(handle, NULL, &filetime_a, &filetime_m)) {
|
||||
if (uv__isinf(atime))
|
||||
filetime_a = &now;
|
||||
else if (uv__isnan(atime))
|
||||
filetime_a = NULL;
|
||||
else
|
||||
TIME_T_TO_FILETIME(atime, filetime_a);
|
||||
|
||||
if (uv__isinf(mtime))
|
||||
filetime_m = &now;
|
||||
else if (uv__isnan(mtime))
|
||||
filetime_m = NULL;
|
||||
else
|
||||
TIME_T_TO_FILETIME(mtime, filetime_m);
|
||||
|
||||
if (!SetFileTime(handle, NULL, filetime_a, filetime_m))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2423,16 +2770,17 @@ static void fs__create_junction(uv_fs_t* req, const WCHAR* path,
|
||||
|
||||
path_buf[path_buf_len++] = path[i];
|
||||
}
|
||||
path_buf[path_buf_len++] = L'\\';
|
||||
if (add_slash)
|
||||
path_buf[path_buf_len++] = L'\\';
|
||||
len = path_buf_len - start;
|
||||
|
||||
/* Insert null terminator */
|
||||
path_buf[path_buf_len++] = L'\0';
|
||||
|
||||
/* Set the info about the substitute name */
|
||||
buffer->MountPointReparseBuffer.SubstituteNameOffset = start * sizeof(WCHAR);
|
||||
buffer->MountPointReparseBuffer.SubstituteNameLength = len * sizeof(WCHAR);
|
||||
|
||||
/* Insert null terminator */
|
||||
path_buf[path_buf_len++] = L'\0';
|
||||
|
||||
/* Copy the print name of the target path */
|
||||
start = path_buf_len;
|
||||
add_slash = 0;
|
||||
@ -2450,18 +2798,18 @@ static void fs__create_junction(uv_fs_t* req, const WCHAR* path,
|
||||
path_buf[path_buf_len++] = path[i];
|
||||
}
|
||||
len = path_buf_len - start;
|
||||
if (len == 2) {
|
||||
if (len == 2 || add_slash) {
|
||||
path_buf[path_buf_len++] = L'\\';
|
||||
len++;
|
||||
}
|
||||
|
||||
/* Insert another null terminator */
|
||||
path_buf[path_buf_len++] = L'\0';
|
||||
|
||||
/* Set the info about the print name */
|
||||
buffer->MountPointReparseBuffer.PrintNameOffset = start * sizeof(WCHAR);
|
||||
buffer->MountPointReparseBuffer.PrintNameLength = len * sizeof(WCHAR);
|
||||
|
||||
/* Insert another null terminator */
|
||||
path_buf[path_buf_len++] = L'\0';
|
||||
|
||||
/* Calculate how much buffer space was actually used */
|
||||
used_buf_size = FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) +
|
||||
path_buf_len * sizeof(WCHAR);
|
||||
@ -2830,7 +3178,7 @@ static void uv__fs_done(struct uv__work* w, int status) {
|
||||
uv_fs_t* req;
|
||||
|
||||
req = container_of(w, uv_fs_t, work_req);
|
||||
uv__req_unregister(req->loop, req);
|
||||
uv__req_unregister(req->loop);
|
||||
|
||||
if (status == UV_ECANCELED) {
|
||||
assert(req->result == 0);
|
||||
|
||||
@ -71,10 +71,9 @@ int uv__getaddrinfo_translate_error(int sys_err) {
|
||||
DECLSPEC_IMPORT void WSAAPI FreeAddrInfoW(PADDRINFOW pAddrInfo);
|
||||
#endif
|
||||
|
||||
|
||||
/* Adjust size value to be multiple of 4. Use to keep pointer aligned.
|
||||
* Do we need different versions of this for different architectures? */
|
||||
#define ALIGNED_SIZE(X) ((((X) + 3) >> 2) << 2)
|
||||
static size_t align_offset(size_t off, size_t alignment) {
|
||||
return ((off + alignment - 1) / alignment) * alignment;
|
||||
}
|
||||
|
||||
#ifndef NDIS_IF_MAX_STRING_SIZE
|
||||
#define NDIS_IF_MAX_STRING_SIZE IF_MAX_STRING_SIZE
|
||||
@ -103,17 +102,7 @@ static void uv__getaddrinfo_work(struct uv__work* w) {
|
||||
* Each size calculation is adjusted to avoid unaligned pointers.
|
||||
*/
|
||||
static void uv__getaddrinfo_done(struct uv__work* w, int status) {
|
||||
uv_getaddrinfo_t* req;
|
||||
size_t addrinfo_len = 0;
|
||||
ssize_t name_len = 0;
|
||||
size_t addrinfo_struct_len = ALIGNED_SIZE(sizeof(struct addrinfo));
|
||||
struct addrinfoW* addrinfow_ptr;
|
||||
struct addrinfo* addrinfo_ptr;
|
||||
char* alloc_ptr = NULL;
|
||||
char* cur_ptr = NULL;
|
||||
int r;
|
||||
|
||||
req = container_of(w, uv_getaddrinfo_t, work_req);
|
||||
uv_getaddrinfo_t* req = container_of(w, uv_getaddrinfo_t, work_req);
|
||||
|
||||
/* release input parameter memory */
|
||||
uv__free(req->alloc);
|
||||
@ -126,34 +115,44 @@ static void uv__getaddrinfo_done(struct uv__work* w, int status) {
|
||||
}
|
||||
|
||||
if (req->retcode == 0) {
|
||||
char* alloc_ptr = NULL;
|
||||
size_t cur_off = 0;
|
||||
size_t addrinfo_len;
|
||||
/* Convert addrinfoW to addrinfo. First calculate required length. */
|
||||
addrinfow_ptr = req->addrinfow;
|
||||
struct addrinfoW* addrinfow_ptr = req->addrinfow;
|
||||
while (addrinfow_ptr != NULL) {
|
||||
addrinfo_len += addrinfo_struct_len +
|
||||
ALIGNED_SIZE(addrinfow_ptr->ai_addrlen);
|
||||
cur_off = align_offset(cur_off, sizeof(void*));
|
||||
cur_off += sizeof(struct addrinfo);
|
||||
/* TODO: This alignment could be smaller, if we could
|
||||
portably get the alignment for sockaddr. */
|
||||
cur_off = align_offset(cur_off, sizeof(void*));
|
||||
cur_off += addrinfow_ptr->ai_addrlen;
|
||||
if (addrinfow_ptr->ai_canonname != NULL) {
|
||||
name_len = uv_utf16_length_as_wtf8(addrinfow_ptr->ai_canonname, -1);
|
||||
ssize_t name_len =
|
||||
uv_utf16_length_as_wtf8(addrinfow_ptr->ai_canonname, -1);
|
||||
if (name_len < 0) {
|
||||
req->retcode = name_len;
|
||||
goto complete;
|
||||
}
|
||||
addrinfo_len += ALIGNED_SIZE(name_len + 1);
|
||||
cur_off += name_len + 1;
|
||||
}
|
||||
addrinfow_ptr = addrinfow_ptr->ai_next;
|
||||
}
|
||||
|
||||
/* allocate memory for addrinfo results */
|
||||
alloc_ptr = (char*)uv__malloc(addrinfo_len);
|
||||
addrinfo_len = cur_off;
|
||||
alloc_ptr = uv__malloc(addrinfo_len);
|
||||
|
||||
/* do conversions */
|
||||
if (alloc_ptr != NULL) {
|
||||
cur_ptr = alloc_ptr;
|
||||
struct addrinfo *addrinfo_ptr = (struct addrinfo *)alloc_ptr;
|
||||
cur_off = 0;
|
||||
addrinfow_ptr = req->addrinfow;
|
||||
|
||||
while (addrinfow_ptr != NULL) {
|
||||
for (;;) {
|
||||
cur_off += sizeof(struct addrinfo);
|
||||
assert(cur_off <= addrinfo_len);
|
||||
/* copy addrinfo struct data */
|
||||
assert(cur_ptr + addrinfo_struct_len <= alloc_ptr + addrinfo_len);
|
||||
addrinfo_ptr = (struct addrinfo*)cur_ptr;
|
||||
addrinfo_ptr->ai_family = addrinfow_ptr->ai_family;
|
||||
addrinfo_ptr->ai_socktype = addrinfow_ptr->ai_socktype;
|
||||
addrinfo_ptr->ai_protocol = addrinfow_ptr->ai_protocol;
|
||||
@ -163,35 +162,38 @@ static void uv__getaddrinfo_done(struct uv__work* w, int status) {
|
||||
addrinfo_ptr->ai_addr = NULL;
|
||||
addrinfo_ptr->ai_next = NULL;
|
||||
|
||||
cur_ptr += addrinfo_struct_len;
|
||||
|
||||
/* copy sockaddr */
|
||||
if (addrinfo_ptr->ai_addrlen > 0) {
|
||||
assert(cur_ptr + addrinfo_ptr->ai_addrlen <=
|
||||
alloc_ptr + addrinfo_len);
|
||||
memcpy(cur_ptr, addrinfow_ptr->ai_addr, addrinfo_ptr->ai_addrlen);
|
||||
addrinfo_ptr->ai_addr = (struct sockaddr*)cur_ptr;
|
||||
cur_ptr += ALIGNED_SIZE(addrinfo_ptr->ai_addrlen);
|
||||
cur_off = align_offset(cur_off, sizeof(void *));
|
||||
addrinfo_ptr->ai_addr = (struct sockaddr *)(alloc_ptr + cur_off);
|
||||
cur_off += addrinfo_ptr->ai_addrlen;
|
||||
assert(cur_off <= addrinfo_len);
|
||||
memcpy(addrinfo_ptr->ai_addr,
|
||||
addrinfow_ptr->ai_addr,
|
||||
addrinfo_ptr->ai_addrlen);
|
||||
}
|
||||
|
||||
/* convert canonical name to UTF-8 */
|
||||
if (addrinfow_ptr->ai_canonname != NULL) {
|
||||
name_len = alloc_ptr + addrinfo_len - cur_ptr;
|
||||
r = uv__copy_utf16_to_utf8(addrinfow_ptr->ai_canonname,
|
||||
-1,
|
||||
cur_ptr,
|
||||
(size_t*)&name_len);
|
||||
ssize_t name_len = addrinfo_len - cur_off;
|
||||
addrinfo_ptr->ai_canonname = alloc_ptr + cur_off;
|
||||
int r = uv__copy_utf16_to_utf8(addrinfow_ptr->ai_canonname,
|
||||
-1,
|
||||
addrinfo_ptr->ai_canonname,
|
||||
(size_t*)&name_len);
|
||||
assert(r == 0);
|
||||
addrinfo_ptr->ai_canonname = cur_ptr;
|
||||
cur_ptr += ALIGNED_SIZE(name_len + 1);
|
||||
cur_off += name_len + 1;
|
||||
assert(cur_off <= addrinfo_len);
|
||||
}
|
||||
assert(cur_ptr <= alloc_ptr + addrinfo_len);
|
||||
|
||||
/* set next ptr */
|
||||
addrinfow_ptr = addrinfow_ptr->ai_next;
|
||||
if (addrinfow_ptr != NULL) {
|
||||
addrinfo_ptr->ai_next = (struct addrinfo*)cur_ptr;
|
||||
}
|
||||
if (addrinfow_ptr == NULL)
|
||||
break;
|
||||
cur_off = align_offset(cur_off, sizeof(void *));
|
||||
struct addrinfo *next_addrinfo_ptr = (struct addrinfo *)(alloc_ptr + cur_off);
|
||||
addrinfo_ptr->ai_next = next_addrinfo_ptr;
|
||||
addrinfo_ptr = next_addrinfo_ptr;
|
||||
}
|
||||
req->addrinfo = (struct addrinfo*)alloc_ptr;
|
||||
} else {
|
||||
@ -206,7 +208,7 @@ static void uv__getaddrinfo_done(struct uv__work* w, int status) {
|
||||
}
|
||||
|
||||
complete:
|
||||
uv__req_unregister(req->loop, req);
|
||||
uv__req_unregister(req->loop);
|
||||
|
||||
/* finally do callback with converted result */
|
||||
if (req->getaddrinfo_cb)
|
||||
@ -242,10 +244,12 @@ int uv_getaddrinfo(uv_loop_t* loop,
|
||||
const char* service,
|
||||
const struct addrinfo* hints) {
|
||||
char hostname_ascii[256];
|
||||
size_t off = 0;
|
||||
size_t nodesize = 0;
|
||||
size_t servicesize = 0;
|
||||
size_t serviceoff = 0;
|
||||
size_t hintssize = 0;
|
||||
char* alloc_ptr = NULL;
|
||||
size_t hintoff = 0;
|
||||
ssize_t rc;
|
||||
|
||||
if (req == NULL || (node == NULL && service == NULL)) {
|
||||
@ -268,6 +272,7 @@ int uv_getaddrinfo(uv_loop_t* loop,
|
||||
return rc;
|
||||
nodesize = strlen(hostname_ascii) + 1;
|
||||
node = hostname_ascii;
|
||||
off += nodesize * sizeof(WCHAR);
|
||||
}
|
||||
|
||||
if (service != NULL) {
|
||||
@ -275,27 +280,28 @@ int uv_getaddrinfo(uv_loop_t* loop,
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
servicesize = rc;
|
||||
off = align_offset(off, sizeof(WCHAR));
|
||||
serviceoff = off;
|
||||
off += servicesize * sizeof(WCHAR);
|
||||
}
|
||||
|
||||
if (hints != NULL) {
|
||||
hintssize = ALIGNED_SIZE(sizeof(struct addrinfoW));
|
||||
off = align_offset(off, sizeof(void *));
|
||||
hintoff = off;
|
||||
hintssize = sizeof(struct addrinfoW);
|
||||
off += hintssize;
|
||||
}
|
||||
|
||||
/* allocate memory for inputs, and partition it as needed */
|
||||
alloc_ptr = uv__malloc(ALIGNED_SIZE(nodesize * sizeof(WCHAR)) +
|
||||
ALIGNED_SIZE(servicesize * sizeof(WCHAR)) +
|
||||
hintssize);
|
||||
if (!alloc_ptr)
|
||||
req->alloc = uv__malloc(off);
|
||||
if (!req->alloc)
|
||||
return UV_ENOMEM;
|
||||
|
||||
/* save alloc_ptr now so we can free if error */
|
||||
req->alloc = (void*) alloc_ptr;
|
||||
|
||||
/* Convert node string to UTF16 into allocated memory and save pointer in the
|
||||
* request. The node here has been converted to ascii. */
|
||||
if (node != NULL) {
|
||||
req->node = (WCHAR*) alloc_ptr;
|
||||
uv_wtf8_to_utf16(node, (WCHAR*) alloc_ptr, nodesize);
|
||||
alloc_ptr += ALIGNED_SIZE(nodesize * sizeof(WCHAR));
|
||||
req->node = (WCHAR*) req->alloc;
|
||||
uv_wtf8_to_utf16(node, req->node, nodesize);
|
||||
} else {
|
||||
req->node = NULL;
|
||||
}
|
||||
@ -303,16 +309,15 @@ int uv_getaddrinfo(uv_loop_t* loop,
|
||||
/* Convert service string to UTF16 into allocated memory and save pointer in
|
||||
* the req. */
|
||||
if (service != NULL) {
|
||||
req->service = (WCHAR*) alloc_ptr;
|
||||
uv_wtf8_to_utf16(service, (WCHAR*) alloc_ptr, servicesize);
|
||||
alloc_ptr += ALIGNED_SIZE(servicesize * sizeof(WCHAR));
|
||||
req->service = (WCHAR*) ((char*) req->alloc + serviceoff);
|
||||
uv_wtf8_to_utf16(service, req->service, servicesize);
|
||||
} else {
|
||||
req->service = NULL;
|
||||
}
|
||||
|
||||
/* copy hints to allocated memory and save pointer in req */
|
||||
if (hints != NULL) {
|
||||
req->addrinfow = (struct addrinfoW*) alloc_ptr;
|
||||
req->addrinfow = (struct addrinfoW*) ((char*) req->alloc + hintoff);
|
||||
req->addrinfow->ai_family = hints->ai_family;
|
||||
req->addrinfow->ai_socktype = hints->ai_socktype;
|
||||
req->addrinfow->ai_protocol = hints->ai_protocol;
|
||||
@ -325,7 +330,7 @@ int uv_getaddrinfo(uv_loop_t* loop,
|
||||
req->addrinfow = NULL;
|
||||
}
|
||||
|
||||
uv__req_register(loop, req);
|
||||
uv__req_register(loop);
|
||||
|
||||
if (getaddrinfo_cb) {
|
||||
uv__work_submit(loop,
|
||||
|
||||
@ -82,7 +82,7 @@ static void uv__getnameinfo_done(struct uv__work* w, int status) {
|
||||
char* service;
|
||||
|
||||
req = container_of(w, uv_getnameinfo_t, work_req);
|
||||
uv__req_unregister(req->loop, req);
|
||||
uv__req_unregister(req->loop);
|
||||
host = service = NULL;
|
||||
|
||||
if (status == UV_ECANCELED) {
|
||||
@ -124,7 +124,7 @@ int uv_getnameinfo(uv_loop_t* loop,
|
||||
}
|
||||
|
||||
UV_REQ_INIT(req, UV_GETNAMEINFO);
|
||||
uv__req_register(loop, req);
|
||||
uv__req_register(loop);
|
||||
|
||||
req->getnameinfo_cb = getnameinfo_cb;
|
||||
req->flags = flags;
|
||||
|
||||
@ -330,4 +330,6 @@ void uv__wake_all_loops(void);
|
||||
*/
|
||||
void uv__init_detect_system_wakeup(void);
|
||||
|
||||
int uv_translate_write_sys_error(int sys_errno);
|
||||
|
||||
#endif /* UV_WIN_INTERNAL_H_ */
|
||||
|
||||
279
src/win/pipe.c
279
src/win/pipe.c
@ -106,8 +106,8 @@ static int includes_nul(const char *s, size_t n) {
|
||||
}
|
||||
|
||||
|
||||
static void uv__unique_pipe_name(char* ptr, char* name, size_t size) {
|
||||
snprintf(name, size, "\\\\?\\pipe\\uv\\%p-%lu", ptr, GetCurrentProcessId());
|
||||
static void uv__unique_pipe_name(unsigned long long ptr, char* name, size_t size) {
|
||||
snprintf(name, size, "\\\\?\\pipe\\uv\\%llu-%lu", ptr, GetCurrentProcessId());
|
||||
}
|
||||
|
||||
|
||||
@ -208,7 +208,7 @@ static void close_pipe(uv_pipe_t* pipe) {
|
||||
|
||||
static int uv__pipe_server(
|
||||
HANDLE* pipeHandle_ptr, DWORD access,
|
||||
char* name, size_t nameSize, char* random) {
|
||||
char* name, size_t nameSize, unsigned long long random) {
|
||||
HANDLE pipeHandle;
|
||||
int err;
|
||||
|
||||
@ -249,7 +249,7 @@ static int uv__pipe_server(
|
||||
static int uv__create_pipe_pair(
|
||||
HANDLE* server_pipe_ptr, HANDLE* client_pipe_ptr,
|
||||
unsigned int server_flags, unsigned int client_flags,
|
||||
int inherit_client, char* random) {
|
||||
int inherit_client, unsigned long long random) {
|
||||
/* allowed flags are: UV_READABLE_PIPE | UV_WRITABLE_PIPE | UV_NONBLOCK_PIPE */
|
||||
char pipe_name[64];
|
||||
SECURITY_ATTRIBUTES sa;
|
||||
@ -357,7 +357,12 @@ int uv_pipe(uv_file fds[2], int read_flags, int write_flags) {
|
||||
/* TODO: better source of local randomness than &fds? */
|
||||
read_flags |= UV_READABLE_PIPE;
|
||||
write_flags |= UV_WRITABLE_PIPE;
|
||||
err = uv__create_pipe_pair(&readh, &writeh, read_flags, write_flags, 0, (char*) &fds[0]);
|
||||
err = uv__create_pipe_pair(&readh,
|
||||
&writeh,
|
||||
read_flags,
|
||||
write_flags,
|
||||
0,
|
||||
(uintptr_t) &fds[0]);
|
||||
if (err != 0)
|
||||
return err;
|
||||
temp[0] = _open_osfhandle((intptr_t) readh, 0);
|
||||
@ -421,7 +426,7 @@ int uv__create_stdio_pipe_pair(uv_loop_t* loop,
|
||||
}
|
||||
|
||||
err = uv__create_pipe_pair(&server_pipe, &client_pipe,
|
||||
server_flags, client_flags, 1, (char*) server_pipe);
|
||||
server_flags, client_flags, 1, (uintptr_t) server_pipe);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
@ -667,15 +672,10 @@ void uv__pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) {
|
||||
}
|
||||
handle->pipe.conn.ipc_xfer_queue_length = 0;
|
||||
|
||||
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
|
||||
if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) {
|
||||
UnregisterWait(handle->read_req.wait_handle);
|
||||
handle->read_req.wait_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
if (handle->read_req.event_handle != NULL) {
|
||||
CloseHandle(handle->read_req.event_handle);
|
||||
handle->read_req.event_handle = NULL;
|
||||
}
|
||||
assert(handle->read_req.wait_handle == INVALID_HANDLE_VALUE);
|
||||
if (handle->read_req.event_handle != NULL) {
|
||||
CloseHandle(handle->read_req.event_handle);
|
||||
handle->read_req.event_handle = NULL;
|
||||
}
|
||||
|
||||
if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)
|
||||
@ -868,7 +868,7 @@ void uv_pipe_connect(uv_connect_t* req,
|
||||
SET_REQ_ERROR(req, err);
|
||||
uv__insert_pending_req(loop, (uv_req_t*) req);
|
||||
handle->reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
}
|
||||
}
|
||||
|
||||
@ -959,7 +959,7 @@ int uv_pipe_connect2(uv_connect_t* req,
|
||||
goto error;
|
||||
}
|
||||
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
handle->reqs_pending++;
|
||||
|
||||
return 0;
|
||||
@ -974,7 +974,7 @@ int uv_pipe_connect2(uv_connect_t* req,
|
||||
SET_REQ_SUCCESS(req);
|
||||
uv__insert_pending_req(loop, (uv_req_t*) req);
|
||||
handle->reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
@ -992,7 +992,7 @@ error:
|
||||
SET_REQ_ERROR(req, err);
|
||||
uv__insert_pending_req(loop, (uv_req_t*) req);
|
||||
handle->reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1161,9 +1161,9 @@ int uv__pipe_accept(uv_pipe_t* server, uv_stream_t* client) {
|
||||
|
||||
err = uv__tcp_xfer_import(
|
||||
(uv_tcp_t*) client, item->xfer_type, &item->xfer_info);
|
||||
|
||||
|
||||
uv__free(item);
|
||||
|
||||
|
||||
if (err != 0)
|
||||
return err;
|
||||
|
||||
@ -1417,13 +1417,12 @@ static void uv__pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) {
|
||||
}
|
||||
|
||||
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
|
||||
if (req->wait_handle == INVALID_HANDLE_VALUE) {
|
||||
if (!RegisterWaitForSingleObject(&req->wait_handle,
|
||||
req->event_handle, post_completion_read_wait, (void*) req,
|
||||
INFINITE, WT_EXECUTEINWAITTHREAD)) {
|
||||
SET_REQ_ERROR(req, GetLastError());
|
||||
goto error;
|
||||
}
|
||||
assert(req->wait_handle == INVALID_HANDLE_VALUE);
|
||||
if (!RegisterWaitForSingleObject(&req->wait_handle,
|
||||
req->event_handle, post_completion_read_wait, (void*) req,
|
||||
INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) {
|
||||
SET_REQ_ERROR(req, GetLastError());
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1451,16 +1450,16 @@ int uv__pipe_read_start(uv_pipe_t* handle,
|
||||
handle->read_cb = read_cb;
|
||||
handle->alloc_cb = alloc_cb;
|
||||
|
||||
if (handle->read_req.event_handle == NULL) {
|
||||
handle->read_req.event_handle = CreateEvent(NULL, 0, 0, NULL);
|
||||
if (handle->read_req.event_handle == NULL) {
|
||||
uv_fatal_error(GetLastError(), "CreateEvent");
|
||||
}
|
||||
}
|
||||
|
||||
/* If reading was stopped and then started again, there could still be a read
|
||||
* request pending. */
|
||||
if (!(handle->flags & UV_HANDLE_READ_PENDING)) {
|
||||
if (handle->flags & UV_HANDLE_EMULATE_IOCP &&
|
||||
handle->read_req.event_handle == NULL) {
|
||||
handle->read_req.event_handle = CreateEvent(NULL, 0, 0, NULL);
|
||||
if (handle->read_req.event_handle == NULL) {
|
||||
uv_fatal_error(GetLastError(), "CreateEvent");
|
||||
}
|
||||
}
|
||||
uv__pipe_queue_read(loop, handle);
|
||||
}
|
||||
|
||||
@ -1638,7 +1637,7 @@ static int uv__pipe_write_data(uv_loop_t* loop,
|
||||
req->u.io.queued_bytes = 0;
|
||||
}
|
||||
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
handle->reqs_pending++;
|
||||
handle->stream.conn.write_reqs_pending++;
|
||||
POST_COMPLETION_FOR_REQ(loop, req);
|
||||
@ -1686,7 +1685,7 @@ static int uv__pipe_write_data(uv_loop_t* loop,
|
||||
CloseHandle(req->event_handle);
|
||||
req->event_handle = NULL;
|
||||
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
handle->reqs_pending++;
|
||||
handle->stream.conn.write_reqs_pending++;
|
||||
return 0;
|
||||
@ -1713,13 +1712,13 @@ static int uv__pipe_write_data(uv_loop_t* loop,
|
||||
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
|
||||
if (!RegisterWaitForSingleObject(&req->wait_handle,
|
||||
req->event_handle, post_completion_write_wait, (void*) req,
|
||||
INFINITE, WT_EXECUTEINWAITTHREAD)) {
|
||||
INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) {
|
||||
return GetLastError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
handle->reqs_pending++;
|
||||
handle->stream.conn.write_reqs_pending++;
|
||||
|
||||
@ -1739,7 +1738,7 @@ static DWORD uv__pipe_get_ipc_remote_pid(uv_pipe_t* handle) {
|
||||
GetNamedPipeServerProcessId(handle->handle, pid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return *pid;
|
||||
}
|
||||
|
||||
@ -1889,7 +1888,7 @@ static void uv__pipe_read_error(uv_loop_t* loop, uv_pipe_t* handle, int error,
|
||||
|
||||
|
||||
static void uv__pipe_read_error_or_eof(uv_loop_t* loop, uv_pipe_t* handle,
|
||||
int error, uv_buf_t buf) {
|
||||
DWORD error, uv_buf_t buf) {
|
||||
if (error == ERROR_BROKEN_PIPE) {
|
||||
uv__pipe_read_eof(loop, handle, buf);
|
||||
} else {
|
||||
@ -1919,17 +1918,25 @@ static void uv__pipe_queue_ipc_xfer_info(
|
||||
/* Read an exact number of bytes from a pipe. If an error or end-of-file is
|
||||
* encountered before the requested number of bytes are read, an error is
|
||||
* returned. */
|
||||
static int uv__pipe_read_exactly(HANDLE h, void* buffer, DWORD count) {
|
||||
DWORD bytes_read, bytes_read_now;
|
||||
static DWORD uv__pipe_read_exactly(uv_pipe_t* handle, void* buffer, DWORD count) {
|
||||
uv_read_t* req;
|
||||
DWORD bytes_read;
|
||||
DWORD bytes_read_now;
|
||||
|
||||
bytes_read = 0;
|
||||
while (bytes_read < count) {
|
||||
if (!ReadFile(h,
|
||||
req = &handle->read_req;
|
||||
memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
|
||||
req->u.io.overlapped.hEvent = (HANDLE) ((uintptr_t) req->event_handle | 1);
|
||||
if (!ReadFile(handle->handle,
|
||||
(char*) buffer + bytes_read,
|
||||
count - bytes_read,
|
||||
&bytes_read_now,
|
||||
NULL)) {
|
||||
return GetLastError();
|
||||
&req->u.io.overlapped)) {
|
||||
if (GetLastError() != ERROR_IO_PENDING)
|
||||
return GetLastError();
|
||||
if (!GetOverlappedResult(handle->handle, &req->u.io.overlapped, &bytes_read_now, TRUE))
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
bytes_read += bytes_read_now;
|
||||
@ -1940,16 +1947,19 @@ static int uv__pipe_read_exactly(HANDLE h, void* buffer, DWORD count) {
|
||||
}
|
||||
|
||||
|
||||
static DWORD uv__pipe_read_data(uv_loop_t* loop,
|
||||
uv_pipe_t* handle,
|
||||
DWORD suggested_bytes,
|
||||
DWORD max_bytes) {
|
||||
DWORD bytes_read;
|
||||
static int uv__pipe_read_data(uv_loop_t* loop,
|
||||
uv_pipe_t* handle,
|
||||
DWORD* bytes_read, /* inout argument */
|
||||
DWORD max_bytes) {
|
||||
uv_buf_t buf;
|
||||
uv_read_t* req;
|
||||
DWORD r;
|
||||
DWORD bytes_available;
|
||||
int more;
|
||||
|
||||
/* Ask the user for a buffer to read data into. */
|
||||
buf = uv_buf_init(NULL, 0);
|
||||
handle->alloc_cb((uv_handle_t*) handle, suggested_bytes, &buf);
|
||||
handle->alloc_cb((uv_handle_t*) handle, *bytes_read, &buf);
|
||||
if (buf.base == NULL || buf.len == 0) {
|
||||
handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf);
|
||||
return 0; /* Break out of read loop. */
|
||||
@ -1958,33 +1968,77 @@ static DWORD uv__pipe_read_data(uv_loop_t* loop,
|
||||
/* Ensure we read at most the smaller of:
|
||||
* (a) the length of the user-allocated buffer.
|
||||
* (b) the maximum data length as specified by the `max_bytes` argument.
|
||||
* (c) the amount of data that can be read non-blocking
|
||||
*/
|
||||
if (max_bytes > buf.len)
|
||||
max_bytes = buf.len;
|
||||
|
||||
/* Read into the user buffer. */
|
||||
if (!ReadFile(handle->handle, buf.base, max_bytes, &bytes_read, NULL)) {
|
||||
uv__pipe_read_error_or_eof(loop, handle, GetLastError(), buf);
|
||||
return 0; /* Break out of read loop. */
|
||||
if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) {
|
||||
/* The user failed to supply a pipe that can be used non-blocking or with
|
||||
* threads. Try to estimate the amount of data that is safe to read without
|
||||
* blocking, in a race-y way however. */
|
||||
bytes_available = 0;
|
||||
if (!PeekNamedPipe(handle->handle, NULL, 0, NULL, &bytes_available, NULL)) {
|
||||
r = GetLastError();
|
||||
} else {
|
||||
if (max_bytes > bytes_available)
|
||||
max_bytes = bytes_available;
|
||||
*bytes_read = 0;
|
||||
if (max_bytes == 0 || ReadFile(handle->handle, buf.base, max_bytes, bytes_read, NULL))
|
||||
r = ERROR_SUCCESS;
|
||||
else
|
||||
r = GetLastError();
|
||||
}
|
||||
more = max_bytes < bytes_available;
|
||||
} else {
|
||||
/* Read into the user buffer.
|
||||
* Prepare an Event so that we can cancel if it doesn't complete immediately.
|
||||
*/
|
||||
req = &handle->read_req;
|
||||
memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
|
||||
req->u.io.overlapped.hEvent = (HANDLE) ((uintptr_t) req->event_handle | 1);
|
||||
if (ReadFile(handle->handle, buf.base, max_bytes, bytes_read, &req->u.io.overlapped)) {
|
||||
r = ERROR_SUCCESS;
|
||||
} else {
|
||||
r = GetLastError();
|
||||
*bytes_read = 0;
|
||||
if (r == ERROR_IO_PENDING) {
|
||||
r = CancelIoEx(handle->handle, &req->u.io.overlapped);
|
||||
assert(r || GetLastError() == ERROR_NOT_FOUND);
|
||||
if (GetOverlappedResult(handle->handle, &req->u.io.overlapped, bytes_read, TRUE)) {
|
||||
r = ERROR_SUCCESS;
|
||||
} else {
|
||||
r = GetLastError();
|
||||
*bytes_read = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
more = *bytes_read == max_bytes;
|
||||
}
|
||||
|
||||
/* Call the read callback. */
|
||||
handle->read_cb((uv_stream_t*) handle, bytes_read, &buf);
|
||||
if (r == ERROR_SUCCESS || r == ERROR_OPERATION_ABORTED)
|
||||
handle->read_cb((uv_stream_t*) handle, *bytes_read, &buf);
|
||||
else
|
||||
uv__pipe_read_error_or_eof(loop, handle, r, buf);
|
||||
|
||||
return bytes_read;
|
||||
return more;
|
||||
}
|
||||
|
||||
|
||||
static DWORD uv__pipe_read_ipc(uv_loop_t* loop, uv_pipe_t* handle) {
|
||||
uint32_t* data_remaining = &handle->pipe.conn.ipc_data_frame.payload_remaining;
|
||||
int err;
|
||||
static int uv__pipe_read_ipc(uv_loop_t* loop, uv_pipe_t* handle) {
|
||||
uint32_t* data_remaining;
|
||||
DWORD err;
|
||||
DWORD more;
|
||||
DWORD bytes_read;
|
||||
|
||||
data_remaining = &handle->pipe.conn.ipc_data_frame.payload_remaining;
|
||||
|
||||
if (*data_remaining > 0) {
|
||||
/* Read frame data payload. */
|
||||
DWORD bytes_read =
|
||||
uv__pipe_read_data(loop, handle, *data_remaining, *data_remaining);
|
||||
bytes_read = *data_remaining;
|
||||
more = uv__pipe_read_data(loop, handle, &bytes_read, bytes_read);
|
||||
*data_remaining -= bytes_read;
|
||||
return bytes_read;
|
||||
|
||||
} else {
|
||||
/* Start of a new IPC frame. */
|
||||
@ -1995,7 +2049,7 @@ static DWORD uv__pipe_read_ipc(uv_loop_t* loop, uv_pipe_t* handle) {
|
||||
|
||||
/* Read the IPC frame header. */
|
||||
err = uv__pipe_read_exactly(
|
||||
handle->handle, &frame_header, sizeof frame_header);
|
||||
handle, &frame_header, sizeof frame_header);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
@ -2031,21 +2085,24 @@ static DWORD uv__pipe_read_ipc(uv_loop_t* loop, uv_pipe_t* handle) {
|
||||
|
||||
/* If no socket xfer info follows, return here. Data will be read in a
|
||||
* subsequent invocation of uv__pipe_read_ipc(). */
|
||||
if (xfer_type == UV__IPC_SOCKET_XFER_NONE)
|
||||
return sizeof frame_header; /* Number of bytes read. */
|
||||
if (xfer_type != UV__IPC_SOCKET_XFER_NONE) {
|
||||
/* Read transferred socket information. */
|
||||
err = uv__pipe_read_exactly(handle, &xfer_info, sizeof xfer_info);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* Read transferred socket information. */
|
||||
err = uv__pipe_read_exactly(handle->handle, &xfer_info, sizeof xfer_info);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* Store the pending socket info. */
|
||||
uv__pipe_queue_ipc_xfer_info(handle, xfer_type, &xfer_info);
|
||||
|
||||
/* Return number of bytes read. */
|
||||
return sizeof frame_header + sizeof xfer_info;
|
||||
/* Store the pending socket info. */
|
||||
uv__pipe_queue_ipc_xfer_info(handle, xfer_type, &xfer_info);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return whether the caller should immediately try another read call to get
|
||||
* more data. Calling uv__pipe_read_exactly will hang if there isn't data
|
||||
* available, so we cannot do this unless we are guaranteed not to reach that.
|
||||
*/
|
||||
more = *data_remaining > 0;
|
||||
return more;
|
||||
|
||||
invalid:
|
||||
/* Invalid frame. */
|
||||
err = WSAECONNABORTED; /* Maps to UV_ECONNABORTED. */
|
||||
@ -2059,12 +2116,20 @@ error:
|
||||
void uv__process_pipe_read_req(uv_loop_t* loop,
|
||||
uv_pipe_t* handle,
|
||||
uv_req_t* req) {
|
||||
DWORD err;
|
||||
DWORD more;
|
||||
DWORD bytes_requested;
|
||||
assert(handle->type == UV_NAMED_PIPE);
|
||||
|
||||
handle->flags &= ~(UV_HANDLE_READ_PENDING | UV_HANDLE_CANCELLATION_PENDING);
|
||||
DECREASE_PENDING_REQ_COUNT(handle);
|
||||
eof_timer_stop(handle);
|
||||
|
||||
if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) {
|
||||
UnregisterWait(handle->read_req.wait_handle);
|
||||
handle->read_req.wait_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
/* At this point, we're done with bookkeeping. If the user has stopped
|
||||
* reading the pipe in the meantime, there is nothing left to do, since there
|
||||
* is no callback that we can call. */
|
||||
@ -2073,7 +2138,7 @@ void uv__process_pipe_read_req(uv_loop_t* loop,
|
||||
|
||||
if (!REQ_SUCCESS(req)) {
|
||||
/* An error occurred doing the zero-read. */
|
||||
DWORD err = GET_REQ_ERROR(req);
|
||||
err = GET_REQ_ERROR(req);
|
||||
|
||||
/* If the read was cancelled by uv__pipe_interrupt_read(), the request may
|
||||
* indicate an ERROR_OPERATION_ABORTED error. This error isn't relevant to
|
||||
@ -2084,34 +2149,18 @@ void uv__process_pipe_read_req(uv_loop_t* loop,
|
||||
} else {
|
||||
/* The zero-read completed without error, indicating there is data
|
||||
* available in the kernel buffer. */
|
||||
DWORD avail;
|
||||
|
||||
/* Get the number of bytes available. */
|
||||
avail = 0;
|
||||
if (!PeekNamedPipe(handle->handle, NULL, 0, NULL, &avail, NULL))
|
||||
uv__pipe_read_error_or_eof(loop, handle, GetLastError(), uv_null_buf_);
|
||||
|
||||
/* Read until we've either read all the bytes available, or the 'reading'
|
||||
* flag is cleared. */
|
||||
while (avail > 0 && handle->flags & UV_HANDLE_READING) {
|
||||
while (handle->flags & UV_HANDLE_READING) {
|
||||
bytes_requested = 65536;
|
||||
/* Depending on the type of pipe, read either IPC frames or raw data. */
|
||||
DWORD bytes_read =
|
||||
handle->ipc ? uv__pipe_read_ipc(loop, handle)
|
||||
: uv__pipe_read_data(loop, handle, avail, (DWORD) -1);
|
||||
if (handle->ipc)
|
||||
more = uv__pipe_read_ipc(loop, handle);
|
||||
else
|
||||
more = uv__pipe_read_data(loop, handle, &bytes_requested, INT32_MAX);
|
||||
|
||||
/* If no bytes were read, treat this as an indication that an error
|
||||
* occurred, and break out of the read loop. */
|
||||
if (bytes_read == 0)
|
||||
if (more == 0)
|
||||
break;
|
||||
|
||||
/* It is possible that more bytes were read than we thought were
|
||||
* available. To prevent `avail` from underflowing, break out of the loop
|
||||
* if this is the case. */
|
||||
if (bytes_read > avail)
|
||||
break;
|
||||
|
||||
/* Recompute the number of bytes available. */
|
||||
avail -= bytes_read;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2132,17 +2181,15 @@ void uv__process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle,
|
||||
assert(handle->write_queue_size >= req->u.io.queued_bytes);
|
||||
handle->write_queue_size -= req->u.io.queued_bytes;
|
||||
|
||||
UNREGISTER_HANDLE_REQ(loop, handle, req);
|
||||
UNREGISTER_HANDLE_REQ(loop, handle);
|
||||
|
||||
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
|
||||
if (req->wait_handle != INVALID_HANDLE_VALUE) {
|
||||
UnregisterWait(req->wait_handle);
|
||||
req->wait_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
if (req->event_handle) {
|
||||
CloseHandle(req->event_handle);
|
||||
req->event_handle = NULL;
|
||||
}
|
||||
if (req->wait_handle != INVALID_HANDLE_VALUE) {
|
||||
UnregisterWait(req->wait_handle);
|
||||
req->wait_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
if (req->event_handle) {
|
||||
CloseHandle(req->event_handle);
|
||||
req->event_handle = NULL;
|
||||
}
|
||||
|
||||
err = GET_REQ_ERROR(req);
|
||||
@ -2219,7 +2266,7 @@ void uv__process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle,
|
||||
|
||||
assert(handle->type == UV_NAMED_PIPE);
|
||||
|
||||
UNREGISTER_HANDLE_REQ(loop, handle, req);
|
||||
UNREGISTER_HANDLE_REQ(loop, handle);
|
||||
|
||||
err = 0;
|
||||
if (REQ_SUCCESS(req)) {
|
||||
@ -2251,7 +2298,7 @@ void uv__process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle,
|
||||
|
||||
/* Clear the shutdown_req field so we don't go here again. */
|
||||
handle->stream.conn.shutdown_req = NULL;
|
||||
UNREGISTER_HANDLE_REQ(loop, handle, req);
|
||||
UNREGISTER_HANDLE_REQ(loop, handle);
|
||||
|
||||
if (handle->flags & UV_HANDLE_CLOSING) {
|
||||
/* Already closing. Cancel the shutdown. */
|
||||
@ -2555,6 +2602,9 @@ int uv_pipe_pending_count(uv_pipe_t* handle) {
|
||||
|
||||
|
||||
int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size) {
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (handle->flags & UV_HANDLE_BOUND)
|
||||
return uv__pipe_getname(handle, buffer, size);
|
||||
|
||||
@ -2569,6 +2619,9 @@ int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size) {
|
||||
|
||||
|
||||
int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size) {
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
/* emulate unix behaviour */
|
||||
if (handle->flags & UV_HANDLE_BOUND)
|
||||
return UV_ENOTCONN;
|
||||
|
||||
@ -46,12 +46,12 @@
|
||||
#define CHILD_STDIO_CRT_FLAGS(buffer, fd) \
|
||||
*((unsigned char*) (buffer) + sizeof(int) + fd)
|
||||
|
||||
#define CHILD_STDIO_HANDLE(buffer, fd) \
|
||||
*((HANDLE*) ((unsigned char*) (buffer) + \
|
||||
sizeof(int) + \
|
||||
sizeof(unsigned char) * \
|
||||
CHILD_STDIO_COUNT((buffer)) + \
|
||||
sizeof(HANDLE) * (fd)))
|
||||
#define CHILD_STDIO_HANDLE(buffer, fd) \
|
||||
((void*) ((unsigned char*) (buffer) + \
|
||||
sizeof(int) + \
|
||||
sizeof(unsigned char) * \
|
||||
CHILD_STDIO_COUNT((buffer)) + \
|
||||
sizeof(HANDLE) * (fd)))
|
||||
|
||||
|
||||
/* CRT file descriptor mode flags */
|
||||
@ -194,7 +194,7 @@ int uv__stdio_create(uv_loop_t* loop,
|
||||
CHILD_STDIO_COUNT(buffer) = count;
|
||||
for (i = 0; i < count; i++) {
|
||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
|
||||
CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE;
|
||||
memset(CHILD_STDIO_HANDLE(buffer, i), 0xFF, sizeof(HANDLE));
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
@ -215,14 +215,15 @@ int uv__stdio_create(uv_loop_t* loop,
|
||||
* handles in the stdio buffer are initialized with.
|
||||
* INVALID_HANDLE_VALUE, which should be okay. */
|
||||
if (i <= 2) {
|
||||
HANDLE nul;
|
||||
DWORD access = (i == 0) ? FILE_GENERIC_READ :
|
||||
FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES;
|
||||
|
||||
err = uv__create_nul_handle(&CHILD_STDIO_HANDLE(buffer, i),
|
||||
access);
|
||||
err = uv__create_nul_handle(&nul, access);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
memcpy(CHILD_STDIO_HANDLE(buffer, i), &nul, sizeof(HANDLE));
|
||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
|
||||
}
|
||||
break;
|
||||
@ -247,7 +248,7 @@ int uv__stdio_create(uv_loop_t* loop,
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
CHILD_STDIO_HANDLE(buffer, i) = child_pipe;
|
||||
memcpy(CHILD_STDIO_HANDLE(buffer, i), &child_pipe, sizeof(HANDLE));
|
||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE;
|
||||
break;
|
||||
}
|
||||
@ -263,7 +264,7 @@ int uv__stdio_create(uv_loop_t* loop,
|
||||
* error. */
|
||||
if (fdopt.data.fd <= 2 && err == ERROR_INVALID_HANDLE) {
|
||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
|
||||
CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE;
|
||||
memset(CHILD_STDIO_HANDLE(buffer, i), 0xFF, sizeof(HANDLE));
|
||||
break;
|
||||
}
|
||||
goto error;
|
||||
@ -298,7 +299,7 @@ int uv__stdio_create(uv_loop_t* loop,
|
||||
return -1;
|
||||
}
|
||||
|
||||
CHILD_STDIO_HANDLE(buffer, i) = child_handle;
|
||||
memcpy(CHILD_STDIO_HANDLE(buffer, i), &child_handle, sizeof(HANDLE));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -334,7 +335,7 @@ int uv__stdio_create(uv_loop_t* loop,
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
CHILD_STDIO_HANDLE(buffer, i) = child_handle;
|
||||
memcpy(CHILD_STDIO_HANDLE(buffer, i), &child_handle, sizeof(HANDLE));
|
||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = crt_flags;
|
||||
break;
|
||||
}
|
||||
@ -359,7 +360,7 @@ void uv__stdio_destroy(BYTE* buffer) {
|
||||
|
||||
count = CHILD_STDIO_COUNT(buffer);
|
||||
for (i = 0; i < count; i++) {
|
||||
HANDLE handle = CHILD_STDIO_HANDLE(buffer, i);
|
||||
HANDLE handle = uv__stdio_handle(buffer, i);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(handle);
|
||||
}
|
||||
@ -374,7 +375,7 @@ void uv__stdio_noinherit(BYTE* buffer) {
|
||||
|
||||
count = CHILD_STDIO_COUNT(buffer);
|
||||
for (i = 0; i < count; i++) {
|
||||
HANDLE handle = CHILD_STDIO_HANDLE(buffer, i);
|
||||
HANDLE handle = uv__stdio_handle(buffer, i);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
|
||||
}
|
||||
@ -412,5 +413,7 @@ WORD uv__stdio_size(BYTE* buffer) {
|
||||
|
||||
|
||||
HANDLE uv__stdio_handle(BYTE* buffer, int fd) {
|
||||
return CHILD_STDIO_HANDLE(buffer, fd);
|
||||
HANDLE handle;
|
||||
memcpy(&handle, CHILD_STDIO_HANDLE(buffer, fd), sizeof(HANDLE));
|
||||
return handle;
|
||||
}
|
||||
|
||||
@ -26,7 +26,6 @@
|
||||
#include <signal.h>
|
||||
#include <limits.h>
|
||||
#include <wchar.h>
|
||||
#include <malloc.h> /* _alloca */
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
@ -598,11 +597,9 @@ error:
|
||||
}
|
||||
|
||||
|
||||
int env_strncmp(const wchar_t* a, int na, const wchar_t* b) {
|
||||
static int env_strncmp(const wchar_t* a, int na, const wchar_t* b) {
|
||||
wchar_t* a_eq;
|
||||
wchar_t* b_eq;
|
||||
wchar_t* A;
|
||||
wchar_t* B;
|
||||
int nb;
|
||||
int r;
|
||||
|
||||
@ -617,27 +614,8 @@ int env_strncmp(const wchar_t* a, int na, const wchar_t* b) {
|
||||
assert(b_eq);
|
||||
nb = b_eq - b;
|
||||
|
||||
A = _alloca((na+1) * sizeof(wchar_t));
|
||||
B = _alloca((nb+1) * sizeof(wchar_t));
|
||||
|
||||
r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, a, na, A, na);
|
||||
assert(r==na);
|
||||
A[na] = L'\0';
|
||||
r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, b, nb, B, nb);
|
||||
assert(r==nb);
|
||||
B[nb] = L'\0';
|
||||
|
||||
for (;;) {
|
||||
wchar_t AA = *A++;
|
||||
wchar_t BB = *B++;
|
||||
if (AA < BB) {
|
||||
return -1;
|
||||
} else if (AA > BB) {
|
||||
return 1;
|
||||
} else if (!AA && !BB) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
r = CompareStringOrdinal(a, na, b, nb, /*case insensitive*/TRUE);
|
||||
return r - CSTR_EQUAL;
|
||||
}
|
||||
|
||||
|
||||
@ -676,6 +654,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) {
|
||||
WCHAR* dst_copy;
|
||||
WCHAR** ptr_copy;
|
||||
WCHAR** env_copy;
|
||||
char* p;
|
||||
size_t required_vars_value_len[ARRAY_SIZE(required_vars)];
|
||||
|
||||
/* first pass: determine size in UTF-16 */
|
||||
@ -691,11 +670,13 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) {
|
||||
}
|
||||
|
||||
/* second pass: copy to UTF-16 environment block */
|
||||
dst_copy = uv__malloc(env_len * sizeof(WCHAR));
|
||||
if (dst_copy == NULL && env_len > 0) {
|
||||
len = env_block_count * sizeof(WCHAR*);
|
||||
p = uv__malloc(len + env_len * sizeof(WCHAR));
|
||||
if (p == NULL) {
|
||||
return UV_ENOMEM;
|
||||
}
|
||||
env_copy = _alloca(env_block_count * sizeof(WCHAR*));
|
||||
env_copy = (void*) &p[0];
|
||||
dst_copy = (void*) &p[len];
|
||||
|
||||
ptr = dst_copy;
|
||||
ptr_copy = env_copy;
|
||||
@ -745,7 +726,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) {
|
||||
/* final pass: copy, in sort order, and inserting required variables */
|
||||
dst = uv__malloc((1+env_len) * sizeof(WCHAR));
|
||||
if (!dst) {
|
||||
uv__free(dst_copy);
|
||||
uv__free(p);
|
||||
return UV_ENOMEM;
|
||||
}
|
||||
|
||||
@ -790,7 +771,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) {
|
||||
assert(env_len == (size_t) (ptr - dst));
|
||||
*ptr = L'\0';
|
||||
|
||||
uv__free(dst_copy);
|
||||
uv__free(p);
|
||||
*dst_ptr = dst;
|
||||
return 0;
|
||||
}
|
||||
@ -917,7 +898,7 @@ int uv_spawn(uv_loop_t* loop,
|
||||
*env = NULL, *cwd = NULL;
|
||||
STARTUPINFOW startup;
|
||||
PROCESS_INFORMATION info;
|
||||
DWORD process_flags;
|
||||
DWORD process_flags, cwd_len;
|
||||
BYTE* child_stdio_buffer;
|
||||
|
||||
uv__process_init(loop, process);
|
||||
@ -966,9 +947,10 @@ int uv_spawn(uv_loop_t* loop,
|
||||
if (err)
|
||||
goto done_uv;
|
||||
|
||||
cwd_len = wcslen(cwd);
|
||||
} else {
|
||||
/* Inherit cwd */
|
||||
DWORD cwd_len, r;
|
||||
DWORD r;
|
||||
|
||||
cwd_len = GetCurrentDirectoryW(0, NULL);
|
||||
if (!cwd_len) {
|
||||
@ -989,6 +971,15 @@ int uv_spawn(uv_loop_t* loop,
|
||||
}
|
||||
}
|
||||
|
||||
/* If cwd is too long, shorten it */
|
||||
if (cwd_len >= MAX_PATH) {
|
||||
cwd_len = GetShortPathNameW(cwd, cwd, cwd_len);
|
||||
if (cwd_len == 0) {
|
||||
err = GetLastError();
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get PATH environment variable. */
|
||||
path = find_path(env);
|
||||
if (path == NULL) {
|
||||
|
||||
@ -53,16 +53,16 @@
|
||||
(uv__ntstatus_to_winsock_error(GET_REQ_STATUS((req))))
|
||||
|
||||
|
||||
#define REGISTER_HANDLE_REQ(loop, handle, req) \
|
||||
#define REGISTER_HANDLE_REQ(loop, handle) \
|
||||
do { \
|
||||
INCREASE_ACTIVE_COUNT((loop), (handle)); \
|
||||
uv__req_register((loop), (req)); \
|
||||
uv__req_register((loop)); \
|
||||
} while (0)
|
||||
|
||||
#define UNREGISTER_HANDLE_REQ(loop, handle, req) \
|
||||
#define UNREGISTER_HANDLE_REQ(loop, handle) \
|
||||
do { \
|
||||
DECREASE_ACTIVE_COUNT((loop), (handle)); \
|
||||
uv__req_unregister((loop), (req)); \
|
||||
uv__req_unregister((loop)); \
|
||||
} while (0)
|
||||
|
||||
|
||||
@ -83,7 +83,7 @@
|
||||
|
||||
|
||||
INLINE static uv_req_t* uv__overlapped_to_req(OVERLAPPED* overlapped) {
|
||||
return CONTAINING_RECORD(overlapped, uv_req_t, u.io.overlapped);
|
||||
return container_of(overlapped, uv_req_t, u.io.overlapped);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -91,7 +91,7 @@ int uv__signal_dispatch(int signum) {
|
||||
|
||||
for (handle = RB_NFIND(uv_signal_tree_s, &uv__signal_tree, &lookup);
|
||||
handle != NULL && handle->signum == signum;
|
||||
handle = RB_NEXT(uv_signal_tree_s, &uv__signal_tree, handle)) {
|
||||
handle = RB_NEXT(uv_signal_tree_s, handle)) {
|
||||
unsigned long previous = InterlockedExchange(
|
||||
(volatile LONG*) &handle->pending_signum, signum);
|
||||
|
||||
|
||||
@ -131,7 +131,7 @@ int uv_write(uv_write_t* req,
|
||||
case UV_NAMED_PIPE:
|
||||
err = uv__pipe_write(
|
||||
loop, req, (uv_pipe_t*) handle, bufs, nbufs, NULL, cb);
|
||||
break;
|
||||
return uv_translate_write_sys_error(err);
|
||||
case UV_TTY:
|
||||
err = uv__tty_write(loop, req, (uv_tty_t*) handle, bufs, nbufs, cb);
|
||||
break;
|
||||
@ -164,7 +164,7 @@ int uv_write2(uv_write_t* req,
|
||||
|
||||
err = uv__pipe_write(
|
||||
loop, req, (uv_pipe_t*) handle, bufs, nbufs, send_handle, cb);
|
||||
return uv_translate_sys_error(err);
|
||||
return uv_translate_write_sys_error(err);
|
||||
}
|
||||
|
||||
|
||||
@ -216,7 +216,7 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) {
|
||||
handle->flags &= ~UV_HANDLE_WRITABLE;
|
||||
handle->stream.conn.shutdown_req = req;
|
||||
handle->reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
|
||||
if (handle->stream.conn.write_reqs_pending == 0) {
|
||||
if (handle->type == UV_NAMED_PIPE)
|
||||
|
||||
@ -58,11 +58,17 @@ static int uv__tcp_keepalive(uv_tcp_t* handle, SOCKET socket, int enable, unsign
|
||||
return WSAGetLastError();
|
||||
}
|
||||
|
||||
if (enable && setsockopt(socket,
|
||||
IPPROTO_TCP,
|
||||
TCP_KEEPALIVE,
|
||||
(const char*)&delay,
|
||||
sizeof delay) == -1) {
|
||||
if (!enable)
|
||||
return 0;
|
||||
|
||||
if (delay < 1)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (setsockopt(socket,
|
||||
IPPROTO_TCP,
|
||||
TCP_KEEPALIVE,
|
||||
(const char*)&delay,
|
||||
sizeof delay) == -1) {
|
||||
return WSAGetLastError();
|
||||
}
|
||||
|
||||
@ -206,7 +212,7 @@ void uv__process_tcp_shutdown_req(uv_loop_t* loop, uv_tcp_t* stream, uv_shutdown
|
||||
assert(stream->flags & UV_HANDLE_CONNECTION);
|
||||
|
||||
stream->stream.conn.shutdown_req = NULL;
|
||||
UNREGISTER_HANDLE_REQ(loop, stream, req);
|
||||
UNREGISTER_HANDLE_REQ(loop, stream);
|
||||
|
||||
err = 0;
|
||||
if (stream->flags & UV_HANDLE_CLOSING)
|
||||
@ -286,6 +292,12 @@ static int uv__tcp_try_bind(uv_tcp_t* handle,
|
||||
DWORD err;
|
||||
int r;
|
||||
|
||||
/* There is no SO_REUSEPORT on Windows, Windows only knows SO_REUSEADDR.
|
||||
* so we just return an error directly when UV_TCP_REUSEPORT is requested
|
||||
* for binding the socket. */
|
||||
if (flags & UV_TCP_REUSEPORT)
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
|
||||
if (handle->socket == INVALID_SOCKET) {
|
||||
SOCKET sock;
|
||||
|
||||
@ -822,7 +834,7 @@ out:
|
||||
if (handle->delayed_error != 0) {
|
||||
/* Process the req without IOCP. */
|
||||
handle->reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
uv__insert_pending_req(loop, (uv_req_t*)req);
|
||||
return 0;
|
||||
}
|
||||
@ -838,12 +850,12 @@ out:
|
||||
if (UV_SUCCEEDED_WITHOUT_IOCP(success)) {
|
||||
/* Process the req without IOCP. */
|
||||
handle->reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
uv__insert_pending_req(loop, (uv_req_t*)req);
|
||||
} else if (UV_SUCCEEDED_WITH_IOCP(success)) {
|
||||
/* The req will be processed with IOCP. */
|
||||
handle->reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
} else {
|
||||
return WSAGetLastError();
|
||||
}
|
||||
@ -913,14 +925,14 @@ int uv__tcp_write(uv_loop_t* loop,
|
||||
req->u.io.queued_bytes = 0;
|
||||
handle->reqs_pending++;
|
||||
handle->stream.conn.write_reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
uv__insert_pending_req(loop, (uv_req_t*) req);
|
||||
} else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
|
||||
/* Request queued by the kernel. */
|
||||
req->u.io.queued_bytes = uv__count_bufs(bufs, nbufs);
|
||||
handle->reqs_pending++;
|
||||
handle->stream.conn.write_reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
handle->write_queue_size += req->u.io.queued_bytes;
|
||||
if (handle->flags & UV_HANDLE_EMULATE_IOCP &&
|
||||
!RegisterWaitForSingleObject(&req->wait_handle,
|
||||
@ -934,7 +946,7 @@ int uv__tcp_write(uv_loop_t* loop,
|
||||
req->u.io.queued_bytes = 0;
|
||||
handle->reqs_pending++;
|
||||
handle->stream.conn.write_reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
SET_REQ_ERROR(req, WSAGetLastError());
|
||||
uv__insert_pending_req(loop, (uv_req_t*) req);
|
||||
}
|
||||
@ -1105,7 +1117,7 @@ void uv__process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle,
|
||||
assert(handle->write_queue_size >= req->u.io.queued_bytes);
|
||||
handle->write_queue_size -= req->u.io.queued_bytes;
|
||||
|
||||
UNREGISTER_HANDLE_REQ(loop, handle, req);
|
||||
UNREGISTER_HANDLE_REQ(loop, handle);
|
||||
|
||||
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
|
||||
if (req->wait_handle != INVALID_HANDLE_VALUE) {
|
||||
@ -1197,7 +1209,7 @@ void uv__process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle,
|
||||
|
||||
assert(handle->type == UV_TCP);
|
||||
|
||||
UNREGISTER_HANDLE_REQ(loop, handle, req);
|
||||
UNREGISTER_HANDLE_REQ(loop, handle);
|
||||
|
||||
err = 0;
|
||||
if (handle->delayed_error) {
|
||||
@ -1551,11 +1563,6 @@ int uv__tcp_connect(uv_connect_t* req,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef WSA_FLAG_NO_HANDLE_INHERIT
|
||||
/* Added in Windows 7 SP1. Specify this to avoid race conditions, */
|
||||
/* but also manually clear the inherit flag in case this failed. */
|
||||
#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
|
||||
#endif
|
||||
|
||||
int uv_socketpair(int type, int protocol, uv_os_sock_t fds[2], int flags0, int flags1) {
|
||||
SOCKET server = INVALID_SOCKET;
|
||||
|
||||
142
src/win/thread.c
142
src/win/thread.c
@ -32,45 +32,23 @@
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
static void uv__once_inner(uv_once_t* guard, void (*callback)(void)) {
|
||||
DWORD result;
|
||||
HANDLE existing_event, created_event;
|
||||
typedef void (*uv__once_cb)(void);
|
||||
|
||||
created_event = CreateEvent(NULL, 1, 0, NULL);
|
||||
if (created_event == 0) {
|
||||
/* Could fail in a low-memory situation? */
|
||||
uv_fatal_error(GetLastError(), "CreateEvent");
|
||||
}
|
||||
typedef struct {
|
||||
uv__once_cb callback;
|
||||
} uv__once_data_t;
|
||||
|
||||
existing_event = InterlockedCompareExchangePointer(&guard->event,
|
||||
created_event,
|
||||
NULL);
|
||||
static BOOL WINAPI uv__once_inner(INIT_ONCE *once, void* param, void** context) {
|
||||
uv__once_data_t* data = param;
|
||||
|
||||
if (existing_event == NULL) {
|
||||
/* We won the race */
|
||||
callback();
|
||||
data->callback();
|
||||
|
||||
result = SetEvent(created_event);
|
||||
assert(result);
|
||||
guard->ran = 1;
|
||||
|
||||
} else {
|
||||
/* We lost the race. Destroy the event we created and wait for the existing
|
||||
* one to become signaled. */
|
||||
CloseHandle(created_event);
|
||||
result = WaitForSingleObject(existing_event, INFINITE);
|
||||
assert(result == WAIT_OBJECT_0);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
void uv_once(uv_once_t* guard, void (*callback)(void)) {
|
||||
/* Fast case - avoid WaitForSingleObject. */
|
||||
if (guard->ran) {
|
||||
return;
|
||||
}
|
||||
|
||||
uv__once_inner(guard, callback);
|
||||
void uv_once(uv_once_t* guard, uv__once_cb callback) {
|
||||
uv__once_data_t data = { .callback = callback };
|
||||
InitOnceExecuteOnce(&guard->init_once, uv__once_inner, (void*) &data, NULL);
|
||||
}
|
||||
|
||||
|
||||
@ -79,6 +57,9 @@ STATIC_ASSERT(sizeof(uv_thread_t) <= sizeof(void*));
|
||||
|
||||
static uv_key_t uv__current_thread_key;
|
||||
static uv_once_t uv__current_thread_init_guard = UV_ONCE_INIT;
|
||||
static uv_once_t uv__thread_name_once = UV_ONCE_INIT;
|
||||
HRESULT (WINAPI *pGetThreadDescription)(HANDLE, PWSTR*);
|
||||
HRESULT (WINAPI *pSetThreadDescription)(HANDLE, PCWSTR);
|
||||
|
||||
|
||||
static void uv__init_current_thread_key(void) {
|
||||
@ -117,6 +98,15 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
|
||||
return uv_thread_create_ex(tid, ¶ms, entry, arg);
|
||||
}
|
||||
|
||||
|
||||
int uv_thread_detach(uv_thread_t *tid) {
|
||||
if (CloseHandle(*tid) == 0)
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_thread_create_ex(uv_thread_t* tid,
|
||||
const uv_thread_options_t* params,
|
||||
void (*entry)(void *arg),
|
||||
@ -291,6 +281,92 @@ int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) {
|
||||
}
|
||||
|
||||
|
||||
static void uv__thread_name_init_once(void) {
|
||||
HMODULE m;
|
||||
|
||||
m = GetModuleHandleA("api-ms-win-core-processthreads-l1-1-3.dll");
|
||||
if (m != NULL) {
|
||||
pGetThreadDescription = (void*) GetProcAddress(m, "GetThreadDescription");
|
||||
pSetThreadDescription = (void*) GetProcAddress(m, "SetThreadDescription");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv_thread_setname(const char* name) {
|
||||
HRESULT hr;
|
||||
WCHAR* namew;
|
||||
int err;
|
||||
char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
|
||||
uv_once(&uv__thread_name_once, uv__thread_name_init_once);
|
||||
|
||||
if (pSetThreadDescription == NULL)
|
||||
return UV_ENOSYS;
|
||||
|
||||
if (name == NULL)
|
||||
return UV_EINVAL;
|
||||
|
||||
strncpy(namebuf, name, sizeof(namebuf) - 1);
|
||||
namebuf[sizeof(namebuf) - 1] = '\0';
|
||||
|
||||
namew = NULL;
|
||||
err = uv__convert_utf8_to_utf16(namebuf, &namew);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
hr = pSetThreadDescription(GetCurrentThread(), namew);
|
||||
uv__free(namew);
|
||||
if (FAILED(hr))
|
||||
return uv_translate_sys_error(HRESULT_CODE(hr));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_thread_getname(uv_thread_t* tid, char* name, size_t size) {
|
||||
HRESULT hr;
|
||||
WCHAR* namew;
|
||||
char* thread_name;
|
||||
size_t buf_size;
|
||||
int r;
|
||||
DWORD exit_code;
|
||||
|
||||
uv_once(&uv__thread_name_once, uv__thread_name_init_once);
|
||||
|
||||
if (pGetThreadDescription == NULL)
|
||||
return UV_ENOSYS;
|
||||
|
||||
if (name == NULL || size == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (tid == NULL || *tid == NULL)
|
||||
return UV_EINVAL;
|
||||
|
||||
/* Check if the thread handle is valid */
|
||||
if (!GetExitCodeThread(*tid, &exit_code) || exit_code != STILL_ACTIVE)
|
||||
return UV_ENOENT;
|
||||
|
||||
namew = NULL;
|
||||
thread_name = NULL;
|
||||
hr = pGetThreadDescription(*tid, &namew);
|
||||
if (FAILED(hr))
|
||||
return uv_translate_sys_error(HRESULT_CODE(hr));
|
||||
|
||||
buf_size = size;
|
||||
r = uv__copy_utf16_to_utf8(namew, -1, name, &buf_size);
|
||||
if (r == UV_ENOBUFS) {
|
||||
r = uv__convert_utf16_to_utf8(namew, wcslen(namew), &thread_name);
|
||||
if (r == 0) {
|
||||
uv__strscpy(name, thread_name, size);
|
||||
uv__free(thread_name);
|
||||
}
|
||||
}
|
||||
|
||||
LocalFree(namew);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
int uv_mutex_init(uv_mutex_t* mutex) {
|
||||
InitializeCriticalSection(mutex);
|
||||
return 0;
|
||||
|
||||
@ -58,6 +58,9 @@
|
||||
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
|
||||
#endif
|
||||
#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
|
||||
#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
|
||||
#endif
|
||||
|
||||
#define CURSOR_SIZE_SMALL 25
|
||||
#define CURSOR_SIZE_LARGE 100
|
||||
@ -119,7 +122,10 @@ static int uv_tty_virtual_width = -1;
|
||||
* handle signalling SIGWINCH
|
||||
*/
|
||||
|
||||
static HANDLE uv__tty_console_handle = INVALID_HANDLE_VALUE;
|
||||
static HANDLE uv__tty_console_handle_out = INVALID_HANDLE_VALUE;
|
||||
static HANDLE uv__tty_console_handle_in = INVALID_HANDLE_VALUE;
|
||||
static DWORD uv__tty_console_in_original_mode = (DWORD)-1;
|
||||
static volatile LONG uv__tty_console_in_need_mode_reset = 0;
|
||||
static int uv__tty_console_height = -1;
|
||||
static int uv__tty_console_width = -1;
|
||||
static HANDLE uv__tty_console_resized = INVALID_HANDLE_VALUE;
|
||||
@ -159,19 +165,21 @@ static uv_tty_vtermstate_t uv__vterm_state = UV_TTY_UNSUPPORTED;
|
||||
static void uv__determine_vterm_state(HANDLE handle);
|
||||
|
||||
void uv__console_init(void) {
|
||||
DWORD dwMode;
|
||||
|
||||
if (uv_sem_init(&uv_tty_output_lock, 1))
|
||||
abort();
|
||||
uv__tty_console_handle = CreateFileW(L"CONOUT$",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_WRITE,
|
||||
0,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
0);
|
||||
if (uv__tty_console_handle != INVALID_HANDLE_VALUE) {
|
||||
uv__tty_console_handle_out = CreateFileW(L"CONOUT$",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_WRITE,
|
||||
0,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
0);
|
||||
if (uv__tty_console_handle_out != INVALID_HANDLE_VALUE) {
|
||||
CONSOLE_SCREEN_BUFFER_INFO sb_info;
|
||||
uv_mutex_init(&uv__tty_console_resize_mutex);
|
||||
if (GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info)) {
|
||||
if (GetConsoleScreenBufferInfo(uv__tty_console_handle_out, &sb_info)) {
|
||||
uv__tty_console_width = sb_info.dwSize.X;
|
||||
uv__tty_console_height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1;
|
||||
}
|
||||
@ -179,6 +187,18 @@ void uv__console_init(void) {
|
||||
NULL,
|
||||
WT_EXECUTELONGFUNCTION);
|
||||
}
|
||||
uv__tty_console_handle_in = CreateFileW(L"CONIN$",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ,
|
||||
0,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
0);
|
||||
if (uv__tty_console_handle_in != INVALID_HANDLE_VALUE) {
|
||||
if (GetConsoleMode(uv__tty_console_handle_in, &dwMode)) {
|
||||
uv__tty_console_in_original_mode = dwMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -253,7 +273,9 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int unused) {
|
||||
/* Initialize TTY input specific fields. */
|
||||
tty->flags |= UV_HANDLE_TTY_READABLE | UV_HANDLE_READABLE;
|
||||
/* TODO: remove me in v2.x. */
|
||||
tty->tty.rd.unused_ = NULL;
|
||||
tty->tty.rd.mode.unused_ = NULL;
|
||||
/* Partially overwrites unused_ again. */
|
||||
tty->tty.rd.mode.mode = 0;
|
||||
tty->tty.rd.read_line_buffer = uv_null_buf_;
|
||||
tty->tty.rd.read_raw_wait = NULL;
|
||||
|
||||
@ -344,6 +366,7 @@ static void uv__tty_capture_initial_style(
|
||||
|
||||
int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
|
||||
DWORD flags;
|
||||
DWORD try_set_flags;
|
||||
unsigned char was_reading;
|
||||
uv_alloc_cb alloc_cb;
|
||||
uv_read_cb read_cb;
|
||||
@ -353,14 +376,19 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
if (!!mode == !!(tty->flags & UV_HANDLE_TTY_RAW)) {
|
||||
if ((int)mode == tty->tty.rd.mode.mode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
try_set_flags = 0;
|
||||
switch (mode) {
|
||||
case UV_TTY_MODE_NORMAL:
|
||||
flags = ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
|
||||
break;
|
||||
case UV_TTY_MODE_RAW_VT:
|
||||
try_set_flags = ENABLE_VIRTUAL_TERMINAL_INPUT;
|
||||
InterlockedExchange(&uv__tty_console_in_need_mode_reset, 1);
|
||||
/* fallthrough */
|
||||
case UV_TTY_MODE_RAW:
|
||||
flags = ENABLE_WINDOW_INPUT;
|
||||
break;
|
||||
@ -386,16 +414,16 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
|
||||
}
|
||||
|
||||
uv_sem_wait(&uv_tty_output_lock);
|
||||
if (!SetConsoleMode(tty->handle, flags)) {
|
||||
if (!SetConsoleMode(tty->handle, flags | try_set_flags) &&
|
||||
!SetConsoleMode(tty->handle, flags)) {
|
||||
err = uv_translate_sys_error(GetLastError());
|
||||
uv_sem_post(&uv_tty_output_lock);
|
||||
return err;
|
||||
}
|
||||
uv_sem_post(&uv_tty_output_lock);
|
||||
|
||||
/* Update flag. */
|
||||
tty->flags &= ~UV_HANDLE_TTY_RAW;
|
||||
tty->flags |= mode ? UV_HANDLE_TTY_RAW : 0;
|
||||
/* Update mode. */
|
||||
tty->tty.rd.mode.mode = mode;
|
||||
|
||||
/* If we just stopped reading, restart. */
|
||||
if (was_reading) {
|
||||
@ -614,7 +642,7 @@ static void uv__tty_queue_read_line(uv_loop_t* loop, uv_tty_t* handle) {
|
||||
|
||||
|
||||
static void uv__tty_queue_read(uv_loop_t* loop, uv_tty_t* handle) {
|
||||
if (handle->flags & UV_HANDLE_TTY_RAW) {
|
||||
if (uv__is_raw_tty_mode(handle->tty.rd.mode.mode)) {
|
||||
uv__tty_queue_read_raw(loop, handle);
|
||||
} else {
|
||||
uv__tty_queue_read_line(loop, handle);
|
||||
@ -702,7 +730,7 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle,
|
||||
handle->flags &= ~UV_HANDLE_READ_PENDING;
|
||||
|
||||
if (!(handle->flags & UV_HANDLE_READING) ||
|
||||
!(handle->flags & UV_HANDLE_TTY_RAW)) {
|
||||
!(uv__is_raw_tty_mode(handle->tty.rd.mode.mode))) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -1050,7 +1078,7 @@ int uv__tty_read_stop(uv_tty_t* handle) {
|
||||
if (!(handle->flags & UV_HANDLE_READ_PENDING))
|
||||
return 0;
|
||||
|
||||
if (handle->flags & UV_HANDLE_TTY_RAW) {
|
||||
if (uv__is_raw_tty_mode(handle->tty.rd.mode.mode)) {
|
||||
/* Cancel raw read. Write some bullshit event to force the console wait to
|
||||
* return. */
|
||||
memset(&record, 0, sizeof record);
|
||||
@ -2183,7 +2211,7 @@ int uv__tty_write(uv_loop_t* loop,
|
||||
|
||||
handle->reqs_pending++;
|
||||
handle->stream.conn.write_reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
|
||||
req->u.io.queued_bytes = 0;
|
||||
|
||||
@ -2219,7 +2247,7 @@ void uv__process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
|
||||
int err;
|
||||
|
||||
handle->write_queue_size -= req->u.io.queued_bytes;
|
||||
UNREGISTER_HANDLE_REQ(loop, handle, req);
|
||||
UNREGISTER_HANDLE_REQ(loop, handle);
|
||||
|
||||
if (req->cb) {
|
||||
err = GET_REQ_ERROR(req);
|
||||
@ -2263,7 +2291,7 @@ void uv__process_tty_shutdown_req(uv_loop_t* loop, uv_tty_t* stream, uv_shutdown
|
||||
assert(req);
|
||||
|
||||
stream->stream.conn.shutdown_req = NULL;
|
||||
UNREGISTER_HANDLE_REQ(loop, stream, req);
|
||||
UNREGISTER_HANDLE_REQ(loop, stream);
|
||||
|
||||
/* TTY shutdown is really just a no-op */
|
||||
if (req->cb) {
|
||||
@ -2293,7 +2321,17 @@ void uv__tty_endgame(uv_loop_t* loop, uv_tty_t* handle) {
|
||||
|
||||
|
||||
int uv_tty_reset_mode(void) {
|
||||
/* Not necessary to do anything. */
|
||||
/**
|
||||
* Shells on Windows do know to reset output flags after a program exits,
|
||||
* but not necessarily input flags, so we do that for them.
|
||||
*/
|
||||
if (
|
||||
uv__tty_console_handle_in != INVALID_HANDLE_VALUE &&
|
||||
uv__tty_console_in_original_mode != (DWORD)-1 &&
|
||||
InterlockedExchange(&uv__tty_console_in_need_mode_reset, 0) != 0
|
||||
) {
|
||||
SetConsoleMode(uv__tty_console_handle_in, uv__tty_console_in_original_mode);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2380,8 +2418,8 @@ static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param) {
|
||||
/* Make sure to not overwhelm the system with resize events */
|
||||
Sleep(33);
|
||||
WaitForSingleObject(uv__tty_console_resized, INFINITE);
|
||||
uv__tty_console_signal_resize();
|
||||
ResetEvent(uv__tty_console_resized);
|
||||
uv__tty_console_signal_resize();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -2390,7 +2428,7 @@ static void uv__tty_console_signal_resize(void) {
|
||||
CONSOLE_SCREEN_BUFFER_INFO sb_info;
|
||||
int width, height;
|
||||
|
||||
if (!GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info))
|
||||
if (!GetConsoleScreenBufferInfo(uv__tty_console_handle_out, &sb_info))
|
||||
return;
|
||||
|
||||
width = sb_info.dwSize.X;
|
||||
|
||||
@ -200,6 +200,12 @@ static int uv__udp_maybe_bind(uv_udp_t* handle,
|
||||
if (handle->flags & UV_HANDLE_BOUND)
|
||||
return 0;
|
||||
|
||||
/* There is no SO_REUSEPORT on Windows, Windows only knows SO_REUSEADDR.
|
||||
* so we just return an error directly when UV_UDP_REUSEPORT is requested
|
||||
* for binding the socket. */
|
||||
if (flags & UV_UDP_REUSEPORT)
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
|
||||
if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6) {
|
||||
/* UV_UDP_IPV6ONLY is supported only for IPV6 sockets */
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -376,7 +382,7 @@ static int uv__send(uv_udp_send_t* req,
|
||||
handle->reqs_pending++;
|
||||
handle->send_queue_size += req->u.io.queued_bytes;
|
||||
handle->send_queue_count++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
uv__insert_pending_req(loop, (uv_req_t*)req);
|
||||
} else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
|
||||
/* Request queued by the kernel. */
|
||||
@ -384,7 +390,7 @@ static int uv__send(uv_udp_send_t* req,
|
||||
handle->reqs_pending++;
|
||||
handle->send_queue_size += req->u.io.queued_bytes;
|
||||
handle->send_queue_count++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
} else {
|
||||
/* Send failed due to an error. */
|
||||
return WSAGetLastError();
|
||||
@ -527,7 +533,7 @@ void uv__process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle,
|
||||
handle->send_queue_size -= req->u.io.queued_bytes;
|
||||
handle->send_queue_count--;
|
||||
|
||||
UNREGISTER_HANDLE_REQ(loop, handle, req);
|
||||
UNREGISTER_HANDLE_REQ(loop, handle);
|
||||
|
||||
if (req->cb) {
|
||||
err = 0;
|
||||
@ -1095,7 +1101,8 @@ int uv__udp_try_send(uv_udp_t* handle,
|
||||
struct sockaddr_storage converted;
|
||||
int err;
|
||||
|
||||
assert(nbufs > 0);
|
||||
if (nbufs < 1)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (addr != NULL) {
|
||||
err = uv__convert_to_localhost_if_unspecified(addr, &converted);
|
||||
@ -1135,3 +1142,21 @@ int uv__udp_try_send(uv_udp_t* handle,
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_try_send2(uv_udp_t* handle,
|
||||
unsigned int count,
|
||||
uv_buf_t* bufs[/*count*/],
|
||||
unsigned int nbufs[/*count*/],
|
||||
struct sockaddr* addrs[/*count*/]) {
|
||||
unsigned int i;
|
||||
int r;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
r = uv_udp_try_send(handle, bufs[i], nbufs[i], addrs[i]);
|
||||
if (r < 0)
|
||||
return i > 0 ? i : r; /* Error if first packet, else send count. */
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
185
src/win/util.c
185
src/win/util.c
@ -191,7 +191,7 @@ int uv_cwd(char* buffer, size_t* size) {
|
||||
WCHAR *utf16_buffer;
|
||||
int r;
|
||||
|
||||
if (buffer == NULL || size == NULL) {
|
||||
if (buffer == NULL || size == NULL || *size == 0) {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
@ -316,25 +316,19 @@ uv_pid_t uv_os_getpid(void) {
|
||||
|
||||
|
||||
uv_pid_t uv_os_getppid(void) {
|
||||
int parent_pid = -1;
|
||||
HANDLE handle;
|
||||
PROCESSENTRY32 pe;
|
||||
DWORD current_pid = GetCurrentProcessId();
|
||||
NTSTATUS nt_status;
|
||||
PROCESS_BASIC_INFORMATION basic_info;
|
||||
|
||||
pe.dwSize = sizeof(PROCESSENTRY32);
|
||||
handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
|
||||
if (Process32First(handle, &pe)) {
|
||||
do {
|
||||
if (pe.th32ProcessID == current_pid) {
|
||||
parent_pid = pe.th32ParentProcessID;
|
||||
break;
|
||||
}
|
||||
} while( Process32Next(handle, &pe));
|
||||
nt_status = pNtQueryInformationProcess(GetCurrentProcess(),
|
||||
ProcessBasicInformation,
|
||||
&basic_info,
|
||||
sizeof(basic_info),
|
||||
NULL);
|
||||
if (NT_SUCCESS(nt_status)) {
|
||||
return basic_info.InheritedFromUniqueProcessId;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
CloseHandle(handle);
|
||||
return parent_pid;
|
||||
}
|
||||
|
||||
|
||||
@ -512,19 +506,23 @@ int uv_uptime(double* uptime) {
|
||||
|
||||
|
||||
unsigned int uv_available_parallelism(void) {
|
||||
SYSTEM_INFO info;
|
||||
unsigned rc;
|
||||
DWORD_PTR procmask;
|
||||
DWORD_PTR sysmask;
|
||||
int count;
|
||||
int i;
|
||||
|
||||
/* TODO(bnoordhuis) Use GetLogicalProcessorInformationEx() to support systems
|
||||
* with > 64 CPUs? See https://github.com/libuv/libuv/pull/3458
|
||||
*/
|
||||
GetSystemInfo(&info);
|
||||
count = 0;
|
||||
if (GetProcessAffinityMask(GetCurrentProcess(), &procmask, &sysmask))
|
||||
for (i = 0; i < 8 * sizeof(procmask); i++)
|
||||
count += 1 & (procmask >> i);
|
||||
|
||||
rc = info.dwNumberOfProcessors;
|
||||
if (rc < 1)
|
||||
rc = 1;
|
||||
if (count > 0)
|
||||
return count;
|
||||
|
||||
return rc;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@ -876,56 +874,100 @@ void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||
|
||||
|
||||
int uv_getrusage(uv_rusage_t *uv_rusage) {
|
||||
FILETIME createTime, exitTime, kernelTime, userTime;
|
||||
SYSTEMTIME kernelSystemTime, userSystemTime;
|
||||
PROCESS_MEMORY_COUNTERS memCounters;
|
||||
IO_COUNTERS ioCounters;
|
||||
FILETIME create_time, exit_time, kernel_time, user_time;
|
||||
SYSTEMTIME kernel_system_time, user_system_time;
|
||||
PROCESS_MEMORY_COUNTERS mem_counters;
|
||||
IO_COUNTERS io_counters;
|
||||
int ret;
|
||||
|
||||
ret = GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime);
|
||||
ret = GetProcessTimes(GetCurrentProcess(),
|
||||
&create_time,
|
||||
&exit_time,
|
||||
&kernel_time,
|
||||
&user_time);
|
||||
if (ret == 0) {
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
}
|
||||
|
||||
ret = FileTimeToSystemTime(&kernelTime, &kernelSystemTime);
|
||||
ret = FileTimeToSystemTime(&kernel_time, &kernel_system_time);
|
||||
if (ret == 0) {
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
}
|
||||
|
||||
ret = FileTimeToSystemTime(&userTime, &userSystemTime);
|
||||
ret = FileTimeToSystemTime(&user_time, &user_system_time);
|
||||
if (ret == 0) {
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
}
|
||||
|
||||
ret = GetProcessMemoryInfo(GetCurrentProcess(),
|
||||
&memCounters,
|
||||
sizeof(memCounters));
|
||||
&mem_counters,
|
||||
sizeof(mem_counters));
|
||||
if (ret == 0) {
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
}
|
||||
|
||||
ret = GetProcessIoCounters(GetCurrentProcess(), &ioCounters);
|
||||
ret = GetProcessIoCounters(GetCurrentProcess(), &io_counters);
|
||||
if (ret == 0) {
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
}
|
||||
|
||||
memset(uv_rusage, 0, sizeof(*uv_rusage));
|
||||
|
||||
uv_rusage->ru_utime.tv_sec = userSystemTime.wHour * 3600 +
|
||||
userSystemTime.wMinute * 60 +
|
||||
userSystemTime.wSecond;
|
||||
uv_rusage->ru_utime.tv_usec = userSystemTime.wMilliseconds * 1000;
|
||||
uv_rusage->ru_utime.tv_sec = user_system_time.wHour * 3600 +
|
||||
user_system_time.wMinute * 60 +
|
||||
user_system_time.wSecond;
|
||||
uv_rusage->ru_utime.tv_usec = user_system_time.wMilliseconds * 1000;
|
||||
|
||||
uv_rusage->ru_stime.tv_sec = kernelSystemTime.wHour * 3600 +
|
||||
kernelSystemTime.wMinute * 60 +
|
||||
kernelSystemTime.wSecond;
|
||||
uv_rusage->ru_stime.tv_usec = kernelSystemTime.wMilliseconds * 1000;
|
||||
uv_rusage->ru_stime.tv_sec = kernel_system_time.wHour * 3600 +
|
||||
kernel_system_time.wMinute * 60 +
|
||||
kernel_system_time.wSecond;
|
||||
uv_rusage->ru_stime.tv_usec = kernel_system_time.wMilliseconds * 1000;
|
||||
|
||||
uv_rusage->ru_majflt = (uint64_t) memCounters.PageFaultCount;
|
||||
uv_rusage->ru_maxrss = (uint64_t) memCounters.PeakWorkingSetSize / 1024;
|
||||
uv_rusage->ru_majflt = (uint64_t) mem_counters.PageFaultCount;
|
||||
uv_rusage->ru_maxrss = (uint64_t) mem_counters.PeakWorkingSetSize / 1024;
|
||||
|
||||
uv_rusage->ru_oublock = (uint64_t) ioCounters.WriteOperationCount;
|
||||
uv_rusage->ru_inblock = (uint64_t) ioCounters.ReadOperationCount;
|
||||
uv_rusage->ru_oublock = (uint64_t) io_counters.WriteOperationCount;
|
||||
uv_rusage->ru_inblock = (uint64_t) io_counters.ReadOperationCount;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_getrusage_thread(uv_rusage_t* uv_rusage) {
|
||||
FILETIME create_time, exit_time, kernel_time, user_time;
|
||||
SYSTEMTIME kernel_system_time, user_system_time;
|
||||
int ret;
|
||||
|
||||
ret = GetThreadTimes(GetCurrentThread(),
|
||||
&create_time,
|
||||
&exit_time,
|
||||
&kernel_time,
|
||||
&user_time);
|
||||
if (ret == 0) {
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
}
|
||||
|
||||
ret = FileTimeToSystemTime(&kernel_time, &kernel_system_time);
|
||||
if (ret == 0) {
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
}
|
||||
|
||||
ret = FileTimeToSystemTime(&user_time, &user_system_time);
|
||||
if (ret == 0) {
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
}
|
||||
|
||||
memset(uv_rusage, 0, sizeof(*uv_rusage));
|
||||
|
||||
uv_rusage->ru_utime.tv_sec = user_system_time.wHour * 3600 +
|
||||
user_system_time.wMinute * 60 +
|
||||
user_system_time.wSecond;
|
||||
uv_rusage->ru_utime.tv_usec = user_system_time.wMilliseconds * 1000;
|
||||
|
||||
uv_rusage->ru_stime.tv_sec = kernel_system_time.wHour * 3600 +
|
||||
kernel_system_time.wMinute * 60 +
|
||||
kernel_system_time.wSecond;
|
||||
uv_rusage->ru_stime.tv_usec = kernel_system_time.wMilliseconds * 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -942,8 +984,13 @@ int uv_os_homedir(char* buffer, size_t* size) {
|
||||
r = uv_os_getenv("USERPROFILE", buffer, size);
|
||||
|
||||
/* Don't return an error if USERPROFILE was not found. */
|
||||
if (r != UV_ENOENT)
|
||||
if (r != UV_ENOENT) {
|
||||
/* USERPROFILE is empty or invalid */
|
||||
if (r == 0 && *size < 3) {
|
||||
return UV_ENOENT;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/* USERPROFILE is not set, so call uv_os_get_passwd() */
|
||||
r = uv_os_get_passwd(&pwd);
|
||||
@ -969,6 +1016,7 @@ int uv_os_homedir(char* buffer, size_t* size) {
|
||||
|
||||
|
||||
int uv_os_tmpdir(char* buffer, size_t* size) {
|
||||
int r;
|
||||
wchar_t *path;
|
||||
size_t len;
|
||||
|
||||
@ -980,6 +1028,12 @@ int uv_os_tmpdir(char* buffer, size_t* size) {
|
||||
if (len == 0) {
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
}
|
||||
|
||||
/* tmp path is empty or invalid */
|
||||
if (len < 3) {
|
||||
return UV_ENOENT;
|
||||
}
|
||||
|
||||
/* Include space for terminating null char. */
|
||||
len += 1;
|
||||
path = uv__malloc(len * sizeof(wchar_t));
|
||||
@ -1001,7 +1055,9 @@ int uv_os_tmpdir(char* buffer, size_t* size) {
|
||||
path[len] = L'\0';
|
||||
}
|
||||
|
||||
return uv__copy_utf16_to_utf8(path, len, buffer, size);
|
||||
r = uv__copy_utf16_to_utf8(path, len, buffer, size);
|
||||
uv__free(path);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
@ -1259,6 +1315,9 @@ int uv_os_getenv(const char* name, char* buffer, size_t* size) {
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
len = GetEnvironmentVariableW(name_w, var, varlen);
|
||||
|
||||
if (len == 0)
|
||||
r = uv_translate_sys_error(GetLastError());
|
||||
|
||||
if (len < varlen)
|
||||
break;
|
||||
|
||||
@ -1280,15 +1339,8 @@ int uv_os_getenv(const char* name, char* buffer, size_t* size) {
|
||||
uv__free(name_w);
|
||||
name_w = NULL;
|
||||
|
||||
if (len == 0) {
|
||||
r = GetLastError();
|
||||
if (r != ERROR_SUCCESS) {
|
||||
r = uv_translate_sys_error(r);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
r = uv__copy_utf16_to_utf8(var, len, buffer, size);
|
||||
if (r == 0)
|
||||
r = uv__copy_utf16_to_utf8(var, len, buffer, size);
|
||||
|
||||
fail:
|
||||
|
||||
@ -1528,20 +1580,7 @@ int uv_os_uname(uv_utsname_t* buffer) {
|
||||
os_info.dwOSVersionInfoSize = sizeof(os_info);
|
||||
os_info.szCSDVersion[0] = L'\0';
|
||||
|
||||
/* Try calling RtlGetVersion(), and fall back to the deprecated GetVersionEx()
|
||||
if RtlGetVersion() is not available. */
|
||||
if (pRtlGetVersion) {
|
||||
pRtlGetVersion(&os_info);
|
||||
} else {
|
||||
/* Silence GetVersionEx() deprecation warning. */
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(suppress : 4996)
|
||||
#endif
|
||||
if (GetVersionExW(&os_info) == 0) {
|
||||
r = uv_translate_sys_error(GetLastError());
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
pRtlGetVersion(&os_info);
|
||||
|
||||
/* Populate the version field. */
|
||||
version_size = 0;
|
||||
@ -1597,7 +1636,7 @@ int uv_os_uname(uv_utsname_t* buffer) {
|
||||
version_size = sizeof(buffer->version) - version_size;
|
||||
r = uv__copy_utf16_to_utf8(os_info.szCSDVersion,
|
||||
-1,
|
||||
buffer->version +
|
||||
buffer->version +
|
||||
sizeof(buffer->version) - version_size,
|
||||
&version_size);
|
||||
if (r)
|
||||
|
||||
@ -36,9 +36,6 @@ sNtQueryDirectoryFile pNtQueryDirectoryFile;
|
||||
sNtQuerySystemInformation pNtQuerySystemInformation;
|
||||
sNtQueryInformationProcess pNtQueryInformationProcess;
|
||||
|
||||
/* Kernel32 function pointers */
|
||||
sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
|
||||
|
||||
/* Powrprof.dll function pointer */
|
||||
sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification;
|
||||
|
||||
@ -48,12 +45,15 @@ sSetWinEventHook pSetWinEventHook;
|
||||
/* ws2_32.dll function pointer */
|
||||
uv_sGetHostNameW pGetHostNameW;
|
||||
|
||||
/* api-ms-win-core-file-l2-1-4.dll function pointer */
|
||||
sGetFileInformationByName pGetFileInformationByName;
|
||||
|
||||
void uv__winapi_init(void) {
|
||||
HMODULE ntdll_module;
|
||||
HMODULE powrprof_module;
|
||||
HMODULE user32_module;
|
||||
HMODULE kernel32_module;
|
||||
HMODULE ws2_32_module;
|
||||
HMODULE api_win_core_file_module;
|
||||
|
||||
ntdll_module = GetModuleHandleA("ntdll.dll");
|
||||
if (ntdll_module == NULL) {
|
||||
@ -99,7 +99,7 @@ void uv__winapi_init(void) {
|
||||
|
||||
pNtQueryDirectoryFile = (sNtQueryDirectoryFile)
|
||||
GetProcAddress(ntdll_module, "NtQueryDirectoryFile");
|
||||
if (pNtQueryVolumeInformationFile == NULL) {
|
||||
if (pNtQueryDirectoryFile == NULL) {
|
||||
uv_fatal_error(GetLastError(), "GetProcAddress");
|
||||
}
|
||||
|
||||
@ -117,15 +117,6 @@ void uv__winapi_init(void) {
|
||||
uv_fatal_error(GetLastError(), "GetProcAddress");
|
||||
}
|
||||
|
||||
kernel32_module = GetModuleHandleA("kernel32.dll");
|
||||
if (kernel32_module == NULL) {
|
||||
uv_fatal_error(GetLastError(), "GetModuleHandleA");
|
||||
}
|
||||
|
||||
pGetQueuedCompletionStatusEx = (sGetQueuedCompletionStatusEx) GetProcAddress(
|
||||
kernel32_module,
|
||||
"GetQueuedCompletionStatusEx");
|
||||
|
||||
powrprof_module = LoadLibraryExA("powrprof.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
if (powrprof_module != NULL) {
|
||||
pPowerRegisterSuspendResumeNotification = (sPowerRegisterSuspendResumeNotification)
|
||||
@ -144,4 +135,10 @@ void uv__winapi_init(void) {
|
||||
ws2_32_module,
|
||||
"GetHostNameW");
|
||||
}
|
||||
|
||||
api_win_core_file_module = GetModuleHandleA("api-ms-win-core-file-l2-1-4.dll");
|
||||
if (api_win_core_file_module != NULL) {
|
||||
pGetFileInformationByName = (sGetFileInformationByName)GetProcAddress(
|
||||
api_win_core_file_module, "GetFileInformationByName");
|
||||
}
|
||||
}
|
||||
|
||||
168
src/win/winapi.h
168
src/win/winapi.h
@ -4125,41 +4125,61 @@ typedef const UNICODE_STRING *PCUNICODE_STRING;
|
||||
# define DEVICE_TYPE DWORD
|
||||
#endif
|
||||
|
||||
/* MinGW already has a definition for REPARSE_DATA_BUFFER, but mingw-w64 does
|
||||
* not.
|
||||
*/
|
||||
#if defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR)
|
||||
typedef struct _REPARSE_DATA_BUFFER {
|
||||
ULONG ReparseTag;
|
||||
USHORT ReparseDataLength;
|
||||
USHORT Reserved;
|
||||
union {
|
||||
struct {
|
||||
USHORT SubstituteNameOffset;
|
||||
USHORT SubstituteNameLength;
|
||||
USHORT PrintNameOffset;
|
||||
USHORT PrintNameLength;
|
||||
ULONG Flags;
|
||||
WCHAR PathBuffer[1];
|
||||
} SymbolicLinkReparseBuffer;
|
||||
struct {
|
||||
USHORT SubstituteNameOffset;
|
||||
USHORT SubstituteNameLength;
|
||||
USHORT PrintNameOffset;
|
||||
USHORT PrintNameLength;
|
||||
WCHAR PathBuffer[1];
|
||||
} MountPointReparseBuffer;
|
||||
struct {
|
||||
UCHAR DataBuffer[1];
|
||||
} GenericReparseBuffer;
|
||||
struct {
|
||||
ULONG StringCount;
|
||||
WCHAR StringList[1];
|
||||
} AppExecLinkReparseBuffer;
|
||||
};
|
||||
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
|
||||
#ifndef NTDDI_WIN11_ZN
|
||||
# define NTDDI_WIN11_ZN 0x0A00000E
|
||||
#endif
|
||||
|
||||
/* API is defined in newer SDKS */
|
||||
#if (NTDDI_VERSION < NTDDI_WIN11_ZN)
|
||||
typedef struct _FILE_STAT_BASIC_INFORMATION {
|
||||
LARGE_INTEGER FileId;
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
LARGE_INTEGER AllocationSize;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
ULONG FileAttributes;
|
||||
ULONG ReparseTag;
|
||||
ULONG NumberOfLinks;
|
||||
ULONG DeviceType;
|
||||
ULONG DeviceCharacteristics;
|
||||
ULONG Reserved;
|
||||
LARGE_INTEGER VolumeSerialNumber;
|
||||
FILE_ID_128 FileId128;
|
||||
} FILE_STAT_BASIC_INFORMATION;
|
||||
#endif
|
||||
|
||||
typedef struct _REPARSE_DATA_BUFFER {
|
||||
ULONG ReparseTag;
|
||||
USHORT ReparseDataLength;
|
||||
USHORT Reserved;
|
||||
union {
|
||||
struct {
|
||||
USHORT SubstituteNameOffset;
|
||||
USHORT SubstituteNameLength;
|
||||
USHORT PrintNameOffset;
|
||||
USHORT PrintNameLength;
|
||||
ULONG Flags;
|
||||
WCHAR PathBuffer[1];
|
||||
} SymbolicLinkReparseBuffer;
|
||||
struct {
|
||||
USHORT SubstituteNameOffset;
|
||||
USHORT SubstituteNameLength;
|
||||
USHORT PrintNameOffset;
|
||||
USHORT PrintNameLength;
|
||||
WCHAR PathBuffer[1];
|
||||
} MountPointReparseBuffer;
|
||||
struct {
|
||||
UCHAR DataBuffer[1];
|
||||
} GenericReparseBuffer;
|
||||
struct {
|
||||
ULONG StringCount;
|
||||
WCHAR StringList[1];
|
||||
} AppExecLinkReparseBuffer;
|
||||
};
|
||||
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
|
||||
|
||||
typedef struct _IO_STATUS_BLOCK {
|
||||
union {
|
||||
NTSTATUS Status;
|
||||
@ -4224,6 +4244,15 @@ typedef enum _FILE_INFORMATION_CLASS {
|
||||
FileNumaNodeInformation,
|
||||
FileStandardLinkInformation,
|
||||
FileRemoteProtocolInformation,
|
||||
FileRenameInformationBypassAccessCheck,
|
||||
FileLinkInformationBypassAccessCheck,
|
||||
FileVolumeNameInformation,
|
||||
FileIdInformation,
|
||||
FileIdExtdDirectoryInformation,
|
||||
FileReplaceCompletionInformation,
|
||||
FileHardLinkFullIdInformation,
|
||||
FileIdExtdBothDirectoryInformation,
|
||||
FileDispositionInformationEx, /* based on https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ne-wdm-_file_information_class */
|
||||
FileMaximumInformation
|
||||
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
|
||||
|
||||
@ -4258,6 +4287,22 @@ typedef struct _FILE_BOTH_DIR_INFORMATION {
|
||||
WCHAR FileName[1];
|
||||
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;
|
||||
|
||||
typedef struct _FILE_ID_FULL_DIR_INFORMATION {
|
||||
ULONG NextEntryOffset;
|
||||
ULONG FileIndex;
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
LARGE_INTEGER AllocationSize;
|
||||
ULONG FileAttributes;
|
||||
ULONG FileNameLength;
|
||||
ULONG EaSize;
|
||||
LARGE_INTEGER FileId;
|
||||
WCHAR FileName[1];
|
||||
} FILE_ID_FULL_DIR_INFORMATION, *PFILE_ID_FULL_DIR_INFORMATION;
|
||||
|
||||
typedef struct _FILE_BASIC_INFORMATION {
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
@ -4323,6 +4368,10 @@ typedef struct _FILE_DISPOSITION_INFORMATION {
|
||||
BOOLEAN DeleteFile;
|
||||
} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION;
|
||||
|
||||
typedef struct _FILE_DISPOSITION_INFORMATION_EX {
|
||||
DWORD Flags;
|
||||
} FILE_DISPOSITION_INFORMATION_EX, *PFILE_DISPOSITION_INFORMATION_EX;
|
||||
|
||||
typedef struct _FILE_PIPE_LOCAL_INFORMATION {
|
||||
ULONG NamedPipeType;
|
||||
ULONG NamedPipeConfiguration;
|
||||
@ -4427,6 +4476,14 @@ typedef struct _FILE_FS_SECTOR_SIZE_INFORMATION {
|
||||
ULONG ByteOffsetForPartitionAlignment;
|
||||
} FILE_FS_SECTOR_SIZE_INFORMATION, *PFILE_FS_SECTOR_SIZE_INFORMATION;
|
||||
|
||||
typedef struct _PROCESS_BASIC_INFORMATION {
|
||||
PVOID Reserved1;
|
||||
PVOID PebBaseAddress;
|
||||
PVOID Reserved2[2];
|
||||
ULONG_PTR UniqueProcessId;
|
||||
ULONG_PTR InheritedFromUniqueProcessId;
|
||||
} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION;
|
||||
|
||||
typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION {
|
||||
LARGE_INTEGER IdleTime;
|
||||
LARGE_INTEGER KernelTime;
|
||||
@ -4440,6 +4497,10 @@ typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION {
|
||||
# define SystemProcessorPerformanceInformation 8
|
||||
#endif
|
||||
|
||||
#ifndef ProcessBasicInformation
|
||||
# define ProcessBasicInformation 0
|
||||
#endif
|
||||
|
||||
#ifndef ProcessConsoleHostProcess
|
||||
# define ProcessConsoleHostProcess 49
|
||||
#endif
|
||||
@ -4611,15 +4672,6 @@ typedef NTSTATUS (NTAPI *sNtQueryInformationProcess)
|
||||
# define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1
|
||||
#endif
|
||||
|
||||
#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
|
||||
typedef struct _OVERLAPPED_ENTRY {
|
||||
ULONG_PTR lpCompletionKey;
|
||||
LPOVERLAPPED lpOverlapped;
|
||||
ULONG_PTR Internal;
|
||||
DWORD dwNumberOfBytesTransferred;
|
||||
} OVERLAPPED_ENTRY, *LPOVERLAPPED_ENTRY;
|
||||
#endif
|
||||
|
||||
/* from wincon.h */
|
||||
#ifndef ENABLE_INSERT_MODE
|
||||
# define ENABLE_INSERT_MODE 0x20
|
||||
@ -4666,14 +4718,6 @@ typedef NTSTATUS (NTAPI *sNtQueryInformationProcess)
|
||||
# define ERROR_MUI_FILE_NOT_LOADED 15105
|
||||
#endif
|
||||
|
||||
typedef BOOL (WINAPI *sGetQueuedCompletionStatusEx)
|
||||
(HANDLE CompletionPort,
|
||||
LPOVERLAPPED_ENTRY lpCompletionPortEntries,
|
||||
ULONG ulCount,
|
||||
PULONG ulNumEntriesRemoved,
|
||||
DWORD dwMilliseconds,
|
||||
BOOL fAlertable);
|
||||
|
||||
/* from powerbase.h */
|
||||
#ifndef DEVICE_NOTIFY_CALLBACK
|
||||
# define DEVICE_NOTIFY_CALLBACK 2
|
||||
@ -4739,6 +4783,24 @@ typedef struct _TCP_INITIAL_RTO_PARAMETERS {
|
||||
# define SIO_TCP_INITIAL_RTO _WSAIOW(IOC_VENDOR,17)
|
||||
#endif
|
||||
|
||||
/* from winnt.h */
|
||||
/* API is defined in newer SDKS */
|
||||
#if (NTDDI_VERSION < NTDDI_WIN11_ZN)
|
||||
typedef enum _FILE_INFO_BY_NAME_CLASS {
|
||||
FileStatByNameInfo,
|
||||
FileStatLxByNameInfo,
|
||||
FileCaseSensitiveByNameInfo,
|
||||
FileStatBasicByNameInfo,
|
||||
MaximumFileInfoByNameClass
|
||||
} FILE_INFO_BY_NAME_CLASS;
|
||||
#endif
|
||||
|
||||
typedef BOOL(WINAPI* sGetFileInformationByName)(
|
||||
PCWSTR FileName,
|
||||
FILE_INFO_BY_NAME_CLASS FileInformationClass,
|
||||
PVOID FileInfoBuffer,
|
||||
ULONG FileInfoBufferSize);
|
||||
|
||||
/* Ntdll function pointers */
|
||||
extern sRtlGetVersion pRtlGetVersion;
|
||||
extern sRtlNtStatusToDosError pRtlNtStatusToDosError;
|
||||
@ -4750,15 +4812,15 @@ extern sNtQueryDirectoryFile pNtQueryDirectoryFile;
|
||||
extern sNtQuerySystemInformation pNtQuerySystemInformation;
|
||||
extern sNtQueryInformationProcess pNtQueryInformationProcess;
|
||||
|
||||
/* Kernel32 function pointers */
|
||||
extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
|
||||
|
||||
/* Powrprof.dll function pointer */
|
||||
extern sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification;
|
||||
|
||||
/* User32.dll function pointer */
|
||||
extern sSetWinEventHook pSetWinEventHook;
|
||||
|
||||
/* api-ms-win-core-file-l2-1-4.dll function pointers */
|
||||
extern sGetFileInformationByName pGetFileInformationByName;
|
||||
|
||||
/* ws2_32.dll function pointer */
|
||||
/* mingw doesn't have this definition, so let's declare it here locally */
|
||||
typedef int (WINAPI *uv_sGetHostNameW)
|
||||
|
||||
@ -154,47 +154,6 @@ typedef struct _AFD_RECV_INFO {
|
||||
#define IOCTL_AFD_POLL \
|
||||
_AFD_CONTROL_CODE(AFD_POLL, METHOD_BUFFERED)
|
||||
|
||||
#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
|
||||
typedef struct _IP_ADAPTER_UNICAST_ADDRESS_XP {
|
||||
/* FIXME: __C89_NAMELESS was removed */
|
||||
/* __C89_NAMELESS */ union {
|
||||
ULONGLONG Alignment;
|
||||
/* __C89_NAMELESS */ struct {
|
||||
ULONG Length;
|
||||
DWORD Flags;
|
||||
};
|
||||
};
|
||||
struct _IP_ADAPTER_UNICAST_ADDRESS_XP *Next;
|
||||
SOCKET_ADDRESS Address;
|
||||
IP_PREFIX_ORIGIN PrefixOrigin;
|
||||
IP_SUFFIX_ORIGIN SuffixOrigin;
|
||||
IP_DAD_STATE DadState;
|
||||
ULONG ValidLifetime;
|
||||
ULONG PreferredLifetime;
|
||||
ULONG LeaseLifetime;
|
||||
} IP_ADAPTER_UNICAST_ADDRESS_XP,*PIP_ADAPTER_UNICAST_ADDRESS_XP;
|
||||
|
||||
typedef struct _IP_ADAPTER_UNICAST_ADDRESS_LH {
|
||||
union {
|
||||
ULONGLONG Alignment;
|
||||
struct {
|
||||
ULONG Length;
|
||||
DWORD Flags;
|
||||
};
|
||||
};
|
||||
struct _IP_ADAPTER_UNICAST_ADDRESS_LH *Next;
|
||||
SOCKET_ADDRESS Address;
|
||||
IP_PREFIX_ORIGIN PrefixOrigin;
|
||||
IP_SUFFIX_ORIGIN SuffixOrigin;
|
||||
IP_DAD_STATE DadState;
|
||||
ULONG ValidLifetime;
|
||||
ULONG PreferredLifetime;
|
||||
ULONG LeaseLifetime;
|
||||
UINT8 OnLinkPrefixLength;
|
||||
} IP_ADAPTER_UNICAST_ADDRESS_LH,*PIP_ADAPTER_UNICAST_ADDRESS_LH;
|
||||
|
||||
#endif
|
||||
|
||||
int uv__convert_to_localhost_if_unspecified(const struct sockaddr* addr,
|
||||
struct sockaddr_storage* storage);
|
||||
|
||||
|
||||
@ -27,6 +27,11 @@
|
||||
#include "task.h"
|
||||
#include "uv.h"
|
||||
|
||||
/* Refs: https://github.com/libuv/libuv/issues/4369 */
|
||||
#if defined(__ANDROID__)
|
||||
#include <android/fdsan.h>
|
||||
#endif
|
||||
|
||||
char executable_path[sizeof(executable_path)];
|
||||
|
||||
|
||||
@ -142,6 +147,13 @@ void log_tap_result(int test_count,
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void enable_fdsan(void) {
|
||||
/* Refs: https://github.com/libuv/libuv/issues/4369 */
|
||||
#if defined(__ANDROID__)
|
||||
android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ALWAYS);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int run_test(const char* test,
|
||||
int benchmark_output,
|
||||
@ -160,6 +172,8 @@ int run_test(const char* test,
|
||||
main_proc = NULL;
|
||||
process_count = 0;
|
||||
|
||||
enable_fdsan();
|
||||
|
||||
#ifndef _WIN32
|
||||
/* Clean up stale socket from previous run. */
|
||||
remove(TEST_PIPENAME);
|
||||
|
||||
@ -53,6 +53,10 @@
|
||||
# define TEST_PIPENAME "\\\\.\\pipe\\uv-test"
|
||||
# define TEST_PIPENAME_2 "\\\\.\\pipe\\uv-test2"
|
||||
# define TEST_PIPENAME_3 "\\\\.\\pipe\\uv-test3"
|
||||
#elif __ANDROID__
|
||||
# define TEST_PIPENAME "/data/local/tmp/uv-test-sock"
|
||||
# define TEST_PIPENAME_2 "/data/local/tmp/uv-test-sock2"
|
||||
# define TEST_PIPENAME_3 "/data/local/tmp/uv-test-sock3"
|
||||
#else
|
||||
# define TEST_PIPENAME "/tmp/uv-test-sock"
|
||||
# define TEST_PIPENAME_2 "/tmp/uv-test-sock2"
|
||||
|
||||
@ -75,6 +75,14 @@ TEST_IMPL(emfile) {
|
||||
ASSERT_EQ(errno, EMFILE);
|
||||
close(maxfd);
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
/* Android connect syscall requires an extra file descriptor
|
||||
*
|
||||
* It fails in uv__tcp_connect
|
||||
* */
|
||||
close(maxfd - 1);
|
||||
#endif
|
||||
|
||||
/* Now connect and use up the last available file descriptor. The EMFILE
|
||||
* handling logic in src/unix/stream.c should ensure that connect_cb() runs
|
||||
* whereas connection_cb() should *not* run.
|
||||
|
||||
@ -64,7 +64,7 @@ TEST_IMPL(error_message) {
|
||||
|
||||
TEST_IMPL(sys_error) {
|
||||
#if defined(_WIN32)
|
||||
ASSERT_EQ(uv_translate_sys_error(ERROR_NOACCESS), UV_EACCES);
|
||||
ASSERT_EQ(uv_translate_sys_error(ERROR_NOACCESS), UV_EFAULT);
|
||||
ASSERT_EQ(uv_translate_sys_error(ERROR_ELEVATION_REQUIRED), UV_EACCES);
|
||||
ASSERT_EQ(uv_translate_sys_error(WSAEADDRINUSE), UV_EADDRINUSE);
|
||||
ASSERT_EQ(uv_translate_sys_error(ERROR_BAD_PIPE), UV_EPIPE);
|
||||
|
||||
@ -46,6 +46,8 @@ static void handle_result(uv_fs_t* req) {
|
||||
uv_fs_t stat_req;
|
||||
uint64_t size;
|
||||
uint64_t mode;
|
||||
uint64_t uid;
|
||||
uint64_t gid;
|
||||
int r;
|
||||
|
||||
ASSERT_EQ(req->fs_type, UV_FS_COPYFILE);
|
||||
@ -56,11 +58,15 @@ static void handle_result(uv_fs_t* req) {
|
||||
ASSERT_OK(r);
|
||||
size = stat_req.statbuf.st_size;
|
||||
mode = stat_req.statbuf.st_mode;
|
||||
uid = stat_req.statbuf.st_uid;
|
||||
gid = stat_req.statbuf.st_gid;
|
||||
uv_fs_req_cleanup(&stat_req);
|
||||
r = uv_fs_stat(NULL, &stat_req, dst, NULL);
|
||||
ASSERT_OK(r);
|
||||
ASSERT_EQ(stat_req.statbuf.st_size, size);
|
||||
ASSERT_EQ(stat_req.statbuf.st_mode, mode);
|
||||
ASSERT_EQ(stat_req.statbuf.st_uid, uid);
|
||||
ASSERT_EQ(stat_req.statbuf.st_gid, gid);
|
||||
uv_fs_req_cleanup(&stat_req);
|
||||
uv_fs_req_cleanup(req);
|
||||
result_check_count++;
|
||||
|
||||
@ -29,16 +29,6 @@
|
||||
# include <AvailabilityMacros.h>
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_KQUEUE
|
||||
# if defined(__APPLE__) || \
|
||||
defined(__DragonFly__) || \
|
||||
defined(__FreeBSD__) || \
|
||||
defined(__OpenBSD__) || \
|
||||
defined(__NetBSD__)
|
||||
# define HAVE_KQUEUE 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static uv_fs_event_t fs_event;
|
||||
static const char file_prefix[] = "fsevent-";
|
||||
static const int fs_event_file_count = 16;
|
||||
@ -91,6 +81,22 @@ static void create_file(const char* name) {
|
||||
uv_fs_req_cleanup(&req);
|
||||
}
|
||||
|
||||
static int delete_dir(const char* name) {
|
||||
int r;
|
||||
uv_fs_t req;
|
||||
r = uv_fs_rmdir(NULL, &req, name, NULL);
|
||||
uv_fs_req_cleanup(&req);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int delete_file(const char* name) {
|
||||
int r;
|
||||
uv_fs_t req;
|
||||
r = uv_fs_unlink(NULL, &req, name, NULL);
|
||||
uv_fs_req_cleanup(&req);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void touch_file(const char* name) {
|
||||
int r;
|
||||
uv_file file;
|
||||
@ -139,6 +145,26 @@ static void fs_event_cb_dir(uv_fs_event_t* handle, const char* filename,
|
||||
uv_close((uv_handle_t*)handle, close_cb);
|
||||
}
|
||||
|
||||
static void fs_event_cb_del_dir(uv_fs_event_t* handle,
|
||||
const char* filename,
|
||||
int events,
|
||||
int status) {
|
||||
++fs_event_cb_called;
|
||||
ASSERT_PTR_EQ(handle, &fs_event);
|
||||
ASSERT_OK(status);
|
||||
ASSERT(events == UV_CHANGE || events == UV_RENAME);
|
||||
/* There is a bug in the FreeBSD kernel where the filename is sometimes NULL.
|
||||
* Refs: https://github.com/libuv/libuv/issues/4606
|
||||
*/
|
||||
#if defined(__FreeBSD__)
|
||||
ASSERT(filename == NULL || strcmp(filename, "watch_del_dir") == 0);
|
||||
#else
|
||||
ASSERT_OK(strcmp(filename, "watch_del_dir"));
|
||||
#endif
|
||||
ASSERT_OK(uv_fs_event_stop(handle));
|
||||
uv_close((uv_handle_t*)handle, close_cb);
|
||||
}
|
||||
|
||||
static const char* fs_event_get_filename(int i) {
|
||||
snprintf(fs_event_filename,
|
||||
sizeof(fs_event_filename),
|
||||
@ -162,6 +188,15 @@ static void fs_event_create_files(uv_timer_t* handle) {
|
||||
}
|
||||
}
|
||||
|
||||
static void fs_event_del_dir(uv_timer_t* handle) {
|
||||
int r;
|
||||
|
||||
r = delete_dir("watch_del_dir");
|
||||
ASSERT_OK(r);
|
||||
|
||||
uv_close((uv_handle_t*)handle, close_cb);
|
||||
}
|
||||
|
||||
static void fs_event_unlink_files(uv_timer_t* handle) {
|
||||
int r;
|
||||
int i;
|
||||
@ -170,7 +205,7 @@ static void fs_event_unlink_files(uv_timer_t* handle) {
|
||||
if (handle == NULL) {
|
||||
/* Unlink all files */
|
||||
for (i = 0; i < 16; i++) {
|
||||
r = remove(fs_event_get_filename(i));
|
||||
r = delete_file(fs_event_get_filename(i));
|
||||
if (handle != NULL)
|
||||
ASSERT_OK(r);
|
||||
}
|
||||
@ -179,7 +214,7 @@ static void fs_event_unlink_files(uv_timer_t* handle) {
|
||||
ASSERT_LT(fs_event_removed, fs_event_file_count);
|
||||
|
||||
/* Remove the file */
|
||||
ASSERT_OK(remove(fs_event_get_filename(fs_event_removed)));
|
||||
ASSERT_OK(delete_file(fs_event_get_filename(fs_event_removed)));
|
||||
|
||||
if (++fs_event_removed < fs_event_file_count) {
|
||||
/* Remove another file on a different event loop tick. We do it this way
|
||||
@ -197,12 +232,13 @@ static void fs_event_cb_dir_multi_file(uv_fs_event_t* handle,
|
||||
ASSERT_PTR_EQ(handle, &fs_event);
|
||||
ASSERT_OK(status);
|
||||
ASSERT(events == UV_CHANGE || events == UV_RENAME);
|
||||
#if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
|
||||
ASSERT_OK(strncmp(filename, file_prefix, sizeof(file_prefix) - 1));
|
||||
#else
|
||||
ASSERT_NE(filename == NULL ||
|
||||
strncmp(filename, file_prefix, sizeof(file_prefix) - 1) == 0, 0);
|
||||
#endif
|
||||
#if defined(__APPLE__) || defined(_WIN32) || defined(__linux__)
|
||||
ASSERT_NOT_NULL(filename);
|
||||
ASSERT_MEM_EQ(filename, file_prefix, sizeof(file_prefix) - 1);
|
||||
#else
|
||||
if (filename != NULL)
|
||||
ASSERT_MEM_EQ(filename, file_prefix, sizeof(file_prefix) - 1);
|
||||
#endif
|
||||
|
||||
if (fs_event_created + fs_event_removed == fs_event_file_count) {
|
||||
/* Once we've processed all create events, delete all files */
|
||||
@ -246,7 +282,7 @@ static void fs_event_unlink_files_in_subdir(uv_timer_t* handle) {
|
||||
if (handle == NULL) {
|
||||
/* Unlink all files */
|
||||
for (i = 0; i < 16; i++) {
|
||||
r = remove(fs_event_get_filename_in_subdir(i));
|
||||
r = delete_file(fs_event_get_filename_in_subdir(i));
|
||||
if (handle != NULL)
|
||||
ASSERT_OK(r);
|
||||
}
|
||||
@ -255,7 +291,7 @@ static void fs_event_unlink_files_in_subdir(uv_timer_t* handle) {
|
||||
ASSERT_LT(fs_event_removed, fs_event_file_count);
|
||||
|
||||
/* Remove the file */
|
||||
ASSERT_OK(remove(fs_event_get_filename_in_subdir(fs_event_removed)));
|
||||
ASSERT_OK(delete_file(fs_event_get_filename_in_subdir(fs_event_removed)));
|
||||
|
||||
if (++fs_event_removed < fs_event_file_count) {
|
||||
/* Remove another file on a different event loop tick. We do it this way
|
||||
@ -416,9 +452,9 @@ TEST_IMPL(fs_event_watch_dir) {
|
||||
|
||||
/* Setup */
|
||||
fs_event_unlink_files(NULL);
|
||||
remove("watch_dir/file2");
|
||||
remove("watch_dir/file1");
|
||||
remove("watch_dir/");
|
||||
delete_file("watch_dir/file2");
|
||||
delete_file("watch_dir/file1");
|
||||
delete_dir("watch_dir/");
|
||||
create_dir("watch_dir");
|
||||
|
||||
r = uv_fs_event_init(loop, &fs_event);
|
||||
@ -437,9 +473,47 @@ TEST_IMPL(fs_event_watch_dir) {
|
||||
|
||||
/* Cleanup */
|
||||
fs_event_unlink_files(NULL);
|
||||
remove("watch_dir/file2");
|
||||
remove("watch_dir/file1");
|
||||
remove("watch_dir/");
|
||||
delete_file("watch_dir/file2");
|
||||
delete_file("watch_dir/file1");
|
||||
delete_dir("watch_dir/");
|
||||
|
||||
MAKE_VALGRIND_HAPPY(loop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST_IMPL(fs_event_watch_delete_dir) {
|
||||
#if defined(NO_FS_EVENTS)
|
||||
RETURN_SKIP(NO_FS_EVENTS);
|
||||
#elif defined(__MVS__)
|
||||
RETURN_SKIP("Directory watching not supported on this platform.");
|
||||
#elif defined(__APPLE__) && defined(__TSAN__)
|
||||
RETURN_SKIP("Times out under TSAN.");
|
||||
#endif
|
||||
|
||||
uv_loop_t* loop = uv_default_loop();
|
||||
int r;
|
||||
|
||||
/* Setup */
|
||||
fs_event_unlink_files(NULL);
|
||||
delete_dir("watch_del_dir/");
|
||||
create_dir("watch_del_dir");
|
||||
|
||||
r = uv_fs_event_init(loop, &fs_event);
|
||||
ASSERT_OK(r);
|
||||
r = uv_fs_event_start(&fs_event, fs_event_cb_del_dir, "watch_del_dir", 0);
|
||||
ASSERT_OK(r);
|
||||
r = uv_timer_init(loop, &timer);
|
||||
ASSERT_OK(r);
|
||||
r = uv_timer_start(&timer, fs_event_del_dir, 100, 0);
|
||||
ASSERT_OK(r);
|
||||
|
||||
uv_run(loop, UV_RUN_DEFAULT);
|
||||
|
||||
ASSERT_EQ(1, fs_event_cb_called);
|
||||
ASSERT_EQ(2, close_cb_called);
|
||||
|
||||
/* Cleanup */
|
||||
fs_event_unlink_files(NULL);
|
||||
|
||||
MAKE_VALGRIND_HAPPY(loop);
|
||||
return 0;
|
||||
@ -457,10 +531,10 @@ TEST_IMPL(fs_event_watch_dir_recursive) {
|
||||
/* Setup */
|
||||
loop = uv_default_loop();
|
||||
fs_event_unlink_files(NULL);
|
||||
remove("watch_dir/file2");
|
||||
remove("watch_dir/file1");
|
||||
remove("watch_dir/subdir");
|
||||
remove("watch_dir/");
|
||||
delete_file("watch_dir/file2");
|
||||
delete_file("watch_dir/file1");
|
||||
delete_dir("watch_dir/subdir");
|
||||
delete_dir("watch_dir/");
|
||||
create_dir("watch_dir");
|
||||
create_dir("watch_dir/subdir");
|
||||
|
||||
@ -500,10 +574,10 @@ TEST_IMPL(fs_event_watch_dir_recursive) {
|
||||
|
||||
/* Cleanup */
|
||||
fs_event_unlink_files_in_subdir(NULL);
|
||||
remove("watch_dir/file2");
|
||||
remove("watch_dir/file1");
|
||||
remove("watch_dir/subdir");
|
||||
remove("watch_dir/");
|
||||
delete_file("watch_dir/file2");
|
||||
delete_file("watch_dir/file1");
|
||||
delete_dir("watch_dir/subdir");
|
||||
delete_dir("watch_dir/");
|
||||
|
||||
MAKE_VALGRIND_HAPPY(loop);
|
||||
return 0;
|
||||
@ -521,8 +595,8 @@ TEST_IMPL(fs_event_watch_dir_short_path) {
|
||||
|
||||
/* Setup */
|
||||
loop = uv_default_loop();
|
||||
remove("watch_dir/file1");
|
||||
remove("watch_dir/");
|
||||
delete_file("watch_dir/file1");
|
||||
delete_dir("watch_dir/");
|
||||
create_dir("watch_dir");
|
||||
create_file("watch_dir/file1");
|
||||
|
||||
@ -549,8 +623,8 @@ TEST_IMPL(fs_event_watch_dir_short_path) {
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
remove("watch_dir/file1");
|
||||
remove("watch_dir/");
|
||||
delete_file("watch_dir/file1");
|
||||
delete_dir("watch_dir/");
|
||||
|
||||
MAKE_VALGRIND_HAPPY(loop);
|
||||
|
||||
@ -571,9 +645,9 @@ TEST_IMPL(fs_event_watch_file) {
|
||||
int r;
|
||||
|
||||
/* Setup */
|
||||
remove("watch_dir/file2");
|
||||
remove("watch_dir/file1");
|
||||
remove("watch_dir/");
|
||||
delete_file("watch_dir/file2");
|
||||
delete_file("watch_dir/file1");
|
||||
delete_dir("watch_dir/");
|
||||
create_dir("watch_dir");
|
||||
create_file("watch_dir/file1");
|
||||
create_file("watch_dir/file2");
|
||||
@ -594,9 +668,9 @@ TEST_IMPL(fs_event_watch_file) {
|
||||
ASSERT_EQ(2, close_cb_called);
|
||||
|
||||
/* Cleanup */
|
||||
remove("watch_dir/file2");
|
||||
remove("watch_dir/file1");
|
||||
remove("watch_dir/");
|
||||
delete_file("watch_dir/file2");
|
||||
delete_file("watch_dir/file1");
|
||||
delete_dir("watch_dir/");
|
||||
|
||||
MAKE_VALGRIND_HAPPY(loop);
|
||||
return 0;
|
||||
@ -618,9 +692,9 @@ TEST_IMPL(fs_event_watch_file_exact_path) {
|
||||
loop = uv_default_loop();
|
||||
|
||||
/* Setup */
|
||||
remove("watch_dir/file.js");
|
||||
remove("watch_dir/file.jsx");
|
||||
remove("watch_dir/");
|
||||
delete_file("watch_dir/file.js");
|
||||
delete_file("watch_dir/file.jsx");
|
||||
delete_dir("watch_dir/");
|
||||
create_dir("watch_dir");
|
||||
create_file("watch_dir/file.js");
|
||||
create_file("watch_dir/file.jsx");
|
||||
@ -647,9 +721,9 @@ TEST_IMPL(fs_event_watch_file_exact_path) {
|
||||
ASSERT_EQ(2, timer_cb_exact_called);
|
||||
|
||||
/* Cleanup */
|
||||
remove("watch_dir/file.js");
|
||||
remove("watch_dir/file.jsx");
|
||||
remove("watch_dir/");
|
||||
delete_file("watch_dir/file.js");
|
||||
delete_file("watch_dir/file.jsx");
|
||||
delete_dir("watch_dir/");
|
||||
|
||||
MAKE_VALGRIND_HAPPY(loop);
|
||||
return 0;
|
||||
@ -690,7 +764,7 @@ TEST_IMPL(fs_event_watch_file_current_dir) {
|
||||
loop = uv_default_loop();
|
||||
|
||||
/* Setup */
|
||||
remove("watch_file");
|
||||
delete_file("watch_file");
|
||||
create_file("watch_file");
|
||||
#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_12)
|
||||
/* Empirically, kevent seems to (sometimes) report the preceding
|
||||
@ -728,7 +802,7 @@ TEST_IMPL(fs_event_watch_file_current_dir) {
|
||||
ASSERT_EQ(1, close_cb_called);
|
||||
|
||||
/* Cleanup */
|
||||
remove("watch_file");
|
||||
delete_file("watch_file");
|
||||
|
||||
MAKE_VALGRIND_HAPPY(loop);
|
||||
return 0;
|
||||
@ -770,8 +844,8 @@ TEST_IMPL(fs_event_no_callback_after_close) {
|
||||
int r;
|
||||
|
||||
/* Setup */
|
||||
remove("watch_dir/file1");
|
||||
remove("watch_dir/");
|
||||
delete_file("watch_dir/file1");
|
||||
delete_dir("watch_dir/");
|
||||
create_dir("watch_dir");
|
||||
create_file("watch_dir/file1");
|
||||
|
||||
@ -792,8 +866,8 @@ TEST_IMPL(fs_event_no_callback_after_close) {
|
||||
ASSERT_EQ(1, close_cb_called);
|
||||
|
||||
/* Cleanup */
|
||||
remove("watch_dir/file1");
|
||||
remove("watch_dir/");
|
||||
delete_file("watch_dir/file1");
|
||||
delete_dir("watch_dir/");
|
||||
|
||||
MAKE_VALGRIND_HAPPY(loop);
|
||||
return 0;
|
||||
@ -808,8 +882,8 @@ TEST_IMPL(fs_event_no_callback_on_close) {
|
||||
int r;
|
||||
|
||||
/* Setup */
|
||||
remove("watch_dir/file1");
|
||||
remove("watch_dir/");
|
||||
delete_file("watch_dir/file1");
|
||||
delete_dir("watch_dir/");
|
||||
create_dir("watch_dir");
|
||||
create_file("watch_dir/file1");
|
||||
|
||||
@ -829,8 +903,8 @@ TEST_IMPL(fs_event_no_callback_on_close) {
|
||||
ASSERT_EQ(1, close_cb_called);
|
||||
|
||||
/* Cleanup */
|
||||
remove("watch_dir/file1");
|
||||
remove("watch_dir/");
|
||||
delete_file("watch_dir/file1");
|
||||
delete_dir("watch_dir/");
|
||||
|
||||
MAKE_VALGRIND_HAPPY(loop);
|
||||
return 0;
|
||||
@ -902,8 +976,8 @@ TEST_IMPL(fs_event_close_with_pending_event) {
|
||||
ASSERT_EQ(1, close_cb_called);
|
||||
|
||||
/* Clean up */
|
||||
remove("watch_dir/file");
|
||||
remove("watch_dir/");
|
||||
delete_file("watch_dir/file");
|
||||
delete_dir("watch_dir/");
|
||||
|
||||
MAKE_VALGRIND_HAPPY(loop);
|
||||
return 0;
|
||||
@ -927,7 +1001,7 @@ TEST_IMPL(fs_event_close_with_pending_delete_event) {
|
||||
ASSERT_OK(r);
|
||||
|
||||
/* Generate an fs event. */
|
||||
remove("watch_dir/file");
|
||||
delete_file("watch_dir/file");
|
||||
|
||||
/* Allow time for the remove event to propagate to the pending list. */
|
||||
/* XXX - perhaps just for __sun? */
|
||||
@ -941,7 +1015,7 @@ TEST_IMPL(fs_event_close_with_pending_delete_event) {
|
||||
ASSERT_EQ(1, close_cb_called);
|
||||
|
||||
/* Clean up */
|
||||
remove("watch_dir/");
|
||||
delete_dir("watch_dir/");
|
||||
|
||||
MAKE_VALGRIND_HAPPY(loop);
|
||||
return 0;
|
||||
@ -984,7 +1058,7 @@ TEST_IMPL(fs_event_close_in_callback) {
|
||||
|
||||
/* Clean up */
|
||||
fs_event_unlink_files(NULL);
|
||||
remove("watch_dir/");
|
||||
delete_dir("watch_dir/");
|
||||
|
||||
MAKE_VALGRIND_HAPPY(loop);
|
||||
return 0;
|
||||
@ -1020,7 +1094,7 @@ TEST_IMPL(fs_event_start_and_close) {
|
||||
|
||||
ASSERT_EQ(2, close_cb_called);
|
||||
|
||||
remove("watch_dir/");
|
||||
delete_dir("watch_dir/");
|
||||
MAKE_VALGRIND_HAPPY(loop);
|
||||
return 0;
|
||||
}
|
||||
@ -1054,7 +1128,7 @@ TEST_IMPL(fs_event_getpath) {
|
||||
ASSERT_EQ(r, UV_EINVAL);
|
||||
r = uv_fs_event_start(&fs_event, fail_cb, watch_dir[i], 0);
|
||||
ASSERT_OK(r);
|
||||
len = 0;
|
||||
len = 1;
|
||||
r = uv_fs_event_getpath(&fs_event, buf, &len);
|
||||
ASSERT_EQ(r, UV_ENOBUFS);
|
||||
ASSERT_LT(len, sizeof buf); /* sanity check */
|
||||
@ -1073,111 +1147,11 @@ TEST_IMPL(fs_event_getpath) {
|
||||
close_cb_called = 0;
|
||||
}
|
||||
|
||||
remove("watch_dir/");
|
||||
delete_dir("watch_dir/");
|
||||
MAKE_VALGRIND_HAPPY(loop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
||||
static int fs_event_error_reported;
|
||||
|
||||
static void fs_event_error_report_cb(uv_fs_event_t* handle,
|
||||
const char* filename,
|
||||
int events,
|
||||
int status) {
|
||||
if (status != 0)
|
||||
fs_event_error_reported = status;
|
||||
}
|
||||
|
||||
static void timer_cb_nop(uv_timer_t* handle) {
|
||||
++timer_cb_called;
|
||||
uv_close((uv_handle_t*) handle, close_cb);
|
||||
}
|
||||
|
||||
static void fs_event_error_report_close_cb(uv_handle_t* handle) {
|
||||
ASSERT_NOT_NULL(handle);
|
||||
close_cb_called++;
|
||||
|
||||
/* handle is allocated on-stack, no need to free it */
|
||||
}
|
||||
|
||||
|
||||
TEST_IMPL(fs_event_error_reporting) {
|
||||
unsigned int i;
|
||||
uv_loop_t loops[1024];
|
||||
uv_fs_event_t events[ARRAY_SIZE(loops)];
|
||||
uv_loop_t* loop;
|
||||
uv_fs_event_t* event;
|
||||
|
||||
TEST_FILE_LIMIT(ARRAY_SIZE(loops) * 3);
|
||||
|
||||
remove("watch_dir/");
|
||||
create_dir("watch_dir");
|
||||
|
||||
/* Create a lot of loops, and start FSEventStream in each of them.
|
||||
* Eventually, this should create enough streams to make FSEventStreamStart()
|
||||
* fail.
|
||||
*/
|
||||
for (i = 0; i < ARRAY_SIZE(loops); i++) {
|
||||
loop = &loops[i];
|
||||
ASSERT_OK(uv_loop_init(loop));
|
||||
event = &events[i];
|
||||
|
||||
timer_cb_called = 0;
|
||||
close_cb_called = 0;
|
||||
ASSERT_OK(uv_fs_event_init(loop, event));
|
||||
ASSERT_OK(uv_fs_event_start(event,
|
||||
fs_event_error_report_cb,
|
||||
"watch_dir",
|
||||
0));
|
||||
uv_unref((uv_handle_t*) event);
|
||||
|
||||
/* Let loop run for some time */
|
||||
ASSERT_OK(uv_timer_init(loop, &timer));
|
||||
ASSERT_OK(uv_timer_start(&timer, timer_cb_nop, 2, 0));
|
||||
uv_run(loop, UV_RUN_DEFAULT);
|
||||
ASSERT_EQ(1, timer_cb_called);
|
||||
ASSERT_EQ(1, close_cb_called);
|
||||
if (fs_event_error_reported != 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/* At least one loop should fail */
|
||||
ASSERT_EQ(fs_event_error_reported, UV_EMFILE);
|
||||
|
||||
/* Stop and close all events, and destroy loops */
|
||||
do {
|
||||
loop = &loops[i];
|
||||
event = &events[i];
|
||||
|
||||
ASSERT_OK(uv_fs_event_stop(event));
|
||||
uv_ref((uv_handle_t*) event);
|
||||
uv_close((uv_handle_t*) event, fs_event_error_report_close_cb);
|
||||
|
||||
close_cb_called = 0;
|
||||
uv_run(loop, UV_RUN_DEFAULT);
|
||||
ASSERT_EQ(1, close_cb_called);
|
||||
|
||||
uv_loop_close(loop);
|
||||
} while (i-- != 0);
|
||||
|
||||
remove("watch_dir/");
|
||||
MAKE_VALGRIND_HAPPY(uv_default_loop());
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else /* !defined(__APPLE__) */
|
||||
|
||||
TEST_IMPL(fs_event_error_reporting) {
|
||||
/* No-op, needed only for FSEvents backend */
|
||||
|
||||
MAKE_VALGRIND_HAPPY(uv_default_loop());
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* defined(__APPLE__) */
|
||||
|
||||
TEST_IMPL(fs_event_watch_invalid_path) {
|
||||
#if defined(NO_FS_EVENTS)
|
||||
RETURN_SKIP(NO_FS_EVENTS);
|
||||
@ -1216,7 +1190,7 @@ TEST_IMPL(fs_event_stop_in_cb) {
|
||||
RETURN_SKIP(NO_FS_EVENTS);
|
||||
#endif
|
||||
|
||||
remove(path);
|
||||
delete_file(path);
|
||||
create_file(path);
|
||||
|
||||
ASSERT_OK(uv_fs_event_init(uv_default_loop(), &fs));
|
||||
@ -1239,7 +1213,7 @@ TEST_IMPL(fs_event_stop_in_cb) {
|
||||
ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT));
|
||||
ASSERT_EQ(1, fs_event_cb_stop_calls);
|
||||
|
||||
remove(path);
|
||||
delete_file(path);
|
||||
|
||||
MAKE_VALGRIND_HAPPY(uv_default_loop());
|
||||
return 0;
|
||||
|
||||
@ -29,6 +29,7 @@ static uv_fs_t readdir_req;
|
||||
static uv_fs_t closedir_req;
|
||||
|
||||
static uv_dirent_t dirents[1];
|
||||
static uv_dirent_t symlink_dirents[2];
|
||||
|
||||
static int empty_opendir_cb_count;
|
||||
static int empty_closedir_cb_count;
|
||||
@ -460,3 +461,88 @@ TEST_IMPL(fs_readdir_non_empty_dir) {
|
||||
MAKE_VALGRIND_HAPPY(uv_default_loop());
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void readdir_symlink_readdir_cb(uv_fs_t* req) {
|
||||
uv_dir_t* dir;
|
||||
|
||||
ASSERT_PTR_EQ(req, &readdir_req);
|
||||
ASSERT_EQ(req->fs_type, UV_FS_READDIR);
|
||||
dir = req->ptr;
|
||||
|
||||
if (req->result == 0) {
|
||||
uv_fs_req_cleanup(req);
|
||||
ASSERT_EQ(3, non_empty_readdir_cb_count);
|
||||
uv_fs_closedir(uv_default_loop(),
|
||||
&closedir_req,
|
||||
dir,
|
||||
non_empty_closedir_cb);
|
||||
} else {
|
||||
if (strcmp(symlink_dirents[0].name, "test_symlink") == 0) {
|
||||
ASSERT_EQ(symlink_dirents[0].type, UV_DIRENT_LINK);
|
||||
} else {
|
||||
ASSERT_EQ(symlink_dirents[1].type, UV_DIRENT_LINK);
|
||||
}
|
||||
uv_fs_req_cleanup(req);
|
||||
}
|
||||
}
|
||||
|
||||
static void readdir_symlink_opendir_cb(uv_fs_t* req) {
|
||||
uv_dir_t* dir;
|
||||
int r;
|
||||
|
||||
ASSERT_PTR_EQ(req, &opendir_req);
|
||||
ASSERT_EQ(req->fs_type, UV_FS_OPENDIR);
|
||||
ASSERT_OK(req->result);
|
||||
ASSERT_NOT_NULL(req->ptr);
|
||||
|
||||
dir = req->ptr;
|
||||
dir->dirents = symlink_dirents;
|
||||
dir->nentries = ARRAY_SIZE(symlink_dirents);
|
||||
|
||||
r = uv_fs_readdir(uv_default_loop(),
|
||||
&readdir_req,
|
||||
dir,
|
||||
readdir_symlink_readdir_cb);
|
||||
ASSERT_OK(r);
|
||||
uv_fs_req_cleanup(req);
|
||||
}
|
||||
|
||||
static void cleanup_symlink_test_files(void) {
|
||||
uv_fs_t req;
|
||||
|
||||
uv_fs_rmdir(NULL, &req, "test_symlink_dir/test_subdir", NULL);
|
||||
uv_fs_req_cleanup(&req);
|
||||
uv_fs_unlink(NULL, &req, "test_symlink_dir/test_symlink", NULL);
|
||||
uv_fs_req_cleanup(&req);
|
||||
uv_fs_rmdir(NULL, &req, "test_symlink_dir", NULL);
|
||||
uv_fs_req_cleanup(&req);
|
||||
}
|
||||
|
||||
TEST_IMPL(fs_readdir_symlink) {
|
||||
|
||||
uv_fs_t mkdir_req;
|
||||
uv_fs_t symlink_req;
|
||||
int r;
|
||||
|
||||
cleanup_symlink_test_files();
|
||||
|
||||
r = uv_fs_mkdir(uv_default_loop(), &mkdir_req, "test_symlink_dir", 0755, NULL);
|
||||
ASSERT_OK(r);
|
||||
|
||||
r = uv_fs_mkdir(uv_default_loop(), &mkdir_req, "test_symlink_dir/test_subdir", 0755, NULL);
|
||||
ASSERT_OK(r);
|
||||
|
||||
r = uv_fs_symlink(uv_default_loop(), &symlink_req, "test_symlink_dir/test_subdir", "test_symlink_dir/test_symlink", UV_FS_SYMLINK_DIR, NULL);
|
||||
ASSERT_OK(r);
|
||||
|
||||
r = uv_fs_opendir(uv_default_loop(), &opendir_req, "test_symlink_dir", readdir_symlink_opendir_cb);
|
||||
ASSERT_OK(r);
|
||||
|
||||
r = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
|
||||
ASSERT_OK(r);
|
||||
|
||||
cleanup_symlink_test_files();
|
||||
|
||||
MAKE_VALGRIND_HAPPY(uv_default_loop());
|
||||
return 0;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user