Compare commits
239 Commits
| 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 | ||
|
|
6912038d72 | ||
|
|
2c15345016 | ||
|
|
ff9587991f | ||
|
|
625d3d275a | ||
|
|
a7c44d6748 | ||
|
|
e8458b2402 | ||
|
|
6b56200cc8 | ||
|
|
cc9e96147f | ||
|
|
507f3046d1 | ||
|
|
7b9e37c7da | ||
|
|
009d7414bc | ||
|
|
08a1e7fd23 | ||
|
|
99e576612d | ||
|
|
e9f29cb984 | ||
|
|
c858a14764 | ||
|
|
e0327e1d50 | ||
|
|
3530bcc303 | ||
|
|
0f2d7e784a | ||
|
|
bb6fbcf6e7 | ||
|
|
10f313631c | ||
|
|
129362f356 | ||
|
|
3f7191e5c2 | ||
|
|
535efdf319 | ||
|
|
f98516ddd5 | ||
|
|
3b6a1a14ca | ||
|
|
a7cbda92b6 | ||
|
|
a407b232f0 | ||
|
|
160cd5629e | ||
|
|
7d092913b3 | ||
|
|
a9381cdb03 | ||
|
|
e72a91e063 | ||
|
|
64bd28f5ba | ||
|
|
1dd0ab1315 | ||
|
|
8861a97efa | ||
|
|
51a22f60d6 | ||
|
|
8a499e1331 | ||
|
|
34db4c21b1 | ||
|
|
1479b76310 | ||
|
|
a7d5255122 | ||
|
|
12bd89bbc3 | ||
|
|
5e302730cd | ||
|
|
a5c01d4de3 | ||
|
|
de43f42735 | ||
|
|
fc70430b09 | ||
|
|
7ba94d3909 | ||
|
|
bfbe4e38d7 | ||
|
|
4785ad6337 | ||
|
|
6be130e1b8 | ||
|
|
f144429365 | ||
|
|
35da5ded3b | ||
|
|
b9421d7066 | ||
|
|
54d8364c24 | ||
|
|
d843b7cf7f | ||
|
|
e135dfe183 | ||
|
|
31e4b90c3c | ||
|
|
874363f652 | ||
|
|
f01219dfb7 | ||
|
|
f067f50ae4 | ||
|
|
4107b8d4db | ||
|
|
815693f715 | ||
|
|
97b7873cba |
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"
|
||||||
23
.github/stale.yml
vendored
23
.github/stale.yml
vendored
@ -1,23 +0,0 @@
|
|||||||
# Number of days of inactivity before an issue becomes stale
|
|
||||||
daysUntilStale: 28
|
|
||||||
# Number of days of inactivity before a stale issue is closed
|
|
||||||
# Set to false to disable. If disabled, issues still need to be closed
|
|
||||||
# manually, but will remain marked as stale.
|
|
||||||
daysUntilClose: false
|
|
||||||
# Issues with these labels will never be considered stale
|
|
||||||
exemptLabels:
|
|
||||||
- v2
|
|
||||||
- enhancement
|
|
||||||
- good first issue
|
|
||||||
- feature-request
|
|
||||||
- doc
|
|
||||||
- bug
|
|
||||||
- not-stale
|
|
||||||
# Label to use when marking an issue as stale
|
|
||||||
staleLabel: stale
|
|
||||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
|
||||||
markComment: >
|
|
||||||
This issue has been automatically marked as stale because it has not had
|
|
||||||
recent activity. Thank you for your contributions.
|
|
||||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
|
||||||
closeComment: false
|
|
||||||
4
.github/workflows/CI-docs.yml
vendored
4
.github/workflows/CI-docs.yml
vendored
@ -11,8 +11,8 @@ jobs:
|
|||||||
docs-src:
|
docs-src:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.9'
|
python-version: '3.9'
|
||||||
cache: 'pip' # caching pip dependencies
|
cache: 'pip' # caching pip dependencies
|
||||||
|
|||||||
3
.github/workflows/CI-sample.yml
vendored
3
.github/workflows/CI-sample.yml
vendored
@ -6,6 +6,7 @@ on:
|
|||||||
- '**'
|
- '**'
|
||||||
- '!docs/**'
|
- '!docs/**'
|
||||||
- '!.**'
|
- '!.**'
|
||||||
|
- 'docs/code/**'
|
||||||
- '.github/workflows/CI-sample.yml'
|
- '.github/workflows/CI-sample.yml'
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
@ -20,7 +21,7 @@ jobs:
|
|||||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: setup
|
- name: setup
|
||||||
run: cmake -E make_directory ${{runner.workspace}}/libuv/docs/code/build
|
run: cmake -E make_directory ${{runner.workspace}}/libuv/docs/code/build
|
||||||
- name: configure
|
- name: configure
|
||||||
|
|||||||
124
.github/workflows/CI-unix.yml
vendored
124
.github/workflows/CI-unix.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
|||||||
build-linux:
|
build-linux:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: configure
|
- name: configure
|
||||||
run: |
|
run: |
|
||||||
./autogen.sh
|
./autogen.sh
|
||||||
@ -29,28 +29,67 @@ jobs:
|
|||||||
|
|
||||||
build-android:
|
build-android:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container: reactnativecommunity/react-native-android:2020-5-20
|
env:
|
||||||
|
ANDROID_AVD_HOME: /root/.android/avd
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Envinfo
|
- name: Envinfo
|
||||||
run: npx envinfo
|
run: npx envinfo
|
||||||
- name: Configure android arm64
|
- name: Enable KVM
|
||||||
# see build options you can use in https://developer.android.com/ndk/guides/cmake
|
|
||||||
run: |
|
run: |
|
||||||
mkdir build
|
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||||
cd build
|
sudo udevadm control --reload-rules
|
||||||
$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 ..
|
sudo udevadm trigger --name-match=kvm
|
||||||
- name: Build android arm64
|
- name: Build and Test
|
||||||
run: |
|
uses: reactivecircus/android-emulator-runner@v2
|
||||||
$ANDROID_HOME/cmake/3.10.2.4988404/bin/cmake --build build
|
with:
|
||||||
ls -lh build
|
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:
|
build-macos:
|
||||||
runs-on: macos-11
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [macos-13, macos-14]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Envinfo
|
- name: Envinfo
|
||||||
run: npx 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
|
- name: Setup
|
||||||
run: |
|
run: |
|
||||||
brew install ninja automake libtool
|
brew install ninja automake libtool
|
||||||
@ -81,9 +120,13 @@ jobs:
|
|||||||
make -C build-auto -j4
|
make -C build-auto -j4
|
||||||
|
|
||||||
build-ios:
|
build-ios:
|
||||||
runs-on: macos-11
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [macos-13, macos-14]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: |
|
run: |
|
||||||
mkdir build-ios
|
mkdir build-ios
|
||||||
@ -95,45 +138,36 @@ jobs:
|
|||||||
ls -lh build-ios
|
ls -lh build-ios
|
||||||
|
|
||||||
build-cross-qemu:
|
build-cross-qemu:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
name: build-cross-qemu-${{ matrix.config.target }}
|
name: build-cross-qemu-${{ matrix.config.target }}
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
config:
|
config:
|
||||||
- {target: arm, toolchain: gcc-arm-linux-gnueabi, cc: arm-linux-gnueabi-gcc, qemu: qemu-arm-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-static }
|
- {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-static }
|
- {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-static }
|
- {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-static }
|
- {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-static }
|
- {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-static }
|
- {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-static }
|
- {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-static }
|
- {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-static }
|
- {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-static }
|
- {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-static }
|
- {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-static}
|
- {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-static}
|
- {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-static}
|
- {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-static}
|
- {target: ppc64 (u64 slots), toolchain: gcc-powerpc64-linux-gnu, cc: powerpc64-linux-gnu-gcc, qemu: qemu-ppc64 }
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Install QEMU
|
- name: Install qemu and ${{ matrix.config.toolchain }}
|
||||||
# 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 }}
|
|
||||||
run: |
|
run: |
|
||||||
sudo apt update
|
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 }}
|
- name: Configure with ${{ matrix.config.cc }}
|
||||||
run: |
|
run: |
|
||||||
mkdir build
|
mkdir build
|
||||||
|
|||||||
32
.github/workflows/CI-win.yml
vendored
32
.github/workflows/CI-win.yml
vendored
@ -29,30 +29,32 @@ jobs:
|
|||||||
- {toolchain: Visual Studio 17 2022, arch: x64, server: 2022, config: UBSAN}
|
- {toolchain: Visual Studio 17 2022, arch: x64, server: 2022, config: UBSAN}
|
||||||
- {toolchain: Visual Studio 17 2022, arch: arm64, server: 2022}
|
- {toolchain: Visual Studio 17 2022, arch: arm64, server: 2022}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Envinfo
|
|
||||||
run: npx envinfo
|
|
||||||
- name: Build
|
- name: Build
|
||||||
shell: cmd
|
|
||||||
run:
|
run:
|
||||||
cmake -S . -B build -DBUILD_TESTING=ON
|
cmake -S . -B build -DBUILD_TESTING=ON
|
||||||
-G "${{ matrix.config.toolchain }}" -A ${{ matrix.config.arch }}
|
-G "${{ matrix.config.toolchain }}" -A ${{ matrix.config.arch }}
|
||||||
${{ matrix.config.config == 'ASAN' && '-DASAN=on -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded' ||
|
${{ matrix.config.config == 'ASAN' && '-DASAN=on -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded' || '' }}
|
||||||
matrix.config.config == 'UBSAN' && '-DUBSAN=on' || '' }}
|
|
||||||
|
|
||||||
cmake --build build --config RelWithDebInfo
|
cmake --build build --config RelWithDebInfo
|
||||||
|
|
||||||
|
${{ matrix.config.config == 'ASAN' && 'Copy-Item -Path "build\\*.exe" -Destination "build\\RelWithDebInfo\\"' || '' }}
|
||||||
|
|
||||||
|
${{ matrix.config.config == 'ASAN' && 'Copy-Item -Path "build\\*.dll" -Destination "build\\RelWithDebInfo\\"' || '' }}
|
||||||
|
|
||||||
ls -l build
|
ls -l build
|
||||||
- name: platform_output
|
|
||||||
if: ${{ matrix.config.arch != 'arm64' }}
|
ls -l build\\RelWithDebInfo
|
||||||
shell: cmd
|
|
||||||
run:
|
|
||||||
build\\RelWithDebInfo\\uv_run_tests.exe platform_output
|
|
||||||
- name: platform_output_a
|
- name: platform_output_a
|
||||||
if: ${{ matrix.config.arch != 'arm64' }}
|
if: ${{ matrix.config.arch != 'arm64' }}
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run:
|
run:
|
||||||
build\\RelWithDebInfo\\uv_run_tests_a.exe platform_output
|
build\\RelWithDebInfo\\uv_run_tests_a.exe platform_output
|
||||||
|
- name: platform_output
|
||||||
|
if: ${{ matrix.config.arch != 'arm64' }}
|
||||||
|
shell: cmd
|
||||||
|
run:
|
||||||
|
build\\RelWithDebInfo\\uv_run_tests.exe platform_output
|
||||||
- name: Test
|
- name: Test
|
||||||
# only valid with libuv-master with the fix for
|
# only valid with libuv-master with the fix for
|
||||||
# https://github.com/libuv/leps/blob/master/005-windows-handles-not-fd.md
|
# https://github.com/libuv/leps/blob/master/005-windows-handles-not-fd.md
|
||||||
@ -78,7 +80,7 @@ jobs:
|
|||||||
- {arch: i686, server: 2022, libgcc: dw2 }
|
- {arch: i686, server: 2022, libgcc: dw2 }
|
||||||
- {arch: x86_64, server: 2022, libgcc: seh }
|
- {arch: x86_64, server: 2022, libgcc: seh }
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- name: Install mingw32 environment
|
- name: Install mingw32 environment
|
||||||
run: |
|
run: |
|
||||||
sudo apt update
|
sudo apt update
|
||||||
@ -90,13 +92,13 @@ jobs:
|
|||||||
cmake --install build --prefix "`pwd`/build/usr"
|
cmake --install build --prefix "`pwd`/build/usr"
|
||||||
mkdir -p build/usr/test build/usr/bin
|
mkdir -p build/usr/test build/usr/bin
|
||||||
cp -av test/fixtures build/usr/test
|
cp -av test/fixtures build/usr/test
|
||||||
cp -av build/uv_run_tests_a.exe build/uv_run_tests.exe \
|
cp -av build/uv_run_tests_a.exe build/uv_run_tests.exe build/uv_run_tests_a_no_ext build/uv_run_tests_no_ext \
|
||||||
`${{ matrix.config.arch }}-w64-mingw32-gcc -print-file-name=libgcc_s_${{ matrix.config.libgcc }}-1.dll` \
|
`${{ matrix.config.arch }}-w64-mingw32-gcc -print-file-name=libgcc_s_${{ matrix.config.libgcc }}-1.dll` \
|
||||||
`${{ matrix.config.arch }}-w64-mingw32-gcc -print-file-name=libwinpthread-1.dll` \
|
`${{ matrix.config.arch }}-w64-mingw32-gcc -print-file-name=libwinpthread-1.dll` \
|
||||||
`${{ matrix.config.arch }}-w64-mingw32-gcc -print-file-name=libatomic-1.dll` \
|
`${{ matrix.config.arch }}-w64-mingw32-gcc -print-file-name=libatomic-1.dll` \
|
||||||
build/usr/bin
|
build/usr/bin
|
||||||
- name: Upload build artifacts
|
- name: Upload build artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: mingw-${{ matrix.config.arch }}
|
name: mingw-${{ matrix.config.arch }}
|
||||||
path: build/usr/**/*
|
path: build/usr/**/*
|
||||||
@ -114,7 +116,7 @@ jobs:
|
|||||||
- {arch: x86_64, server: 2022}
|
- {arch: x86_64, server: 2022}
|
||||||
steps:
|
steps:
|
||||||
- name: Download build artifacts
|
- name: Download build artifacts
|
||||||
uses: actions/download-artifact@v2
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: mingw-${{ matrix.config.arch }}
|
name: mingw-${{ matrix.config.arch }}
|
||||||
- name: Test
|
- name: Test
|
||||||
|
|||||||
15
.github/workflows/sanitizer.yml
vendored
15
.github/workflows/sanitizer.yml
vendored
@ -16,13 +16,18 @@ jobs:
|
|||||||
sanitizers-linux:
|
sanitizers-linux:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Setup
|
- name: Setup
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get install ninja-build
|
sudo apt-get install ninja-build
|
||||||
- name: Envinfo
|
- name: Envinfo
|
||||||
run: npx 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
|
- name: ASAN Build
|
||||||
run: |
|
run: |
|
||||||
mkdir build-asan
|
mkdir build-asan
|
||||||
@ -62,9 +67,9 @@ jobs:
|
|||||||
./build-ubsan/uv_run_tests_a
|
./build-ubsan/uv_run_tests_a
|
||||||
|
|
||||||
sanitizers-macos:
|
sanitizers-macos:
|
||||||
runs-on: macos-11
|
runs-on: macos-13
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Envinfo
|
- name: Envinfo
|
||||||
run: npx envinfo
|
run: npx envinfo
|
||||||
@ -99,14 +104,14 @@ jobs:
|
|||||||
sanitizers-windows:
|
sanitizers-windows:
|
||||||
runs-on: windows-2022
|
runs-on: windows-2022
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Setup
|
- name: Setup
|
||||||
run: |
|
run: |
|
||||||
choco install ninja
|
choco install ninja
|
||||||
|
|
||||||
# Note: clang shipped with VS2022 has an issue where the UBSAN runtime doesn't link.
|
# Note: clang shipped with VS2022 has an issue where the UBSAN runtime doesn't link.
|
||||||
- name: Install LLVM and Clang
|
- name: Install LLVM and Clang
|
||||||
uses: KyleMayes/install-llvm-action@v1
|
uses: KyleMayes/install-llvm-action@v2
|
||||||
with:
|
with:
|
||||||
version: "17"
|
version: "17"
|
||||||
|
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@ -7,9 +7,11 @@
|
|||||||
*.sdf
|
*.sdf
|
||||||
*.suo
|
*.suo
|
||||||
.vs/
|
.vs/
|
||||||
|
.vscode/
|
||||||
*.VC.db
|
*.VC.db
|
||||||
*.VC.opendb
|
*.VC.opendb
|
||||||
core
|
core
|
||||||
|
.cache
|
||||||
vgcore.*
|
vgcore.*
|
||||||
.buildstamp
|
.buildstamp
|
||||||
.dirstamp
|
.dirstamp
|
||||||
@ -74,3 +76,5 @@ cmake-build-debug/
|
|||||||
|
|
||||||
# make dist output
|
# make dist output
|
||||||
libuv-*.tar.*
|
libuv-*.tar.*
|
||||||
|
/dist.libuv.org/
|
||||||
|
/libuv-release-tool/
|
||||||
|
|||||||
6
.mailmap
6
.mailmap
@ -4,6 +4,7 @@ Aaron Bieber <qbit@deftly.net> <deftly@gmail.com>
|
|||||||
Alan Gutierrez <alan@prettyrobots.com> <alan@blogometer.com>
|
Alan Gutierrez <alan@prettyrobots.com> <alan@blogometer.com>
|
||||||
Andrius Bentkus <andrius.bentkus@gmail.com> <toxedvirus@gmail.com>
|
Andrius Bentkus <andrius.bentkus@gmail.com> <toxedvirus@gmail.com>
|
||||||
Andy Fiddaman <andy@omniosce.org> <omnios@citrus-it.co.uk>
|
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> <i@bertbelder.com>
|
||||||
Bert Belder <bertbelder@gmail.com> <info@2bs.nl>
|
Bert Belder <bertbelder@gmail.com> <info@2bs.nl>
|
||||||
Bert Belder <bertbelder@gmail.com> <user@ChrUbuntu.(none)>
|
Bert Belder <bertbelder@gmail.com> <user@ChrUbuntu.(none)>
|
||||||
@ -18,6 +19,7 @@ David Carlier <devnexen@gmail.com>
|
|||||||
Devchandra Meetei Leishangthem <dlmeetei@gmail.com>
|
Devchandra Meetei Leishangthem <dlmeetei@gmail.com>
|
||||||
Fedor Indutny <fedor.indutny@gmail.com> <fedor@indutny.com>
|
Fedor Indutny <fedor.indutny@gmail.com> <fedor@indutny.com>
|
||||||
Frank Denis <github@pureftpd.org>
|
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>
|
Imran Iqbal <imrani@ca.ibm.com> <imran@imraniqbal.org>
|
||||||
Isaac Z. Schlueter <i@izs.me>
|
Isaac Z. Schlueter <i@izs.me>
|
||||||
Jason Williams <necmon@yahoo.com>
|
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>
|
Michael Penick <michael.penick@datastax.com> <mpenick@users.noreply.github.com>
|
||||||
Nicholas Vavilov <vvnicholas@gmail.com>
|
Nicholas Vavilov <vvnicholas@gmail.com>
|
||||||
Nick Logan <ugexe@cpan.org> <nlogan@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>
|
||||||
Rasmus Christian Pedersen <zerhacken@yahoo.com> <ruysch@outlook.com>
|
Rasmus Christian Pedersen <zerhacken@yahoo.com> <ruysch@outlook.com>
|
||||||
Richard Lau <rlau@redhat.com> <riclau@uk.ibm.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>
|
Sam Roberts <vieuxtech@gmail.com> <sam@strongloop.com>
|
||||||
San-Tai Hsu <vanilla@fatpipi.com>
|
San-Tai Hsu <vanilla@fatpipi.com>
|
||||||
Santiago Gimeno <santiago.gimeno@quantion.es> <santiago.gimeno@gmail.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>
|
Saúl Ibarra Corretgé <saghul@gmail.com> <s@saghul.net>
|
||||||
Shigeki Ohtsu <ohtsu@iij.ad.jp> <ohtsu@ohtsu.org>
|
Shigeki Ohtsu <ohtsu@iij.ad.jp> <ohtsu@ohtsu.org>
|
||||||
Shuowang (Wayne) Zhang <shuowang.zhang@ibm.com>
|
Shuowang (Wayne) Zhang <shuowang.zhang@ibm.com>
|
||||||
|
|||||||
@ -2,7 +2,7 @@ version: 2
|
|||||||
|
|
||||||
sphinx:
|
sphinx:
|
||||||
builder: html
|
builder: html
|
||||||
configuration: null
|
configuration: docs/src/conf.py
|
||||||
fail_on_warning: false
|
fail_on_warning: false
|
||||||
|
|
||||||
build:
|
build:
|
||||||
|
|||||||
32
AUTHORS
32
AUTHORS
@ -560,3 +560,35 @@ prubel <paul@rubels.net>
|
|||||||
Per Allansson <65364157+per-allansson@users.noreply.github.com>
|
Per Allansson <65364157+per-allansson@users.noreply.github.com>
|
||||||
Matheus Izvekov <mizvekov@gmail.com>
|
Matheus Izvekov <mizvekov@gmail.com>
|
||||||
Christian Heimlich <chris@pcserenity.com>
|
Christian Heimlich <chris@pcserenity.com>
|
||||||
|
Hao Hu <33607772+hhu8@users.noreply.github.com>
|
||||||
|
matoro <12038583+matoro@users.noreply.github.com>
|
||||||
|
Bo Anderson <mail@boanderson.me>
|
||||||
|
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)
|
if(POLICY CMP0091)
|
||||||
cmake_policy(SET CMP0091 NEW) # Enable MSVC_RUNTIME_LIBRARY setting
|
cmake_policy(SET CMP0091 NEW) # Enable MSVC_RUNTIME_LIBRARY setting
|
||||||
@ -81,15 +81,20 @@ if(TSAN)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(UBSAN)
|
if(UBSAN)
|
||||||
|
cmake_minimum_required(VERSION 3.13)
|
||||||
list(APPEND uv_defines __UBSAN__=1)
|
list(APPEND uv_defines __UBSAN__=1)
|
||||||
if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|GNU|Clang")
|
if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|GNU|Clang")
|
||||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined")
|
add_compile_options("-fsanitize=undefined" "-fno-sanitize-recover=undefined")
|
||||||
set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined")
|
if (NOT WIN32)
|
||||||
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined")
|
add_link_options("-fsanitize=undefined")
|
||||||
elseif(MSVC)
|
endif()
|
||||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=undefined")
|
if(MSVC)
|
||||||
|
add_compile_options("/Oy-")
|
||||||
|
else()
|
||||||
|
add_compile_options("-fno-omit-frame-pointer")
|
||||||
|
endif()
|
||||||
else()
|
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()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -161,6 +166,11 @@ list(APPEND uv_cflags ${lint-utf8-msvc} )
|
|||||||
check_c_compiler_flag(-fno-strict-aliasing UV_F_STRICT_ALIASING)
|
check_c_compiler_flag(-fno-strict-aliasing UV_F_STRICT_ALIASING)
|
||||||
list(APPEND uv_cflags $<$<BOOL:${UV_F_STRICT_ALIASING}>:-fno-strict-aliasing>)
|
list(APPEND uv_cflags $<$<BOOL:${UV_F_STRICT_ALIASING}>:-fno-strict-aliasing>)
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
# Error on calling undeclared functions.
|
||||||
|
list(APPEND uv_cflags "/we4013")
|
||||||
|
endif()
|
||||||
|
|
||||||
set(uv_sources
|
set(uv_sources
|
||||||
src/fs-poll.c
|
src/fs-poll.c
|
||||||
src/idna.c
|
src/idna.c
|
||||||
@ -176,7 +186,7 @@ set(uv_sources
|
|||||||
src/version.c)
|
src/version.c)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
list(APPEND uv_defines WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0602)
|
list(APPEND uv_defines WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0A00 _CRT_DECLARE_NONSTDC_NAMES=0)
|
||||||
list(APPEND uv_libraries
|
list(APPEND uv_libraries
|
||||||
psapi
|
psapi
|
||||||
user32
|
user32
|
||||||
@ -186,7 +196,6 @@ if(WIN32)
|
|||||||
ws2_32
|
ws2_32
|
||||||
dbghelp
|
dbghelp
|
||||||
ole32
|
ole32
|
||||||
uuid
|
|
||||||
shell32)
|
shell32)
|
||||||
list(APPEND uv_sources
|
list(APPEND uv_sources
|
||||||
src/win/async.c
|
src/win/async.c
|
||||||
@ -303,6 +312,7 @@ if(APPLE)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "GNU")
|
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_libraries dl)
|
||||||
list(APPEND uv_sources
|
list(APPEND uv_sources
|
||||||
src/unix/bsd-ifaddrs.c
|
src/unix/bsd-ifaddrs.c
|
||||||
@ -562,6 +572,7 @@ if(LIBUV_BUILD_TESTS)
|
|||||||
test/test-hrtime.c
|
test/test-hrtime.c
|
||||||
test/test-idle.c
|
test/test-idle.c
|
||||||
test/test-idna.c
|
test/test-idna.c
|
||||||
|
test/test-iouring-pollhup.c
|
||||||
test/test-ip4-addr.c
|
test/test-ip4-addr.c
|
||||||
test/test-ip6-addr.c
|
test/test-ip6-addr.c
|
||||||
test/test-ip-name.c
|
test/test-ip-name.c
|
||||||
@ -639,6 +650,7 @@ if(LIBUV_BUILD_TESTS)
|
|||||||
test/test-tcp-oob.c
|
test/test-tcp-oob.c
|
||||||
test/test-tcp-open.c
|
test/test-tcp-open.c
|
||||||
test/test-tcp-read-stop.c
|
test/test-tcp-read-stop.c
|
||||||
|
test/test-tcp-reuseport.c
|
||||||
test/test-tcp-read-stop-start.c
|
test/test-tcp-read-stop-start.c
|
||||||
test/test-tcp-rst.c
|
test/test-tcp-rst.c
|
||||||
test/test-tcp-shutdown-after-write.c
|
test/test-tcp-shutdown-after-write.c
|
||||||
@ -655,6 +667,8 @@ if(LIBUV_BUILD_TESTS)
|
|||||||
test/test-thread-affinity.c
|
test/test-thread-affinity.c
|
||||||
test/test-thread-equal.c
|
test/test-thread-equal.c
|
||||||
test/test-thread.c
|
test/test-thread.c
|
||||||
|
test/test-thread-name.c
|
||||||
|
test/test-thread-priority.c
|
||||||
test/test-threadpool-cancel.c
|
test/test-threadpool-cancel.c
|
||||||
test/test-threadpool.c
|
test/test-threadpool.c
|
||||||
test/test-timer-again.c
|
test/test-timer-again.c
|
||||||
@ -686,6 +700,7 @@ if(LIBUV_BUILD_TESTS)
|
|||||||
test/test-udp-send-unreachable.c
|
test/test-udp-send-unreachable.c
|
||||||
test/test-udp-try-send.c
|
test/test-udp-try-send.c
|
||||||
test/test-udp-recv-in-a-row.c
|
test/test-udp-recv-in-a-row.c
|
||||||
|
test/test-udp-reuseport.c
|
||||||
test/test-uname.c
|
test/test-uname.c
|
||||||
test/test-walk-handles.c
|
test/test-walk-handles.c
|
||||||
test/test-watcher-cross-stop.c)
|
test/test-watcher-cross-stop.c)
|
||||||
@ -702,6 +717,12 @@ if(LIBUV_BUILD_TESTS)
|
|||||||
set_tests_properties(uv_test PROPERTIES ENVIRONMENT
|
set_tests_properties(uv_test PROPERTIES ENVIRONMENT
|
||||||
"LIBPATH=${CMAKE_BINARY_DIR}:$ENV{LIBPATH}")
|
"LIBPATH=${CMAKE_BINARY_DIR}:$ENV{LIBPATH}")
|
||||||
endif()
|
endif()
|
||||||
|
if(WIN32)
|
||||||
|
add_custom_command(TARGET uv_run_tests POST_BUILD
|
||||||
|
COMMAND "${CMAKE_COMMAND}" -E copy
|
||||||
|
"$<TARGET_FILE:uv_run_tests>"
|
||||||
|
"$<TARGET_FILE_DIR:uv_run_tests>/uv_run_tests_no_ext")
|
||||||
|
endif()
|
||||||
add_executable(uv_run_tests_a ${uv_test_sources} uv_win_longpath.manifest)
|
add_executable(uv_run_tests_a ${uv_test_sources} uv_win_longpath.manifest)
|
||||||
target_compile_definitions(uv_run_tests_a PRIVATE ${uv_defines})
|
target_compile_definitions(uv_run_tests_a PRIVATE ${uv_defines})
|
||||||
target_compile_options(uv_run_tests_a PRIVATE ${uv_cflags})
|
target_compile_options(uv_run_tests_a PRIVATE ${uv_cflags})
|
||||||
@ -718,6 +739,12 @@ if(LIBUV_BUILD_TESTS)
|
|||||||
set_target_properties(uv_run_tests PROPERTIES LINKER_LANGUAGE CXX)
|
set_target_properties(uv_run_tests PROPERTIES LINKER_LANGUAGE CXX)
|
||||||
set_target_properties(uv_run_tests_a PROPERTIES LINKER_LANGUAGE CXX)
|
set_target_properties(uv_run_tests_a PROPERTIES LINKER_LANGUAGE CXX)
|
||||||
endif()
|
endif()
|
||||||
|
if(WIN32)
|
||||||
|
add_custom_command(TARGET uv_run_tests_a POST_BUILD
|
||||||
|
COMMAND "${CMAKE_COMMAND}" -E copy
|
||||||
|
"$<TARGET_FILE:uv_run_tests_a>"
|
||||||
|
"$<TARGET_FILE_DIR:uv_run_tests_a>/uv_run_tests_a_no_ext")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Now for some gibbering horrors from beyond the stars...
|
# Now for some gibbering horrors from beyond the stars...
|
||||||
@ -762,8 +789,22 @@ endif()
|
|||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
set(CMAKE_DEBUG_POSTFIX d)
|
set(CMAKE_DEBUG_POSTFIX d)
|
||||||
|
get_filename_component(CMAKE_C_COMPILER_DIR ${CMAKE_C_COMPILER} DIRECTORY)
|
||||||
|
if(ASAN)
|
||||||
|
file(INSTALL "${CMAKE_C_COMPILER_DIR}/llvm-symbolizer.exe" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
|
||||||
|
file(INSTALL "${CMAKE_C_COMPILER_DIR}/clang_rt.asan_dynamic-x86_64.dll" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
|
||||||
|
file(INSTALL "${CMAKE_C_COMPILER_DIR}/clang_rt.asan_dbg_dynamic-x86_64.dll" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(BUILD_SHARED_LIBS)
|
||||||
|
set(LIB_SELECTED uv)
|
||||||
|
else()
|
||||||
|
set(LIB_SELECTED uv_a)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(libuv::libuv ALIAS ${LIB_SELECTED})
|
||||||
|
|
||||||
message(STATUS "summary of build options:
|
message(STATUS "summary of build options:
|
||||||
Install prefix: ${CMAKE_INSTALL_PREFIX}
|
Install prefix: ${CMAKE_INSTALL_PREFIX}
|
||||||
Target system: ${CMAKE_SYSTEM_NAME}
|
Target system: ${CMAKE_SYSTEM_NAME}
|
||||||
|
|||||||
431
ChangeLog
431
ChangeLog
@ -1,4 +1,427 @@
|
|||||||
2023.11.06, Version 1.47.0 (Stable)
|
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:
|
||||||
|
|
||||||
|
* misc: remove deprecated stalebot file (Jameson Nash)
|
||||||
|
|
||||||
|
* build: disable windows asan buildbot (Ben Noordhuis)
|
||||||
|
|
||||||
|
* test: don't run tcp_writealot under msan (Ben Noordhuis)
|
||||||
|
|
||||||
|
* build,win: remove extraneous -lshell32 (Ben Noordhuis)
|
||||||
|
|
||||||
|
* unix: ignore ifaddrs with NULL ifa_addr (Stephen Gallagher)
|
||||||
|
|
||||||
|
* unix,win: utility for setting priority for thread (Hao Hu)
|
||||||
|
|
||||||
|
* pipe: add back error handling to connect / bind (Jameson Nash)
|
||||||
|
|
||||||
|
* test: check if ipv6 link-local traffic is routable (Ben Noordhuis)
|
||||||
|
|
||||||
|
* win: remove check for UV_PIPE_NO_TRUNCATE (Jameson Nash)
|
||||||
|
|
||||||
|
* linux: disable io_uring on hppa below kernel 6.1.51 (matoro)
|
||||||
|
|
||||||
|
* unix,win: fix read past end of pipe name buffer (Ben Noordhuis)
|
||||||
|
|
||||||
|
* unix: unbreak macOS < 10.14 (Sergey Fedorov)
|
||||||
|
|
||||||
|
* aix: disable ipv6 link local (Abdirahim Musse)
|
||||||
|
|
||||||
|
* doc: move cjihrig to emeriti (cjihrig)
|
||||||
|
|
||||||
|
* unix: correct pwritev conditional (Bo Anderson)
|
||||||
|
|
||||||
|
* test_fs.c: Fix issue on 32-bit systems using btrfs (Stephen Gallagher)
|
||||||
|
|
||||||
|
* misc: ignore libuv-release-tool files (Jameson Nash)
|
||||||
|
|
||||||
|
* win: honor NoDefaultCurrentDirectoryInExePath env var (Ardi Nugraha)
|
||||||
|
|
||||||
|
* idna: fix compilation warning (Saúl Ibarra Corretgé)
|
||||||
|
|
||||||
|
* linux: remove HAVE_IFADDRS_H macro (Ben Noordhuis)
|
||||||
|
|
||||||
|
* test: skip tcp-write-in-a-row on IBM i (Abdirahim Musse)
|
||||||
|
|
||||||
|
* build,win: work around missing uuid.dll on MinGW (Anton Bachin)
|
||||||
|
|
||||||
|
* win: stop using deprecated names (Matheus Izvekov)
|
||||||
|
|
||||||
|
* unix,win: fix busy loop with zero timeout timers (Matheus Izvekov)
|
||||||
|
|
||||||
|
* aix,ibmi: use uv_interface_addresses instead of getifaddrs (Abdirahim Musse)
|
||||||
|
|
||||||
|
* linux: fix bind/connect for abstract sockets (Santiago Gimeno)
|
||||||
|
|
||||||
|
* win: replace c99 comments with c89 comments (Trevor Flynn)
|
||||||
|
|
||||||
|
* build: add .cache clangd folder to .gitignore (Juan José Arboleda)
|
||||||
|
|
||||||
|
* unix: support full TCP keep-alive on Solaris (Andy Pan)
|
||||||
|
|
||||||
|
* freebsd: fix F_KINFO file path handling (David Carlier)
|
||||||
|
|
||||||
|
* linux: retry fs op if unsupported by io_uring (Santiago Gimeno)
|
||||||
|
|
||||||
|
* freebsd: fix build on non-intel archs (David Carlier)
|
||||||
|
|
||||||
|
* unix: optimize uv__tcp_keepalive cpp directives (Andy Pan)
|
||||||
|
|
||||||
|
* linux: disable io_uring on ppc64 and ppc64le (Brad King)
|
||||||
|
|
||||||
|
* doc: add very basic Security Policy document (Santiago Gimeno)
|
||||||
|
|
||||||
|
* build: re-enable msvc-asan job on CI (Jameson Nash)
|
||||||
|
|
||||||
|
* win/spawn: optionally run executable paths with no file extension (Brad King)
|
||||||
|
|
||||||
|
* win: fix ESRCH implementation (Jameson Nash)
|
||||||
|
|
||||||
|
* unix,win: reset the timer queue on stop (Santiago Gimeno)
|
||||||
|
|
||||||
|
* fix: always zero-terminate idna output (Ben Noordhuis)
|
||||||
|
|
||||||
|
* fix: reject zero-length idna inputs (Ben Noordhuis)
|
||||||
|
|
||||||
|
* test: empty strings are not valid IDNA (Santiago Gimeno)
|
||||||
|
|
||||||
|
* Merge pull request from GHSA-f74f-cvh7-c6q6 (Ben Noordhuis)
|
||||||
|
|
||||||
|
|
||||||
|
2023.11.06, Version 1.47.0 (Stable), be6b81a352d17513c95be153afcb3148f1a451cd
|
||||||
|
|
||||||
Changes since version 1.46.0:
|
Changes since version 1.46.0:
|
||||||
|
|
||||||
@ -820,7 +1243,7 @@ Changes since version 1.41.0:
|
|||||||
|
|
||||||
* zos: treat __rfim_utok as binary (Shuowang (Wayne) Zhang)
|
* 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)
|
* zos: use custom proctitle implementation (Shuowang (Wayne) Zhang)
|
||||||
|
|
||||||
@ -3326,7 +3749,7 @@ Changes since version 1.9.1:
|
|||||||
|
|
||||||
* zos: implement uv__io_check_fd (John Barboza)
|
* 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)
|
* win,tty: add support for ANSI codes in win10 v1511 (Imran Iqbal)
|
||||||
|
|
||||||
@ -5429,7 +5852,7 @@ Changes since version 0.11.8:
|
|||||||
is an int64_t, and no longer an int. (Bert Belder)
|
is an int64_t, and no longer an int. (Bert Belder)
|
||||||
|
|
||||||
* process: make uv_spawn() return some types of errors immediately on windows,
|
* 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)
|
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.
|
* [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
|
* [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
|
* [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
|
* [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
|
* [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
|
* [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
|
* [node.pas](https://github.com/vovach777/node.pas) NodeJS-like ecosystem
|
||||||
* Haskell
|
* Haskell
|
||||||
* [Z.Haskell](https://z.haskell.world)
|
* [Z.Haskell](https://z.haskell.world)
|
||||||
|
* C3
|
||||||
|
* [libuv.c3l](https://github.com/velikoss/libuv.c3l)
|
||||||
|
|||||||
14
Makefile.am
14
Makefile.am
@ -59,7 +59,7 @@ if WINNT
|
|||||||
uvinclude_HEADERS += include/uv/win.h include/uv/tree.h
|
uvinclude_HEADERS += include/uv/win.h include/uv/tree.h
|
||||||
AM_CPPFLAGS += -I$(top_srcdir)/src/win \
|
AM_CPPFLAGS += -I$(top_srcdir)/src/win \
|
||||||
-DWIN32_LEAN_AND_MEAN \
|
-DWIN32_LEAN_AND_MEAN \
|
||||||
-D_WIN32_WINNT=0x0602
|
-D_WIN32_WINNT=0x0A00
|
||||||
libuv_la_SOURCES += src/win/async.c \
|
libuv_la_SOURCES += src/win/async.c \
|
||||||
src/win/atomicops-inl.h \
|
src/win/atomicops-inl.h \
|
||||||
src/win/core.c \
|
src/win/core.c \
|
||||||
@ -136,6 +136,12 @@ TESTS = test/run-tests
|
|||||||
check_PROGRAMS = test/run-tests
|
check_PROGRAMS = test/run-tests
|
||||||
test_run_tests_CFLAGS = $(AM_CFLAGS)
|
test_run_tests_CFLAGS = $(AM_CFLAGS)
|
||||||
|
|
||||||
|
if WINNT
|
||||||
|
check-am: test/run-tests_no_ext
|
||||||
|
test/run-tests_no_ext: test/run-tests$(EXEEXT)
|
||||||
|
cp test/run-tests$(EXEEXT) test/run-tests_no_ext
|
||||||
|
endif
|
||||||
|
|
||||||
if SUNOS
|
if SUNOS
|
||||||
# Can't be turned into a CC_CHECK_CFLAGS in configure.ac, it makes compilers
|
# Can't be turned into a CC_CHECK_CFLAGS in configure.ac, it makes compilers
|
||||||
# on other platforms complain that the argument is unused during compilation.
|
# on other platforms complain that the argument is unused during compilation.
|
||||||
@ -192,6 +198,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
|
|||||||
test/test-hrtime.c \
|
test/test-hrtime.c \
|
||||||
test/test-idle.c \
|
test/test-idle.c \
|
||||||
test/test-idna.c \
|
test/test-idna.c \
|
||||||
|
test/test-iouring-pollhup.c \
|
||||||
test/test-ip4-addr.c \
|
test/test-ip4-addr.c \
|
||||||
test/test-ip6-addr.c \
|
test/test-ip6-addr.c \
|
||||||
test/test-ip-name.c \
|
test/test-ip-name.c \
|
||||||
@ -269,6 +276,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
|
|||||||
test/test-tcp-flags.c \
|
test/test-tcp-flags.c \
|
||||||
test/test-tcp-open.c \
|
test/test-tcp-open.c \
|
||||||
test/test-tcp-read-stop.c \
|
test/test-tcp-read-stop.c \
|
||||||
|
test/test-tcp-reuseport.c \
|
||||||
test/test-tcp-read-stop-start.c \
|
test/test-tcp-read-stop-start.c \
|
||||||
test/test-tcp-rst.c \
|
test/test-tcp-rst.c \
|
||||||
test/test-tcp-shutdown-after-write.c \
|
test/test-tcp-shutdown-after-write.c \
|
||||||
@ -286,6 +294,8 @@ test_run_tests_SOURCES = test/blackhole-server.c \
|
|||||||
test/test-thread-equal.c \
|
test/test-thread-equal.c \
|
||||||
test/test-thread.c \
|
test/test-thread.c \
|
||||||
test/test-thread-affinity.c \
|
test/test-thread-affinity.c \
|
||||||
|
test/test-thread-name.c \
|
||||||
|
test/test-thread-priority.c \
|
||||||
test/test-threadpool-cancel.c \
|
test/test-threadpool-cancel.c \
|
||||||
test/test-threadpool.c \
|
test/test-threadpool.c \
|
||||||
test/test-timer-again.c \
|
test/test-timer-again.c \
|
||||||
@ -317,6 +327,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
|
|||||||
test/test-udp-send-unreachable.c \
|
test/test-udp-send-unreachable.c \
|
||||||
test/test-udp-try-send.c \
|
test/test-udp-try-send.c \
|
||||||
test/test-udp-recv-in-a-row.c \
|
test/test-udp-recv-in-a-row.c \
|
||||||
|
test/test-udp-reuseport.c \
|
||||||
test/test-uname.c \
|
test/test-uname.c \
|
||||||
test/test-walk-handles.c \
|
test/test-walk-handles.c \
|
||||||
test/test-watcher-cross-stop.c
|
test/test-watcher-cross-stop.c
|
||||||
@ -420,6 +431,7 @@ libuv_la_CFLAGS += -D_DARWIN_UNLIMITED_SELECT=1
|
|||||||
libuv_la_SOURCES += src/unix/bsd-ifaddrs.c \
|
libuv_la_SOURCES += src/unix/bsd-ifaddrs.c \
|
||||||
src/unix/darwin-proctitle.c \
|
src/unix/darwin-proctitle.c \
|
||||||
src/unix/darwin-stub.h \
|
src/unix/darwin-stub.h \
|
||||||
|
src/unix/darwin-syscalls.h \
|
||||||
src/unix/darwin.c \
|
src/unix/darwin.c \
|
||||||
src/unix/fsevents.c \
|
src/unix/fsevents.c \
|
||||||
src/unix/kqueue.c \
|
src/unix/kqueue.c \
|
||||||
|
|||||||
12
README.md
12
README.md
@ -232,6 +232,18 @@ $ ./bootstrap-vcpkg.sh # for bash
|
|||||||
$ ./vcpkg install libuv
|
$ ./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
|
### Running tests
|
||||||
|
|
||||||
Some tests are timing sensitive. Relaxing test timeouts may be necessary
|
Some tests are timing sensitive. Relaxing test timeouts may be necessary
|
||||||
|
|||||||
27
SECURITY.md
Normal file
27
SECURITY.md
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
Currently, we are providing security updates for the latest release in the v1.x series:
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
| ------- | ------------------ |
|
||||||
|
| Latest v1.x | :white_check_mark: |
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
If you believe you have found a security vulnerability in `libuv`, please use the [GitHub's private vulnerability reporting feature](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability) in the [libuv repository](https://github.com/libuv/libuv) to report it to us.
|
||||||
|
|
||||||
|
This will allow us to assess the risk, and make a fix available before we add a bug report to the GitHub repository.
|
||||||
|
|
||||||
|
Please do:
|
||||||
|
|
||||||
|
* Provide as much information as you can about the vulnerability.
|
||||||
|
* Provide details about your configuration and environment, if applicable.
|
||||||
|
|
||||||
|
Please do not:
|
||||||
|
|
||||||
|
* Post any information about the vulnerability in public places.
|
||||||
|
* Attempt to exploit the vulnerability yourself.
|
||||||
|
|
||||||
|
We take all security bugs seriously. Thank you for improving the security of `libuv`. We appreciate your efforts and responsible disclosure and will make every effort to acknowledge your contributions.
|
||||||
@ -4,14 +4,14 @@
|
|||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| GNU/Linux | Tier 1 | Linux >= 3.10 with glibc >= 2.17 | |
|
| GNU/Linux | Tier 1 | Linux >= 3.10 with glibc >= 2.17 | |
|
||||||
| macOS | Tier 1 | macOS >= 11 | Currently supported macOS releases |
|
| 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 | |
|
| FreeBSD | Tier 2 | >= 12 | |
|
||||||
| AIX | Tier 2 | >= 6 | Maintainers: @libuv/aix |
|
| AIX | Tier 2 | >= 6 | Maintainers: @libuv/aix |
|
||||||
| IBM i | Tier 2 | >= IBM i 7.2 | Maintainers: @libuv/ibmi |
|
| IBM i | Tier 2 | >= IBM i 7.2 | Maintainers: @libuv/ibmi |
|
||||||
| z/OS | Tier 2 | >= V2R2 | Maintainers: @libuv/zos |
|
| z/OS | Tier 2 | >= V2R2 | Maintainers: @libuv/zos |
|
||||||
| Linux with musl | Tier 2 | musl >= 1.0 | |
|
| Linux with musl | Tier 2 | musl >= 1.0 | |
|
||||||
| Android | Tier 3 | NDK >= r15b | Android 7.0, `-DANDROID_PLATFORM=android-24` |
|
| Android | Tier 3 | NDK >= r15b | Android 7.0, `-DANDROID_PLATFORM=android-24` |
|
||||||
| MinGW | Tier 3 | MinGW32 and MinGW-w64 | |
|
| MinGW | Tier 3 | MinGW-w64 | |
|
||||||
| SunOS | Tier 3 | Solaris 121 and later | |
|
| SunOS | Tier 3 | Solaris 121 and later | |
|
||||||
| Other | Tier 3 | N/A | |
|
| Other | Tier 3 | N/A | |
|
||||||
|
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
|
||||||
AC_PREREQ(2.57)
|
AC_PREREQ(2.57)
|
||||||
AC_INIT([libuv], [1.47.0], [https://github.com/libuv/libuv/issues])
|
AC_INIT([libuv], [1.50.1-dev], [https://github.com/libuv/libuv/issues])
|
||||||
AC_CONFIG_MACRO_DIR([m4])
|
AC_CONFIG_MACRO_DIR([m4])
|
||||||
m4_include([m4/libuv-extra-automake-flags.m4])
|
m4_include([m4/libuv-extra-automake-flags.m4])
|
||||||
m4_include([m4/as_case.m4])
|
m4_include([m4/as_case.m4])
|
||||||
@ -74,7 +74,7 @@ AM_CONDITIONAL([OS400], [AS_CASE([$host_os],[os400], [true], [false])
|
|||||||
AM_CONDITIONAL([SUNOS], [AS_CASE([$host_os],[solaris*], [true], [false])])
|
AM_CONDITIONAL([SUNOS], [AS_CASE([$host_os],[solaris*], [true], [false])])
|
||||||
AM_CONDITIONAL([WINNT], [AS_CASE([$host_os],[mingw*], [true], [false])])
|
AM_CONDITIONAL([WINNT], [AS_CASE([$host_os],[mingw*], [true], [false])])
|
||||||
AS_CASE([$host_os],[mingw*], [
|
AS_CASE([$host_os],[mingw*], [
|
||||||
LIBS="$LIBS -lws2_32 -lpsapi -liphlpapi -lshell32 -luserenv -luser32 -ldbghelp -lole32 -luuid -lshell32"
|
LIBS="$LIBS -lws2_32 -lpsapi -liphlpapi -luserenv -luser32 -ldbghelp -lole32 -lshell32"
|
||||||
])
|
])
|
||||||
AS_CASE([$host_os], [solaris2.10], [
|
AS_CASE([$host_os], [solaris2.10], [
|
||||||
CFLAGS="$CFLAGS -DSUNOS_NO_IFADDRS"
|
CFLAGS="$CFLAGS -DSUNOS_NO_IFADDRS"
|
||||||
|
|||||||
@ -5,25 +5,27 @@
|
|||||||
|
|
||||||
uv_loop_t *loop;
|
uv_loop_t *loop;
|
||||||
uv_tty_t tty;
|
uv_tty_t tty;
|
||||||
int main() {
|
|
||||||
loop = uv_default_loop();
|
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
uv_write_t req;
|
||||||
|
uv_buf_t buf;
|
||||||
|
uv_write_t req1;
|
||||||
|
uv_buf_t buf1;
|
||||||
|
|
||||||
|
loop = uv_default_loop();
|
||||||
uv_tty_init(loop, &tty, STDOUT_FILENO, 0);
|
uv_tty_init(loop, &tty, STDOUT_FILENO, 0);
|
||||||
uv_tty_set_mode(&tty, UV_TTY_MODE_NORMAL);
|
uv_tty_set_mode(&tty, UV_TTY_MODE_NORMAL);
|
||||||
|
|
||||||
if (uv_guess_handle(1) == UV_TTY) {
|
if (uv_guess_handle(1) == UV_TTY) {
|
||||||
uv_write_t req;
|
buf1.base = "\033[41;37m";
|
||||||
uv_buf_t buf;
|
buf1.len = strlen(buf1.base);
|
||||||
buf.base = "\033[41;37m";
|
uv_write(&req1, (uv_stream_t*) &tty, &buf1, 1, NULL);
|
||||||
buf.len = strlen(buf.base);
|
|
||||||
uv_write(&req, (uv_stream_t*) &tty, &buf, 1, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uv_write_t req;
|
|
||||||
uv_buf_t buf;
|
|
||||||
buf.base = "Hello TTY\n";
|
buf.base = "Hello TTY\n";
|
||||||
buf.len = strlen(buf.base);
|
buf.len = strlen(buf.base);
|
||||||
uv_write(&req, (uv_stream_t*) &tty, &buf, 1, NULL);
|
uv_write(&req, (uv_stream_t*) &tty, &buf, 1, NULL);
|
||||||
|
|
||||||
uv_tty_reset_mode();
|
uv_tty_reset_mode();
|
||||||
return uv_run(loop, UV_RUN_DEFAULT);
|
return uv_run(loop, UV_RUN_DEFAULT);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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
|
`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
|
a (sometimes significant) increase in throughput there should be no change in
|
||||||
observable behavior. Libuv reverts to using its threadpool when the necessary
|
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::
|
.. note::
|
||||||
On Windows `uv_fs_*` functions use utf-8 encoding.
|
On Windows `uv_fs_*` functions use utf-8 encoding.
|
||||||
@ -129,10 +132,9 @@ Data types
|
|||||||
uint64_t f_spare[4];
|
uint64_t f_spare[4];
|
||||||
} uv_statfs_t;
|
} uv_statfs_t;
|
||||||
|
|
||||||
.. c:enum:: uv_dirent_t
|
.. c:enum:: uv_dirent_type_t
|
||||||
|
|
||||||
Cross platform (reduced) equivalent of ``struct dirent``.
|
Type of dirent.
|
||||||
Used in :c:func:`uv_fs_scandir_next`.
|
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
@ -147,6 +149,14 @@ Data types
|
|||||||
UV_DIRENT_BLOCK
|
UV_DIRENT_BLOCK
|
||||||
} uv_dirent_type_t;
|
} 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 {
|
typedef struct uv_dirent_s {
|
||||||
const char* name;
|
const char* name;
|
||||||
uv_dirent_type_t type;
|
uv_dirent_type_t type;
|
||||||
@ -420,6 +430,12 @@ API
|
|||||||
|
|
||||||
Equivalent to :man:`utime(2)`, :man:`futimes(3)` and :man:`lutimes(3)` respectively.
|
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::
|
.. note::
|
||||||
z/OS: `uv_fs_lutime()` is not implemented for z/OS. It can still be called but will return
|
z/OS: `uv_fs_lutime()` is not implemented for z/OS. It can still be called but will return
|
||||||
``UV_ENOSYS``.
|
``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)
|
.. 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`.
|
The resulting string is stored in `req->ptr`.
|
||||||
|
|
||||||
.. warning::
|
.. warning::
|
||||||
@ -653,7 +669,7 @@ File open constants
|
|||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
`UV_FS_O_RANDOM` is only supported on Windows via
|
`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
|
.. c:macro:: UV_FS_O_RDONLY
|
||||||
|
|
||||||
@ -670,7 +686,7 @@ File open constants
|
|||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
`UV_FS_O_SEQUENTIAL` is only supported on Windows via
|
`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
|
.. c:macro:: UV_FS_O_SHORT_LIVED
|
||||||
|
|
||||||
@ -678,7 +694,7 @@ File open constants
|
|||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
`UV_FS_O_SHORT_LIVED` is only supported on Windows via
|
`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
|
.. c:macro:: UV_FS_O_SYMLINK
|
||||||
|
|
||||||
@ -699,7 +715,7 @@ File open constants
|
|||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
`UV_FS_O_TEMPORARY` is only supported on Windows via
|
`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
|
.. 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
|
be a relative path to a file contained in the directory, or `NULL` if the
|
||||||
file name cannot be determined.
|
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.
|
Event types that :c:type:`uv_fs_event_t` handles monitor.
|
||||||
|
|
||||||
@ -58,7 +63,7 @@ Data types
|
|||||||
UV_CHANGE = 2
|
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
|
Flags that can be passed to :c:func:`uv_fs_event_start` to control its
|
||||||
behavior.
|
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)
|
.. 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
|
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
|
.. note:: Currently the only supported flag is ``UV_FS_EVENT_RECURSIVE`` and
|
||||||
only on OSX and Windows.
|
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)
|
.. 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
|
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.
|
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
|
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
|
involved, and while only snippets are included in the book, it is recommended
|
||||||
to read the full code to really understand it.
|
to read the full code to really understand it.
|
||||||
|
|||||||
@ -94,7 +94,7 @@ Public members
|
|||||||
|
|
||||||
.. c:member:: uv_handle_type uv_handle_t.type
|
.. 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
|
.. c:member:: void* uv_handle_t.data
|
||||||
|
|
||||||
@ -248,7 +248,7 @@ just for some handle types.
|
|||||||
|
|
||||||
.. versionadded:: 1.19.0
|
.. 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`.
|
Sets `handle->data` to `data`.
|
||||||
|
|
||||||
|
|||||||
@ -58,5 +58,5 @@ libuv can be downloaded from `here <https://dist.libuv.org/dist/>`_.
|
|||||||
Installation
|
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.
|
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
|
.. c:enum:: uv_run_mode
|
||||||
|
|
||||||
Mode used to run the loop with :c:func:`uv_run`.
|
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`.
|
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.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)
|
.. c:function:: int uv_loop_close(uv_loop_t* loop)
|
||||||
|
|
||||||
Releases all internal loop resources. Call this function only when the loop
|
Releases all internal loop resources. Call this function only when the loop
|
||||||
@ -238,7 +256,7 @@ API
|
|||||||
|
|
||||||
.. versionadded:: 1.19.0
|
.. 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`.
|
Sets `loop->data` to `data`.
|
||||||
|
|
||||||
|
|||||||
@ -199,6 +199,18 @@ Data types
|
|||||||
char* homedir;
|
char* homedir;
|
||||||
} uv_passwd_t;
|
} 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
|
.. c:type:: uv_utsname_t
|
||||||
|
|
||||||
Data type for operating system name and version information.
|
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.
|
On Windows not all fields are set, the unsupported fields are filled with zeroes.
|
||||||
See :c:type:`uv_rusage_t` for more details.
|
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)
|
.. c:function:: uv_pid_t uv_os_getpid(void)
|
||||||
|
|
||||||
Returns the current process ID.
|
Returns the current process ID.
|
||||||
@ -566,6 +589,35 @@ API
|
|||||||
|
|
||||||
.. versionadded:: 1.9.0
|
.. 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)
|
.. c:function:: void uv_os_free_passwd(uv_passwd_t* pwd)
|
||||||
|
|
||||||
Frees the `pwd` memory previously allocated with :c:func:`uv_os_get_passwd`.
|
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`.
|
Type definition for callback passed to :c:func:`uv_poll_start`.
|
||||||
|
|
||||||
.. c:type:: uv_poll_event
|
.. c:enum:: uv_poll_event
|
||||||
|
|
||||||
Poll event types
|
Poll event types
|
||||||
|
|
||||||
|
|||||||
@ -40,7 +40,7 @@ Data types
|
|||||||
will indicate the exit status and the signal that caused the process to
|
will indicate the exit status and the signal that caused the process to
|
||||||
terminate, if any.
|
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`.
|
Flags to be set on the flags field of :c:type:`uv_process_options_t`.
|
||||||
|
|
||||||
@ -85,7 +85,14 @@ Data types
|
|||||||
* option is only meaningful on Windows systems. On Unix it is silently
|
* option is only meaningful on Windows systems. On Unix it is silently
|
||||||
* ignored.
|
* ignored.
|
||||||
*/
|
*/
|
||||||
UV_PROCESS_WINDOWS_HIDE_GUI = (1 << 6)
|
UV_PROCESS_WINDOWS_HIDE_GUI = (1 << 6),
|
||||||
|
/*
|
||||||
|
* On Windows, if the path to the program to execute, specified in
|
||||||
|
* uv_process_options_t's file field, has a directory component,
|
||||||
|
* search for the exact file name before trying variants with
|
||||||
|
* extensions like '.exe' or '.cmd'.
|
||||||
|
*/
|
||||||
|
UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME = (1 << 7)
|
||||||
};
|
};
|
||||||
|
|
||||||
.. c:type:: uv_stdio_container_t
|
.. c:type:: uv_stdio_container_t
|
||||||
@ -183,7 +190,7 @@ Public members
|
|||||||
Command line arguments. args[0] should be the path to the program. On
|
Command line arguments. args[0] should be the path to the program. On
|
||||||
Windows this uses `CreateProcess` which concatenates the arguments into a
|
Windows this uses `CreateProcess` which concatenates the arguments into a
|
||||||
string this can cause some strange errors. See the
|
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
|
.. c:member:: char** uv_process_options_t.env
|
||||||
|
|
||||||
@ -196,7 +203,7 @@ Public members
|
|||||||
.. c:member:: unsigned int uv_process_options_t.flags
|
.. c:member:: unsigned int uv_process_options_t.flags
|
||||||
|
|
||||||
Various flags that control how :c:func:`uv_spawn` behaves. See
|
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:: int uv_process_options_t.stdio_count
|
||||||
.. c:member:: uv_stdio_container_t* uv_process_options_t.stdio
|
.. c:member:: uv_stdio_container_t* uv_process_options_t.stdio
|
||||||
@ -262,6 +269,9 @@ API
|
|||||||
.. versionchanged:: 1.24.0 Added `UV_PROCESS_WINDOWS_HIDE_CONSOLE` and
|
.. versionchanged:: 1.24.0 Added `UV_PROCESS_WINDOWS_HIDE_CONSOLE` and
|
||||||
`UV_PROCESS_WINDOWS_HIDE_GUI` flags.
|
`UV_PROCESS_WINDOWS_HIDE_GUI` flags.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.48.0 Added the
|
||||||
|
`UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME` flag.
|
||||||
|
|
||||||
.. c:function:: int uv_process_kill(uv_process_t* handle, int signum)
|
.. c:function:: int uv_process_kill(uv_process_t* handle, int signum)
|
||||||
|
|
||||||
Sends the specified signal to the given process handle. Check the documentation
|
Sends the specified signal to the given process handle. Check the documentation
|
||||||
|
|||||||
@ -21,17 +21,9 @@ Data types
|
|||||||
|
|
||||||
Union of all request types.
|
Union of all request types.
|
||||||
|
|
||||||
|
.. c:enum:: uv_req_type
|
||||||
|
|
||||||
Public members
|
The kind of the libuv request.
|
||||||
^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
.. 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.
|
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
@ -50,6 +42,18 @@ Public members
|
|||||||
} uv_req_type;
|
} 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
|
API
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -95,7 +99,7 @@ API
|
|||||||
|
|
||||||
.. versionadded:: 1.19.0
|
.. 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`.
|
Sets `req->data` to `data`.
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,28 @@ Data types
|
|||||||
|
|
||||||
TCP handle type.
|
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
|
Public members
|
||||||
^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^
|
||||||
@ -65,6 +87,10 @@ API
|
|||||||
at the end of this procedure, then the handle is destroyed with a
|
at the end of this procedure, then the handle is destroyed with a
|
||||||
``UV_ETIMEDOUT`` error passed to the corresponding callback.
|
``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)
|
.. c:function:: int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable)
|
||||||
|
|
||||||
Enable / disable simultaneous asynchronous accept requests that are
|
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)
|
.. 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
|
Bind the handle to an address and port.
|
||||||
initialized ``struct sockaddr_in`` or ``struct sockaddr_in6``.
|
|
||||||
|
|
||||||
When the port is already taken, you can expect to see an ``UV_EADDRINUSE``
|
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,
|
error from :c:func:`uv_listen` or :c:func:`uv_tcp_connect` unless you specify
|
||||||
a successful call to this function does not guarantee that the call
|
``UV_TCP_REUSEPORT`` in `flags` for all the binding sockets. That is, a successful
|
||||||
to :c:func:`uv_listen` or :c:func:`uv_tcp_connect` will succeed as well.
|
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
|
:param handle: TCP handle. It should have been initialized with :c:func:`uv_tcp_init`.
|
||||||
is disabled and only IPv6 is used.
|
|
||||||
|
: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)
|
.. 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
|
.. 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)
|
.. 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.
|
Like :c:func:`uv_thread_create`, but additionally specifies options for creating a new thread.
|
||||||
@ -132,6 +140,45 @@ Threads
|
|||||||
.. c:function:: int uv_thread_join(uv_thread_t *tid)
|
.. 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_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
|
||||||
|
privilege to set specific priorities on some platforms.
|
||||||
|
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
|
||||||
|
output parameter priority is platform dependent.
|
||||||
|
For Linux, when schedule policy is SCHED_OTHER (default), priority is 0.
|
||||||
|
|
||||||
Thread-local storage
|
Thread-local storage
|
||||||
^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
|||||||
@ -17,6 +17,8 @@ is 1024).
|
|||||||
.. versionchanged:: 1.45.0 threads now have an 8 MB stack instead of the
|
.. versionchanged:: 1.45.0 threads now have an 8 MB stack instead of the
|
||||||
(sometimes too low) platform default.
|
(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
|
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`)
|
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
|
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.
|
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
|
Data types
|
||||||
----------
|
----------
|
||||||
@ -64,11 +73,6 @@ API
|
|||||||
duration, and will follow normal timer semantics in the case of a
|
duration, and will follow normal timer semantics in the case of a
|
||||||
time-slice overrun.
|
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::
|
.. note::
|
||||||
If the repeat value is set from a timer callback it does not immediately take effect.
|
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,
|
If the timer was non-repeating before, it will have been stopped. If it was repeating,
|
||||||
|
|||||||
@ -27,10 +27,15 @@ Data types
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
/* Initial/normal terminal mode */
|
/* Initial/normal terminal mode */
|
||||||
UV_TTY_MODE_NORMAL,
|
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,
|
UV_TTY_MODE_RAW,
|
||||||
/* Binary-safe I/O mode for IPC (Unix-only) */
|
/* 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;
|
} uv_tty_mode_t;
|
||||||
|
|
||||||
.. c:enum:: uv_tty_vtermstate_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)
|
.. 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
|
.. 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.
|
Set the TTY using the specified terminal mode.
|
||||||
|
|
||||||
|
|||||||
@ -18,7 +18,7 @@ Data types
|
|||||||
|
|
||||||
UDP send request type.
|
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`..
|
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. */
|
/* Disables dual stack mode. */
|
||||||
UV_UDP_IPV6ONLY = 1,
|
UV_UDP_IPV6ONLY = 1,
|
||||||
/*
|
/*
|
||||||
* Indicates message was truncated because read buffer was too small. The
|
* Indicates message was truncated because read buffer was too small. The
|
||||||
* remainder was discarded by the OS. Used in uv_udp_recv_cb.
|
* remainder was discarded by the OS. Used in uv_udp_recv_cb.
|
||||||
*/
|
*/
|
||||||
UV_UDP_PARTIAL = 2,
|
UV_UDP_PARTIAL = 2,
|
||||||
/*
|
/*
|
||||||
* Indicates if SO_REUSEADDR will be set when binding the handle in
|
* Indicates if SO_REUSEADDR will be set when binding the handle.
|
||||||
* uv_udp_bind.
|
* This sets the SO_REUSEPORT socket flag on the BSDs (except for
|
||||||
* This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other
|
* DragonFlyBSD), OS X, and other platforms where SO_REUSEPORTs don't
|
||||||
* Unix platforms, it sets the SO_REUSEADDR flag. What that means is that
|
* have the capability of load balancing, as the opposite of what
|
||||||
* multiple threads or processes can bind to the same address without error
|
* UV_UDP_REUSEPORT would do. On other Unix platforms, it sets the
|
||||||
* (provided they all set the flag) but only the last one to bind will receive
|
* SO_REUSEADDR flag. What that means is that multiple threads or
|
||||||
* any traffic, in effect "stealing" the port from the previous listener.
|
* 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,
|
UV_UDP_REUSEADDR = 4,
|
||||||
/*
|
/*
|
||||||
* Indicates that the message was received by recvmmsg, so the buffer provided
|
* Indicates that the message was received by recvmmsg, so the buffer provided
|
||||||
@ -62,8 +64,20 @@ Data types
|
|||||||
*/
|
*/
|
||||||
UV_UDP_LINUX_RECVERR = 32,
|
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
|
UV_UDP_RECVMMSG = 256
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -186,11 +200,24 @@ API
|
|||||||
with the address and port to bind to.
|
with the address and port to bind to.
|
||||||
|
|
||||||
:param flags: Indicate how the socket will be bound,
|
:param flags: Indicate how the socket will be bound,
|
||||||
``UV_UDP_IPV6ONLY``, ``UV_UDP_REUSEADDR``, and ``UV_UDP_RECVERR``
|
``UV_UDP_IPV6ONLY``, ``UV_UDP_REUSEADDR``, ``UV_UDP_REUSEPORT``,
|
||||||
are supported.
|
and ``UV_UDP_RECVERR`` are supported.
|
||||||
|
|
||||||
:returns: 0 on success, or an error code < 0 on failure.
|
: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)
|
.. 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
|
Associate the UDP handle to a remote address and port, so every
|
||||||
@ -285,7 +312,9 @@ API
|
|||||||
local sockets.
|
local sockets.
|
||||||
|
|
||||||
:param handle: UDP handle. Should have been initialized with
|
: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.
|
:param on: 1 for on, 0 for off.
|
||||||
|
|
||||||
@ -296,7 +325,9 @@ API
|
|||||||
Set the multicast ttl.
|
Set the multicast ttl.
|
||||||
|
|
||||||
:param handle: UDP handle. Should have been initialized with
|
: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.
|
:param ttl: 1 through 255.
|
||||||
|
|
||||||
@ -307,7 +338,9 @@ API
|
|||||||
Set the multicast interface to send or receive data on.
|
Set the multicast interface to send or receive data on.
|
||||||
|
|
||||||
:param handle: UDP handle. Should have been initialized with
|
: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.
|
:param interface_addr: interface address.
|
||||||
|
|
||||||
@ -318,7 +351,9 @@ API
|
|||||||
Set broadcast on or off.
|
Set broadcast on or off.
|
||||||
|
|
||||||
:param handle: UDP handle. Should have been initialized with
|
: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.
|
:param on: 1 for on, 0 for off.
|
||||||
|
|
||||||
@ -329,7 +364,9 @@ API
|
|||||||
Set the time to live.
|
Set the time to live.
|
||||||
|
|
||||||
:param handle: UDP handle. Should have been initialized with
|
: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.
|
:param ttl: 1 through 255.
|
||||||
|
|
||||||
@ -389,6 +426,20 @@ API
|
|||||||
|
|
||||||
.. versionchanged:: 1.27.0 added support for connected sockets
|
.. 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)
|
.. 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
|
Prepare for receiving data. If the socket has not previously been bound
|
||||||
|
|||||||
106
include/uv.h
106
include/uv.h
@ -58,6 +58,7 @@ extern "C" {
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
/* Internal type, do not use. */
|
/* Internal type, do not use. */
|
||||||
struct uv__queue {
|
struct uv__queue {
|
||||||
@ -157,6 +158,7 @@ struct uv__queue {
|
|||||||
XX(ESOCKTNOSUPPORT, "socket type not supported") \
|
XX(ESOCKTNOSUPPORT, "socket type not supported") \
|
||||||
XX(ENODATA, "no data available") \
|
XX(ENODATA, "no data available") \
|
||||||
XX(EUNATCH, "protocol driver not attached") \
|
XX(EUNATCH, "protocol driver not attached") \
|
||||||
|
XX(ENOEXEC, "exec format error") \
|
||||||
|
|
||||||
#define UV_HANDLE_TYPE_MAP(XX) \
|
#define UV_HANDLE_TYPE_MAP(XX) \
|
||||||
XX(ASYNC, async) \
|
XX(ASYNC, async) \
|
||||||
@ -260,7 +262,9 @@ typedef struct uv_metrics_s uv_metrics_t;
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
UV_LOOP_BLOCK_SIGNAL = 0,
|
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;
|
} uv_loop_option;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -604,7 +608,18 @@ UV_EXTERN int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable);
|
|||||||
|
|
||||||
enum uv_tcp_flags {
|
enum uv_tcp_flags {
|
||||||
/* Used with uv_tcp_bind, when an IPv6 address is used. */
|
/* 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,
|
UV_EXTERN int uv_tcp_bind(uv_tcp_t* handle,
|
||||||
@ -645,10 +660,13 @@ enum uv_udp_flags {
|
|||||||
UV_UDP_PARTIAL = 2,
|
UV_UDP_PARTIAL = 2,
|
||||||
/*
|
/*
|
||||||
* Indicates if SO_REUSEADDR will be set when binding the handle.
|
* 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
|
* This sets the SO_REUSEPORT socket flag on the BSDs (except for
|
||||||
* Unix platforms, it sets the SO_REUSEADDR flag. What that means is that
|
* DragonFlyBSD), OS X, and other platforms where SO_REUSEPORTs don't
|
||||||
* multiple threads or processes can bind to the same address without error
|
* have the capability of load balancing, as the opposite of what
|
||||||
* (provided they all set the flag) but only the last one to bind will receive
|
* 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.
|
* any traffic, in effect "stealing" the port from the previous listener.
|
||||||
*/
|
*/
|
||||||
UV_UDP_REUSEADDR = 4,
|
UV_UDP_REUSEADDR = 4,
|
||||||
@ -671,6 +689,18 @@ enum uv_udp_flags {
|
|||||||
* This flag is no-op on platforms other than Linux.
|
* This flag is no-op on platforms other than Linux.
|
||||||
*/
|
*/
|
||||||
UV_UDP_LINUX_RECVERR = 32,
|
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.
|
* 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[],
|
const uv_buf_t bufs[],
|
||||||
unsigned int nbufs,
|
unsigned int nbufs,
|
||||||
const struct sockaddr* addr);
|
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_EXTERN int uv_udp_recv_start(uv_udp_t* handle,
|
||||||
uv_alloc_cb alloc_cb,
|
uv_alloc_cb alloc_cb,
|
||||||
uv_udp_recv_cb recv_cb);
|
uv_udp_recv_cb recv_cb);
|
||||||
@ -770,10 +806,15 @@ struct uv_tty_s {
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
/* Initial/normal terminal mode */
|
/* Initial/normal terminal mode */
|
||||||
UV_TTY_MODE_NORMAL,
|
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,
|
UV_TTY_MODE_RAW,
|
||||||
/* Binary-safe I/O mode for IPC (Unix-only) */
|
/* 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;
|
} uv_tty_mode_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -1106,7 +1147,14 @@ enum uv_process_flags {
|
|||||||
* option is only meaningful on Windows systems. On Unix it is silently
|
* option is only meaningful on Windows systems. On Unix it is silently
|
||||||
* ignored.
|
* ignored.
|
||||||
*/
|
*/
|
||||||
UV_PROCESS_WINDOWS_HIDE_GUI = (1 << 6)
|
UV_PROCESS_WINDOWS_HIDE_GUI = (1 << 6),
|
||||||
|
/*
|
||||||
|
* On Windows, if the path to the program to execute, specified in
|
||||||
|
* uv_process_options_t's file field, has a directory component,
|
||||||
|
* search for the exact file name before trying variants with
|
||||||
|
* extensions like '.exe' or '.cmd'.
|
||||||
|
*/
|
||||||
|
UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME = (1 << 7)
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1253,6 +1301,7 @@ typedef struct {
|
|||||||
} uv_rusage_t;
|
} uv_rusage_t;
|
||||||
|
|
||||||
UV_EXTERN int uv_getrusage(uv_rusage_t* rusage);
|
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_homedir(char* buffer, size_t* size);
|
||||||
UV_EXTERN int uv_os_tmpdir(char* buffer, size_t* size);
|
UV_EXTERN int uv_os_tmpdir(char* buffer, size_t* size);
|
||||||
@ -1284,6 +1333,17 @@ UV_EXTERN uv_pid_t uv_os_getppid(void);
|
|||||||
UV_EXTERN int uv_os_getpriority(uv_pid_t pid, int* priority);
|
UV_EXTERN int uv_os_getpriority(uv_pid_t pid, int* priority);
|
||||||
UV_EXTERN int uv_os_setpriority(uv_pid_t pid, int priority);
|
UV_EXTERN int uv_os_setpriority(uv_pid_t pid, int priority);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
UV_THREAD_PRIORITY_HIGHEST = 2,
|
||||||
|
UV_THREAD_PRIORITY_ABOVE_NORMAL = 1,
|
||||||
|
UV_THREAD_PRIORITY_NORMAL = 0,
|
||||||
|
UV_THREAD_PRIORITY_BELOW_NORMAL = -1,
|
||||||
|
UV_THREAD_PRIORITY_LOWEST = -2,
|
||||||
|
};
|
||||||
|
|
||||||
|
UV_EXTERN int uv_thread_getpriority(uv_thread_t tid, int* priority);
|
||||||
|
UV_EXTERN int uv_thread_setpriority(uv_thread_t tid, int priority);
|
||||||
|
|
||||||
UV_EXTERN unsigned int uv_available_parallelism(void);
|
UV_EXTERN unsigned int uv_available_parallelism(void);
|
||||||
UV_EXTERN int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count);
|
UV_EXTERN int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count);
|
||||||
UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count);
|
UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count);
|
||||||
@ -1531,6 +1591,8 @@ UV_EXTERN int uv_fs_chmod(uv_loop_t* loop,
|
|||||||
const char* path,
|
const char* path,
|
||||||
int mode,
|
int mode,
|
||||||
uv_fs_cb cb);
|
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_EXTERN int uv_fs_utime(uv_loop_t* loop,
|
||||||
uv_fs_t* req,
|
uv_fs_t* req,
|
||||||
const char* path,
|
const char* path,
|
||||||
@ -1823,6 +1885,7 @@ UV_EXTERN int uv_gettimeofday(uv_timeval64_t* tv);
|
|||||||
typedef void (*uv_thread_cb)(void* arg);
|
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_create(uv_thread_t* tid, uv_thread_cb entry, void* arg);
|
||||||
|
UV_EXTERN int uv_thread_detach(uv_thread_t* tid);
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
UV_THREAD_NO_FLAGS = 0x00,
|
UV_THREAD_NO_FLAGS = 0x00,
|
||||||
@ -1852,6 +1915,9 @@ UV_EXTERN int uv_thread_getcpu(void);
|
|||||||
UV_EXTERN uv_thread_t uv_thread_self(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_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_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. */
|
/* The presence of these unions force similar struct layout. */
|
||||||
#define XX(_, name) uv_ ## name ## _t name;
|
#define XX(_, name) uv_ ## name ## _t name;
|
||||||
@ -1885,17 +1951,17 @@ struct uv_loop_s {
|
|||||||
UV_EXTERN void* uv_loop_get_data(const uv_loop_t*);
|
UV_EXTERN void* uv_loop_get_data(const uv_loop_t*);
|
||||||
UV_EXTERN void uv_loop_set_data(uv_loop_t*, void* data);
|
UV_EXTERN void uv_loop_set_data(uv_loop_t*, void* data);
|
||||||
|
|
||||||
/* String utilities needed internally for dealing with Windows. */
|
/* Unicode utilities needed for dealing with Windows. */
|
||||||
size_t uv_utf16_length_as_wtf8(const uint16_t* utf16,
|
UV_EXTERN size_t uv_utf16_length_as_wtf8(const uint16_t* utf16,
|
||||||
ssize_t utf16_len);
|
ssize_t utf16_len);
|
||||||
int uv_utf16_to_wtf8(const uint16_t* utf16,
|
UV_EXTERN int uv_utf16_to_wtf8(const uint16_t* utf16,
|
||||||
ssize_t utf16_len,
|
ssize_t utf16_len,
|
||||||
char** wtf8_ptr,
|
char** wtf8_ptr,
|
||||||
size_t* wtf8_len_ptr);
|
size_t* wtf8_len_ptr);
|
||||||
ssize_t uv_wtf8_length_as_utf16(const char* wtf8);
|
UV_EXTERN ssize_t uv_wtf8_length_as_utf16(const char* wtf8);
|
||||||
void uv_wtf8_to_utf16(const char* wtf8,
|
UV_EXTERN void uv_wtf8_to_utf16(const char* wtf8,
|
||||||
uint16_t* utf16,
|
uint16_t* utf16,
|
||||||
size_t utf16_len);
|
size_t utf16_len);
|
||||||
|
|
||||||
/* Don't export the private CPP symbols. */
|
/* Don't export the private CPP symbols. */
|
||||||
#undef UV_HANDLE_TYPE_PRIVATE
|
#undef UV_HANDLE_TYPE_PRIVATE
|
||||||
|
|||||||
@ -474,4 +474,10 @@
|
|||||||
# define UV__EUNATCH (-4023)
|
# define UV__EUNATCH (-4023)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(ENOEXEC) && !defined(_WIN32)
|
||||||
|
# define UV__ENOEXEC UV__ERR(ENOEXEC)
|
||||||
|
#else
|
||||||
|
# define UV__ENOEXEC (-4022)
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* UV_ERRNO_H_ */
|
#endif /* UV_ERRNO_H_ */
|
||||||
|
|||||||
@ -35,21 +35,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This file defines data structures for different types of trees:
|
* This file defines data structures for red-black 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);
|
|
||||||
*
|
|
||||||
* A red-black tree is a binary search tree with the node color as an
|
* A red-black tree is a binary search tree with the node color as an
|
||||||
* extra attribute. It fulfills a set of conditions:
|
* extra attribute. It fulfills a set of conditions:
|
||||||
* - every search path from the root to a leaf consists of the
|
* - 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).
|
* 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 */
|
/* Macros that define a red-black tree */
|
||||||
#define RB_HEAD(name, type) \
|
#define RB_HEAD(name, type) \
|
||||||
struct name { \
|
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_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
|
||||||
#define RB_FIND(name, x, y) name##_RB_FIND(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_NFIND(name, x, y) name##_RB_NFIND(x, y)
|
||||||
#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
|
#define RB_NEXT(name, x) name##_RB_NEXT(x)
|
||||||
#define RB_PREV(name, x, y) name##_RB_PREV(y)
|
#define RB_PREV(name, x) name##_RB_PREV(x)
|
||||||
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
|
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
|
||||||
#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
|
#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
|
||||||
|
|
||||||
|
|||||||
@ -271,7 +271,10 @@ typedef struct {
|
|||||||
|
|
||||||
#define UV_UDP_SEND_PRIVATE_FIELDS \
|
#define UV_UDP_SEND_PRIVATE_FIELDS \
|
||||||
struct uv__queue queue; \
|
struct uv__queue queue; \
|
||||||
struct sockaddr_storage addr; \
|
union { \
|
||||||
|
struct sockaddr addr; \
|
||||||
|
struct sockaddr_storage storage; \
|
||||||
|
} u; \
|
||||||
unsigned int nbufs; \
|
unsigned int nbufs; \
|
||||||
uv_buf_t* bufs; \
|
uv_buf_t* bufs; \
|
||||||
ssize_t status; \
|
ssize_t status; \
|
||||||
@ -328,7 +331,10 @@ typedef struct {
|
|||||||
|
|
||||||
#define UV_TIMER_PRIVATE_FIELDS \
|
#define UV_TIMER_PRIVATE_FIELDS \
|
||||||
uv_timer_cb timer_cb; \
|
uv_timer_cb timer_cb; \
|
||||||
void* heap_node[3]; \
|
union { \
|
||||||
|
void* heap[3]; \
|
||||||
|
struct uv__queue queue; \
|
||||||
|
} node; \
|
||||||
uint64_t timeout; \
|
uint64_t timeout; \
|
||||||
uint64_t repeat; \
|
uint64_t repeat; \
|
||||||
uint64_t start_id;
|
uint64_t start_id;
|
||||||
|
|||||||
@ -31,10 +31,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define UV_VERSION_MAJOR 1
|
#define UV_VERSION_MAJOR 1
|
||||||
#define UV_VERSION_MINOR 47
|
#define UV_VERSION_MINOR 50
|
||||||
#define UV_VERSION_PATCH 0
|
#define UV_VERSION_PATCH 1
|
||||||
#define UV_VERSION_IS_RELEASE 1
|
#define UV_VERSION_IS_RELEASE 0
|
||||||
#define UV_VERSION_SUFFIX ""
|
#define UV_VERSION_SUFFIX "dev"
|
||||||
|
|
||||||
#define UV_VERSION_HEX ((UV_VERSION_MAJOR << 16) | \
|
#define UV_VERSION_HEX ((UV_VERSION_MAJOR << 16) | \
|
||||||
(UV_VERSION_MINOR << 8) | \
|
(UV_VERSION_MINOR << 8) | \
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _WIN32_WINNT
|
#ifndef _WIN32_WINNT
|
||||||
# define _WIN32_WINNT 0x0600
|
# define _WIN32_WINNT 0x0A00
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED)
|
#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED)
|
||||||
@ -32,20 +32,12 @@ typedef intptr_t ssize_t;
|
|||||||
|
|
||||||
#include <winsock2.h>
|
#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
|
#ifndef LOCALE_INVARIANT
|
||||||
# define LOCALE_INVARIANT 0x007f
|
# define LOCALE_INVARIANT 0x007f
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <mswsock.h>
|
#include <mswsock.h>
|
||||||
// Disable the typedef in mstcpip.h of MinGW.
|
/* Disable the typedef in mstcpip.h of MinGW. */
|
||||||
#define _TCP_INITIAL_RTO_PARAMETERS _TCP_INITIAL_RTO_PARAMETERS__AVOID
|
#define _TCP_INITIAL_RTO_PARAMETERS _TCP_INITIAL_RTO_PARAMETERS__AVOID
|
||||||
#define TCP_INITIAL_RTO_PARAMETERS TCP_INITIAL_RTO_PARAMETERS__AVOID
|
#define TCP_INITIAL_RTO_PARAMETERS TCP_INITIAL_RTO_PARAMETERS__AVOID
|
||||||
#define PTCP_INITIAL_RTO_PARAMETERS PTCP_INITIAL_RTO_PARAMETERS__AVOID
|
#define PTCP_INITIAL_RTO_PARAMETERS PTCP_INITIAL_RTO_PARAMETERS__AVOID
|
||||||
@ -70,7 +62,7 @@ typedef struct pollfd {
|
|||||||
# define S_IFLNK 0xA000
|
# define S_IFLNK 0xA000
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Define missing in Windows Kit Include\{VERSION}\ucrt\sys\stat.h
|
/* Define missing in Windows Kit Include\{VERSION}\ucrt\sys\stat.h */
|
||||||
#if defined(_CRT_INTERNAL_NONSTDC_NAMES) && _CRT_INTERNAL_NONSTDC_NAMES && !defined(S_IFIFO)
|
#if defined(_CRT_INTERNAL_NONSTDC_NAMES) && _CRT_INTERNAL_NONSTDC_NAMES && !defined(S_IFIFO)
|
||||||
# define S_IFIFO _S_IFIFO
|
# define S_IFIFO _S_IFIFO
|
||||||
#endif
|
#endif
|
||||||
@ -290,8 +282,8 @@ typedef struct {
|
|||||||
#define UV_ONCE_INIT { 0, NULL }
|
#define UV_ONCE_INIT { 0, NULL }
|
||||||
|
|
||||||
typedef struct uv_once_s {
|
typedef struct uv_once_s {
|
||||||
unsigned char ran;
|
unsigned char unused;
|
||||||
HANDLE event;
|
INIT_ONCE init_once;
|
||||||
} uv_once_t;
|
} uv_once_t;
|
||||||
|
|
||||||
/* Platform-specific definitions for uv_spawn support. */
|
/* Platform-specific definitions for uv_spawn support. */
|
||||||
@ -507,8 +499,11 @@ typedef struct {
|
|||||||
union { \
|
union { \
|
||||||
struct { \
|
struct { \
|
||||||
/* Used for readable TTY handles */ \
|
/* Used for readable TTY handles */ \
|
||||||
/* TODO: remove me in v2.x. */ \
|
union { \
|
||||||
HANDLE unused_; \
|
/* TODO: remove me in v2.x. */ \
|
||||||
|
HANDLE unused_; \
|
||||||
|
int mode; \
|
||||||
|
} mode; \
|
||||||
uv_buf_t read_line_buffer; \
|
uv_buf_t read_line_buffer; \
|
||||||
HANDLE read_raw_wait; \
|
HANDLE read_raw_wait; \
|
||||||
/* Fields used for translating win keystrokes into vt100 characters */ \
|
/* Fields used for translating win keystrokes into vt100 characters */ \
|
||||||
@ -550,7 +545,10 @@ typedef struct {
|
|||||||
unsigned char events;
|
unsigned char events;
|
||||||
|
|
||||||
#define UV_TIMER_PRIVATE_FIELDS \
|
#define UV_TIMER_PRIVATE_FIELDS \
|
||||||
void* heap_node[3]; \
|
union { \
|
||||||
|
void* heap[3]; \
|
||||||
|
struct uv__queue queue; \
|
||||||
|
} node; \
|
||||||
int unused; \
|
int unused; \
|
||||||
uint64_t timeout; \
|
uint64_t timeout; \
|
||||||
uint64_t repeat; \
|
uint64_t repeat; \
|
||||||
|
|||||||
@ -139,6 +139,9 @@ int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buffer, size_t* size) {
|
|||||||
struct poll_ctx* ctx;
|
struct poll_ctx* ctx;
|
||||||
size_t required_len;
|
size_t required_len;
|
||||||
|
|
||||||
|
if (buffer == NULL || size == NULL || *size == 0)
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
if (!uv_is_active((uv_handle_t*)handle)) {
|
if (!uv_is_active((uv_handle_t*)handle)) {
|
||||||
*size = 0;
|
*size = 0;
|
||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
|
|||||||
11
src/idna.c
11
src/idna.c
@ -322,6 +322,9 @@ ssize_t uv__idna_toascii(const char* s, const char* se, char* d, char* de) {
|
|||||||
char* ds;
|
char* ds;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
if (s == se)
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
ds = d;
|
ds = d;
|
||||||
|
|
||||||
si = s;
|
si = s;
|
||||||
@ -356,9 +359,10 @@ ssize_t uv__idna_toascii(const char* s, const char* se, char* d, char* de) {
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d < de)
|
if (d >= de)
|
||||||
*d++ = '\0';
|
return UV_EINVAL;
|
||||||
|
|
||||||
|
*d++ = '\0';
|
||||||
return d - ds; /* Number of bytes written. */
|
return d - ds; /* Number of bytes written. */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,7 +393,7 @@ void uv_wtf8_to_utf16(const char* source_ptr,
|
|||||||
code_point = uv__wtf8_decode1(&source_ptr);
|
code_point = uv__wtf8_decode1(&source_ptr);
|
||||||
/* uv_wtf8_length_as_utf16 should have been called and checked first. */
|
/* uv_wtf8_length_as_utf16 should have been called and checked first. */
|
||||||
assert(code_point >= 0);
|
assert(code_point >= 0);
|
||||||
if (code_point > 0x10000) {
|
if (code_point > 0xFFFF) {
|
||||||
assert(code_point < 0x10FFFF);
|
assert(code_point < 0x10FFFF);
|
||||||
*w_target++ = (((code_point - 0x10000) >> 10) + 0xD800);
|
*w_target++ = (((code_point - 0x10000) >> 10) + 0xD800);
|
||||||
*w_target++ = ((code_point - 0x10000) & 0x3FF) + 0xDC00;
|
*w_target++ = ((code_point - 0x10000) & 0x3FF) + 0xDC00;
|
||||||
@ -400,6 +404,7 @@ void uv_wtf8_to_utf16(const char* source_ptr,
|
|||||||
}
|
}
|
||||||
} while (*source_ptr++);
|
} while (*source_ptr++);
|
||||||
|
|
||||||
|
(void)w_target_len;
|
||||||
assert(w_target_len == 0);
|
assert(w_target_len == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -82,7 +82,7 @@ static void uv__random_done(struct uv__work* w, int status) {
|
|||||||
uv_random_t* req;
|
uv_random_t* req;
|
||||||
|
|
||||||
req = container_of(w, uv_random_t, work_req);
|
req = container_of(w, uv_random_t, work_req);
|
||||||
uv__req_unregister(req->loop, req);
|
uv__req_unregister(req->loop);
|
||||||
|
|
||||||
if (status == 0)
|
if (status == 0)
|
||||||
status = req->status;
|
status = req->status;
|
||||||
|
|||||||
@ -59,6 +59,7 @@ static void worker(void* arg) {
|
|||||||
struct uv__queue* q;
|
struct uv__queue* q;
|
||||||
int is_slow_work;
|
int is_slow_work;
|
||||||
|
|
||||||
|
uv_thread_setname("libuv-worker");
|
||||||
uv_sem_post((uv_sem_t*) arg);
|
uv_sem_post((uv_sem_t*) arg);
|
||||||
arg = NULL;
|
arg = NULL;
|
||||||
|
|
||||||
@ -356,7 +357,7 @@ static void uv__queue_done(struct uv__work* w, int err) {
|
|||||||
uv_work_t* req;
|
uv_work_t* req;
|
||||||
|
|
||||||
req = container_of(w, uv_work_t, work_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)
|
if (req->after_work_cb == NULL)
|
||||||
return;
|
return;
|
||||||
|
|||||||
43
src/timer.c
43
src/timer.c
@ -40,8 +40,8 @@ static int timer_less_than(const struct heap_node* ha,
|
|||||||
const uv_timer_t* a;
|
const uv_timer_t* a;
|
||||||
const uv_timer_t* b;
|
const uv_timer_t* b;
|
||||||
|
|
||||||
a = container_of(ha, uv_timer_t, heap_node);
|
a = container_of(ha, uv_timer_t, node.heap);
|
||||||
b = container_of(hb, uv_timer_t, heap_node);
|
b = container_of(hb, uv_timer_t, node.heap);
|
||||||
|
|
||||||
if (a->timeout < b->timeout)
|
if (a->timeout < b->timeout)
|
||||||
return 1;
|
return 1;
|
||||||
@ -60,6 +60,7 @@ int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) {
|
|||||||
handle->timer_cb = NULL;
|
handle->timer_cb = NULL;
|
||||||
handle->timeout = 0;
|
handle->timeout = 0;
|
||||||
handle->repeat = 0;
|
handle->repeat = 0;
|
||||||
|
uv__queue_init(&handle->node.queue);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,8 +74,7 @@ int uv_timer_start(uv_timer_t* handle,
|
|||||||
if (uv__is_closing(handle) || cb == NULL)
|
if (uv__is_closing(handle) || cb == NULL)
|
||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
|
|
||||||
if (uv__is_active(handle))
|
uv_timer_stop(handle);
|
||||||
uv_timer_stop(handle);
|
|
||||||
|
|
||||||
clamped_timeout = handle->loop->time + timeout;
|
clamped_timeout = handle->loop->time + timeout;
|
||||||
if (clamped_timeout < timeout)
|
if (clamped_timeout < timeout)
|
||||||
@ -87,7 +87,7 @@ int uv_timer_start(uv_timer_t* handle,
|
|||||||
handle->start_id = handle->loop->timer_counter++;
|
handle->start_id = handle->loop->timer_counter++;
|
||||||
|
|
||||||
heap_insert(timer_heap(handle->loop),
|
heap_insert(timer_heap(handle->loop),
|
||||||
(struct heap_node*) &handle->heap_node,
|
(struct heap_node*) &handle->node.heap,
|
||||||
timer_less_than);
|
timer_less_than);
|
||||||
uv__handle_start(handle);
|
uv__handle_start(handle);
|
||||||
|
|
||||||
@ -96,14 +96,16 @@ int uv_timer_start(uv_timer_t* handle,
|
|||||||
|
|
||||||
|
|
||||||
int uv_timer_stop(uv_timer_t* handle) {
|
int uv_timer_stop(uv_timer_t* handle) {
|
||||||
if (!uv__is_active(handle))
|
if (uv__is_active(handle)) {
|
||||||
return 0;
|
heap_remove(timer_heap(handle->loop),
|
||||||
|
(struct heap_node*) &handle->node.heap,
|
||||||
heap_remove(timer_heap(handle->loop),
|
timer_less_than);
|
||||||
(struct heap_node*) &handle->heap_node,
|
uv__handle_stop(handle);
|
||||||
timer_less_than);
|
} else {
|
||||||
uv__handle_stop(handle);
|
uv__queue_remove(&handle->node.queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
uv__queue_init(&handle->node.queue);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +150,7 @@ int uv__next_timeout(const uv_loop_t* loop) {
|
|||||||
if (heap_node == NULL)
|
if (heap_node == NULL)
|
||||||
return -1; /* block indefinitely */
|
return -1; /* block indefinitely */
|
||||||
|
|
||||||
handle = container_of(heap_node, uv_timer_t, heap_node);
|
handle = container_of(heap_node, uv_timer_t, node.heap);
|
||||||
if (handle->timeout <= loop->time)
|
if (handle->timeout <= loop->time)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -163,17 +165,30 @@ int uv__next_timeout(const uv_loop_t* loop) {
|
|||||||
void uv__run_timers(uv_loop_t* loop) {
|
void uv__run_timers(uv_loop_t* loop) {
|
||||||
struct heap_node* heap_node;
|
struct heap_node* heap_node;
|
||||||
uv_timer_t* handle;
|
uv_timer_t* handle;
|
||||||
|
struct uv__queue* queue_node;
|
||||||
|
struct uv__queue ready_queue;
|
||||||
|
|
||||||
|
uv__queue_init(&ready_queue);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
heap_node = heap_min(timer_heap(loop));
|
heap_node = heap_min(timer_heap(loop));
|
||||||
if (heap_node == NULL)
|
if (heap_node == NULL)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
handle = container_of(heap_node, uv_timer_t, heap_node);
|
handle = container_of(heap_node, uv_timer_t, node.heap);
|
||||||
if (handle->timeout > loop->time)
|
if (handle->timeout > loop->time)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
uv_timer_stop(handle);
|
uv_timer_stop(handle);
|
||||||
|
uv__queue_insert_tail(&ready_queue, &handle->node.queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!uv__queue_empty(&ready_queue)) {
|
||||||
|
queue_node = uv__queue_head(&ready_queue);
|
||||||
|
uv__queue_remove(queue_node);
|
||||||
|
uv__queue_init(queue_node);
|
||||||
|
handle = container_of(queue_node, uv_timer_t, node.queue);
|
||||||
|
|
||||||
uv_timer_again(handle);
|
uv_timer_again(handle);
|
||||||
handle->timer_cb(handle);
|
handle->timer_cb(handle);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1120,6 +1120,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
|||||||
struct ifreq *ifr, *p, flg;
|
struct ifreq *ifr, *p, flg;
|
||||||
struct in6_ifreq if6;
|
struct in6_ifreq if6;
|
||||||
struct sockaddr_dl* sa_addr;
|
struct sockaddr_dl* sa_addr;
|
||||||
|
size_t namelen;
|
||||||
|
char* name;
|
||||||
|
|
||||||
ifc.ifc_req = NULL;
|
ifc.ifc_req = NULL;
|
||||||
sock6fd = -1;
|
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))
|
#define ADDR_SIZE(p) MAX((p).sa_len, sizeof(p))
|
||||||
|
|
||||||
/* Count all up and running ipv4/ipv6 addresses */
|
/* Count all up and running ipv4/ipv6 addresses */
|
||||||
|
namelen = 0;
|
||||||
ifr = ifc.ifc_req;
|
ifr = ifc.ifc_req;
|
||||||
while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) {
|
while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) {
|
||||||
p = ifr;
|
p = ifr;
|
||||||
@ -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))
|
if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
namelen += strlen(ent->ifa_name) + 1;
|
||||||
(*count)++;
|
(*count)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1182,8 +1186,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
/* Alloc the return interface structs */
|
/* Alloc the return interface structs */
|
||||||
*addresses = uv__calloc(*count, sizeof(**addresses));
|
*addresses = uv__calloc(1, *count * sizeof(**addresses) + namelen);
|
||||||
if (!(*addresses)) {
|
if (*addresses == NULL) {
|
||||||
r = UV_ENOMEM;
|
r = UV_ENOMEM;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
@ -1210,7 +1214,9 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
|||||||
|
|
||||||
/* All conditions above must match count loop */
|
/* All conditions above must match count loop */
|
||||||
|
|
||||||
address->name = uv__strdup(p->ifr_name);
|
namelen = strlen(p->ifr_name) + 1;
|
||||||
|
address->name = memcpy(name, p->ifr_name, namelen);
|
||||||
|
name += namelen;
|
||||||
|
|
||||||
if (inet6)
|
if (inet6)
|
||||||
address->address.address6 = *((struct sockaddr_in6*) &p->ifr_addr);
|
address->address.address6 = *((struct sockaddr_in6*) &p->ifr_addr);
|
||||||
@ -1282,13 +1288,7 @@ cleanup:
|
|||||||
|
|
||||||
|
|
||||||
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||||
int count) {
|
int count) {
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < count; ++i) {
|
|
||||||
uv__free(addresses[i].name);
|
|
||||||
}
|
|
||||||
|
|
||||||
uv__free(addresses);
|
uv__free(addresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -38,6 +38,34 @@
|
|||||||
#include <sys/eventfd.h>
|
#include <sys/eventfd.h>
|
||||||
#endif
|
#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 void uv__async_send(uv_loop_t* loop);
|
||||||
static int uv__async_start(uv_loop_t* loop);
|
static int uv__async_start(uv_loop_t* loop);
|
||||||
static void uv__cpu_relax(void);
|
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);
|
assert(w == &loop->async_io_watcher);
|
||||||
|
|
||||||
|
#if UV__KQUEUE_EVFILT_USER
|
||||||
|
for (;!kqueue_evfilt_user_support;) {
|
||||||
|
#else
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
#endif
|
||||||
r = read(w->fd, buf, sizeof(buf));
|
r = read(w->fd, buf, sizeof(buf));
|
||||||
|
|
||||||
if (r == sizeof(buf))
|
if (r == sizeof(buf))
|
||||||
@ -195,6 +227,17 @@ static void uv__async_send(uv_loop_t* loop) {
|
|||||||
len = sizeof(val);
|
len = sizeof(val);
|
||||||
fd = loop->async_io_watcher.fd; /* eventfd */
|
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
|
#endif
|
||||||
|
|
||||||
do
|
do
|
||||||
@ -215,6 +258,9 @@ static void uv__async_send(uv_loop_t* loop) {
|
|||||||
static int uv__async_start(uv_loop_t* loop) {
|
static int uv__async_start(uv_loop_t* loop) {
|
||||||
int pipefd[2];
|
int pipefd[2];
|
||||||
int err;
|
int err;
|
||||||
|
#if UV__KQUEUE_EVFILT_USER
|
||||||
|
struct kevent ev;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (loop->async_io_watcher.fd != -1)
|
if (loop->async_io_watcher.fd != -1)
|
||||||
return 0;
|
return 0;
|
||||||
@ -226,6 +272,36 @@ static int uv__async_start(uv_loop_t* loop) {
|
|||||||
|
|
||||||
pipefd[0] = err;
|
pipefd[0] = err;
|
||||||
pipefd[1] = -1;
|
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
|
#else
|
||||||
err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE);
|
err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE);
|
||||||
if (err < 0)
|
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);
|
uv__io_start(loop, &loop->async_io_watcher, POLLIN);
|
||||||
loop->async_wfd = pipefd[1];
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -65,13 +65,13 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* TODO(bnoordhuis) share with linux.c */
|
||||||
int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||||
|
uv_interface_address_t* address;
|
||||||
struct ifaddrs* addrs;
|
struct ifaddrs* addrs;
|
||||||
struct ifaddrs* ent;
|
struct ifaddrs* ent;
|
||||||
uv_interface_address_t* address;
|
size_t namelen;
|
||||||
#if !(defined(__CYGWIN__) || defined(__MSYS__)) && !defined(__GNU__)
|
char* name;
|
||||||
int i;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
*count = 0;
|
*count = 0;
|
||||||
*addresses = NULL;
|
*addresses = NULL;
|
||||||
@ -80,9 +80,11 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
|||||||
return UV__ERR(errno);
|
return UV__ERR(errno);
|
||||||
|
|
||||||
/* Count the number of interfaces */
|
/* Count the number of interfaces */
|
||||||
|
namelen = 0;
|
||||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
|
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
|
||||||
continue;
|
continue;
|
||||||
|
namelen += strlen(ent->ifa_name) + 1;
|
||||||
(*count)++;
|
(*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() */
|
/* 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) {
|
if (*addresses == NULL) {
|
||||||
freeifaddrs(addrs);
|
freeifaddrs(addrs);
|
||||||
return UV_ENOMEM;
|
return UV_ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
name = (char*) &(*addresses)[*count];
|
||||||
address = *addresses;
|
address = *addresses;
|
||||||
|
|
||||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
|
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
|
||||||
continue;
|
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) {
|
if (ent->ifa_addr->sa_family == AF_INET6) {
|
||||||
address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
|
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__)
|
#if !(defined(__CYGWIN__) || defined(__MSYS__)) && !defined(__GNU__)
|
||||||
/* Fill in physical addresses for each interface */
|
/* Fill in physical addresses for each interface */
|
||||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||||
|
int i;
|
||||||
|
|
||||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))
|
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))
|
||||||
continue;
|
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,
|
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||||
int count) {
|
int count) {
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
uv__free(addresses[i].name);
|
|
||||||
}
|
|
||||||
|
|
||||||
uv__free(addresses);
|
uv__free(addresses);
|
||||||
}
|
}
|
||||||
|
|||||||
346
src/unix/core.c
346
src/unix/core.c
@ -52,8 +52,11 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
|
# include <mach/mach.h>
|
||||||
|
# include <mach/thread_info.h>
|
||||||
# include <sys/filio.h>
|
# include <sys/filio.h>
|
||||||
# endif /* defined(__APPLE__) */
|
# include <sys/sysctl.h>
|
||||||
|
#endif /* defined(__APPLE__) */
|
||||||
|
|
||||||
|
|
||||||
#if defined(__APPLE__) && !TARGET_OS_IPHONE
|
#if defined(__APPLE__) && !TARGET_OS_IPHONE
|
||||||
@ -90,9 +93,19 @@ extern char** environ;
|
|||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
# include <sched.h>
|
# include <sched.h>
|
||||||
# include <sys/syscall.h>
|
# include <sys/syscall.h>
|
||||||
|
# define gettid() syscall(SYS_gettid)
|
||||||
# define uv__accept4 accept4
|
# define uv__accept4 accept4
|
||||||
#endif
|
#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__)
|
#if defined(__linux__) && defined(__SANITIZE_THREAD__) && defined(__clang__)
|
||||||
# include <sanitizer/linux_syscall_hooks.h>
|
# include <sanitizer/linux_syscall_hooks.h>
|
||||||
#endif
|
#endif
|
||||||
@ -155,7 +168,7 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case UV_TTY:
|
case UV_TTY:
|
||||||
uv__stream_close((uv_stream_t*)handle);
|
uv__tty_close((uv_tty_t*)handle);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UV_TCP:
|
case UV_TCP:
|
||||||
@ -740,7 +753,7 @@ ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) {
|
|||||||
int uv_cwd(char* buffer, size_t* size) {
|
int uv_cwd(char* buffer, size_t* size) {
|
||||||
char scratch[1 + UV__PATH_MAX];
|
char scratch[1 + UV__PATH_MAX];
|
||||||
|
|
||||||
if (buffer == NULL || size == NULL)
|
if (buffer == NULL || size == NULL || *size == 0)
|
||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
|
|
||||||
/* Try to read directly into the user's buffer first... */
|
/* Try to read directly into the user's buffer first... */
|
||||||
@ -988,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;
|
struct rusage usage;
|
||||||
|
|
||||||
if (getrusage(RUSAGE_SELF, &usage))
|
if (getrusage(who, &usage))
|
||||||
return UV__ERR(errno);
|
return UV__ERR(errno);
|
||||||
|
|
||||||
rusage->ru_utime.tv_sec = usage.ru_utime.tv_sec;
|
rusage->ru_utime.tv_sec = usage.ru_utime.tv_sec;
|
||||||
@ -1023,13 +1036,57 @@ int uv_getrusage(uv_rusage_t* rusage) {
|
|||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
rusage->ru_maxrss /= 1024; /* macOS and iOS report bytes. */
|
rusage->ru_maxrss /= 1024; /* macOS and iOS report bytes. */
|
||||||
#elif defined(__sun)
|
#elif defined(__sun)
|
||||||
rusage->ru_maxrss /= getpagesize() / 1024; /* Solaris reports pages. */
|
rusage->ru_maxrss *= getpagesize() / 1024; /* Solaris reports pages. */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 0;
|
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) {
|
int uv__open_cloexec(const char* path, int flags) {
|
||||||
#if defined(O_CLOEXEC)
|
#if defined(O_CLOEXEC)
|
||||||
int fd;
|
int fd;
|
||||||
@ -1557,6 +1614,135 @@ int uv_os_setpriority(uv_pid_t pid, int priority) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the function succeeds, the return value is 0.
|
||||||
|
* If the function fails, the return value is non-zero.
|
||||||
|
* for Linux, when schedule policy is SCHED_OTHER (default), priority is 0.
|
||||||
|
* So the output parameter priority is actually the nice value.
|
||||||
|
*/
|
||||||
|
int uv_thread_getpriority(uv_thread_t tid, int* priority) {
|
||||||
|
int r;
|
||||||
|
int policy;
|
||||||
|
struct sched_param param;
|
||||||
|
#ifdef __linux__
|
||||||
|
pid_t pid = gettid();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (priority == NULL)
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
|
r = pthread_getschedparam(tid, &policy, ¶m);
|
||||||
|
if (r != 0)
|
||||||
|
return UV__ERR(errno);
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
if (SCHED_OTHER == policy && pthread_equal(tid, pthread_self())) {
|
||||||
|
errno = 0;
|
||||||
|
r = getpriority(PRIO_PROCESS, pid);
|
||||||
|
if (r == -1 && errno != 0)
|
||||||
|
return UV__ERR(errno);
|
||||||
|
*priority = r;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
*priority = param.sched_priority;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
static int set_nice_for_calling_thread(int priority) {
|
||||||
|
int r;
|
||||||
|
int nice;
|
||||||
|
|
||||||
|
if (priority < UV_THREAD_PRIORITY_LOWEST || priority > UV_THREAD_PRIORITY_HIGHEST)
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
|
pid_t pid = gettid();
|
||||||
|
nice = 0 - priority * 2;
|
||||||
|
r = setpriority(PRIO_PROCESS, pid, nice);
|
||||||
|
if (r != 0)
|
||||||
|
return UV__ERR(errno);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the function succeeds, the return value is 0.
|
||||||
|
* 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;
|
||||||
|
int range;
|
||||||
|
int prio;
|
||||||
|
int policy;
|
||||||
|
struct sched_param param;
|
||||||
|
|
||||||
|
if (priority < UV_THREAD_PRIORITY_LOWEST || priority > UV_THREAD_PRIORITY_HIGHEST)
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
|
r = pthread_getschedparam(tid, &policy, ¶m);
|
||||||
|
if (r != 0)
|
||||||
|
return UV__ERR(errno);
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
/**
|
||||||
|
* for Linux, when schedule policy is SCHED_OTHER (default), priority must be 0,
|
||||||
|
* we should set the nice value in this case.
|
||||||
|
*/
|
||||||
|
if (SCHED_OTHER == policy && pthread_equal(tid, pthread_self()))
|
||||||
|
return set_nice_for_calling_thread(priority);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __PASE__
|
||||||
|
min = 1;
|
||||||
|
max = 127;
|
||||||
|
#else
|
||||||
|
min = sched_get_priority_min(policy);
|
||||||
|
max = sched_get_priority_max(policy);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (min == -1 || max == -1)
|
||||||
|
return UV__ERR(errno);
|
||||||
|
|
||||||
|
range = max - min;
|
||||||
|
|
||||||
|
switch (priority) {
|
||||||
|
case UV_THREAD_PRIORITY_HIGHEST:
|
||||||
|
prio = max;
|
||||||
|
break;
|
||||||
|
case UV_THREAD_PRIORITY_ABOVE_NORMAL:
|
||||||
|
prio = min + range * 3 / 4;
|
||||||
|
break;
|
||||||
|
case UV_THREAD_PRIORITY_NORMAL:
|
||||||
|
prio = min + range / 2;
|
||||||
|
break;
|
||||||
|
case UV_THREAD_PRIORITY_BELOW_NORMAL:
|
||||||
|
prio = min + range / 4;
|
||||||
|
break;
|
||||||
|
case UV_THREAD_PRIORITY_LOWEST:
|
||||||
|
prio = min;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param.sched_priority != prio) {
|
||||||
|
param.sched_priority = prio;
|
||||||
|
r = pthread_setschedparam(tid, policy, ¶m);
|
||||||
|
if (r != 0)
|
||||||
|
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) {
|
int uv_os_uname(uv_utsname_t* buffer) {
|
||||||
struct utsname buf;
|
struct utsname buf;
|
||||||
@ -1739,11 +1925,31 @@ int uv__search_path(const char* prog, char* buf, size_t* buflen) {
|
|||||||
return UV_EINVAL;
|
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) {
|
unsigned int uv_available_parallelism(void) {
|
||||||
|
long rc = -1;
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
cpu_set_t set;
|
cpu_set_t set;
|
||||||
long rc;
|
|
||||||
|
|
||||||
memset(&set, 0, sizeof(set));
|
memset(&set, 0, sizeof(set));
|
||||||
|
|
||||||
@ -1752,29 +1958,127 @@ unsigned int uv_available_parallelism(void) {
|
|||||||
* before falling back to sysconf(_SC_NPROCESSORS_ONLN).
|
* before falling back to sysconf(_SC_NPROCESSORS_ONLN).
|
||||||
*/
|
*/
|
||||||
if (0 == sched_getaffinity(0, sizeof(set), &set))
|
if (0 == sched_getaffinity(0, sizeof(set), &set))
|
||||||
rc = CPU_COUNT(&set);
|
rc = uv__cpu_count(&set);
|
||||||
else
|
|
||||||
rc = sysconf(_SC_NPROCESSORS_ONLN);
|
|
||||||
|
|
||||||
if (rc < 1)
|
|
||||||
rc = 1;
|
|
||||||
|
|
||||||
return (unsigned) rc;
|
|
||||||
#elif defined(__MVS__)
|
#elif defined(__MVS__)
|
||||||
int rc;
|
|
||||||
|
|
||||||
rc = __get_num_online_cpus();
|
rc = __get_num_online_cpus();
|
||||||
if (rc < 1)
|
if (rc < 1)
|
||||||
rc = 1;
|
rc = 1;
|
||||||
|
|
||||||
return (unsigned) rc;
|
return (unsigned) rc;
|
||||||
#else /* __linux__ */
|
#elif defined(__FreeBSD__)
|
||||||
long rc;
|
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)
|
if (rc < 1)
|
||||||
rc = 1;
|
rc = 1;
|
||||||
|
|
||||||
return (unsigned) rc;
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,9 +36,45 @@ int uv_uptime(double* uptime) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int uv_resident_set_memory(size_t* rss) {
|
int uv_resident_set_memory(size_t* rss) {
|
||||||
/* FIXME: read /proc/meminfo? */
|
char buf[1024];
|
||||||
*rss = 0;
|
const char* s;
|
||||||
|
long val;
|
||||||
|
int rc;
|
||||||
|
int i;
|
||||||
|
struct sysinfo si;
|
||||||
|
|
||||||
|
/* rss: 24th element */
|
||||||
|
rc = uv__slurp("/proc/self/stat", buf, sizeof(buf));
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* find the last ')' */
|
||||||
|
s = strrchr(buf, ')');
|
||||||
|
if (s == NULL)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
for (i = 1; i <= 22; i++) {
|
||||||
|
s = strchr(s + 1, ' ');
|
||||||
|
if (s == NULL)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
val = strtol(s, NULL, 10);
|
||||||
|
if (val < 0 || errno != 0)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
do
|
||||||
|
rc = sysinfo(&si);
|
||||||
|
while (rc == -1 && errno == EINTR);
|
||||||
|
if (rc == -1)
|
||||||
|
return UV__ERR(errno);
|
||||||
|
|
||||||
|
*rss = val * si.mem_unit;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
return UV_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
|
int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
|
||||||
|
|||||||
@ -33,25 +33,9 @@
|
|||||||
#include "darwin-stub.h"
|
#include "darwin-stub.h"
|
||||||
#endif
|
#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) {
|
int uv__set_process_title(const char* title) {
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
return uv__pthread_setname_np(title);
|
return uv__thread_setname(title);
|
||||||
#else
|
#else
|
||||||
CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef,
|
CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef,
|
||||||
const char*,
|
const char*,
|
||||||
@ -177,7 +161,7 @@ int uv__set_process_title(const char* title) {
|
|||||||
goto out;
|
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;
|
err = 0;
|
||||||
|
|
||||||
out:
|
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 <stdint.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <mach/mach.h>
|
#include <mach/mach.h>
|
||||||
#include <mach/mach_time.h>
|
#include <mach/mach_time.h>
|
||||||
#include <mach-o/dyld.h> /* _NSGetExecutablePath */
|
#include <mach-o/dyld.h> /* _NSGetExecutablePath */
|
||||||
@ -34,7 +33,6 @@
|
|||||||
#include <unistd.h> /* sysconf */
|
#include <unistd.h> /* sysconf */
|
||||||
|
|
||||||
static uv_once_t once = UV_ONCE_INIT;
|
static uv_once_t once = UV_ONCE_INIT;
|
||||||
static uint64_t (*time_func)(void);
|
|
||||||
static mach_timebase_info_data_t timebase;
|
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) {
|
static void uv__hrtime_init_once(void) {
|
||||||
if (KERN_SUCCESS != mach_timebase_info(&timebase))
|
if (KERN_SUCCESS != mach_timebase_info(&timebase))
|
||||||
abort();
|
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) {
|
uint64_t uv__hrtime(uv_clocktype_t type) {
|
||||||
uv_once(&once, uv__hrtime_init_once);
|
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 <errno.h>
|
||||||
|
|
||||||
#include <paths.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/types.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
|
|||||||
292
src/unix/fs.c
292
src/unix/fs.c
@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -82,15 +83,6 @@
|
|||||||
# include <sys/statfs.h>
|
# include <sys/statfs.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__CYGWIN__) || \
|
|
||||||
(defined(__HAIKU__) && B_HAIKU_VERSION < B_HAIKU_VERSION_1_PRE_BETA_5) || \
|
|
||||||
(defined(__sun) && !defined(__illumos__))
|
|
||||||
#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
|
#if defined(_AIX) && _XOPEN_SOURCE <= 600
|
||||||
extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */
|
extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */
|
||||||
#endif
|
#endif
|
||||||
@ -147,7 +139,7 @@ extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */
|
|||||||
#define POST \
|
#define POST \
|
||||||
do { \
|
do { \
|
||||||
if (cb != NULL) { \
|
if (cb != NULL) { \
|
||||||
uv__req_register(loop, req); \
|
uv__req_register(loop); \
|
||||||
uv__work_submit(loop, \
|
uv__work_submit(loop, \
|
||||||
&req->work_req, \
|
&req->work_req, \
|
||||||
UV__WORK_FAST_IO, \
|
UV__WORK_FAST_IO, \
|
||||||
@ -211,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;
|
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_sec = time;
|
||||||
ts.tv_nsec = (time - ts.tv_sec) * 1e9;
|
ts.tv_nsec = (time - ts.tv_sec) * 1e9;
|
||||||
|
|
||||||
@ -229,41 +236,23 @@ UV_UNUSED(static struct timespec uv__fs_to_timespec(double time)) {
|
|||||||
}
|
}
|
||||||
return ts;
|
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) {
|
static ssize_t uv__fs_futime(uv_fs_t* req) {
|
||||||
#if defined(__linux__) \
|
#if defined(__APPLE__) \
|
||||||
|| defined(_AIX71) \
|
|| defined(_AIX71) \
|
||||||
|
|| defined(__DragonFly__) \
|
||||||
|
|| defined(__FreeBSD__) \
|
||||||
|| defined(__HAIKU__) \
|
|| defined(__HAIKU__) \
|
||||||
|| defined(__GNU__)
|
|| defined(__NetBSD__) \
|
||||||
|
|| defined(__OpenBSD__) \
|
||||||
|
|| defined(__linux__) \
|
||||||
|
|| defined(__sun)
|
||||||
struct timespec ts[2];
|
struct timespec ts[2];
|
||||||
ts[0] = uv__fs_to_timespec(req->atime);
|
ts[0] = uv__fs_to_timespec(req->atime);
|
||||||
ts[1] = uv__fs_to_timespec(req->mtime);
|
ts[1] = uv__fs_to_timespec(req->mtime);
|
||||||
return futimens(req->file, ts);
|
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__)
|
#elif defined(__MVS__)
|
||||||
attrib_t atr;
|
attrib_t atr;
|
||||||
memset(&atr, 0, sizeof(atr));
|
memset(&atr, 0, sizeof(atr));
|
||||||
@ -404,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) {
|
static ssize_t uv__fs_read(uv_fs_t* req) {
|
||||||
const struct iovec* bufs;
|
const struct iovec* bufs;
|
||||||
unsigned int iovmax;
|
unsigned int iovmax;
|
||||||
@ -431,7 +534,7 @@ static ssize_t uv__fs_read(uv_fs_t* req) {
|
|||||||
if (nbufs == 1)
|
if (nbufs == 1)
|
||||||
r = pread(fd, bufs->iov_base, bufs->iov_len, off);
|
r = pread(fd, bufs->iov_base, bufs->iov_len, off);
|
||||||
else if (nbufs > 1)
|
else if (nbufs > 1)
|
||||||
r = preadv(fd, bufs, nbufs, off);
|
r = uv__preadv(fd, bufs, nbufs, off);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __PASE__
|
#ifdef __PASE__
|
||||||
@ -689,14 +792,23 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) {
|
|||||||
|
|
||||||
static ssize_t uv__fs_realpath(uv_fs_t* req) {
|
static ssize_t uv__fs_realpath(uv_fs_t* req) {
|
||||||
char* buf;
|
char* buf;
|
||||||
|
char* tmp;
|
||||||
|
|
||||||
#if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809L
|
#if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809L
|
||||||
buf = realpath(req->path, NULL);
|
tmp = realpath(req->path, NULL);
|
||||||
if (buf == NULL)
|
if (tmp == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
buf = uv__strdup(tmp);
|
||||||
|
free(tmp); /* _Not_ uv__free. */
|
||||||
|
if (buf == NULL) {
|
||||||
|
errno = ENOMEM;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
|
|
||||||
|
(void)tmp;
|
||||||
|
|
||||||
len = uv__fs_pathmax_size(req->path);
|
len = uv__fs_pathmax_size(req->path);
|
||||||
buf = uv__malloc(len + 1);
|
buf = uv__malloc(len + 1);
|
||||||
|
|
||||||
@ -960,7 +1072,10 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) {
|
|||||||
|
|
||||||
return -1;
|
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;
|
off_t len;
|
||||||
ssize_t r;
|
ssize_t r;
|
||||||
@ -1024,25 +1139,20 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) {
|
|||||||
|
|
||||||
|
|
||||||
static ssize_t uv__fs_utime(uv_fs_t* req) {
|
static ssize_t uv__fs_utime(uv_fs_t* req) {
|
||||||
#if defined(__linux__) \
|
#if defined(__APPLE__) \
|
||||||
|| defined(_AIX71) \
|
|| defined(_AIX71) \
|
||||||
|| defined(__sun) \
|
|| defined(__DragonFly__) \
|
||||||
|| defined(__HAIKU__)
|
|| defined(__FreeBSD__) \
|
||||||
|
|| defined(__HAIKU__) \
|
||||||
|
|| defined(__NetBSD__) \
|
||||||
|
|| defined(__OpenBSD__) \
|
||||||
|
|| defined(__linux__) \
|
||||||
|
|| defined(__sun)
|
||||||
struct timespec ts[2];
|
struct timespec ts[2];
|
||||||
ts[0] = uv__fs_to_timespec(req->atime);
|
ts[0] = uv__fs_to_timespec(req->atime);
|
||||||
ts[1] = uv__fs_to_timespec(req->mtime);
|
ts[1] = uv__fs_to_timespec(req->mtime);
|
||||||
return utimensat(AT_FDCWD, req->path, ts, 0);
|
return utimensat(AT_FDCWD, req->path, ts, 0);
|
||||||
#elif defined(__APPLE__) \
|
#elif defined(_AIX) && !defined(_AIX71)
|
||||||
|| 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)
|
|
||||||
struct utimbuf buf;
|
struct utimbuf buf;
|
||||||
buf.actime = req->atime;
|
buf.actime = req->atime;
|
||||||
buf.modtime = req->mtime;
|
buf.modtime = req->mtime;
|
||||||
@ -1063,24 +1173,19 @@ static ssize_t uv__fs_utime(uv_fs_t* req) {
|
|||||||
|
|
||||||
|
|
||||||
static ssize_t uv__fs_lutime(uv_fs_t* req) {
|
static ssize_t uv__fs_lutime(uv_fs_t* req) {
|
||||||
#if defined(__linux__) || \
|
#if defined(__APPLE__) \
|
||||||
defined(_AIX71) || \
|
|| defined(_AIX71) \
|
||||||
defined(__sun) || \
|
|| defined(__DragonFly__) \
|
||||||
defined(__HAIKU__) || \
|
|| defined(__FreeBSD__) \
|
||||||
defined(__GNU__) || \
|
|| defined(__HAIKU__) \
|
||||||
defined(__OpenBSD__)
|
|| defined(__NetBSD__) \
|
||||||
|
|| defined(__OpenBSD__) \
|
||||||
|
|| defined(__linux__) \
|
||||||
|
|| defined(__sun)
|
||||||
struct timespec ts[2];
|
struct timespec ts[2];
|
||||||
ts[0] = uv__fs_to_timespec(req->atime);
|
ts[0] = uv__fs_to_timespec(req->atime);
|
||||||
ts[1] = uv__fs_to_timespec(req->mtime);
|
ts[1] = uv__fs_to_timespec(req->mtime);
|
||||||
return utimensat(AT_FDCWD, req->path, ts, AT_SYMLINK_NOFOLLOW);
|
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
|
#else
|
||||||
errno = ENOSYS;
|
errno = ENOSYS;
|
||||||
return -1;
|
return -1;
|
||||||
@ -1110,7 +1215,7 @@ static ssize_t uv__fs_write(uv_fs_t* req) {
|
|||||||
if (nbufs == 1)
|
if (nbufs == 1)
|
||||||
r = pwrite(fd, bufs->iov_base, bufs->iov_len, off);
|
r = pwrite(fd, bufs->iov_base, bufs->iov_len, off);
|
||||||
else if (nbufs > 1)
|
else if (nbufs > 1)
|
||||||
r = pwritev(fd, bufs, nbufs, off);
|
r = uv__pwritev(fd, bufs, nbufs, off);
|
||||||
}
|
}
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
@ -1123,6 +1228,7 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) {
|
|||||||
uv_file dstfd;
|
uv_file dstfd;
|
||||||
struct stat src_statsbuf;
|
struct stat src_statsbuf;
|
||||||
struct stat dst_statsbuf;
|
struct stat dst_statsbuf;
|
||||||
|
struct timespec times[2];
|
||||||
int dst_flags;
|
int dst_flags;
|
||||||
int result;
|
int result;
|
||||||
int err;
|
int err;
|
||||||
@ -1200,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) {
|
if (fchmod(dstfd, src_statsbuf.st_mode) == -1) {
|
||||||
err = UV__ERR(errno);
|
err = UV__ERR(errno);
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
@ -1617,7 +1752,7 @@ static void uv__fs_done(struct uv__work* w, int status) {
|
|||||||
uv_fs_t* req;
|
uv_fs_t* req;
|
||||||
|
|
||||||
req = container_of(w, uv_fs_t, work_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) {
|
if (status == UV_ECANCELED) {
|
||||||
assert(req->result == 0);
|
assert(req->result == 0);
|
||||||
@ -1628,6 +1763,16 @@ 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);
|
||||||
|
uv__work_submit(loop,
|
||||||
|
&req->work_req,
|
||||||
|
UV__WORK_FAST_IO,
|
||||||
|
uv__fs_work,
|
||||||
|
uv__fs_done);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int uv_fs_access(uv_loop_t* loop,
|
int uv_fs_access(uv_loop_t* loop,
|
||||||
uv_fs_t* req,
|
uv_fs_t* req,
|
||||||
const char* path,
|
const char* path,
|
||||||
@ -1754,6 +1899,9 @@ int uv_fs_ftruncate(uv_loop_t* loop,
|
|||||||
INIT(FTRUNCATE);
|
INIT(FTRUNCATE);
|
||||||
req->file = file;
|
req->file = file;
|
||||||
req->off = off;
|
req->off = off;
|
||||||
|
if (cb != NULL)
|
||||||
|
if (uv__iou_fs_ftruncate(loop, req))
|
||||||
|
return 0;
|
||||||
POST;
|
POST;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -276,10 +276,6 @@ static void uv__fsevents_event_cb(const FSEventStreamRef streamRef,
|
|||||||
path += handle->realpath_len;
|
path += handle->realpath_len;
|
||||||
len -= handle->realpath_len;
|
len -= handle->realpath_len;
|
||||||
|
|
||||||
/* Ignore events with path equal to directory itself */
|
|
||||||
if (len <= 1 && (flags & kFSEventStreamEventFlagItemIsDir))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
/* Since we're using fsevents to watch the file itself,
|
/* Since we're using fsevents to watch the file itself,
|
||||||
* realpath == path, and we now need to get the basename of the file back
|
* realpath == path, and we now need to get the basename of the file back
|
||||||
@ -793,6 +789,7 @@ int uv__cf_loop_signal(uv_loop_t* loop,
|
|||||||
|
|
||||||
/* Runs in UV loop to initialize handle */
|
/* Runs in UV loop to initialize handle */
|
||||||
int uv__fsevents_init(uv_fs_event_t* handle) {
|
int uv__fsevents_init(uv_fs_event_t* handle) {
|
||||||
|
char* buf;
|
||||||
int err;
|
int err;
|
||||||
uv__cf_loop_state_t* state;
|
uv__cf_loop_state_t* state;
|
||||||
|
|
||||||
@ -801,9 +798,13 @@ int uv__fsevents_init(uv_fs_event_t* handle) {
|
|||||||
return err;
|
return err;
|
||||||
|
|
||||||
/* Get absolute path to file */
|
/* Get absolute path to file */
|
||||||
handle->realpath = realpath(handle->path, NULL);
|
buf = realpath(handle->path, NULL);
|
||||||
if (handle->realpath == NULL)
|
if (buf == NULL)
|
||||||
return UV__ERR(errno);
|
return UV__ERR(errno);
|
||||||
|
handle->realpath = uv__strdup(buf);
|
||||||
|
free(buf); /* _Not_ uv__free. */
|
||||||
|
if (handle->realpath == NULL)
|
||||||
|
return UV_ENOMEM;
|
||||||
handle->realpath_len = strlen(handle->realpath);
|
handle->realpath_len = strlen(handle->realpath);
|
||||||
|
|
||||||
/* Initialize event queue */
|
/* Initialize event queue */
|
||||||
|
|||||||
@ -109,7 +109,7 @@ static void uv__getaddrinfo_done(struct uv__work* w, int status) {
|
|||||||
uv_getaddrinfo_t* req;
|
uv_getaddrinfo_t* req;
|
||||||
|
|
||||||
req = container_of(w, uv_getaddrinfo_t, work_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(). */
|
/* See initialization in uv_getaddrinfo(). */
|
||||||
if (req->hints)
|
if (req->hints)
|
||||||
|
|||||||
@ -58,7 +58,7 @@ static void uv__getnameinfo_done(struct uv__work* w, int status) {
|
|||||||
char* service;
|
char* service;
|
||||||
|
|
||||||
req = container_of(w, uv_getnameinfo_t, work_req);
|
req = container_of(w, uv_getnameinfo_t, work_req);
|
||||||
uv__req_unregister(req->loop, req);
|
uv__req_unregister(req->loop);
|
||||||
host = service = NULL;
|
host = service = NULL;
|
||||||
|
|
||||||
if (status == UV_ECANCELED) {
|
if (status == UV_ECANCELED) {
|
||||||
|
|||||||
@ -403,6 +403,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
|||||||
return UV_ENOSYS;
|
return UV_ENOSYS;
|
||||||
|
|
||||||
/* The first loop to get the size of the array to be allocated */
|
/* The first loop to get the size of the array to be allocated */
|
||||||
|
namelen = 0;
|
||||||
for (cur = ifap; cur; cur = cur->ifa_next) {
|
for (cur = ifap; cur; cur = cur->ifa_next) {
|
||||||
if (!(cur->ifa_addr->sa_family == AF_INET6 ||
|
if (!(cur->ifa_addr->sa_family == AF_INET6 ||
|
||||||
cur->ifa_addr->sa_family == AF_INET))
|
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))
|
if (!(cur->ifa_flags & IFF_UP && cur->ifa_flags & IFF_RUNNING))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
namelen += strlen(cur->ifa_name) + 1;
|
||||||
(*count)++;
|
(*count)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,11 +422,13 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Alloc the return interface structs */
|
/* Alloc the return interface structs */
|
||||||
*addresses = uv__calloc(*count, sizeof(**addresses));
|
*addresses = uv__calloc(1, *count * sizeof(**addresses) + namelen);
|
||||||
if (*addresses == NULL) {
|
if (*addresses == NULL) {
|
||||||
Qp2freeifaddrs(ifap);
|
Qp2freeifaddrs(ifap);
|
||||||
return UV_ENOMEM;
|
return UV_ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
name = (char*) &(*addresses)[*count];
|
||||||
address = *addresses;
|
address = *addresses;
|
||||||
|
|
||||||
/* The second loop to fill in the array */
|
/* 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))
|
if (!(cur->ifa_flags & IFF_UP && cur->ifa_flags & IFF_RUNNING))
|
||||||
continue;
|
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);
|
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) {
|
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||||
int i;
|
int count) {
|
||||||
|
|
||||||
for (i = 0; i < count; ++i) {
|
|
||||||
uv__free(addresses[i].name);
|
|
||||||
}
|
|
||||||
|
|
||||||
uv__free(addresses);
|
uv__free(addresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -35,6 +35,10 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.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) \
|
#define uv__msan_unpoison(p, n) \
|
||||||
do { \
|
do { \
|
||||||
@ -71,8 +75,11 @@
|
|||||||
# include <poll.h>
|
# include <poll.h>
|
||||||
#endif /* _AIX */
|
#endif /* _AIX */
|
||||||
|
|
||||||
#if defined(__APPLE__) && !TARGET_OS_IPHONE
|
#if defined(__APPLE__)
|
||||||
# include <AvailabilityMacros.h>
|
# include "darwin-syscalls.h"
|
||||||
|
# if !TARGET_OS_IPHONE
|
||||||
|
# include <AvailabilityMacros.h>
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -157,7 +164,8 @@ typedef struct uv__stream_queued_fds_s uv__stream_queued_fds_t;
|
|||||||
/* loop flags */
|
/* loop flags */
|
||||||
enum {
|
enum {
|
||||||
UV_LOOP_BLOCK_SIGPROF = 0x1,
|
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 */
|
/* flags of excluding ifaddr */
|
||||||
@ -243,6 +251,7 @@ int uv__close(int fd); /* preserves errno */
|
|||||||
int uv__close_nocheckstdio(int fd);
|
int uv__close_nocheckstdio(int fd);
|
||||||
int uv__close_nocancel(int fd);
|
int uv__close_nocancel(int fd);
|
||||||
int uv__socket(int domain, int type, int protocol);
|
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);
|
ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags);
|
||||||
void uv__make_close_pending(uv_handle_t* handle);
|
void uv__make_close_pending(uv_handle_t* handle);
|
||||||
int uv__getiovmax(void);
|
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_nodelay(int fd, int on);
|
||||||
int uv__tcp_keepalive(int fd, int on, unsigned int delay);
|
int uv__tcp_keepalive(int fd, int on, unsigned int delay);
|
||||||
|
|
||||||
|
/* tty */
|
||||||
|
void uv__tty_close(uv_tty_t* handle);
|
||||||
|
|
||||||
/* pipe */
|
/* pipe */
|
||||||
int uv__pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb);
|
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__process_close(uv_process_t* handle);
|
||||||
void uv__stream_close(uv_stream_t* handle);
|
void uv__stream_close(uv_stream_t* handle);
|
||||||
void uv__tcp_close(uv_tcp_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);
|
size_t uv__thread_stack_size(void);
|
||||||
void uv__udp_close(uv_udp_t* handle);
|
void uv__udp_close(uv_udp_t* handle);
|
||||||
void uv__udp_finish_close(uv_udp_t* handle);
|
void uv__udp_finish_close(uv_udp_t* handle);
|
||||||
@ -332,6 +346,7 @@ int uv__random_sysctl(void* buf, size_t buflen);
|
|||||||
/* io_uring */
|
/* io_uring */
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
int uv__iou_fs_close(uv_loop_t* loop, uv_fs_t* req);
|
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,
|
int uv__iou_fs_fsync_or_fdatasync(uv_loop_t* loop,
|
||||||
uv_fs_t* req,
|
uv_fs_t* req,
|
||||||
uint32_t fsync_flags);
|
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);
|
int uv__iou_fs_unlink(uv_loop_t* loop, uv_fs_t* req);
|
||||||
#else
|
#else
|
||||||
#define uv__iou_fs_close(loop, req) 0
|
#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_fsync_or_fdatasync(loop, req, fsync_flags) 0
|
||||||
#define uv__iou_fs_link(loop, req) 0
|
#define uv__iou_fs_link(loop, req) 0
|
||||||
#define uv__iou_fs_mkdir(loop, req) 0
|
#define uv__iou_fs_mkdir(loop, req) 0
|
||||||
@ -425,6 +441,7 @@ UV_UNUSED(static int uv__stat(const char* path, struct stat* s)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__)
|
||||||
|
void uv__fs_post(uv_loop_t* loop, uv_fs_t* req);
|
||||||
ssize_t
|
ssize_t
|
||||||
uv__fs_copy_file_range(int fd_in,
|
uv__fs_copy_file_range(int fd_in,
|
||||||
off_t* off_in,
|
off_t* off_in,
|
||||||
@ -471,4 +488,44 @@ uv__fs_copy_file_range(int fd_in,
|
|||||||
#define UV__CPU_AFFINITY_SUPPORTED 0
|
#define UV__CPU_AFFINITY_SUPPORTED 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
typedef struct {
|
||||||
|
long long quota_per_period;
|
||||||
|
long long period_length;
|
||||||
|
double proportions;
|
||||||
|
} uv__cpu_constraint;
|
||||||
|
|
||||||
|
int uv__get_constrained_cpu(uv__cpu_constraint* constraint);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__sun) && !defined(__illumos__)
|
||||||
|
#ifdef SO_FLOW_NAME
|
||||||
|
/* Since it's impossible to detect the Solaris 11.4 version via OS macros,
|
||||||
|
* so we check the presence of the socket option SO_FLOW_NAME that was first
|
||||||
|
* introduced to Solaris 11.4 and define a custom macro for determining 11.4.
|
||||||
|
*/
|
||||||
|
#define UV__SOLARIS_11_4 (1)
|
||||||
|
#else
|
||||||
|
#define UV__SOLARIS_11_4 (0)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(EVFILT_USER) && defined(NOTE_TRIGGER)
|
||||||
|
/* EVFILT_USER is available since OS X 10.6, DragonFlyBSD 4.0,
|
||||||
|
* FreeBSD 8.1, and NetBSD 10.0.
|
||||||
|
*
|
||||||
|
* Note that even though EVFILT_USER is defined on the current system,
|
||||||
|
* it may still fail to work at runtime somehow. In that case, we fall
|
||||||
|
* back to pipe-based signaling.
|
||||||
|
*/
|
||||||
|
#define UV__KQUEUE_EVFILT_USER 1
|
||||||
|
/* Magic number of identifier used for EVFILT_USER during runtime detection.
|
||||||
|
* There are no Google hits for this number when I create it. That way,
|
||||||
|
* people will be directed here if this number gets printed due to some
|
||||||
|
* kqueue error and they google for help. */
|
||||||
|
#define UV__KQUEUE_EVFILT_USER_IDENT 0x1e7e7711
|
||||||
|
#else
|
||||||
|
#define UV__KQUEUE_EVFILT_USER 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* UV_UNIX_INTERNAL_H_ */
|
#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) {
|
int uv__io_check_fd(uv_loop_t* loop, int fd) {
|
||||||
struct kevent ev;
|
struct kevent ev[2];
|
||||||
int rc;
|
struct stat sb;
|
||||||
|
#ifdef __APPLE__
|
||||||
|
char path[MAXPATHLEN];
|
||||||
|
#endif
|
||||||
|
|
||||||
rc = 0;
|
if (uv__fstat(fd, &sb))
|
||||||
EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
|
return UV__ERR(errno);
|
||||||
if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
|
|
||||||
rc = UV__ERR(errno);
|
|
||||||
|
|
||||||
EV_SET(&ev, fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
|
/* On FreeBSD, kqueue only supports EVFILT_READ notification for regular files
|
||||||
if (rc == 0)
|
* and always reports ready events for writing, resulting in busy-looping.
|
||||||
if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
|
*
|
||||||
abort();
|
* 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;
|
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) {
|
if (ev->filter == EVFILT_VNODE) {
|
||||||
assert(w->events == POLLIN);
|
assert(w->events == POLLIN);
|
||||||
assert(w->pevents == POLLIN);
|
assert(w->pevents == POLLIN);
|
||||||
@ -488,11 +526,21 @@ static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) {
|
|||||||
* the struct's kf_structsize must be initialised beforehand
|
* the struct's kf_structsize must be initialised beforehand
|
||||||
* whether with the KINFO_FILE_SIZE constant or this way.
|
* whether with the KINFO_FILE_SIZE constant or this way.
|
||||||
*/
|
*/
|
||||||
|
struct stat statbuf;
|
||||||
struct kinfo_file kf;
|
struct kinfo_file kf;
|
||||||
kf.kf_structsize = sizeof(kf);
|
|
||||||
|
|
||||||
if (fcntl(handle->event_watcher.fd, F_KINFO, &kf) == 0)
|
if (handle->event_watcher.fd != -1 &&
|
||||||
path = uv__basename_r(kf.kf_path);
|
(!uv__fstat(handle->event_watcher.fd, &statbuf) && !(statbuf.st_mode & S_IFDIR))) {
|
||||||
|
/* we are purposely not using KINFO_FILE_SIZE here
|
||||||
|
* as it is not available on non intl archs
|
||||||
|
* and here it gives 1392 too on intel.
|
||||||
|
* anyway, the man page also mentions we can proceed
|
||||||
|
* this way.
|
||||||
|
*/
|
||||||
|
kf.kf_structsize = sizeof(kf);
|
||||||
|
if (fcntl(handle->event_watcher.fd, F_KINFO, &kf) == 0)
|
||||||
|
path = uv__basename_r(kf.kf_path);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
handle->cb(handle, path, events, 0);
|
handle->cb(handle, path, events, 0);
|
||||||
|
|
||||||
|
|||||||
468
src/unix/linux.c
468
src/unix/linux.c
@ -37,12 +37,16 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <ifaddrs.h>
|
||||||
|
#include <net/ethernet.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
|
#include <netpacket/packet.h>
|
||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
#include <sys/inotify.h>
|
#include <sys/inotify.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/prctl.h>
|
#include <sys/prctl.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#include <sys/sysinfo.h>
|
#include <sys/sysinfo.h>
|
||||||
@ -120,27 +124,9 @@
|
|||||||
# endif
|
# endif
|
||||||
#endif /* __NR_getrandom */
|
#endif /* __NR_getrandom */
|
||||||
|
|
||||||
#define HAVE_IFADDRS_H 1
|
|
||||||
|
|
||||||
# if defined(__ANDROID_API__) && __ANDROID_API__ < 24
|
|
||||||
# undef HAVE_IFADDRS_H
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __UCLIBC__
|
|
||||||
# if __UCLIBC_MAJOR__ < 0 && __UCLIBC_MINOR__ < 9 && __UCLIBC_SUBLEVEL__ < 32
|
|
||||||
# undef HAVE_IFADDRS_H
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_IFADDRS_H
|
|
||||||
# include <ifaddrs.h>
|
|
||||||
# include <sys/socket.h>
|
|
||||||
# include <net/ethernet.h>
|
|
||||||
# include <netpacket/packet.h>
|
|
||||||
#endif /* HAVE_IFADDRS_H */
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
UV__IORING_SETUP_SQPOLL = 2u,
|
UV__IORING_SETUP_SQPOLL = 2u,
|
||||||
|
UV__IORING_SETUP_NO_SQARRAY = 0x10000u,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@ -162,6 +148,7 @@ enum {
|
|||||||
UV__IORING_OP_MKDIRAT = 37,
|
UV__IORING_OP_MKDIRAT = 37,
|
||||||
UV__IORING_OP_SYMLINKAT = 38,
|
UV__IORING_OP_SYMLINKAT = 38,
|
||||||
UV__IORING_OP_LINKAT = 39,
|
UV__IORING_OP_LINKAT = 39,
|
||||||
|
UV__IORING_OP_FTRUNCATE = 55,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@ -174,10 +161,6 @@ enum {
|
|||||||
UV__IORING_SQ_CQ_OVERFLOW = 2u,
|
UV__IORING_SQ_CQ_OVERFLOW = 2u,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
|
||||||
UV__MKDIRAT_SYMLINKAT_LINKAT = 1u,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct uv__io_cqring_offsets {
|
struct uv__io_cqring_offsets {
|
||||||
uint32_t head;
|
uint32_t head;
|
||||||
uint32_t tail;
|
uint32_t tail;
|
||||||
@ -472,29 +455,42 @@ 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__)
|
#if defined(__ANDROID_API__)
|
||||||
return 0; /* Possibly available but blocked by seccomp. */
|
return 0; /* Possibly available but blocked by seccomp. */
|
||||||
#elif defined(__arm__) && __SIZEOF_POINTER__ == 4
|
#elif defined(__arm__) && __SIZEOF_POINTER__ == 4
|
||||||
/* See https://github.com/libuv/libuv/issues/4158. */
|
/* See https://github.com/libuv/libuv/issues/4158. */
|
||||||
return 0; /* All 32 bits kernels appear buggy. */
|
return 0; /* All 32 bits kernels appear buggy. */
|
||||||
|
#elif defined(__powerpc64__) || defined(__ppc64__)
|
||||||
|
/* See https://github.com/libuv/libuv/issues/4283. */
|
||||||
|
return 0; /* Random SIGSEGV in signal handler. */
|
||||||
#else
|
#else
|
||||||
/* Ternary: unknown=0, yes=1, no=-1 */
|
/* Ternary: unknown=0, yes=1, no=-1 */
|
||||||
static _Atomic int use_io_uring;
|
static _Atomic int use_io_uring;
|
||||||
char* val;
|
char* val;
|
||||||
int use;
|
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);
|
use = atomic_load_explicit(&use_io_uring, memory_order_relaxed);
|
||||||
|
|
||||||
if (use == 0) {
|
if (use == 0) {
|
||||||
/* Older kernels have a bug where the sqpoll thread uses 100% CPU. */
|
|
||||||
use = uv__kernel_version() >= /* 5.10.186 */ 0x050ABA ? 1 : -1;
|
|
||||||
|
|
||||||
/* But users can still enable it if they so desire. */
|
|
||||||
val = getenv("UV_USE_IO_URING");
|
val = getenv("UV_USE_IO_URING");
|
||||||
if (val != NULL)
|
use = val != NULL && atoi(val) > 0 ? 1 : -1;
|
||||||
use = atoi(val) ? 1 : -1;
|
|
||||||
|
|
||||||
atomic_store_explicit(&use_io_uring, use, memory_order_relaxed);
|
atomic_store_explicit(&use_io_uring, use, memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,22 +509,29 @@ static void uv__iou_init(int epollfd,
|
|||||||
size_t sqlen;
|
size_t sqlen;
|
||||||
size_t maxlen;
|
size_t maxlen;
|
||||||
size_t sqelen;
|
size_t sqelen;
|
||||||
|
unsigned kernel_version;
|
||||||
|
uint32_t* sqarray;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
char* sq;
|
char* sq;
|
||||||
char* sqe;
|
char* sqe;
|
||||||
int ringfd;
|
int ringfd;
|
||||||
|
int no_sqarray;
|
||||||
|
|
||||||
sq = MAP_FAILED;
|
sq = MAP_FAILED;
|
||||||
sqe = MAP_FAILED;
|
sqe = MAP_FAILED;
|
||||||
|
|
||||||
if (!uv__use_io_uring())
|
if (!uv__use_io_uring(flags))
|
||||||
return;
|
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.
|
/* SQPOLL required CAP_SYS_NICE until linux v5.12 relaxed that requirement.
|
||||||
* Mostly academic because we check for a v5.13 kernel afterwards anyway.
|
* Mostly academic because we check for a v5.13 kernel afterwards anyway.
|
||||||
*/
|
*/
|
||||||
memset(¶ms, 0, sizeof(params));
|
memset(¶ms, 0, sizeof(params));
|
||||||
params.flags = flags;
|
params.flags = flags | no_sqarray;
|
||||||
|
|
||||||
if (flags & UV__IORING_SETUP_SQPOLL)
|
if (flags & UV__IORING_SETUP_SQPOLL)
|
||||||
params.sq_thread_idle = 10; /* milliseconds */
|
params.sq_thread_idle = 10; /* milliseconds */
|
||||||
@ -590,7 +593,6 @@ static void uv__iou_init(int epollfd,
|
|||||||
iou->sqhead = (uint32_t*) (sq + params.sq_off.head);
|
iou->sqhead = (uint32_t*) (sq + params.sq_off.head);
|
||||||
iou->sqtail = (uint32_t*) (sq + params.sq_off.tail);
|
iou->sqtail = (uint32_t*) (sq + params.sq_off.tail);
|
||||||
iou->sqmask = *(uint32_t*) (sq + params.sq_off.ring_mask);
|
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->sqflags = (uint32_t*) (sq + params.sq_off.flags);
|
||||||
iou->cqhead = (uint32_t*) (sq + params.cq_off.head);
|
iou->cqhead = (uint32_t*) (sq + params.cq_off.head);
|
||||||
iou->cqtail = (uint32_t*) (sq + params.cq_off.tail);
|
iou->cqtail = (uint32_t*) (sq + params.cq_off.tail);
|
||||||
@ -604,13 +606,13 @@ static void uv__iou_init(int epollfd,
|
|||||||
iou->sqelen = sqelen;
|
iou->sqelen = sqelen;
|
||||||
iou->ringfd = ringfd;
|
iou->ringfd = ringfd;
|
||||||
iou->in_flight = 0;
|
iou->in_flight = 0;
|
||||||
iou->flags = 0;
|
|
||||||
|
|
||||||
if (uv__kernel_version() >= /* 5.15.0 */ 0x050F00)
|
if (no_sqarray)
|
||||||
iou->flags |= UV__MKDIRAT_SYMLINKAT_LINKAT;
|
return;
|
||||||
|
|
||||||
|
sqarray = (uint32_t*) (sq + params.sq_off.array);
|
||||||
for (i = 0; i <= iou->sqmask; i++)
|
for (i = 0; i <= iou->sqmask; i++)
|
||||||
iou->sqarray[i] = i; /* Slot -> sqe identity mapping. */
|
sqarray[i] = i; /* Slot -> sqe identity mapping. */
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -626,7 +628,7 @@ fail:
|
|||||||
|
|
||||||
|
|
||||||
static void uv__iou_delete(struct uv__iou* iou) {
|
static void uv__iou_delete(struct uv__iou* iou) {
|
||||||
if (iou->ringfd != -1) {
|
if (iou->ringfd > -1) {
|
||||||
munmap(iou->sq, iou->maxlen);
|
munmap(iou->sq, iou->maxlen);
|
||||||
munmap(iou->sqe, iou->sqelen);
|
munmap(iou->sqe, iou->sqelen);
|
||||||
uv__close(iou->ringfd);
|
uv__close(iou->ringfd);
|
||||||
@ -640,7 +642,7 @@ int uv__platform_loop_init(uv_loop_t* loop) {
|
|||||||
|
|
||||||
lfields = uv__get_internal_fields(loop);
|
lfields = uv__get_internal_fields(loop);
|
||||||
lfields->ctl.ringfd = -1;
|
lfields->ctl.ringfd = -1;
|
||||||
lfields->iou.ringfd = -1;
|
lfields->iou.ringfd = -2; /* "uninitialized" */
|
||||||
|
|
||||||
loop->inotify_watchers = NULL;
|
loop->inotify_watchers = NULL;
|
||||||
loop->inotify_fd = -1;
|
loop->inotify_fd = -1;
|
||||||
@ -649,7 +651,6 @@ int uv__platform_loop_init(uv_loop_t* loop) {
|
|||||||
if (loop->backend_fd == -1)
|
if (loop->backend_fd == -1)
|
||||||
return UV__ERR(errno);
|
return UV__ERR(errno);
|
||||||
|
|
||||||
uv__iou_init(loop->backend_fd, &lfields->iou, 64, UV__IORING_SETUP_SQPOLL);
|
|
||||||
uv__iou_init(loop->backend_fd, &lfields->ctl, 256, 0);
|
uv__iou_init(loop->backend_fd, &lfields->ctl, 256, 0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -717,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
|
* This avoids a problem where the same file description remains open
|
||||||
* in another process, causing repeated junk epoll events.
|
* 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.
|
* 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
|
* 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.
|
* has the EPOLLWAKEUP flag set generates spurious audit syslog warnings.
|
||||||
*/
|
*/
|
||||||
memset(&dummy, 0, sizeof(dummy));
|
memset(&dummy, 0, sizeof(dummy));
|
||||||
|
epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -768,6 +763,22 @@ static struct uv__io_uring_sqe* uv__iou_get_sqe(struct uv__iou* iou,
|
|||||||
uint32_t mask;
|
uint32_t mask;
|
||||||
uint32_t slot;
|
uint32_t slot;
|
||||||
|
|
||||||
|
/* Lazily create the ring. State machine: -2 means uninitialized, -1 means
|
||||||
|
* initialization failed. Anything else is a valid ring file descriptor.
|
||||||
|
*/
|
||||||
|
if (iou->ringfd == -2) {
|
||||||
|
/* By default, the SQPOLL is not created. Enable only if the loop is
|
||||||
|
* configured with UV_LOOP_USE_IO_URING_SQPOLL 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" */
|
||||||
|
}
|
||||||
|
|
||||||
if (iou->ringfd == -1)
|
if (iou->ringfd == -1)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@ -791,7 +802,7 @@ static struct uv__io_uring_sqe* uv__iou_get_sqe(struct uv__iou* iou,
|
|||||||
req->work_req.done = NULL;
|
req->work_req.done = NULL;
|
||||||
uv__queue_init(&req->work_req.wq);
|
uv__queue_init(&req->work_req.wq);
|
||||||
|
|
||||||
uv__req_register(loop, req);
|
uv__req_register(loop);
|
||||||
iou->in_flight++;
|
iou->in_flight++;
|
||||||
|
|
||||||
return sqe;
|
return sqe;
|
||||||
@ -854,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,
|
int uv__iou_fs_fsync_or_fdatasync(uv_loop_t* loop,
|
||||||
uv_fs_t* req,
|
uv_fs_t* req,
|
||||||
uint32_t fsync_flags) {
|
uint32_t fsync_flags) {
|
||||||
@ -883,11 +914,10 @@ int uv__iou_fs_link(uv_loop_t* loop, uv_fs_t* req) {
|
|||||||
struct uv__io_uring_sqe* sqe;
|
struct uv__io_uring_sqe* sqe;
|
||||||
struct uv__iou* iou;
|
struct uv__iou* iou;
|
||||||
|
|
||||||
iou = &uv__get_internal_fields(loop)->iou;
|
if (uv__kernel_version() < /* 5.15.0 */0x050F00)
|
||||||
|
|
||||||
if (!(iou->flags & UV__MKDIRAT_SYMLINKAT_LINKAT))
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
iou = &uv__get_internal_fields(loop)->iou;
|
||||||
sqe = uv__iou_get_sqe(iou, loop, req);
|
sqe = uv__iou_get_sqe(iou, loop, req);
|
||||||
if (sqe == NULL)
|
if (sqe == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
@ -908,11 +938,10 @@ int uv__iou_fs_mkdir(uv_loop_t* loop, uv_fs_t* req) {
|
|||||||
struct uv__io_uring_sqe* sqe;
|
struct uv__io_uring_sqe* sqe;
|
||||||
struct uv__iou* iou;
|
struct uv__iou* iou;
|
||||||
|
|
||||||
iou = &uv__get_internal_fields(loop)->iou;
|
if (uv__kernel_version() < /* 5.15.0 */0x050F00)
|
||||||
|
|
||||||
if (!(iou->flags & UV__MKDIRAT_SYMLINKAT_LINKAT))
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
iou = &uv__get_internal_fields(loop)->iou;
|
||||||
sqe = uv__iou_get_sqe(iou, loop, req);
|
sqe = uv__iou_get_sqe(iou, loop, req);
|
||||||
if (sqe == NULL)
|
if (sqe == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
@ -976,11 +1005,10 @@ int uv__iou_fs_symlink(uv_loop_t* loop, uv_fs_t* req) {
|
|||||||
struct uv__io_uring_sqe* sqe;
|
struct uv__io_uring_sqe* sqe;
|
||||||
struct uv__iou* iou;
|
struct uv__iou* iou;
|
||||||
|
|
||||||
iou = &uv__get_internal_fields(loop)->iou;
|
if (uv__kernel_version() < /* 5.15.0 */0x050F00)
|
||||||
|
|
||||||
if (!(iou->flags & UV__MKDIRAT_SYMLINKAT_LINKAT))
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
iou = &uv__get_internal_fields(loop)->iou;
|
||||||
sqe = uv__iou_get_sqe(iou, loop, req);
|
sqe = uv__iou_get_sqe(iou, loop, req);
|
||||||
if (sqe == NULL)
|
if (sqe == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
@ -1159,9 +1187,15 @@ static void uv__poll_io_uring(uv_loop_t* loop, struct uv__iou* iou) {
|
|||||||
req = (uv_fs_t*) (uintptr_t) e->user_data;
|
req = (uv_fs_t*) (uintptr_t) e->user_data;
|
||||||
assert(req->type == UV_FS);
|
assert(req->type == UV_FS);
|
||||||
|
|
||||||
uv__req_unregister(loop, req);
|
uv__req_unregister(loop);
|
||||||
iou->in_flight--;
|
iou->in_flight--;
|
||||||
|
|
||||||
|
/* If the op is not supported by the kernel retry using the thread pool */
|
||||||
|
if (e->res == -EOPNOTSUPP) {
|
||||||
|
uv__fs_post(loop, req);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* io_uring stores error codes as negative numbers, same as libuv. */
|
/* io_uring stores error codes as negative numbers, same as libuv. */
|
||||||
req->result = e->res;
|
req->result = e->res;
|
||||||
|
|
||||||
@ -1205,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,
|
static void uv__epoll_ctl_prep(int epollfd,
|
||||||
struct uv__iou* ctl,
|
struct uv__iou* ctl,
|
||||||
struct epoll_event (*events)[256],
|
struct epoll_event (*events)[256],
|
||||||
@ -1216,45 +1254,28 @@ static void uv__epoll_ctl_prep(int epollfd,
|
|||||||
uint32_t mask;
|
uint32_t mask;
|
||||||
uint32_t slot;
|
uint32_t slot;
|
||||||
|
|
||||||
if (ctl->ringfd == -1) {
|
assert(op == EPOLL_CTL_ADD || op == EPOLL_CTL_MOD);
|
||||||
if (!epoll_ctl(epollfd, op, fd, e))
|
assert(ctl->ringfd != -1);
|
||||||
return;
|
|
||||||
|
|
||||||
if (op == EPOLL_CTL_DEL)
|
mask = ctl->sqmask;
|
||||||
return; /* Ignore errors, may be racing with another thread. */
|
slot = (*ctl->sqtail)++ & mask;
|
||||||
|
|
||||||
if (op != EPOLL_CTL_ADD)
|
pe = &(*events)[slot];
|
||||||
abort();
|
*pe = *e;
|
||||||
|
|
||||||
if (errno != EEXIST)
|
sqe = ctl->sqe;
|
||||||
abort();
|
sqe = &sqe[slot];
|
||||||
|
|
||||||
/* File descriptor that's been watched before, update event mask. */
|
memset(sqe, 0, sizeof(*sqe));
|
||||||
if (!epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, e))
|
sqe->addr = (uintptr_t) pe;
|
||||||
return;
|
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();
|
if ((*ctl->sqhead & mask) == (*ctl->sqtail & mask))
|
||||||
} else {
|
uv__epoll_ctl_flush(epollfd, ctl, events);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1395,8 +1416,22 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
|||||||
w->events = w->pevents;
|
w->events = w->pevents;
|
||||||
e.events = w->pevents;
|
e.events = w->pevents;
|
||||||
e.data.fd = w->fd;
|
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;
|
inv.events = events;
|
||||||
@ -1484,8 +1519,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
|||||||
*
|
*
|
||||||
* Ignore all errors because we may be racing with another thread
|
* Ignore all errors because we may be racing with another thread
|
||||||
* when the file descriptor is closed.
|
* 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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1620,36 +1659,17 @@ done:
|
|||||||
int uv_resident_set_memory(size_t* rss) {
|
int uv_resident_set_memory(size_t* rss) {
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
const char* s;
|
const char* s;
|
||||||
ssize_t n;
|
|
||||||
long val;
|
long val;
|
||||||
int fd;
|
int rc;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
do
|
/* rss: 24th element */
|
||||||
fd = open("/proc/self/stat", O_RDONLY);
|
rc = uv__slurp("/proc/self/stat", buf, sizeof(buf));
|
||||||
while (fd == -1 && errno == EINTR);
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
|
||||||
if (fd == -1)
|
/* find the last ')' */
|
||||||
return UV__ERR(errno);
|
s = strrchr(buf, ')');
|
||||||
|
|
||||||
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, ')');
|
|
||||||
if (s == NULL)
|
if (s == NULL)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@ -1661,9 +1681,7 @@ int uv_resident_set_memory(size_t* rss) {
|
|||||||
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
val = strtol(s, NULL, 10);
|
val = strtol(s, NULL, 10);
|
||||||
if (errno != 0)
|
if (val < 0 || errno != 0)
|
||||||
goto err;
|
|
||||||
if (val < 0)
|
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
*rss = val * getpagesize();
|
*rss = val * getpagesize();
|
||||||
@ -1696,16 +1714,22 @@ int uv_uptime(double* uptime) {
|
|||||||
int uv_cpu_info(uv_cpu_info_t** ci, int* count) {
|
int uv_cpu_info(uv_cpu_info_t** ci, int* count) {
|
||||||
#if defined(__PPC__)
|
#if defined(__PPC__)
|
||||||
static const char model_marker[] = "cpu\t\t: ";
|
static const char model_marker[] = "cpu\t\t: ";
|
||||||
|
static const char model_marker2[] = "";
|
||||||
#elif defined(__arm__)
|
#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__)
|
#elif defined(__aarch64__)
|
||||||
static const char model_marker[] = "CPU part\t: ";
|
static const char model_marker[] = "CPU part\t: ";
|
||||||
|
static const char model_marker2[] = "";
|
||||||
#elif defined(__mips__)
|
#elif defined(__mips__)
|
||||||
static const char model_marker[] = "cpu model\t\t: ";
|
static const char model_marker[] = "cpu model\t\t: ";
|
||||||
|
static const char model_marker2[] = "";
|
||||||
#elif defined(__loongarch__)
|
#elif defined(__loongarch__)
|
||||||
static const char model_marker[] = "cpu family\t\t: ";
|
static const char model_marker[] = "cpu family\t\t: ";
|
||||||
|
static const char model_marker2[] = "";
|
||||||
#else
|
#else
|
||||||
static const char model_marker[] = "model name\t: ";
|
static const char model_marker[] = "model name\t: ";
|
||||||
|
static const char model_marker2[] = "";
|
||||||
#endif
|
#endif
|
||||||
static const char parts[] =
|
static const char parts[] =
|
||||||
#ifdef __aarch64__
|
#ifdef __aarch64__
|
||||||
@ -1804,14 +1828,22 @@ int uv_cpu_info(uv_cpu_info_t** ci, int* count) {
|
|||||||
if (1 != fscanf(fp, "processor\t: %u\n", &cpu))
|
if (1 != fscanf(fp, "processor\t: %u\n", &cpu))
|
||||||
break; /* Parse error. */
|
break; /* Parse error. */
|
||||||
|
|
||||||
found = 0;
|
while (fgets(buf, sizeof(buf), fp)) {
|
||||||
while (!found && fgets(buf, sizeof(buf), fp))
|
if (!strncmp(buf, model_marker, sizeof(model_marker) - 1)) {
|
||||||
found = !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; /* Not found. */
|
||||||
goto next;
|
|
||||||
|
|
||||||
p = buf + sizeof(model_marker) - 1;
|
parts:
|
||||||
n = (int) strcspn(p, "\n");
|
n = (int) strcspn(p, "\n");
|
||||||
|
|
||||||
/* arm64: translate CPU part code to model name. */
|
/* arm64: translate CPU part code to model name. */
|
||||||
@ -1908,7 +1940,6 @@ nocpuinfo:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef HAVE_IFADDRS_H
|
|
||||||
static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
|
static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
|
||||||
if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
|
if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
|
||||||
return 1;
|
return 1;
|
||||||
@ -1922,18 +1953,16 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
|
|||||||
return exclude_type;
|
return exclude_type;
|
||||||
return !exclude_type;
|
return !exclude_type;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
|
/* TODO(bnoordhuis) share with bsd-ifaddrs.c */
|
||||||
int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||||
#ifndef HAVE_IFADDRS_H
|
|
||||||
*count = 0;
|
|
||||||
*addresses = NULL;
|
|
||||||
return UV_ENOSYS;
|
|
||||||
#else
|
|
||||||
struct ifaddrs *addrs, *ent;
|
|
||||||
uv_interface_address_t* address;
|
uv_interface_address_t* address;
|
||||||
|
struct sockaddr_ll* sll;
|
||||||
|
struct ifaddrs* addrs;
|
||||||
|
struct ifaddrs* ent;
|
||||||
|
size_t namelen;
|
||||||
|
char* name;
|
||||||
int i;
|
int i;
|
||||||
struct sockaddr_ll *sll;
|
|
||||||
|
|
||||||
*count = 0;
|
*count = 0;
|
||||||
*addresses = NULL;
|
*addresses = NULL;
|
||||||
@ -1942,10 +1971,12 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
|||||||
return UV__ERR(errno);
|
return UV__ERR(errno);
|
||||||
|
|
||||||
/* Count the number of interfaces */
|
/* Count the number of interfaces */
|
||||||
|
namelen = 0;
|
||||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
|
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
namelen += strlen(ent->ifa_name) + 1;
|
||||||
(*count)++;
|
(*count)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1955,19 +1986,22 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Make sure the memory is initiallized to zero using calloc() */
|
/* 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)) {
|
if (*addresses == NULL) {
|
||||||
freeifaddrs(addrs);
|
freeifaddrs(addrs);
|
||||||
return UV_ENOMEM;
|
return UV_ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
name = (char*) &(*addresses)[*count];
|
||||||
address = *addresses;
|
address = *addresses;
|
||||||
|
|
||||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
|
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
|
||||||
continue;
|
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) {
|
if (ent->ifa_addr->sa_family == AF_INET6) {
|
||||||
address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
|
address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
|
||||||
@ -2008,18 +2042,12 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
|||||||
freeifaddrs(addrs);
|
freeifaddrs(addrs);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* TODO(bnoordhuis) share with bsd-ifaddrs.c */
|
||||||
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||||
int count) {
|
int count) {
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
uv__free(addresses[i].name);
|
|
||||||
}
|
|
||||||
|
|
||||||
uv__free(addresses);
|
uv__free(addresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2276,6 +2304,136 @@ uint64_t uv_get_available_memory(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int uv__get_cgroupv2_constrained_cpu(const char* cgroup,
|
||||||
|
uv__cpu_constraint* constraint) {
|
||||||
|
char path[256];
|
||||||
|
char buf[1024];
|
||||||
|
unsigned int weight;
|
||||||
|
int cgroup_size;
|
||||||
|
const char* cgroup_trimmed;
|
||||||
|
char quota_buf[16];
|
||||||
|
|
||||||
|
if (strncmp(cgroup, "0::/", 4) != 0)
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
|
/* Trim ending \n by replacing it with a 0 */
|
||||||
|
cgroup_trimmed = cgroup + sizeof("0::/") - 1; /* Skip the prefix "0::/" */
|
||||||
|
cgroup_size = (int)strcspn(cgroup_trimmed, "\n"); /* Find the first slash */
|
||||||
|
|
||||||
|
/* Construct the path to the cpu.max file */
|
||||||
|
snprintf(path, sizeof(path), "/sys/fs/cgroup/%.*s/cpu.max", cgroup_size,
|
||||||
|
cgroup_trimmed);
|
||||||
|
|
||||||
|
/* Read cpu.max */
|
||||||
|
if (uv__slurp(path, buf, sizeof(buf)) < 0)
|
||||||
|
return UV_EIO;
|
||||||
|
|
||||||
|
if (sscanf(buf, "%15s %llu", quota_buf, &constraint->period_length) != 2)
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
|
if (strncmp(quota_buf, "max", 3) == 0)
|
||||||
|
constraint->quota_per_period = LLONG_MAX;
|
||||||
|
else if (sscanf(quota_buf, "%lld", &constraint->quota_per_period) != 1)
|
||||||
|
return UV_EINVAL; // conversion failed
|
||||||
|
|
||||||
|
/* Construct the path to the cpu.weight file */
|
||||||
|
snprintf(path, sizeof(path), "/sys/fs/cgroup/%.*s/cpu.weight", cgroup_size,
|
||||||
|
cgroup_trimmed);
|
||||||
|
|
||||||
|
/* Read cpu.weight */
|
||||||
|
if (uv__slurp(path, buf, sizeof(buf)) < 0)
|
||||||
|
return UV_EIO;
|
||||||
|
|
||||||
|
if (sscanf(buf, "%u", &weight) != 1)
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
|
constraint->proportions = (double)weight / 100.0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* uv__cgroup1_find_cpu_controller(const char* cgroup,
|
||||||
|
int* cgroup_size) {
|
||||||
|
/* Seek to the cpu controller line. */
|
||||||
|
char* cgroup_cpu = strstr(cgroup, ":cpu,");
|
||||||
|
|
||||||
|
if (cgroup_cpu != NULL) {
|
||||||
|
/* Skip the controller prefix to the start of the cgroup path. */
|
||||||
|
cgroup_cpu += sizeof(":cpu,") - 1;
|
||||||
|
/* Determine the length of the cgroup path, excluding the newline. */
|
||||||
|
*cgroup_size = (int)strcspn(cgroup_cpu, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return cgroup_cpu;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int uv__get_cgroupv1_constrained_cpu(const char* cgroup,
|
||||||
|
uv__cpu_constraint* constraint) {
|
||||||
|
char path[256];
|
||||||
|
char buf[1024];
|
||||||
|
unsigned int shares;
|
||||||
|
int cgroup_size;
|
||||||
|
char* cgroup_cpu;
|
||||||
|
|
||||||
|
cgroup_cpu = uv__cgroup1_find_cpu_controller(cgroup, &cgroup_size);
|
||||||
|
|
||||||
|
if (cgroup_cpu == NULL)
|
||||||
|
return UV_EIO;
|
||||||
|
|
||||||
|
/* Construct the path to the cpu.cfs_quota_us file */
|
||||||
|
snprintf(path, sizeof(path), "/sys/fs/cgroup/%.*s/cpu.cfs_quota_us",
|
||||||
|
cgroup_size, cgroup_cpu);
|
||||||
|
|
||||||
|
if (uv__slurp(path, buf, sizeof(buf)) < 0)
|
||||||
|
return UV_EIO;
|
||||||
|
|
||||||
|
if (sscanf(buf, "%lld", &constraint->quota_per_period) != 1)
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
|
/* Construct the path to the cpu.cfs_period_us file */
|
||||||
|
snprintf(path, sizeof(path), "/sys/fs/cgroup/%.*s/cpu.cfs_period_us",
|
||||||
|
cgroup_size, cgroup_cpu);
|
||||||
|
|
||||||
|
/* Read cpu.cfs_period_us */
|
||||||
|
if (uv__slurp(path, buf, sizeof(buf)) < 0)
|
||||||
|
return UV_EIO;
|
||||||
|
|
||||||
|
if (sscanf(buf, "%lld", &constraint->period_length) != 1)
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
|
/* Construct the path to the cpu.shares file */
|
||||||
|
snprintf(path, sizeof(path), "/sys/fs/cgroup/%.*s/cpu.shares", cgroup_size,
|
||||||
|
cgroup_cpu);
|
||||||
|
|
||||||
|
/* Read cpu.shares */
|
||||||
|
if (uv__slurp(path, buf, sizeof(buf)) < 0)
|
||||||
|
return UV_EIO;
|
||||||
|
|
||||||
|
if (sscanf(buf, "%u", &shares) != 1)
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
|
constraint->proportions = (double)shares / 1024.0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uv__get_constrained_cpu(uv__cpu_constraint* constraint) {
|
||||||
|
char cgroup[1024];
|
||||||
|
|
||||||
|
/* Read the cgroup from /proc/self/cgroup */
|
||||||
|
if (uv__slurp("/proc/self/cgroup", cgroup, sizeof(cgroup)) < 0)
|
||||||
|
return UV_EIO;
|
||||||
|
|
||||||
|
/* Check if the system is using cgroup v2 by examining /proc/self/cgroup
|
||||||
|
* The entry for cgroup v2 is always in the format "0::$PATH"
|
||||||
|
* see https://docs.kernel.org/admin-guide/cgroup-v2.html */
|
||||||
|
if (strncmp(cgroup, "0::/", 4) == 0)
|
||||||
|
return uv__get_cgroupv2_constrained_cpu(cgroup, constraint);
|
||||||
|
else
|
||||||
|
return uv__get_cgroupv1_constrained_cpu(cgroup, constraint);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void uv_loadavg(double avg[3]) {
|
void uv_loadavg(double avg[3]) {
|
||||||
struct sysinfo info;
|
struct sysinfo info;
|
||||||
char buf[128]; /* Large enough to hold all of /proc/loadavg. */
|
char buf[128]; /* Large enough to hold all of /proc/loadavg. */
|
||||||
|
|||||||
@ -217,6 +217,14 @@ int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) {
|
|||||||
return 0;
|
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)
|
if (option != UV_LOOP_BLOCK_SIGNAL)
|
||||||
return UV_ENOSYS;
|
return UV_ENOSYS;
|
||||||
|
|
||||||
|
|||||||
136
src/unix/pipe.c
136
src/unix/pipe.c
@ -30,6 +30,19 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* Does the file path contain embedded nul bytes? */
|
||||||
|
static int includes_nul(const char *s, size_t n) {
|
||||||
|
if (n == 0)
|
||||||
|
return 0;
|
||||||
|
#ifdef __linux__
|
||||||
|
/* Accept abstract socket namespace path ("\0/virtual/path"). */
|
||||||
|
s++;
|
||||||
|
n--;
|
||||||
|
#endif
|
||||||
|
return NULL != memchr(s, '\0', n);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) {
|
int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) {
|
||||||
uv__stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE);
|
uv__stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE);
|
||||||
handle->shutdown_req = NULL;
|
handle->shutdown_req = NULL;
|
||||||
@ -53,6 +66,7 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
|||||||
char* pipe_fname;
|
char* pipe_fname;
|
||||||
int sockfd;
|
int sockfd;
|
||||||
int err;
|
int err;
|
||||||
|
socklen_t addrlen;
|
||||||
|
|
||||||
pipe_fname = NULL;
|
pipe_fname = NULL;
|
||||||
|
|
||||||
@ -62,15 +76,17 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
|||||||
if (name == NULL)
|
if (name == NULL)
|
||||||
return UV_EINVAL;
|
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)
|
if (namelen == 0)
|
||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
|
|
||||||
#ifndef __linux__
|
|
||||||
/* Abstract socket namespace only works on Linux. */
|
|
||||||
if (*name == '\0')
|
|
||||||
return UV_EINVAL;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (includes_nul(name, namelen))
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
if (flags & UV_PIPE_NO_TRUNCATE)
|
if (flags & UV_PIPE_NO_TRUNCATE)
|
||||||
if (namelen > sizeof(saddr.sun_path))
|
if (namelen > sizeof(saddr.sun_path))
|
||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
@ -90,10 +106,15 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
|||||||
* We unlink the file later but abstract sockets disappear
|
* We unlink the file later but abstract sockets disappear
|
||||||
* automatically since they're not real file system entities.
|
* automatically since they're not real file system entities.
|
||||||
*/
|
*/
|
||||||
if (*name != '\0') {
|
if (*name == '\0') {
|
||||||
pipe_fname = uv__strdup(name);
|
addrlen = offsetof(struct sockaddr_un, sun_path) + namelen;
|
||||||
|
} else {
|
||||||
|
pipe_fname = uv__malloc(namelen + 1);
|
||||||
if (pipe_fname == NULL)
|
if (pipe_fname == NULL)
|
||||||
return UV_ENOMEM;
|
return UV_ENOMEM;
|
||||||
|
memcpy(pipe_fname, name, namelen);
|
||||||
|
pipe_fname[namelen] = '\0';
|
||||||
|
addrlen = sizeof saddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = uv__socket(AF_UNIX, SOCK_STREAM, 0);
|
err = uv__socket(AF_UNIX, SOCK_STREAM, 0);
|
||||||
@ -105,7 +126,7 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
|||||||
memcpy(&saddr.sun_path, name, namelen);
|
memcpy(&saddr.sun_path, name, namelen);
|
||||||
saddr.sun_family = AF_UNIX;
|
saddr.sun_family = AF_UNIX;
|
||||||
|
|
||||||
if (bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr)) {
|
if (bind(sockfd, (struct sockaddr*)&saddr, addrlen)) {
|
||||||
err = UV__ERR(errno);
|
err = UV__ERR(errno);
|
||||||
/* Convert ENOENT to EACCES for compatibility with Windows. */
|
/* Convert ENOENT to EACCES for compatibility with Windows. */
|
||||||
if (err == UV_ENOENT)
|
if (err == UV_ENOENT)
|
||||||
@ -117,7 +138,7 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
|||||||
|
|
||||||
/* Success. */
|
/* Success. */
|
||||||
handle->flags |= UV_HANDLE_BOUND;
|
handle->flags |= UV_HANDLE_BOUND;
|
||||||
handle->pipe_fname = pipe_fname; /* NULL or a strdup'ed copy. */
|
handle->pipe_fname = pipe_fname; /* NULL or a copy of |name| */
|
||||||
handle->io_watcher.fd = sockfd;
|
handle->io_watcher.fd = sockfd;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -210,7 +231,22 @@ void uv_pipe_connect(uv_connect_t* req,
|
|||||||
uv_pipe_t* handle,
|
uv_pipe_t* handle,
|
||||||
const char* name,
|
const char* name,
|
||||||
uv_connect_cb cb) {
|
uv_connect_cb cb) {
|
||||||
uv_pipe_connect2(req, handle, name, strlen(name), 0, cb);
|
int err;
|
||||||
|
|
||||||
|
err = uv_pipe_connect2(req, handle, name, strlen(name), 0, cb);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
handle->delayed_error = err;
|
||||||
|
handle->connect_req = req;
|
||||||
|
|
||||||
|
uv__req_init(handle->loop, req, UV_CONNECT);
|
||||||
|
req->handle = (uv_stream_t*) handle;
|
||||||
|
req->cb = cb;
|
||||||
|
uv__queue_init(&req->queue);
|
||||||
|
|
||||||
|
/* Force callback to run on next tick in case of error. */
|
||||||
|
uv__io_feed(handle->loop, &handle->io_watcher);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -224,6 +260,7 @@ int uv_pipe_connect2(uv_connect_t* req,
|
|||||||
int new_sock;
|
int new_sock;
|
||||||
int err;
|
int err;
|
||||||
int r;
|
int r;
|
||||||
|
socklen_t addrlen;
|
||||||
|
|
||||||
if (flags & ~UV_PIPE_NO_TRUNCATE)
|
if (flags & ~UV_PIPE_NO_TRUNCATE)
|
||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
@ -234,11 +271,8 @@ int uv_pipe_connect2(uv_connect_t* req,
|
|||||||
if (namelen == 0)
|
if (namelen == 0)
|
||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
|
|
||||||
#ifndef __linux__
|
if (includes_nul(name, namelen))
|
||||||
/* Abstract socket namespace only works on Linux. */
|
|
||||||
if (*name == '\0')
|
|
||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
#endif
|
|
||||||
|
|
||||||
if (flags & UV_PIPE_NO_TRUNCATE)
|
if (flags & UV_PIPE_NO_TRUNCATE)
|
||||||
if (namelen > sizeof(saddr.sun_path))
|
if (namelen > sizeof(saddr.sun_path))
|
||||||
@ -261,9 +295,13 @@ int uv_pipe_connect2(uv_connect_t* req,
|
|||||||
memcpy(&saddr.sun_path, name, namelen);
|
memcpy(&saddr.sun_path, name, namelen);
|
||||||
saddr.sun_family = AF_UNIX;
|
saddr.sun_family = AF_UNIX;
|
||||||
|
|
||||||
|
if (*name == '\0')
|
||||||
|
addrlen = offsetof(struct sockaddr_un, sun_path) + namelen;
|
||||||
|
else
|
||||||
|
addrlen = sizeof saddr;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
r = connect(uv__stream_fd(handle),
|
r = connect(uv__stream_fd(handle), (struct sockaddr*)&saddr, addrlen);
|
||||||
(struct sockaddr*)&saddr, sizeof saddr);
|
|
||||||
}
|
}
|
||||||
while (r == -1 && errno == EINTR);
|
while (r == -1 && errno == EINTR);
|
||||||
|
|
||||||
@ -295,7 +333,7 @@ out:
|
|||||||
handle->connect_req = req;
|
handle->connect_req = req;
|
||||||
|
|
||||||
uv__req_init(handle->loop, req, UV_CONNECT);
|
uv__req_init(handle->loop, req, UV_CONNECT);
|
||||||
req->handle = (uv_stream_t*)handle;
|
req->handle = (uv_stream_t*) handle;
|
||||||
req->cb = cb;
|
req->cb = cb;
|
||||||
uv__queue_init(&req->queue);
|
uv__queue_init(&req->queue);
|
||||||
|
|
||||||
@ -311,10 +349,20 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
|
|||||||
uv__peersockfunc func,
|
uv__peersockfunc func,
|
||||||
char* buffer,
|
char* buffer,
|
||||||
size_t* size) {
|
size_t* size) {
|
||||||
|
#if defined(__linux__)
|
||||||
|
static const int is_linux = 1;
|
||||||
|
#else
|
||||||
|
static const int is_linux = 0;
|
||||||
|
#endif
|
||||||
struct sockaddr_un sa;
|
struct sockaddr_un sa;
|
||||||
socklen_t addrlen;
|
socklen_t addrlen;
|
||||||
|
size_t slop;
|
||||||
|
char* p;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
if (buffer == NULL || size == NULL || *size == 0)
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
addrlen = sizeof(sa);
|
addrlen = sizeof(sa);
|
||||||
memset(&sa, 0, addrlen);
|
memset(&sa, 0, addrlen);
|
||||||
err = uv__getsockpeername((const uv_handle_t*) handle,
|
err = uv__getsockpeername((const uv_handle_t*) handle,
|
||||||
@ -326,17 +374,20 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__linux__)
|
slop = 1;
|
||||||
if (sa.sun_path[0] == 0)
|
if (is_linux && sa.sun_path[0] == '\0') {
|
||||||
/* Linux abstract namespace */
|
/* Linux abstract namespace. Not zero-terminated. */
|
||||||
|
slop = 0;
|
||||||
addrlen -= offsetof(struct sockaddr_un, sun_path);
|
addrlen -= offsetof(struct sockaddr_un, sun_path);
|
||||||
else
|
} else {
|
||||||
#endif
|
p = memchr(sa.sun_path, '\0', sizeof(sa.sun_path));
|
||||||
addrlen = strlen(sa.sun_path);
|
if (p == NULL)
|
||||||
|
p = ARRAY_END(sa.sun_path);
|
||||||
|
addrlen = p - sa.sun_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((size_t)addrlen + slop > *size) {
|
||||||
if ((size_t)addrlen >= *size) {
|
*size = addrlen + slop;
|
||||||
*size = addrlen + 1;
|
|
||||||
return UV_ENOBUFS;
|
return UV_ENOBUFS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,7 +447,7 @@ uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) {
|
|||||||
int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
|
int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
|
||||||
unsigned desired_mode;
|
unsigned desired_mode;
|
||||||
struct stat pipe_stat;
|
struct stat pipe_stat;
|
||||||
char* name_buffer;
|
char name_buffer[1 + UV__PATH_MAX];
|
||||||
size_t name_len;
|
size_t name_len;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -409,26 +460,14 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
|
|||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
|
|
||||||
/* Unfortunately fchmod does not work on all platforms, we will use chmod. */
|
/* Unfortunately fchmod does not work on all platforms, we will use chmod. */
|
||||||
name_len = 0;
|
name_len = sizeof(name_buffer);
|
||||||
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;
|
|
||||||
|
|
||||||
r = uv_pipe_getsockname(handle, name_buffer, &name_len);
|
r = uv_pipe_getsockname(handle, name_buffer, &name_len);
|
||||||
if (r != 0) {
|
if (r != 0)
|
||||||
uv__free(name_buffer);
|
|
||||||
return r;
|
return r;
|
||||||
}
|
|
||||||
|
|
||||||
/* stat must be used as fstat has a bug on Darwin */
|
/* stat must be used as fstat has a bug on Darwin */
|
||||||
if (uv__stat(name_buffer, &pipe_stat) == -1) {
|
if (uv__stat(name_buffer, &pipe_stat) == -1)
|
||||||
uv__free(name_buffer);
|
return UV__ERR(errno);
|
||||||
return -errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
desired_mode = 0;
|
desired_mode = 0;
|
||||||
if (mode & UV_READABLE)
|
if (mode & UV_READABLE)
|
||||||
@ -437,15 +476,12 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
|
|||||||
desired_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
|
desired_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
|
||||||
|
|
||||||
/* Exit early if pipe already has desired mode. */
|
/* Exit early if pipe already has desired mode. */
|
||||||
if ((pipe_stat.st_mode & desired_mode) == desired_mode) {
|
if ((pipe_stat.st_mode & desired_mode) == desired_mode)
|
||||||
uv__free(name_buffer);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
pipe_stat.st_mode |= desired_mode;
|
pipe_stat.st_mode |= desired_mode;
|
||||||
|
|
||||||
r = chmod(name_buffer, pipe_stat.st_mode);
|
r = chmod(name_buffer, pipe_stat.st_mode);
|
||||||
uv__free(name_buffer);
|
|
||||||
|
|
||||||
return r != -1 ? 0 : UV__ERR(errno);
|
return r != -1 ? 0 : UV__ERR(errno);
|
||||||
}
|
}
|
||||||
@ -454,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) {
|
int uv_pipe(uv_os_fd_t fds[2], int read_flags, int write_flags) {
|
||||||
uv_os_fd_t temp[2];
|
uv_os_fd_t temp[2];
|
||||||
int err;
|
int err;
|
||||||
#if defined(__FreeBSD__) || defined(__linux__)
|
#if defined(__linux__) || \
|
||||||
|
defined(__FreeBSD__) || \
|
||||||
|
defined(__OpenBSD__) || \
|
||||||
|
defined(__DragonFly__) || \
|
||||||
|
defined(__NetBSD__)
|
||||||
int flags = O_CLOEXEC;
|
int flags = O_CLOEXEC;
|
||||||
|
|
||||||
if ((read_flags & UV_NONBLOCK_PIPE) && (write_flags & UV_NONBLOCK_PIPE))
|
if ((read_flags & UV_NONBLOCK_PIPE) && (write_flags & UV_NONBLOCK_PIPE))
|
||||||
|
|||||||
@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
|
|
||||||
static void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
|
static void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
|
||||||
|
|||||||
@ -55,7 +55,8 @@
|
|||||||
extern char **environ;
|
extern char **environ;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__) || \
|
||||||
|
defined(__GNU__)
|
||||||
# include <grp.h>
|
# include <grp.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -63,11 +64,7 @@ extern char **environ;
|
|||||||
# include "zos-base.h"
|
# include "zos-base.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__APPLE__) || \
|
#ifdef UV_HAVE_KQUEUE
|
||||||
defined(__DragonFly__) || \
|
|
||||||
defined(__FreeBSD__) || \
|
|
||||||
defined(__NetBSD__) || \
|
|
||||||
defined(__OpenBSD__)
|
|
||||||
#include <sys/event.h>
|
#include <sys/event.h>
|
||||||
#else
|
#else
|
||||||
#define UV_USE_SIGCHLD
|
#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]) {
|
static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) {
|
||||||
int mask;
|
int mask;
|
||||||
int fd;
|
int fd;
|
||||||
|
int ret;
|
||||||
|
int size;
|
||||||
|
int i;
|
||||||
|
|
||||||
mask = UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD | UV_INHERIT_STREAM;
|
mask = UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD | UV_INHERIT_STREAM;
|
||||||
|
size = 64 * 1024;
|
||||||
|
|
||||||
switch (container->flags & mask) {
|
switch (container->flags & mask) {
|
||||||
case UV_IGNORE:
|
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);
|
assert(container->data.stream != NULL);
|
||||||
if (container->data.stream->type != UV_NAMED_PIPE)
|
if (container->data.stream->type != UV_NAMED_PIPE)
|
||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
else
|
else {
|
||||||
return uv_socketpair(SOCK_STREAM, 0, fds, 0, 0);
|
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_FD:
|
||||||
case UV_INHERIT_STREAM:
|
case UV_INHERIT_STREAM:
|
||||||
@ -972,6 +982,7 @@ int uv_spawn(uv_loop_t* loop,
|
|||||||
assert(!(options->flags & ~(UV_PROCESS_DETACHED |
|
assert(!(options->flags & ~(UV_PROCESS_DETACHED |
|
||||||
UV_PROCESS_SETGID |
|
UV_PROCESS_SETGID |
|
||||||
UV_PROCESS_SETUID |
|
UV_PROCESS_SETUID |
|
||||||
|
UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME |
|
||||||
UV_PROCESS_WINDOWS_HIDE |
|
UV_PROCESS_WINDOWS_HIDE |
|
||||||
UV_PROCESS_WINDOWS_HIDE_CONSOLE |
|
UV_PROCESS_WINDOWS_HIDE_CONSOLE |
|
||||||
UV_PROCESS_WINDOWS_HIDE_GUI |
|
UV_PROCESS_WINDOWS_HIDE_GUI |
|
||||||
|
|||||||
@ -195,7 +195,7 @@ static void uv__signal_handler(int signum) {
|
|||||||
|
|
||||||
for (handle = uv__signal_first_handle(signum);
|
for (handle = uv__signal_first_handle(signum);
|
||||||
handle != NULL && handle->signum == 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;
|
int r;
|
||||||
|
|
||||||
msg.signum = signum;
|
msg.signum = signum;
|
||||||
|
|||||||
@ -457,7 +457,7 @@ void uv__stream_destroy(uv_stream_t* stream) {
|
|||||||
assert(stream->flags & UV_HANDLE_CLOSED);
|
assert(stream->flags & UV_HANDLE_CLOSED);
|
||||||
|
|
||||||
if (stream->connect_req) {
|
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->cb(stream->connect_req, UV_ECANCELED);
|
||||||
stream->connect_req = NULL;
|
stream->connect_req = NULL;
|
||||||
}
|
}
|
||||||
@ -642,7 +642,7 @@ static void uv__drain(uv_stream_t* stream) {
|
|||||||
if ((stream->flags & UV_HANDLE_CLOSING) ||
|
if ((stream->flags & UV_HANDLE_CLOSING) ||
|
||||||
!(stream->flags & UV_HANDLE_SHUT)) {
|
!(stream->flags & UV_HANDLE_SHUT)) {
|
||||||
stream->shutdown_req = NULL;
|
stream->shutdown_req = NULL;
|
||||||
uv__req_unregister(stream->loop, req);
|
uv__req_unregister(stream->loop);
|
||||||
|
|
||||||
err = 0;
|
err = 0;
|
||||||
if (stream->flags & UV_HANDLE_CLOSING)
|
if (stream->flags & UV_HANDLE_CLOSING)
|
||||||
@ -698,7 +698,8 @@ static int uv__write_req_update(uv_stream_t* stream,
|
|||||||
|
|
||||||
do {
|
do {
|
||||||
len = n < buf->len ? n : buf->len;
|
len = n < buf->len ? n : buf->len;
|
||||||
buf->base += len;
|
if (buf->len != 0)
|
||||||
|
buf->base += len;
|
||||||
buf->len -= len;
|
buf->len -= len;
|
||||||
buf += (buf->len == 0); /* Advance to next buffer if this one is empty. */
|
buf += (buf->len == 0); /* Advance to next buffer if this one is empty. */
|
||||||
n -= len;
|
n -= len;
|
||||||
@ -912,7 +913,7 @@ static void uv__write_callbacks(uv_stream_t* stream) {
|
|||||||
q = uv__queue_head(&pq);
|
q = uv__queue_head(&pq);
|
||||||
req = uv__queue_data(q, uv_write_t, queue);
|
req = uv__queue_data(q, uv_write_t, queue);
|
||||||
uv__queue_remove(q);
|
uv__queue_remove(q);
|
||||||
uv__req_unregister(stream->loop, req);
|
uv__req_unregister(stream->loop);
|
||||||
|
|
||||||
if (req->bufs != NULL) {
|
if (req->bufs != NULL) {
|
||||||
stream->write_queue_size -= uv__write_req_size(req);
|
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) {
|
static int uv__stream_recv_cmsg(uv_stream_t* stream, struct msghdr* msg) {
|
||||||
struct cmsghdr* cmsg;
|
struct cmsghdr* cmsg;
|
||||||
|
char* p;
|
||||||
|
char* pe;
|
||||||
int fd;
|
int fd;
|
||||||
int err;
|
int err;
|
||||||
size_t i;
|
|
||||||
size_t count;
|
size_t count;
|
||||||
|
|
||||||
|
err = 0;
|
||||||
for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
|
for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
|
||||||
if (cmsg->cmsg_type != SCM_RIGHTS) {
|
if (cmsg->cmsg_type != SCM_RIGHTS) {
|
||||||
fprintf(stderr, "ignoring non-SCM_RIGHTS ancillary data: %d\n",
|
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);
|
assert(count % sizeof(fd) == 0);
|
||||||
count /= sizeof(fd);
|
count /= sizeof(fd);
|
||||||
|
|
||||||
for (i = 0; i < count; i++) {
|
p = (void*) CMSG_DATA(cmsg);
|
||||||
memcpy(&fd, (char*) CMSG_DATA(cmsg) + i * sizeof(fd), sizeof(fd));
|
pe = p + count * sizeof(fd);
|
||||||
/* Already has accepted fd, queue now */
|
|
||||||
if (stream->accepted_fd != -1) {
|
while (p < pe) {
|
||||||
err = uv__stream_queue_fd(stream, fd);
|
memcpy(&fd, p, sizeof(fd));
|
||||||
if (err != 0) {
|
p += sizeof(fd);
|
||||||
/* Close rest */
|
|
||||||
for (; i < count; i++)
|
if (err == 0) {
|
||||||
uv__close(fd);
|
if (stream->accepted_fd == -1)
|
||||||
return err;
|
stream->accepted_fd = fd;
|
||||||
}
|
else
|
||||||
} else {
|
err = uv__stream_queue_fd(stream, fd);
|
||||||
stream->accepted_fd = 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;
|
return;
|
||||||
|
|
||||||
stream->connect_req = NULL;
|
stream->connect_req = NULL;
|
||||||
uv__req_unregister(stream->loop, req);
|
uv__req_unregister(stream->loop);
|
||||||
|
|
||||||
if (error < 0 || uv__queue_empty(&stream->write_queue)) {
|
if (error < 0 || uv__queue_empty(&stream->write_queue)) {
|
||||||
uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT);
|
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;
|
uv_interface_address_t* address;
|
||||||
struct ifaddrs* addrs;
|
struct ifaddrs* addrs;
|
||||||
struct ifaddrs* ent;
|
struct ifaddrs* ent;
|
||||||
|
size_t namelen;
|
||||||
|
char* name;
|
||||||
|
|
||||||
*count = 0;
|
*count = 0;
|
||||||
*addresses = NULL;
|
*addresses = NULL;
|
||||||
@ -834,9 +836,11 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
|||||||
return UV__ERR(errno);
|
return UV__ERR(errno);
|
||||||
|
|
||||||
/* Count the number of interfaces */
|
/* Count the number of interfaces */
|
||||||
|
namelen = 0;
|
||||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||||
if (uv__ifaddr_exclude(ent))
|
if (uv__ifaddr_exclude(ent))
|
||||||
continue;
|
continue;
|
||||||
|
namelen += strlen(ent->ifa_name) + 1;
|
||||||
(*count)++;
|
(*count)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -845,19 +849,22 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
*addresses = uv__malloc(*count * sizeof(**addresses));
|
*addresses = uv__calloc(1, *count * sizeof(**addresses) + namelen);
|
||||||
if (!(*addresses)) {
|
if (*addresses == NULL) {
|
||||||
freeifaddrs(addrs);
|
freeifaddrs(addrs);
|
||||||
return UV_ENOMEM;
|
return UV_ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
name = (char*) &(*addresses)[*count];
|
||||||
address = *addresses;
|
address = *addresses;
|
||||||
|
|
||||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||||
if (uv__ifaddr_exclude(ent))
|
if (uv__ifaddr_exclude(ent))
|
||||||
continue;
|
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) {
|
if (ent->ifa_addr->sa_family == AF_INET6) {
|
||||||
address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
|
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 */
|
#endif /* SUNOS_NO_IFADDRS */
|
||||||
|
|
||||||
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||||
int count) {
|
int count) {
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < count; i++) {
|
|
||||||
uv__free(addresses[i].name);
|
|
||||||
}
|
|
||||||
|
|
||||||
uv__free(addresses);
|
uv__free(addresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
143
src/unix/tcp.c
143
src/unix/tcp.c
@ -30,12 +30,8 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
||||||
#if defined(__PASE__)
|
/* ifaddrs is not implemented on AIX and IBM i PASE */
|
||||||
#include <as400_protos.h>
|
#if !defined(_AIX)
|
||||||
#define ifaddrs ifaddrs_pase
|
|
||||||
#define getifaddrs Qp2getifaddrs
|
|
||||||
#define freeifaddrs Qp2freeifaddrs
|
|
||||||
#else
|
|
||||||
#include <ifaddrs.h>
|
#include <ifaddrs.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -171,6 +167,12 @@ int uv__tcp_bind(uv_tcp_t* tcp,
|
|||||||
if (setsockopt(tcp->io_watcher.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
|
if (setsockopt(tcp->io_watcher.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
|
||||||
return UV__ERR(errno);
|
return UV__ERR(errno);
|
||||||
|
|
||||||
|
if (flags & UV_TCP_REUSEPORT) {
|
||||||
|
err = uv__sock_reuseport(tcp->io_watcher.fd);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef __OpenBSD__
|
#ifndef __OpenBSD__
|
||||||
#ifdef IPV6_V6ONLY
|
#ifdef IPV6_V6ONLY
|
||||||
if (addr->sa_family == AF_INET6) {
|
if (addr->sa_family == AF_INET6) {
|
||||||
@ -225,16 +227,39 @@ static int uv__is_ipv6_link_local(const struct sockaddr* addr) {
|
|||||||
|
|
||||||
static int uv__ipv6_link_local_scope_id(void) {
|
static int uv__ipv6_link_local_scope_id(void) {
|
||||||
struct sockaddr_in6* a6;
|
struct sockaddr_in6* a6;
|
||||||
|
int rv;
|
||||||
|
#if defined(_AIX)
|
||||||
|
/* AIX & IBM i do not have ifaddrs
|
||||||
|
* so fallback to use uv_interface_addresses */
|
||||||
|
uv_interface_address_t* interfaces;
|
||||||
|
uv_interface_address_t* ifa;
|
||||||
|
int count, i;
|
||||||
|
|
||||||
|
if (uv_interface_addresses(&interfaces, &count))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
rv = 0;
|
||||||
|
|
||||||
|
for (ifa = interfaces; ifa != &interfaces[count]; ifa++) {
|
||||||
|
if (uv__is_ipv6_link_local((struct sockaddr*) &ifa->address)) {
|
||||||
|
rv = ifa->address.address6.sin6_scope_id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uv_free_interface_addresses(interfaces, count);
|
||||||
|
|
||||||
|
#else
|
||||||
struct ifaddrs* ifa;
|
struct ifaddrs* ifa;
|
||||||
struct ifaddrs* p;
|
struct ifaddrs* p;
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (getifaddrs(&ifa))
|
if (getifaddrs(&ifa))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
for (p = ifa; p != NULL; p = p->ifa_next)
|
for (p = ifa; p != NULL; p = p->ifa_next)
|
||||||
if (uv__is_ipv6_link_local(p->ifa_addr))
|
if (p->ifa_addr != NULL)
|
||||||
break;
|
if (uv__is_ipv6_link_local(p->ifa_addr))
|
||||||
|
break;
|
||||||
|
|
||||||
rv = 0;
|
rv = 0;
|
||||||
if (p != NULL) {
|
if (p != NULL) {
|
||||||
@ -243,6 +268,8 @@ static int uv__ipv6_link_local_scope_id(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
freeifaddrs(ifa);
|
freeifaddrs(ifa);
|
||||||
|
#endif /* defined(_AIX) */
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,10 +458,20 @@ 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 uv__tcp_keepalive(int fd, int on, unsigned int delay) {
|
||||||
|
int idle;
|
||||||
int intvl;
|
int intvl;
|
||||||
int cnt;
|
int cnt;
|
||||||
|
|
||||||
|
(void) &idle;
|
||||||
(void) &intvl;
|
(void) &intvl;
|
||||||
(void) &cnt;
|
(void) &cnt;
|
||||||
|
|
||||||
@ -444,20 +481,87 @@ int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
|
|||||||
if (!on)
|
if (!on)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
#ifdef TCP_KEEPIDLE
|
if (delay < 1)
|
||||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay)))
|
return UV_EINVAL;
|
||||||
|
|
||||||
|
#ifdef __sun
|
||||||
|
/* The implementation of TCP keep-alive on Solaris/SmartOS is a bit unusual
|
||||||
|
* compared to other Unix-like systems.
|
||||||
|
* Thus, we need to specialize it on Solaris.
|
||||||
|
*
|
||||||
|
* There are two keep-alive mechanisms on Solaris:
|
||||||
|
* - By default, the first keep-alive probe is sent out after a TCP connection is idle for two hours.
|
||||||
|
* If the peer does not respond to the probe within eight minutes, the TCP connection is aborted.
|
||||||
|
* You can alter the interval for sending out the first probe using the socket option TCP_KEEPALIVE_THRESHOLD
|
||||||
|
* in milliseconds or TCP_KEEPIDLE in seconds.
|
||||||
|
* The system default is controlled by the TCP ndd parameter tcp_keepalive_interval. The minimum value is ten seconds.
|
||||||
|
* The maximum is ten days, while the default is two hours. If you receive no response to the probe,
|
||||||
|
* you can use the TCP_KEEPALIVE_ABORT_THRESHOLD socket option to change the time threshold for aborting a TCP connection.
|
||||||
|
* The option value is an unsigned integer in milliseconds. The value zero indicates that TCP should never time out and
|
||||||
|
* abort the connection when probing. The system default is controlled by the TCP ndd parameter tcp_keepalive_abort_interval.
|
||||||
|
* The default is eight minutes.
|
||||||
|
*
|
||||||
|
* - The second implementation is activated if socket option TCP_KEEPINTVL and/or TCP_KEEPCNT are set.
|
||||||
|
* The time between each consequent probes is set by TCP_KEEPINTVL in seconds.
|
||||||
|
* The minimum value is ten seconds. The maximum is ten days, while the default is two hours.
|
||||||
|
* The TCP connection will be aborted after certain amount of probes, which is set by TCP_KEEPCNT, without receiving response.
|
||||||
|
*/
|
||||||
|
|
||||||
|
idle = delay;
|
||||||
|
/* Kernel expects at least 10 seconds. */
|
||||||
|
if (idle < 10)
|
||||||
|
idle = 10;
|
||||||
|
/* Kernel expects at most 10 days. */
|
||||||
|
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);
|
return UV__ERR(errno);
|
||||||
/* Solaris/SmartOS, if you don't support keep-alive,
|
|
||||||
* then don't advertise it in your system headers...
|
intvl = 10; /* required at least 10 seconds */
|
||||||
*/
|
UV_KEEPALIVE_FACTOR(intvl);
|
||||||
/* FIXME(bnoordhuis) That's possibly because sizeof(delay) should be 1. */
|
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl)))
|
||||||
#elif defined(TCP_KEEPALIVE) && !defined(__sun)
|
return UV__ERR(errno);
|
||||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay)))
|
|
||||||
|
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`.
|
||||||
|
*/
|
||||||
|
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. */
|
||||||
|
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, &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, &idle, sizeof(idle)))
|
||||||
return UV__ERR(errno);
|
return UV__ERR(errno);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TCP_KEEPINTVL
|
#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)))
|
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl)))
|
||||||
return UV__ERR(errno);
|
return UV__ERR(errno);
|
||||||
#endif
|
#endif
|
||||||
@ -468,6 +572,7 @@ int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
|
|||||||
return UV__ERR(errno);
|
return UV__ERR(errno);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif /* !defined(__sun) */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -525,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) {
|
int uv_socketpair(int type, int protocol, uv_os_sock_t fds[2], int flags0, int flags1) {
|
||||||
uv_os_sock_t temp[2];
|
uv_os_sock_t temp[2];
|
||||||
int err;
|
int err;
|
||||||
#if defined(__FreeBSD__) || defined(__linux__)
|
#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC)
|
||||||
int flags;
|
int flags;
|
||||||
|
|
||||||
flags = type | SOCK_CLOEXEC;
|
flags = type | SOCK_CLOEXEC;
|
||||||
|
|||||||
@ -23,6 +23,9 @@
|
|||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#ifdef __OpenBSD__
|
||||||
|
#include <pthread_np.h>
|
||||||
|
#endif
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.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);
|
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,
|
int uv_thread_create_ex(uv_thread_t* tid,
|
||||||
const uv_thread_options_t* params,
|
const uv_thread_options_t* params,
|
||||||
void (*entry)(void *arg),
|
void (*entry)(void *arg),
|
||||||
@ -205,7 +214,7 @@ int uv_thread_setaffinity(uv_thread_t* tid,
|
|||||||
if (cpumask[i])
|
if (cpumask[i])
|
||||||
CPU_SET(i, &cpuset);
|
CPU_SET(i, &cpuset);
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__) || defined(__OHOS__)
|
||||||
if (sched_setaffinity(pthread_gettid_np(*tid), sizeof(cpuset), &cpuset))
|
if (sched_setaffinity(pthread_gettid_np(*tid), sizeof(cpuset), &cpuset))
|
||||||
r = errno;
|
r = errno;
|
||||||
else
|
else
|
||||||
@ -233,7 +242,7 @@ int uv_thread_getaffinity(uv_thread_t* tid,
|
|||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
|
|
||||||
CPU_ZERO(&cpuset);
|
CPU_ZERO(&cpuset);
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__) || defined(__OHOS__)
|
||||||
if (sched_getaffinity(pthread_gettid_np(*tid), sizeof(cpuset), &cpuset))
|
if (sched_getaffinity(pthread_gettid_np(*tid), sizeof(cpuset), &cpuset))
|
||||||
r = errno;
|
r = errno;
|
||||||
else
|
else
|
||||||
@ -291,6 +300,18 @@ int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) {
|
|||||||
return pthread_equal(*t1, *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) {
|
int uv_mutex_init(uv_mutex_t* mutex) {
|
||||||
#if defined(NDEBUG) || !defined(PTHREAD_MUTEX_ERRORCHECK)
|
#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))
|
if (pthread_setspecific(*key, value))
|
||||||
abort();
|
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 fd;
|
||||||
int rc;
|
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)
|
if (tty->mode == (int) mode)
|
||||||
return 0;
|
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:
|
case UV_TTY_MODE_IO:
|
||||||
uv__tty_make_raw(&tmp);
|
uv__tty_make_raw(&tmp);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Apply changes after draining */
|
/* 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) {
|
int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) {
|
||||||
struct winsize ws;
|
struct winsize ws;
|
||||||
int err;
|
int err;
|
||||||
@ -452,7 +490,7 @@ int uv_tty_reset_mode(void) {
|
|||||||
saved_errno = errno;
|
saved_errno = errno;
|
||||||
|
|
||||||
if (atomic_exchange(&termios_spinlock, 1))
|
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;
|
err = 0;
|
||||||
if (orig_termios_fd != -1)
|
if (orig_termios_fd != -1)
|
||||||
|
|||||||
423
src/unix/udp.c
423
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,
|
static int uv__udp_maybe_deferred_bind(uv_udp_t* handle,
|
||||||
int domain,
|
int domain,
|
||||||
unsigned int flags);
|
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) {
|
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);
|
uv__queue_remove(q);
|
||||||
|
|
||||||
req = uv__queue_data(q, uv_udp_send_t, queue);
|
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_size -= uv__count_bufs(req->bufs, req->nbufs);
|
||||||
handle->send_queue_count--;
|
handle->send_queue_count--;
|
||||||
@ -141,14 +145,14 @@ static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents) {
|
|||||||
if (revents & POLLIN)
|
if (revents & POLLIN)
|
||||||
uv__udp_recvmsg(handle);
|
uv__udp_recvmsg(handle);
|
||||||
|
|
||||||
if (revents & POLLOUT) {
|
if (revents & POLLOUT && !uv__is_closing(handle)) {
|
||||||
uv__udp_sendmsg(handle);
|
uv__udp_sendmsg(handle);
|
||||||
uv__udp_run_completed(handle);
|
uv__udp_run_completed(handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) {
|
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 sockaddr_in6 peers[20];
|
||||||
struct iovec iov[ARRAY_SIZE(peers)];
|
struct iovec iov[ARRAY_SIZE(peers)];
|
||||||
struct mmsghdr msgs[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_control = NULL;
|
||||||
msgs[k].msg_hdr.msg_controllen = 0;
|
msgs[k].msg_hdr.msg_controllen = 0;
|
||||||
msgs[k].msg_hdr.msg_flags = 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
|
do
|
||||||
nread = recvmmsg(handle->io_watcher.fd, msgs, chunks, 0, NULL);
|
nread = recvmmsg(handle->io_watcher.fd, msgs, chunks, 0, NULL);
|
||||||
while (nread == -1 && errno == EINTR);
|
while (nread == -1 && errno == EINTR);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (nread < 1) {
|
if (nread < 1) {
|
||||||
if (nread == 0 || errno == EAGAIN || errno == EWOULDBLOCK)
|
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);
|
handle->recv_cb(handle, 0, buf, NULL, UV_UDP_MMSG_FREE);
|
||||||
}
|
}
|
||||||
return nread;
|
return nread;
|
||||||
#else /* __linux__ || ____FreeBSD__ */
|
#else /* __linux__ || ____FreeBSD__ || __APPLE__ */
|
||||||
return UV_ENOSYS;
|
return UV_ENOSYS;
|
||||||
#endif /* __linux__ || ____FreeBSD__ */
|
#endif /* __linux__ || ____FreeBSD__ || __APPLE__ */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uv__udp_recvmsg(uv_udp_t* handle) {
|
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);
|
&& 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
|
/* 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
|
* Linux as of 3.9, DragonflyBSD 3.6, AIX 7.2.5 have the SO_REUSEPORT socket
|
||||||
* are different from the BSDs: it _shares_ the port rather than steal it
|
* option but with semantics that are different from the BSDs: it _shares_
|
||||||
* from the current listener. While useful, it's not something we can emulate
|
* the port rather than steals it from the current listener. While useful,
|
||||||
* on other platforms so we don't enable it.
|
* 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
|
* zOS does not support getsockname with SO_REUSEPORT option when using
|
||||||
* AF_UNIX.
|
* AF_UNIX.
|
||||||
*/
|
*/
|
||||||
static int uv__set_reuse(int fd) {
|
static int uv__sock_reuseaddr(int fd) {
|
||||||
int yes;
|
int yes;
|
||||||
yes = 1;
|
yes = 1;
|
||||||
|
|
||||||
@ -449,7 +318,7 @@ static int uv__set_reuse(int fd) {
|
|||||||
return UV__ERR(errno);
|
return UV__ERR(errno);
|
||||||
}
|
}
|
||||||
#elif defined(SO_REUSEPORT) && !defined(__linux__) && !defined(__GNU__) && \
|
#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)))
|
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
|
||||||
return UV__ERR(errno);
|
return UV__ERR(errno);
|
||||||
#else
|
#else
|
||||||
@ -492,7 +361,8 @@ int uv__udp_bind(uv_udp_t* handle,
|
|||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
/* Check for bad flags. */
|
/* 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;
|
return UV_EINVAL;
|
||||||
|
|
||||||
/* Cannot set IPv6-only mode on non-IPv6 socket. */
|
/* 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) {
|
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)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -708,11 +584,11 @@ int uv__udp_send(uv_udp_send_t* req,
|
|||||||
empty_queue = (handle->send_queue_count == 0);
|
empty_queue = (handle->send_queue_count == 0);
|
||||||
|
|
||||||
uv__req_init(handle->loop, req, UV_UDP_SEND);
|
uv__req_init(handle->loop, req, UV_UDP_SEND);
|
||||||
assert(addrlen <= sizeof(req->addr));
|
assert(addrlen <= sizeof(req->u.storage));
|
||||||
if (addr == NULL)
|
if (addr == NULL)
|
||||||
req->addr.ss_family = AF_UNSPEC;
|
req->u.storage.ss_family = AF_UNSPEC;
|
||||||
else
|
else
|
||||||
memcpy(&req->addr, addr, addrlen);
|
memcpy(&req->u.storage, addr, addrlen);
|
||||||
req->send_cb = send_cb;
|
req->send_cb = send_cb;
|
||||||
req->handle = handle;
|
req->handle = handle;
|
||||||
req->nbufs = nbufs;
|
req->nbufs = nbufs;
|
||||||
@ -722,7 +598,7 @@ int uv__udp_send(uv_udp_send_t* req,
|
|||||||
req->bufs = uv__malloc(nbufs * sizeof(bufs[0]));
|
req->bufs = uv__malloc(nbufs * sizeof(bufs[0]));
|
||||||
|
|
||||||
if (req->bufs == NULL) {
|
if (req->bufs == NULL) {
|
||||||
uv__req_unregister(handle->loop, req);
|
uv__req_unregister(handle->loop);
|
||||||
return UV_ENOMEM;
|
return UV_ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -755,10 +631,9 @@ int uv__udp_try_send(uv_udp_t* handle,
|
|||||||
const struct sockaddr* addr,
|
const struct sockaddr* addr,
|
||||||
unsigned int addrlen) {
|
unsigned int addrlen) {
|
||||||
int err;
|
int err;
|
||||||
struct msghdr h;
|
|
||||||
ssize_t size;
|
|
||||||
|
|
||||||
assert(nbufs > 0);
|
if (nbufs < 1)
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
/* already sending a message */
|
/* already sending a message */
|
||||||
if (handle->send_queue_count != 0)
|
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);
|
assert(handle->flags & UV_HANDLE_UDP_CONNECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&h, 0, sizeof h);
|
err = uv__udp_sendmsg1(handle->io_watcher.fd, bufs, nbufs, addr);
|
||||||
h.msg_name = (struct sockaddr*) addr;
|
if (err > 0)
|
||||||
h.msg_namelen = addrlen;
|
return uv__count_bufs(bufs, nbufs);
|
||||||
h.msg_iov = (struct iovec*) bufs;
|
|
||||||
h.msg_iovlen = nbufs;
|
|
||||||
|
|
||||||
do {
|
return err;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1015,7 +877,7 @@ int uv__udp_init_ex(uv_loop_t* loop,
|
|||||||
|
|
||||||
|
|
||||||
int uv_udp_using_recvmmsg(const uv_udp_t* handle) {
|
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)
|
if (handle->flags & UV_HANDLE_UDP_RECVMMSG)
|
||||||
return 1;
|
return 1;
|
||||||
#endif
|
#endif
|
||||||
@ -1037,7 +899,7 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
|
|||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
err = uv__set_reuse(sock);
|
err = uv__sock_reuseaddr(sock);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
@ -1366,3 +1228,194 @@ int uv__udp_recv_stop(uv_udp_t* handle) {
|
|||||||
|
|
||||||
return 0;
|
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,
|
int uv_udp_recv_start(uv_udp_t* handle,
|
||||||
uv_alloc_cb alloc_cb,
|
uv_alloc_cb alloc_cb,
|
||||||
uv_udp_recv_cb recv_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) {
|
int uv_fs_event_getpath(uv_fs_event_t* handle, char* buffer, size_t* size) {
|
||||||
size_t required_len;
|
size_t required_len;
|
||||||
|
|
||||||
|
if (buffer == NULL || size == NULL || *size == 0)
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
if (!uv__is_active(handle)) {
|
if (!uv__is_active(handle)) {
|
||||||
*size = 0;
|
*size = 0;
|
||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
|
|||||||
@ -31,6 +31,7 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "uv.h"
|
#include "uv.h"
|
||||||
#include "uv/tree.h"
|
#include "uv/tree.h"
|
||||||
@ -125,7 +126,7 @@ enum {
|
|||||||
|
|
||||||
/* Only used by uv_tty_t handles. */
|
/* Only used by uv_tty_t handles. */
|
||||||
UV_HANDLE_TTY_READABLE = 0x01000000,
|
UV_HANDLE_TTY_READABLE = 0x01000000,
|
||||||
UV_HANDLE_TTY_RAW = 0x02000000,
|
UV_HANDLE_UNUSED0 = 0x02000000,
|
||||||
UV_HANDLE_TTY_SAVED_POSITION = 0x04000000,
|
UV_HANDLE_TTY_SAVED_POSITION = 0x04000000,
|
||||||
UV_HANDLE_TTY_SAVED_ATTRIBUTES = 0x08000000,
|
UV_HANDLE_TTY_SAVED_ATTRIBUTES = 0x08000000,
|
||||||
|
|
||||||
@ -140,6 +141,10 @@ enum {
|
|||||||
UV_HANDLE_REAP = 0x10000000
|
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);
|
int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap);
|
||||||
|
|
||||||
void uv__loop_close(uv_loop_t* loop);
|
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,
|
const struct sockaddr* addr,
|
||||||
unsigned int addrlen);
|
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,
|
int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloccb,
|
||||||
uv_udp_recv_cb recv_cb);
|
uv_udp_recv_cb recv_cb);
|
||||||
|
|
||||||
@ -233,13 +244,13 @@ void uv__threadpool_cleanup(void);
|
|||||||
#define uv__has_active_reqs(loop) \
|
#define uv__has_active_reqs(loop) \
|
||||||
((loop)->active_reqs.count > 0)
|
((loop)->active_reqs.count > 0)
|
||||||
|
|
||||||
#define uv__req_register(loop, req) \
|
#define uv__req_register(loop) \
|
||||||
do { \
|
do { \
|
||||||
(loop)->active_reqs.count++; \
|
(loop)->active_reqs.count++; \
|
||||||
} \
|
} \
|
||||||
while (0)
|
while (0)
|
||||||
|
|
||||||
#define uv__req_unregister(loop, req) \
|
#define uv__req_unregister(loop) \
|
||||||
do { \
|
do { \
|
||||||
assert(uv__has_active_reqs(loop)); \
|
assert(uv__has_active_reqs(loop)); \
|
||||||
(loop)->active_reqs.count--; \
|
(loop)->active_reqs.count--; \
|
||||||
@ -349,7 +360,7 @@ void uv__threadpool_cleanup(void);
|
|||||||
#define uv__req_init(loop, req, typ) \
|
#define uv__req_init(loop, req, typ) \
|
||||||
do { \
|
do { \
|
||||||
UV_REQ_INIT(req, typ); \
|
UV_REQ_INIT(req, typ); \
|
||||||
uv__req_register(loop, req); \
|
uv__req_register(loop); \
|
||||||
} \
|
} \
|
||||||
while (0)
|
while (0)
|
||||||
|
|
||||||
@ -400,7 +411,6 @@ void uv__metrics_set_provider_entry_time(uv_loop_t* loop);
|
|||||||
struct uv__iou {
|
struct uv__iou {
|
||||||
uint32_t* sqhead;
|
uint32_t* sqhead;
|
||||||
uint32_t* sqtail;
|
uint32_t* sqtail;
|
||||||
uint32_t* sqarray;
|
|
||||||
uint32_t sqmask;
|
uint32_t sqmask;
|
||||||
uint32_t* sqflags;
|
uint32_t* sqflags;
|
||||||
uint32_t* cqhead;
|
uint32_t* cqhead;
|
||||||
@ -415,7 +425,6 @@ struct uv__iou {
|
|||||||
size_t sqelen;
|
size_t sqelen;
|
||||||
int ringfd;
|
int ringfd;
|
||||||
uint32_t in_flight;
|
uint32_t in_flight;
|
||||||
uint32_t flags;
|
|
||||||
};
|
};
|
||||||
#endif /* __linux__ */
|
#endif /* __linux__ */
|
||||||
|
|
||||||
@ -430,4 +439,36 @@ struct uv__loop_internal_fields_s {
|
|||||||
#endif /* __linux__ */
|
#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_ */
|
#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) {
|
static void uv__poll(uv_loop_t* loop, DWORD timeout) {
|
||||||
uv__loop_internal_fields_t* lfields;
|
uv__loop_internal_fields_t* lfields;
|
||||||
BOOL success;
|
BOOL success;
|
||||||
@ -553,12 +462,12 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) {
|
|||||||
*/
|
*/
|
||||||
lfields->current_timeout = timeout;
|
lfields->current_timeout = timeout;
|
||||||
|
|
||||||
success = pGetQueuedCompletionStatusEx(loop->iocp,
|
success = GetQueuedCompletionStatusEx(loop->iocp,
|
||||||
overlappeds,
|
overlappeds,
|
||||||
ARRAY_SIZE(overlappeds),
|
ARRAY_SIZE(overlappeds),
|
||||||
&count,
|
&count,
|
||||||
timeout,
|
timeout,
|
||||||
FALSE);
|
FALSE);
|
||||||
|
|
||||||
if (reset_timeout != 0) {
|
if (reset_timeout != 0) {
|
||||||
timeout = user_timeout;
|
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
|
/* 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
|
* then the timeout will be updated and the loop will run again. In either
|
||||||
* case the idle time will need to be updated.
|
* 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);
|
uv__metrics_inc_loop_count(loop);
|
||||||
|
|
||||||
if (pGetQueuedCompletionStatusEx)
|
uv__poll(loop, timeout);
|
||||||
uv__poll(loop, timeout);
|
|
||||||
else
|
|
||||||
uv__poll_wine(loop, timeout);
|
|
||||||
|
|
||||||
/* Process immediate callbacks (e.g. write_cb) a small fixed number of
|
/* Process immediate callbacks (e.g. write_cb) a small fixed number of
|
||||||
* times to avoid loop starvation.*/
|
* times to avoid loop starvation.*/
|
||||||
|
|||||||
@ -69,7 +69,6 @@ int uv_translate_sys_error(int sys_errno) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (sys_errno) {
|
switch (sys_errno) {
|
||||||
case ERROR_NOACCESS: return UV_EACCES;
|
|
||||||
case WSAEACCES: return UV_EACCES;
|
case WSAEACCES: return UV_EACCES;
|
||||||
case ERROR_ELEVATION_REQUIRED: return UV_EACCES;
|
case ERROR_ELEVATION_REQUIRED: return UV_EACCES;
|
||||||
case ERROR_CANT_ACCESS_FILE: return UV_EACCES;
|
case ERROR_CANT_ACCESS_FILE: return UV_EACCES;
|
||||||
@ -78,6 +77,7 @@ int uv_translate_sys_error(int sys_errno) {
|
|||||||
case WSAEADDRNOTAVAIL: return UV_EADDRNOTAVAIL;
|
case WSAEADDRNOTAVAIL: return UV_EADDRNOTAVAIL;
|
||||||
case WSAEAFNOSUPPORT: return UV_EAFNOSUPPORT;
|
case WSAEAFNOSUPPORT: return UV_EAFNOSUPPORT;
|
||||||
case WSAEWOULDBLOCK: return UV_EAGAIN;
|
case WSAEWOULDBLOCK: return UV_EAGAIN;
|
||||||
|
case ERROR_NO_DATA: return UV_EAGAIN;
|
||||||
case WSAEALREADY: return UV_EALREADY;
|
case WSAEALREADY: return UV_EALREADY;
|
||||||
case ERROR_INVALID_FLAGS: return UV_EBADF;
|
case ERROR_INVALID_FLAGS: return UV_EBADF;
|
||||||
case ERROR_INVALID_HANDLE: 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 WSAECONNRESET: return UV_ECONNRESET;
|
||||||
case ERROR_ALREADY_EXISTS: return UV_EEXIST;
|
case ERROR_ALREADY_EXISTS: return UV_EEXIST;
|
||||||
case ERROR_FILE_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 WSAEFAULT: return UV_EFAULT;
|
||||||
case ERROR_HOST_UNREACHABLE: return UV_EHOSTUNREACH;
|
case ERROR_HOST_UNREACHABLE: return UV_EHOSTUNREACH;
|
||||||
case WSAEHOSTUNREACH: 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 ERROR_TOO_MANY_OPEN_FILES: return UV_EMFILE;
|
||||||
case WSAEMFILE: return UV_EMFILE;
|
case WSAEMFILE: return UV_EMFILE;
|
||||||
case WSAEMSGSIZE: return UV_EMSGSIZE;
|
case WSAEMSGSIZE: return UV_EMSGSIZE;
|
||||||
|
case ERROR_BUFFER_OVERFLOW: return UV_ENAMETOOLONG;
|
||||||
case ERROR_FILENAME_EXCED_RANGE: return UV_ENAMETOOLONG;
|
case ERROR_FILENAME_EXCED_RANGE: return UV_ENAMETOOLONG;
|
||||||
case ERROR_NETWORK_UNREACHABLE: return UV_ENETUNREACH;
|
case ERROR_NETWORK_UNREACHABLE: return UV_ENETUNREACH;
|
||||||
case WSAENETUNREACH: 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_ACCESS_DENIED: return UV_EPERM;
|
||||||
case ERROR_PRIVILEGE_NOT_HELD: return UV_EPERM;
|
case ERROR_PRIVILEGE_NOT_HELD: return UV_EPERM;
|
||||||
case ERROR_BAD_PIPE: return UV_EPIPE;
|
case ERROR_BAD_PIPE: return UV_EPIPE;
|
||||||
case ERROR_NO_DATA: return UV_EPIPE;
|
|
||||||
case ERROR_PIPE_NOT_CONNECTED: return UV_EPIPE;
|
case ERROR_PIPE_NOT_CONNECTED: return UV_EPIPE;
|
||||||
case WSAESHUTDOWN: return UV_EPIPE;
|
case WSAESHUTDOWN: return UV_EPIPE;
|
||||||
case WSAEPROTONOSUPPORT: return UV_EPROTONOSUPPORT;
|
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_INVALID_FUNCTION: return UV_EISDIR;
|
||||||
case ERROR_META_EXPANSION_TOO_LONG: return UV_E2BIG;
|
case ERROR_META_EXPANSION_TOO_LONG: return UV_E2BIG;
|
||||||
case WSAESOCKTNOSUPPORT: return UV_ESOCKTNOSUPPORT;
|
case WSAESOCKTNOSUPPORT: return UV_ESOCKTNOSUPPORT;
|
||||||
|
case ERROR_BAD_EXE_FORMAT: return UV_EFTYPE;
|
||||||
default: return UV_UNKNOWN;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -114,7 +114,7 @@ static int uv__split_path(const WCHAR* filename, WCHAR** dir,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*file = wcsdup(filename);
|
*file = _wcsdup(filename);
|
||||||
} else {
|
} else {
|
||||||
if (dir) {
|
if (dir) {
|
||||||
*dir = (WCHAR*)uv__malloc((i + 2) * sizeof(WCHAR));
|
*dir = (WCHAR*)uv__malloc((i + 2) * sizeof(WCHAR));
|
||||||
@ -253,6 +253,8 @@ short_path_done:
|
|||||||
}
|
}
|
||||||
|
|
||||||
dir_to_watch = dir;
|
dir_to_watch = dir;
|
||||||
|
uv__free(short_path);
|
||||||
|
short_path = NULL;
|
||||||
uv__free(pathw);
|
uv__free(pathw);
|
||||||
pathw = NULL;
|
pathw = NULL;
|
||||||
}
|
}
|
||||||
@ -561,7 +563,27 @@ void uv__process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = GET_REQ_ERROR(req);
|
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) {
|
if (handle->flags & UV_HANDLE_CLOSING) {
|
||||||
|
|||||||
546
src/win/fs.c
546
src/win/fs.c
@ -46,6 +46,30 @@
|
|||||||
#define UV_FS_FREE_PTR 0x0008
|
#define UV_FS_FREE_PTR 0x0008
|
||||||
#define UV_FS_CLEANEDUP 0x0010
|
#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) \
|
#define INIT(subtype) \
|
||||||
do { \
|
do { \
|
||||||
@ -58,7 +82,7 @@
|
|||||||
#define POST \
|
#define POST \
|
||||||
do { \
|
do { \
|
||||||
if (cb != NULL) { \
|
if (cb != NULL) { \
|
||||||
uv__req_register(loop, req); \
|
uv__req_register(loop); \
|
||||||
uv__work_submit(loop, \
|
uv__work_submit(loop, \
|
||||||
&req->work_req, \
|
&req->work_req, \
|
||||||
UV__WORK_FAST_IO, \
|
UV__WORK_FAST_IO, \
|
||||||
@ -97,13 +121,14 @@
|
|||||||
return; \
|
return; \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MILLION ((int64_t) 1000 * 1000)
|
#define NSEC_PER_TICK 100
|
||||||
#define BILLION ((int64_t) 1000 * 1000 * 1000)
|
#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) {
|
static void uv__filetime_to_timespec(uv_timespec_t *ts, int64_t filetime) {
|
||||||
filetime -= 116444736 * BILLION;
|
filetime -= WIN_TO_UNIX_TICK_OFFSET;
|
||||||
ts->tv_sec = (long) (filetime / (10 * MILLION));
|
ts->tv_sec = filetime / TICKS_PER_SEC;
|
||||||
ts->tv_nsec = (long) ((filetime - ts->tv_sec * 10 * MILLION) * 100U);
|
ts->tv_nsec = (filetime % TICKS_PER_SEC) * NSEC_PER_TICK;
|
||||||
if (ts->tv_nsec < 0) {
|
if (ts->tv_nsec < 0) {
|
||||||
ts->tv_sec -= 1;
|
ts->tv_sec -= 1;
|
||||||
ts->tv_nsec += 1e9;
|
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) \
|
#define TIME_T_TO_FILETIME(time, filetime_ptr) \
|
||||||
do { \
|
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)->dwLowDateTime = (uint64_t) bigtime & 0xFFFFFFFF; \
|
||||||
(filetime_ptr)->dwHighDateTime = (uint64_t) bigtime >> 32; \
|
(filetime_ptr)->dwHighDateTime = (uint64_t) bigtime >> 32; \
|
||||||
} while(0)
|
} while(0)
|
||||||
@ -136,6 +161,16 @@ static int uv__file_symlink_usermode_flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGE
|
|||||||
|
|
||||||
static DWORD uv__allocation_granularity;
|
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) {
|
void uv__fs_init(void) {
|
||||||
SYSTEM_INFO system_info;
|
SYSTEM_INFO system_info;
|
||||||
@ -407,8 +442,8 @@ void fs__open(uv_fs_t* req) {
|
|||||||
|
|
||||||
/* Obtain the active umask. umask() never fails and returns the previous
|
/* Obtain the active umask. umask() never fails and returns the previous
|
||||||
* umask. */
|
* umask. */
|
||||||
current_umask = umask(0);
|
current_umask = _umask(0);
|
||||||
umask(current_umask);
|
_umask(current_umask);
|
||||||
|
|
||||||
/* convert flags and mode to CreateFile parameters */
|
/* convert flags and mode to CreateFile parameters */
|
||||||
switch (flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR)) {
|
switch (flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR)) {
|
||||||
@ -1056,27 +1091,20 @@ void fs__write(uv_fs_t* req) {
|
|||||||
error = ERROR_INVALID_FLAGS;
|
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) {
|
static void fs__unlink_rmdir(uv_fs_t* req, BOOL isrmdir) {
|
||||||
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) {
|
|
||||||
const WCHAR* pathw = req->file.pathw;
|
const WCHAR* pathw = req->file.pathw;
|
||||||
HANDLE handle;
|
HANDLE handle;
|
||||||
BY_HANDLE_FILE_INFORMATION info;
|
BY_HANDLE_FILE_INFORMATION info;
|
||||||
FILE_DISPOSITION_INFORMATION disposition;
|
FILE_DISPOSITION_INFORMATION disposition;
|
||||||
|
FILE_DISPOSITION_INFORMATION_EX disposition_ex;
|
||||||
IO_STATUS_BLOCK iosb;
|
IO_STATUS_BLOCK iosb;
|
||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
|
DWORD error;
|
||||||
|
|
||||||
handle = CreateFileW(pathw,
|
handle = CreateFileW(pathw,
|
||||||
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | DELETE,
|
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | DELETE,
|
||||||
@ -1097,10 +1125,18 @@ void fs__unlink(uv_fs_t* req) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
if (isrmdir && !(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||||
/* Do not allow deletion of directories, unless it is a symlink. When the
|
/* Error if we're in rmdir mode but it is not a dir.
|
||||||
* path refers to a non-symlink directory, report EPERM as mandated by
|
* TODO: change it to UV_NOTDIR in v2. */
|
||||||
* POSIX.1. */
|
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. */
|
/* Check if it is a reparse point. If it's not, it's a normal directory. */
|
||||||
if (!(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
|
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
|
/* Read the reparse point and check if it is a valid symlink. If not, don't
|
||||||
* unlink. */
|
* unlink. */
|
||||||
if (fs__readlink_handle(handle, NULL, NULL) < 0) {
|
if (fs__readlink_handle(handle, NULL, NULL) < 0) {
|
||||||
DWORD error = GetLastError();
|
error = GetLastError();
|
||||||
if (error == ERROR_SYMLINK_NOT_SUPPORTED)
|
if (error == ERROR_SYMLINK_NOT_SUPPORTED)
|
||||||
error = ERROR_ACCESS_DENIED;
|
error = ERROR_ACCESS_DENIED;
|
||||||
SET_REQ_WIN32_ERROR(req, error);
|
SET_REQ_WIN32_ERROR(req, error);
|
||||||
@ -1121,42 +1157,77 @@ void fs__unlink(uv_fs_t* req) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
|
/* Try posix delete first */
|
||||||
/* Remove read-only attribute */
|
disposition_ex.Flags = FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS |
|
||||||
FILE_BASIC_INFORMATION basic = { 0 };
|
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,
|
status = pNtSetInformationFile(handle,
|
||||||
&iosb,
|
&iosb,
|
||||||
&disposition,
|
&disposition_ex,
|
||||||
sizeof disposition,
|
sizeof disposition_ex,
|
||||||
FileDispositionInformation);
|
FileDispositionInformationEx);
|
||||||
if (NT_SUCCESS(status)) {
|
if (NT_SUCCESS(status)) {
|
||||||
SET_REQ_SUCCESS(req);
|
SET_REQ_SUCCESS(req);
|
||||||
} else {
|
} 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);
|
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) {
|
void fs__mkdir(uv_fs_t* req) {
|
||||||
/* TODO: use req->mode. */
|
/* TODO: use req->mode. */
|
||||||
if (CreateDirectoryW(req->file.pathw, NULL)) {
|
if (CreateDirectoryW(req->file.pathw, NULL)) {
|
||||||
@ -1593,12 +1664,12 @@ void fs__readdir(uv_fs_t* req) {
|
|||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
/* Copy file type. */
|
/* Copy file type. */
|
||||||
if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
|
if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0)
|
||||||
dent.d_type = UV__DT_DIR;
|
dent.d_type = UV__DT_CHAR;
|
||||||
else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0)
|
else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0)
|
||||||
dent.d_type = UV__DT_LINK;
|
dent.d_type = UV__DT_LINK;
|
||||||
else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0)
|
else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
|
||||||
dent.d_type = UV__DT_CHAR;
|
dent.d_type = UV__DT_DIR;
|
||||||
else
|
else
|
||||||
dent.d_type = UV__DT_FILE;
|
dent.d_type = UV__DT_FILE;
|
||||||
|
|
||||||
@ -1627,6 +1698,43 @@ void fs__closedir(uv_fs_t* req) {
|
|||||||
SET_REQ_RESULT(req, 0);
|
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,
|
INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
|
||||||
int do_lstat) {
|
int do_lstat) {
|
||||||
size_t target_length = 0;
|
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;
|
FILE_FS_VOLUME_INFORMATION volume_info;
|
||||||
NTSTATUS nt_status;
|
NTSTATUS nt_status;
|
||||||
IO_STATUS_BLOCK io_status;
|
IO_STATUS_BLOCK io_status;
|
||||||
|
FILE_STAT_BASIC_INFORMATION stat_info;
|
||||||
|
|
||||||
nt_status = pNtQueryVolumeInformationFile(handle,
|
nt_status = pNtQueryVolumeInformationFile(handle,
|
||||||
&io_status,
|
&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 it's NUL device set fields as reasonable as possible and return. */
|
||||||
if (device_info.DeviceType == FILE_DEVICE_NULL) {
|
if (device_info.DeviceType == FILE_DEVICE_NULL) {
|
||||||
memset(statbuf, 0, sizeof(uv_stat_t));
|
fs__stat_assign_statbuf_null(statbuf);
|
||||||
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;
|
|
||||||
return 0;
|
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. */
|
/* Buffer overflow (a warning status code) is expected here. */
|
||||||
if (io_status.Status == STATUS_NOT_IMPLEMENTED) {
|
if (io_status.Status == STATUS_NOT_IMPLEMENTED) {
|
||||||
statbuf->st_dev = 0;
|
stat_info.VolumeSerialNumber.QuadPart = 0;
|
||||||
} else if (NT_ERROR(nt_status)) {
|
} else if (NT_ERROR(nt_status)) {
|
||||||
SetLastError(pRtlNtStatusToDosError(nt_status));
|
SetLastError(pRtlNtStatusToDosError(nt_status));
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} 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
|
/* 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.
|
* 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.
|
* target. Otherwise, reparse points must be treated as regular files.
|
||||||
*/
|
*/
|
||||||
if (do_lstat &&
|
if (do_lstat &&
|
||||||
(file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
|
(stat_info.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;
|
|
||||||
statbuf->st_mode |= S_IFLNK;
|
statbuf->st_mode |= S_IFLNK;
|
||||||
statbuf->st_size = target_length;
|
statbuf->st_size = stat_info.EndOfFile.QuadPart;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statbuf->st_mode == 0) {
|
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_mode |= _S_IFDIR;
|
||||||
statbuf->st_size = 0;
|
statbuf->st_size = 0;
|
||||||
} else {
|
} else {
|
||||||
statbuf->st_mode |= _S_IFREG;
|
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);
|
statbuf->st_mode |= _S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6);
|
||||||
else
|
else
|
||||||
statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
|
statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
|
||||||
((_S_IREAD | _S_IWRITE) >> 6);
|
((_S_IREAD | _S_IWRITE) >> 6);
|
||||||
|
|
||||||
uv__filetime_to_timespec(&statbuf->st_atim,
|
uv__filetime_to_timespec(&statbuf->st_atim,
|
||||||
file_info.BasicInformation.LastAccessTime.QuadPart);
|
stat_info.LastAccessTime.QuadPart);
|
||||||
uv__filetime_to_timespec(&statbuf->st_ctim,
|
uv__filetime_to_timespec(&statbuf->st_ctim,
|
||||||
file_info.BasicInformation.ChangeTime.QuadPart);
|
stat_info.ChangeTime.QuadPart);
|
||||||
uv__filetime_to_timespec(&statbuf->st_mtim,
|
uv__filetime_to_timespec(&statbuf->st_mtim,
|
||||||
file_info.BasicInformation.LastWriteTime.QuadPart);
|
stat_info.LastWriteTime.QuadPart);
|
||||||
uv__filetime_to_timespec(&statbuf->st_birthtim,
|
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. */
|
/* st_blocks contains the on-disk allocation size in 512-byte units. */
|
||||||
statbuf->st_blocks =
|
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
|
/* 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
|
* 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_uid = 0;
|
||||||
statbuf->st_rdev = 0;
|
statbuf->st_rdev = 0;
|
||||||
statbuf->st_gen = 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,
|
INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
|
||||||
int do_lstat,
|
int do_lstat,
|
||||||
@ -1817,6 +2134,17 @@ INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
|
|||||||
DWORD flags;
|
DWORD flags;
|
||||||
DWORD ret;
|
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;
|
flags = FILE_FLAG_BACKUP_SEMANTICS;
|
||||||
if (do_lstat)
|
if (do_lstat)
|
||||||
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
|
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
|
||||||
@ -1829,8 +2157,12 @@ INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
|
|||||||
flags,
|
flags,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
if (handle == INVALID_HANDLE_VALUE)
|
if (handle == INVALID_HANDLE_VALUE) {
|
||||||
return GetLastError();
|
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)
|
if (fs__stat_handle(handle, statbuf, do_lstat) != 0)
|
||||||
ret = GetLastError();
|
ret = GetLastError();
|
||||||
@ -2248,14 +2580,29 @@ fchmod_cleanup:
|
|||||||
|
|
||||||
|
|
||||||
INLINE static int fs__utime_handle(HANDLE handle, double atime, double mtime) {
|
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);
|
if (uv__isinf(atime) || uv__isinf(mtime))
|
||||||
TIME_T_TO_FILETIME(mtime, &filetime_m);
|
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 -1;
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
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++] = path[i];
|
||||||
}
|
}
|
||||||
path_buf[path_buf_len++] = L'\\';
|
if (add_slash)
|
||||||
|
path_buf[path_buf_len++] = L'\\';
|
||||||
len = path_buf_len - start;
|
len = path_buf_len - start;
|
||||||
|
|
||||||
|
/* Insert null terminator */
|
||||||
|
path_buf[path_buf_len++] = L'\0';
|
||||||
|
|
||||||
/* Set the info about the substitute name */
|
/* Set the info about the substitute name */
|
||||||
buffer->MountPointReparseBuffer.SubstituteNameOffset = start * sizeof(WCHAR);
|
buffer->MountPointReparseBuffer.SubstituteNameOffset = start * sizeof(WCHAR);
|
||||||
buffer->MountPointReparseBuffer.SubstituteNameLength = len * 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 */
|
/* Copy the print name of the target path */
|
||||||
start = path_buf_len;
|
start = path_buf_len;
|
||||||
add_slash = 0;
|
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];
|
path_buf[path_buf_len++] = path[i];
|
||||||
}
|
}
|
||||||
len = path_buf_len - start;
|
len = path_buf_len - start;
|
||||||
if (len == 2) {
|
if (len == 2 || add_slash) {
|
||||||
path_buf[path_buf_len++] = L'\\';
|
path_buf[path_buf_len++] = L'\\';
|
||||||
len++;
|
len++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Insert another null terminator */
|
||||||
|
path_buf[path_buf_len++] = L'\0';
|
||||||
|
|
||||||
/* Set the info about the print name */
|
/* Set the info about the print name */
|
||||||
buffer->MountPointReparseBuffer.PrintNameOffset = start * sizeof(WCHAR);
|
buffer->MountPointReparseBuffer.PrintNameOffset = start * sizeof(WCHAR);
|
||||||
buffer->MountPointReparseBuffer.PrintNameLength = len * 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 */
|
/* Calculate how much buffer space was actually used */
|
||||||
used_buf_size = FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) +
|
used_buf_size = FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) +
|
||||||
path_buf_len * sizeof(WCHAR);
|
path_buf_len * sizeof(WCHAR);
|
||||||
@ -2830,7 +3178,7 @@ static void uv__fs_done(struct uv__work* w, int status) {
|
|||||||
uv_fs_t* req;
|
uv_fs_t* req;
|
||||||
|
|
||||||
req = container_of(w, uv_fs_t, work_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) {
|
if (status == UV_ECANCELED) {
|
||||||
assert(req->result == 0);
|
assert(req->result == 0);
|
||||||
|
|||||||
@ -71,10 +71,9 @@ int uv__getaddrinfo_translate_error(int sys_err) {
|
|||||||
DECLSPEC_IMPORT void WSAAPI FreeAddrInfoW(PADDRINFOW pAddrInfo);
|
DECLSPEC_IMPORT void WSAAPI FreeAddrInfoW(PADDRINFOW pAddrInfo);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static size_t align_offset(size_t off, size_t alignment) {
|
||||||
/* Adjust size value to be multiple of 4. Use to keep pointer aligned.
|
return ((off + alignment - 1) / alignment) * alignment;
|
||||||
* Do we need different versions of this for different architectures? */
|
}
|
||||||
#define ALIGNED_SIZE(X) ((((X) + 3) >> 2) << 2)
|
|
||||||
|
|
||||||
#ifndef NDIS_IF_MAX_STRING_SIZE
|
#ifndef NDIS_IF_MAX_STRING_SIZE
|
||||||
#define NDIS_IF_MAX_STRING_SIZE 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.
|
* Each size calculation is adjusted to avoid unaligned pointers.
|
||||||
*/
|
*/
|
||||||
static void uv__getaddrinfo_done(struct uv__work* w, int status) {
|
static void uv__getaddrinfo_done(struct uv__work* w, int status) {
|
||||||
uv_getaddrinfo_t* req;
|
uv_getaddrinfo_t* req = container_of(w, uv_getaddrinfo_t, work_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);
|
|
||||||
|
|
||||||
/* release input parameter memory */
|
/* release input parameter memory */
|
||||||
uv__free(req->alloc);
|
uv__free(req->alloc);
|
||||||
@ -126,34 +115,44 @@ static void uv__getaddrinfo_done(struct uv__work* w, int status) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (req->retcode == 0) {
|
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. */
|
/* Convert addrinfoW to addrinfo. First calculate required length. */
|
||||||
addrinfow_ptr = req->addrinfow;
|
struct addrinfoW* addrinfow_ptr = req->addrinfow;
|
||||||
while (addrinfow_ptr != NULL) {
|
while (addrinfow_ptr != NULL) {
|
||||||
addrinfo_len += addrinfo_struct_len +
|
cur_off = align_offset(cur_off, sizeof(void*));
|
||||||
ALIGNED_SIZE(addrinfow_ptr->ai_addrlen);
|
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) {
|
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) {
|
if (name_len < 0) {
|
||||||
req->retcode = name_len;
|
req->retcode = name_len;
|
||||||
goto complete;
|
goto complete;
|
||||||
}
|
}
|
||||||
addrinfo_len += ALIGNED_SIZE(name_len + 1);
|
cur_off += name_len + 1;
|
||||||
}
|
}
|
||||||
addrinfow_ptr = addrinfow_ptr->ai_next;
|
addrinfow_ptr = addrinfow_ptr->ai_next;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* allocate memory for addrinfo results */
|
/* allocate memory for addrinfo results */
|
||||||
alloc_ptr = (char*)uv__malloc(addrinfo_len);
|
addrinfo_len = cur_off;
|
||||||
|
alloc_ptr = uv__malloc(addrinfo_len);
|
||||||
|
|
||||||
/* do conversions */
|
/* do conversions */
|
||||||
if (alloc_ptr != NULL) {
|
if (alloc_ptr != NULL) {
|
||||||
cur_ptr = alloc_ptr;
|
struct addrinfo *addrinfo_ptr = (struct addrinfo *)alloc_ptr;
|
||||||
|
cur_off = 0;
|
||||||
addrinfow_ptr = req->addrinfow;
|
addrinfow_ptr = req->addrinfow;
|
||||||
|
|
||||||
while (addrinfow_ptr != NULL) {
|
for (;;) {
|
||||||
|
cur_off += sizeof(struct addrinfo);
|
||||||
|
assert(cur_off <= addrinfo_len);
|
||||||
/* copy addrinfo struct data */
|
/* 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_family = addrinfow_ptr->ai_family;
|
||||||
addrinfo_ptr->ai_socktype = addrinfow_ptr->ai_socktype;
|
addrinfo_ptr->ai_socktype = addrinfow_ptr->ai_socktype;
|
||||||
addrinfo_ptr->ai_protocol = addrinfow_ptr->ai_protocol;
|
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_addr = NULL;
|
||||||
addrinfo_ptr->ai_next = NULL;
|
addrinfo_ptr->ai_next = NULL;
|
||||||
|
|
||||||
cur_ptr += addrinfo_struct_len;
|
|
||||||
|
|
||||||
/* copy sockaddr */
|
/* copy sockaddr */
|
||||||
if (addrinfo_ptr->ai_addrlen > 0) {
|
if (addrinfo_ptr->ai_addrlen > 0) {
|
||||||
assert(cur_ptr + addrinfo_ptr->ai_addrlen <=
|
cur_off = align_offset(cur_off, sizeof(void *));
|
||||||
alloc_ptr + addrinfo_len);
|
addrinfo_ptr->ai_addr = (struct sockaddr *)(alloc_ptr + cur_off);
|
||||||
memcpy(cur_ptr, addrinfow_ptr->ai_addr, addrinfo_ptr->ai_addrlen);
|
cur_off += addrinfo_ptr->ai_addrlen;
|
||||||
addrinfo_ptr->ai_addr = (struct sockaddr*)cur_ptr;
|
assert(cur_off <= addrinfo_len);
|
||||||
cur_ptr += ALIGNED_SIZE(addrinfo_ptr->ai_addrlen);
|
memcpy(addrinfo_ptr->ai_addr,
|
||||||
|
addrinfow_ptr->ai_addr,
|
||||||
|
addrinfo_ptr->ai_addrlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* convert canonical name to UTF-8 */
|
/* convert canonical name to UTF-8 */
|
||||||
if (addrinfow_ptr->ai_canonname != NULL) {
|
if (addrinfow_ptr->ai_canonname != NULL) {
|
||||||
name_len = alloc_ptr + addrinfo_len - cur_ptr;
|
ssize_t name_len = addrinfo_len - cur_off;
|
||||||
r = uv__copy_utf16_to_utf8(addrinfow_ptr->ai_canonname,
|
addrinfo_ptr->ai_canonname = alloc_ptr + cur_off;
|
||||||
-1,
|
int r = uv__copy_utf16_to_utf8(addrinfow_ptr->ai_canonname,
|
||||||
cur_ptr,
|
-1,
|
||||||
(size_t*)&name_len);
|
addrinfo_ptr->ai_canonname,
|
||||||
|
(size_t*)&name_len);
|
||||||
assert(r == 0);
|
assert(r == 0);
|
||||||
addrinfo_ptr->ai_canonname = cur_ptr;
|
cur_off += name_len + 1;
|
||||||
cur_ptr += ALIGNED_SIZE(name_len + 1);
|
assert(cur_off <= addrinfo_len);
|
||||||
}
|
}
|
||||||
assert(cur_ptr <= alloc_ptr + addrinfo_len);
|
|
||||||
|
|
||||||
/* set next ptr */
|
/* set next ptr */
|
||||||
addrinfow_ptr = addrinfow_ptr->ai_next;
|
addrinfow_ptr = addrinfow_ptr->ai_next;
|
||||||
if (addrinfow_ptr != NULL) {
|
if (addrinfow_ptr == NULL)
|
||||||
addrinfo_ptr->ai_next = (struct addrinfo*)cur_ptr;
|
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;
|
req->addrinfo = (struct addrinfo*)alloc_ptr;
|
||||||
} else {
|
} else {
|
||||||
@ -206,7 +208,7 @@ static void uv__getaddrinfo_done(struct uv__work* w, int status) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
complete:
|
complete:
|
||||||
uv__req_unregister(req->loop, req);
|
uv__req_unregister(req->loop);
|
||||||
|
|
||||||
/* finally do callback with converted result */
|
/* finally do callback with converted result */
|
||||||
if (req->getaddrinfo_cb)
|
if (req->getaddrinfo_cb)
|
||||||
@ -242,10 +244,12 @@ int uv_getaddrinfo(uv_loop_t* loop,
|
|||||||
const char* service,
|
const char* service,
|
||||||
const struct addrinfo* hints) {
|
const struct addrinfo* hints) {
|
||||||
char hostname_ascii[256];
|
char hostname_ascii[256];
|
||||||
|
size_t off = 0;
|
||||||
size_t nodesize = 0;
|
size_t nodesize = 0;
|
||||||
size_t servicesize = 0;
|
size_t servicesize = 0;
|
||||||
|
size_t serviceoff = 0;
|
||||||
size_t hintssize = 0;
|
size_t hintssize = 0;
|
||||||
char* alloc_ptr = NULL;
|
size_t hintoff = 0;
|
||||||
ssize_t rc;
|
ssize_t rc;
|
||||||
|
|
||||||
if (req == NULL || (node == NULL && service == NULL)) {
|
if (req == NULL || (node == NULL && service == NULL)) {
|
||||||
@ -268,6 +272,7 @@ int uv_getaddrinfo(uv_loop_t* loop,
|
|||||||
return rc;
|
return rc;
|
||||||
nodesize = strlen(hostname_ascii) + 1;
|
nodesize = strlen(hostname_ascii) + 1;
|
||||||
node = hostname_ascii;
|
node = hostname_ascii;
|
||||||
|
off += nodesize * sizeof(WCHAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (service != NULL) {
|
if (service != NULL) {
|
||||||
@ -275,27 +280,28 @@ int uv_getaddrinfo(uv_loop_t* loop,
|
|||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
return rc;
|
return rc;
|
||||||
servicesize = rc;
|
servicesize = rc;
|
||||||
|
off = align_offset(off, sizeof(WCHAR));
|
||||||
|
serviceoff = off;
|
||||||
|
off += servicesize * sizeof(WCHAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hints != NULL) {
|
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 */
|
/* allocate memory for inputs, and partition it as needed */
|
||||||
alloc_ptr = uv__malloc(ALIGNED_SIZE(nodesize * sizeof(WCHAR)) +
|
req->alloc = uv__malloc(off);
|
||||||
ALIGNED_SIZE(servicesize * sizeof(WCHAR)) +
|
if (!req->alloc)
|
||||||
hintssize);
|
|
||||||
if (!alloc_ptr)
|
|
||||||
return UV_ENOMEM;
|
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
|
/* Convert node string to UTF16 into allocated memory and save pointer in the
|
||||||
* request. The node here has been converted to ascii. */
|
* request. The node here has been converted to ascii. */
|
||||||
if (node != NULL) {
|
if (node != NULL) {
|
||||||
req->node = (WCHAR*) alloc_ptr;
|
req->node = (WCHAR*) req->alloc;
|
||||||
uv_wtf8_to_utf16(node, (WCHAR*) alloc_ptr, nodesize);
|
uv_wtf8_to_utf16(node, req->node, nodesize);
|
||||||
alloc_ptr += ALIGNED_SIZE(nodesize * sizeof(WCHAR));
|
|
||||||
} else {
|
} else {
|
||||||
req->node = NULL;
|
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
|
/* Convert service string to UTF16 into allocated memory and save pointer in
|
||||||
* the req. */
|
* the req. */
|
||||||
if (service != NULL) {
|
if (service != NULL) {
|
||||||
req->service = (WCHAR*) alloc_ptr;
|
req->service = (WCHAR*) ((char*) req->alloc + serviceoff);
|
||||||
uv_wtf8_to_utf16(service, (WCHAR*) alloc_ptr, servicesize);
|
uv_wtf8_to_utf16(service, req->service, servicesize);
|
||||||
alloc_ptr += ALIGNED_SIZE(servicesize * sizeof(WCHAR));
|
|
||||||
} else {
|
} else {
|
||||||
req->service = NULL;
|
req->service = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* copy hints to allocated memory and save pointer in req */
|
/* copy hints to allocated memory and save pointer in req */
|
||||||
if (hints != NULL) {
|
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_family = hints->ai_family;
|
||||||
req->addrinfow->ai_socktype = hints->ai_socktype;
|
req->addrinfow->ai_socktype = hints->ai_socktype;
|
||||||
req->addrinfow->ai_protocol = hints->ai_protocol;
|
req->addrinfow->ai_protocol = hints->ai_protocol;
|
||||||
@ -325,7 +330,7 @@ int uv_getaddrinfo(uv_loop_t* loop,
|
|||||||
req->addrinfow = NULL;
|
req->addrinfow = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
uv__req_register(loop, req);
|
uv__req_register(loop);
|
||||||
|
|
||||||
if (getaddrinfo_cb) {
|
if (getaddrinfo_cb) {
|
||||||
uv__work_submit(loop,
|
uv__work_submit(loop,
|
||||||
|
|||||||
@ -82,7 +82,7 @@ static void uv__getnameinfo_done(struct uv__work* w, int status) {
|
|||||||
char* service;
|
char* service;
|
||||||
|
|
||||||
req = container_of(w, uv_getnameinfo_t, work_req);
|
req = container_of(w, uv_getnameinfo_t, work_req);
|
||||||
uv__req_unregister(req->loop, req);
|
uv__req_unregister(req->loop);
|
||||||
host = service = NULL;
|
host = service = NULL;
|
||||||
|
|
||||||
if (status == UV_ECANCELED) {
|
if (status == UV_ECANCELED) {
|
||||||
@ -124,7 +124,7 @@ int uv_getnameinfo(uv_loop_t* loop,
|
|||||||
}
|
}
|
||||||
|
|
||||||
UV_REQ_INIT(req, UV_GETNAMEINFO);
|
UV_REQ_INIT(req, UV_GETNAMEINFO);
|
||||||
uv__req_register(loop, req);
|
uv__req_register(loop);
|
||||||
|
|
||||||
req->getnameinfo_cb = getnameinfo_cb;
|
req->getnameinfo_cb = getnameinfo_cb;
|
||||||
req->flags = flags;
|
req->flags = flags;
|
||||||
|
|||||||
@ -330,4 +330,6 @@ void uv__wake_all_loops(void);
|
|||||||
*/
|
*/
|
||||||
void uv__init_detect_system_wakeup(void);
|
void uv__init_detect_system_wakeup(void);
|
||||||
|
|
||||||
|
int uv_translate_write_sys_error(int sys_errno);
|
||||||
|
|
||||||
#endif /* UV_WIN_INTERNAL_H_ */
|
#endif /* UV_WIN_INTERNAL_H_ */
|
||||||
|
|||||||
376
src/win/pipe.c
376
src/win/pipe.c
@ -98,8 +98,16 @@ static void eof_timer_destroy(uv_pipe_t* pipe);
|
|||||||
static void eof_timer_close_cb(uv_handle_t* handle);
|
static void eof_timer_close_cb(uv_handle_t* handle);
|
||||||
|
|
||||||
|
|
||||||
static void uv__unique_pipe_name(char* ptr, char* name, size_t size) {
|
/* Does the file path contain embedded nul bytes? */
|
||||||
snprintf(name, size, "\\\\?\\pipe\\uv\\%p-%lu", ptr, GetCurrentProcessId());
|
static int includes_nul(const char *s, size_t n) {
|
||||||
|
if (n == 0)
|
||||||
|
return 0;
|
||||||
|
return NULL != memchr(s, '\0', n);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void uv__unique_pipe_name(unsigned long long ptr, char* name, size_t size) {
|
||||||
|
snprintf(name, size, "\\\\?\\pipe\\uv\\%llu-%lu", ptr, GetCurrentProcessId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -191,7 +199,7 @@ static void close_pipe(uv_pipe_t* pipe) {
|
|||||||
if (pipe->u.fd == -1)
|
if (pipe->u.fd == -1)
|
||||||
CloseHandle(pipe->handle);
|
CloseHandle(pipe->handle);
|
||||||
else
|
else
|
||||||
close(pipe->u.fd);
|
_close(pipe->u.fd);
|
||||||
|
|
||||||
pipe->u.fd = -1;
|
pipe->u.fd = -1;
|
||||||
pipe->handle = INVALID_HANDLE_VALUE;
|
pipe->handle = INVALID_HANDLE_VALUE;
|
||||||
@ -200,7 +208,7 @@ static void close_pipe(uv_pipe_t* pipe) {
|
|||||||
|
|
||||||
static int uv__pipe_server(
|
static int uv__pipe_server(
|
||||||
HANDLE* pipeHandle_ptr, DWORD access,
|
HANDLE* pipeHandle_ptr, DWORD access,
|
||||||
char* name, size_t nameSize, char* random) {
|
char* name, size_t nameSize, unsigned long long random) {
|
||||||
HANDLE pipeHandle;
|
HANDLE pipeHandle;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
@ -241,7 +249,7 @@ static int uv__pipe_server(
|
|||||||
static int uv__create_pipe_pair(
|
static int uv__create_pipe_pair(
|
||||||
HANDLE* server_pipe_ptr, HANDLE* client_pipe_ptr,
|
HANDLE* server_pipe_ptr, HANDLE* client_pipe_ptr,
|
||||||
unsigned int server_flags, unsigned int client_flags,
|
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 */
|
/* allowed flags are: UV_READABLE_PIPE | UV_WRITABLE_PIPE | UV_NONBLOCK_PIPE */
|
||||||
char pipe_name[64];
|
char pipe_name[64];
|
||||||
SECURITY_ATTRIBUTES sa;
|
SECURITY_ATTRIBUTES sa;
|
||||||
@ -349,7 +357,12 @@ int uv_pipe(uv_file fds[2], int read_flags, int write_flags) {
|
|||||||
/* TODO: better source of local randomness than &fds? */
|
/* TODO: better source of local randomness than &fds? */
|
||||||
read_flags |= UV_READABLE_PIPE;
|
read_flags |= UV_READABLE_PIPE;
|
||||||
write_flags |= UV_WRITABLE_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)
|
if (err != 0)
|
||||||
return err;
|
return err;
|
||||||
temp[0] = _open_osfhandle((intptr_t) readh, 0);
|
temp[0] = _open_osfhandle((intptr_t) readh, 0);
|
||||||
@ -413,7 +426,7 @@ int uv__create_stdio_pipe_pair(uv_loop_t* loop,
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = uv__create_pipe_pair(&server_pipe, &client_pipe,
|
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)
|
if (err)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
@ -659,15 +672,10 @@ void uv__pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) {
|
|||||||
}
|
}
|
||||||
handle->pipe.conn.ipc_xfer_queue_length = 0;
|
handle->pipe.conn.ipc_xfer_queue_length = 0;
|
||||||
|
|
||||||
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
|
assert(handle->read_req.wait_handle == INVALID_HANDLE_VALUE);
|
||||||
if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) {
|
if (handle->read_req.event_handle != NULL) {
|
||||||
UnregisterWait(handle->read_req.wait_handle);
|
CloseHandle(handle->read_req.event_handle);
|
||||||
handle->read_req.wait_handle = INVALID_HANDLE_VALUE;
|
handle->read_req.event_handle = NULL;
|
||||||
}
|
|
||||||
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)
|
if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)
|
||||||
@ -705,6 +713,7 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
|||||||
uv_loop_t* loop = handle->loop;
|
uv_loop_t* loop = handle->loop;
|
||||||
int i, err;
|
int i, err;
|
||||||
uv_pipe_accept_t* req;
|
uv_pipe_accept_t* req;
|
||||||
|
char* name_copy;
|
||||||
|
|
||||||
if (flags & ~UV_PIPE_NO_TRUNCATE) {
|
if (flags & ~UV_PIPE_NO_TRUNCATE) {
|
||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
@ -718,16 +727,10 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
|||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*name == '\0') {
|
if (includes_nul(name, namelen)) {
|
||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & UV_PIPE_NO_TRUNCATE) {
|
|
||||||
if (namelen > 256) {
|
|
||||||
return UV_EINVAL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handle->flags & UV_HANDLE_BOUND) {
|
if (handle->flags & UV_HANDLE_BOUND) {
|
||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
}
|
}
|
||||||
@ -736,14 +739,24 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
|||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
name_copy = uv__malloc(namelen + 1);
|
||||||
|
if (name_copy == NULL) {
|
||||||
|
return UV_ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(name_copy, name, namelen);
|
||||||
|
name_copy[namelen] = '\0';
|
||||||
|
|
||||||
if (!(handle->flags & UV_HANDLE_PIPESERVER)) {
|
if (!(handle->flags & UV_HANDLE_PIPESERVER)) {
|
||||||
handle->pipe.serv.pending_instances = default_pending_pipe_instances;
|
handle->pipe.serv.pending_instances = default_pending_pipe_instances;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = UV_ENOMEM;
|
||||||
handle->pipe.serv.accept_reqs = (uv_pipe_accept_t*)
|
handle->pipe.serv.accept_reqs = (uv_pipe_accept_t*)
|
||||||
uv__malloc(sizeof(uv_pipe_accept_t) * handle->pipe.serv.pending_instances);
|
uv__malloc(sizeof(uv_pipe_accept_t) * handle->pipe.serv.pending_instances);
|
||||||
if (!handle->pipe.serv.accept_reqs)
|
if (handle->pipe.serv.accept_reqs == NULL) {
|
||||||
return UV_ENOMEM;
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < handle->pipe.serv.pending_instances; i++) {
|
for (i = 0; i < handle->pipe.serv.pending_instances; i++) {
|
||||||
req = &handle->pipe.serv.accept_reqs[i];
|
req = &handle->pipe.serv.accept_reqs[i];
|
||||||
@ -753,9 +766,14 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
|||||||
req->next_pending = NULL;
|
req->next_pending = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = uv__convert_utf8_to_utf16(name, &handle->name);
|
/* TODO(bnoordhuis) Add converters that take a |length| parameter. */
|
||||||
if (err)
|
err = uv__convert_utf8_to_utf16(name_copy, &handle->name);
|
||||||
return err;
|
uv__free(name_copy);
|
||||||
|
name_copy = NULL;
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Attempt to create the first pipe with FILE_FLAG_FIRST_PIPE_INSTANCE.
|
* Attempt to create the first pipe with FILE_FLAG_FIRST_PIPE_INSTANCE.
|
||||||
@ -767,9 +785,11 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
|||||||
TRUE)) {
|
TRUE)) {
|
||||||
err = GetLastError();
|
err = GetLastError();
|
||||||
if (err == ERROR_ACCESS_DENIED) {
|
if (err == ERROR_ACCESS_DENIED) {
|
||||||
err = WSAEADDRINUSE; /* Translates to UV_EADDRINUSE. */
|
err = UV_EADDRINUSE;
|
||||||
} else if (err == ERROR_PATH_NOT_FOUND || err == ERROR_INVALID_NAME) {
|
} else if (err == ERROR_PATH_NOT_FOUND || err == ERROR_INVALID_NAME) {
|
||||||
err = WSAEACCES; /* Translates to UV_EACCES. */
|
err = UV_EACCES;
|
||||||
|
} else {
|
||||||
|
err = uv_translate_sys_error(err);
|
||||||
}
|
}
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@ -781,10 +801,13 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
uv__free(handle->pipe.serv.accept_reqs);
|
||||||
uv__free(handle->name);
|
uv__free(handle->name);
|
||||||
|
uv__free(name_copy);
|
||||||
|
handle->pipe.serv.accept_reqs = NULL;
|
||||||
handle->name = NULL;
|
handle->name = NULL;
|
||||||
|
|
||||||
return uv_translate_sys_error(err);
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -834,7 +857,19 @@ void uv_pipe_connect(uv_connect_t* req,
|
|||||||
uv_pipe_t* handle,
|
uv_pipe_t* handle,
|
||||||
const char* name,
|
const char* name,
|
||||||
uv_connect_cb cb) {
|
uv_connect_cb cb) {
|
||||||
uv_pipe_connect2(req, handle, name, strlen(name), 0, cb);
|
uv_loop_t* loop;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = uv_pipe_connect2(req, handle, name, strlen(name), 0, cb);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
loop = handle->loop;
|
||||||
|
/* Make this req pending reporting an error. */
|
||||||
|
SET_REQ_ERROR(req, err);
|
||||||
|
uv__insert_pending_req(loop, (uv_req_t*) req);
|
||||||
|
handle->reqs_pending++;
|
||||||
|
REGISTER_HANDLE_REQ(loop, handle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -844,11 +879,20 @@ int uv_pipe_connect2(uv_connect_t* req,
|
|||||||
size_t namelen,
|
size_t namelen,
|
||||||
unsigned int flags,
|
unsigned int flags,
|
||||||
uv_connect_cb cb) {
|
uv_connect_cb cb) {
|
||||||
uv_loop_t* loop = handle->loop;
|
uv_loop_t* loop;
|
||||||
int err;
|
int err;
|
||||||
size_t nameSize;
|
size_t nameSize;
|
||||||
HANDLE pipeHandle = INVALID_HANDLE_VALUE;
|
HANDLE pipeHandle = INVALID_HANDLE_VALUE;
|
||||||
DWORD duplex_flags;
|
DWORD duplex_flags;
|
||||||
|
char* name_copy;
|
||||||
|
|
||||||
|
loop = handle->loop;
|
||||||
|
UV_REQ_INIT(req, UV_CONNECT);
|
||||||
|
req->handle = (uv_stream_t*) handle;
|
||||||
|
req->cb = cb;
|
||||||
|
req->u.connect.pipeHandle = INVALID_HANDLE_VALUE;
|
||||||
|
req->u.connect.duplex_flags = 0;
|
||||||
|
req->u.connect.name = NULL;
|
||||||
|
|
||||||
if (flags & ~UV_PIPE_NO_TRUNCATE) {
|
if (flags & ~UV_PIPE_NO_TRUNCATE) {
|
||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
@ -862,22 +906,17 @@ int uv_pipe_connect2(uv_connect_t* req,
|
|||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*name == '\0') {
|
if (includes_nul(name, namelen)) {
|
||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & UV_PIPE_NO_TRUNCATE) {
|
name_copy = uv__malloc(namelen + 1);
|
||||||
if (namelen > 256) {
|
if (name_copy == NULL) {
|
||||||
return UV_EINVAL;
|
return UV_ENOMEM;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UV_REQ_INIT(req, UV_CONNECT);
|
memcpy(name_copy, name, namelen);
|
||||||
req->handle = (uv_stream_t*) handle;
|
name_copy[namelen] = '\0';
|
||||||
req->cb = cb;
|
|
||||||
req->u.connect.pipeHandle = INVALID_HANDLE_VALUE;
|
|
||||||
req->u.connect.duplex_flags = 0;
|
|
||||||
req->u.connect.name = NULL;
|
|
||||||
|
|
||||||
if (handle->flags & UV_HANDLE_PIPESERVER) {
|
if (handle->flags & UV_HANDLE_PIPESERVER) {
|
||||||
err = ERROR_INVALID_PARAMETER;
|
err = ERROR_INVALID_PARAMETER;
|
||||||
@ -889,7 +928,11 @@ int uv_pipe_connect2(uv_connect_t* req,
|
|||||||
}
|
}
|
||||||
uv__pipe_connection_init(handle);
|
uv__pipe_connection_init(handle);
|
||||||
|
|
||||||
err = uv__convert_utf8_to_utf16(name, &handle->name);
|
/* TODO(bnoordhuis) Add converters that take a |length| parameter. */
|
||||||
|
err = uv__convert_utf8_to_utf16(name_copy, &handle->name);
|
||||||
|
uv__free(name_copy);
|
||||||
|
name_copy = NULL;
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
err = ERROR_NO_UNICODE_TRANSLATION;
|
err = ERROR_NO_UNICODE_TRANSLATION;
|
||||||
goto error;
|
goto error;
|
||||||
@ -916,7 +959,7 @@ int uv_pipe_connect2(uv_connect_t* req,
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
REGISTER_HANDLE_REQ(loop, handle);
|
||||||
handle->reqs_pending++;
|
handle->reqs_pending++;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -931,10 +974,12 @@ int uv_pipe_connect2(uv_connect_t* req,
|
|||||||
SET_REQ_SUCCESS(req);
|
SET_REQ_SUCCESS(req);
|
||||||
uv__insert_pending_req(loop, (uv_req_t*) req);
|
uv__insert_pending_req(loop, (uv_req_t*) req);
|
||||||
handle->reqs_pending++;
|
handle->reqs_pending++;
|
||||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
REGISTER_HANDLE_REQ(loop, handle);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
uv__free(name_copy);
|
||||||
|
|
||||||
if (handle->name) {
|
if (handle->name) {
|
||||||
uv__free(handle->name);
|
uv__free(handle->name);
|
||||||
handle->name = NULL;
|
handle->name = NULL;
|
||||||
@ -947,7 +992,7 @@ error:
|
|||||||
SET_REQ_ERROR(req, err);
|
SET_REQ_ERROR(req, err);
|
||||||
uv__insert_pending_req(loop, (uv_req_t*) req);
|
uv__insert_pending_req(loop, (uv_req_t*) req);
|
||||||
handle->reqs_pending++;
|
handle->reqs_pending++;
|
||||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
REGISTER_HANDLE_REQ(loop, handle);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1372,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 (handle->flags & UV_HANDLE_EMULATE_IOCP) {
|
||||||
if (req->wait_handle == INVALID_HANDLE_VALUE) {
|
assert(req->wait_handle == INVALID_HANDLE_VALUE);
|
||||||
if (!RegisterWaitForSingleObject(&req->wait_handle,
|
if (!RegisterWaitForSingleObject(&req->wait_handle,
|
||||||
req->event_handle, post_completion_read_wait, (void*) req,
|
req->event_handle, post_completion_read_wait, (void*) req,
|
||||||
INFINITE, WT_EXECUTEINWAITTHREAD)) {
|
INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) {
|
||||||
SET_REQ_ERROR(req, GetLastError());
|
SET_REQ_ERROR(req, GetLastError());
|
||||||
goto error;
|
goto error;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1406,16 +1450,16 @@ int uv__pipe_read_start(uv_pipe_t* handle,
|
|||||||
handle->read_cb = read_cb;
|
handle->read_cb = read_cb;
|
||||||
handle->alloc_cb = alloc_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
|
/* If reading was stopped and then started again, there could still be a read
|
||||||
* request pending. */
|
* request pending. */
|
||||||
if (!(handle->flags & UV_HANDLE_READ_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);
|
uv__pipe_queue_read(loop, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1593,7 +1637,7 @@ static int uv__pipe_write_data(uv_loop_t* loop,
|
|||||||
req->u.io.queued_bytes = 0;
|
req->u.io.queued_bytes = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
REGISTER_HANDLE_REQ(loop, handle);
|
||||||
handle->reqs_pending++;
|
handle->reqs_pending++;
|
||||||
handle->stream.conn.write_reqs_pending++;
|
handle->stream.conn.write_reqs_pending++;
|
||||||
POST_COMPLETION_FOR_REQ(loop, req);
|
POST_COMPLETION_FOR_REQ(loop, req);
|
||||||
@ -1641,7 +1685,7 @@ static int uv__pipe_write_data(uv_loop_t* loop,
|
|||||||
CloseHandle(req->event_handle);
|
CloseHandle(req->event_handle);
|
||||||
req->event_handle = NULL;
|
req->event_handle = NULL;
|
||||||
|
|
||||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
REGISTER_HANDLE_REQ(loop, handle);
|
||||||
handle->reqs_pending++;
|
handle->reqs_pending++;
|
||||||
handle->stream.conn.write_reqs_pending++;
|
handle->stream.conn.write_reqs_pending++;
|
||||||
return 0;
|
return 0;
|
||||||
@ -1668,13 +1712,13 @@ static int uv__pipe_write_data(uv_loop_t* loop,
|
|||||||
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
|
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
|
||||||
if (!RegisterWaitForSingleObject(&req->wait_handle,
|
if (!RegisterWaitForSingleObject(&req->wait_handle,
|
||||||
req->event_handle, post_completion_write_wait, (void*) req,
|
req->event_handle, post_completion_write_wait, (void*) req,
|
||||||
INFINITE, WT_EXECUTEINWAITTHREAD)) {
|
INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) {
|
||||||
return GetLastError();
|
return GetLastError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
REGISTER_HANDLE_REQ(loop, handle);
|
||||||
handle->reqs_pending++;
|
handle->reqs_pending++;
|
||||||
handle->stream.conn.write_reqs_pending++;
|
handle->stream.conn.write_reqs_pending++;
|
||||||
|
|
||||||
@ -1844,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,
|
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) {
|
if (error == ERROR_BROKEN_PIPE) {
|
||||||
uv__pipe_read_eof(loop, handle, buf);
|
uv__pipe_read_eof(loop, handle, buf);
|
||||||
} else {
|
} else {
|
||||||
@ -1874,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
|
/* 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
|
* encountered before the requested number of bytes are read, an error is
|
||||||
* returned. */
|
* returned. */
|
||||||
static int uv__pipe_read_exactly(HANDLE h, void* buffer, DWORD count) {
|
static DWORD uv__pipe_read_exactly(uv_pipe_t* handle, void* buffer, DWORD count) {
|
||||||
DWORD bytes_read, bytes_read_now;
|
uv_read_t* req;
|
||||||
|
DWORD bytes_read;
|
||||||
|
DWORD bytes_read_now;
|
||||||
|
|
||||||
bytes_read = 0;
|
bytes_read = 0;
|
||||||
while (bytes_read < count) {
|
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,
|
(char*) buffer + bytes_read,
|
||||||
count - bytes_read,
|
count - bytes_read,
|
||||||
&bytes_read_now,
|
&bytes_read_now,
|
||||||
NULL)) {
|
&req->u.io.overlapped)) {
|
||||||
return GetLastError();
|
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;
|
bytes_read += bytes_read_now;
|
||||||
@ -1895,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,
|
static int uv__pipe_read_data(uv_loop_t* loop,
|
||||||
uv_pipe_t* handle,
|
uv_pipe_t* handle,
|
||||||
DWORD suggested_bytes,
|
DWORD* bytes_read, /* inout argument */
|
||||||
DWORD max_bytes) {
|
DWORD max_bytes) {
|
||||||
DWORD bytes_read;
|
|
||||||
uv_buf_t buf;
|
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. */
|
/* Ask the user for a buffer to read data into. */
|
||||||
buf = uv_buf_init(NULL, 0);
|
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) {
|
if (buf.base == NULL || buf.len == 0) {
|
||||||
handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf);
|
handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf);
|
||||||
return 0; /* Break out of read loop. */
|
return 0; /* Break out of read loop. */
|
||||||
@ -1913,33 +1968,77 @@ static DWORD uv__pipe_read_data(uv_loop_t* loop,
|
|||||||
/* Ensure we read at most the smaller of:
|
/* Ensure we read at most the smaller of:
|
||||||
* (a) the length of the user-allocated buffer.
|
* (a) the length of the user-allocated buffer.
|
||||||
* (b) the maximum data length as specified by the `max_bytes` argument.
|
* (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)
|
if (max_bytes > buf.len)
|
||||||
max_bytes = buf.len;
|
max_bytes = buf.len;
|
||||||
|
|
||||||
/* Read into the user buffer. */
|
if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) {
|
||||||
if (!ReadFile(handle->handle, buf.base, max_bytes, &bytes_read, NULL)) {
|
/* The user failed to supply a pipe that can be used non-blocking or with
|
||||||
uv__pipe_read_error_or_eof(loop, handle, GetLastError(), buf);
|
* threads. Try to estimate the amount of data that is safe to read without
|
||||||
return 0; /* Break out of read loop. */
|
* 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. */
|
/* 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) {
|
static int uv__pipe_read_ipc(uv_loop_t* loop, uv_pipe_t* handle) {
|
||||||
uint32_t* data_remaining = &handle->pipe.conn.ipc_data_frame.payload_remaining;
|
uint32_t* data_remaining;
|
||||||
int err;
|
DWORD err;
|
||||||
|
DWORD more;
|
||||||
|
DWORD bytes_read;
|
||||||
|
|
||||||
|
data_remaining = &handle->pipe.conn.ipc_data_frame.payload_remaining;
|
||||||
|
|
||||||
if (*data_remaining > 0) {
|
if (*data_remaining > 0) {
|
||||||
/* Read frame data payload. */
|
/* Read frame data payload. */
|
||||||
DWORD bytes_read =
|
bytes_read = *data_remaining;
|
||||||
uv__pipe_read_data(loop, handle, *data_remaining, *data_remaining);
|
more = uv__pipe_read_data(loop, handle, &bytes_read, bytes_read);
|
||||||
*data_remaining -= bytes_read;
|
*data_remaining -= bytes_read;
|
||||||
return bytes_read;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* Start of a new IPC frame. */
|
/* Start of a new IPC frame. */
|
||||||
@ -1950,7 +2049,7 @@ static DWORD uv__pipe_read_ipc(uv_loop_t* loop, uv_pipe_t* handle) {
|
|||||||
|
|
||||||
/* Read the IPC frame header. */
|
/* Read the IPC frame header. */
|
||||||
err = uv__pipe_read_exactly(
|
err = uv__pipe_read_exactly(
|
||||||
handle->handle, &frame_header, sizeof frame_header);
|
handle, &frame_header, sizeof frame_header);
|
||||||
if (err)
|
if (err)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
@ -1986,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
|
/* If no socket xfer info follows, return here. Data will be read in a
|
||||||
* subsequent invocation of uv__pipe_read_ipc(). */
|
* subsequent invocation of uv__pipe_read_ipc(). */
|
||||||
if (xfer_type == UV__IPC_SOCKET_XFER_NONE)
|
if (xfer_type != UV__IPC_SOCKET_XFER_NONE) {
|
||||||
return sizeof frame_header; /* Number of bytes read. */
|
/* Read transferred socket information. */
|
||||||
|
err = uv__pipe_read_exactly(handle, &xfer_info, sizeof xfer_info);
|
||||||
|
if (err)
|
||||||
|
goto error;
|
||||||
|
|
||||||
/* Read transferred socket information. */
|
/* Store the pending socket info. */
|
||||||
err = uv__pipe_read_exactly(handle->handle, &xfer_info, sizeof xfer_info);
|
uv__pipe_queue_ipc_xfer_info(handle, xfer_type, &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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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:
|
||||||
/* Invalid frame. */
|
/* Invalid frame. */
|
||||||
err = WSAECONNABORTED; /* Maps to UV_ECONNABORTED. */
|
err = WSAECONNABORTED; /* Maps to UV_ECONNABORTED. */
|
||||||
@ -2014,12 +2116,20 @@ error:
|
|||||||
void uv__process_pipe_read_req(uv_loop_t* loop,
|
void uv__process_pipe_read_req(uv_loop_t* loop,
|
||||||
uv_pipe_t* handle,
|
uv_pipe_t* handle,
|
||||||
uv_req_t* req) {
|
uv_req_t* req) {
|
||||||
|
DWORD err;
|
||||||
|
DWORD more;
|
||||||
|
DWORD bytes_requested;
|
||||||
assert(handle->type == UV_NAMED_PIPE);
|
assert(handle->type == UV_NAMED_PIPE);
|
||||||
|
|
||||||
handle->flags &= ~(UV_HANDLE_READ_PENDING | UV_HANDLE_CANCELLATION_PENDING);
|
handle->flags &= ~(UV_HANDLE_READ_PENDING | UV_HANDLE_CANCELLATION_PENDING);
|
||||||
DECREASE_PENDING_REQ_COUNT(handle);
|
DECREASE_PENDING_REQ_COUNT(handle);
|
||||||
eof_timer_stop(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
|
/* 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
|
* reading the pipe in the meantime, there is nothing left to do, since there
|
||||||
* is no callback that we can call. */
|
* is no callback that we can call. */
|
||||||
@ -2028,7 +2138,7 @@ void uv__process_pipe_read_req(uv_loop_t* loop,
|
|||||||
|
|
||||||
if (!REQ_SUCCESS(req)) {
|
if (!REQ_SUCCESS(req)) {
|
||||||
/* An error occurred doing the zero-read. */
|
/* 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
|
/* 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
|
* indicate an ERROR_OPERATION_ABORTED error. This error isn't relevant to
|
||||||
@ -2039,34 +2149,18 @@ void uv__process_pipe_read_req(uv_loop_t* loop,
|
|||||||
} else {
|
} else {
|
||||||
/* The zero-read completed without error, indicating there is data
|
/* The zero-read completed without error, indicating there is data
|
||||||
* available in the kernel buffer. */
|
* available in the kernel buffer. */
|
||||||
DWORD avail;
|
while (handle->flags & UV_HANDLE_READING) {
|
||||||
|
bytes_requested = 65536;
|
||||||
/* 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) {
|
|
||||||
/* Depending on the type of pipe, read either IPC frames or raw data. */
|
/* Depending on the type of pipe, read either IPC frames or raw data. */
|
||||||
DWORD bytes_read =
|
if (handle->ipc)
|
||||||
handle->ipc ? uv__pipe_read_ipc(loop, handle)
|
more = uv__pipe_read_ipc(loop, handle);
|
||||||
: uv__pipe_read_data(loop, handle, avail, (DWORD) -1);
|
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
|
/* If no bytes were read, treat this as an indication that an error
|
||||||
* occurred, and break out of the read loop. */
|
* occurred, and break out of the read loop. */
|
||||||
if (bytes_read == 0)
|
if (more == 0)
|
||||||
break;
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2087,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);
|
assert(handle->write_queue_size >= req->u.io.queued_bytes);
|
||||||
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) {
|
||||||
if (req->wait_handle != INVALID_HANDLE_VALUE) {
|
UnregisterWait(req->wait_handle);
|
||||||
UnregisterWait(req->wait_handle);
|
req->wait_handle = INVALID_HANDLE_VALUE;
|
||||||
req->wait_handle = INVALID_HANDLE_VALUE;
|
}
|
||||||
}
|
if (req->event_handle) {
|
||||||
if (req->event_handle) {
|
CloseHandle(req->event_handle);
|
||||||
CloseHandle(req->event_handle);
|
req->event_handle = NULL;
|
||||||
req->event_handle = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = GET_REQ_ERROR(req);
|
err = GET_REQ_ERROR(req);
|
||||||
@ -2174,7 +2266,7 @@ void uv__process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle,
|
|||||||
|
|
||||||
assert(handle->type == UV_NAMED_PIPE);
|
assert(handle->type == UV_NAMED_PIPE);
|
||||||
|
|
||||||
UNREGISTER_HANDLE_REQ(loop, handle, req);
|
UNREGISTER_HANDLE_REQ(loop, handle);
|
||||||
|
|
||||||
err = 0;
|
err = 0;
|
||||||
if (REQ_SUCCESS(req)) {
|
if (REQ_SUCCESS(req)) {
|
||||||
@ -2206,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. */
|
/* Clear the shutdown_req field so we don't go here again. */
|
||||||
handle->stream.conn.shutdown_req = NULL;
|
handle->stream.conn.shutdown_req = NULL;
|
||||||
UNREGISTER_HANDLE_REQ(loop, handle, req);
|
UNREGISTER_HANDLE_REQ(loop, handle);
|
||||||
|
|
||||||
if (handle->flags & UV_HANDLE_CLOSING) {
|
if (handle->flags & UV_HANDLE_CLOSING) {
|
||||||
/* Already closing. Cancel the shutdown. */
|
/* Already closing. Cancel the shutdown. */
|
||||||
@ -2510,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) {
|
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)
|
if (handle->flags & UV_HANDLE_BOUND)
|
||||||
return uv__pipe_getname(handle, buffer, size);
|
return uv__pipe_getname(handle, buffer, size);
|
||||||
|
|
||||||
@ -2524,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) {
|
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 */
|
/* emulate unix behaviour */
|
||||||
if (handle->flags & UV_HANDLE_BOUND)
|
if (handle->flags & UV_HANDLE_BOUND)
|
||||||
return UV_ENOTCONN;
|
return UV_ENOTCONN;
|
||||||
|
|||||||
@ -46,12 +46,12 @@
|
|||||||
#define CHILD_STDIO_CRT_FLAGS(buffer, fd) \
|
#define CHILD_STDIO_CRT_FLAGS(buffer, fd) \
|
||||||
*((unsigned char*) (buffer) + sizeof(int) + fd)
|
*((unsigned char*) (buffer) + sizeof(int) + fd)
|
||||||
|
|
||||||
#define CHILD_STDIO_HANDLE(buffer, fd) \
|
#define CHILD_STDIO_HANDLE(buffer, fd) \
|
||||||
*((HANDLE*) ((unsigned char*) (buffer) + \
|
((void*) ((unsigned char*) (buffer) + \
|
||||||
sizeof(int) + \
|
sizeof(int) + \
|
||||||
sizeof(unsigned char) * \
|
sizeof(unsigned char) * \
|
||||||
CHILD_STDIO_COUNT((buffer)) + \
|
CHILD_STDIO_COUNT((buffer)) + \
|
||||||
sizeof(HANDLE) * (fd)))
|
sizeof(HANDLE) * (fd)))
|
||||||
|
|
||||||
|
|
||||||
/* CRT file descriptor mode flags */
|
/* CRT file descriptor mode flags */
|
||||||
@ -194,7 +194,7 @@ int uv__stdio_create(uv_loop_t* loop,
|
|||||||
CHILD_STDIO_COUNT(buffer) = count;
|
CHILD_STDIO_COUNT(buffer) = count;
|
||||||
for (i = 0; i < count; i++) {
|
for (i = 0; i < count; i++) {
|
||||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
|
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++) {
|
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.
|
* handles in the stdio buffer are initialized with.
|
||||||
* INVALID_HANDLE_VALUE, which should be okay. */
|
* INVALID_HANDLE_VALUE, which should be okay. */
|
||||||
if (i <= 2) {
|
if (i <= 2) {
|
||||||
|
HANDLE nul;
|
||||||
DWORD access = (i == 0) ? FILE_GENERIC_READ :
|
DWORD access = (i == 0) ? FILE_GENERIC_READ :
|
||||||
FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES;
|
FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES;
|
||||||
|
|
||||||
err = uv__create_nul_handle(&CHILD_STDIO_HANDLE(buffer, i),
|
err = uv__create_nul_handle(&nul, access);
|
||||||
access);
|
|
||||||
if (err)
|
if (err)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
memcpy(CHILD_STDIO_HANDLE(buffer, i), &nul, sizeof(HANDLE));
|
||||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
|
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -247,7 +248,7 @@ int uv__stdio_create(uv_loop_t* loop,
|
|||||||
if (err)
|
if (err)
|
||||||
goto error;
|
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;
|
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -263,7 +264,7 @@ int uv__stdio_create(uv_loop_t* loop,
|
|||||||
* error. */
|
* error. */
|
||||||
if (fdopt.data.fd <= 2 && err == ERROR_INVALID_HANDLE) {
|
if (fdopt.data.fd <= 2 && err == ERROR_INVALID_HANDLE) {
|
||||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
goto error;
|
goto error;
|
||||||
@ -298,7 +299,7 @@ int uv__stdio_create(uv_loop_t* loop,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
CHILD_STDIO_HANDLE(buffer, i) = child_handle;
|
memcpy(CHILD_STDIO_HANDLE(buffer, i), &child_handle, sizeof(HANDLE));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,7 +335,7 @@ int uv__stdio_create(uv_loop_t* loop,
|
|||||||
if (err)
|
if (err)
|
||||||
goto error;
|
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;
|
CHILD_STDIO_CRT_FLAGS(buffer, i) = crt_flags;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -359,7 +360,7 @@ void uv__stdio_destroy(BYTE* buffer) {
|
|||||||
|
|
||||||
count = CHILD_STDIO_COUNT(buffer);
|
count = CHILD_STDIO_COUNT(buffer);
|
||||||
for (i = 0; i < count; i++) {
|
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) {
|
if (handle != INVALID_HANDLE_VALUE) {
|
||||||
CloseHandle(handle);
|
CloseHandle(handle);
|
||||||
}
|
}
|
||||||
@ -374,7 +375,7 @@ void uv__stdio_noinherit(BYTE* buffer) {
|
|||||||
|
|
||||||
count = CHILD_STDIO_COUNT(buffer);
|
count = CHILD_STDIO_COUNT(buffer);
|
||||||
for (i = 0; i < count; i++) {
|
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) {
|
if (handle != INVALID_HANDLE_VALUE) {
|
||||||
SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
|
SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
|
||||||
}
|
}
|
||||||
@ -412,5 +413,7 @@ WORD uv__stdio_size(BYTE* buffer) {
|
|||||||
|
|
||||||
|
|
||||||
HANDLE uv__stdio_handle(BYTE* buffer, int fd) {
|
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 <signal.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
#include <malloc.h> /* alloca */
|
|
||||||
|
|
||||||
#include "uv.h"
|
#include "uv.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
@ -304,8 +303,9 @@ static WCHAR* path_search_walk_ext(const WCHAR *dir,
|
|||||||
* - If there's really only a filename, check the current directory for file,
|
* - If there's really only a filename, check the current directory for file,
|
||||||
* then search all path directories.
|
* then search all path directories.
|
||||||
*
|
*
|
||||||
* - If filename specified has *any* extension, search for the file with the
|
* - If filename specified has *any* extension, or already contains a path
|
||||||
* specified extension first.
|
* and the UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME flag is specified,
|
||||||
|
* search for the file with the exact specified filename first.
|
||||||
*
|
*
|
||||||
* - If the literal filename is not found in a directory, try *appending*
|
* - If the literal filename is not found in a directory, try *appending*
|
||||||
* (not replacing) .com first and then .exe.
|
* (not replacing) .com first and then .exe.
|
||||||
@ -331,7 +331,8 @@ static WCHAR* path_search_walk_ext(const WCHAR *dir,
|
|||||||
*/
|
*/
|
||||||
static WCHAR* search_path(const WCHAR *file,
|
static WCHAR* search_path(const WCHAR *file,
|
||||||
WCHAR *cwd,
|
WCHAR *cwd,
|
||||||
const WCHAR *path) {
|
const WCHAR *path,
|
||||||
|
unsigned int flags) {
|
||||||
int file_has_dir;
|
int file_has_dir;
|
||||||
WCHAR* result = NULL;
|
WCHAR* result = NULL;
|
||||||
WCHAR *file_name_start;
|
WCHAR *file_name_start;
|
||||||
@ -372,16 +373,18 @@ static WCHAR* search_path(const WCHAR *file,
|
|||||||
file, file_name_start - file,
|
file, file_name_start - file,
|
||||||
file_name_start, file_len - (file_name_start - file),
|
file_name_start, file_len - (file_name_start - file),
|
||||||
cwd, cwd_len,
|
cwd, cwd_len,
|
||||||
name_has_ext);
|
name_has_ext || (flags & UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
dir_end = path;
|
dir_end = path;
|
||||||
|
|
||||||
/* The file is really only a name; look in cwd first, then scan path */
|
if (NeedCurrentDirectoryForExePathW(L"")) {
|
||||||
result = path_search_walk_ext(L"", 0,
|
/* The file is really only a name; look in cwd first, then scan path */
|
||||||
file, file_len,
|
result = path_search_walk_ext(L"", 0,
|
||||||
cwd, cwd_len,
|
file, file_len,
|
||||||
name_has_ext);
|
cwd, cwd_len,
|
||||||
|
name_has_ext);
|
||||||
|
}
|
||||||
|
|
||||||
while (result == NULL) {
|
while (result == NULL) {
|
||||||
if (dir_end == NULL || *dir_end == L'\0') {
|
if (dir_end == NULL || *dir_end == L'\0') {
|
||||||
@ -509,7 +512,7 @@ WCHAR* quote_cmd_arg(const WCHAR *source, WCHAR *target) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
target[0] = L'\0';
|
target[0] = L'\0';
|
||||||
wcsrev(start);
|
_wcsrev(start);
|
||||||
*(target++) = L'"';
|
*(target++) = L'"';
|
||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
@ -594,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* a_eq;
|
||||||
wchar_t* b_eq;
|
wchar_t* b_eq;
|
||||||
wchar_t* A;
|
|
||||||
wchar_t* B;
|
|
||||||
int nb;
|
int nb;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -613,27 +614,8 @@ int env_strncmp(const wchar_t* a, int na, const wchar_t* b) {
|
|||||||
assert(b_eq);
|
assert(b_eq);
|
||||||
nb = b_eq - b;
|
nb = b_eq - b;
|
||||||
|
|
||||||
A = alloca((na+1) * sizeof(wchar_t));
|
r = CompareStringOrdinal(a, na, b, nb, /*case insensitive*/TRUE);
|
||||||
B = alloca((nb+1) * sizeof(wchar_t));
|
return r - CSTR_EQUAL;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -672,6 +654,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) {
|
|||||||
WCHAR* dst_copy;
|
WCHAR* dst_copy;
|
||||||
WCHAR** ptr_copy;
|
WCHAR** ptr_copy;
|
||||||
WCHAR** env_copy;
|
WCHAR** env_copy;
|
||||||
|
char* p;
|
||||||
size_t required_vars_value_len[ARRAY_SIZE(required_vars)];
|
size_t required_vars_value_len[ARRAY_SIZE(required_vars)];
|
||||||
|
|
||||||
/* first pass: determine size in UTF-16 */
|
/* first pass: determine size in UTF-16 */
|
||||||
@ -687,11 +670,13 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* second pass: copy to UTF-16 environment block */
|
/* second pass: copy to UTF-16 environment block */
|
||||||
dst_copy = uv__malloc(env_len * sizeof(WCHAR));
|
len = env_block_count * sizeof(WCHAR*);
|
||||||
if (dst_copy == NULL && env_len > 0) {
|
p = uv__malloc(len + env_len * sizeof(WCHAR));
|
||||||
|
if (p == NULL) {
|
||||||
return UV_ENOMEM;
|
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 = dst_copy;
|
||||||
ptr_copy = env_copy;
|
ptr_copy = env_copy;
|
||||||
@ -741,7 +726,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) {
|
|||||||
/* final pass: copy, in sort order, and inserting required variables */
|
/* final pass: copy, in sort order, and inserting required variables */
|
||||||
dst = uv__malloc((1+env_len) * sizeof(WCHAR));
|
dst = uv__malloc((1+env_len) * sizeof(WCHAR));
|
||||||
if (!dst) {
|
if (!dst) {
|
||||||
uv__free(dst_copy);
|
uv__free(p);
|
||||||
return UV_ENOMEM;
|
return UV_ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -786,7 +771,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) {
|
|||||||
assert(env_len == (size_t) (ptr - dst));
|
assert(env_len == (size_t) (ptr - dst));
|
||||||
*ptr = L'\0';
|
*ptr = L'\0';
|
||||||
|
|
||||||
uv__free(dst_copy);
|
uv__free(p);
|
||||||
*dst_ptr = dst;
|
*dst_ptr = dst;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -913,7 +898,7 @@ int uv_spawn(uv_loop_t* loop,
|
|||||||
*env = NULL, *cwd = NULL;
|
*env = NULL, *cwd = NULL;
|
||||||
STARTUPINFOW startup;
|
STARTUPINFOW startup;
|
||||||
PROCESS_INFORMATION info;
|
PROCESS_INFORMATION info;
|
||||||
DWORD process_flags;
|
DWORD process_flags, cwd_len;
|
||||||
BYTE* child_stdio_buffer;
|
BYTE* child_stdio_buffer;
|
||||||
|
|
||||||
uv__process_init(loop, process);
|
uv__process_init(loop, process);
|
||||||
@ -933,6 +918,7 @@ int uv_spawn(uv_loop_t* loop,
|
|||||||
assert(!(options->flags & ~(UV_PROCESS_DETACHED |
|
assert(!(options->flags & ~(UV_PROCESS_DETACHED |
|
||||||
UV_PROCESS_SETGID |
|
UV_PROCESS_SETGID |
|
||||||
UV_PROCESS_SETUID |
|
UV_PROCESS_SETUID |
|
||||||
|
UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME |
|
||||||
UV_PROCESS_WINDOWS_HIDE |
|
UV_PROCESS_WINDOWS_HIDE |
|
||||||
UV_PROCESS_WINDOWS_HIDE_CONSOLE |
|
UV_PROCESS_WINDOWS_HIDE_CONSOLE |
|
||||||
UV_PROCESS_WINDOWS_HIDE_GUI |
|
UV_PROCESS_WINDOWS_HIDE_GUI |
|
||||||
@ -961,9 +947,10 @@ int uv_spawn(uv_loop_t* loop,
|
|||||||
if (err)
|
if (err)
|
||||||
goto done_uv;
|
goto done_uv;
|
||||||
|
|
||||||
|
cwd_len = wcslen(cwd);
|
||||||
} else {
|
} else {
|
||||||
/* Inherit cwd */
|
/* Inherit cwd */
|
||||||
DWORD cwd_len, r;
|
DWORD r;
|
||||||
|
|
||||||
cwd_len = GetCurrentDirectoryW(0, NULL);
|
cwd_len = GetCurrentDirectoryW(0, NULL);
|
||||||
if (!cwd_len) {
|
if (!cwd_len) {
|
||||||
@ -984,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. */
|
/* Get PATH environment variable. */
|
||||||
path = find_path(env);
|
path = find_path(env);
|
||||||
if (path == NULL) {
|
if (path == NULL) {
|
||||||
@ -1012,7 +1008,8 @@ int uv_spawn(uv_loop_t* loop,
|
|||||||
|
|
||||||
application_path = search_path(application,
|
application_path = search_path(application,
|
||||||
cwd,
|
cwd,
|
||||||
path);
|
path,
|
||||||
|
options->flags);
|
||||||
if (application_path == NULL) {
|
if (application_path == NULL) {
|
||||||
/* Not found. */
|
/* Not found. */
|
||||||
err = ERROR_FILE_NOT_FOUND;
|
err = ERROR_FILE_NOT_FOUND;
|
||||||
@ -1210,9 +1207,18 @@ static int uv__kill(HANDLE process_handle, int signum) {
|
|||||||
(PVOID) dump_folder,
|
(PVOID) dump_folder,
|
||||||
&dump_folder_len);
|
&dump_folder_len);
|
||||||
if (ret != ERROR_SUCCESS) {
|
if (ret != ERROR_SUCCESS) {
|
||||||
|
/* Workaround for missing uuid.dll on MinGW. */
|
||||||
|
static const GUID FOLDERID_LocalAppData_libuv = {
|
||||||
|
0xf1b32785, 0x6fba, 0x4fcf,
|
||||||
|
{0x9d, 0x55, 0x7b, 0x8e, 0x7f, 0x15, 0x70, 0x91}
|
||||||
|
};
|
||||||
|
|
||||||
/* Default value for `dump_folder` is `%LOCALAPPDATA%\CrashDumps`. */
|
/* Default value for `dump_folder` is `%LOCALAPPDATA%\CrashDumps`. */
|
||||||
WCHAR* localappdata;
|
WCHAR* localappdata;
|
||||||
SHGetKnownFolderPath(&FOLDERID_LocalAppData, 0, NULL, &localappdata);
|
SHGetKnownFolderPath(&FOLDERID_LocalAppData_libuv,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
&localappdata);
|
||||||
_snwprintf_s(dump_folder,
|
_snwprintf_s(dump_folder,
|
||||||
sizeof(dump_folder),
|
sizeof(dump_folder),
|
||||||
_TRUNCATE,
|
_TRUNCATE,
|
||||||
@ -1292,19 +1298,35 @@ static int uv__kill(HANDLE process_handle, int signum) {
|
|||||||
case SIGINT: {
|
case SIGINT: {
|
||||||
/* Unconditionally terminate the process. On Windows, killed processes
|
/* Unconditionally terminate the process. On Windows, killed processes
|
||||||
* normally return 1. */
|
* normally return 1. */
|
||||||
DWORD status;
|
|
||||||
int err;
|
int err;
|
||||||
|
DWORD status;
|
||||||
|
|
||||||
if (TerminateProcess(process_handle, 1))
|
if (TerminateProcess(process_handle, 1))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* If the process already exited before TerminateProcess was called,.
|
/* If the process already exited before TerminateProcess was called,
|
||||||
* TerminateProcess will fail with ERROR_ACCESS_DENIED. */
|
* TerminateProcess will fail with ERROR_ACCESS_DENIED. */
|
||||||
err = GetLastError();
|
err = GetLastError();
|
||||||
if (err == ERROR_ACCESS_DENIED &&
|
if (err == ERROR_ACCESS_DENIED) {
|
||||||
GetExitCodeProcess(process_handle, &status) &&
|
/* First check using GetExitCodeProcess() with status different from
|
||||||
status != STILL_ACTIVE) {
|
* STILL_ACTIVE (259). This check can be set incorrectly by the process,
|
||||||
return UV_ESRCH;
|
* though that is uncommon. */
|
||||||
|
if (GetExitCodeProcess(process_handle, &status) &&
|
||||||
|
status != STILL_ACTIVE) {
|
||||||
|
return UV_ESRCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* But the process could have exited with code == STILL_ACTIVE, use then
|
||||||
|
* WaitForSingleObject with timeout zero. This is prone to a race
|
||||||
|
* condition as it could return WAIT_TIMEOUT because the handle might
|
||||||
|
* not have been signaled yet.That would result in returning the wrong
|
||||||
|
* error code here (UV_EACCES instead of UV_ESRCH), but we cannot fix
|
||||||
|
* the kernel synchronization issue that TerminateProcess is
|
||||||
|
* inconsistent with WaitForSingleObject with just the APIs available to
|
||||||
|
* us in user space. */
|
||||||
|
if (WaitForSingleObject(process_handle, 0) == WAIT_OBJECT_0) {
|
||||||
|
return UV_ESRCH;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return uv_translate_sys_error(err);
|
return uv_translate_sys_error(err);
|
||||||
@ -1320,7 +1342,16 @@ static int uv__kill(HANDLE process_handle, int signum) {
|
|||||||
if (status != STILL_ACTIVE)
|
if (status != STILL_ACTIVE)
|
||||||
return UV_ESRCH;
|
return UV_ESRCH;
|
||||||
|
|
||||||
return 0;
|
switch (WaitForSingleObject(process_handle, 0)) {
|
||||||
|
case WAIT_OBJECT_0:
|
||||||
|
return UV_ESRCH;
|
||||||
|
case WAIT_FAILED:
|
||||||
|
return uv_translate_sys_error(GetLastError());
|
||||||
|
case WAIT_TIMEOUT:
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
return UV_UNKNOWN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -1355,7 +1386,7 @@ int uv_kill(int pid, int signum) {
|
|||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
process_handle = GetCurrentProcess();
|
process_handle = GetCurrentProcess();
|
||||||
} else {
|
} else {
|
||||||
process_handle = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION,
|
process_handle = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
|
||||||
FALSE,
|
FALSE,
|
||||||
pid);
|
pid);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,16 +53,16 @@
|
|||||||
(uv__ntstatus_to_winsock_error(GET_REQ_STATUS((req))))
|
(uv__ntstatus_to_winsock_error(GET_REQ_STATUS((req))))
|
||||||
|
|
||||||
|
|
||||||
#define REGISTER_HANDLE_REQ(loop, handle, req) \
|
#define REGISTER_HANDLE_REQ(loop, handle) \
|
||||||
do { \
|
do { \
|
||||||
INCREASE_ACTIVE_COUNT((loop), (handle)); \
|
INCREASE_ACTIVE_COUNT((loop), (handle)); \
|
||||||
uv__req_register((loop), (req)); \
|
uv__req_register((loop)); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
#define UNREGISTER_HANDLE_REQ(loop, handle, req) \
|
#define UNREGISTER_HANDLE_REQ(loop, handle) \
|
||||||
do { \
|
do { \
|
||||||
DECREASE_ACTIVE_COUNT((loop), (handle)); \
|
DECREASE_ACTIVE_COUNT((loop), (handle)); \
|
||||||
uv__req_unregister((loop), (req)); \
|
uv__req_unregister((loop)); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
@ -83,7 +83,7 @@
|
|||||||
|
|
||||||
|
|
||||||
INLINE static uv_req_t* uv__overlapped_to_req(OVERLAPPED* overlapped) {
|
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);
|
for (handle = RB_NFIND(uv_signal_tree_s, &uv__signal_tree, &lookup);
|
||||||
handle != NULL && handle->signum == 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)) {
|
||||||
unsigned long previous = InterlockedExchange(
|
unsigned long previous = InterlockedExchange(
|
||||||
(volatile LONG*) &handle->pending_signum, signum);
|
(volatile LONG*) &handle->pending_signum, signum);
|
||||||
|
|
||||||
|
|||||||
@ -131,7 +131,7 @@ int uv_write(uv_write_t* req,
|
|||||||
case UV_NAMED_PIPE:
|
case UV_NAMED_PIPE:
|
||||||
err = uv__pipe_write(
|
err = uv__pipe_write(
|
||||||
loop, req, (uv_pipe_t*) handle, bufs, nbufs, NULL, cb);
|
loop, req, (uv_pipe_t*) handle, bufs, nbufs, NULL, cb);
|
||||||
break;
|
return uv_translate_write_sys_error(err);
|
||||||
case UV_TTY:
|
case UV_TTY:
|
||||||
err = uv__tty_write(loop, req, (uv_tty_t*) handle, bufs, nbufs, cb);
|
err = uv__tty_write(loop, req, (uv_tty_t*) handle, bufs, nbufs, cb);
|
||||||
break;
|
break;
|
||||||
@ -164,7 +164,7 @@ int uv_write2(uv_write_t* req,
|
|||||||
|
|
||||||
err = uv__pipe_write(
|
err = uv__pipe_write(
|
||||||
loop, req, (uv_pipe_t*) handle, bufs, nbufs, send_handle, cb);
|
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->flags &= ~UV_HANDLE_WRITABLE;
|
||||||
handle->stream.conn.shutdown_req = req;
|
handle->stream.conn.shutdown_req = req;
|
||||||
handle->reqs_pending++;
|
handle->reqs_pending++;
|
||||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
REGISTER_HANDLE_REQ(loop, handle);
|
||||||
|
|
||||||
if (handle->stream.conn.write_reqs_pending == 0) {
|
if (handle->stream.conn.write_reqs_pending == 0) {
|
||||||
if (handle->type == UV_NAMED_PIPE)
|
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();
|
return WSAGetLastError();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enable && setsockopt(socket,
|
if (!enable)
|
||||||
IPPROTO_TCP,
|
return 0;
|
||||||
TCP_KEEPALIVE,
|
|
||||||
(const char*)&delay,
|
if (delay < 1)
|
||||||
sizeof delay) == -1) {
|
return UV_EINVAL;
|
||||||
|
|
||||||
|
if (setsockopt(socket,
|
||||||
|
IPPROTO_TCP,
|
||||||
|
TCP_KEEPALIVE,
|
||||||
|
(const char*)&delay,
|
||||||
|
sizeof delay) == -1) {
|
||||||
return WSAGetLastError();
|
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);
|
assert(stream->flags & UV_HANDLE_CONNECTION);
|
||||||
|
|
||||||
stream->stream.conn.shutdown_req = NULL;
|
stream->stream.conn.shutdown_req = NULL;
|
||||||
UNREGISTER_HANDLE_REQ(loop, stream, req);
|
UNREGISTER_HANDLE_REQ(loop, stream);
|
||||||
|
|
||||||
err = 0;
|
err = 0;
|
||||||
if (stream->flags & UV_HANDLE_CLOSING)
|
if (stream->flags & UV_HANDLE_CLOSING)
|
||||||
@ -286,6 +292,12 @@ static int uv__tcp_try_bind(uv_tcp_t* handle,
|
|||||||
DWORD err;
|
DWORD err;
|
||||||
int r;
|
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) {
|
if (handle->socket == INVALID_SOCKET) {
|
||||||
SOCKET sock;
|
SOCKET sock;
|
||||||
|
|
||||||
@ -822,7 +834,7 @@ out:
|
|||||||
if (handle->delayed_error != 0) {
|
if (handle->delayed_error != 0) {
|
||||||
/* Process the req without IOCP. */
|
/* Process the req without IOCP. */
|
||||||
handle->reqs_pending++;
|
handle->reqs_pending++;
|
||||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
REGISTER_HANDLE_REQ(loop, handle);
|
||||||
uv__insert_pending_req(loop, (uv_req_t*)req);
|
uv__insert_pending_req(loop, (uv_req_t*)req);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -838,12 +850,12 @@ out:
|
|||||||
if (UV_SUCCEEDED_WITHOUT_IOCP(success)) {
|
if (UV_SUCCEEDED_WITHOUT_IOCP(success)) {
|
||||||
/* Process the req without IOCP. */
|
/* Process the req without IOCP. */
|
||||||
handle->reqs_pending++;
|
handle->reqs_pending++;
|
||||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
REGISTER_HANDLE_REQ(loop, handle);
|
||||||
uv__insert_pending_req(loop, (uv_req_t*)req);
|
uv__insert_pending_req(loop, (uv_req_t*)req);
|
||||||
} else if (UV_SUCCEEDED_WITH_IOCP(success)) {
|
} else if (UV_SUCCEEDED_WITH_IOCP(success)) {
|
||||||
/* The req will be processed with IOCP. */
|
/* The req will be processed with IOCP. */
|
||||||
handle->reqs_pending++;
|
handle->reqs_pending++;
|
||||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
REGISTER_HANDLE_REQ(loop, handle);
|
||||||
} else {
|
} else {
|
||||||
return WSAGetLastError();
|
return WSAGetLastError();
|
||||||
}
|
}
|
||||||
@ -913,14 +925,14 @@ int uv__tcp_write(uv_loop_t* loop,
|
|||||||
req->u.io.queued_bytes = 0;
|
req->u.io.queued_bytes = 0;
|
||||||
handle->reqs_pending++;
|
handle->reqs_pending++;
|
||||||
handle->stream.conn.write_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);
|
uv__insert_pending_req(loop, (uv_req_t*) req);
|
||||||
} else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
|
} else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
|
||||||
/* Request queued by the kernel. */
|
/* Request queued by the kernel. */
|
||||||
req->u.io.queued_bytes = uv__count_bufs(bufs, nbufs);
|
req->u.io.queued_bytes = uv__count_bufs(bufs, nbufs);
|
||||||
handle->reqs_pending++;
|
handle->reqs_pending++;
|
||||||
handle->stream.conn.write_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;
|
handle->write_queue_size += req->u.io.queued_bytes;
|
||||||
if (handle->flags & UV_HANDLE_EMULATE_IOCP &&
|
if (handle->flags & UV_HANDLE_EMULATE_IOCP &&
|
||||||
!RegisterWaitForSingleObject(&req->wait_handle,
|
!RegisterWaitForSingleObject(&req->wait_handle,
|
||||||
@ -934,7 +946,7 @@ int uv__tcp_write(uv_loop_t* loop,
|
|||||||
req->u.io.queued_bytes = 0;
|
req->u.io.queued_bytes = 0;
|
||||||
handle->reqs_pending++;
|
handle->reqs_pending++;
|
||||||
handle->stream.conn.write_reqs_pending++;
|
handle->stream.conn.write_reqs_pending++;
|
||||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
REGISTER_HANDLE_REQ(loop, handle);
|
||||||
SET_REQ_ERROR(req, WSAGetLastError());
|
SET_REQ_ERROR(req, WSAGetLastError());
|
||||||
uv__insert_pending_req(loop, (uv_req_t*) req);
|
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);
|
assert(handle->write_queue_size >= req->u.io.queued_bytes);
|
||||||
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 (handle->flags & UV_HANDLE_EMULATE_IOCP) {
|
||||||
if (req->wait_handle != INVALID_HANDLE_VALUE) {
|
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);
|
assert(handle->type == UV_TCP);
|
||||||
|
|
||||||
UNREGISTER_HANDLE_REQ(loop, handle, req);
|
UNREGISTER_HANDLE_REQ(loop, handle);
|
||||||
|
|
||||||
err = 0;
|
err = 0;
|
||||||
if (handle->delayed_error) {
|
if (handle->delayed_error) {
|
||||||
@ -1551,11 +1563,6 @@ int uv__tcp_connect(uv_connect_t* req,
|
|||||||
return 0;
|
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) {
|
int uv_socketpair(int type, int protocol, uv_os_sock_t fds[2], int flags0, int flags1) {
|
||||||
SOCKET server = INVALID_SOCKET;
|
SOCKET server = INVALID_SOCKET;
|
||||||
|
|||||||
142
src/win/thread.c
142
src/win/thread.c
@ -32,45 +32,23 @@
|
|||||||
#include "uv.h"
|
#include "uv.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
static void uv__once_inner(uv_once_t* guard, void (*callback)(void)) {
|
typedef void (*uv__once_cb)(void);
|
||||||
DWORD result;
|
|
||||||
HANDLE existing_event, created_event;
|
|
||||||
|
|
||||||
created_event = CreateEvent(NULL, 1, 0, NULL);
|
typedef struct {
|
||||||
if (created_event == 0) {
|
uv__once_cb callback;
|
||||||
/* Could fail in a low-memory situation? */
|
} uv__once_data_t;
|
||||||
uv_fatal_error(GetLastError(), "CreateEvent");
|
|
||||||
}
|
|
||||||
|
|
||||||
existing_event = InterlockedCompareExchangePointer(&guard->event,
|
static BOOL WINAPI uv__once_inner(INIT_ONCE *once, void* param, void** context) {
|
||||||
created_event,
|
uv__once_data_t* data = param;
|
||||||
NULL);
|
|
||||||
|
|
||||||
if (existing_event == NULL) {
|
data->callback();
|
||||||
/* We won the race */
|
|
||||||
callback();
|
|
||||||
|
|
||||||
result = SetEvent(created_event);
|
return TRUE;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void uv_once(uv_once_t* guard, uv__once_cb callback) {
|
||||||
void uv_once(uv_once_t* guard, void (*callback)(void)) {
|
uv__once_data_t data = { .callback = callback };
|
||||||
/* Fast case - avoid WaitForSingleObject. */
|
InitOnceExecuteOnce(&guard->init_once, uv__once_inner, (void*) &data, NULL);
|
||||||
if (guard->ran) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uv__once_inner(guard, callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -79,6 +57,9 @@ STATIC_ASSERT(sizeof(uv_thread_t) <= sizeof(void*));
|
|||||||
|
|
||||||
static uv_key_t uv__current_thread_key;
|
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__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) {
|
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);
|
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,
|
int uv_thread_create_ex(uv_thread_t* tid,
|
||||||
const uv_thread_options_t* params,
|
const uv_thread_options_t* params,
|
||||||
void (*entry)(void *arg),
|
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) {
|
int uv_mutex_init(uv_mutex_t* mutex) {
|
||||||
InitializeCriticalSection(mutex);
|
InitializeCriticalSection(mutex);
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -58,6 +58,9 @@
|
|||||||
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||||
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
|
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
|
||||||
#endif
|
#endif
|
||||||
|
#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
|
||||||
|
#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
|
||||||
|
#endif
|
||||||
|
|
||||||
#define CURSOR_SIZE_SMALL 25
|
#define CURSOR_SIZE_SMALL 25
|
||||||
#define CURSOR_SIZE_LARGE 100
|
#define CURSOR_SIZE_LARGE 100
|
||||||
@ -119,7 +122,10 @@ static int uv_tty_virtual_width = -1;
|
|||||||
* handle signalling SIGWINCH
|
* 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_height = -1;
|
||||||
static int uv__tty_console_width = -1;
|
static int uv__tty_console_width = -1;
|
||||||
static HANDLE uv__tty_console_resized = INVALID_HANDLE_VALUE;
|
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);
|
static void uv__determine_vterm_state(HANDLE handle);
|
||||||
|
|
||||||
void uv__console_init(void) {
|
void uv__console_init(void) {
|
||||||
|
DWORD dwMode;
|
||||||
|
|
||||||
if (uv_sem_init(&uv_tty_output_lock, 1))
|
if (uv_sem_init(&uv_tty_output_lock, 1))
|
||||||
abort();
|
abort();
|
||||||
uv__tty_console_handle = CreateFileW(L"CONOUT$",
|
uv__tty_console_handle_out = CreateFileW(L"CONOUT$",
|
||||||
GENERIC_READ | GENERIC_WRITE,
|
GENERIC_READ | GENERIC_WRITE,
|
||||||
FILE_SHARE_WRITE,
|
FILE_SHARE_WRITE,
|
||||||
0,
|
0,
|
||||||
OPEN_EXISTING,
|
OPEN_EXISTING,
|
||||||
0,
|
0,
|
||||||
0);
|
0);
|
||||||
if (uv__tty_console_handle != INVALID_HANDLE_VALUE) {
|
if (uv__tty_console_handle_out != INVALID_HANDLE_VALUE) {
|
||||||
CONSOLE_SCREEN_BUFFER_INFO sb_info;
|
CONSOLE_SCREEN_BUFFER_INFO sb_info;
|
||||||
uv_mutex_init(&uv__tty_console_resize_mutex);
|
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_width = sb_info.dwSize.X;
|
||||||
uv__tty_console_height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1;
|
uv__tty_console_height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1;
|
||||||
}
|
}
|
||||||
@ -179,6 +187,18 @@ void uv__console_init(void) {
|
|||||||
NULL,
|
NULL,
|
||||||
WT_EXECUTELONGFUNCTION);
|
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. */
|
/* Initialize TTY input specific fields. */
|
||||||
tty->flags |= UV_HANDLE_TTY_READABLE | UV_HANDLE_READABLE;
|
tty->flags |= UV_HANDLE_TTY_READABLE | UV_HANDLE_READABLE;
|
||||||
/* TODO: remove me in v2.x. */
|
/* 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_line_buffer = uv_null_buf_;
|
||||||
tty->tty.rd.read_raw_wait = NULL;
|
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) {
|
int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
|
||||||
DWORD flags;
|
DWORD flags;
|
||||||
|
DWORD try_set_flags;
|
||||||
unsigned char was_reading;
|
unsigned char was_reading;
|
||||||
uv_alloc_cb alloc_cb;
|
uv_alloc_cb alloc_cb;
|
||||||
uv_read_cb read_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;
|
return UV_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!!mode == !!(tty->flags & UV_HANDLE_TTY_RAW)) {
|
if ((int)mode == tty->tty.rd.mode.mode) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try_set_flags = 0;
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case UV_TTY_MODE_NORMAL:
|
case UV_TTY_MODE_NORMAL:
|
||||||
flags = ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
|
flags = ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
|
||||||
break;
|
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:
|
case UV_TTY_MODE_RAW:
|
||||||
flags = ENABLE_WINDOW_INPUT;
|
flags = ENABLE_WINDOW_INPUT;
|
||||||
break;
|
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);
|
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());
|
err = uv_translate_sys_error(GetLastError());
|
||||||
uv_sem_post(&uv_tty_output_lock);
|
uv_sem_post(&uv_tty_output_lock);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
uv_sem_post(&uv_tty_output_lock);
|
uv_sem_post(&uv_tty_output_lock);
|
||||||
|
|
||||||
/* Update flag. */
|
/* Update mode. */
|
||||||
tty->flags &= ~UV_HANDLE_TTY_RAW;
|
tty->tty.rd.mode.mode = mode;
|
||||||
tty->flags |= mode ? UV_HANDLE_TTY_RAW : 0;
|
|
||||||
|
|
||||||
/* If we just stopped reading, restart. */
|
/* If we just stopped reading, restart. */
|
||||||
if (was_reading) {
|
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) {
|
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);
|
uv__tty_queue_read_raw(loop, handle);
|
||||||
} else {
|
} else {
|
||||||
uv__tty_queue_read_line(loop, handle);
|
uv__tty_queue_read_line(loop, handle);
|
||||||
@ -695,14 +723,14 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle,
|
|||||||
|
|
||||||
DWORD records_left, records_read;
|
DWORD records_left, records_read;
|
||||||
uv_buf_t buf;
|
uv_buf_t buf;
|
||||||
off_t buf_used;
|
_off_t buf_used;
|
||||||
|
|
||||||
assert(handle->type == UV_TTY);
|
assert(handle->type == UV_TTY);
|
||||||
assert(handle->flags & UV_HANDLE_TTY_READABLE);
|
assert(handle->flags & UV_HANDLE_TTY_READABLE);
|
||||||
handle->flags &= ~UV_HANDLE_READ_PENDING;
|
handle->flags &= ~UV_HANDLE_READ_PENDING;
|
||||||
|
|
||||||
if (!(handle->flags & UV_HANDLE_READING) ||
|
if (!(handle->flags & UV_HANDLE_READING) ||
|
||||||
!(handle->flags & UV_HANDLE_TTY_RAW)) {
|
!(uv__is_raw_tty_mode(handle->tty.rd.mode.mode))) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1050,7 +1078,7 @@ int uv__tty_read_stop(uv_tty_t* handle) {
|
|||||||
if (!(handle->flags & UV_HANDLE_READ_PENDING))
|
if (!(handle->flags & UV_HANDLE_READ_PENDING))
|
||||||
return 0;
|
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
|
/* Cancel raw read. Write some bullshit event to force the console wait to
|
||||||
* return. */
|
* return. */
|
||||||
memset(&record, 0, sizeof record);
|
memset(&record, 0, sizeof record);
|
||||||
@ -2183,7 +2211,7 @@ int uv__tty_write(uv_loop_t* loop,
|
|||||||
|
|
||||||
handle->reqs_pending++;
|
handle->reqs_pending++;
|
||||||
handle->stream.conn.write_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;
|
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;
|
int err;
|
||||||
|
|
||||||
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 (req->cb) {
|
if (req->cb) {
|
||||||
err = GET_REQ_ERROR(req);
|
err = GET_REQ_ERROR(req);
|
||||||
@ -2246,7 +2274,7 @@ void uv__tty_close(uv_tty_t* handle) {
|
|||||||
if (handle->u.fd == -1)
|
if (handle->u.fd == -1)
|
||||||
CloseHandle(handle->handle);
|
CloseHandle(handle->handle);
|
||||||
else
|
else
|
||||||
close(handle->u.fd);
|
_close(handle->u.fd);
|
||||||
|
|
||||||
handle->u.fd = -1;
|
handle->u.fd = -1;
|
||||||
handle->handle = INVALID_HANDLE_VALUE;
|
handle->handle = INVALID_HANDLE_VALUE;
|
||||||
@ -2263,7 +2291,7 @@ void uv__process_tty_shutdown_req(uv_loop_t* loop, uv_tty_t* stream, uv_shutdown
|
|||||||
assert(req);
|
assert(req);
|
||||||
|
|
||||||
stream->stream.conn.shutdown_req = NULL;
|
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 */
|
/* TTY shutdown is really just a no-op */
|
||||||
if (req->cb) {
|
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) {
|
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;
|
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 */
|
/* Make sure to not overwhelm the system with resize events */
|
||||||
Sleep(33);
|
Sleep(33);
|
||||||
WaitForSingleObject(uv__tty_console_resized, INFINITE);
|
WaitForSingleObject(uv__tty_console_resized, INFINITE);
|
||||||
uv__tty_console_signal_resize();
|
|
||||||
ResetEvent(uv__tty_console_resized);
|
ResetEvent(uv__tty_console_resized);
|
||||||
|
uv__tty_console_signal_resize();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -2390,7 +2428,7 @@ static void uv__tty_console_signal_resize(void) {
|
|||||||
CONSOLE_SCREEN_BUFFER_INFO sb_info;
|
CONSOLE_SCREEN_BUFFER_INFO sb_info;
|
||||||
int width, height;
|
int width, height;
|
||||||
|
|
||||||
if (!GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info))
|
if (!GetConsoleScreenBufferInfo(uv__tty_console_handle_out, &sb_info))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
width = sb_info.dwSize.X;
|
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)
|
if (handle->flags & UV_HANDLE_BOUND)
|
||||||
return 0;
|
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) {
|
if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6) {
|
||||||
/* UV_UDP_IPV6ONLY is supported only for IPV6 sockets */
|
/* UV_UDP_IPV6ONLY is supported only for IPV6 sockets */
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
@ -376,7 +382,7 @@ static int uv__send(uv_udp_send_t* req,
|
|||||||
handle->reqs_pending++;
|
handle->reqs_pending++;
|
||||||
handle->send_queue_size += req->u.io.queued_bytes;
|
handle->send_queue_size += req->u.io.queued_bytes;
|
||||||
handle->send_queue_count++;
|
handle->send_queue_count++;
|
||||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
REGISTER_HANDLE_REQ(loop, handle);
|
||||||
uv__insert_pending_req(loop, (uv_req_t*)req);
|
uv__insert_pending_req(loop, (uv_req_t*)req);
|
||||||
} else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
|
} else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
|
||||||
/* Request queued by the kernel. */
|
/* Request queued by the kernel. */
|
||||||
@ -384,7 +390,7 @@ static int uv__send(uv_udp_send_t* req,
|
|||||||
handle->reqs_pending++;
|
handle->reqs_pending++;
|
||||||
handle->send_queue_size += req->u.io.queued_bytes;
|
handle->send_queue_size += req->u.io.queued_bytes;
|
||||||
handle->send_queue_count++;
|
handle->send_queue_count++;
|
||||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
REGISTER_HANDLE_REQ(loop, handle);
|
||||||
} else {
|
} else {
|
||||||
/* Send failed due to an error. */
|
/* Send failed due to an error. */
|
||||||
return WSAGetLastError();
|
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_size -= req->u.io.queued_bytes;
|
||||||
handle->send_queue_count--;
|
handle->send_queue_count--;
|
||||||
|
|
||||||
UNREGISTER_HANDLE_REQ(loop, handle, req);
|
UNREGISTER_HANDLE_REQ(loop, handle);
|
||||||
|
|
||||||
if (req->cb) {
|
if (req->cb) {
|
||||||
err = 0;
|
err = 0;
|
||||||
@ -1095,7 +1101,8 @@ int uv__udp_try_send(uv_udp_t* handle,
|
|||||||
struct sockaddr_storage converted;
|
struct sockaddr_storage converted;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
assert(nbufs > 0);
|
if (nbufs < 1)
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
if (addr != NULL) {
|
if (addr != NULL) {
|
||||||
err = uv__convert_to_localhost_if_unspecified(addr, &converted);
|
err = uv__convert_to_localhost_if_unspecified(addr, &converted);
|
||||||
@ -1135,3 +1142,21 @@ int uv__udp_try_send(uv_udp_t* handle,
|
|||||||
|
|
||||||
return bytes;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
225
src/win/util.c
225
src/win/util.c
@ -191,7 +191,7 @@ int uv_cwd(char* buffer, size_t* size) {
|
|||||||
WCHAR *utf16_buffer;
|
WCHAR *utf16_buffer;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (buffer == NULL || size == NULL) {
|
if (buffer == NULL || size == NULL || *size == 0) {
|
||||||
return UV_EINVAL;
|
return UV_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,25 +316,19 @@ uv_pid_t uv_os_getpid(void) {
|
|||||||
|
|
||||||
|
|
||||||
uv_pid_t uv_os_getppid(void) {
|
uv_pid_t uv_os_getppid(void) {
|
||||||
int parent_pid = -1;
|
NTSTATUS nt_status;
|
||||||
HANDLE handle;
|
PROCESS_BASIC_INFORMATION basic_info;
|
||||||
PROCESSENTRY32 pe;
|
|
||||||
DWORD current_pid = GetCurrentProcessId();
|
|
||||||
|
|
||||||
pe.dwSize = sizeof(PROCESSENTRY32);
|
nt_status = pNtQueryInformationProcess(GetCurrentProcess(),
|
||||||
handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
ProcessBasicInformation,
|
||||||
|
&basic_info,
|
||||||
if (Process32First(handle, &pe)) {
|
sizeof(basic_info),
|
||||||
do {
|
NULL);
|
||||||
if (pe.th32ProcessID == current_pid) {
|
if (NT_SUCCESS(nt_status)) {
|
||||||
parent_pid = pe.th32ParentProcessID;
|
return basic_info.InheritedFromUniqueProcessId;
|
||||||
break;
|
} else {
|
||||||
}
|
return -1;
|
||||||
} while( Process32Next(handle, &pe));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CloseHandle(handle);
|
|
||||||
return parent_pid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -512,19 +506,23 @@ int uv_uptime(double* uptime) {
|
|||||||
|
|
||||||
|
|
||||||
unsigned int uv_available_parallelism(void) {
|
unsigned int uv_available_parallelism(void) {
|
||||||
SYSTEM_INFO info;
|
DWORD_PTR procmask;
|
||||||
unsigned rc;
|
DWORD_PTR sysmask;
|
||||||
|
int count;
|
||||||
|
int i;
|
||||||
|
|
||||||
/* TODO(bnoordhuis) Use GetLogicalProcessorInformationEx() to support systems
|
/* TODO(bnoordhuis) Use GetLogicalProcessorInformationEx() to support systems
|
||||||
* with > 64 CPUs? See https://github.com/libuv/libuv/pull/3458
|
* 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 (count > 0)
|
||||||
if (rc < 1)
|
return count;
|
||||||
rc = 1;
|
|
||||||
|
|
||||||
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) {
|
int uv_getrusage(uv_rusage_t *uv_rusage) {
|
||||||
FILETIME createTime, exitTime, kernelTime, userTime;
|
FILETIME create_time, exit_time, kernel_time, user_time;
|
||||||
SYSTEMTIME kernelSystemTime, userSystemTime;
|
SYSTEMTIME kernel_system_time, user_system_time;
|
||||||
PROCESS_MEMORY_COUNTERS memCounters;
|
PROCESS_MEMORY_COUNTERS mem_counters;
|
||||||
IO_COUNTERS ioCounters;
|
IO_COUNTERS io_counters;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime);
|
ret = GetProcessTimes(GetCurrentProcess(),
|
||||||
|
&create_time,
|
||||||
|
&exit_time,
|
||||||
|
&kernel_time,
|
||||||
|
&user_time);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
return uv_translate_sys_error(GetLastError());
|
return uv_translate_sys_error(GetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = FileTimeToSystemTime(&kernelTime, &kernelSystemTime);
|
ret = FileTimeToSystemTime(&kernel_time, &kernel_system_time);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
return uv_translate_sys_error(GetLastError());
|
return uv_translate_sys_error(GetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = FileTimeToSystemTime(&userTime, &userSystemTime);
|
ret = FileTimeToSystemTime(&user_time, &user_system_time);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
return uv_translate_sys_error(GetLastError());
|
return uv_translate_sys_error(GetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = GetProcessMemoryInfo(GetCurrentProcess(),
|
ret = GetProcessMemoryInfo(GetCurrentProcess(),
|
||||||
&memCounters,
|
&mem_counters,
|
||||||
sizeof(memCounters));
|
sizeof(mem_counters));
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
return uv_translate_sys_error(GetLastError());
|
return uv_translate_sys_error(GetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = GetProcessIoCounters(GetCurrentProcess(), &ioCounters);
|
ret = GetProcessIoCounters(GetCurrentProcess(), &io_counters);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
return uv_translate_sys_error(GetLastError());
|
return uv_translate_sys_error(GetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(uv_rusage, 0, sizeof(*uv_rusage));
|
memset(uv_rusage, 0, sizeof(*uv_rusage));
|
||||||
|
|
||||||
uv_rusage->ru_utime.tv_sec = userSystemTime.wHour * 3600 +
|
uv_rusage->ru_utime.tv_sec = user_system_time.wHour * 3600 +
|
||||||
userSystemTime.wMinute * 60 +
|
user_system_time.wMinute * 60 +
|
||||||
userSystemTime.wSecond;
|
user_system_time.wSecond;
|
||||||
uv_rusage->ru_utime.tv_usec = userSystemTime.wMilliseconds * 1000;
|
uv_rusage->ru_utime.tv_usec = user_system_time.wMilliseconds * 1000;
|
||||||
|
|
||||||
uv_rusage->ru_stime.tv_sec = kernelSystemTime.wHour * 3600 +
|
uv_rusage->ru_stime.tv_sec = kernel_system_time.wHour * 3600 +
|
||||||
kernelSystemTime.wMinute * 60 +
|
kernel_system_time.wMinute * 60 +
|
||||||
kernelSystemTime.wSecond;
|
kernel_system_time.wSecond;
|
||||||
uv_rusage->ru_stime.tv_usec = kernelSystemTime.wMilliseconds * 1000;
|
uv_rusage->ru_stime.tv_usec = kernel_system_time.wMilliseconds * 1000;
|
||||||
|
|
||||||
uv_rusage->ru_majflt = (uint64_t) memCounters.PageFaultCount;
|
uv_rusage->ru_majflt = (uint64_t) mem_counters.PageFaultCount;
|
||||||
uv_rusage->ru_maxrss = (uint64_t) memCounters.PeakWorkingSetSize / 1024;
|
uv_rusage->ru_maxrss = (uint64_t) mem_counters.PeakWorkingSetSize / 1024;
|
||||||
|
|
||||||
uv_rusage->ru_oublock = (uint64_t) ioCounters.WriteOperationCount;
|
uv_rusage->ru_oublock = (uint64_t) io_counters.WriteOperationCount;
|
||||||
uv_rusage->ru_inblock = (uint64_t) ioCounters.ReadOperationCount;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -942,8 +984,13 @@ int uv_os_homedir(char* buffer, size_t* size) {
|
|||||||
r = uv_os_getenv("USERPROFILE", buffer, size);
|
r = uv_os_getenv("USERPROFILE", buffer, size);
|
||||||
|
|
||||||
/* Don't return an error if USERPROFILE was not found. */
|
/* 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;
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
/* USERPROFILE is not set, so call uv_os_get_passwd() */
|
/* USERPROFILE is not set, so call uv_os_get_passwd() */
|
||||||
r = uv_os_get_passwd(&pwd);
|
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 uv_os_tmpdir(char* buffer, size_t* size) {
|
||||||
|
int r;
|
||||||
wchar_t *path;
|
wchar_t *path;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
@ -980,6 +1028,12 @@ int uv_os_tmpdir(char* buffer, size_t* size) {
|
|||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
return uv_translate_sys_error(GetLastError());
|
return uv_translate_sys_error(GetLastError());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* tmp path is empty or invalid */
|
||||||
|
if (len < 3) {
|
||||||
|
return UV_ENOENT;
|
||||||
|
}
|
||||||
|
|
||||||
/* Include space for terminating null char. */
|
/* Include space for terminating null char. */
|
||||||
len += 1;
|
len += 1;
|
||||||
path = uv__malloc(len * sizeof(wchar_t));
|
path = uv__malloc(len * sizeof(wchar_t));
|
||||||
@ -1001,7 +1055,9 @@ int uv_os_tmpdir(char* buffer, size_t* size) {
|
|||||||
path[len] = L'\0';
|
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);
|
SetLastError(ERROR_SUCCESS);
|
||||||
len = GetEnvironmentVariableW(name_w, var, varlen);
|
len = GetEnvironmentVariableW(name_w, var, varlen);
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
r = uv_translate_sys_error(GetLastError());
|
||||||
|
|
||||||
if (len < varlen)
|
if (len < varlen)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -1280,15 +1339,8 @@ int uv_os_getenv(const char* name, char* buffer, size_t* size) {
|
|||||||
uv__free(name_w);
|
uv__free(name_w);
|
||||||
name_w = NULL;
|
name_w = NULL;
|
||||||
|
|
||||||
if (len == 0) {
|
if (r == 0)
|
||||||
r = GetLastError();
|
r = uv__copy_utf16_to_utf8(var, len, buffer, size);
|
||||||
if (r != ERROR_SUCCESS) {
|
|
||||||
r = uv_translate_sys_error(r);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r = uv__copy_utf16_to_utf8(var, len, buffer, size);
|
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
|
||||||
@ -1466,6 +1518,48 @@ int uv_os_setpriority(uv_pid_t pid, int priority) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int uv_thread_getpriority(uv_thread_t tid, int* priority) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (priority == NULL)
|
||||||
|
return UV_EINVAL;
|
||||||
|
|
||||||
|
r = GetThreadPriority(tid);
|
||||||
|
if (r == THREAD_PRIORITY_ERROR_RETURN)
|
||||||
|
return uv_translate_sys_error(GetLastError());
|
||||||
|
|
||||||
|
*priority = r;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int uv_thread_setpriority(uv_thread_t tid, int priority) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
switch (priority) {
|
||||||
|
case UV_THREAD_PRIORITY_HIGHEST:
|
||||||
|
r = SetThreadPriority(tid, THREAD_PRIORITY_HIGHEST);
|
||||||
|
break;
|
||||||
|
case UV_THREAD_PRIORITY_ABOVE_NORMAL:
|
||||||
|
r = SetThreadPriority(tid, THREAD_PRIORITY_ABOVE_NORMAL);
|
||||||
|
break;
|
||||||
|
case UV_THREAD_PRIORITY_NORMAL:
|
||||||
|
r = SetThreadPriority(tid, THREAD_PRIORITY_NORMAL);
|
||||||
|
break;
|
||||||
|
case UV_THREAD_PRIORITY_BELOW_NORMAL:
|
||||||
|
r = SetThreadPriority(tid, THREAD_PRIORITY_BELOW_NORMAL);
|
||||||
|
break;
|
||||||
|
case UV_THREAD_PRIORITY_LOWEST:
|
||||||
|
r = SetThreadPriority(tid, THREAD_PRIORITY_LOWEST);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r == 0)
|
||||||
|
return uv_translate_sys_error(GetLastError());
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int uv_os_uname(uv_utsname_t* buffer) {
|
int uv_os_uname(uv_utsname_t* buffer) {
|
||||||
/* Implementation loosely based on
|
/* Implementation loosely based on
|
||||||
@ -1486,20 +1580,7 @@ int uv_os_uname(uv_utsname_t* buffer) {
|
|||||||
os_info.dwOSVersionInfoSize = sizeof(os_info);
|
os_info.dwOSVersionInfoSize = sizeof(os_info);
|
||||||
os_info.szCSDVersion[0] = L'\0';
|
os_info.szCSDVersion[0] = L'\0';
|
||||||
|
|
||||||
/* Try calling RtlGetVersion(), and fall back to the deprecated GetVersionEx()
|
pRtlGetVersion(&os_info);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Populate the version field. */
|
/* Populate the version field. */
|
||||||
version_size = 0;
|
version_size = 0;
|
||||||
|
|||||||
@ -36,9 +36,6 @@ sNtQueryDirectoryFile pNtQueryDirectoryFile;
|
|||||||
sNtQuerySystemInformation pNtQuerySystemInformation;
|
sNtQuerySystemInformation pNtQuerySystemInformation;
|
||||||
sNtQueryInformationProcess pNtQueryInformationProcess;
|
sNtQueryInformationProcess pNtQueryInformationProcess;
|
||||||
|
|
||||||
/* Kernel32 function pointers */
|
|
||||||
sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
|
|
||||||
|
|
||||||
/* Powrprof.dll function pointer */
|
/* Powrprof.dll function pointer */
|
||||||
sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification;
|
sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification;
|
||||||
|
|
||||||
@ -48,12 +45,15 @@ sSetWinEventHook pSetWinEventHook;
|
|||||||
/* ws2_32.dll function pointer */
|
/* ws2_32.dll function pointer */
|
||||||
uv_sGetHostNameW pGetHostNameW;
|
uv_sGetHostNameW pGetHostNameW;
|
||||||
|
|
||||||
|
/* api-ms-win-core-file-l2-1-4.dll function pointer */
|
||||||
|
sGetFileInformationByName pGetFileInformationByName;
|
||||||
|
|
||||||
void uv__winapi_init(void) {
|
void uv__winapi_init(void) {
|
||||||
HMODULE ntdll_module;
|
HMODULE ntdll_module;
|
||||||
HMODULE powrprof_module;
|
HMODULE powrprof_module;
|
||||||
HMODULE user32_module;
|
HMODULE user32_module;
|
||||||
HMODULE kernel32_module;
|
|
||||||
HMODULE ws2_32_module;
|
HMODULE ws2_32_module;
|
||||||
|
HMODULE api_win_core_file_module;
|
||||||
|
|
||||||
ntdll_module = GetModuleHandleA("ntdll.dll");
|
ntdll_module = GetModuleHandleA("ntdll.dll");
|
||||||
if (ntdll_module == NULL) {
|
if (ntdll_module == NULL) {
|
||||||
@ -99,7 +99,7 @@ void uv__winapi_init(void) {
|
|||||||
|
|
||||||
pNtQueryDirectoryFile = (sNtQueryDirectoryFile)
|
pNtQueryDirectoryFile = (sNtQueryDirectoryFile)
|
||||||
GetProcAddress(ntdll_module, "NtQueryDirectoryFile");
|
GetProcAddress(ntdll_module, "NtQueryDirectoryFile");
|
||||||
if (pNtQueryVolumeInformationFile == NULL) {
|
if (pNtQueryDirectoryFile == NULL) {
|
||||||
uv_fatal_error(GetLastError(), "GetProcAddress");
|
uv_fatal_error(GetLastError(), "GetProcAddress");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,15 +117,6 @@ void uv__winapi_init(void) {
|
|||||||
uv_fatal_error(GetLastError(), "GetProcAddress");
|
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);
|
powrprof_module = LoadLibraryExA("powrprof.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||||
if (powrprof_module != NULL) {
|
if (powrprof_module != NULL) {
|
||||||
pPowerRegisterSuspendResumeNotification = (sPowerRegisterSuspendResumeNotification)
|
pPowerRegisterSuspendResumeNotification = (sPowerRegisterSuspendResumeNotification)
|
||||||
@ -144,4 +135,10 @@ void uv__winapi_init(void) {
|
|||||||
ws2_32_module,
|
ws2_32_module,
|
||||||
"GetHostNameW");
|
"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
|
# define DEVICE_TYPE DWORD
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* MinGW already has a definition for REPARSE_DATA_BUFFER, but mingw-w64 does
|
#ifndef NTDDI_WIN11_ZN
|
||||||
* not.
|
# define NTDDI_WIN11_ZN 0x0A00000E
|
||||||
*/
|
|
||||||
#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;
|
|
||||||
#endif
|
#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 {
|
typedef struct _IO_STATUS_BLOCK {
|
||||||
union {
|
union {
|
||||||
NTSTATUS Status;
|
NTSTATUS Status;
|
||||||
@ -4224,6 +4244,15 @@ typedef enum _FILE_INFORMATION_CLASS {
|
|||||||
FileNumaNodeInformation,
|
FileNumaNodeInformation,
|
||||||
FileStandardLinkInformation,
|
FileStandardLinkInformation,
|
||||||
FileRemoteProtocolInformation,
|
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
|
FileMaximumInformation
|
||||||
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
|
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
|
||||||
|
|
||||||
@ -4258,6 +4287,22 @@ typedef struct _FILE_BOTH_DIR_INFORMATION {
|
|||||||
WCHAR FileName[1];
|
WCHAR FileName[1];
|
||||||
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;
|
} 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 {
|
typedef struct _FILE_BASIC_INFORMATION {
|
||||||
LARGE_INTEGER CreationTime;
|
LARGE_INTEGER CreationTime;
|
||||||
LARGE_INTEGER LastAccessTime;
|
LARGE_INTEGER LastAccessTime;
|
||||||
@ -4323,6 +4368,10 @@ typedef struct _FILE_DISPOSITION_INFORMATION {
|
|||||||
BOOLEAN DeleteFile;
|
BOOLEAN DeleteFile;
|
||||||
} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION;
|
} 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 {
|
typedef struct _FILE_PIPE_LOCAL_INFORMATION {
|
||||||
ULONG NamedPipeType;
|
ULONG NamedPipeType;
|
||||||
ULONG NamedPipeConfiguration;
|
ULONG NamedPipeConfiguration;
|
||||||
@ -4427,6 +4476,14 @@ typedef struct _FILE_FS_SECTOR_SIZE_INFORMATION {
|
|||||||
ULONG ByteOffsetForPartitionAlignment;
|
ULONG ByteOffsetForPartitionAlignment;
|
||||||
} FILE_FS_SECTOR_SIZE_INFORMATION, *PFILE_FS_SECTOR_SIZE_INFORMATION;
|
} 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 {
|
typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION {
|
||||||
LARGE_INTEGER IdleTime;
|
LARGE_INTEGER IdleTime;
|
||||||
LARGE_INTEGER KernelTime;
|
LARGE_INTEGER KernelTime;
|
||||||
@ -4440,6 +4497,10 @@ typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION {
|
|||||||
# define SystemProcessorPerformanceInformation 8
|
# define SystemProcessorPerformanceInformation 8
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef ProcessBasicInformation
|
||||||
|
# define ProcessBasicInformation 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef ProcessConsoleHostProcess
|
#ifndef ProcessConsoleHostProcess
|
||||||
# define ProcessConsoleHostProcess 49
|
# define ProcessConsoleHostProcess 49
|
||||||
#endif
|
#endif
|
||||||
@ -4611,15 +4672,6 @@ typedef NTSTATUS (NTAPI *sNtQueryInformationProcess)
|
|||||||
# define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1
|
# define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1
|
||||||
#endif
|
#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 */
|
/* from wincon.h */
|
||||||
#ifndef ENABLE_INSERT_MODE
|
#ifndef ENABLE_INSERT_MODE
|
||||||
# define ENABLE_INSERT_MODE 0x20
|
# define ENABLE_INSERT_MODE 0x20
|
||||||
@ -4666,14 +4718,6 @@ typedef NTSTATUS (NTAPI *sNtQueryInformationProcess)
|
|||||||
# define ERROR_MUI_FILE_NOT_LOADED 15105
|
# define ERROR_MUI_FILE_NOT_LOADED 15105
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef BOOL (WINAPI *sGetQueuedCompletionStatusEx)
|
|
||||||
(HANDLE CompletionPort,
|
|
||||||
LPOVERLAPPED_ENTRY lpCompletionPortEntries,
|
|
||||||
ULONG ulCount,
|
|
||||||
PULONG ulNumEntriesRemoved,
|
|
||||||
DWORD dwMilliseconds,
|
|
||||||
BOOL fAlertable);
|
|
||||||
|
|
||||||
/* from powerbase.h */
|
/* from powerbase.h */
|
||||||
#ifndef DEVICE_NOTIFY_CALLBACK
|
#ifndef DEVICE_NOTIFY_CALLBACK
|
||||||
# define DEVICE_NOTIFY_CALLBACK 2
|
# define DEVICE_NOTIFY_CALLBACK 2
|
||||||
@ -4739,6 +4783,24 @@ typedef struct _TCP_INITIAL_RTO_PARAMETERS {
|
|||||||
# define SIO_TCP_INITIAL_RTO _WSAIOW(IOC_VENDOR,17)
|
# define SIO_TCP_INITIAL_RTO _WSAIOW(IOC_VENDOR,17)
|
||||||
#endif
|
#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 */
|
/* Ntdll function pointers */
|
||||||
extern sRtlGetVersion pRtlGetVersion;
|
extern sRtlGetVersion pRtlGetVersion;
|
||||||
extern sRtlNtStatusToDosError pRtlNtStatusToDosError;
|
extern sRtlNtStatusToDosError pRtlNtStatusToDosError;
|
||||||
@ -4750,15 +4812,15 @@ extern sNtQueryDirectoryFile pNtQueryDirectoryFile;
|
|||||||
extern sNtQuerySystemInformation pNtQuerySystemInformation;
|
extern sNtQuerySystemInformation pNtQuerySystemInformation;
|
||||||
extern sNtQueryInformationProcess pNtQueryInformationProcess;
|
extern sNtQueryInformationProcess pNtQueryInformationProcess;
|
||||||
|
|
||||||
/* Kernel32 function pointers */
|
|
||||||
extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
|
|
||||||
|
|
||||||
/* Powrprof.dll function pointer */
|
/* Powrprof.dll function pointer */
|
||||||
extern sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification;
|
extern sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification;
|
||||||
|
|
||||||
/* User32.dll function pointer */
|
/* User32.dll function pointer */
|
||||||
extern sSetWinEventHook pSetWinEventHook;
|
extern sSetWinEventHook pSetWinEventHook;
|
||||||
|
|
||||||
|
/* api-ms-win-core-file-l2-1-4.dll function pointers */
|
||||||
|
extern sGetFileInformationByName pGetFileInformationByName;
|
||||||
|
|
||||||
/* ws2_32.dll function pointer */
|
/* ws2_32.dll function pointer */
|
||||||
/* mingw doesn't have this definition, so let's declare it here locally */
|
/* mingw doesn't have this definition, so let's declare it here locally */
|
||||||
typedef int (WINAPI *uv_sGetHostNameW)
|
typedef int (WINAPI *uv_sGetHostNameW)
|
||||||
|
|||||||
@ -154,47 +154,6 @@ typedef struct _AFD_RECV_INFO {
|
|||||||
#define IOCTL_AFD_POLL \
|
#define IOCTL_AFD_POLL \
|
||||||
_AFD_CONTROL_CODE(AFD_POLL, METHOD_BUFFERED)
|
_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,
|
int uv__convert_to_localhost_if_unspecified(const struct sockaddr* addr,
|
||||||
struct sockaddr_storage* storage);
|
struct sockaddr_storage* storage);
|
||||||
|
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include <io.h>
|
# include <io.h>
|
||||||
|
# define read _read
|
||||||
#else
|
#else
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -310,7 +310,7 @@ static int clear_line(void) {
|
|||||||
COORD coord;
|
COORD coord;
|
||||||
DWORD written;
|
DWORD written;
|
||||||
|
|
||||||
handle = (HANDLE)_get_osfhandle(fileno(stderr));
|
handle = (HANDLE)_get_osfhandle(_fileno(stderr));
|
||||||
if (handle == INVALID_HANDLE_VALUE)
|
if (handle == INVALID_HANDLE_VALUE)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user