mirror of
https://github.com/libuv/libuv
synced 2025-03-28 21:13:16 +00:00
Compare commits
249 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c1a9f01f22 | ||
![]() |
bb706f5fe7 | ||
![]() |
2b96e47f12 | ||
![]() |
ea1cf034be | ||
![]() |
4681d5d570 | ||
![]() |
c727be4df9 | ||
![]() |
98a4bab92a | ||
![]() |
352d992916 | ||
![]() |
92eacd19a1 | ||
![]() |
545ecf515f | ||
![]() |
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:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v4
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.9'
|
||||
cache: 'pip' # caching pip dependencies
|
||||
|
3
.github/workflows/CI-sample.yml
vendored
3
.github/workflows/CI-sample.yml
vendored
@ -6,6 +6,7 @@ on:
|
||||
- '**'
|
||||
- '!docs/**'
|
||||
- '!.**'
|
||||
- 'docs/code/**'
|
||||
- '.github/workflows/CI-sample.yml'
|
||||
push:
|
||||
branches:
|
||||
@ -20,7 +21,7 @@ jobs:
|
||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||
runs-on: ${{matrix.os}}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: setup
|
||||
run: cmake -E make_directory ${{runner.workspace}}/libuv/docs/code/build
|
||||
- name: configure
|
||||
|
124
.github/workflows/CI-unix.yml
vendored
124
.github/workflows/CI-unix.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: configure
|
||||
run: |
|
||||
./autogen.sh
|
||||
@ -29,28 +29,67 @@ jobs:
|
||||
|
||||
build-android:
|
||||
runs-on: ubuntu-latest
|
||||
container: reactnativecommunity/react-native-android:2020-5-20
|
||||
env:
|
||||
ANDROID_AVD_HOME: /root/.android/avd
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Envinfo
|
||||
run: npx envinfo
|
||||
- name: Configure android arm64
|
||||
# see build options you can use in https://developer.android.com/ndk/guides/cmake
|
||||
- name: Enable KVM
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
$ANDROID_HOME/cmake/3.10.2.4988404/bin/cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_HOME/ndk/20.0.5594570/build/cmake/android.toolchain.cmake -DCMAKE_BUILD_TYPE=Release -DANDROID_ABI="arm64-v8a" -DANDROID_PLATFORM=android-24 ..
|
||||
- name: Build android arm64
|
||||
run: |
|
||||
$ANDROID_HOME/cmake/3.10.2.4988404/bin/cmake --build build
|
||||
ls -lh build
|
||||
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger --name-match=kvm
|
||||
- name: Build and Test
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: 30
|
||||
arch: x86_64
|
||||
target: google_apis
|
||||
ram-size: 2048M
|
||||
emulator-options: -no-audio -no-window -gpu off -no-boot-anim -netdelay none -netspeed full -writable-system -no-snapshot-save -no-snapshot-load -no-snapshot
|
||||
disable-animations: true
|
||||
script: |
|
||||
echo "::group::Configure"
|
||||
cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DCMAKE_BUILD_TYPE=Release -DANDROID_ABI="x86_64" -DANDROID_PLATFORM=android-30
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Build"
|
||||
cmake --build build
|
||||
|
||||
## Correct some ld bugs that cause problems with libuv tests
|
||||
wget "https://github.com/termux/termux-elf-cleaner/releases/download/v2.2.1/termux-elf-cleaner" -P build
|
||||
chmod a+x build/termux-elf-cleaner
|
||||
build/termux-elf-cleaner --api-level 30 ./build/uv_run_tests
|
||||
build/termux-elf-cleaner --api-level 30 ./build/uv_run_tests_a
|
||||
|
||||
adb shell "su 0 setenforce 0" # to allow some syscalls like link, chmod, etc.
|
||||
|
||||
## Push the build and test fixtures to the device
|
||||
adb push build /data/local/tmp
|
||||
adb shell mkdir /data/local/tmp/build/test
|
||||
adb push test/fixtures /data/local/tmp/build/test
|
||||
echo "::endgroup::"
|
||||
|
||||
## Run the tests
|
||||
file build/uv_run_tests_a
|
||||
adb shell "cd /data/local/tmp/build && env UV_TEST_TIMEOUT_MULTIPLIER=5 ./uv_run_tests_a"
|
||||
|
||||
build-macos:
|
||||
runs-on: macos-11
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-13, macos-14]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Envinfo
|
||||
run: npx envinfo
|
||||
- name: Disable Firewall
|
||||
run: |
|
||||
/usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
|
||||
sudo defaults write /Library/Preferences/com.apple.alf globalstate -int 0
|
||||
/usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
|
||||
- name: Setup
|
||||
run: |
|
||||
brew install ninja automake libtool
|
||||
@ -81,9 +120,13 @@ jobs:
|
||||
make -C build-auto -j4
|
||||
|
||||
build-ios:
|
||||
runs-on: macos-11
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-13, macos-14]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Configure
|
||||
run: |
|
||||
mkdir build-ios
|
||||
@ -95,45 +138,36 @@ jobs:
|
||||
ls -lh build-ios
|
||||
|
||||
build-cross-qemu:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
name: build-cross-qemu-${{ matrix.config.target }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- {target: arm, toolchain: gcc-arm-linux-gnueabi, cc: arm-linux-gnueabi-gcc, qemu: qemu-arm-static }
|
||||
- {target: armhf, toolchain: gcc-arm-linux-gnueabihf, cc: arm-linux-gnueabihf-gcc, qemu: qemu-arm-static }
|
||||
- {target: aarch64, toolchain: gcc-aarch64-linux-gnu, cc: aarch64-linux-gnu-gcc, qemu: qemu-aarch64-static }
|
||||
- {target: riscv64, toolchain: gcc-riscv64-linux-gnu, cc: riscv64-linux-gnu-gcc, qemu: qemu-riscv64-static }
|
||||
- {target: ppc, toolchain: gcc-powerpc-linux-gnu, cc: powerpc-linux-gnu-gcc, qemu: qemu-ppc-static }
|
||||
- {target: ppc64, toolchain: gcc-powerpc64-linux-gnu, cc: powerpc64-linux-gnu-gcc, qemu: qemu-ppc64-static }
|
||||
- {target: ppc64le, toolchain: gcc-powerpc64le-linux-gnu, cc: powerpc64le-linux-gnu-gcc, qemu: qemu-ppc64le-static }
|
||||
- {target: s390x, toolchain: gcc-s390x-linux-gnu, cc: s390x-linux-gnu-gcc, qemu: qemu-s390x-static }
|
||||
- {target: mips, toolchain: gcc-mips-linux-gnu, cc: mips-linux-gnu-gcc, qemu: qemu-mips-static }
|
||||
- {target: mips64, toolchain: gcc-mips64-linux-gnuabi64, cc: mips64-linux-gnuabi64-gcc, qemu: qemu-mips64-static }
|
||||
- {target: mipsel, toolchain: gcc-mipsel-linux-gnu, cc: mipsel-linux-gnu-gcc, qemu: qemu-mipsel-static }
|
||||
- {target: mips64el,toolchain: gcc-mips64el-linux-gnuabi64, cc: mips64el-linux-gnuabi64-gcc,qemu: qemu-mips64el-static }
|
||||
- {target: arm (u64 slots), toolchain: gcc-arm-linux-gnueabi, cc: arm-linux-gnueabi-gcc, qemu: qemu-arm-static}
|
||||
- {target: aarch64 (u64 slots), toolchain: gcc-aarch64-linux-gnu, cc: aarch64-linux-gnu-gcc, qemu: qemu-aarch64-static}
|
||||
- {target: ppc (u64 slots), toolchain: gcc-powerpc-linux-gnu, cc: powerpc-linux-gnu-gcc, qemu: qemu-ppc-static}
|
||||
- {target: ppc64 (u64 slots), toolchain: gcc-powerpc64-linux-gnu, cc: powerpc64-linux-gnu-gcc, qemu: qemu-ppc64-static}
|
||||
- {target: arm, toolchain: gcc-arm-linux-gnueabi, cc: arm-linux-gnueabi-gcc, qemu: qemu-arm }
|
||||
- {target: armhf, toolchain: gcc-arm-linux-gnueabihf, cc: arm-linux-gnueabihf-gcc, qemu: qemu-arm }
|
||||
- {target: aarch64, toolchain: gcc-aarch64-linux-gnu, cc: aarch64-linux-gnu-gcc, qemu: qemu-aarch64 }
|
||||
- {target: riscv64, toolchain: gcc-riscv64-linux-gnu, cc: riscv64-linux-gnu-gcc, qemu: qemu-riscv64 }
|
||||
- {target: ppc, toolchain: gcc-powerpc-linux-gnu, cc: powerpc-linux-gnu-gcc, qemu: qemu-ppc }
|
||||
- {target: ppc64, toolchain: gcc-powerpc64-linux-gnu, cc: powerpc64-linux-gnu-gcc, qemu: qemu-ppc64 }
|
||||
- {target: ppc64le, toolchain: gcc-powerpc64le-linux-gnu, cc: powerpc64le-linux-gnu-gcc, qemu: qemu-ppc64le }
|
||||
- {target: s390x, toolchain: gcc-s390x-linux-gnu, cc: s390x-linux-gnu-gcc, qemu: qemu-s390x }
|
||||
- {target: mips, toolchain: gcc-mips-linux-gnu, cc: mips-linux-gnu-gcc, qemu: qemu-mips }
|
||||
- {target: mips64, toolchain: gcc-mips64-linux-gnuabi64, cc: mips64-linux-gnuabi64-gcc, qemu: qemu-mips64 }
|
||||
- {target: mipsel, toolchain: gcc-mipsel-linux-gnu, cc: mipsel-linux-gnu-gcc, qemu: qemu-mipsel }
|
||||
- {target: mips64el, toolchain: gcc-mips64el-linux-gnuabi64, cc: mips64el-linux-gnuabi64-gcc,qemu: qemu-mips64el }
|
||||
- {target: arm (u64 slots), toolchain: gcc-arm-linux-gnueabi, cc: arm-linux-gnueabi-gcc, qemu: qemu-arm }
|
||||
- {target: aarch64 (u64 slots), toolchain: gcc-aarch64-linux-gnu, cc: aarch64-linux-gnu-gcc, qemu: qemu-aarch64 }
|
||||
- {target: ppc (u64 slots), toolchain: gcc-powerpc-linux-gnu, cc: powerpc-linux-gnu-gcc, qemu: qemu-ppc }
|
||||
- {target: ppc64 (u64 slots), toolchain: gcc-powerpc64-linux-gnu, cc: powerpc64-linux-gnu-gcc, qemu: qemu-ppc64 }
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install QEMU
|
||||
# this ensure install latest qemu on ubuntu, apt get version is old
|
||||
env:
|
||||
QEMU_SRC: "http://archive.ubuntu.com/ubuntu/pool/universe/q/qemu"
|
||||
QEMU_VER: "qemu-user-static_7\\.2+dfsg-.*_amd64.deb$"
|
||||
run: |
|
||||
DEB=`curl -s $QEMU_SRC/ | grep -o -E 'href="([^"#]+)"' | cut -d'"' -f2 | grep $QEMU_VER | tail -1`
|
||||
wget $QEMU_SRC/$DEB
|
||||
sudo dpkg -i $DEB
|
||||
- name: Install ${{ matrix.config.toolchain }}
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install qemu and ${{ matrix.config.toolchain }}
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install ${{ matrix.config.toolchain }} -y
|
||||
sudo apt install qemu-user qemu-user-binfmt ${{ matrix.config.toolchain }} -y
|
||||
- name: Configure with ${{ matrix.config.cc }}
|
||||
run: |
|
||||
mkdir build
|
||||
|
35
.github/workflows/CI-win.yml
vendored
35
.github/workflows/CI-win.yml
vendored
@ -21,38 +21,39 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- {toolchain: Visual Studio 16 2019, arch: Win32, server: 2019}
|
||||
- {toolchain: Visual Studio 16 2019, arch: x64, server: 2019}
|
||||
- {toolchain: Visual Studio 17 2022, arch: Win32, server: 2022}
|
||||
- {toolchain: Visual Studio 17 2022, arch: x64, server: 2022}
|
||||
- {toolchain: Visual Studio 17 2022, arch: x64, server: 2022, config: ASAN}
|
||||
- {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: x64, server: 2025}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Envinfo
|
||||
run: npx envinfo
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
shell: cmd
|
||||
run:
|
||||
cmake -S . -B build -DBUILD_TESTING=ON
|
||||
-G "${{ matrix.config.toolchain }}" -A ${{ matrix.config.arch }}
|
||||
${{ matrix.config.config == 'ASAN' && '-DASAN=on -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded' ||
|
||||
matrix.config.config == 'UBSAN' && '-DUBSAN=on' || '' }}
|
||||
${{ matrix.config.config == 'ASAN' && '-DASAN=on -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded' || '' }}
|
||||
|
||||
cmake --build build --config RelWithDebInfo
|
||||
|
||||
${{ 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
|
||||
- name: platform_output
|
||||
if: ${{ matrix.config.arch != 'arm64' }}
|
||||
shell: cmd
|
||||
run:
|
||||
build\\RelWithDebInfo\\uv_run_tests.exe platform_output
|
||||
|
||||
ls -l build\\RelWithDebInfo
|
||||
- name: platform_output_a
|
||||
if: ${{ matrix.config.arch != 'arm64' }}
|
||||
shell: cmd
|
||||
run:
|
||||
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
|
||||
# only valid with libuv-master with the fix for
|
||||
# https://github.com/libuv/leps/blob/master/005-windows-handles-not-fd.md
|
||||
@ -78,7 +79,7 @@ jobs:
|
||||
- {arch: i686, server: 2022, libgcc: dw2 }
|
||||
- {arch: x86_64, server: 2022, libgcc: seh }
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install mingw32 environment
|
||||
run: |
|
||||
sudo apt update
|
||||
@ -90,13 +91,13 @@ jobs:
|
||||
cmake --install build --prefix "`pwd`/build/usr"
|
||||
mkdir -p build/usr/test build/usr/bin
|
||||
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=libwinpthread-1.dll` \
|
||||
`${{ matrix.config.arch }}-w64-mingw32-gcc -print-file-name=libatomic-1.dll` \
|
||||
build/usr/bin
|
||||
- name: Upload build artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: mingw-${{ matrix.config.arch }}
|
||||
path: build/usr/**/*
|
||||
@ -114,7 +115,7 @@ jobs:
|
||||
- {arch: x86_64, server: 2022}
|
||||
steps:
|
||||
- name: Download build artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: mingw-${{ matrix.config.arch }}
|
||||
- name: Test
|
||||
|
15
.github/workflows/sanitizer.yml
vendored
15
.github/workflows/sanitizer.yml
vendored
@ -16,13 +16,18 @@ jobs:
|
||||
sanitizers-linux:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup
|
||||
run: |
|
||||
sudo apt-get install ninja-build
|
||||
- name: Envinfo
|
||||
run: npx envinfo
|
||||
|
||||
# [AM]SAN fail on newer kernels due to a bigger PIE slide
|
||||
- name: Disable ASLR
|
||||
run: |
|
||||
sudo sysctl -w kernel.randomize_va_space=0
|
||||
|
||||
- name: ASAN Build
|
||||
run: |
|
||||
mkdir build-asan
|
||||
@ -62,9 +67,9 @@ jobs:
|
||||
./build-ubsan/uv_run_tests_a
|
||||
|
||||
sanitizers-macos:
|
||||
runs-on: macos-11
|
||||
runs-on: macos-13
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Envinfo
|
||||
run: npx envinfo
|
||||
@ -99,14 +104,14 @@ jobs:
|
||||
sanitizers-windows:
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup
|
||||
run: |
|
||||
choco install ninja
|
||||
|
||||
# Note: clang shipped with VS2022 has an issue where the UBSAN runtime doesn't link.
|
||||
- name: Install LLVM and Clang
|
||||
uses: KyleMayes/install-llvm-action@v1
|
||||
uses: KyleMayes/install-llvm-action@v2
|
||||
with:
|
||||
version: "17"
|
||||
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -7,9 +7,11 @@
|
||||
*.sdf
|
||||
*.suo
|
||||
.vs/
|
||||
.vscode/
|
||||
*.VC.db
|
||||
*.VC.opendb
|
||||
core
|
||||
.cache
|
||||
vgcore.*
|
||||
.buildstamp
|
||||
.dirstamp
|
||||
@ -74,3 +76,5 @@ cmake-build-debug/
|
||||
|
||||
# make dist output
|
||||
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>
|
||||
Andrius Bentkus <andrius.bentkus@gmail.com> <toxedvirus@gmail.com>
|
||||
Andy Fiddaman <andy@omniosce.org> <omnios@citrus-it.co.uk>
|
||||
Andy Pan <panjf2000@gmail.com> <i@andypan.me>
|
||||
Bert Belder <bertbelder@gmail.com> <i@bertbelder.com>
|
||||
Bert Belder <bertbelder@gmail.com> <info@2bs.nl>
|
||||
Bert Belder <bertbelder@gmail.com> <user@ChrUbuntu.(none)>
|
||||
@ -18,6 +19,7 @@ David Carlier <devnexen@gmail.com>
|
||||
Devchandra Meetei Leishangthem <dlmeetei@gmail.com>
|
||||
Fedor Indutny <fedor.indutny@gmail.com> <fedor@indutny.com>
|
||||
Frank Denis <github@pureftpd.org>
|
||||
Hüseyin Açacak <110401522+huseyinacacak-janea@users.noreply.github.com> <huseyin@janeasystems.com>
|
||||
Imran Iqbal <imrani@ca.ibm.com> <imran@imraniqbal.org>
|
||||
Isaac Z. Schlueter <i@izs.me>
|
||||
Jason Williams <necmon@yahoo.com>
|
||||
@ -37,6 +39,7 @@ Michael Neumann <mneumann@think.localnet> <mneumann@ntecs.de>
|
||||
Michael Penick <michael.penick@datastax.com> <mpenick@users.noreply.github.com>
|
||||
Nicholas Vavilov <vvnicholas@gmail.com>
|
||||
Nick Logan <ugexe@cpan.org> <nlogan@gmail.com>
|
||||
Olivier Valentin <ovalenti@redhat.com> <valentio@free.fr>
|
||||
Rasmus Christian Pedersen <zerhacken@yahoo.com>
|
||||
Rasmus Christian Pedersen <zerhacken@yahoo.com> <ruysch@outlook.com>
|
||||
Richard Lau <rlau@redhat.com> <riclau@uk.ibm.com>
|
||||
@ -47,7 +50,8 @@ Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
|
||||
Sam Roberts <vieuxtech@gmail.com> <sam@strongloop.com>
|
||||
San-Tai Hsu <vanilla@fatpipi.com>
|
||||
Santiago Gimeno <santiago.gimeno@quantion.es> <santiago.gimeno@gmail.com>
|
||||
Saúl Ibarra Corretgé <saghul@gmail.com>
|
||||
Saúl Ibarra Corretgé <s@saghul.net>
|
||||
Saúl Ibarra Corretgé <s@saghul.net> <saghul@gmail.com>
|
||||
Saúl Ibarra Corretgé <saghul@gmail.com> <s@saghul.net>
|
||||
Shigeki Ohtsu <ohtsu@iij.ad.jp> <ohtsu@ohtsu.org>
|
||||
Shuowang (Wayne) Zhang <shuowang.zhang@ibm.com>
|
||||
|
@ -2,7 +2,7 @@ version: 2
|
||||
|
||||
sphinx:
|
||||
builder: html
|
||||
configuration: null
|
||||
configuration: docs/src/conf.py
|
||||
fail_on_warning: false
|
||||
|
||||
build:
|
||||
|
32
AUTHORS
32
AUTHORS
@ -560,3 +560,35 @@ prubel <paul@rubels.net>
|
||||
Per Allansson <65364157+per-allansson@users.noreply.github.com>
|
||||
Matheus Izvekov <mizvekov@gmail.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)
|
||||
cmake_policy(SET CMP0091 NEW) # Enable MSVC_RUNTIME_LIBRARY setting
|
||||
@ -20,7 +20,7 @@ include(CTest)
|
||||
set(CMAKE_C_VISIBILITY_PRESET hidden)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_EXTENSIONS ON)
|
||||
set(CMAKE_C_STANDARD 90)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
@ -81,15 +81,20 @@ if(TSAN)
|
||||
endif()
|
||||
|
||||
if(UBSAN)
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
list(APPEND uv_defines __UBSAN__=1)
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|GNU|Clang")
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined")
|
||||
set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined")
|
||||
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=undefined")
|
||||
elseif(MSVC)
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=undefined")
|
||||
add_compile_options("-fsanitize=undefined" "-fno-sanitize-recover=undefined")
|
||||
if (NOT WIN32)
|
||||
add_link_options("-fsanitize=undefined")
|
||||
endif()
|
||||
if(MSVC)
|
||||
add_compile_options("/Oy-")
|
||||
else()
|
||||
add_compile_options("-fno-omit-frame-pointer")
|
||||
endif()
|
||||
else()
|
||||
message(SEND_ERROR "UndefinedBehaviorSanitizer support requires clang, gcc, or msvc. Try again with -DCMAKE_C_COMPILER.")
|
||||
message(SEND_ERROR "UndefinedBehaviorSanitizer support requires clang or gcc. Try again with -DCMAKE_C_COMPILER.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@ -161,6 +166,11 @@ list(APPEND uv_cflags ${lint-utf8-msvc} )
|
||||
check_c_compiler_flag(-fno-strict-aliasing UV_F_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
|
||||
src/fs-poll.c
|
||||
src/idna.c
|
||||
@ -176,7 +186,7 @@ set(uv_sources
|
||||
src/version.c)
|
||||
|
||||
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
|
||||
psapi
|
||||
user32
|
||||
@ -186,7 +196,6 @@ if(WIN32)
|
||||
ws2_32
|
||||
dbghelp
|
||||
ole32
|
||||
uuid
|
||||
shell32)
|
||||
list(APPEND uv_sources
|
||||
src/win/async.c
|
||||
@ -303,6 +312,7 @@ if(APPLE)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "GNU")
|
||||
list(APPEND uv_defines _GNU_SOURCE _POSIX_C_SOURCE=200112 _XOPEN_SOURCE=500)
|
||||
list(APPEND uv_libraries dl)
|
||||
list(APPEND uv_sources
|
||||
src/unix/bsd-ifaddrs.c
|
||||
@ -424,6 +434,7 @@ endif()
|
||||
|
||||
if(APPLE OR CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD|Linux|NetBSD|OpenBSD")
|
||||
list(APPEND uv_test_libraries util)
|
||||
list(APPEND uv_libraries m)
|
||||
endif()
|
||||
|
||||
if(CYGWIN OR MSYS)
|
||||
@ -562,6 +573,7 @@ if(LIBUV_BUILD_TESTS)
|
||||
test/test-hrtime.c
|
||||
test/test-idle.c
|
||||
test/test-idna.c
|
||||
test/test-iouring-pollhup.c
|
||||
test/test-ip4-addr.c
|
||||
test/test-ip6-addr.c
|
||||
test/test-ip-name.c
|
||||
@ -639,6 +651,7 @@ if(LIBUV_BUILD_TESTS)
|
||||
test/test-tcp-oob.c
|
||||
test/test-tcp-open.c
|
||||
test/test-tcp-read-stop.c
|
||||
test/test-tcp-reuseport.c
|
||||
test/test-tcp-read-stop-start.c
|
||||
test/test-tcp-rst.c
|
||||
test/test-tcp-shutdown-after-write.c
|
||||
@ -655,6 +668,8 @@ if(LIBUV_BUILD_TESTS)
|
||||
test/test-thread-affinity.c
|
||||
test/test-thread-equal.c
|
||||
test/test-thread.c
|
||||
test/test-thread-name.c
|
||||
test/test-thread-priority.c
|
||||
test/test-threadpool-cancel.c
|
||||
test/test-threadpool.c
|
||||
test/test-timer-again.c
|
||||
@ -686,6 +701,7 @@ if(LIBUV_BUILD_TESTS)
|
||||
test/test-udp-send-unreachable.c
|
||||
test/test-udp-try-send.c
|
||||
test/test-udp-recv-in-a-row.c
|
||||
test/test-udp-reuseport.c
|
||||
test/test-uname.c
|
||||
test/test-walk-handles.c
|
||||
test/test-watcher-cross-stop.c)
|
||||
@ -702,6 +718,12 @@ if(LIBUV_BUILD_TESTS)
|
||||
set_tests_properties(uv_test PROPERTIES ENVIRONMENT
|
||||
"LIBPATH=${CMAKE_BINARY_DIR}:$ENV{LIBPATH}")
|
||||
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)
|
||||
target_compile_definitions(uv_run_tests_a PRIVATE ${uv_defines})
|
||||
target_compile_options(uv_run_tests_a PRIVATE ${uv_cflags})
|
||||
@ -718,6 +740,12 @@ if(LIBUV_BUILD_TESTS)
|
||||
set_target_properties(uv_run_tests PROPERTIES LINKER_LANGUAGE CXX)
|
||||
set_target_properties(uv_run_tests_a PROPERTIES LINKER_LANGUAGE CXX)
|
||||
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()
|
||||
|
||||
# Now for some gibbering horrors from beyond the stars...
|
||||
@ -762,8 +790,22 @@ endif()
|
||||
|
||||
if(MSVC)
|
||||
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()
|
||||
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set(LIB_SELECTED uv)
|
||||
else()
|
||||
set(LIB_SELECTED uv_a)
|
||||
endif()
|
||||
|
||||
add_library(libuv::libuv ALIAS ${LIB_SELECTED})
|
||||
|
||||
message(STATUS "summary of build options:
|
||||
Install prefix: ${CMAKE_INSTALL_PREFIX}
|
||||
Target system: ${CMAKE_SYSTEM_NAME}
|
||||
|
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:
|
||||
|
||||
@ -820,7 +1243,7 @@ Changes since version 1.41.0:
|
||||
|
||||
* zos: treat __rfim_utok as binary (Shuowang (Wayne) Zhang)
|
||||
|
||||
* zos: use execvpe() to set environ explictly (Shuowang (Wayne) Zhang)
|
||||
* zos: use execvpe() to set environ explicitly (Shuowang (Wayne) Zhang)
|
||||
|
||||
* zos: use custom proctitle implementation (Shuowang (Wayne) Zhang)
|
||||
|
||||
@ -3326,7 +3749,7 @@ Changes since version 1.9.1:
|
||||
|
||||
* zos: implement uv__io_check_fd (John Barboza)
|
||||
|
||||
* unix: unneccessary use const qualifier in container_of (John Barboza)
|
||||
* unix: unnecessary use const qualifier in container_of (John Barboza)
|
||||
|
||||
* win,tty: add support for ANSI codes in win10 v1511 (Imran Iqbal)
|
||||
|
||||
@ -5429,7 +5852,7 @@ Changes since version 0.11.8:
|
||||
is an int64_t, and no longer an int. (Bert Belder)
|
||||
|
||||
* process: make uv_spawn() return some types of errors immediately on windows,
|
||||
instead of passing the error code the the exit callback. This brings it on
|
||||
instead of passing the error code the exit callback. This brings it on
|
||||
par with libuv's behavior on unix. (Bert Belder)
|
||||
|
||||
|
||||
|
3
LINKS.md
3
LINKS.md
@ -37,6 +37,7 @@
|
||||
* [Pixie-io](https://github.com/pixie-io/pixie): Open-source observability tool for Kubernetes applications.
|
||||
* [potion](https://github.com/perl11/potion)/[p2](https://github.com/perl11/p2): runtime
|
||||
* [racer](https://libraries.io/rubygems/racer): Ruby web server written as an C extension
|
||||
* [scala-native-loop](https://github.com/scala-native/scala-native-loop): Extensible event loop and async-oriented IO for Scala Native; powered by libuv
|
||||
* [Socket Runtime](https://sockets.sh): A runtime for creating native cross-platform software on mobile and desktop using HTML, CSS, and JavaScript
|
||||
* [spider-gazelle](https://github.com/cotag/spider-gazelle): Ruby web server using libuv bindings
|
||||
* [Suave](http://suave.io/): A simple web development F# library providing a lightweight web server and a set of combinators to manipulate route flow and task composition
|
||||
@ -107,3 +108,5 @@
|
||||
* [node.pas](https://github.com/vovach777/node.pas) NodeJS-like ecosystem
|
||||
* Haskell
|
||||
* [Z.Haskell](https://z.haskell.world)
|
||||
* C3
|
||||
* [libuv.c3l](https://github.com/velikoss/libuv.c3l)
|
||||
|
14
Makefile.am
14
Makefile.am
@ -59,7 +59,7 @@ if WINNT
|
||||
uvinclude_HEADERS += include/uv/win.h include/uv/tree.h
|
||||
AM_CPPFLAGS += -I$(top_srcdir)/src/win \
|
||||
-DWIN32_LEAN_AND_MEAN \
|
||||
-D_WIN32_WINNT=0x0602
|
||||
-D_WIN32_WINNT=0x0A00
|
||||
libuv_la_SOURCES += src/win/async.c \
|
||||
src/win/atomicops-inl.h \
|
||||
src/win/core.c \
|
||||
@ -136,6 +136,12 @@ TESTS = test/run-tests
|
||||
check_PROGRAMS = test/run-tests
|
||||
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
|
||||
# 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.
|
||||
@ -192,6 +198,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
|
||||
test/test-hrtime.c \
|
||||
test/test-idle.c \
|
||||
test/test-idna.c \
|
||||
test/test-iouring-pollhup.c \
|
||||
test/test-ip4-addr.c \
|
||||
test/test-ip6-addr.c \
|
||||
test/test-ip-name.c \
|
||||
@ -269,6 +276,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \
|
||||
test/test-tcp-flags.c \
|
||||
test/test-tcp-open.c \
|
||||
test/test-tcp-read-stop.c \
|
||||
test/test-tcp-reuseport.c \
|
||||
test/test-tcp-read-stop-start.c \
|
||||
test/test-tcp-rst.c \
|
||||
test/test-tcp-shutdown-after-write.c \
|
||||
@ -286,6 +294,8 @@ test_run_tests_SOURCES = test/blackhole-server.c \
|
||||
test/test-thread-equal.c \
|
||||
test/test-thread.c \
|
||||
test/test-thread-affinity.c \
|
||||
test/test-thread-name.c \
|
||||
test/test-thread-priority.c \
|
||||
test/test-threadpool-cancel.c \
|
||||
test/test-threadpool.c \
|
||||
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-try-send.c \
|
||||
test/test-udp-recv-in-a-row.c \
|
||||
test/test-udp-reuseport.c \
|
||||
test/test-uname.c \
|
||||
test/test-walk-handles.c \
|
||||
test/test-watcher-cross-stop.c
|
||||
@ -420,6 +431,7 @@ libuv_la_CFLAGS += -D_DARWIN_UNLIMITED_SELECT=1
|
||||
libuv_la_SOURCES += src/unix/bsd-ifaddrs.c \
|
||||
src/unix/darwin-proctitle.c \
|
||||
src/unix/darwin-stub.h \
|
||||
src/unix/darwin-syscalls.h \
|
||||
src/unix/darwin.c \
|
||||
src/unix/fsevents.c \
|
||||
src/unix/kqueue.c \
|
||||
|
12
README.md
12
README.md
@ -232,6 +232,18 @@ $ ./bootstrap-vcpkg.sh # for bash
|
||||
$ ./vcpkg install libuv
|
||||
```
|
||||
|
||||
### Install with Conan
|
||||
|
||||
You can install pre-built binaries for libuv or build it from source using [Conan](https://conan.io/). Use the following command:
|
||||
|
||||
```bash
|
||||
conan install --requires="libuv/[*]" --build=missing
|
||||
```
|
||||
|
||||
The libuv Conan recipe is kept up to date by Conan maintainers and community contributors.
|
||||
If the version is out of date, please [create an issue or pull request](https://github.com/conan-io/conan-center-index) on the ConanCenterIndex repository.
|
||||
|
||||
|
||||
### Running tests
|
||||
|
||||
Some tests are timing sensitive. Relaxing test timeouts may be necessary
|
||||
|
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 | |
|
||||
| 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 2017 and later are supported |
|
||||
| FreeBSD | Tier 2 | >= 12 | |
|
||||
| AIX | Tier 2 | >= 6 | Maintainers: @libuv/aix |
|
||||
| IBM i | Tier 2 | >= IBM i 7.2 | Maintainers: @libuv/ibmi |
|
||||
| z/OS | Tier 2 | >= V2R2 | Maintainers: @libuv/zos |
|
||||
| Linux with musl | Tier 2 | musl >= 1.0 | |
|
||||
| Android | Tier 3 | NDK >= r15b | Android 7.0, `-DANDROID_PLATFORM=android-24` |
|
||||
| MinGW | Tier 3 | MinGW32 and MinGW-w64 | |
|
||||
| MinGW | Tier 3 | MinGW-w64 | |
|
||||
| SunOS | Tier 3 | Solaris 121 and later | |
|
||||
| Other | Tier 3 | N/A | |
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
AC_PREREQ(2.57)
|
||||
AC_INIT([libuv], [1.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])
|
||||
m4_include([m4/libuv-extra-automake-flags.m4])
|
||||
m4_include([m4/as_case.m4])
|
||||
@ -33,7 +33,7 @@ CC_ATTRIBUTE_VISIBILITY([default], [
|
||||
# we exclude -fno-strict-aliasing for xlc
|
||||
CC_CHECK_FLAG_SUPPORTED_APPEND([-fno-strict-aliasing])
|
||||
CC_CHECK_CFLAGS_APPEND([-g])
|
||||
CC_CHECK_CFLAGS_APPEND([-std=gnu89])
|
||||
CC_CHECK_CFLAGS_APPEND([-std=gnu11])
|
||||
CC_CHECK_CFLAGS_APPEND([-Wall])
|
||||
CC_CHECK_CFLAGS_APPEND([-Wextra])
|
||||
CC_CHECK_CFLAGS_APPEND([-Wno-long-long])
|
||||
@ -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([WINNT], [AS_CASE([$host_os],[mingw*], [true], [false])])
|
||||
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], [
|
||||
CFLAGS="$CFLAGS -DSUNOS_NO_IFADDRS"
|
||||
|
@ -18,22 +18,21 @@ int main(int argc, char **argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uv_lib_t *lib = (uv_lib_t*) malloc(sizeof(uv_lib_t));
|
||||
uv_lib_t lib;
|
||||
while (--argc) {
|
||||
fprintf(stderr, "Loading %s\n", argv[argc]);
|
||||
if (uv_dlopen(argv[argc], lib)) {
|
||||
fprintf(stderr, "Error: %s\n", uv_dlerror(lib));
|
||||
if (uv_dlopen(argv[argc], &lib)) {
|
||||
fprintf(stderr, "Error: %s\n", uv_dlerror(&lib));
|
||||
continue;
|
||||
}
|
||||
|
||||
init_plugin_function init_plugin;
|
||||
if (uv_dlsym(lib, "initialize", (void **) &init_plugin)) {
|
||||
fprintf(stderr, "dlsym error: %s\n", uv_dlerror(lib));
|
||||
if (uv_dlsym(&lib, "initialize", (void **) &init_plugin)) {
|
||||
fprintf(stderr, "dlsym error: %s\n", uv_dlerror(&lib));
|
||||
continue;
|
||||
}
|
||||
|
||||
init_plugin();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -5,25 +5,27 @@
|
||||
|
||||
uv_loop_t *loop;
|
||||
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_set_mode(&tty, UV_TTY_MODE_NORMAL);
|
||||
|
||||
if (uv_guess_handle(1) == UV_TTY) {
|
||||
uv_write_t req;
|
||||
uv_buf_t buf;
|
||||
buf.base = "\033[41;37m";
|
||||
buf.len = strlen(buf.base);
|
||||
uv_write(&req, (uv_stream_t*) &tty, &buf, 1, NULL);
|
||||
buf1.base = "\033[41;37m";
|
||||
buf1.len = strlen(buf1.base);
|
||||
uv_write(&req1, (uv_stream_t*) &tty, &buf1, 1, NULL);
|
||||
}
|
||||
|
||||
uv_write_t req;
|
||||
uv_buf_t buf;
|
||||
buf.base = "Hello TTY\n";
|
||||
buf.len = strlen(buf.base);
|
||||
uv_write(&req, (uv_stream_t*) &tty, &buf, 1, NULL);
|
||||
|
||||
uv_tty_reset_mode();
|
||||
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
|
||||
a (sometimes significant) increase in throughput there should be no change in
|
||||
observable behavior. Libuv reverts to using its threadpool when the necessary
|
||||
kernel features are unavailable or unsuitable.
|
||||
kernel features are unavailable or unsuitable. Starting with libuv v1.49.0 this
|
||||
behavior was reverted and Libuv on Linux by default will be using the threadpool
|
||||
again. In order to enable io_uring the :c:type:`uv_loop_t` instance must be
|
||||
configured with the :c:type:`UV_LOOP_ENABLE_IO_URING_SQPOLL` option.
|
||||
|
||||
.. note::
|
||||
On Windows `uv_fs_*` functions use utf-8 encoding.
|
||||
@ -129,10 +132,9 @@ Data types
|
||||
uint64_t f_spare[4];
|
||||
} uv_statfs_t;
|
||||
|
||||
.. c:enum:: uv_dirent_t
|
||||
.. c:enum:: uv_dirent_type_t
|
||||
|
||||
Cross platform (reduced) equivalent of ``struct dirent``.
|
||||
Used in :c:func:`uv_fs_scandir_next`.
|
||||
Type of dirent.
|
||||
|
||||
::
|
||||
|
||||
@ -147,6 +149,14 @@ Data types
|
||||
UV_DIRENT_BLOCK
|
||||
} uv_dirent_type_t;
|
||||
|
||||
|
||||
.. c:type:: uv_dirent_t
|
||||
|
||||
Cross platform (reduced) equivalent of ``struct dirent``.
|
||||
Used in :c:func:`uv_fs_scandir_next`.
|
||||
|
||||
::
|
||||
|
||||
typedef struct uv_dirent_s {
|
||||
const char* name;
|
||||
uv_dirent_type_t type;
|
||||
@ -420,6 +430,12 @@ API
|
||||
|
||||
Equivalent to :man:`utime(2)`, :man:`futimes(3)` and :man:`lutimes(3)` respectively.
|
||||
|
||||
Passing `UV_FS_UTIME_NOW` as the atime or mtime sets the timestamp to the
|
||||
current time.
|
||||
|
||||
Passing `UV_FS_UTIME_OMIT` as the atime or mtime leaves the timestamp
|
||||
untouched.
|
||||
|
||||
.. note::
|
||||
z/OS: `uv_fs_lutime()` is not implemented for z/OS. It can still be called but will return
|
||||
``UV_ENOSYS``.
|
||||
@ -454,7 +470,7 @@ API
|
||||
|
||||
.. c:function:: int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb)
|
||||
|
||||
Equivalent to :man:`realpath(3)` on Unix. Windows uses `GetFinalPathNameByHandle <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlea>`_.
|
||||
Equivalent to :man:`realpath(3)` on Unix. Windows uses `GetFinalPathNameByHandleW <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew>`_.
|
||||
The resulting string is stored in `req->ptr`.
|
||||
|
||||
.. warning::
|
||||
@ -653,7 +669,7 @@ File open constants
|
||||
|
||||
.. note::
|
||||
`UV_FS_O_RANDOM` is only supported on Windows via
|
||||
`FILE_FLAG_RANDOM_ACCESS <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea>`_.
|
||||
`FILE_FLAG_RANDOM_ACCESS <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew>`_.
|
||||
|
||||
.. c:macro:: UV_FS_O_RDONLY
|
||||
|
||||
@ -670,7 +686,7 @@ File open constants
|
||||
|
||||
.. note::
|
||||
`UV_FS_O_SEQUENTIAL` is only supported on Windows via
|
||||
`FILE_FLAG_SEQUENTIAL_SCAN <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea>`_.
|
||||
`FILE_FLAG_SEQUENTIAL_SCAN <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew>`_.
|
||||
|
||||
.. c:macro:: UV_FS_O_SHORT_LIVED
|
||||
|
||||
@ -678,7 +694,7 @@ File open constants
|
||||
|
||||
.. note::
|
||||
`UV_FS_O_SHORT_LIVED` is only supported on Windows via
|
||||
`FILE_ATTRIBUTE_TEMPORARY <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea>`_.
|
||||
`FILE_ATTRIBUTE_TEMPORARY <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew>`_.
|
||||
|
||||
.. c:macro:: UV_FS_O_SYMLINK
|
||||
|
||||
@ -699,7 +715,7 @@ File open constants
|
||||
|
||||
.. note::
|
||||
`UV_FS_O_TEMPORARY` is only supported on Windows via
|
||||
`FILE_ATTRIBUTE_TEMPORARY <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea>`_.
|
||||
`FILE_ATTRIBUTE_TEMPORARY <https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew>`_.
|
||||
|
||||
.. c:macro:: UV_FS_O_TRUNC
|
||||
|
||||
|
@ -45,9 +45,14 @@ Data types
|
||||
be a relative path to a file contained in the directory, or `NULL` if the
|
||||
file name cannot be determined.
|
||||
|
||||
The `events` parameter is an ORed mask of :c:type:`uv_fs_event` elements.
|
||||
The `events` parameter is an ORed mask of :c:enum:`uv_fs_event` elements.
|
||||
|
||||
.. c:type:: uv_fs_event
|
||||
.. note::
|
||||
For FreeBSD path could sometimes be `NULL` due to a kernel bug.
|
||||
|
||||
.. _Reference: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=197695
|
||||
|
||||
.. c:enum:: uv_fs_event
|
||||
|
||||
Event types that :c:type:`uv_fs_event_t` handles monitor.
|
||||
|
||||
@ -58,7 +63,7 @@ Data types
|
||||
UV_CHANGE = 2
|
||||
};
|
||||
|
||||
.. c:type:: uv_fs_event_flags
|
||||
.. c:enum:: uv_fs_event_flags
|
||||
|
||||
Flags that can be passed to :c:func:`uv_fs_event_start` to control its
|
||||
behavior.
|
||||
@ -109,10 +114,13 @@ API
|
||||
.. c:function:: int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, const char* path, unsigned int flags)
|
||||
|
||||
Start the handle with the given callback, which will watch the specified
|
||||
`path` for changes. `flags` can be an ORed mask of :c:type:`uv_fs_event_flags`.
|
||||
`path` for changes. `flags` can be an ORed mask of :c:enum:`uv_fs_event_flags`.
|
||||
|
||||
.. note:: Currently the only supported flag is ``UV_FS_EVENT_RECURSIVE`` and
|
||||
only on OSX and Windows.
|
||||
.. note:: On macOS, events collected by the OS immediately before calling
|
||||
``uv_fs_event_start`` might be reported to the `uv_fs_event_cb`
|
||||
callback.
|
||||
|
||||
.. c:function:: int uv_fs_event_stop(uv_fs_event_t* handle)
|
||||
|
||||
|
@ -333,7 +333,7 @@ to hand off their I/O to other processes. Applications include load-balancing
|
||||
servers, worker processes and other ways to make optimum use of CPU. libuv only
|
||||
supports sending **TCP sockets or other pipes** over pipes for now.
|
||||
|
||||
To demonstrate, we will look at a echo server implementation that hands of
|
||||
To demonstrate, we will look at an echo server implementation that hands off
|
||||
clients to worker processes in a round-robin fashion. This program is a bit
|
||||
involved, and while only snippets are included in the book, it is recommended
|
||||
to read the full code to really understand it.
|
||||
|
@ -94,7 +94,7 @@ Public members
|
||||
|
||||
.. c:member:: uv_handle_type uv_handle_t.type
|
||||
|
||||
The :c:type:`uv_handle_type`, indicating the type of the underlying handle. Readonly.
|
||||
The :c:enum:`uv_handle_type`, indicating the type of the underlying handle. Readonly.
|
||||
|
||||
.. c:member:: void* uv_handle_t.data
|
||||
|
||||
@ -248,7 +248,7 @@ just for some handle types.
|
||||
|
||||
.. versionadded:: 1.19.0
|
||||
|
||||
.. c:function:: void* uv_handle_set_data(uv_handle_t* handle, void* data)
|
||||
.. c:function:: void uv_handle_set_data(uv_handle_t* handle, void* data)
|
||||
|
||||
Sets `handle->data` to `data`.
|
||||
|
||||
|
@ -58,5 +58,5 @@ libuv can be downloaded from `here <https://dist.libuv.org/dist/>`_.
|
||||
Installation
|
||||
------------
|
||||
|
||||
Installation instructions can be found in `the README <https://github.com/libuv/libuv/blob/master/README.md>`_.
|
||||
Installation instructions can be found in the `README <https://github.com/libuv/libuv/blob/master/README.md>`_.
|
||||
|
||||
|
@ -16,6 +16,19 @@ Data types
|
||||
|
||||
Loop data type.
|
||||
|
||||
.. c:enum:: uv_loop_option
|
||||
|
||||
Additional loop options.
|
||||
See :c:func:`uv_loop_configure`.
|
||||
|
||||
::
|
||||
|
||||
typedef enum {
|
||||
UV_LOOP_BLOCK_SIGNAL = 0,
|
||||
UV_METRICS_IDLE_TIME,
|
||||
UV_LOOP_USE_IO_URING_SQPOLL
|
||||
} uv_loop_option;
|
||||
|
||||
.. c:enum:: uv_run_mode
|
||||
|
||||
Mode used to run the loop with :c:func:`uv_run`.
|
||||
@ -73,8 +86,13 @@ API
|
||||
|
||||
This option is necessary to use :c:func:`uv_metrics_idle_time`.
|
||||
|
||||
- UV_LOOP_ENABLE_IO_URING_SQPOLL: Enable SQPOLL io_uring instance to handle
|
||||
asynchronous file system operations.
|
||||
|
||||
.. versionchanged:: 1.39.0 added the UV_METRICS_IDLE_TIME option.
|
||||
|
||||
.. versionchanged:: 1.49.0 added the UV_LOOP_ENABLE_IO_URING_SQPOLL option.
|
||||
|
||||
.. c:function:: int uv_loop_close(uv_loop_t* loop)
|
||||
|
||||
Releases all internal loop resources. Call this function only when the loop
|
||||
@ -238,7 +256,7 @@ API
|
||||
|
||||
.. versionadded:: 1.19.0
|
||||
|
||||
.. c:function:: void* uv_loop_set_data(uv_loop_t* loop, void* data)
|
||||
.. c:function:: void uv_loop_set_data(uv_loop_t* loop, void* data)
|
||||
|
||||
Sets `loop->data` to `data`.
|
||||
|
||||
|
@ -199,6 +199,18 @@ Data types
|
||||
char* homedir;
|
||||
} uv_passwd_t;
|
||||
|
||||
.. c:type:: uv_group_t
|
||||
|
||||
Data type for group file information.
|
||||
|
||||
::
|
||||
|
||||
typedef struct uv_group_s {
|
||||
char* groupname;
|
||||
unsigned long gid;
|
||||
char** members;
|
||||
} uv_group_t;
|
||||
|
||||
.. c:type:: uv_utsname_t
|
||||
|
||||
Data type for operating system name and version information.
|
||||
@ -348,6 +360,17 @@ API
|
||||
On Windows not all fields are set, the unsupported fields are filled with zeroes.
|
||||
See :c:type:`uv_rusage_t` for more details.
|
||||
|
||||
.. c:function:: int uv_getrusage_thread(uv_rusage_t* rusage)
|
||||
|
||||
Gets the resource usage measures for the calling thread.
|
||||
|
||||
.. versionadded:: 1.50.0
|
||||
|
||||
.. note::
|
||||
Not supported on all platforms. May return `UV_ENOTSUP`.
|
||||
On macOS and Windows not all fields are set, the unsupported fields are filled with zeroes.
|
||||
See :c:type:`uv_rusage_t` for more details.
|
||||
|
||||
.. c:function:: uv_pid_t uv_os_getpid(void)
|
||||
|
||||
Returns the current process ID.
|
||||
@ -566,6 +589,35 @@ API
|
||||
|
||||
.. versionadded:: 1.9.0
|
||||
|
||||
.. c:function:: int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid)
|
||||
|
||||
Gets a subset of the password file entry for the provided uid.
|
||||
The populated data includes the username, euid, gid, shell,
|
||||
and home directory. On non-Windows systems, all data comes from
|
||||
:man:`getpwuid_r(3)`. On Windows, uid and gid are set to -1 and have no
|
||||
meaning, and shell is `NULL`. After successfully calling this function, the
|
||||
memory allocated to `pwd` needs to be freed with
|
||||
:c:func:`uv_os_free_passwd`.
|
||||
|
||||
.. versionadded:: 1.45.0
|
||||
|
||||
.. c:function:: int uv_os_get_group(uv_group_t* group, uv_uid_t gid)
|
||||
|
||||
Gets a subset of the group file entry for the provided uid.
|
||||
The populated data includes the group name, gid, and members. On non-Windows
|
||||
systems, all data comes from :man:`getgrgid_r(3)`. On Windows, uid and gid
|
||||
are set to -1 and have no meaning. After successfully calling this function,
|
||||
the memory allocated to `group` needs to be freed with
|
||||
:c:func:`uv_os_free_group`.
|
||||
|
||||
.. versionadded:: 1.45.0
|
||||
|
||||
.. c:function:: void uv_os_free_group(uv_passwd_t* pwd)
|
||||
|
||||
Frees the memory previously allocated with :c:func:`uv_os_get_group`.
|
||||
|
||||
.. versionadded:: 1.45.0
|
||||
|
||||
.. c:function:: void uv_os_free_passwd(uv_passwd_t* pwd)
|
||||
|
||||
Frees the `pwd` memory previously allocated with :c:func:`uv_os_get_passwd`.
|
||||
|
@ -45,7 +45,7 @@ Data types
|
||||
|
||||
Type definition for callback passed to :c:func:`uv_poll_start`.
|
||||
|
||||
.. c:type:: uv_poll_event
|
||||
.. c:enum:: uv_poll_event
|
||||
|
||||
Poll event types
|
||||
|
||||
|
@ -40,7 +40,7 @@ Data types
|
||||
will indicate the exit status and the signal that caused the process to
|
||||
terminate, if any.
|
||||
|
||||
.. c:type:: uv_process_flags
|
||||
.. c:enum:: uv_process_flags
|
||||
|
||||
Flags to be set on the flags field of :c:type:`uv_process_options_t`.
|
||||
|
||||
@ -85,7 +85,14 @@ Data types
|
||||
* option is only meaningful on Windows systems. On Unix it is silently
|
||||
* 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
|
||||
@ -183,7 +190,7 @@ Public members
|
||||
Command line arguments. args[0] should be the path to the program. On
|
||||
Windows this uses `CreateProcess` which concatenates the arguments into a
|
||||
string this can cause some strange errors. See the
|
||||
``UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS`` flag on :c:type:`uv_process_flags`.
|
||||
``UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS`` flag on :c:enum:`uv_process_flags`.
|
||||
|
||||
.. c:member:: char** uv_process_options_t.env
|
||||
|
||||
@ -196,7 +203,7 @@ Public members
|
||||
.. c:member:: unsigned int uv_process_options_t.flags
|
||||
|
||||
Various flags that control how :c:func:`uv_spawn` behaves. See
|
||||
:c:type:`uv_process_flags`.
|
||||
:c:enum:`uv_process_flags`.
|
||||
|
||||
.. c:member:: int uv_process_options_t.stdio_count
|
||||
.. c:member:: uv_stdio_container_t* uv_process_options_t.stdio
|
||||
@ -262,6 +269,9 @@ API
|
||||
.. versionchanged:: 1.24.0 Added `UV_PROCESS_WINDOWS_HIDE_CONSOLE` and
|
||||
`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)
|
||||
|
||||
Sends the specified signal to the given process handle. Check the documentation
|
||||
|
@ -21,17 +21,9 @@ Data types
|
||||
|
||||
Union of all request types.
|
||||
|
||||
.. c:enum:: uv_req_type
|
||||
|
||||
Public members
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
.. c:member:: void* uv_req_t.data
|
||||
|
||||
Space for user-defined arbitrary data. libuv does not use this field.
|
||||
|
||||
.. c:member:: uv_req_type uv_req_t.type
|
||||
|
||||
Indicated the type of request. Readonly.
|
||||
The kind of the libuv request.
|
||||
|
||||
::
|
||||
|
||||
@ -50,6 +42,18 @@ Public members
|
||||
} uv_req_type;
|
||||
|
||||
|
||||
Public members
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
.. c:member:: void* uv_req_t.data
|
||||
|
||||
Space for user-defined arbitrary data. libuv does not use this field.
|
||||
|
||||
.. c:member:: uv_req_type uv_req_t.type
|
||||
|
||||
The :c:enum:`uv_req_type`, indicating the type of the request. Readonly.
|
||||
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
@ -95,7 +99,7 @@ API
|
||||
|
||||
.. versionadded:: 1.19.0
|
||||
|
||||
.. c:function:: void* uv_req_set_data(uv_req_t* req, void* data)
|
||||
.. c:function:: void uv_req_set_data(uv_req_t* req, void* data)
|
||||
|
||||
Sets `req->data` to `data`.
|
||||
|
||||
|
@ -16,6 +16,28 @@ Data types
|
||||
|
||||
TCP handle type.
|
||||
|
||||
.. c:enum:: uv_tcp_flags
|
||||
|
||||
Flags used in :c:func:`uv_tcp_bind`.
|
||||
|
||||
::
|
||||
|
||||
enum uv_tcp_flags {
|
||||
/* Used with uv_tcp_bind, when an IPv6 address is used. */
|
||||
UV_TCP_IPV6ONLY = 1,
|
||||
|
||||
/* Enable SO_REUSEPORT socket option when binding the handle.
|
||||
* This allows completely duplicate bindings by multiple processes
|
||||
* or threads if they all set SO_REUSEPORT before binding the port.
|
||||
* Incoming connections are distributed across the participating
|
||||
* listener sockets.
|
||||
*
|
||||
* This flag is available only on Linux 3.9+, DragonFlyBSD 3.6+,
|
||||
* FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ for now.
|
||||
*/
|
||||
UV_TCP_REUSEPORT = 2,
|
||||
};
|
||||
|
||||
|
||||
Public members
|
||||
^^^^^^^^^^^^^^
|
||||
@ -65,6 +87,10 @@ API
|
||||
at the end of this procedure, then the handle is destroyed with a
|
||||
``UV_ETIMEDOUT`` error passed to the corresponding callback.
|
||||
|
||||
If `delay` is less than 1 then ``UV_EINVAL`` is returned.
|
||||
|
||||
.. versionchanged:: 1.49.0 If `delay` is less than 1 then ``UV_EINVAL``` is returned.
|
||||
|
||||
.. c:function:: int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable)
|
||||
|
||||
Enable / disable simultaneous asynchronous accept requests that are
|
||||
@ -77,16 +103,34 @@ API
|
||||
|
||||
.. c:function:: int uv_tcp_bind(uv_tcp_t* handle, const struct sockaddr* addr, unsigned int flags)
|
||||
|
||||
Bind the handle to an address and port. `addr` should point to an
|
||||
initialized ``struct sockaddr_in`` or ``struct sockaddr_in6``.
|
||||
Bind the handle to an address and port.
|
||||
|
||||
When the port is already taken, you can expect to see an ``UV_EADDRINUSE``
|
||||
error from :c:func:`uv_listen` or :c:func:`uv_tcp_connect`. That is,
|
||||
a successful call to this function does not guarantee that the call
|
||||
to :c:func:`uv_listen` or :c:func:`uv_tcp_connect` will succeed as well.
|
||||
error from :c:func:`uv_listen` or :c:func:`uv_tcp_connect` unless you specify
|
||||
``UV_TCP_REUSEPORT`` in `flags` for all the binding sockets. That is, a successful
|
||||
call to this function does not guarantee that the call to :c:func:`uv_listen` or
|
||||
:c:func:`uv_tcp_connect` will succeed as well.
|
||||
|
||||
`flags` can contain ``UV_TCP_IPV6ONLY``, in which case dual-stack support
|
||||
is disabled and only IPv6 is used.
|
||||
:param handle: TCP handle. It should have been initialized with :c:func:`uv_tcp_init`.
|
||||
|
||||
:param addr: Address to bind to. It should point to an initialized ``struct sockaddr_in``
|
||||
or ``struct sockaddr_in6``.
|
||||
|
||||
:param flags: Flags that control the behavior of binding the socket.
|
||||
``UV_TCP_IPV6ONLY`` can be contained in `flags` to disable dual-stack
|
||||
support and only use IPv6.
|
||||
``UV_TCP_REUSEPORT`` can be contained in `flags` to enable the socket option
|
||||
`SO_REUSEPORT` with the capability of load balancing that distribute incoming
|
||||
connections across all listening sockets in multiple processes or threads.
|
||||
|
||||
:returns: 0 on success, or an error code < 0 on failure.
|
||||
|
||||
.. versionchanged:: 1.49.0 added the ``UV_TCP_REUSEPORT`` flag.
|
||||
|
||||
.. note::
|
||||
``UV_TCP_REUSEPORT`` flag is available only on Linux 3.9+, DragonFlyBSD 3.6+,
|
||||
FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ at the moment. On other platforms
|
||||
this function will return an UV_ENOTSUP error.
|
||||
|
||||
.. c:function:: int uv_tcp_getsockname(const uv_tcp_t* handle, struct sockaddr* name, int* namelen)
|
||||
|
||||
|
@ -78,6 +78,14 @@ Threads
|
||||
|
||||
.. versionchanged:: 1.4.1 returns a UV_E* error code on failure
|
||||
|
||||
.. c:function:: int uv_thread_detach(uv_thread_t* tid)
|
||||
|
||||
Detaches a thread. Detached threads automatically release their
|
||||
resources upon termination, eliminating the need for the application to
|
||||
call `uv_thread_join`.
|
||||
|
||||
.. versionadded:: 1.50.0
|
||||
|
||||
.. c:function:: int uv_thread_create_ex(uv_thread_t* tid, const uv_thread_options_t* params, uv_thread_cb entry, void* arg)
|
||||
|
||||
Like :c:func:`uv_thread_create`, but additionally specifies options for creating a new thread.
|
||||
@ -132,6 +140,45 @@ Threads
|
||||
.. c:function:: int uv_thread_join(uv_thread_t *tid)
|
||||
.. c:function:: int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2)
|
||||
|
||||
.. c:function:: int uv_thread_setname(const char* name)
|
||||
|
||||
Sets the name of the current thread. Different platforms define different limits on the max number of characters
|
||||
a thread name can be: Linux, IBM i (16), macOS (64), Windows (32767), and NetBSD (32), etc. `uv_thread_setname()`
|
||||
will truncate it in case `name` is larger than the limit of the platform.
|
||||
|
||||
Not supported on Windows Server 2016, returns `UV_ENOSYS`.
|
||||
|
||||
.. versionadded:: 1.50.0
|
||||
|
||||
.. c:function:: int uv_thread_getname(uv_thread_t* tid, char* name, size_t* size)
|
||||
|
||||
Gets the name of the thread specified by `tid`. The thread name is copied, with the trailing NUL, into the buffer
|
||||
pointed to by `name`. The `size` parameter specifies the size of the buffer pointed to by `name`.
|
||||
The buffer should be large enough to hold the name of the thread plus the trailing NUL, or it will be truncated to fit
|
||||
with the trailing NUL.
|
||||
|
||||
Not supported on Windows Server 2016, returns `UV_ENOSYS`.
|
||||
|
||||
.. versionadded:: 1.50.0
|
||||
|
||||
.. c:function:: int uv_thread_setpriority(uv_thread_t tid, int priority)
|
||||
|
||||
If the function succeeds, the return value is 0.
|
||||
If the function fails, the return value is less than zero.
|
||||
Sets the scheduling priority of the thread specified by tid. It requires elevated
|
||||
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
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
@ -17,6 +17,8 @@ is 1024).
|
||||
.. versionchanged:: 1.45.0 threads now have an 8 MB stack instead of the
|
||||
(sometimes too low) platform default.
|
||||
|
||||
.. versionchanged:: 1.50.0 threads now have a default name of libuv-worker.
|
||||
|
||||
The threadpool is global and shared across all event loops. When a particular
|
||||
function makes use of the threadpool (i.e. when using :c:func:`uv_queue_work`)
|
||||
libuv preallocates and initializes the maximum number of threads allowed by
|
||||
|
@ -6,6 +6,15 @@
|
||||
|
||||
Timer handles are used to schedule callbacks to be called in the future.
|
||||
|
||||
Timers are either single-shot or repeating. Repeating timers do not adjust
|
||||
for overhead but are rearmed relative to the event loop's idea of "now".
|
||||
|
||||
Libuv updates its idea of "now" right before executing timer callbacks, and
|
||||
right after waking up from waiting for I/O. See also :c:func:`uv_update_time`.
|
||||
|
||||
Example: a repeating timer with a 50 ms interval whose callback takes 17 ms
|
||||
to complete, runs again 33 ms later. If other tasks take longer than 33 ms,
|
||||
the timer callback runs as soon as possible.
|
||||
|
||||
Data types
|
||||
----------
|
||||
@ -64,11 +73,6 @@ API
|
||||
duration, and will follow normal timer semantics in the case of a
|
||||
time-slice overrun.
|
||||
|
||||
For example, if a 50ms repeating timer first runs for 17ms, it will be
|
||||
scheduled to run again 33ms later. If other tasks consume more than the
|
||||
33ms following the first timer callback, then the callback will run as soon
|
||||
as possible.
|
||||
|
||||
.. note::
|
||||
If the repeat value is set from a timer callback it does not immediately take effect.
|
||||
If the timer was non-repeating before, it will have been stopped. If it was repeating,
|
||||
|
@ -27,10 +27,15 @@ Data types
|
||||
typedef enum {
|
||||
/* Initial/normal terminal mode */
|
||||
UV_TTY_MODE_NORMAL,
|
||||
/* Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled) */
|
||||
/*
|
||||
* Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled).
|
||||
* May become equivalent to UV_TTY_MODE_RAW_VT in future libuv versions.
|
||||
*/
|
||||
UV_TTY_MODE_RAW,
|
||||
/* Binary-safe I/O mode for IPC (Unix-only) */
|
||||
UV_TTY_MODE_IO
|
||||
UV_TTY_MODE_IO,
|
||||
/* Raw input mode. On Windows ENABLE_VIRTUAL_TERMINAL_INPUT is also set. */
|
||||
UV_TTY_MODE_RAW_VT
|
||||
} uv_tty_mode_t;
|
||||
|
||||
.. c:enum:: uv_tty_vtermstate_t
|
||||
@ -98,7 +103,7 @@ API
|
||||
.. c:function:: int uv_tty_set_mode(uv_tty_t* handle, uv_tty_mode_t mode)
|
||||
|
||||
.. versionchanged:: 1.2.0: the mode is specified as a
|
||||
:c:type:`uv_tty_mode_t` value.
|
||||
:c:enum:`uv_tty_mode_t` value.
|
||||
|
||||
Set the TTY using the specified terminal mode.
|
||||
|
||||
|
@ -18,7 +18,7 @@ Data types
|
||||
|
||||
UDP send request type.
|
||||
|
||||
.. c:type:: uv_udp_flags
|
||||
.. c:enum:: uv_udp_flags
|
||||
|
||||
Flags used in :c:func:`uv_udp_bind` and :c:type:`uv_udp_recv_cb`..
|
||||
|
||||
@ -28,19 +28,21 @@ Data types
|
||||
/* Disables dual stack mode. */
|
||||
UV_UDP_IPV6ONLY = 1,
|
||||
/*
|
||||
* Indicates message was truncated because read buffer was too small. The
|
||||
* remainder was discarded by the OS. Used in uv_udp_recv_cb.
|
||||
*/
|
||||
* Indicates message was truncated because read buffer was too small. The
|
||||
* remainder was discarded by the OS. Used in uv_udp_recv_cb.
|
||||
*/
|
||||
UV_UDP_PARTIAL = 2,
|
||||
/*
|
||||
* Indicates if SO_REUSEADDR will be set when binding the handle in
|
||||
* uv_udp_bind.
|
||||
* This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other
|
||||
* Unix platforms, it sets the SO_REUSEADDR flag. What that means is that
|
||||
* multiple threads or processes can bind to the same address without error
|
||||
* (provided they all set the flag) but only the last one to bind will receive
|
||||
* any traffic, in effect "stealing" the port from the previous listener.
|
||||
*/
|
||||
* Indicates if SO_REUSEADDR will be set when binding the handle.
|
||||
* This sets the SO_REUSEPORT socket flag on the BSDs (except for
|
||||
* DragonFlyBSD), OS X, and other platforms where SO_REUSEPORTs don't
|
||||
* have the capability of load balancing, as the opposite of what
|
||||
* UV_UDP_REUSEPORT would do. On other Unix platforms, it sets the
|
||||
* SO_REUSEADDR flag. What that means is that multiple threads or
|
||||
* processes can bind to the same address without error (provided
|
||||
* they all set the flag) but only the last one to bind will receive
|
||||
* any traffic, in effect "stealing" the port from the previous listener.
|
||||
*/
|
||||
UV_UDP_REUSEADDR = 4,
|
||||
/*
|
||||
* Indicates that the message was received by recvmmsg, so the buffer provided
|
||||
@ -62,8 +64,20 @@ Data types
|
||||
*/
|
||||
UV_UDP_LINUX_RECVERR = 32,
|
||||
/*
|
||||
* Indicates that recvmmsg should be used, if available.
|
||||
*/
|
||||
* Indicates if SO_REUSEPORT will be set when binding the handle.
|
||||
* This sets the SO_REUSEPORT socket option on supported platforms.
|
||||
* Unlike UV_UDP_REUSEADDR, this flag will make multiple threads or
|
||||
* processes that are binding to the same address and port "share"
|
||||
* the port, which means incoming datagrams are distributed across
|
||||
* the receiving sockets among threads or processes.
|
||||
*
|
||||
* This flag is available only on Linux 3.9+, DragonFlyBSD 3.6+,
|
||||
* FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ for now.
|
||||
*/
|
||||
UV_UDP_REUSEPORT = 64,
|
||||
/*
|
||||
* Indicates that recvmmsg should be used, if available.
|
||||
*/
|
||||
UV_UDP_RECVMMSG = 256
|
||||
};
|
||||
|
||||
@ -186,11 +200,24 @@ API
|
||||
with the address and port to bind to.
|
||||
|
||||
:param flags: Indicate how the socket will be bound,
|
||||
``UV_UDP_IPV6ONLY``, ``UV_UDP_REUSEADDR``, and ``UV_UDP_RECVERR``
|
||||
are supported.
|
||||
``UV_UDP_IPV6ONLY``, ``UV_UDP_REUSEADDR``, ``UV_UDP_REUSEPORT``,
|
||||
and ``UV_UDP_RECVERR`` are supported.
|
||||
|
||||
:returns: 0 on success, or an error code < 0 on failure.
|
||||
|
||||
.. versionchanged:: 1.49.0 added the ``UV_UDP_REUSEPORT`` flag.
|
||||
|
||||
.. note::
|
||||
``UV_UDP_REUSEPORT`` flag is available only on Linux 3.9+, DragonFlyBSD 3.6+,
|
||||
FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ at the moment. On other platforms
|
||||
this function will return an UV_ENOTSUP error.
|
||||
For platforms where `SO_REUSEPORT`s have the capability of load balancing,
|
||||
specifying both ``UV_UDP_REUSEADDR`` and ``UV_UDP_REUSEPORT`` in flags is allowed
|
||||
and `SO_REUSEPORT` will always override the behavior of `SO_REUSEADDR`.
|
||||
For platforms where `SO_REUSEPORT`s don't have the capability of load balancing,
|
||||
specifying both ``UV_UDP_REUSEADDR`` and ``UV_UDP_REUSEPORT`` in flags will fail,
|
||||
returning an UV_ENOTSUP error.
|
||||
|
||||
.. c:function:: int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr)
|
||||
|
||||
Associate the UDP handle to a remote address and port, so every
|
||||
@ -285,7 +312,9 @@ API
|
||||
local sockets.
|
||||
|
||||
:param handle: UDP handle. Should have been initialized with
|
||||
:c:func:`uv_udp_init`.
|
||||
:c:func:`uv_udp_init_ex` as either ``AF_INET`` or ``AF_INET6``, or have
|
||||
been bound to an address explicitly with :c:func:`uv_udp_bind`, or
|
||||
implicitly with :c:func:`uv_udp_send()` or :c:func:`uv_udp_recv_start`.
|
||||
|
||||
:param on: 1 for on, 0 for off.
|
||||
|
||||
@ -296,7 +325,9 @@ API
|
||||
Set the multicast ttl.
|
||||
|
||||
:param handle: UDP handle. Should have been initialized with
|
||||
:c:func:`uv_udp_init`.
|
||||
:c:func:`uv_udp_init_ex` as either ``AF_INET`` or ``AF_INET6``, or have
|
||||
been bound to an address explicitly with :c:func:`uv_udp_bind`, or
|
||||
implicitly with :c:func:`uv_udp_send()` or :c:func:`uv_udp_recv_start`.
|
||||
|
||||
:param ttl: 1 through 255.
|
||||
|
||||
@ -307,7 +338,9 @@ API
|
||||
Set the multicast interface to send or receive data on.
|
||||
|
||||
:param handle: UDP handle. Should have been initialized with
|
||||
:c:func:`uv_udp_init`.
|
||||
:c:func:`uv_udp_init_ex` as either ``AF_INET`` or ``AF_INET6``, or have
|
||||
been bound to an address explicitly with :c:func:`uv_udp_bind`, or
|
||||
implicitly with :c:func:`uv_udp_send()` or :c:func:`uv_udp_recv_start`.
|
||||
|
||||
:param interface_addr: interface address.
|
||||
|
||||
@ -318,7 +351,9 @@ API
|
||||
Set broadcast on or off.
|
||||
|
||||
:param handle: UDP handle. Should have been initialized with
|
||||
:c:func:`uv_udp_init`.
|
||||
:c:func:`uv_udp_init_ex` as either ``AF_INET`` or ``AF_INET6``, or have
|
||||
been bound to an address explicitly with :c:func:`uv_udp_bind`, or
|
||||
implicitly with :c:func:`uv_udp_send()` or :c:func:`uv_udp_recv_start`.
|
||||
|
||||
:param on: 1 for on, 0 for off.
|
||||
|
||||
@ -329,7 +364,9 @@ API
|
||||
Set the time to live.
|
||||
|
||||
:param handle: UDP handle. Should have been initialized with
|
||||
:c:func:`uv_udp_init`.
|
||||
:c:func:`uv_udp_init_ex` as either ``AF_INET`` or ``AF_INET6``, or have
|
||||
been bound to an address explicitly with :c:func:`uv_udp_bind`, or
|
||||
implicitly with :c:func:`uv_udp_send()` or :c:func:`uv_udp_recv_start`.
|
||||
|
||||
:param ttl: 1 through 255.
|
||||
|
||||
@ -389,6 +426,20 @@ API
|
||||
|
||||
.. versionchanged:: 1.27.0 added support for connected sockets
|
||||
|
||||
.. c:function:: int uv_udp_try_send2(uv_udp_t* handle, unsigned int count, uv_buf_t* bufs[/*count*/], unsigned int nbufs[/*count*/], struct sockaddr* addrs[/*count*/], unsigned int flags)
|
||||
|
||||
Like :c:func:`uv_udp_try_send`, but can send multiple datagrams.
|
||||
Lightweight abstraction around :man:`sendmmsg(2)`, with a :man:`sendmsg(2)`
|
||||
fallback loop for platforms that do not support the former. The handle must
|
||||
be fully initialized; call c:func:`uv_udp_bind` first.
|
||||
|
||||
:returns: >= 0: number of datagrams sent. Zero only if `count` was zero.
|
||||
< 0: negative error code. Only if sending the first datagram fails,
|
||||
otherwise returns a positive send count. ``UV_EAGAIN`` when datagrams
|
||||
cannot be sent right now; fall back to :c:func:`uv_udp_send`.
|
||||
|
||||
.. versionadded:: 1.50.0
|
||||
|
||||
.. c:function:: int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb)
|
||||
|
||||
Prepare for receiving data. If the socket has not previously been bound
|
||||
|
106
include/uv.h
106
include/uv.h
@ -58,6 +58,7 @@ extern "C" {
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
|
||||
/* Internal type, do not use. */
|
||||
struct uv__queue {
|
||||
@ -157,6 +158,7 @@ struct uv__queue {
|
||||
XX(ESOCKTNOSUPPORT, "socket type not supported") \
|
||||
XX(ENODATA, "no data available") \
|
||||
XX(EUNATCH, "protocol driver not attached") \
|
||||
XX(ENOEXEC, "exec format error") \
|
||||
|
||||
#define UV_HANDLE_TYPE_MAP(XX) \
|
||||
XX(ASYNC, async) \
|
||||
@ -260,7 +262,9 @@ typedef struct uv_metrics_s uv_metrics_t;
|
||||
|
||||
typedef enum {
|
||||
UV_LOOP_BLOCK_SIGNAL = 0,
|
||||
UV_METRICS_IDLE_TIME
|
||||
UV_METRICS_IDLE_TIME,
|
||||
UV_LOOP_USE_IO_URING_SQPOLL
|
||||
#define UV_LOOP_USE_IO_URING_SQPOLL UV_LOOP_USE_IO_URING_SQPOLL
|
||||
} uv_loop_option;
|
||||
|
||||
typedef enum {
|
||||
@ -604,7 +608,18 @@ UV_EXTERN int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable);
|
||||
|
||||
enum uv_tcp_flags {
|
||||
/* Used with uv_tcp_bind, when an IPv6 address is used. */
|
||||
UV_TCP_IPV6ONLY = 1
|
||||
UV_TCP_IPV6ONLY = 1,
|
||||
|
||||
/* Enable SO_REUSEPORT socket option when binding the handle.
|
||||
* This allows completely duplicate bindings by multiple processes
|
||||
* or threads if they all set SO_REUSEPORT before binding the port.
|
||||
* Incoming connections are distributed across the participating
|
||||
* listener sockets.
|
||||
*
|
||||
* This flag is available only on Linux 3.9+, DragonFlyBSD 3.6+,
|
||||
* FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ for now.
|
||||
*/
|
||||
UV_TCP_REUSEPORT = 2,
|
||||
};
|
||||
|
||||
UV_EXTERN int uv_tcp_bind(uv_tcp_t* handle,
|
||||
@ -645,10 +660,13 @@ enum uv_udp_flags {
|
||||
UV_UDP_PARTIAL = 2,
|
||||
/*
|
||||
* Indicates if SO_REUSEADDR will be set when binding the handle.
|
||||
* This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other
|
||||
* Unix platforms, it sets the SO_REUSEADDR flag. What that means is that
|
||||
* multiple threads or processes can bind to the same address without error
|
||||
* (provided they all set the flag) but only the last one to bind will receive
|
||||
* This sets the SO_REUSEPORT socket flag on the BSDs (except for
|
||||
* DragonFlyBSD), OS X, and other platforms where SO_REUSEPORTs don't
|
||||
* have the capability of load balancing, as the opposite of what
|
||||
* UV_UDP_REUSEPORT would do. On other Unix platforms, it sets the
|
||||
* SO_REUSEADDR flag. What that means is that multiple threads or
|
||||
* processes can bind to the same address without error (provided
|
||||
* they all set the flag) but only the last one to bind will receive
|
||||
* any traffic, in effect "stealing" the port from the previous listener.
|
||||
*/
|
||||
UV_UDP_REUSEADDR = 4,
|
||||
@ -671,6 +689,18 @@ enum uv_udp_flags {
|
||||
* This flag is no-op on platforms other than Linux.
|
||||
*/
|
||||
UV_UDP_LINUX_RECVERR = 32,
|
||||
/*
|
||||
* Indicates if SO_REUSEPORT will be set when binding the handle.
|
||||
* This sets the SO_REUSEPORT socket option on supported platforms.
|
||||
* Unlike UV_UDP_REUSEADDR, this flag will make multiple threads or
|
||||
* processes that are binding to the same address and port "share"
|
||||
* the port, which means incoming datagrams are distributed across
|
||||
* the receiving sockets among threads or processes.
|
||||
*
|
||||
* This flag is available only on Linux 3.9+, DragonFlyBSD 3.6+,
|
||||
* FreeBSD 12.0+, Solaris 11.4, and AIX 7.2.5+ for now.
|
||||
*/
|
||||
UV_UDP_REUSEPORT = 64,
|
||||
/*
|
||||
* Indicates that recvmmsg should be used, if available.
|
||||
*/
|
||||
@ -747,6 +777,12 @@ UV_EXTERN int uv_udp_try_send(uv_udp_t* handle,
|
||||
const uv_buf_t bufs[],
|
||||
unsigned int nbufs,
|
||||
const struct sockaddr* addr);
|
||||
UV_EXTERN int uv_udp_try_send2(uv_udp_t* handle,
|
||||
unsigned int count,
|
||||
uv_buf_t* bufs[/*count*/],
|
||||
unsigned int nbufs[/*count*/],
|
||||
struct sockaddr* addrs[/*count*/],
|
||||
unsigned int flags);
|
||||
UV_EXTERN int uv_udp_recv_start(uv_udp_t* handle,
|
||||
uv_alloc_cb alloc_cb,
|
||||
uv_udp_recv_cb recv_cb);
|
||||
@ -770,10 +806,15 @@ struct uv_tty_s {
|
||||
typedef enum {
|
||||
/* Initial/normal terminal mode */
|
||||
UV_TTY_MODE_NORMAL,
|
||||
/* Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled) */
|
||||
/*
|
||||
* Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled).
|
||||
* May become equivalent to UV_TTY_MODE_RAW_VT in future libuv versions.
|
||||
*/
|
||||
UV_TTY_MODE_RAW,
|
||||
/* Binary-safe I/O mode for IPC (Unix-only) */
|
||||
UV_TTY_MODE_IO
|
||||
UV_TTY_MODE_IO,
|
||||
/* Raw input mode. On Windows ENABLE_VIRTUAL_TERMINAL_INPUT is also set. */
|
||||
UV_TTY_MODE_RAW_VT
|
||||
} uv_tty_mode_t;
|
||||
|
||||
typedef enum {
|
||||
@ -1106,7 +1147,14 @@ enum uv_process_flags {
|
||||
* option is only meaningful on Windows systems. On Unix it is silently
|
||||
* 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_EXTERN int uv_getrusage(uv_rusage_t* rusage);
|
||||
UV_EXTERN int uv_getrusage_thread(uv_rusage_t* rusage);
|
||||
|
||||
UV_EXTERN int uv_os_homedir(char* buffer, size_t* size);
|
||||
UV_EXTERN int uv_os_tmpdir(char* buffer, size_t* size);
|
||||
@ -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_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 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);
|
||||
@ -1531,6 +1591,8 @@ UV_EXTERN int uv_fs_chmod(uv_loop_t* loop,
|
||||
const char* path,
|
||||
int mode,
|
||||
uv_fs_cb cb);
|
||||
#define UV_FS_UTIME_NOW (INFINITY)
|
||||
#define UV_FS_UTIME_OMIT (NAN)
|
||||
UV_EXTERN int uv_fs_utime(uv_loop_t* loop,
|
||||
uv_fs_t* req,
|
||||
const char* path,
|
||||
@ -1823,6 +1885,7 @@ UV_EXTERN int uv_gettimeofday(uv_timeval64_t* tv);
|
||||
typedef void (*uv_thread_cb)(void* arg);
|
||||
|
||||
UV_EXTERN int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg);
|
||||
UV_EXTERN int uv_thread_detach(uv_thread_t* tid);
|
||||
|
||||
typedef enum {
|
||||
UV_THREAD_NO_FLAGS = 0x00,
|
||||
@ -1852,6 +1915,9 @@ UV_EXTERN int uv_thread_getcpu(void);
|
||||
UV_EXTERN uv_thread_t uv_thread_self(void);
|
||||
UV_EXTERN int uv_thread_join(uv_thread_t *tid);
|
||||
UV_EXTERN int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2);
|
||||
UV_EXTERN int uv_thread_setname(const char* name);
|
||||
UV_EXTERN int uv_thread_getname(uv_thread_t* tid, char* name, size_t size);
|
||||
|
||||
|
||||
/* The presence of these unions force similar struct layout. */
|
||||
#define XX(_, name) uv_ ## name ## _t name;
|
||||
@ -1885,17 +1951,17 @@ struct uv_loop_s {
|
||||
UV_EXTERN void* uv_loop_get_data(const uv_loop_t*);
|
||||
UV_EXTERN void uv_loop_set_data(uv_loop_t*, void* data);
|
||||
|
||||
/* String utilities needed internally for dealing with Windows. */
|
||||
size_t uv_utf16_length_as_wtf8(const uint16_t* utf16,
|
||||
ssize_t utf16_len);
|
||||
int uv_utf16_to_wtf8(const uint16_t* utf16,
|
||||
ssize_t utf16_len,
|
||||
char** wtf8_ptr,
|
||||
size_t* wtf8_len_ptr);
|
||||
ssize_t uv_wtf8_length_as_utf16(const char* wtf8);
|
||||
void uv_wtf8_to_utf16(const char* wtf8,
|
||||
uint16_t* utf16,
|
||||
size_t utf16_len);
|
||||
/* Unicode utilities needed for dealing with Windows. */
|
||||
UV_EXTERN size_t uv_utf16_length_as_wtf8(const uint16_t* utf16,
|
||||
ssize_t utf16_len);
|
||||
UV_EXTERN int uv_utf16_to_wtf8(const uint16_t* utf16,
|
||||
ssize_t utf16_len,
|
||||
char** wtf8_ptr,
|
||||
size_t* wtf8_len_ptr);
|
||||
UV_EXTERN ssize_t uv_wtf8_length_as_utf16(const char* wtf8);
|
||||
UV_EXTERN void uv_wtf8_to_utf16(const char* wtf8,
|
||||
uint16_t* utf16,
|
||||
size_t utf16_len);
|
||||
|
||||
/* Don't export the private CPP symbols. */
|
||||
#undef UV_HANDLE_TYPE_PRIVATE
|
||||
|
@ -474,4 +474,10 @@
|
||||
# define UV__EUNATCH (-4023)
|
||||
#endif
|
||||
|
||||
#if defined(ENOEXEC) && !defined(_WIN32)
|
||||
# define UV__ENOEXEC UV__ERR(ENOEXEC)
|
||||
#else
|
||||
# define UV__ENOEXEC (-4022)
|
||||
#endif
|
||||
|
||||
#endif /* UV_ERRNO_H_ */
|
||||
|
@ -35,21 +35,7 @@
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This file defines data structures for different types of trees:
|
||||
* splay trees and red-black trees.
|
||||
*
|
||||
* A splay tree is a self-organizing data structure. Every operation
|
||||
* on the tree causes a splay to happen. The splay moves the requested
|
||||
* node to the root of the tree and partly rebalances it.
|
||||
*
|
||||
* This has the benefit that request locality causes faster lookups as
|
||||
* the requested nodes move to the top of the tree. On the other hand,
|
||||
* every lookup causes memory writes.
|
||||
*
|
||||
* The Balance Theorem bounds the total access time for m operations
|
||||
* and n inserts on an initially empty tree as O((m + n)lg n). The
|
||||
* amortized cost for a sequence of m accesses to a splay tree is O(lg n);
|
||||
*
|
||||
* This file defines data structures for red-black trees.
|
||||
* A red-black tree is a binary search tree with the node color as an
|
||||
* extra attribute. It fulfills a set of conditions:
|
||||
* - every search path from the root to a leaf consists of the
|
||||
@ -61,239 +47,6 @@
|
||||
* The maximum height of a red-black tree is 2lg (n+1).
|
||||
*/
|
||||
|
||||
#define SPLAY_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sph_root; /* root of the tree */ \
|
||||
}
|
||||
|
||||
#define SPLAY_INITIALIZER(root) \
|
||||
{ NULL }
|
||||
|
||||
#define SPLAY_INIT(root) do { \
|
||||
(root)->sph_root = NULL; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *spe_left; /* left element */ \
|
||||
struct type *spe_right; /* right element */ \
|
||||
}
|
||||
|
||||
#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
|
||||
#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
|
||||
#define SPLAY_ROOT(head) (head)->sph_root
|
||||
#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
|
||||
|
||||
/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
|
||||
#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
|
||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
|
||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
||||
(head)->sph_root = tmp; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
|
||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
||||
(head)->sph_root = tmp; \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_LINKLEFT(head, tmp, field) do { \
|
||||
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
|
||||
tmp = (head)->sph_root; \
|
||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_LINKRIGHT(head, tmp, field) do { \
|
||||
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
|
||||
tmp = (head)->sph_root; \
|
||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
|
||||
SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
|
||||
SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
|
||||
} while (/*CONSTCOND*/ 0)
|
||||
|
||||
/* Generates prototypes and inline functions */
|
||||
|
||||
#define SPLAY_PROTOTYPE(name, type, field, cmp) \
|
||||
void name##_SPLAY(struct name *, struct type *); \
|
||||
void name##_SPLAY_MINMAX(struct name *, int); \
|
||||
struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
|
||||
struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
|
||||
\
|
||||
/* Finds the node with the same key as elm */ \
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_FIND(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
if (SPLAY_EMPTY(head)) \
|
||||
return(NULL); \
|
||||
name##_SPLAY(head, elm); \
|
||||
if ((cmp)(elm, (head)->sph_root) == 0) \
|
||||
return (head->sph_root); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_NEXT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
name##_SPLAY(head, elm); \
|
||||
if (SPLAY_RIGHT(elm, field) != NULL) { \
|
||||
elm = SPLAY_RIGHT(elm, field); \
|
||||
while (SPLAY_LEFT(elm, field) != NULL) { \
|
||||
elm = SPLAY_LEFT(elm, field); \
|
||||
} \
|
||||
} else \
|
||||
elm = NULL; \
|
||||
return (elm); \
|
||||
} \
|
||||
\
|
||||
static __inline struct type * \
|
||||
name##_SPLAY_MIN_MAX(struct name *head, int val) \
|
||||
{ \
|
||||
name##_SPLAY_MINMAX(head, val); \
|
||||
return (SPLAY_ROOT(head)); \
|
||||
}
|
||||
|
||||
/* Main splay operation.
|
||||
* Moves node close to the key of elm to top
|
||||
*/
|
||||
#define SPLAY_GENERATE(name, type, field, cmp) \
|
||||
struct type * \
|
||||
name##_SPLAY_INSERT(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
if (SPLAY_EMPTY(head)) { \
|
||||
SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
|
||||
} else { \
|
||||
int __comp; \
|
||||
name##_SPLAY(head, elm); \
|
||||
__comp = (cmp)(elm, (head)->sph_root); \
|
||||
if(__comp < 0) { \
|
||||
SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field); \
|
||||
SPLAY_RIGHT(elm, field) = (head)->sph_root; \
|
||||
SPLAY_LEFT((head)->sph_root, field) = NULL; \
|
||||
} else if (__comp > 0) { \
|
||||
SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
SPLAY_LEFT(elm, field) = (head)->sph_root; \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = NULL; \
|
||||
} else \
|
||||
return ((head)->sph_root); \
|
||||
} \
|
||||
(head)->sph_root = (elm); \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
struct type * \
|
||||
name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type *__tmp; \
|
||||
if (SPLAY_EMPTY(head)) \
|
||||
return (NULL); \
|
||||
name##_SPLAY(head, elm); \
|
||||
if ((cmp)(elm, (head)->sph_root) == 0) { \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
|
||||
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
} else { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
|
||||
name##_SPLAY(head, elm); \
|
||||
SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
|
||||
} \
|
||||
return (elm); \
|
||||
} \
|
||||
return (NULL); \
|
||||
} \
|
||||
\
|
||||
void \
|
||||
name##_SPLAY(struct name *head, struct type *elm) \
|
||||
{ \
|
||||
struct type __node, *__left, *__right, *__tmp; \
|
||||
int __comp; \
|
||||
\
|
||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \
|
||||
__left = __right = &__node; \
|
||||
\
|
||||
while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \
|
||||
if (__comp < 0) { \
|
||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if ((cmp)(elm, __tmp) < 0){ \
|
||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL) \
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKLEFT(head, __right, field); \
|
||||
} else if (__comp > 0) { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if ((cmp)(elm, __tmp) > 0){ \
|
||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKRIGHT(head, __left, field); \
|
||||
} \
|
||||
} \
|
||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
||||
} \
|
||||
\
|
||||
/* Splay with either the minimum or the maximum element \
|
||||
* Used to find minimum or maximum element in tree. \
|
||||
*/ \
|
||||
void name##_SPLAY_MINMAX(struct name *head, int __comp) \
|
||||
{ \
|
||||
struct type __node, *__left, *__right, *__tmp; \
|
||||
\
|
||||
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \
|
||||
__left = __right = &__node; \
|
||||
\
|
||||
for (;;) { \
|
||||
if (__comp < 0) { \
|
||||
__tmp = SPLAY_LEFT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if (__comp < 0){ \
|
||||
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
|
||||
if (SPLAY_LEFT((head)->sph_root, field) == NULL) \
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKLEFT(head, __right, field); \
|
||||
} else if (__comp > 0) { \
|
||||
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
|
||||
if (__tmp == NULL) \
|
||||
break; \
|
||||
if (__comp > 0) { \
|
||||
SPLAY_ROTATE_LEFT(head, __tmp, field); \
|
||||
if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \
|
||||
break; \
|
||||
} \
|
||||
SPLAY_LINKRIGHT(head, __left, field); \
|
||||
} \
|
||||
} \
|
||||
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
|
||||
}
|
||||
|
||||
#define SPLAY_NEGINF -1
|
||||
#define SPLAY_INF 1
|
||||
|
||||
#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
|
||||
#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
|
||||
#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
|
||||
#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
|
||||
#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
|
||||
: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
|
||||
#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
|
||||
: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
|
||||
|
||||
#define SPLAY_FOREACH(x, name, head) \
|
||||
for ((x) = SPLAY_MIN(name, head); \
|
||||
(x) != NULL; \
|
||||
(x) = SPLAY_NEXT(name, head, x))
|
||||
|
||||
/* Macros that define a red-black tree */
|
||||
#define RB_HEAD(name, type) \
|
||||
struct name { \
|
||||
@ -730,8 +483,8 @@ name##_RB_MINMAX(struct name *head, int val) \
|
||||
#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
|
||||
#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
|
||||
#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y)
|
||||
#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
|
||||
#define RB_PREV(name, x, y) name##_RB_PREV(y)
|
||||
#define RB_NEXT(name, x) name##_RB_NEXT(x)
|
||||
#define RB_PREV(name, x) name##_RB_PREV(x)
|
||||
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
|
||||
#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
|
||||
|
||||
|
@ -271,7 +271,10 @@ typedef struct {
|
||||
|
||||
#define UV_UDP_SEND_PRIVATE_FIELDS \
|
||||
struct uv__queue queue; \
|
||||
struct sockaddr_storage addr; \
|
||||
union { \
|
||||
struct sockaddr addr; \
|
||||
struct sockaddr_storage storage; \
|
||||
} u; \
|
||||
unsigned int nbufs; \
|
||||
uv_buf_t* bufs; \
|
||||
ssize_t status; \
|
||||
@ -328,7 +331,10 @@ typedef struct {
|
||||
|
||||
#define UV_TIMER_PRIVATE_FIELDS \
|
||||
uv_timer_cb timer_cb; \
|
||||
void* heap_node[3]; \
|
||||
union { \
|
||||
void* heap[3]; \
|
||||
struct uv__queue queue; \
|
||||
} node; \
|
||||
uint64_t timeout; \
|
||||
uint64_t repeat; \
|
||||
uint64_t start_id;
|
||||
|
@ -31,10 +31,10 @@
|
||||
*/
|
||||
|
||||
#define UV_VERSION_MAJOR 1
|
||||
#define UV_VERSION_MINOR 47
|
||||
#define UV_VERSION_PATCH 0
|
||||
#define UV_VERSION_IS_RELEASE 1
|
||||
#define UV_VERSION_SUFFIX ""
|
||||
#define UV_VERSION_MINOR 50
|
||||
#define UV_VERSION_PATCH 1
|
||||
#define UV_VERSION_IS_RELEASE 0
|
||||
#define UV_VERSION_SUFFIX "dev"
|
||||
|
||||
#define UV_VERSION_HEX ((UV_VERSION_MAJOR << 16) | \
|
||||
(UV_VERSION_MINOR << 8) | \
|
||||
|
@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
#ifndef _WIN32_WINNT
|
||||
# define _WIN32_WINNT 0x0600
|
||||
# define _WIN32_WINNT 0x0A00
|
||||
#endif
|
||||
|
||||
#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED)
|
||||
@ -32,20 +32,12 @@ typedef intptr_t ssize_t;
|
||||
|
||||
#include <winsock2.h>
|
||||
|
||||
#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
|
||||
typedef struct pollfd {
|
||||
SOCKET fd;
|
||||
short events;
|
||||
short revents;
|
||||
} WSAPOLLFD, *PWSAPOLLFD, *LPWSAPOLLFD;
|
||||
#endif
|
||||
|
||||
#ifndef LOCALE_INVARIANT
|
||||
# define LOCALE_INVARIANT 0x007f
|
||||
#endif
|
||||
|
||||
#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 PTCP_INITIAL_RTO_PARAMETERS PTCP_INITIAL_RTO_PARAMETERS__AVOID
|
||||
@ -70,7 +62,7 @@ typedef struct pollfd {
|
||||
# define S_IFLNK 0xA000
|
||||
#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)
|
||||
# define S_IFIFO _S_IFIFO
|
||||
#endif
|
||||
@ -290,8 +282,8 @@ typedef struct {
|
||||
#define UV_ONCE_INIT { 0, NULL }
|
||||
|
||||
typedef struct uv_once_s {
|
||||
unsigned char ran;
|
||||
HANDLE event;
|
||||
unsigned char unused;
|
||||
INIT_ONCE init_once;
|
||||
} uv_once_t;
|
||||
|
||||
/* Platform-specific definitions for uv_spawn support. */
|
||||
@ -507,8 +499,11 @@ typedef struct {
|
||||
union { \
|
||||
struct { \
|
||||
/* Used for readable TTY handles */ \
|
||||
/* TODO: remove me in v2.x. */ \
|
||||
HANDLE unused_; \
|
||||
union { \
|
||||
/* TODO: remove me in v2.x. */ \
|
||||
HANDLE unused_; \
|
||||
int mode; \
|
||||
} mode; \
|
||||
uv_buf_t read_line_buffer; \
|
||||
HANDLE read_raw_wait; \
|
||||
/* Fields used for translating win keystrokes into vt100 characters */ \
|
||||
@ -550,7 +545,10 @@ typedef struct {
|
||||
unsigned char events;
|
||||
|
||||
#define UV_TIMER_PRIVATE_FIELDS \
|
||||
void* heap_node[3]; \
|
||||
union { \
|
||||
void* heap[3]; \
|
||||
struct uv__queue queue; \
|
||||
} node; \
|
||||
int unused; \
|
||||
uint64_t timeout; \
|
||||
uint64_t repeat; \
|
||||
|
@ -51,7 +51,7 @@ struct poll_ctx {
|
||||
static int statbuf_eq(const uv_stat_t* a, const uv_stat_t* b);
|
||||
static void poll_cb(uv_fs_t* req);
|
||||
static void timer_cb(uv_timer_t* timer);
|
||||
static void timer_close_cb(uv_handle_t* handle);
|
||||
static void timer_close_cb(uv_handle_t* timer);
|
||||
|
||||
static uv_stat_t zero_statbuf;
|
||||
|
||||
@ -139,6 +139,9 @@ int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buffer, size_t* size) {
|
||||
struct poll_ctx* ctx;
|
||||
size_t required_len;
|
||||
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (!uv_is_active((uv_handle_t*)handle)) {
|
||||
*size = 0;
|
||||
return UV_EINVAL;
|
||||
|
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;
|
||||
int rc;
|
||||
|
||||
if (s == se)
|
||||
return UV_EINVAL;
|
||||
|
||||
ds = d;
|
||||
|
||||
si = s;
|
||||
@ -356,9 +359,10 @@ ssize_t uv__idna_toascii(const char* s, const char* se, char* d, char* de) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (d < de)
|
||||
*d++ = '\0';
|
||||
if (d >= de)
|
||||
return UV_EINVAL;
|
||||
|
||||
*d++ = '\0';
|
||||
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);
|
||||
/* uv_wtf8_length_as_utf16 should have been called and checked first. */
|
||||
assert(code_point >= 0);
|
||||
if (code_point > 0x10000) {
|
||||
if (code_point > 0xFFFF) {
|
||||
assert(code_point < 0x10FFFF);
|
||||
*w_target++ = (((code_point - 0x10000) >> 10) + 0xD800);
|
||||
*w_target++ = ((code_point - 0x10000) & 0x3FF) + 0xDC00;
|
||||
@ -400,6 +404,7 @@ void uv_wtf8_to_utf16(const char* source_ptr,
|
||||
}
|
||||
} while (*source_ptr++);
|
||||
|
||||
(void)w_target_len;
|
||||
assert(w_target_len == 0);
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ static void uv__random_done(struct uv__work* w, int status) {
|
||||
uv_random_t* req;
|
||||
|
||||
req = container_of(w, uv_random_t, work_req);
|
||||
uv__req_unregister(req->loop, req);
|
||||
uv__req_unregister(req->loop);
|
||||
|
||||
if (status == 0)
|
||||
status = req->status;
|
||||
|
@ -59,6 +59,7 @@ static void worker(void* arg) {
|
||||
struct uv__queue* q;
|
||||
int is_slow_work;
|
||||
|
||||
uv_thread_setname("libuv-worker");
|
||||
uv_sem_post((uv_sem_t*) arg);
|
||||
arg = NULL;
|
||||
|
||||
@ -356,7 +357,7 @@ static void uv__queue_done(struct uv__work* w, int err) {
|
||||
uv_work_t* req;
|
||||
|
||||
req = container_of(w, uv_work_t, work_req);
|
||||
uv__req_unregister(req->loop, req);
|
||||
uv__req_unregister(req->loop);
|
||||
|
||||
if (req->after_work_cb == NULL)
|
||||
return;
|
||||
|
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* b;
|
||||
|
||||
a = container_of(ha, uv_timer_t, heap_node);
|
||||
b = container_of(hb, uv_timer_t, heap_node);
|
||||
a = container_of(ha, uv_timer_t, node.heap);
|
||||
b = container_of(hb, uv_timer_t, node.heap);
|
||||
|
||||
if (a->timeout < b->timeout)
|
||||
return 1;
|
||||
@ -60,6 +60,7 @@ int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) {
|
||||
handle->timer_cb = NULL;
|
||||
handle->timeout = 0;
|
||||
handle->repeat = 0;
|
||||
uv__queue_init(&handle->node.queue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -73,8 +74,7 @@ int uv_timer_start(uv_timer_t* handle,
|
||||
if (uv__is_closing(handle) || cb == NULL)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (uv__is_active(handle))
|
||||
uv_timer_stop(handle);
|
||||
uv_timer_stop(handle);
|
||||
|
||||
clamped_timeout = handle->loop->time + timeout;
|
||||
if (clamped_timeout < timeout)
|
||||
@ -87,7 +87,7 @@ int uv_timer_start(uv_timer_t* handle,
|
||||
handle->start_id = handle->loop->timer_counter++;
|
||||
|
||||
heap_insert(timer_heap(handle->loop),
|
||||
(struct heap_node*) &handle->heap_node,
|
||||
(struct heap_node*) &handle->node.heap,
|
||||
timer_less_than);
|
||||
uv__handle_start(handle);
|
||||
|
||||
@ -96,14 +96,16 @@ int uv_timer_start(uv_timer_t* handle,
|
||||
|
||||
|
||||
int uv_timer_stop(uv_timer_t* handle) {
|
||||
if (!uv__is_active(handle))
|
||||
return 0;
|
||||
|
||||
heap_remove(timer_heap(handle->loop),
|
||||
(struct heap_node*) &handle->heap_node,
|
||||
timer_less_than);
|
||||
uv__handle_stop(handle);
|
||||
if (uv__is_active(handle)) {
|
||||
heap_remove(timer_heap(handle->loop),
|
||||
(struct heap_node*) &handle->node.heap,
|
||||
timer_less_than);
|
||||
uv__handle_stop(handle);
|
||||
} else {
|
||||
uv__queue_remove(&handle->node.queue);
|
||||
}
|
||||
|
||||
uv__queue_init(&handle->node.queue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -148,7 +150,7 @@ int uv__next_timeout(const uv_loop_t* loop) {
|
||||
if (heap_node == NULL)
|
||||
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)
|
||||
return 0;
|
||||
|
||||
@ -163,17 +165,30 @@ int uv__next_timeout(const uv_loop_t* loop) {
|
||||
void uv__run_timers(uv_loop_t* loop) {
|
||||
struct heap_node* heap_node;
|
||||
uv_timer_t* handle;
|
||||
struct uv__queue* queue_node;
|
||||
struct uv__queue ready_queue;
|
||||
|
||||
uv__queue_init(&ready_queue);
|
||||
|
||||
for (;;) {
|
||||
heap_node = heap_min(timer_heap(loop));
|
||||
if (heap_node == NULL)
|
||||
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)
|
||||
break;
|
||||
|
||||
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);
|
||||
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 in6_ifreq if6;
|
||||
struct sockaddr_dl* sa_addr;
|
||||
size_t namelen;
|
||||
char* name;
|
||||
|
||||
ifc.ifc_req = NULL;
|
||||
sock6fd = -1;
|
||||
@ -1156,6 +1158,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
#define ADDR_SIZE(p) MAX((p).sa_len, sizeof(p))
|
||||
|
||||
/* Count all up and running ipv4/ipv6 addresses */
|
||||
namelen = 0;
|
||||
ifr = ifc.ifc_req;
|
||||
while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) {
|
||||
p = ifr;
|
||||
@ -1175,6 +1178,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING))
|
||||
continue;
|
||||
|
||||
namelen += strlen(p->ifr_name) + 1;
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
@ -1182,11 +1186,12 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
goto cleanup;
|
||||
|
||||
/* Alloc the return interface structs */
|
||||
*addresses = uv__calloc(*count, sizeof(**addresses));
|
||||
if (!(*addresses)) {
|
||||
*addresses = uv__calloc(1, *count * sizeof(**addresses) + namelen);
|
||||
if (*addresses == NULL) {
|
||||
r = UV_ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
name = (char*) &(*addresses)[*count];
|
||||
address = *addresses;
|
||||
|
||||
ifr = ifc.ifc_req;
|
||||
@ -1210,7 +1215,9 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
|
||||
/* All conditions above must match count loop */
|
||||
|
||||
address->name = uv__strdup(p->ifr_name);
|
||||
namelen = strlen(p->ifr_name) + 1;
|
||||
address->name = memcpy(name, p->ifr_name, namelen);
|
||||
name += namelen;
|
||||
|
||||
if (inet6)
|
||||
address->address.address6 = *((struct sockaddr_in6*) &p->ifr_addr);
|
||||
@ -1282,13 +1289,7 @@ cleanup:
|
||||
|
||||
|
||||
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||
int count) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
uv__free(addresses[i].name);
|
||||
}
|
||||
|
||||
int count) {
|
||||
uv__free(addresses);
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,34 @@
|
||||
#include <sys/eventfd.h>
|
||||
#endif
|
||||
|
||||
#if UV__KQUEUE_EVFILT_USER
|
||||
static uv_once_t kqueue_runtime_detection_guard = UV_ONCE_INIT;
|
||||
static int kqueue_evfilt_user_support = 1;
|
||||
|
||||
|
||||
static void uv__kqueue_runtime_detection(void) {
|
||||
int kq;
|
||||
struct kevent ev[2];
|
||||
struct timespec timeout = {0, 0};
|
||||
|
||||
/* Perform the runtime detection to ensure that kqueue with
|
||||
* EVFILT_USER actually works. */
|
||||
kq = kqueue();
|
||||
EV_SET(ev, UV__KQUEUE_EVFILT_USER_IDENT, EVFILT_USER,
|
||||
EV_ADD | EV_CLEAR, 0, 0, 0);
|
||||
EV_SET(ev + 1, UV__KQUEUE_EVFILT_USER_IDENT, EVFILT_USER,
|
||||
0, NOTE_TRIGGER, 0, 0);
|
||||
if (kevent(kq, ev, 2, ev, 1, &timeout) < 1 ||
|
||||
ev[0].filter != EVFILT_USER ||
|
||||
ev[0].ident != UV__KQUEUE_EVFILT_USER_IDENT ||
|
||||
ev[0].flags & EV_ERROR)
|
||||
/* If we wind up here, we can assume that EVFILT_USER is defined but
|
||||
* broken on the current system. */
|
||||
kqueue_evfilt_user_support = 0;
|
||||
uv__close(kq);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void uv__async_send(uv_loop_t* loop);
|
||||
static int uv__async_start(uv_loop_t* loop);
|
||||
static void uv__cpu_relax(void);
|
||||
@ -139,7 +167,11 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
|
||||
|
||||
assert(w == &loop->async_io_watcher);
|
||||
|
||||
#if UV__KQUEUE_EVFILT_USER
|
||||
for (;!kqueue_evfilt_user_support;) {
|
||||
#else
|
||||
for (;;) {
|
||||
#endif
|
||||
r = read(w->fd, buf, sizeof(buf));
|
||||
|
||||
if (r == sizeof(buf))
|
||||
@ -195,6 +227,17 @@ static void uv__async_send(uv_loop_t* loop) {
|
||||
len = sizeof(val);
|
||||
fd = loop->async_io_watcher.fd; /* eventfd */
|
||||
}
|
||||
#elif UV__KQUEUE_EVFILT_USER
|
||||
struct kevent ev;
|
||||
|
||||
if (kqueue_evfilt_user_support) {
|
||||
fd = loop->async_io_watcher.fd; /* magic number for EVFILT_USER */
|
||||
EV_SET(&ev, fd, EVFILT_USER, 0, NOTE_TRIGGER, 0, 0);
|
||||
r = kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL);
|
||||
if (r == 0)
|
||||
return;
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
|
||||
do
|
||||
@ -215,6 +258,9 @@ static void uv__async_send(uv_loop_t* loop) {
|
||||
static int uv__async_start(uv_loop_t* loop) {
|
||||
int pipefd[2];
|
||||
int err;
|
||||
#if UV__KQUEUE_EVFILT_USER
|
||||
struct kevent ev;
|
||||
#endif
|
||||
|
||||
if (loop->async_io_watcher.fd != -1)
|
||||
return 0;
|
||||
@ -226,6 +272,36 @@ static int uv__async_start(uv_loop_t* loop) {
|
||||
|
||||
pipefd[0] = err;
|
||||
pipefd[1] = -1;
|
||||
#elif UV__KQUEUE_EVFILT_USER
|
||||
uv_once(&kqueue_runtime_detection_guard, uv__kqueue_runtime_detection);
|
||||
if (kqueue_evfilt_user_support) {
|
||||
/* In order not to break the generic pattern of I/O polling, a valid
|
||||
* file descriptor is required to take up a room in loop->watchers,
|
||||
* thus we create one for that, but this fd will not be actually used,
|
||||
* it's just a placeholder and magic number which is going to be closed
|
||||
* during the cleanup, as other FDs. */
|
||||
err = uv__open_cloexec("/", O_RDONLY);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
pipefd[0] = err;
|
||||
pipefd[1] = -1;
|
||||
|
||||
/* When using EVFILT_USER event to wake up the kqueue, this event must be
|
||||
* registered beforehand. Otherwise, calling kevent() to issue an
|
||||
* unregistered EVFILT_USER event will get an ENOENT.
|
||||
* Since uv__async_send() may happen before uv__io_poll() with multi-threads,
|
||||
* we can't defer this registration of EVFILT_USER event as we did for other
|
||||
* events, but must perform it right away. */
|
||||
EV_SET(&ev, err, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, 0);
|
||||
err = kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL);
|
||||
if (err < 0)
|
||||
return UV__ERR(errno);
|
||||
} else {
|
||||
err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
#else
|
||||
err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE);
|
||||
if (err < 0)
|
||||
@ -236,6 +312,13 @@ static int uv__async_start(uv_loop_t* loop) {
|
||||
uv__io_start(loop, &loop->async_io_watcher, POLLIN);
|
||||
loop->async_wfd = pipefd[1];
|
||||
|
||||
#if UV__KQUEUE_EVFILT_USER
|
||||
/* Prevent the EVFILT_USER event from being added to kqueue redundantly
|
||||
* and mistakenly later in uv__io_poll(). */
|
||||
if (kqueue_evfilt_user_support)
|
||||
loop->async_io_watcher.events = loop->async_io_watcher.pevents;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -65,13 +65,13 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO(bnoordhuis) share with linux.c */
|
||||
int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
uv_interface_address_t* address;
|
||||
struct ifaddrs* addrs;
|
||||
struct ifaddrs* ent;
|
||||
uv_interface_address_t* address;
|
||||
#if !(defined(__CYGWIN__) || defined(__MSYS__)) && !defined(__GNU__)
|
||||
int i;
|
||||
#endif
|
||||
size_t namelen;
|
||||
char* name;
|
||||
|
||||
*count = 0;
|
||||
*addresses = NULL;
|
||||
@ -80,9 +80,11 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
return UV__ERR(errno);
|
||||
|
||||
/* Count the number of interfaces */
|
||||
namelen = 0;
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
|
||||
continue;
|
||||
namelen += strlen(ent->ifa_name) + 1;
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
@ -92,20 +94,22 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
}
|
||||
|
||||
/* Make sure the memory is initiallized to zero using calloc() */
|
||||
*addresses = uv__calloc(*count, sizeof(**addresses));
|
||||
|
||||
*addresses = uv__calloc(1, *count * sizeof(**addresses) + namelen);
|
||||
if (*addresses == NULL) {
|
||||
freeifaddrs(addrs);
|
||||
return UV_ENOMEM;
|
||||
}
|
||||
|
||||
name = (char*) &(*addresses)[*count];
|
||||
address = *addresses;
|
||||
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
|
||||
continue;
|
||||
|
||||
address->name = uv__strdup(ent->ifa_name);
|
||||
namelen = strlen(ent->ifa_name) + 1;
|
||||
address->name = memcpy(name, ent->ifa_name, namelen);
|
||||
name += namelen;
|
||||
|
||||
if (ent->ifa_addr->sa_family == AF_INET6) {
|
||||
address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
|
||||
@ -129,6 +133,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
#if !(defined(__CYGWIN__) || defined(__MSYS__)) && !defined(__GNU__)
|
||||
/* Fill in physical addresses for each interface */
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
int i;
|
||||
|
||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))
|
||||
continue;
|
||||
|
||||
@ -151,13 +157,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
}
|
||||
|
||||
|
||||
/* TODO(bnoordhuis) share with linux.c */
|
||||
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||
int count) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
uv__free(addresses[i].name);
|
||||
}
|
||||
|
||||
uv__free(addresses);
|
||||
}
|
||||
|
346
src/unix/core.c
346
src/unix/core.c
@ -52,8 +52,11 @@
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# include <mach/mach.h>
|
||||
# include <mach/thread_info.h>
|
||||
# include <sys/filio.h>
|
||||
# endif /* defined(__APPLE__) */
|
||||
# include <sys/sysctl.h>
|
||||
#endif /* defined(__APPLE__) */
|
||||
|
||||
|
||||
#if defined(__APPLE__) && !TARGET_OS_IPHONE
|
||||
@ -90,9 +93,19 @@ extern char** environ;
|
||||
#if defined(__linux__)
|
||||
# include <sched.h>
|
||||
# include <sys/syscall.h>
|
||||
# define gettid() syscall(SYS_gettid)
|
||||
# define uv__accept4 accept4
|
||||
#endif
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
# include <sys/param.h>
|
||||
# include <sys/cpuset.h>
|
||||
#endif
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
# include <sched.h>
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) && defined(__SANITIZE_THREAD__) && defined(__clang__)
|
||||
# include <sanitizer/linux_syscall_hooks.h>
|
||||
#endif
|
||||
@ -155,7 +168,7 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) {
|
||||
break;
|
||||
|
||||
case UV_TTY:
|
||||
uv__stream_close((uv_stream_t*)handle);
|
||||
uv__tty_close((uv_tty_t*)handle);
|
||||
break;
|
||||
|
||||
case UV_TCP:
|
||||
@ -740,7 +753,7 @@ ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) {
|
||||
int uv_cwd(char* buffer, size_t* size) {
|
||||
char scratch[1 + UV__PATH_MAX];
|
||||
|
||||
if (buffer == NULL || size == NULL)
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
/* Try to read directly into the user's buffer first... */
|
||||
@ -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;
|
||||
|
||||
if (getrusage(RUSAGE_SELF, &usage))
|
||||
if (getrusage(who, &usage))
|
||||
return UV__ERR(errno);
|
||||
|
||||
rusage->ru_utime.tv_sec = usage.ru_utime.tv_sec;
|
||||
@ -1023,13 +1036,57 @@ int uv_getrusage(uv_rusage_t* rusage) {
|
||||
#if defined(__APPLE__)
|
||||
rusage->ru_maxrss /= 1024; /* macOS and iOS report bytes. */
|
||||
#elif defined(__sun)
|
||||
rusage->ru_maxrss /= getpagesize() / 1024; /* Solaris reports pages. */
|
||||
rusage->ru_maxrss *= getpagesize() / 1024; /* Solaris reports pages. */
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_getrusage(uv_rusage_t* rusage) {
|
||||
return uv__getrusage(RUSAGE_SELF, rusage);
|
||||
}
|
||||
|
||||
|
||||
int uv_getrusage_thread(uv_rusage_t* rusage) {
|
||||
#if defined(__APPLE__)
|
||||
mach_msg_type_number_t count;
|
||||
thread_basic_info_data_t info;
|
||||
kern_return_t kr;
|
||||
thread_t thread;
|
||||
|
||||
thread = mach_thread_self();
|
||||
count = THREAD_BASIC_INFO_COUNT;
|
||||
kr = thread_info(thread,
|
||||
THREAD_BASIC_INFO,
|
||||
(thread_info_t)&info,
|
||||
&count);
|
||||
|
||||
if (kr != KERN_SUCCESS) {
|
||||
mach_port_deallocate(mach_task_self(), thread);
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
memset(rusage, 0, sizeof(*rusage));
|
||||
|
||||
rusage->ru_utime.tv_sec = info.user_time.seconds;
|
||||
rusage->ru_utime.tv_usec = info.user_time.microseconds;
|
||||
rusage->ru_stime.tv_sec = info.system_time.seconds;
|
||||
rusage->ru_stime.tv_usec = info.system_time.microseconds;
|
||||
|
||||
mach_port_deallocate(mach_task_self(), thread);
|
||||
|
||||
return 0;
|
||||
|
||||
#elif defined(RUSAGE_LWP)
|
||||
return uv__getrusage(RUSAGE_LWP, rusage);
|
||||
#elif defined(RUSAGE_THREAD)
|
||||
return uv__getrusage(RUSAGE_THREAD, rusage);
|
||||
#endif /* defined(__APPLE__) */
|
||||
return UV_ENOTSUP;
|
||||
}
|
||||
|
||||
|
||||
int uv__open_cloexec(const char* path, int flags) {
|
||||
#if defined(O_CLOEXEC)
|
||||
int fd;
|
||||
@ -1557,6 +1614,135 @@ int uv_os_setpriority(uv_pid_t pid, int priority) {
|
||||
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) {
|
||||
struct utsname buf;
|
||||
@ -1739,11 +1925,31 @@ int uv__search_path(const char* prog, char* buf, size_t* buflen) {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
#if defined(__linux__) || defined (__FreeBSD__)
|
||||
# define uv__cpu_count(cpuset) CPU_COUNT(cpuset)
|
||||
#elif defined(__NetBSD__)
|
||||
static int uv__cpu_count(cpuset_t* set) {
|
||||
int rc;
|
||||
cpuid_t i;
|
||||
|
||||
rc = 0;
|
||||
for (i = 0;; i++) {
|
||||
int r = cpuset_isset(i, set);
|
||||
if (r < 0)
|
||||
break;
|
||||
if (r)
|
||||
rc++;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif /* __NetBSD__ */
|
||||
|
||||
unsigned int uv_available_parallelism(void) {
|
||||
long rc = -1;
|
||||
|
||||
#ifdef __linux__
|
||||
cpu_set_t set;
|
||||
long rc;
|
||||
|
||||
memset(&set, 0, sizeof(set));
|
||||
|
||||
@ -1752,29 +1958,127 @@ unsigned int uv_available_parallelism(void) {
|
||||
* before falling back to sysconf(_SC_NPROCESSORS_ONLN).
|
||||
*/
|
||||
if (0 == sched_getaffinity(0, sizeof(set), &set))
|
||||
rc = CPU_COUNT(&set);
|
||||
else
|
||||
rc = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
|
||||
if (rc < 1)
|
||||
rc = 1;
|
||||
|
||||
return (unsigned) rc;
|
||||
rc = uv__cpu_count(&set);
|
||||
#elif defined(__MVS__)
|
||||
int rc;
|
||||
|
||||
rc = __get_num_online_cpus();
|
||||
if (rc < 1)
|
||||
rc = 1;
|
||||
|
||||
return (unsigned) rc;
|
||||
#else /* __linux__ */
|
||||
long rc;
|
||||
#elif defined(__FreeBSD__)
|
||||
cpuset_t set;
|
||||
|
||||
memset(&set, 0, sizeof(set));
|
||||
|
||||
if (0 == cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1, sizeof(set), &set))
|
||||
rc = uv__cpu_count(&set);
|
||||
#elif defined(__NetBSD__)
|
||||
cpuset_t* set = cpuset_create();
|
||||
if (set != NULL) {
|
||||
if (0 == sched_getaffinity_np(getpid(), sizeof(set), &set))
|
||||
rc = uv__cpu_count(&set);
|
||||
cpuset_destroy(set);
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
int nprocs;
|
||||
size_t i;
|
||||
size_t len = sizeof(nprocs);
|
||||
static const char *mib[] = {
|
||||
"hw.activecpu",
|
||||
"hw.logicalcpu",
|
||||
"hw.ncpu"
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mib); i++) {
|
||||
if (0 == sysctlbyname(mib[i], &nprocs, &len, NULL, 0) &&
|
||||
len == sizeof(nprocs) &&
|
||||
nprocs > 0) {
|
||||
rc = nprocs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#elif defined(__OpenBSD__)
|
||||
int nprocs;
|
||||
size_t i;
|
||||
size_t len = sizeof(nprocs);
|
||||
static int mib[][2] = {
|
||||
# ifdef HW_NCPUONLINE
|
||||
{ CTL_HW, HW_NCPUONLINE },
|
||||
# endif
|
||||
{ CTL_HW, HW_NCPU }
|
||||
};
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mib); i++) {
|
||||
if (0 == sysctl(mib[i], ARRAY_SIZE(mib[i]), &nprocs, &len, NULL, 0) &&
|
||||
len == sizeof(nprocs) &&
|
||||
nprocs > 0) {
|
||||
rc = nprocs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* __linux__ */
|
||||
|
||||
if (rc < 0)
|
||||
rc = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
|
||||
#ifdef __linux__
|
||||
{
|
||||
double rc_with_cgroup;
|
||||
uv__cpu_constraint c = {0, 0, 0.0};
|
||||
|
||||
if (uv__get_constrained_cpu(&c) == 0 && c.period_length > 0) {
|
||||
rc_with_cgroup = (double)c.quota_per_period / c.period_length * c.proportions;
|
||||
if (rc_with_cgroup < rc)
|
||||
rc = (long)rc_with_cgroup; /* Casting is safe since rc_with_cgroup < rc < LONG_MAX */
|
||||
}
|
||||
}
|
||||
#endif /* __linux__ */
|
||||
|
||||
rc = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
if (rc < 1)
|
||||
rc = 1;
|
||||
|
||||
return (unsigned) rc;
|
||||
#endif /* __linux__ */
|
||||
}
|
||||
|
||||
int uv__sock_reuseport(int fd) {
|
||||
int on = 1;
|
||||
#if defined(__FreeBSD__) && __FreeBSD__ >= 12 && defined(SO_REUSEPORT_LB)
|
||||
/* FreeBSD 12 introduced a new socket option named SO_REUSEPORT_LB
|
||||
* with the capability of load balancing, it's the substitution of
|
||||
* the SO_REUSEPORTs on Linux and DragonFlyBSD. */
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT_LB, &on, sizeof(on)))
|
||||
return UV__ERR(errno);
|
||||
#elif (defined(__linux__) || \
|
||||
defined(_AIX73) || \
|
||||
(defined(__DragonFly__) && __DragonFly_version >= 300600) || \
|
||||
(defined(UV__SOLARIS_11_4) && UV__SOLARIS_11_4)) && \
|
||||
defined(SO_REUSEPORT)
|
||||
/* On Linux 3.9+, the SO_REUSEPORT implementation distributes connections
|
||||
* evenly across all of the threads (or processes) that are blocked in
|
||||
* accept() on the same port. As with TCP, SO_REUSEPORT distributes datagrams
|
||||
* evenly across all of the receiving threads (or process).
|
||||
*
|
||||
* DragonFlyBSD 3.6.0 extended SO_REUSEPORT to distribute workload to
|
||||
* available sockets, which made it the equivalent of Linux's SO_REUSEPORT.
|
||||
*
|
||||
* AIX 7.2.5 added the feature that would add the capability to distribute
|
||||
* incoming connections or datagrams across all listening ports for SO_REUSEPORT.
|
||||
*
|
||||
* Solaris 11 supported SO_REUSEPORT, but it's implemented only for
|
||||
* binding to the same address and port, without load balancing.
|
||||
* Solaris 11.4 extended SO_REUSEPORT with the capability of load balancing.
|
||||
*/
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)))
|
||||
return UV__ERR(errno);
|
||||
#else
|
||||
(void) (fd);
|
||||
(void) (on);
|
||||
/* SO_REUSEPORTs do not have the capability of load balancing on platforms
|
||||
* other than those mentioned above. The semantics are completely different,
|
||||
* therefore we shouldn't enable it, but fail this operation to indicate that
|
||||
* UV_[TCP/UDP]_REUSEPORT is not supported on these platforms. */
|
||||
return UV_ENOTSUP;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -36,9 +36,45 @@ int uv_uptime(double* uptime) {
|
||||
}
|
||||
|
||||
int uv_resident_set_memory(size_t* rss) {
|
||||
/* FIXME: read /proc/meminfo? */
|
||||
*rss = 0;
|
||||
char buf[1024];
|
||||
const char* s;
|
||||
long val;
|
||||
int rc;
|
||||
int i;
|
||||
struct sysinfo si;
|
||||
|
||||
/* rss: 24th element */
|
||||
rc = uv__slurp("/proc/self/stat", buf, sizeof(buf));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* find the last ')' */
|
||||
s = strrchr(buf, ')');
|
||||
if (s == NULL)
|
||||
goto err;
|
||||
|
||||
for (i = 1; i <= 22; i++) {
|
||||
s = strchr(s + 1, ' ');
|
||||
if (s == NULL)
|
||||
goto err;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
val = strtol(s, NULL, 10);
|
||||
if (val < 0 || errno != 0)
|
||||
goto err;
|
||||
|
||||
do
|
||||
rc = sysinfo(&si);
|
||||
while (rc == -1 && errno == EINTR);
|
||||
if (rc == -1)
|
||||
return UV__ERR(errno);
|
||||
|
||||
*rss = val * si.mem_unit;
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
|
||||
|
@ -33,25 +33,9 @@
|
||||
#include "darwin-stub.h"
|
||||
#endif
|
||||
|
||||
|
||||
static int uv__pthread_setname_np(const char* name) {
|
||||
char namebuf[64]; /* MAXTHREADNAMESIZE */
|
||||
int err;
|
||||
|
||||
strncpy(namebuf, name, sizeof(namebuf) - 1);
|
||||
namebuf[sizeof(namebuf) - 1] = '\0';
|
||||
|
||||
err = pthread_setname_np(namebuf);
|
||||
if (err)
|
||||
return UV__ERR(err);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv__set_process_title(const char* title) {
|
||||
#if TARGET_OS_IPHONE
|
||||
return uv__pthread_setname_np(title);
|
||||
return uv__thread_setname(title);
|
||||
#else
|
||||
CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef,
|
||||
const char*,
|
||||
@ -177,7 +161,7 @@ int uv__set_process_title(const char* title) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
uv__pthread_setname_np(title); /* Don't care if it fails. */
|
||||
uv__thread_setname(title); /* Don't care if it fails. */
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
|
17
src/unix/darwin-syscalls.h
Normal file
17
src/unix/darwin-syscalls.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef UV_DARWIN_SYSCALLS_H_
|
||||
#define UV_DARWIN_SYSCALLS_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
/* https://github.com/apple/darwin-xnu/blob/master/bsd/sys/socket.h */
|
||||
|
||||
struct mmsghdr {
|
||||
struct msghdr msg_hdr;
|
||||
size_t msg_len;
|
||||
};
|
||||
|
||||
ssize_t recvmsg_x(int s, const struct mmsghdr* msgp, u_int cnt, int flags);
|
||||
ssize_t sendmsg_x(int s, const struct mmsghdr* msgp, u_int cnt, int flags);
|
||||
|
||||
#endif /* UV_DARWIN_SYSCALLS_H_ */
|
@ -25,7 +25,6 @@
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_time.h>
|
||||
#include <mach-o/dyld.h> /* _NSGetExecutablePath */
|
||||
@ -34,7 +33,6 @@
|
||||
#include <unistd.h> /* sysconf */
|
||||
|
||||
static uv_once_t once = UV_ONCE_INIT;
|
||||
static uint64_t (*time_func)(void);
|
||||
static mach_timebase_info_data_t timebase;
|
||||
|
||||
|
||||
@ -56,16 +54,12 @@ void uv__platform_loop_delete(uv_loop_t* loop) {
|
||||
static void uv__hrtime_init_once(void) {
|
||||
if (KERN_SUCCESS != mach_timebase_info(&timebase))
|
||||
abort();
|
||||
|
||||
time_func = (uint64_t (*)(void)) dlsym(RTLD_DEFAULT, "mach_continuous_time");
|
||||
if (time_func == NULL)
|
||||
time_func = mach_absolute_time;
|
||||
}
|
||||
|
||||
|
||||
uint64_t uv__hrtime(uv_clocktype_t type) {
|
||||
uv_once(&once, uv__hrtime_init_once);
|
||||
return time_func() * timebase.numer / timebase.denom;
|
||||
return mach_continuous_time() * timebase.numer / timebase.denom;
|
||||
}
|
||||
|
||||
|
||||
|
@ -26,7 +26,12 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include <paths.h>
|
||||
#include <sys/user.h>
|
||||
#if defined(__DragonFly__)
|
||||
# include <sys/event.h>
|
||||
# include <sys/kinfo.h>
|
||||
#else
|
||||
# include <sys/user.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
292
src/unix/fs.c
292
src/unix/fs.c
@ -31,6 +31,7 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -82,15 +83,6 @@
|
||||
# include <sys/statfs.h>
|
||||
#endif
|
||||
|
||||
#if defined(__CYGWIN__) || \
|
||||
(defined(__HAIKU__) && B_HAIKU_VERSION < B_HAIKU_VERSION_1_PRE_BETA_5) || \
|
||||
(defined(__sun) && !defined(__illumos__))
|
||||
#define preadv(fd, bufs, nbufs, off) \
|
||||
pread(fd, (bufs)->iov_base, (bufs)->iov_len, off)
|
||||
#define pwritev(fd, bufs, nbufs, off) \
|
||||
pwrite(fd, (bufs)->iov_base, (bufs)->iov_len, off)
|
||||
#endif
|
||||
|
||||
#if defined(_AIX) && _XOPEN_SOURCE <= 600
|
||||
extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */
|
||||
#endif
|
||||
@ -147,7 +139,7 @@ extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */
|
||||
#define POST \
|
||||
do { \
|
||||
if (cb != NULL) { \
|
||||
uv__req_register(loop, req); \
|
||||
uv__req_register(loop); \
|
||||
uv__work_submit(loop, \
|
||||
&req->work_req, \
|
||||
UV__WORK_FAST_IO, \
|
||||
@ -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;
|
||||
|
||||
if (uv__isinf(time))
|
||||
return (struct timespec){UTIME_NOW, UTIME_NOW};
|
||||
if (uv__isnan(time))
|
||||
return (struct timespec){UTIME_OMIT, UTIME_OMIT};
|
||||
|
||||
ts.tv_sec = time;
|
||||
ts.tv_nsec = (time - ts.tv_sec) * 1e9;
|
||||
|
||||
@ -229,41 +236,23 @@ UV_UNUSED(static struct timespec uv__fs_to_timespec(double time)) {
|
||||
}
|
||||
return ts;
|
||||
}
|
||||
#endif
|
||||
|
||||
UV_UNUSED(static struct timeval uv__fs_to_timeval(double time)) {
|
||||
struct timeval tv;
|
||||
tv.tv_sec = time;
|
||||
tv.tv_usec = (time - tv.tv_sec) * 1e6;
|
||||
if (tv.tv_usec < 0) {
|
||||
tv.tv_usec += 1e6;
|
||||
tv.tv_sec -= 1;
|
||||
}
|
||||
return tv;
|
||||
}
|
||||
|
||||
static ssize_t uv__fs_futime(uv_fs_t* req) {
|
||||
#if defined(__linux__) \
|
||||
#if defined(__APPLE__) \
|
||||
|| defined(_AIX71) \
|
||||
|| defined(__DragonFly__) \
|
||||
|| defined(__FreeBSD__) \
|
||||
|| defined(__HAIKU__) \
|
||||
|| defined(__GNU__)
|
||||
|| defined(__NetBSD__) \
|
||||
|| defined(__OpenBSD__) \
|
||||
|| defined(__linux__) \
|
||||
|| defined(__sun)
|
||||
struct timespec ts[2];
|
||||
ts[0] = uv__fs_to_timespec(req->atime);
|
||||
ts[1] = uv__fs_to_timespec(req->mtime);
|
||||
return futimens(req->file, ts);
|
||||
#elif defined(__APPLE__) \
|
||||
|| defined(__DragonFly__) \
|
||||
|| defined(__FreeBSD__) \
|
||||
|| defined(__NetBSD__) \
|
||||
|| defined(__OpenBSD__) \
|
||||
|| defined(__sun)
|
||||
struct timeval tv[2];
|
||||
tv[0] = uv__fs_to_timeval(req->atime);
|
||||
tv[1] = uv__fs_to_timeval(req->mtime);
|
||||
# if defined(__sun)
|
||||
return futimesat(req->file, NULL, tv);
|
||||
# else
|
||||
return futimes(req->file, tv);
|
||||
# endif
|
||||
#elif defined(__MVS__)
|
||||
attrib_t atr;
|
||||
memset(&atr, 0, sizeof(atr));
|
||||
@ -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) {
|
||||
const struct iovec* bufs;
|
||||
unsigned int iovmax;
|
||||
@ -431,7 +534,7 @@ static ssize_t uv__fs_read(uv_fs_t* req) {
|
||||
if (nbufs == 1)
|
||||
r = pread(fd, bufs->iov_base, bufs->iov_len, off);
|
||||
else if (nbufs > 1)
|
||||
r = preadv(fd, bufs, nbufs, off);
|
||||
r = uv__preadv(fd, bufs, nbufs, off);
|
||||
}
|
||||
|
||||
#ifdef __PASE__
|
||||
@ -689,14 +792,23 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) {
|
||||
|
||||
static ssize_t uv__fs_realpath(uv_fs_t* req) {
|
||||
char* buf;
|
||||
char* tmp;
|
||||
|
||||
#if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809L
|
||||
buf = realpath(req->path, NULL);
|
||||
if (buf == NULL)
|
||||
tmp = realpath(req->path, NULL);
|
||||
if (tmp == NULL)
|
||||
return -1;
|
||||
buf = uv__strdup(tmp);
|
||||
free(tmp); /* _Not_ uv__free. */
|
||||
if (buf == NULL) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
#else
|
||||
ssize_t len;
|
||||
|
||||
(void)tmp;
|
||||
|
||||
len = uv__fs_pathmax_size(req->path);
|
||||
buf = uv__malloc(len + 1);
|
||||
|
||||
@ -960,7 +1072,10 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) {
|
||||
|
||||
return -1;
|
||||
}
|
||||
#elif defined(__APPLE__) || defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
/* sendfile() on iOS(arm64) will throw SIGSYS signal cause crash. */
|
||||
#elif (defined(__APPLE__) && !TARGET_OS_IPHONE) \
|
||||
|| defined(__DragonFly__) \
|
||||
|| defined(__FreeBSD__)
|
||||
{
|
||||
off_t len;
|
||||
ssize_t r;
|
||||
@ -1024,25 +1139,20 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) {
|
||||
|
||||
|
||||
static ssize_t uv__fs_utime(uv_fs_t* req) {
|
||||
#if defined(__linux__) \
|
||||
|| defined(_AIX71) \
|
||||
|| defined(__sun) \
|
||||
|| defined(__HAIKU__)
|
||||
#if defined(__APPLE__) \
|
||||
|| defined(_AIX71) \
|
||||
|| defined(__DragonFly__) \
|
||||
|| defined(__FreeBSD__) \
|
||||
|| defined(__HAIKU__) \
|
||||
|| defined(__NetBSD__) \
|
||||
|| defined(__OpenBSD__) \
|
||||
|| defined(__linux__) \
|
||||
|| defined(__sun)
|
||||
struct timespec ts[2];
|
||||
ts[0] = uv__fs_to_timespec(req->atime);
|
||||
ts[1] = uv__fs_to_timespec(req->mtime);
|
||||
return utimensat(AT_FDCWD, req->path, ts, 0);
|
||||
#elif defined(__APPLE__) \
|
||||
|| defined(__DragonFly__) \
|
||||
|| defined(__FreeBSD__) \
|
||||
|| defined(__NetBSD__) \
|
||||
|| defined(__OpenBSD__)
|
||||
struct timeval tv[2];
|
||||
tv[0] = uv__fs_to_timeval(req->atime);
|
||||
tv[1] = uv__fs_to_timeval(req->mtime);
|
||||
return utimes(req->path, tv);
|
||||
#elif defined(_AIX) \
|
||||
&& !defined(_AIX71)
|
||||
#elif defined(_AIX) && !defined(_AIX71)
|
||||
struct utimbuf buf;
|
||||
buf.actime = req->atime;
|
||||
buf.modtime = req->mtime;
|
||||
@ -1063,24 +1173,19 @@ static ssize_t uv__fs_utime(uv_fs_t* req) {
|
||||
|
||||
|
||||
static ssize_t uv__fs_lutime(uv_fs_t* req) {
|
||||
#if defined(__linux__) || \
|
||||
defined(_AIX71) || \
|
||||
defined(__sun) || \
|
||||
defined(__HAIKU__) || \
|
||||
defined(__GNU__) || \
|
||||
defined(__OpenBSD__)
|
||||
#if defined(__APPLE__) \
|
||||
|| defined(_AIX71) \
|
||||
|| defined(__DragonFly__) \
|
||||
|| defined(__FreeBSD__) \
|
||||
|| defined(__HAIKU__) \
|
||||
|| defined(__NetBSD__) \
|
||||
|| defined(__OpenBSD__) \
|
||||
|| defined(__linux__) \
|
||||
|| defined(__sun)
|
||||
struct timespec ts[2];
|
||||
ts[0] = uv__fs_to_timespec(req->atime);
|
||||
ts[1] = uv__fs_to_timespec(req->mtime);
|
||||
return utimensat(AT_FDCWD, req->path, ts, AT_SYMLINK_NOFOLLOW);
|
||||
#elif defined(__APPLE__) || \
|
||||
defined(__DragonFly__) || \
|
||||
defined(__FreeBSD__) || \
|
||||
defined(__NetBSD__)
|
||||
struct timeval tv[2];
|
||||
tv[0] = uv__fs_to_timeval(req->atime);
|
||||
tv[1] = uv__fs_to_timeval(req->mtime);
|
||||
return lutimes(req->path, tv);
|
||||
#else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
@ -1110,7 +1215,7 @@ static ssize_t uv__fs_write(uv_fs_t* req) {
|
||||
if (nbufs == 1)
|
||||
r = pwrite(fd, bufs->iov_base, bufs->iov_len, off);
|
||||
else if (nbufs > 1)
|
||||
r = pwritev(fd, bufs, nbufs, off);
|
||||
r = uv__pwritev(fd, bufs, nbufs, off);
|
||||
}
|
||||
|
||||
return r;
|
||||
@ -1123,6 +1228,7 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) {
|
||||
uv_file dstfd;
|
||||
struct stat src_statsbuf;
|
||||
struct stat dst_statsbuf;
|
||||
struct timespec times[2];
|
||||
int dst_flags;
|
||||
int result;
|
||||
int err;
|
||||
@ -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) {
|
||||
err = UV__ERR(errno);
|
||||
#ifdef __linux__
|
||||
@ -1617,7 +1752,7 @@ static void uv__fs_done(struct uv__work* w, int status) {
|
||||
uv_fs_t* req;
|
||||
|
||||
req = container_of(w, uv_fs_t, work_req);
|
||||
uv__req_unregister(req->loop, req);
|
||||
uv__req_unregister(req->loop);
|
||||
|
||||
if (status == UV_ECANCELED) {
|
||||
assert(req->result == 0);
|
||||
@ -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,
|
||||
uv_fs_t* req,
|
||||
const char* path,
|
||||
@ -1754,6 +1899,9 @@ int uv_fs_ftruncate(uv_loop_t* loop,
|
||||
INIT(FTRUNCATE);
|
||||
req->file = file;
|
||||
req->off = off;
|
||||
if (cb != NULL)
|
||||
if (uv__iou_fs_ftruncate(loop, req))
|
||||
return 0;
|
||||
POST;
|
||||
}
|
||||
|
||||
|
@ -276,10 +276,6 @@ static void uv__fsevents_event_cb(const FSEventStreamRef streamRef,
|
||||
path += handle->realpath_len;
|
||||
len -= handle->realpath_len;
|
||||
|
||||
/* Ignore events with path equal to directory itself */
|
||||
if (len <= 1 && (flags & kFSEventStreamEventFlagItemIsDir))
|
||||
continue;
|
||||
|
||||
if (len == 0) {
|
||||
/* Since we're using fsevents to watch the file itself,
|
||||
* realpath == path, and we now need to get the basename of the file back
|
||||
@ -793,6 +789,7 @@ int uv__cf_loop_signal(uv_loop_t* loop,
|
||||
|
||||
/* Runs in UV loop to initialize handle */
|
||||
int uv__fsevents_init(uv_fs_event_t* handle) {
|
||||
char* buf;
|
||||
int err;
|
||||
uv__cf_loop_state_t* state;
|
||||
|
||||
@ -801,9 +798,13 @@ int uv__fsevents_init(uv_fs_event_t* handle) {
|
||||
return err;
|
||||
|
||||
/* Get absolute path to file */
|
||||
handle->realpath = realpath(handle->path, NULL);
|
||||
if (handle->realpath == NULL)
|
||||
buf = realpath(handle->path, NULL);
|
||||
if (buf == NULL)
|
||||
return UV__ERR(errno);
|
||||
handle->realpath = uv__strdup(buf);
|
||||
free(buf); /* _Not_ uv__free. */
|
||||
if (handle->realpath == NULL)
|
||||
return UV_ENOMEM;
|
||||
handle->realpath_len = strlen(handle->realpath);
|
||||
|
||||
/* Initialize event queue */
|
||||
|
@ -109,7 +109,7 @@ static void uv__getaddrinfo_done(struct uv__work* w, int status) {
|
||||
uv_getaddrinfo_t* req;
|
||||
|
||||
req = container_of(w, uv_getaddrinfo_t, work_req);
|
||||
uv__req_unregister(req->loop, req);
|
||||
uv__req_unregister(req->loop);
|
||||
|
||||
/* See initialization in uv_getaddrinfo(). */
|
||||
if (req->hints)
|
||||
|
@ -58,7 +58,7 @@ static void uv__getnameinfo_done(struct uv__work* w, int status) {
|
||||
char* service;
|
||||
|
||||
req = container_of(w, uv_getnameinfo_t, work_req);
|
||||
uv__req_unregister(req->loop, req);
|
||||
uv__req_unregister(req->loop);
|
||||
host = service = NULL;
|
||||
|
||||
if (status == UV_ECANCELED) {
|
||||
|
@ -394,6 +394,8 @@ static int get_ibmi_physical_address(const char* line, char (*phys_addr)[6]) {
|
||||
int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
uv_interface_address_t* address;
|
||||
struct ifaddrs_pase *ifap = NULL, *cur;
|
||||
size_t namelen;
|
||||
char* name;
|
||||
int inet6, r = 0;
|
||||
|
||||
*count = 0;
|
||||
@ -403,6 +405,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
return UV_ENOSYS;
|
||||
|
||||
/* The first loop to get the size of the array to be allocated */
|
||||
namelen = 0;
|
||||
for (cur = ifap; cur; cur = cur->ifa_next) {
|
||||
if (!(cur->ifa_addr->sa_family == AF_INET6 ||
|
||||
cur->ifa_addr->sa_family == AF_INET))
|
||||
@ -411,6 +414,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
if (!(cur->ifa_flags & IFF_UP && cur->ifa_flags & IFF_RUNNING))
|
||||
continue;
|
||||
|
||||
namelen += strlen(cur->ifa_name) + 1;
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
@ -420,11 +424,13 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
}
|
||||
|
||||
/* Alloc the return interface structs */
|
||||
*addresses = uv__calloc(*count, sizeof(**addresses));
|
||||
*addresses = uv__calloc(1, *count * sizeof(**addresses) + namelen);
|
||||
if (*addresses == NULL) {
|
||||
Qp2freeifaddrs(ifap);
|
||||
return UV_ENOMEM;
|
||||
}
|
||||
|
||||
name = (char*) &(*addresses)[*count];
|
||||
address = *addresses;
|
||||
|
||||
/* The second loop to fill in the array */
|
||||
@ -436,7 +442,9 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
if (!(cur->ifa_flags & IFF_UP && cur->ifa_flags & IFF_RUNNING))
|
||||
continue;
|
||||
|
||||
address->name = uv__strdup(cur->ifa_name);
|
||||
namelen = strlen(cur->ifa_name) + 1;
|
||||
address->name = memcpy(name, cur->ifa_name, namelen);
|
||||
name += namelen;
|
||||
|
||||
inet6 = (cur->ifa_addr->sa_family == AF_INET6);
|
||||
|
||||
@ -497,13 +505,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
}
|
||||
|
||||
|
||||
void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
uv__free(addresses[i].name);
|
||||
}
|
||||
|
||||
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||
int count) {
|
||||
uv__free(addresses);
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,10 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#if defined(__APPLE__) || defined(__DragonFly__) || \
|
||||
defined(__FreeBSD__) || defined(__NetBSD__)
|
||||
#include <sys/event.h>
|
||||
#endif
|
||||
|
||||
#define uv__msan_unpoison(p, n) \
|
||||
do { \
|
||||
@ -71,8 +75,11 @@
|
||||
# include <poll.h>
|
||||
#endif /* _AIX */
|
||||
|
||||
#if defined(__APPLE__) && !TARGET_OS_IPHONE
|
||||
# include <AvailabilityMacros.h>
|
||||
#if defined(__APPLE__)
|
||||
# include "darwin-syscalls.h"
|
||||
# if !TARGET_OS_IPHONE
|
||||
# include <AvailabilityMacros.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -157,7 +164,8 @@ typedef struct uv__stream_queued_fds_s uv__stream_queued_fds_t;
|
||||
/* loop flags */
|
||||
enum {
|
||||
UV_LOOP_BLOCK_SIGPROF = 0x1,
|
||||
UV_LOOP_REAP_CHILDREN = 0x2
|
||||
UV_LOOP_REAP_CHILDREN = 0x2,
|
||||
UV_LOOP_ENABLE_IO_URING_SQPOLL = 0x4
|
||||
};
|
||||
|
||||
/* flags of excluding ifaddr */
|
||||
@ -243,6 +251,7 @@ int uv__close(int fd); /* preserves errno */
|
||||
int uv__close_nocheckstdio(int fd);
|
||||
int uv__close_nocancel(int fd);
|
||||
int uv__socket(int domain, int type, int protocol);
|
||||
int uv__sock_reuseport(int fd);
|
||||
ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags);
|
||||
void uv__make_close_pending(uv_handle_t* handle);
|
||||
int uv__getiovmax(void);
|
||||
@ -287,6 +296,9 @@ int uv__tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb);
|
||||
int uv__tcp_nodelay(int fd, int on);
|
||||
int uv__tcp_keepalive(int fd, int on, unsigned int delay);
|
||||
|
||||
/* tty */
|
||||
void uv__tty_close(uv_tty_t* handle);
|
||||
|
||||
/* pipe */
|
||||
int uv__pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb);
|
||||
|
||||
@ -315,6 +327,8 @@ void uv__prepare_close(uv_prepare_t* handle);
|
||||
void uv__process_close(uv_process_t* handle);
|
||||
void uv__stream_close(uv_stream_t* handle);
|
||||
void uv__tcp_close(uv_tcp_t* handle);
|
||||
int uv__thread_setname(const char* name);
|
||||
int uv__thread_getname(uv_thread_t* tid, char* name, size_t size);
|
||||
size_t uv__thread_stack_size(void);
|
||||
void uv__udp_close(uv_udp_t* handle);
|
||||
void uv__udp_finish_close(uv_udp_t* handle);
|
||||
@ -332,6 +346,7 @@ int uv__random_sysctl(void* buf, size_t buflen);
|
||||
/* io_uring */
|
||||
#ifdef __linux__
|
||||
int uv__iou_fs_close(uv_loop_t* loop, uv_fs_t* req);
|
||||
int uv__iou_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req);
|
||||
int uv__iou_fs_fsync_or_fdatasync(uv_loop_t* loop,
|
||||
uv_fs_t* req,
|
||||
uint32_t fsync_flags);
|
||||
@ -350,6 +365,7 @@ int uv__iou_fs_symlink(uv_loop_t* loop, uv_fs_t* req);
|
||||
int uv__iou_fs_unlink(uv_loop_t* loop, uv_fs_t* req);
|
||||
#else
|
||||
#define uv__iou_fs_close(loop, req) 0
|
||||
#define uv__iou_fs_ftruncate(loop, req) 0
|
||||
#define uv__iou_fs_fsync_or_fdatasync(loop, req, fsync_flags) 0
|
||||
#define uv__iou_fs_link(loop, req) 0
|
||||
#define uv__iou_fs_mkdir(loop, req) 0
|
||||
@ -425,6 +441,7 @@ UV_UNUSED(static int uv__stat(const char* path, struct stat* s)) {
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
void uv__fs_post(uv_loop_t* loop, uv_fs_t* req);
|
||||
ssize_t
|
||||
uv__fs_copy_file_range(int fd_in,
|
||||
off_t* off_in,
|
||||
@ -471,4 +488,44 @@ uv__fs_copy_file_range(int fd_in,
|
||||
#define UV__CPU_AFFINITY_SUPPORTED 0
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
typedef struct {
|
||||
long long quota_per_period;
|
||||
long long period_length;
|
||||
double proportions;
|
||||
} uv__cpu_constraint;
|
||||
|
||||
int uv__get_constrained_cpu(uv__cpu_constraint* constraint);
|
||||
#endif
|
||||
|
||||
#if defined(__sun) && !defined(__illumos__)
|
||||
#ifdef SO_FLOW_NAME
|
||||
/* Since it's impossible to detect the Solaris 11.4 version via OS macros,
|
||||
* so we check the presence of the socket option SO_FLOW_NAME that was first
|
||||
* introduced to Solaris 11.4 and define a custom macro for determining 11.4.
|
||||
*/
|
||||
#define UV__SOLARIS_11_4 (1)
|
||||
#else
|
||||
#define UV__SOLARIS_11_4 (0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(EVFILT_USER) && defined(NOTE_TRIGGER)
|
||||
/* EVFILT_USER is available since OS X 10.6, DragonFlyBSD 4.0,
|
||||
* FreeBSD 8.1, and NetBSD 10.0.
|
||||
*
|
||||
* Note that even though EVFILT_USER is defined on the current system,
|
||||
* it may still fail to work at runtime somehow. In that case, we fall
|
||||
* back to pipe-based signaling.
|
||||
*/
|
||||
#define UV__KQUEUE_EVFILT_USER 1
|
||||
/* Magic number of identifier used for EVFILT_USER during runtime detection.
|
||||
* There are no Google hits for this number when I create it. That way,
|
||||
* people will be directed here if this number gets printed due to some
|
||||
* kqueue error and they google for help. */
|
||||
#define UV__KQUEUE_EVFILT_USER_IDENT 0x1e7e7711
|
||||
#else
|
||||
#define UV__KQUEUE_EVFILT_USER 0
|
||||
#endif
|
||||
|
||||
#endif /* UV_UNIX_INTERNAL_H_ */
|
||||
|
@ -97,20 +97,47 @@ int uv__io_fork(uv_loop_t* loop) {
|
||||
|
||||
|
||||
int uv__io_check_fd(uv_loop_t* loop, int fd) {
|
||||
struct kevent ev;
|
||||
int rc;
|
||||
struct kevent ev[2];
|
||||
struct stat sb;
|
||||
#ifdef __APPLE__
|
||||
char path[MAXPATHLEN];
|
||||
#endif
|
||||
|
||||
rc = 0;
|
||||
EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
|
||||
if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
|
||||
rc = UV__ERR(errno);
|
||||
if (uv__fstat(fd, &sb))
|
||||
return UV__ERR(errno);
|
||||
|
||||
EV_SET(&ev, fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
|
||||
if (rc == 0)
|
||||
if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL))
|
||||
abort();
|
||||
/* On FreeBSD, kqueue only supports EVFILT_READ notification for regular files
|
||||
* and always reports ready events for writing, resulting in busy-looping.
|
||||
*
|
||||
* On Darwin, DragonFlyBSD, NetBSD and OpenBSD, kqueue reports ready events for
|
||||
* regular files as readable and writable only once, acting like an EV_ONESHOT.
|
||||
*
|
||||
* Neither of the above cases should be added to the kqueue.
|
||||
*/
|
||||
if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode))
|
||||
return UV_EINVAL;
|
||||
|
||||
return rc;
|
||||
#ifdef __APPLE__
|
||||
/* On Darwin (both macOS and iOS), in addition to regular files, FIFOs also don't
|
||||
* work properly with kqueue: the disconnection from the last writer won't trigger
|
||||
* an event for kqueue in spite of what the man pages say. Thus, we also disallow
|
||||
* the case of S_IFIFO. */
|
||||
if (S_ISFIFO(sb.st_mode)) {
|
||||
/* File descriptors of FIFO, pipe and kqueue share the same type of file,
|
||||
* therefore there is no way to tell them apart via stat.st_mode&S_IFMT.
|
||||
* Fortunately, FIFO is the only one that has a persisted file on filesystem,
|
||||
* from which we're able to make the distinction for it. */
|
||||
if (!fcntl(fd, F_GETPATH, path))
|
||||
return UV_EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
EV_SET(ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0);
|
||||
EV_SET(ev + 1, fd, EVFILT_READ, EV_DELETE, 0, 0, 0);
|
||||
if (kevent(loop->backend_fd, ev, 2, NULL, 0, NULL))
|
||||
return UV__ERR(errno);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -334,6 +361,17 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#if UV__KQUEUE_EVFILT_USER
|
||||
if (ev->filter == EVFILT_USER) {
|
||||
w = &loop->async_io_watcher;
|
||||
assert(fd == w->fd);
|
||||
uv__metrics_update_idle_time(loop);
|
||||
w->cb(loop, w, w->events);
|
||||
nevents++;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ev->filter == EVFILT_VNODE) {
|
||||
assert(w->events == POLLIN);
|
||||
assert(w->pevents == POLLIN);
|
||||
@ -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
|
||||
* whether with the KINFO_FILE_SIZE constant or this way.
|
||||
*/
|
||||
struct stat statbuf;
|
||||
struct kinfo_file kf;
|
||||
kf.kf_structsize = sizeof(kf);
|
||||
|
||||
if (fcntl(handle->event_watcher.fd, F_KINFO, &kf) == 0)
|
||||
path = uv__basename_r(kf.kf_path);
|
||||
if (handle->event_watcher.fd != -1 &&
|
||||
(!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
|
||||
handle->cb(handle, path, events, 0);
|
||||
|
||||
|
468
src/unix/linux.c
468
src/unix/linux.c
@ -37,12 +37,16 @@
|
||||
#include <errno.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <net/if.h>
|
||||
#include <netpacket/packet.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/sysinfo.h>
|
||||
@ -120,27 +124,9 @@
|
||||
# endif
|
||||
#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 {
|
||||
UV__IORING_SETUP_SQPOLL = 2u,
|
||||
UV__IORING_SETUP_NO_SQARRAY = 0x10000u,
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -162,6 +148,7 @@ enum {
|
||||
UV__IORING_OP_MKDIRAT = 37,
|
||||
UV__IORING_OP_SYMLINKAT = 38,
|
||||
UV__IORING_OP_LINKAT = 39,
|
||||
UV__IORING_OP_FTRUNCATE = 55,
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -174,10 +161,6 @@ enum {
|
||||
UV__IORING_SQ_CQ_OVERFLOW = 2u,
|
||||
};
|
||||
|
||||
enum {
|
||||
UV__MKDIRAT_SYMLINKAT_LINKAT = 1u,
|
||||
};
|
||||
|
||||
struct uv__io_cqring_offsets {
|
||||
uint32_t head;
|
||||
uint32_t tail;
|
||||
@ -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__)
|
||||
return 0; /* Possibly available but blocked by seccomp. */
|
||||
#elif defined(__arm__) && __SIZEOF_POINTER__ == 4
|
||||
/* See https://github.com/libuv/libuv/issues/4158. */
|
||||
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
|
||||
/* Ternary: unknown=0, yes=1, no=-1 */
|
||||
static _Atomic int use_io_uring;
|
||||
char* val;
|
||||
int use;
|
||||
|
||||
#if defined(__hppa__)
|
||||
/* io_uring first supported on parisc in 6.1, functional in .51
|
||||
* https://lore.kernel.org/all/cb912694-b1fe-dbb0-4d8c-d608f3526905@gmx.de/
|
||||
*/
|
||||
if (uv__kernel_version() < /*6.1.51*/0x060133)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
/* SQPOLL is all kinds of buggy but epoll batching should work fine. */
|
||||
if (0 == (flags & UV__IORING_SETUP_SQPOLL))
|
||||
return 1;
|
||||
|
||||
/* Older kernels have a bug where the sqpoll thread uses 100% CPU. */
|
||||
if (uv__kernel_version() < /*5.10.186*/0x050ABA)
|
||||
return 0;
|
||||
|
||||
use = atomic_load_explicit(&use_io_uring, memory_order_relaxed);
|
||||
|
||||
if (use == 0) {
|
||||
/* 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");
|
||||
if (val != NULL)
|
||||
use = atoi(val) ? 1 : -1;
|
||||
|
||||
use = val != NULL && atoi(val) > 0 ? 1 : -1;
|
||||
atomic_store_explicit(&use_io_uring, use, memory_order_relaxed);
|
||||
}
|
||||
|
||||
@ -513,22 +509,29 @@ static void uv__iou_init(int epollfd,
|
||||
size_t sqlen;
|
||||
size_t maxlen;
|
||||
size_t sqelen;
|
||||
unsigned kernel_version;
|
||||
uint32_t* sqarray;
|
||||
uint32_t i;
|
||||
char* sq;
|
||||
char* sqe;
|
||||
int ringfd;
|
||||
int no_sqarray;
|
||||
|
||||
sq = MAP_FAILED;
|
||||
sqe = MAP_FAILED;
|
||||
|
||||
if (!uv__use_io_uring())
|
||||
if (!uv__use_io_uring(flags))
|
||||
return;
|
||||
|
||||
kernel_version = uv__kernel_version();
|
||||
no_sqarray =
|
||||
UV__IORING_SETUP_NO_SQARRAY * (kernel_version >= /* 6.6 */0x060600);
|
||||
|
||||
/* SQPOLL required CAP_SYS_NICE until linux v5.12 relaxed that requirement.
|
||||
* Mostly academic because we check for a v5.13 kernel afterwards anyway.
|
||||
*/
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.flags = flags;
|
||||
params.flags = flags | no_sqarray;
|
||||
|
||||
if (flags & UV__IORING_SETUP_SQPOLL)
|
||||
params.sq_thread_idle = 10; /* milliseconds */
|
||||
@ -590,7 +593,6 @@ static void uv__iou_init(int epollfd,
|
||||
iou->sqhead = (uint32_t*) (sq + params.sq_off.head);
|
||||
iou->sqtail = (uint32_t*) (sq + params.sq_off.tail);
|
||||
iou->sqmask = *(uint32_t*) (sq + params.sq_off.ring_mask);
|
||||
iou->sqarray = (uint32_t*) (sq + params.sq_off.array);
|
||||
iou->sqflags = (uint32_t*) (sq + params.sq_off.flags);
|
||||
iou->cqhead = (uint32_t*) (sq + params.cq_off.head);
|
||||
iou->cqtail = (uint32_t*) (sq + params.cq_off.tail);
|
||||
@ -604,13 +606,13 @@ static void uv__iou_init(int epollfd,
|
||||
iou->sqelen = sqelen;
|
||||
iou->ringfd = ringfd;
|
||||
iou->in_flight = 0;
|
||||
iou->flags = 0;
|
||||
|
||||
if (uv__kernel_version() >= /* 5.15.0 */ 0x050F00)
|
||||
iou->flags |= UV__MKDIRAT_SYMLINKAT_LINKAT;
|
||||
if (no_sqarray)
|
||||
return;
|
||||
|
||||
sqarray = (uint32_t*) (sq + params.sq_off.array);
|
||||
for (i = 0; i <= iou->sqmask; i++)
|
||||
iou->sqarray[i] = i; /* Slot -> sqe identity mapping. */
|
||||
sqarray[i] = i; /* Slot -> sqe identity mapping. */
|
||||
|
||||
return;
|
||||
|
||||
@ -626,7 +628,7 @@ fail:
|
||||
|
||||
|
||||
static void uv__iou_delete(struct uv__iou* iou) {
|
||||
if (iou->ringfd != -1) {
|
||||
if (iou->ringfd > -1) {
|
||||
munmap(iou->sq, iou->maxlen);
|
||||
munmap(iou->sqe, iou->sqelen);
|
||||
uv__close(iou->ringfd);
|
||||
@ -640,7 +642,7 @@ int uv__platform_loop_init(uv_loop_t* loop) {
|
||||
|
||||
lfields = uv__get_internal_fields(loop);
|
||||
lfields->ctl.ringfd = -1;
|
||||
lfields->iou.ringfd = -1;
|
||||
lfields->iou.ringfd = -2; /* "uninitialized" */
|
||||
|
||||
loop->inotify_watchers = NULL;
|
||||
loop->inotify_fd = -1;
|
||||
@ -649,7 +651,6 @@ int uv__platform_loop_init(uv_loop_t* loop) {
|
||||
if (loop->backend_fd == -1)
|
||||
return UV__ERR(errno);
|
||||
|
||||
uv__iou_init(loop->backend_fd, &lfields->iou, 64, UV__IORING_SETUP_SQPOLL);
|
||||
uv__iou_init(loop->backend_fd, &lfields->ctl, 256, 0);
|
||||
|
||||
return 0;
|
||||
@ -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
|
||||
* in another process, causing repeated junk epoll events.
|
||||
*
|
||||
* Perform EPOLL_CTL_DEL immediately instead of going through
|
||||
* io_uring's submit queue, otherwise the file descriptor may
|
||||
* be closed by the time the kernel starts the operation.
|
||||
*
|
||||
* We pass in a dummy epoll_event, to work around a bug in old kernels.
|
||||
*
|
||||
* Work around a bug in kernels 3.10 to 3.19 where passing a struct that
|
||||
* has the EPOLLWAKEUP flag set generates spurious audit syslog warnings.
|
||||
*/
|
||||
memset(&dummy, 0, sizeof(dummy));
|
||||
|
||||
if (inv == NULL) {
|
||||
epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &dummy);
|
||||
} else {
|
||||
uv__epoll_ctl_prep(loop->backend_fd,
|
||||
&lfields->ctl,
|
||||
inv->prep,
|
||||
EPOLL_CTL_DEL,
|
||||
fd,
|
||||
&dummy);
|
||||
}
|
||||
epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &dummy);
|
||||
}
|
||||
|
||||
|
||||
@ -768,6 +763,22 @@ static struct uv__io_uring_sqe* uv__iou_get_sqe(struct uv__iou* iou,
|
||||
uint32_t mask;
|
||||
uint32_t slot;
|
||||
|
||||
/* Lazily create the ring. State machine: -2 means uninitialized, -1 means
|
||||
* initialization failed. Anything else is a valid ring file descriptor.
|
||||
*/
|
||||
if (iou->ringfd == -2) {
|
||||
/* By default, the SQPOLL is not created. Enable only if the loop is
|
||||
* configured with UV_LOOP_USE_IO_URING_SQPOLL 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)
|
||||
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;
|
||||
uv__queue_init(&req->work_req.wq);
|
||||
|
||||
uv__req_register(loop, req);
|
||||
uv__req_register(loop);
|
||||
iou->in_flight++;
|
||||
|
||||
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,
|
||||
uv_fs_t* req,
|
||||
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__iou* iou;
|
||||
|
||||
iou = &uv__get_internal_fields(loop)->iou;
|
||||
|
||||
if (!(iou->flags & UV__MKDIRAT_SYMLINKAT_LINKAT))
|
||||
if (uv__kernel_version() < /* 5.15.0 */0x050F00)
|
||||
return 0;
|
||||
|
||||
iou = &uv__get_internal_fields(loop)->iou;
|
||||
sqe = uv__iou_get_sqe(iou, loop, req);
|
||||
if (sqe == NULL)
|
||||
return 0;
|
||||
@ -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__iou* iou;
|
||||
|
||||
iou = &uv__get_internal_fields(loop)->iou;
|
||||
|
||||
if (!(iou->flags & UV__MKDIRAT_SYMLINKAT_LINKAT))
|
||||
if (uv__kernel_version() < /* 5.15.0 */0x050F00)
|
||||
return 0;
|
||||
|
||||
iou = &uv__get_internal_fields(loop)->iou;
|
||||
sqe = uv__iou_get_sqe(iou, loop, req);
|
||||
if (sqe == NULL)
|
||||
return 0;
|
||||
@ -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__iou* iou;
|
||||
|
||||
iou = &uv__get_internal_fields(loop)->iou;
|
||||
|
||||
if (!(iou->flags & UV__MKDIRAT_SYMLINKAT_LINKAT))
|
||||
if (uv__kernel_version() < /* 5.15.0 */0x050F00)
|
||||
return 0;
|
||||
|
||||
iou = &uv__get_internal_fields(loop)->iou;
|
||||
sqe = uv__iou_get_sqe(iou, loop, req);
|
||||
if (sqe == NULL)
|
||||
return 0;
|
||||
@ -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;
|
||||
assert(req->type == UV_FS);
|
||||
|
||||
uv__req_unregister(loop, req);
|
||||
uv__req_unregister(loop);
|
||||
iou->in_flight--;
|
||||
|
||||
/* If the op is not supported by the kernel retry using the thread pool */
|
||||
if (e->res == -EOPNOTSUPP) {
|
||||
uv__fs_post(loop, req);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* io_uring stores error codes as negative numbers, same as libuv. */
|
||||
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,
|
||||
struct uv__iou* ctl,
|
||||
struct epoll_event (*events)[256],
|
||||
@ -1216,45 +1254,28 @@ static void uv__epoll_ctl_prep(int epollfd,
|
||||
uint32_t mask;
|
||||
uint32_t slot;
|
||||
|
||||
if (ctl->ringfd == -1) {
|
||||
if (!epoll_ctl(epollfd, op, fd, e))
|
||||
return;
|
||||
assert(op == EPOLL_CTL_ADD || op == EPOLL_CTL_MOD);
|
||||
assert(ctl->ringfd != -1);
|
||||
|
||||
if (op == EPOLL_CTL_DEL)
|
||||
return; /* Ignore errors, may be racing with another thread. */
|
||||
mask = ctl->sqmask;
|
||||
slot = (*ctl->sqtail)++ & mask;
|
||||
|
||||
if (op != EPOLL_CTL_ADD)
|
||||
abort();
|
||||
pe = &(*events)[slot];
|
||||
*pe = *e;
|
||||
|
||||
if (errno != EEXIST)
|
||||
abort();
|
||||
sqe = ctl->sqe;
|
||||
sqe = &sqe[slot];
|
||||
|
||||
/* File descriptor that's been watched before, update event mask. */
|
||||
if (!epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, e))
|
||||
return;
|
||||
memset(sqe, 0, sizeof(*sqe));
|
||||
sqe->addr = (uintptr_t) pe;
|
||||
sqe->fd = epollfd;
|
||||
sqe->len = op;
|
||||
sqe->off = fd;
|
||||
sqe->opcode = UV__IORING_OP_EPOLL_CTL;
|
||||
sqe->user_data = op | slot << 2 | (int64_t) fd << 32;
|
||||
|
||||
abort();
|
||||
} else {
|
||||
mask = ctl->sqmask;
|
||||
slot = (*ctl->sqtail)++ & mask;
|
||||
|
||||
pe = &(*events)[slot];
|
||||
*pe = *e;
|
||||
|
||||
sqe = ctl->sqe;
|
||||
sqe = &sqe[slot];
|
||||
|
||||
memset(sqe, 0, sizeof(*sqe));
|
||||
sqe->addr = (uintptr_t) pe;
|
||||
sqe->fd = epollfd;
|
||||
sqe->len = op;
|
||||
sqe->off = fd;
|
||||
sqe->opcode = UV__IORING_OP_EPOLL_CTL;
|
||||
sqe->user_data = op | slot << 2 | (int64_t) fd << 32;
|
||||
|
||||
if ((*ctl->sqhead & mask) == (*ctl->sqtail & mask))
|
||||
uv__epoll_ctl_flush(epollfd, ctl, events);
|
||||
}
|
||||
if ((*ctl->sqhead & mask) == (*ctl->sqtail & mask))
|
||||
uv__epoll_ctl_flush(epollfd, ctl, events);
|
||||
}
|
||||
|
||||
|
||||
@ -1395,8 +1416,22 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
w->events = w->pevents;
|
||||
e.events = w->pevents;
|
||||
e.data.fd = w->fd;
|
||||
fd = w->fd;
|
||||
|
||||
uv__epoll_ctl_prep(epollfd, ctl, &prep, op, w->fd, &e);
|
||||
if (ctl->ringfd != -1) {
|
||||
uv__epoll_ctl_prep(epollfd, ctl, &prep, op, fd, &e);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!epoll_ctl(epollfd, op, fd, &e))
|
||||
continue;
|
||||
|
||||
assert(op == EPOLL_CTL_ADD);
|
||||
assert(errno == EEXIST);
|
||||
|
||||
/* File descriptor that's been watched before, update event mask. */
|
||||
if (epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &e))
|
||||
abort();
|
||||
}
|
||||
|
||||
inv.events = events;
|
||||
@ -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
|
||||
* 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;
|
||||
}
|
||||
|
||||
@ -1620,36 +1659,17 @@ done:
|
||||
int uv_resident_set_memory(size_t* rss) {
|
||||
char buf[1024];
|
||||
const char* s;
|
||||
ssize_t n;
|
||||
long val;
|
||||
int fd;
|
||||
int rc;
|
||||
int i;
|
||||
|
||||
do
|
||||
fd = open("/proc/self/stat", O_RDONLY);
|
||||
while (fd == -1 && errno == EINTR);
|
||||
/* rss: 24th element */
|
||||
rc = uv__slurp("/proc/self/stat", buf, sizeof(buf));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (fd == -1)
|
||||
return UV__ERR(errno);
|
||||
|
||||
do
|
||||
n = read(fd, buf, sizeof(buf) - 1);
|
||||
while (n == -1 && errno == EINTR);
|
||||
|
||||
uv__close(fd);
|
||||
if (n == -1)
|
||||
return UV__ERR(errno);
|
||||
buf[n] = '\0';
|
||||
|
||||
s = strchr(buf, ' ');
|
||||
if (s == NULL)
|
||||
goto err;
|
||||
|
||||
s += 1;
|
||||
if (*s != '(')
|
||||
goto err;
|
||||
|
||||
s = strchr(s, ')');
|
||||
/* find the last ')' */
|
||||
s = strrchr(buf, ')');
|
||||
if (s == NULL)
|
||||
goto err;
|
||||
|
||||
@ -1661,9 +1681,7 @@ int uv_resident_set_memory(size_t* rss) {
|
||||
|
||||
errno = 0;
|
||||
val = strtol(s, NULL, 10);
|
||||
if (errno != 0)
|
||||
goto err;
|
||||
if (val < 0)
|
||||
if (val < 0 || errno != 0)
|
||||
goto err;
|
||||
|
||||
*rss = val * getpagesize();
|
||||
@ -1696,16 +1714,22 @@ int uv_uptime(double* uptime) {
|
||||
int uv_cpu_info(uv_cpu_info_t** ci, int* count) {
|
||||
#if defined(__PPC__)
|
||||
static const char model_marker[] = "cpu\t\t: ";
|
||||
static const char model_marker2[] = "";
|
||||
#elif defined(__arm__)
|
||||
static const char model_marker[] = "Processor\t: ";
|
||||
static const char model_marker[] = "model name\t: ";
|
||||
static const char model_marker2[] = "Processor\t: ";
|
||||
#elif defined(__aarch64__)
|
||||
static const char model_marker[] = "CPU part\t: ";
|
||||
static const char model_marker2[] = "";
|
||||
#elif defined(__mips__)
|
||||
static const char model_marker[] = "cpu model\t\t: ";
|
||||
static const char model_marker2[] = "";
|
||||
#elif defined(__loongarch__)
|
||||
static const char model_marker[] = "cpu family\t\t: ";
|
||||
static const char model_marker2[] = "";
|
||||
#else
|
||||
static const char model_marker[] = "model name\t: ";
|
||||
static const char model_marker2[] = "";
|
||||
#endif
|
||||
static const char parts[] =
|
||||
#ifdef __aarch64__
|
||||
@ -1804,14 +1828,22 @@ int uv_cpu_info(uv_cpu_info_t** ci, int* count) {
|
||||
if (1 != fscanf(fp, "processor\t: %u\n", &cpu))
|
||||
break; /* Parse error. */
|
||||
|
||||
found = 0;
|
||||
while (!found && fgets(buf, sizeof(buf), fp))
|
||||
found = !strncmp(buf, model_marker, sizeof(model_marker) - 1);
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
if (!strncmp(buf, model_marker, sizeof(model_marker) - 1)) {
|
||||
p = buf + sizeof(model_marker) - 1;
|
||||
goto parts;
|
||||
}
|
||||
if (!*model_marker2)
|
||||
continue;
|
||||
if (!strncmp(buf, model_marker2, sizeof(model_marker2) - 1)) {
|
||||
p = buf + sizeof(model_marker2) - 1;
|
||||
goto parts;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
goto next;
|
||||
goto next; /* Not found. */
|
||||
|
||||
p = buf + sizeof(model_marker) - 1;
|
||||
parts:
|
||||
n = (int) strcspn(p, "\n");
|
||||
|
||||
/* arm64: translate CPU part code to model name. */
|
||||
@ -1908,7 +1940,6 @@ nocpuinfo:
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_IFADDRS_H
|
||||
static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
|
||||
if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
|
||||
return 1;
|
||||
@ -1922,18 +1953,16 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent, int 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) {
|
||||
#ifndef HAVE_IFADDRS_H
|
||||
*count = 0;
|
||||
*addresses = NULL;
|
||||
return UV_ENOSYS;
|
||||
#else
|
||||
struct ifaddrs *addrs, *ent;
|
||||
uv_interface_address_t* address;
|
||||
struct sockaddr_ll* sll;
|
||||
struct ifaddrs* addrs;
|
||||
struct ifaddrs* ent;
|
||||
size_t namelen;
|
||||
char* name;
|
||||
int i;
|
||||
struct sockaddr_ll *sll;
|
||||
|
||||
*count = 0;
|
||||
*addresses = NULL;
|
||||
@ -1942,10 +1971,12 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
return UV__ERR(errno);
|
||||
|
||||
/* Count the number of interfaces */
|
||||
namelen = 0;
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
|
||||
continue;
|
||||
|
||||
namelen += strlen(ent->ifa_name) + 1;
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
@ -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() */
|
||||
*addresses = uv__calloc(*count, sizeof(**addresses));
|
||||
if (!(*addresses)) {
|
||||
*addresses = uv__calloc(1, *count * sizeof(**addresses) + namelen);
|
||||
if (*addresses == NULL) {
|
||||
freeifaddrs(addrs);
|
||||
return UV_ENOMEM;
|
||||
}
|
||||
|
||||
name = (char*) &(*addresses)[*count];
|
||||
address = *addresses;
|
||||
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR))
|
||||
continue;
|
||||
|
||||
address->name = uv__strdup(ent->ifa_name);
|
||||
namelen = strlen(ent->ifa_name) + 1;
|
||||
address->name = memcpy(name, ent->ifa_name, namelen);
|
||||
name += namelen;
|
||||
|
||||
if (ent->ifa_addr->sa_family == AF_INET6) {
|
||||
address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
|
||||
@ -2008,18 +2042,12 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
freeifaddrs(addrs);
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* TODO(bnoordhuis) share with bsd-ifaddrs.c */
|
||||
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||
int count) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
uv__free(addresses[i].name);
|
||||
}
|
||||
|
||||
int count) {
|
||||
uv__free(addresses);
|
||||
}
|
||||
|
||||
@ -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]) {
|
||||
struct sysinfo info;
|
||||
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;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
if (option == UV_LOOP_USE_IO_URING_SQPOLL) {
|
||||
loop->flags |= UV_LOOP_ENABLE_IO_URING_SQPOLL;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if (option != UV_LOOP_BLOCK_SIGNAL)
|
||||
return UV_ENOSYS;
|
||||
|
||||
|
@ -211,8 +211,16 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
|
||||
|
||||
which[1] = HW_CPUSPEED;
|
||||
size = sizeof(cpuspeed);
|
||||
if (sysctl(which, ARRAY_SIZE(which), &cpuspeed, &size, NULL, 0))
|
||||
cpuspeed = 0;
|
||||
/*
|
||||
* HW_CPUSPEED can return EOPNOTSUPP if cpuspeed is 0,
|
||||
* so ignore that and continue the flow, because we
|
||||
* still care about the rest of the CPU info.
|
||||
*/
|
||||
if (sysctl(which, ARRAY_SIZE(which), &cpuspeed, &size, NULL, 0) &&
|
||||
(errno != EOPNOTSUPP)) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
size = sizeof(info);
|
||||
for (i = 0; i < numcpus; i++) {
|
||||
|
138
src/unix/pipe.c
138
src/unix/pipe.c
@ -30,6 +30,21 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
/* Does the file path contain embedded nul bytes? */
|
||||
static int includes_invalid_nul(const char *s, size_t n) {
|
||||
if (n == 0)
|
||||
return 0;
|
||||
#ifdef __linux__
|
||||
/* Accept abstract socket namespace paths, throughout which nul bytes have
|
||||
* no special significance ("\0foo\0bar").
|
||||
*/
|
||||
if (s[0] == '\0')
|
||||
return 0;
|
||||
#endif
|
||||
return NULL != memchr(s, '\0', n);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
handle->shutdown_req = NULL;
|
||||
@ -53,6 +68,7 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
||||
char* pipe_fname;
|
||||
int sockfd;
|
||||
int err;
|
||||
socklen_t addrlen;
|
||||
|
||||
pipe_fname = NULL;
|
||||
|
||||
@ -62,15 +78,17 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
||||
if (name == NULL)
|
||||
return UV_EINVAL;
|
||||
|
||||
/* namelen==0 on Linux means autobind the listen socket in the abstract
|
||||
* socket namespace, see `man 7 unix` for details.
|
||||
*/
|
||||
#if !defined(__linux__)
|
||||
if (namelen == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
#ifndef __linux__
|
||||
/* Abstract socket namespace only works on Linux. */
|
||||
if (*name == '\0')
|
||||
return UV_EINVAL;
|
||||
#endif
|
||||
|
||||
if (includes_invalid_nul(name, namelen))
|
||||
return UV_EINVAL;
|
||||
|
||||
if (flags & UV_PIPE_NO_TRUNCATE)
|
||||
if (namelen > sizeof(saddr.sun_path))
|
||||
return UV_EINVAL;
|
||||
@ -90,10 +108,15 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
||||
* We unlink the file later but abstract sockets disappear
|
||||
* automatically since they're not real file system entities.
|
||||
*/
|
||||
if (*name != '\0') {
|
||||
pipe_fname = uv__strdup(name);
|
||||
if (*name == '\0') {
|
||||
addrlen = offsetof(struct sockaddr_un, sun_path) + namelen;
|
||||
} else {
|
||||
pipe_fname = uv__malloc(namelen + 1);
|
||||
if (pipe_fname == NULL)
|
||||
return UV_ENOMEM;
|
||||
memcpy(pipe_fname, name, namelen);
|
||||
pipe_fname[namelen] = '\0';
|
||||
addrlen = sizeof saddr;
|
||||
}
|
||||
|
||||
err = uv__socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
@ -105,7 +128,7 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
||||
memcpy(&saddr.sun_path, name, namelen);
|
||||
saddr.sun_family = AF_UNIX;
|
||||
|
||||
if (bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr)) {
|
||||
if (bind(sockfd, (struct sockaddr*)&saddr, addrlen)) {
|
||||
err = UV__ERR(errno);
|
||||
/* Convert ENOENT to EACCES for compatibility with Windows. */
|
||||
if (err == UV_ENOENT)
|
||||
@ -117,7 +140,7 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
||||
|
||||
/* Success. */
|
||||
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;
|
||||
return 0;
|
||||
|
||||
@ -210,7 +233,22 @@ void uv_pipe_connect(uv_connect_t* req,
|
||||
uv_pipe_t* handle,
|
||||
const char* name,
|
||||
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 +262,7 @@ int uv_pipe_connect2(uv_connect_t* req,
|
||||
int new_sock;
|
||||
int err;
|
||||
int r;
|
||||
socklen_t addrlen;
|
||||
|
||||
if (flags & ~UV_PIPE_NO_TRUNCATE)
|
||||
return UV_EINVAL;
|
||||
@ -234,11 +273,8 @@ int uv_pipe_connect2(uv_connect_t* req,
|
||||
if (namelen == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
#ifndef __linux__
|
||||
/* Abstract socket namespace only works on Linux. */
|
||||
if (*name == '\0')
|
||||
if (includes_invalid_nul(name, namelen))
|
||||
return UV_EINVAL;
|
||||
#endif
|
||||
|
||||
if (flags & UV_PIPE_NO_TRUNCATE)
|
||||
if (namelen > sizeof(saddr.sun_path))
|
||||
@ -261,9 +297,13 @@ int uv_pipe_connect2(uv_connect_t* req,
|
||||
memcpy(&saddr.sun_path, name, namelen);
|
||||
saddr.sun_family = AF_UNIX;
|
||||
|
||||
if (*name == '\0')
|
||||
addrlen = offsetof(struct sockaddr_un, sun_path) + namelen;
|
||||
else
|
||||
addrlen = sizeof saddr;
|
||||
|
||||
do {
|
||||
r = connect(uv__stream_fd(handle),
|
||||
(struct sockaddr*)&saddr, sizeof saddr);
|
||||
r = connect(uv__stream_fd(handle), (struct sockaddr*)&saddr, addrlen);
|
||||
}
|
||||
while (r == -1 && errno == EINTR);
|
||||
|
||||
@ -295,7 +335,7 @@ out:
|
||||
handle->connect_req = req;
|
||||
|
||||
uv__req_init(handle->loop, req, UV_CONNECT);
|
||||
req->handle = (uv_stream_t*)handle;
|
||||
req->handle = (uv_stream_t*) handle;
|
||||
req->cb = cb;
|
||||
uv__queue_init(&req->queue);
|
||||
|
||||
@ -311,10 +351,20 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
|
||||
uv__peersockfunc func,
|
||||
char* buffer,
|
||||
size_t* size) {
|
||||
#if defined(__linux__)
|
||||
static const int is_linux = 1;
|
||||
#else
|
||||
static const int is_linux = 0;
|
||||
#endif
|
||||
struct sockaddr_un sa;
|
||||
socklen_t addrlen;
|
||||
size_t slop;
|
||||
char* p;
|
||||
int err;
|
||||
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
addrlen = sizeof(sa);
|
||||
memset(&sa, 0, addrlen);
|
||||
err = uv__getsockpeername((const uv_handle_t*) handle,
|
||||
@ -326,17 +376,20 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
|
||||
return err;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
if (sa.sun_path[0] == 0)
|
||||
/* Linux abstract namespace */
|
||||
slop = 1;
|
||||
if (is_linux && sa.sun_path[0] == '\0') {
|
||||
/* Linux abstract namespace. Not zero-terminated. */
|
||||
slop = 0;
|
||||
addrlen -= offsetof(struct sockaddr_un, sun_path);
|
||||
else
|
||||
#endif
|
||||
addrlen = strlen(sa.sun_path);
|
||||
} else {
|
||||
p = memchr(sa.sun_path, '\0', sizeof(sa.sun_path));
|
||||
if (p == NULL)
|
||||
p = ARRAY_END(sa.sun_path);
|
||||
addrlen = p - sa.sun_path;
|
||||
}
|
||||
|
||||
|
||||
if ((size_t)addrlen >= *size) {
|
||||
*size = addrlen + 1;
|
||||
if ((size_t)addrlen + slop > *size) {
|
||||
*size = addrlen + slop;
|
||||
return UV_ENOBUFS;
|
||||
}
|
||||
|
||||
@ -396,7 +449,7 @@ uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) {
|
||||
int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
|
||||
unsigned desired_mode;
|
||||
struct stat pipe_stat;
|
||||
char* name_buffer;
|
||||
char name_buffer[1 + UV__PATH_MAX];
|
||||
size_t name_len;
|
||||
int r;
|
||||
|
||||
@ -409,26 +462,14 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
|
||||
return UV_EINVAL;
|
||||
|
||||
/* Unfortunately fchmod does not work on all platforms, we will use chmod. */
|
||||
name_len = 0;
|
||||
r = uv_pipe_getsockname(handle, NULL, &name_len);
|
||||
if (r != UV_ENOBUFS)
|
||||
return r;
|
||||
|
||||
name_buffer = uv__malloc(name_len);
|
||||
if (name_buffer == NULL)
|
||||
return UV_ENOMEM;
|
||||
|
||||
name_len = sizeof(name_buffer);
|
||||
r = uv_pipe_getsockname(handle, name_buffer, &name_len);
|
||||
if (r != 0) {
|
||||
uv__free(name_buffer);
|
||||
if (r != 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* stat must be used as fstat has a bug on Darwin */
|
||||
if (uv__stat(name_buffer, &pipe_stat) == -1) {
|
||||
uv__free(name_buffer);
|
||||
return -errno;
|
||||
}
|
||||
if (uv__stat(name_buffer, &pipe_stat) == -1)
|
||||
return UV__ERR(errno);
|
||||
|
||||
desired_mode = 0;
|
||||
if (mode & UV_READABLE)
|
||||
@ -437,15 +478,12 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
|
||||
desired_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
|
||||
|
||||
/* Exit early if pipe already has desired mode. */
|
||||
if ((pipe_stat.st_mode & desired_mode) == desired_mode) {
|
||||
uv__free(name_buffer);
|
||||
if ((pipe_stat.st_mode & desired_mode) == desired_mode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
pipe_stat.st_mode |= desired_mode;
|
||||
|
||||
r = chmod(name_buffer, pipe_stat.st_mode);
|
||||
uv__free(name_buffer);
|
||||
|
||||
return r != -1 ? 0 : UV__ERR(errno);
|
||||
}
|
||||
@ -454,7 +492,11 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) {
|
||||
int uv_pipe(uv_os_fd_t fds[2], int read_flags, int write_flags) {
|
||||
uv_os_fd_t temp[2];
|
||||
int err;
|
||||
#if defined(__FreeBSD__) || defined(__linux__)
|
||||
#if defined(__linux__) || \
|
||||
defined(__FreeBSD__) || \
|
||||
defined(__OpenBSD__) || \
|
||||
defined(__DragonFly__) || \
|
||||
defined(__NetBSD__)
|
||||
int flags = O_CLOEXEC;
|
||||
|
||||
if ((read_flags & UV_NONBLOCK_PIPE) && (write_flags & UV_NONBLOCK_PIPE))
|
||||
|
@ -24,7 +24,6 @@
|
||||
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
|
||||
static void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
|
||||
|
@ -55,7 +55,8 @@
|
||||
extern char **environ;
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
#if defined(__linux__) || \
|
||||
defined(__GNU__)
|
||||
# include <grp.h>
|
||||
#endif
|
||||
|
||||
@ -63,11 +64,7 @@ extern char **environ;
|
||||
# include "zos-base.h"
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) || \
|
||||
defined(__DragonFly__) || \
|
||||
defined(__FreeBSD__) || \
|
||||
defined(__NetBSD__) || \
|
||||
defined(__OpenBSD__)
|
||||
#ifdef UV_HAVE_KQUEUE
|
||||
#include <sys/event.h>
|
||||
#else
|
||||
#define UV_USE_SIGCHLD
|
||||
@ -191,8 +188,12 @@ void uv__wait_children(uv_loop_t* loop) {
|
||||
static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) {
|
||||
int mask;
|
||||
int fd;
|
||||
int ret;
|
||||
int size;
|
||||
int i;
|
||||
|
||||
mask = UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD | UV_INHERIT_STREAM;
|
||||
size = 64 * 1024;
|
||||
|
||||
switch (container->flags & mask) {
|
||||
case UV_IGNORE:
|
||||
@ -202,8 +203,17 @@ static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) {
|
||||
assert(container->data.stream != NULL);
|
||||
if (container->data.stream->type != UV_NAMED_PIPE)
|
||||
return UV_EINVAL;
|
||||
else
|
||||
return uv_socketpair(SOCK_STREAM, 0, fds, 0, 0);
|
||||
else {
|
||||
ret = uv_socketpair(SOCK_STREAM, 0, fds, 0, 0);
|
||||
|
||||
if (ret == 0)
|
||||
for (i = 0; i < 2; i++) {
|
||||
setsockopt(fds[i], SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
|
||||
setsockopt(fds[i], SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
case UV_INHERIT_FD:
|
||||
case UV_INHERIT_STREAM:
|
||||
@ -972,6 +982,7 @@ int uv_spawn(uv_loop_t* loop,
|
||||
assert(!(options->flags & ~(UV_PROCESS_DETACHED |
|
||||
UV_PROCESS_SETGID |
|
||||
UV_PROCESS_SETUID |
|
||||
UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME |
|
||||
UV_PROCESS_WINDOWS_HIDE |
|
||||
UV_PROCESS_WINDOWS_HIDE_CONSOLE |
|
||||
UV_PROCESS_WINDOWS_HIDE_GUI |
|
||||
|
@ -195,7 +195,7 @@ static void uv__signal_handler(int signum) {
|
||||
|
||||
for (handle = uv__signal_first_handle(signum);
|
||||
handle != NULL && handle->signum == signum;
|
||||
handle = RB_NEXT(uv__signal_tree_s, &uv__signal_tree, handle)) {
|
||||
handle = RB_NEXT(uv__signal_tree_s, handle)) {
|
||||
int r;
|
||||
|
||||
msg.signum = signum;
|
||||
|
@ -457,7 +457,7 @@ void uv__stream_destroy(uv_stream_t* stream) {
|
||||
assert(stream->flags & UV_HANDLE_CLOSED);
|
||||
|
||||
if (stream->connect_req) {
|
||||
uv__req_unregister(stream->loop, stream->connect_req);
|
||||
uv__req_unregister(stream->loop);
|
||||
stream->connect_req->cb(stream->connect_req, UV_ECANCELED);
|
||||
stream->connect_req = NULL;
|
||||
}
|
||||
@ -642,7 +642,7 @@ static void uv__drain(uv_stream_t* stream) {
|
||||
if ((stream->flags & UV_HANDLE_CLOSING) ||
|
||||
!(stream->flags & UV_HANDLE_SHUT)) {
|
||||
stream->shutdown_req = NULL;
|
||||
uv__req_unregister(stream->loop, req);
|
||||
uv__req_unregister(stream->loop);
|
||||
|
||||
err = 0;
|
||||
if (stream->flags & UV_HANDLE_CLOSING)
|
||||
@ -698,7 +698,8 @@ static int uv__write_req_update(uv_stream_t* stream,
|
||||
|
||||
do {
|
||||
len = n < buf->len ? n : buf->len;
|
||||
buf->base += len;
|
||||
if (buf->len != 0)
|
||||
buf->base += len;
|
||||
buf->len -= len;
|
||||
buf += (buf->len == 0); /* Advance to next buffer if this one is empty. */
|
||||
n -= len;
|
||||
@ -912,7 +913,7 @@ static void uv__write_callbacks(uv_stream_t* stream) {
|
||||
q = uv__queue_head(&pq);
|
||||
req = uv__queue_data(q, uv_write_t, queue);
|
||||
uv__queue_remove(q);
|
||||
uv__req_unregister(stream->loop, req);
|
||||
uv__req_unregister(stream->loop);
|
||||
|
||||
if (req->bufs != NULL) {
|
||||
stream->write_queue_size -= uv__write_req_size(req);
|
||||
@ -979,11 +980,13 @@ static int uv__stream_queue_fd(uv_stream_t* stream, int fd) {
|
||||
|
||||
static int uv__stream_recv_cmsg(uv_stream_t* stream, struct msghdr* msg) {
|
||||
struct cmsghdr* cmsg;
|
||||
char* p;
|
||||
char* pe;
|
||||
int fd;
|
||||
int err;
|
||||
size_t i;
|
||||
size_t count;
|
||||
|
||||
err = 0;
|
||||
for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {
|
||||
if (cmsg->cmsg_type != SCM_RIGHTS) {
|
||||
fprintf(stderr, "ignoring non-SCM_RIGHTS ancillary data: %d\n",
|
||||
@ -996,24 +999,26 @@ static int uv__stream_recv_cmsg(uv_stream_t* stream, struct msghdr* msg) {
|
||||
assert(count % sizeof(fd) == 0);
|
||||
count /= sizeof(fd);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
memcpy(&fd, (char*) CMSG_DATA(cmsg) + i * sizeof(fd), sizeof(fd));
|
||||
/* Already has accepted fd, queue now */
|
||||
if (stream->accepted_fd != -1) {
|
||||
err = uv__stream_queue_fd(stream, fd);
|
||||
if (err != 0) {
|
||||
/* Close rest */
|
||||
for (; i < count; i++)
|
||||
uv__close(fd);
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
stream->accepted_fd = fd;
|
||||
p = (void*) CMSG_DATA(cmsg);
|
||||
pe = p + count * sizeof(fd);
|
||||
|
||||
while (p < pe) {
|
||||
memcpy(&fd, p, sizeof(fd));
|
||||
p += sizeof(fd);
|
||||
|
||||
if (err == 0) {
|
||||
if (stream->accepted_fd == -1)
|
||||
stream->accepted_fd = fd;
|
||||
else
|
||||
err = uv__stream_queue_fd(stream, fd);
|
||||
}
|
||||
|
||||
if (err != 0)
|
||||
uv__close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@ -1268,7 +1273,7 @@ static void uv__stream_connect(uv_stream_t* stream) {
|
||||
return;
|
||||
|
||||
stream->connect_req = NULL;
|
||||
uv__req_unregister(stream->loop, req);
|
||||
uv__req_unregister(stream->loop);
|
||||
|
||||
if (error < 0 || uv__queue_empty(&stream->write_queue)) {
|
||||
uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT);
|
||||
|
@ -826,6 +826,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
uv_interface_address_t* address;
|
||||
struct ifaddrs* addrs;
|
||||
struct ifaddrs* ent;
|
||||
size_t namelen;
|
||||
char* name;
|
||||
|
||||
*count = 0;
|
||||
*addresses = NULL;
|
||||
@ -834,9 +836,11 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
return UV__ERR(errno);
|
||||
|
||||
/* Count the number of interfaces */
|
||||
namelen = 0;
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
if (uv__ifaddr_exclude(ent))
|
||||
continue;
|
||||
namelen += strlen(ent->ifa_name) + 1;
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
@ -845,19 +849,22 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*addresses = uv__malloc(*count * sizeof(**addresses));
|
||||
if (!(*addresses)) {
|
||||
*addresses = uv__calloc(1, *count * sizeof(**addresses) + namelen);
|
||||
if (*addresses == NULL) {
|
||||
freeifaddrs(addrs);
|
||||
return UV_ENOMEM;
|
||||
}
|
||||
|
||||
name = (char*) &(*addresses)[*count];
|
||||
address = *addresses;
|
||||
|
||||
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
|
||||
if (uv__ifaddr_exclude(ent))
|
||||
continue;
|
||||
|
||||
address->name = uv__strdup(ent->ifa_name);
|
||||
namelen = strlen(ent->ifa_name) + 1;
|
||||
address->name = memcpy(name, ent->ifa_name, namelen);
|
||||
name += namelen;
|
||||
|
||||
if (ent->ifa_addr->sa_family == AF_INET6) {
|
||||
address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr);
|
||||
@ -885,13 +892,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
|
||||
#endif /* SUNOS_NO_IFADDRS */
|
||||
|
||||
void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||
int count) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
uv__free(addresses[i].name);
|
||||
}
|
||||
|
||||
int count) {
|
||||
uv__free(addresses);
|
||||
}
|
||||
|
||||
|
145
src/unix/tcp.c
145
src/unix/tcp.c
@ -30,12 +30,8 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#if defined(__PASE__)
|
||||
#include <as400_protos.h>
|
||||
#define ifaddrs ifaddrs_pase
|
||||
#define getifaddrs Qp2getifaddrs
|
||||
#define freeifaddrs Qp2freeifaddrs
|
||||
#else
|
||||
/* ifaddrs is not implemented on AIX and IBM i PASE */
|
||||
#if !defined(_AIX)
|
||||
#include <ifaddrs.h>
|
||||
#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)))
|
||||
return UV__ERR(errno);
|
||||
|
||||
if (flags & UV_TCP_REUSEPORT) {
|
||||
err = uv__sock_reuseport(tcp->io_watcher.fd);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifndef __OpenBSD__
|
||||
#ifdef IPV6_V6ONLY
|
||||
if (addr->sa_family == AF_INET6) {
|
||||
@ -225,16 +227,39 @@ static int uv__is_ipv6_link_local(const struct sockaddr* addr) {
|
||||
|
||||
static int uv__ipv6_link_local_scope_id(void) {
|
||||
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* p;
|
||||
int rv;
|
||||
|
||||
if (getifaddrs(&ifa))
|
||||
return 0;
|
||||
|
||||
for (p = ifa; p != NULL; p = p->ifa_next)
|
||||
if (uv__is_ipv6_link_local(p->ifa_addr))
|
||||
break;
|
||||
if (p->ifa_addr != NULL)
|
||||
if (uv__is_ipv6_link_local(p->ifa_addr))
|
||||
break;
|
||||
|
||||
rv = 0;
|
||||
if (p != NULL) {
|
||||
@ -243,6 +268,8 @@ static int uv__ipv6_link_local_scope_id(void) {
|
||||
}
|
||||
|
||||
freeifaddrs(ifa);
|
||||
#endif /* defined(_AIX) */
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -431,33 +458,110 @@ int uv__tcp_nodelay(int fd, int on) {
|
||||
}
|
||||
|
||||
|
||||
#if (defined(UV__SOLARIS_11_4) && !UV__SOLARIS_11_4) || \
|
||||
(defined(__DragonFly__) && __DragonFly_version < 500702)
|
||||
/* DragonFlyBSD <500702 and Solaris <11.4 require millisecond units
|
||||
* for TCP keepalive options. */
|
||||
#define UV_KEEPALIVE_FACTOR(x) (x *= 1000)
|
||||
#else
|
||||
#define UV_KEEPALIVE_FACTOR(x)
|
||||
#endif
|
||||
int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
|
||||
int idle;
|
||||
int intvl;
|
||||
int cnt;
|
||||
|
||||
(void) &idle;
|
||||
(void) &intvl;
|
||||
(void) &cnt;
|
||||
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)))
|
||||
return UV__ERR(errno);
|
||||
|
||||
if (!on)
|
||||
return 0;
|
||||
|
||||
#ifdef TCP_KEEPIDLE
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay)))
|
||||
if (delay < 1)
|
||||
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);
|
||||
/* Solaris/SmartOS, if you don't support keep-alive,
|
||||
* then don't advertise it in your system headers...
|
||||
*/
|
||||
/* FIXME(bnoordhuis) That's possibly because sizeof(delay) should be 1. */
|
||||
#elif defined(TCP_KEEPALIVE) && !defined(__sun)
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay)))
|
||||
|
||||
intvl = 10; /* required at least 10 seconds */
|
||||
UV_KEEPALIVE_FACTOR(intvl);
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl)))
|
||||
return UV__ERR(errno);
|
||||
|
||||
cnt = 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);
|
||||
#endif
|
||||
|
||||
#ifdef TCP_KEEPINTVL
|
||||
intvl = 1; /* 1 second; same as default on Win32 */
|
||||
intvl = 1; /* 1 second; same as default on Win32 */
|
||||
UV_KEEPALIVE_FACTOR(intvl);
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl)))
|
||||
return UV__ERR(errno);
|
||||
#endif
|
||||
@ -468,6 +572,7 @@ int uv__tcp_keepalive(int fd, int on, unsigned int delay) {
|
||||
return UV__ERR(errno);
|
||||
#endif
|
||||
|
||||
#endif /* !defined(__sun) */
|
||||
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) {
|
||||
uv_os_sock_t temp[2];
|
||||
int err;
|
||||
#if defined(__FreeBSD__) || defined(__linux__)
|
||||
#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC)
|
||||
int flags;
|
||||
|
||||
flags = type | SOCK_CLOEXEC;
|
||||
|
@ -23,6 +23,9 @@
|
||||
#include "internal.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#ifdef __OpenBSD__
|
||||
#include <pthread_np.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
@ -126,6 +129,12 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
|
||||
return uv_thread_create_ex(tid, ¶ms, entry, arg);
|
||||
}
|
||||
|
||||
|
||||
int uv_thread_detach(uv_thread_t *tid) {
|
||||
return UV__ERR(pthread_detach(*tid));
|
||||
}
|
||||
|
||||
|
||||
int uv_thread_create_ex(uv_thread_t* tid,
|
||||
const uv_thread_options_t* params,
|
||||
void (*entry)(void *arg),
|
||||
@ -205,7 +214,7 @@ int uv_thread_setaffinity(uv_thread_t* tid,
|
||||
if (cpumask[i])
|
||||
CPU_SET(i, &cpuset);
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#if defined(__ANDROID__) || defined(__OHOS__)
|
||||
if (sched_setaffinity(pthread_gettid_np(*tid), sizeof(cpuset), &cpuset))
|
||||
r = errno;
|
||||
else
|
||||
@ -233,7 +242,7 @@ int uv_thread_getaffinity(uv_thread_t* tid,
|
||||
return UV_EINVAL;
|
||||
|
||||
CPU_ZERO(&cpuset);
|
||||
#if defined(__ANDROID__)
|
||||
#if defined(__ANDROID__) || defined(__OHOS__)
|
||||
if (sched_getaffinity(pthread_gettid_np(*tid), sizeof(cpuset), &cpuset))
|
||||
r = errno;
|
||||
else
|
||||
@ -291,6 +300,18 @@ int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) {
|
||||
return pthread_equal(*t1, *t2);
|
||||
}
|
||||
|
||||
int uv_thread_setname(const char* name) {
|
||||
if (name == NULL)
|
||||
return UV_EINVAL;
|
||||
return uv__thread_setname(name);
|
||||
}
|
||||
|
||||
int uv_thread_getname(uv_thread_t* tid, char* name, size_t size) {
|
||||
if (name == NULL || size == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
return uv__thread_getname(tid, name, size);
|
||||
}
|
||||
|
||||
int uv_mutex_init(uv_mutex_t* mutex) {
|
||||
#if defined(NDEBUG) || !defined(PTHREAD_MUTEX_ERRORCHECK)
|
||||
@ -875,3 +896,80 @@ void uv_key_set(uv_key_t* key, void* value) {
|
||||
if (pthread_setspecific(*key, value))
|
||||
abort();
|
||||
}
|
||||
|
||||
#if defined(_AIX) || defined(__MVS__) || defined(__PASE__)
|
||||
int uv__thread_setname(const char* name) {
|
||||
return UV_ENOSYS;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
int uv__thread_setname(const char* name) {
|
||||
char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
strncpy(namebuf, name, sizeof(namebuf) - 1);
|
||||
namebuf[sizeof(namebuf) - 1] = '\0';
|
||||
int err = pthread_setname_np(namebuf);
|
||||
if (err)
|
||||
return UV__ERR(errno);
|
||||
return 0;
|
||||
}
|
||||
#elif defined(__NetBSD__)
|
||||
int uv__thread_setname(const char* name) {
|
||||
char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
strncpy(namebuf, name, sizeof(namebuf) - 1);
|
||||
namebuf[sizeof(namebuf) - 1] = '\0';
|
||||
return UV__ERR(pthread_setname_np(pthread_self(), "%s", namebuf));
|
||||
}
|
||||
#elif defined(__OpenBSD__)
|
||||
int uv__thread_setname(const char* name) {
|
||||
char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
strncpy(namebuf, name, sizeof(namebuf) - 1);
|
||||
namebuf[sizeof(namebuf) - 1] = '\0';
|
||||
pthread_set_name_np(pthread_self(), namebuf);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int uv__thread_setname(const char* name) {
|
||||
char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
strncpy(namebuf, name, sizeof(namebuf) - 1);
|
||||
namebuf[sizeof(namebuf) - 1] = '\0';
|
||||
return UV__ERR(pthread_setname_np(pthread_self(), namebuf));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (defined(__ANDROID_API__) && __ANDROID_API__ < 26) || \
|
||||
defined(_AIX) || \
|
||||
defined(__MVS__) || \
|
||||
defined(__PASE__)
|
||||
int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) {
|
||||
return UV_ENOSYS;
|
||||
}
|
||||
#elif defined(__OpenBSD__)
|
||||
int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) {
|
||||
char thread_name[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
pthread_get_name_np(*tid, thread_name, sizeof(thread_name));
|
||||
strncpy(name, thread_name, size - 1);
|
||||
name[size - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) {
|
||||
char thread_name[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
if (pthread_getname_np(*tid, thread_name, sizeof(thread_name)) != 0)
|
||||
return UV__ERR(errno);
|
||||
|
||||
strncpy(name, thread_name, size - 1);
|
||||
name[size - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
int uv__thread_getname(uv_thread_t* tid, char* name, size_t size) {
|
||||
int r;
|
||||
char thread_name[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
r = pthread_getname_np(*tid, thread_name, sizeof(thread_name));
|
||||
if (r != 0)
|
||||
return UV__ERR(r);
|
||||
|
||||
strncpy(name, thread_name, size - 1);
|
||||
name[size - 1] = '\0';
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
@ -284,6 +284,11 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
|
||||
int fd;
|
||||
int rc;
|
||||
|
||||
if (uv__is_raw_tty_mode(mode)) {
|
||||
/* There is only a single raw TTY mode on UNIX. */
|
||||
mode = UV_TTY_MODE_RAW;
|
||||
}
|
||||
|
||||
if (tty->mode == (int) mode)
|
||||
return 0;
|
||||
|
||||
@ -324,6 +329,8 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
|
||||
case UV_TTY_MODE_IO:
|
||||
uv__tty_make_raw(&tmp);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
/* Apply changes after draining */
|
||||
@ -335,6 +342,37 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
|
||||
}
|
||||
|
||||
|
||||
void uv__tty_close(uv_tty_t* handle) {
|
||||
int expected;
|
||||
int fd;
|
||||
|
||||
fd = handle->io_watcher.fd;
|
||||
if (fd == -1)
|
||||
goto done;
|
||||
|
||||
/* This is used for uv_tty_reset_mode() */
|
||||
do
|
||||
expected = 0;
|
||||
while (!atomic_compare_exchange_strong(&termios_spinlock, &expected, 1));
|
||||
|
||||
if (fd == orig_termios_fd) {
|
||||
/* XXX(bnoordhuis) the tcsetattr is probably wrong when there are still
|
||||
* other uv_tty_t handles active that refer to the same tty/pty but it's
|
||||
* hard to recognize that particular situation without maintaining some
|
||||
* kind of process-global data structure, and that still won't work in a
|
||||
* multi-process setup.
|
||||
*/
|
||||
uv__tcsetattr(fd, TCSANOW, &orig_termios);
|
||||
orig_termios_fd = -1;
|
||||
}
|
||||
|
||||
atomic_store(&termios_spinlock, 0);
|
||||
|
||||
done:
|
||||
uv__stream_close((uv_stream_t*) handle);
|
||||
}
|
||||
|
||||
|
||||
int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) {
|
||||
struct winsize ws;
|
||||
int err;
|
||||
@ -452,7 +490,7 @@ int uv_tty_reset_mode(void) {
|
||||
saved_errno = errno;
|
||||
|
||||
if (atomic_exchange(&termios_spinlock, 1))
|
||||
return UV_EBUSY; /* In uv_tty_set_mode(). */
|
||||
return UV_EBUSY; /* In uv_tty_set_mode() or uv__tty_close(). */
|
||||
|
||||
err = 0;
|
||||
if (orig_termios_fd != -1)
|
||||
|
438
src/unix/udp.c
438
src/unix/udp.c
@ -47,6 +47,10 @@ static void uv__udp_sendmsg(uv_udp_t* handle);
|
||||
static int uv__udp_maybe_deferred_bind(uv_udp_t* handle,
|
||||
int domain,
|
||||
unsigned int flags);
|
||||
static int uv__udp_sendmsg1(int fd,
|
||||
const uv_buf_t* bufs,
|
||||
unsigned int nbufs,
|
||||
const struct sockaddr* addr);
|
||||
|
||||
|
||||
void uv__udp_close(uv_udp_t* handle) {
|
||||
@ -100,7 +104,7 @@ static void uv__udp_run_completed(uv_udp_t* handle) {
|
||||
uv__queue_remove(q);
|
||||
|
||||
req = uv__queue_data(q, uv_udp_send_t, queue);
|
||||
uv__req_unregister(handle->loop, req);
|
||||
uv__req_unregister(handle->loop);
|
||||
|
||||
handle->send_queue_size -= uv__count_bufs(req->bufs, req->nbufs);
|
||||
handle->send_queue_count--;
|
||||
@ -141,14 +145,14 @@ static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents) {
|
||||
if (revents & POLLIN)
|
||||
uv__udp_recvmsg(handle);
|
||||
|
||||
if (revents & POLLOUT) {
|
||||
if (revents & POLLOUT && !uv__is_closing(handle)) {
|
||||
uv__udp_sendmsg(handle);
|
||||
uv__udp_run_completed(handle);
|
||||
}
|
||||
}
|
||||
|
||||
static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) {
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
|
||||
struct sockaddr_in6 peers[20];
|
||||
struct iovec iov[ARRAY_SIZE(peers)];
|
||||
struct mmsghdr msgs[ARRAY_SIZE(peers)];
|
||||
@ -173,11 +177,18 @@ static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) {
|
||||
msgs[k].msg_hdr.msg_control = NULL;
|
||||
msgs[k].msg_hdr.msg_controllen = 0;
|
||||
msgs[k].msg_hdr.msg_flags = 0;
|
||||
msgs[k].msg_len = 0;
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
do
|
||||
nread = recvmsg_x(handle->io_watcher.fd, msgs, chunks, MSG_DONTWAIT);
|
||||
while (nread == -1 && errno == EINTR);
|
||||
#else
|
||||
do
|
||||
nread = recvmmsg(handle->io_watcher.fd, msgs, chunks, 0, NULL);
|
||||
while (nread == -1 && errno == EINTR);
|
||||
#endif
|
||||
|
||||
if (nread < 1) {
|
||||
if (nread == 0 || errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
@ -204,9 +215,9 @@ static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) {
|
||||
handle->recv_cb(handle, 0, buf, NULL, UV_UDP_MMSG_FREE);
|
||||
}
|
||||
return nread;
|
||||
#else /* __linux__ || ____FreeBSD__ */
|
||||
#else /* __linux__ || ____FreeBSD__ || __APPLE__ */
|
||||
return UV_ENOSYS;
|
||||
#endif /* __linux__ || ____FreeBSD__ */
|
||||
#endif /* __linux__ || ____FreeBSD__ || __APPLE__ */
|
||||
}
|
||||
|
||||
static void uv__udp_recvmsg(uv_udp_t* handle) {
|
||||
@ -275,164 +286,25 @@ static void uv__udp_recvmsg(uv_udp_t* handle) {
|
||||
&& handle->recv_cb != NULL);
|
||||
}
|
||||
|
||||
static void uv__udp_sendmsg(uv_udp_t* handle) {
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
uv_udp_send_t* req;
|
||||
struct mmsghdr h[20];
|
||||
struct mmsghdr* p;
|
||||
struct uv__queue* q;
|
||||
ssize_t npkts;
|
||||
size_t pkts;
|
||||
size_t i;
|
||||
|
||||
if (uv__queue_empty(&handle->write_queue))
|
||||
return;
|
||||
|
||||
write_queue_drain:
|
||||
for (pkts = 0, q = uv__queue_head(&handle->write_queue);
|
||||
pkts < ARRAY_SIZE(h) && q != &handle->write_queue;
|
||||
++pkts, q = uv__queue_head(q)) {
|
||||
assert(q != NULL);
|
||||
req = uv__queue_data(q, uv_udp_send_t, queue);
|
||||
assert(req != NULL);
|
||||
|
||||
p = &h[pkts];
|
||||
memset(p, 0, sizeof(*p));
|
||||
if (req->addr.ss_family == AF_UNSPEC) {
|
||||
p->msg_hdr.msg_name = NULL;
|
||||
p->msg_hdr.msg_namelen = 0;
|
||||
} else {
|
||||
p->msg_hdr.msg_name = &req->addr;
|
||||
if (req->addr.ss_family == AF_INET6)
|
||||
p->msg_hdr.msg_namelen = sizeof(struct sockaddr_in6);
|
||||
else if (req->addr.ss_family == AF_INET)
|
||||
p->msg_hdr.msg_namelen = sizeof(struct sockaddr_in);
|
||||
else if (req->addr.ss_family == AF_UNIX)
|
||||
p->msg_hdr.msg_namelen = sizeof(struct sockaddr_un);
|
||||
else {
|
||||
assert(0 && "unsupported address family");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
h[pkts].msg_hdr.msg_iov = (struct iovec*) req->bufs;
|
||||
h[pkts].msg_hdr.msg_iovlen = req->nbufs;
|
||||
}
|
||||
|
||||
do
|
||||
npkts = sendmmsg(handle->io_watcher.fd, h, pkts, 0);
|
||||
while (npkts == -1 && errno == EINTR);
|
||||
|
||||
if (npkts < 1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
|
||||
return;
|
||||
for (i = 0, q = uv__queue_head(&handle->write_queue);
|
||||
i < pkts && q != &handle->write_queue;
|
||||
++i, q = uv__queue_head(&handle->write_queue)) {
|
||||
assert(q != NULL);
|
||||
req = uv__queue_data(q, uv_udp_send_t, queue);
|
||||
assert(req != NULL);
|
||||
|
||||
req->status = UV__ERR(errno);
|
||||
uv__queue_remove(&req->queue);
|
||||
uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
|
||||
}
|
||||
uv__io_feed(handle->loop, &handle->io_watcher);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Safety: npkts known to be >0 below. Hence cast from ssize_t
|
||||
* to size_t safe.
|
||||
*/
|
||||
for (i = 0, q = uv__queue_head(&handle->write_queue);
|
||||
i < (size_t)npkts && q != &handle->write_queue;
|
||||
++i, q = uv__queue_head(&handle->write_queue)) {
|
||||
assert(q != NULL);
|
||||
req = uv__queue_data(q, uv_udp_send_t, queue);
|
||||
assert(req != NULL);
|
||||
|
||||
req->status = req->bufs[0].len;
|
||||
|
||||
/* Sending a datagram is an atomic operation: either all data
|
||||
* is written or nothing is (and EMSGSIZE is raised). That is
|
||||
* why we don't handle partial writes. Just pop the request
|
||||
* off the write queue and onto the completed queue, done.
|
||||
*/
|
||||
uv__queue_remove(&req->queue);
|
||||
uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
|
||||
}
|
||||
|
||||
/* couldn't batch everything, continue sending (jump to avoid stack growth) */
|
||||
if (!uv__queue_empty(&handle->write_queue))
|
||||
goto write_queue_drain;
|
||||
uv__io_feed(handle->loop, &handle->io_watcher);
|
||||
#else /* __linux__ || ____FreeBSD__ */
|
||||
uv_udp_send_t* req;
|
||||
struct msghdr h;
|
||||
struct uv__queue* q;
|
||||
ssize_t size;
|
||||
|
||||
while (!uv__queue_empty(&handle->write_queue)) {
|
||||
q = uv__queue_head(&handle->write_queue);
|
||||
assert(q != NULL);
|
||||
|
||||
req = uv__queue_data(q, uv_udp_send_t, queue);
|
||||
assert(req != NULL);
|
||||
|
||||
memset(&h, 0, sizeof h);
|
||||
if (req->addr.ss_family == AF_UNSPEC) {
|
||||
h.msg_name = NULL;
|
||||
h.msg_namelen = 0;
|
||||
} else {
|
||||
h.msg_name = &req->addr;
|
||||
if (req->addr.ss_family == AF_INET6)
|
||||
h.msg_namelen = sizeof(struct sockaddr_in6);
|
||||
else if (req->addr.ss_family == AF_INET)
|
||||
h.msg_namelen = sizeof(struct sockaddr_in);
|
||||
else if (req->addr.ss_family == AF_UNIX)
|
||||
h.msg_namelen = sizeof(struct sockaddr_un);
|
||||
else {
|
||||
assert(0 && "unsupported address family");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
h.msg_iov = (struct iovec*) req->bufs;
|
||||
h.msg_iovlen = req->nbufs;
|
||||
|
||||
do {
|
||||
size = sendmsg(handle->io_watcher.fd, &h, 0);
|
||||
} while (size == -1 && errno == EINTR);
|
||||
|
||||
if (size == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
|
||||
break;
|
||||
}
|
||||
|
||||
req->status = (size == -1 ? UV__ERR(errno) : size);
|
||||
|
||||
/* Sending a datagram is an atomic operation: either all data
|
||||
* is written or nothing is (and EMSGSIZE is raised). That is
|
||||
* why we don't handle partial writes. Just pop the request
|
||||
* off the write queue and onto the completed queue, done.
|
||||
*/
|
||||
uv__queue_remove(&req->queue);
|
||||
uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
|
||||
uv__io_feed(handle->loop, &handle->io_watcher);
|
||||
}
|
||||
#endif /* __linux__ || ____FreeBSD__ */
|
||||
}
|
||||
|
||||
/* On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but with some additional
|
||||
* refinements for programs that use multicast.
|
||||
* refinements for programs that use multicast. Therefore we preferentially
|
||||
* set SO_REUSEPORT over SO_REUSEADDR here, but we set SO_REUSEPORT only
|
||||
* when that socket option doesn't have the capability of load balancing.
|
||||
* Otherwise, we fall back to SO_REUSEADDR.
|
||||
*
|
||||
* Linux as of 3.9 has a SO_REUSEPORT socket option but with semantics that
|
||||
* are different from the BSDs: it _shares_ the port rather than steal it
|
||||
* from the current listener. While useful, it's not something we can emulate
|
||||
* on other platforms so we don't enable it.
|
||||
* Linux as of 3.9, DragonflyBSD 3.6, AIX 7.2.5 have the SO_REUSEPORT socket
|
||||
* option but with semantics that are different from the BSDs: it _shares_
|
||||
* the port rather than steals it from the current listener. While useful,
|
||||
* it's not something we can emulate on other platforms so we don't enable it.
|
||||
*
|
||||
* zOS does not support getsockname with SO_REUSEPORT option when using
|
||||
* AF_UNIX.
|
||||
*
|
||||
* Solaris 11.4: SO_REUSEPORT will not load balance when SO_REUSEADDR
|
||||
* is also set, but it's not valid for every socket type.
|
||||
*/
|
||||
static int uv__set_reuse(int fd) {
|
||||
static int uv__sock_reuseaddr(int fd) {
|
||||
int yes;
|
||||
yes = 1;
|
||||
|
||||
@ -448,8 +320,18 @@ static int uv__set_reuse(int fd) {
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
|
||||
return UV__ERR(errno);
|
||||
}
|
||||
#elif defined(SO_REUSEPORT) && !defined(__linux__) && !defined(__GNU__) && \
|
||||
!defined(__sun__)
|
||||
#elif defined(SO_REUSEPORT) && defined(UV__SOLARIS_11_4) && UV__SOLARIS_11_4
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes))) {
|
||||
if (errno != ENOPROTOOPT)
|
||||
return UV__ERR(errno);
|
||||
/* Not all socket types accept SO_REUSEPORT. */
|
||||
errno = 0;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)))
|
||||
return UV__ERR(errno);
|
||||
}
|
||||
#elif defined(SO_REUSEPORT) && \
|
||||
!defined(__linux__) && !defined(__GNU__) && \
|
||||
!defined(__illumos__) && !defined(__DragonFly__) && !defined(_AIX73)
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
|
||||
return UV__ERR(errno);
|
||||
#else
|
||||
@ -492,7 +374,8 @@ int uv__udp_bind(uv_udp_t* handle,
|
||||
int fd;
|
||||
|
||||
/* Check for bad flags. */
|
||||
if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR | UV_UDP_LINUX_RECVERR))
|
||||
if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR |
|
||||
UV_UDP_REUSEPORT | UV_UDP_LINUX_RECVERR))
|
||||
return UV_EINVAL;
|
||||
|
||||
/* Cannot set IPv6-only mode on non-IPv6 socket. */
|
||||
@ -515,7 +398,13 @@ int uv__udp_bind(uv_udp_t* handle,
|
||||
}
|
||||
|
||||
if (flags & UV_UDP_REUSEADDR) {
|
||||
err = uv__set_reuse(fd);
|
||||
err = uv__sock_reuseaddr(fd);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (flags & UV_UDP_REUSEPORT) {
|
||||
err = uv__sock_reuseport(fd);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
@ -708,11 +597,11 @@ int uv__udp_send(uv_udp_send_t* req,
|
||||
empty_queue = (handle->send_queue_count == 0);
|
||||
|
||||
uv__req_init(handle->loop, req, UV_UDP_SEND);
|
||||
assert(addrlen <= sizeof(req->addr));
|
||||
assert(addrlen <= sizeof(req->u.storage));
|
||||
if (addr == NULL)
|
||||
req->addr.ss_family = AF_UNSPEC;
|
||||
req->u.storage.ss_family = AF_UNSPEC;
|
||||
else
|
||||
memcpy(&req->addr, addr, addrlen);
|
||||
memcpy(&req->u.storage, addr, addrlen);
|
||||
req->send_cb = send_cb;
|
||||
req->handle = handle;
|
||||
req->nbufs = nbufs;
|
||||
@ -722,7 +611,7 @@ int uv__udp_send(uv_udp_send_t* req,
|
||||
req->bufs = uv__malloc(nbufs * sizeof(bufs[0]));
|
||||
|
||||
if (req->bufs == NULL) {
|
||||
uv__req_unregister(handle->loop, req);
|
||||
uv__req_unregister(handle->loop);
|
||||
return UV_ENOMEM;
|
||||
}
|
||||
|
||||
@ -755,10 +644,9 @@ int uv__udp_try_send(uv_udp_t* handle,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen) {
|
||||
int err;
|
||||
struct msghdr h;
|
||||
ssize_t size;
|
||||
|
||||
assert(nbufs > 0);
|
||||
if (nbufs < 1)
|
||||
return UV_EINVAL;
|
||||
|
||||
/* already sending a message */
|
||||
if (handle->send_queue_count != 0)
|
||||
@ -772,24 +660,11 @@ int uv__udp_try_send(uv_udp_t* handle,
|
||||
assert(handle->flags & UV_HANDLE_UDP_CONNECTED);
|
||||
}
|
||||
|
||||
memset(&h, 0, sizeof h);
|
||||
h.msg_name = (struct sockaddr*) addr;
|
||||
h.msg_namelen = addrlen;
|
||||
h.msg_iov = (struct iovec*) bufs;
|
||||
h.msg_iovlen = nbufs;
|
||||
err = uv__udp_sendmsg1(handle->io_watcher.fd, bufs, nbufs, addr);
|
||||
if (err > 0)
|
||||
return uv__count_bufs(bufs, nbufs);
|
||||
|
||||
do {
|
||||
size = sendmsg(handle->io_watcher.fd, &h, 0);
|
||||
} while (size == -1 && errno == EINTR);
|
||||
|
||||
if (size == -1) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
|
||||
return UV_EAGAIN;
|
||||
else
|
||||
return UV__ERR(errno);
|
||||
}
|
||||
|
||||
return size;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
@ -1015,7 +890,7 @@ int uv__udp_init_ex(uv_loop_t* loop,
|
||||
|
||||
|
||||
int uv_udp_using_recvmmsg(const uv_udp_t* handle) {
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__)
|
||||
if (handle->flags & UV_HANDLE_UDP_RECVMMSG)
|
||||
return 1;
|
||||
#endif
|
||||
@ -1037,7 +912,7 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = uv__set_reuse(sock);
|
||||
err = uv__sock_reuseaddr(sock);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -1366,3 +1241,194 @@ int uv__udp_recv_stop(uv_udp_t* handle) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int uv__udp_prep_pkt(struct msghdr* h,
|
||||
const uv_buf_t* bufs,
|
||||
const unsigned int nbufs,
|
||||
const struct sockaddr* addr) {
|
||||
memset(h, 0, sizeof(*h));
|
||||
h->msg_name = (void*) addr;
|
||||
h->msg_iov = (void*) bufs;
|
||||
h->msg_iovlen = nbufs;
|
||||
if (addr == NULL)
|
||||
return 0;
|
||||
switch (addr->sa_family) {
|
||||
case AF_INET:
|
||||
h->msg_namelen = sizeof(struct sockaddr_in);
|
||||
return 0;
|
||||
case AF_INET6:
|
||||
h->msg_namelen = sizeof(struct sockaddr_in6);
|
||||
return 0;
|
||||
case AF_UNIX:
|
||||
h->msg_namelen = sizeof(struct sockaddr_un);
|
||||
return 0;
|
||||
case AF_UNSPEC:
|
||||
h->msg_name = NULL;
|
||||
return 0;
|
||||
}
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
|
||||
static int uv__udp_sendmsg1(int fd,
|
||||
const uv_buf_t* bufs,
|
||||
unsigned int nbufs,
|
||||
const struct sockaddr* addr) {
|
||||
struct msghdr h;
|
||||
int r;
|
||||
|
||||
if ((r = uv__udp_prep_pkt(&h, bufs, nbufs, addr)))
|
||||
return r;
|
||||
|
||||
do
|
||||
r = sendmsg(fd, &h, 0);
|
||||
while (r == -1 && errno == EINTR);
|
||||
|
||||
if (r < 0) {
|
||||
r = UV__ERR(errno);
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
|
||||
r = UV_EAGAIN;
|
||||
return r;
|
||||
}
|
||||
|
||||
/* UDP sockets don't EOF so we don't have to handle r=0 specially,
|
||||
* that only happens when the input was a zero-sized buffer.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int uv__udp_sendmsgv(int fd,
|
||||
unsigned int count,
|
||||
uv_buf_t* bufs[/*count*/],
|
||||
unsigned int nbufs[/*count*/],
|
||||
struct sockaddr* addrs[/*count*/]) {
|
||||
unsigned int i;
|
||||
int nsent;
|
||||
int r;
|
||||
|
||||
r = 0;
|
||||
nsent = 0;
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || \
|
||||
(defined(__sun__) && defined(MSG_WAITFORONE))
|
||||
if (count > 1) {
|
||||
for (i = 0; i < count; /*empty*/) {
|
||||
struct mmsghdr m[20];
|
||||
unsigned int n;
|
||||
|
||||
for (n = 0; i < count && n < ARRAY_SIZE(m); i++, n++)
|
||||
if ((r = uv__udp_prep_pkt(&m[n].msg_hdr, bufs[i], nbufs[i], addrs[i])))
|
||||
goto exit;
|
||||
|
||||
do
|
||||
#if defined(__APPLE__)
|
||||
r = sendmsg_x(fd, m, n, MSG_DONTWAIT);
|
||||
#else
|
||||
r = sendmmsg(fd, m, n, 0);
|
||||
#endif
|
||||
while (r == -1 && errno == EINTR);
|
||||
|
||||
if (r < 1)
|
||||
goto exit;
|
||||
|
||||
nsent += r;
|
||||
i += r;
|
||||
}
|
||||
|
||||
goto exit;
|
||||
}
|
||||
#endif /* defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) ||
|
||||
* (defined(__sun__) && defined(MSG_WAITFORONE))
|
||||
*/
|
||||
|
||||
for (i = 0; i < count; i++, nsent++)
|
||||
if ((r = uv__udp_sendmsg1(fd, bufs[i], nbufs[i], addrs[i])))
|
||||
goto exit; /* goto to avoid unused label warning. */
|
||||
|
||||
exit:
|
||||
|
||||
if (nsent > 0)
|
||||
return nsent;
|
||||
|
||||
if (r < 0) {
|
||||
r = UV__ERR(errno);
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS)
|
||||
r = UV_EAGAIN;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static void uv__udp_sendmsg(uv_udp_t* handle) {
|
||||
static const int N = 20;
|
||||
struct sockaddr* addrs[N];
|
||||
unsigned int nbufs[N];
|
||||
uv_buf_t* bufs[N];
|
||||
struct uv__queue* q;
|
||||
uv_udp_send_t* req;
|
||||
int n;
|
||||
|
||||
if (uv__queue_empty(&handle->write_queue))
|
||||
return;
|
||||
|
||||
again:
|
||||
n = 0;
|
||||
q = uv__queue_head(&handle->write_queue);
|
||||
do {
|
||||
req = uv__queue_data(q, uv_udp_send_t, queue);
|
||||
addrs[n] = &req->u.addr;
|
||||
nbufs[n] = req->nbufs;
|
||||
bufs[n] = req->bufs;
|
||||
q = uv__queue_next(q);
|
||||
n++;
|
||||
} while (n < N && q != &handle->write_queue);
|
||||
|
||||
n = uv__udp_sendmsgv(handle->io_watcher.fd, n, bufs, nbufs, addrs);
|
||||
while (n > 0) {
|
||||
q = uv__queue_head(&handle->write_queue);
|
||||
req = uv__queue_data(q, uv_udp_send_t, queue);
|
||||
req->status = uv__count_bufs(req->bufs, req->nbufs);
|
||||
uv__queue_remove(&req->queue);
|
||||
uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
|
||||
n--;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
if (uv__queue_empty(&handle->write_queue))
|
||||
goto feed;
|
||||
goto again;
|
||||
}
|
||||
|
||||
if (n == UV_EAGAIN)
|
||||
return;
|
||||
|
||||
/* Register the error against first request in queue because that
|
||||
* is the request that uv__udp_sendmsgv tried but failed to send,
|
||||
* because if it did send any requests, it won't return an error.
|
||||
*/
|
||||
q = uv__queue_head(&handle->write_queue);
|
||||
req = uv__queue_data(q, uv_udp_send_t, queue);
|
||||
req->status = n;
|
||||
uv__queue_remove(&req->queue);
|
||||
uv__queue_insert_tail(&handle->write_completed_queue, &req->queue);
|
||||
feed:
|
||||
uv__io_feed(handle->loop, &handle->io_watcher);
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_try_send2(uv_udp_t* handle,
|
||||
unsigned int count,
|
||||
uv_buf_t* bufs[/*count*/],
|
||||
unsigned int nbufs[/*count*/],
|
||||
struct sockaddr* addrs[/*count*/]) {
|
||||
int fd;
|
||||
|
||||
fd = handle->io_watcher.fd;
|
||||
if (fd == -1)
|
||||
return UV_EINVAL;
|
||||
|
||||
return uv__udp_sendmsgv(fd, count, bufs, nbufs, addrs);
|
||||
}
|
||||
|
@ -514,6 +514,25 @@ int uv_udp_try_send(uv_udp_t* handle,
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_try_send2(uv_udp_t* handle,
|
||||
unsigned int count,
|
||||
uv_buf_t* bufs[/*count*/],
|
||||
unsigned int nbufs[/*count*/],
|
||||
struct sockaddr* addrs[/*count*/],
|
||||
unsigned int flags) {
|
||||
if (count < 1)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (flags != 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (handle->send_queue_count > 0)
|
||||
return UV_EAGAIN;
|
||||
|
||||
return uv__udp_try_send2(handle, count, bufs, nbufs, addrs);
|
||||
}
|
||||
|
||||
|
||||
int uv_udp_recv_start(uv_udp_t* handle,
|
||||
uv_alloc_cb alloc_cb,
|
||||
uv_udp_recv_cb recv_cb) {
|
||||
@ -644,6 +663,9 @@ int uv_send_buffer_size(uv_handle_t* handle, int *value) {
|
||||
int uv_fs_event_getpath(uv_fs_event_t* handle, char* buffer, size_t* size) {
|
||||
size_t required_len;
|
||||
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (!uv__is_active(handle)) {
|
||||
*size = 0;
|
||||
return UV_EINVAL;
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "uv.h"
|
||||
#include "uv/tree.h"
|
||||
@ -125,7 +126,7 @@ enum {
|
||||
|
||||
/* Only used by uv_tty_t handles. */
|
||||
UV_HANDLE_TTY_READABLE = 0x01000000,
|
||||
UV_HANDLE_TTY_RAW = 0x02000000,
|
||||
UV_HANDLE_UNUSED0 = 0x02000000,
|
||||
UV_HANDLE_TTY_SAVED_POSITION = 0x04000000,
|
||||
UV_HANDLE_TTY_SAVED_ATTRIBUTES = 0x08000000,
|
||||
|
||||
@ -140,6 +141,10 @@ enum {
|
||||
UV_HANDLE_REAP = 0x10000000
|
||||
};
|
||||
|
||||
static inline int uv__is_raw_tty_mode(uv_tty_mode_t m) {
|
||||
return m == UV_TTY_MODE_RAW || m == UV_TTY_MODE_RAW_VT;
|
||||
}
|
||||
|
||||
int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap);
|
||||
|
||||
void uv__loop_close(uv_loop_t* loop);
|
||||
@ -191,6 +196,12 @@ int uv__udp_try_send(uv_udp_t* handle,
|
||||
const struct sockaddr* addr,
|
||||
unsigned int addrlen);
|
||||
|
||||
int uv__udp_try_send2(uv_udp_t* handle,
|
||||
unsigned int count,
|
||||
uv_buf_t* bufs[/*count*/],
|
||||
unsigned int nbufs[/*count*/],
|
||||
struct sockaddr* addrs[/*count*/]);
|
||||
|
||||
int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloccb,
|
||||
uv_udp_recv_cb recv_cb);
|
||||
|
||||
@ -233,13 +244,13 @@ void uv__threadpool_cleanup(void);
|
||||
#define uv__has_active_reqs(loop) \
|
||||
((loop)->active_reqs.count > 0)
|
||||
|
||||
#define uv__req_register(loop, req) \
|
||||
#define uv__req_register(loop) \
|
||||
do { \
|
||||
(loop)->active_reqs.count++; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define uv__req_unregister(loop, req) \
|
||||
#define uv__req_unregister(loop) \
|
||||
do { \
|
||||
assert(uv__has_active_reqs(loop)); \
|
||||
(loop)->active_reqs.count--; \
|
||||
@ -349,7 +360,7 @@ void uv__threadpool_cleanup(void);
|
||||
#define uv__req_init(loop, req, typ) \
|
||||
do { \
|
||||
UV_REQ_INIT(req, typ); \
|
||||
uv__req_register(loop, req); \
|
||||
uv__req_register(loop); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
@ -400,7 +411,6 @@ void uv__metrics_set_provider_entry_time(uv_loop_t* loop);
|
||||
struct uv__iou {
|
||||
uint32_t* sqhead;
|
||||
uint32_t* sqtail;
|
||||
uint32_t* sqarray;
|
||||
uint32_t sqmask;
|
||||
uint32_t* sqflags;
|
||||
uint32_t* cqhead;
|
||||
@ -415,7 +425,6 @@ struct uv__iou {
|
||||
size_t sqelen;
|
||||
int ringfd;
|
||||
uint32_t in_flight;
|
||||
uint32_t flags;
|
||||
};
|
||||
#endif /* __linux__ */
|
||||
|
||||
@ -430,4 +439,36 @@ struct uv__loop_internal_fields_s {
|
||||
#endif /* __linux__ */
|
||||
};
|
||||
|
||||
#if defined(_WIN32)
|
||||
# define UV_PTHREAD_MAX_NAMELEN_NP 32767
|
||||
#elif defined(__APPLE__)
|
||||
# define UV_PTHREAD_MAX_NAMELEN_NP 64
|
||||
#elif defined(__NetBSD__) || defined(__illumos__)
|
||||
# define UV_PTHREAD_MAX_NAMELEN_NP PTHREAD_MAX_NAMELEN_NP
|
||||
#elif defined (__linux__)
|
||||
# define UV_PTHREAD_MAX_NAMELEN_NP 16
|
||||
#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
|
||||
# define UV_PTHREAD_MAX_NAMELEN_NP (MAXCOMLEN + 1)
|
||||
#else
|
||||
# define UV_PTHREAD_MAX_NAMELEN_NP 16
|
||||
#endif
|
||||
|
||||
/* Open-coded so downstream users don't have to link libm. */
|
||||
static inline int uv__isinf(double d) {
|
||||
uint64_t v;
|
||||
|
||||
STATIC_ASSERT(sizeof(v) == sizeof(d));
|
||||
memcpy(&v, &d, sizeof(v));
|
||||
return (v << 1 >> 53) == 2047 && !(v << 12);
|
||||
}
|
||||
|
||||
/* Open-coded so downstream users don't have to link libm. */
|
||||
static inline int uv__isnan(double d) {
|
||||
uint64_t v;
|
||||
|
||||
STATIC_ASSERT(sizeof(v) == sizeof(d));
|
||||
memcpy(&v, &d, sizeof(v));
|
||||
return (v << 1 >> 53) == 2047 && !!(v << 12);
|
||||
}
|
||||
|
||||
#endif /* UV_COMMON_H_ */
|
||||
|
110
src/win/core.c
110
src/win/core.c
@ -423,97 +423,6 @@ int uv_backend_timeout(const uv_loop_t* loop) {
|
||||
}
|
||||
|
||||
|
||||
static void uv__poll_wine(uv_loop_t* loop, DWORD timeout) {
|
||||
uv__loop_internal_fields_t* lfields;
|
||||
DWORD bytes;
|
||||
ULONG_PTR key;
|
||||
OVERLAPPED* overlapped;
|
||||
uv_req_t* req;
|
||||
int repeat;
|
||||
uint64_t timeout_time;
|
||||
uint64_t user_timeout;
|
||||
int reset_timeout;
|
||||
|
||||
lfields = uv__get_internal_fields(loop);
|
||||
timeout_time = loop->time + timeout;
|
||||
|
||||
if (lfields->flags & UV_METRICS_IDLE_TIME) {
|
||||
reset_timeout = 1;
|
||||
user_timeout = timeout;
|
||||
timeout = 0;
|
||||
} else {
|
||||
reset_timeout = 0;
|
||||
}
|
||||
|
||||
for (repeat = 0; ; repeat++) {
|
||||
/* Only need to set the provider_entry_time if timeout != 0. The function
|
||||
* will return early if the loop isn't configured with UV_METRICS_IDLE_TIME.
|
||||
*/
|
||||
if (timeout != 0)
|
||||
uv__metrics_set_provider_entry_time(loop);
|
||||
|
||||
/* Store the current timeout in a location that's globally accessible so
|
||||
* other locations like uv__work_done() can determine whether the queue
|
||||
* of events in the callback were waiting when poll was called.
|
||||
*/
|
||||
lfields->current_timeout = timeout;
|
||||
|
||||
GetQueuedCompletionStatus(loop->iocp,
|
||||
&bytes,
|
||||
&key,
|
||||
&overlapped,
|
||||
timeout);
|
||||
|
||||
if (reset_timeout != 0) {
|
||||
if (overlapped && timeout == 0)
|
||||
uv__metrics_inc_events_waiting(loop, 1);
|
||||
timeout = user_timeout;
|
||||
reset_timeout = 0;
|
||||
}
|
||||
|
||||
/* Placed here because on success the loop will break whether there is an
|
||||
* empty package or not, or if GetQueuedCompletionStatus returned early then
|
||||
* the timeout will be updated and the loop will run again. In either case
|
||||
* the idle time will need to be updated.
|
||||
*/
|
||||
uv__metrics_update_idle_time(loop);
|
||||
|
||||
if (overlapped) {
|
||||
uv__metrics_inc_events(loop, 1);
|
||||
|
||||
/* Package was dequeued */
|
||||
req = uv__overlapped_to_req(overlapped);
|
||||
uv__insert_pending_req(loop, req);
|
||||
|
||||
/* Some time might have passed waiting for I/O,
|
||||
* so update the loop time here.
|
||||
*/
|
||||
uv_update_time(loop);
|
||||
} else if (GetLastError() != WAIT_TIMEOUT) {
|
||||
/* Serious error */
|
||||
uv_fatal_error(GetLastError(), "GetQueuedCompletionStatus");
|
||||
} else if (timeout > 0) {
|
||||
/* GetQueuedCompletionStatus can occasionally return a little early.
|
||||
* Make sure that the desired timeout target time is reached.
|
||||
*/
|
||||
uv_update_time(loop);
|
||||
if (timeout_time > loop->time) {
|
||||
timeout = (DWORD)(timeout_time - loop->time);
|
||||
/* The first call to GetQueuedCompletionStatus should return very
|
||||
* close to the target time and the second should reach it, but
|
||||
* this is not stated in the documentation. To make sure a busy
|
||||
* loop cannot happen, the timeout is increased exponentially
|
||||
* starting on the third round.
|
||||
*/
|
||||
timeout += repeat ? (1 << (repeat - 1)) : 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void uv__poll(uv_loop_t* loop, DWORD timeout) {
|
||||
uv__loop_internal_fields_t* lfields;
|
||||
BOOL success;
|
||||
@ -553,12 +462,12 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) {
|
||||
*/
|
||||
lfields->current_timeout = timeout;
|
||||
|
||||
success = pGetQueuedCompletionStatusEx(loop->iocp,
|
||||
overlappeds,
|
||||
ARRAY_SIZE(overlappeds),
|
||||
&count,
|
||||
timeout,
|
||||
FALSE);
|
||||
success = GetQueuedCompletionStatusEx(loop->iocp,
|
||||
overlappeds,
|
||||
ARRAY_SIZE(overlappeds),
|
||||
&count,
|
||||
timeout,
|
||||
FALSE);
|
||||
|
||||
if (reset_timeout != 0) {
|
||||
timeout = user_timeout;
|
||||
@ -566,7 +475,7 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) {
|
||||
}
|
||||
|
||||
/* Placed here because on success the loop will break whether there is an
|
||||
* empty package or not, or if pGetQueuedCompletionStatusEx returned early
|
||||
* empty package or not, or if GetQueuedCompletionStatusEx returned early
|
||||
* then the timeout will be updated and the loop will run again. In either
|
||||
* case the idle time will need to be updated.
|
||||
*/
|
||||
@ -647,10 +556,7 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) {
|
||||
|
||||
uv__metrics_inc_loop_count(loop);
|
||||
|
||||
if (pGetQueuedCompletionStatusEx)
|
||||
uv__poll(loop, timeout);
|
||||
else
|
||||
uv__poll_wine(loop, timeout);
|
||||
uv__poll(loop, timeout);
|
||||
|
||||
/* Process immediate callbacks (e.g. write_cb) a small fixed number of
|
||||
* times to avoid loop starvation.*/
|
||||
|
@ -69,7 +69,6 @@ int uv_translate_sys_error(int sys_errno) {
|
||||
}
|
||||
|
||||
switch (sys_errno) {
|
||||
case ERROR_NOACCESS: return UV_EACCES;
|
||||
case WSAEACCES: return UV_EACCES;
|
||||
case ERROR_ELEVATION_REQUIRED: return UV_EACCES;
|
||||
case ERROR_CANT_ACCESS_FILE: return UV_EACCES;
|
||||
@ -78,6 +77,7 @@ int uv_translate_sys_error(int sys_errno) {
|
||||
case WSAEADDRNOTAVAIL: return UV_EADDRNOTAVAIL;
|
||||
case WSAEAFNOSUPPORT: return UV_EAFNOSUPPORT;
|
||||
case WSAEWOULDBLOCK: return UV_EAGAIN;
|
||||
case ERROR_NO_DATA: return UV_EAGAIN;
|
||||
case WSAEALREADY: return UV_EALREADY;
|
||||
case ERROR_INVALID_FLAGS: return UV_EBADF;
|
||||
case ERROR_INVALID_HANDLE: return UV_EBADF;
|
||||
@ -95,7 +95,7 @@ int uv_translate_sys_error(int sys_errno) {
|
||||
case WSAECONNRESET: return UV_ECONNRESET;
|
||||
case ERROR_ALREADY_EXISTS: return UV_EEXIST;
|
||||
case ERROR_FILE_EXISTS: return UV_EEXIST;
|
||||
case ERROR_BUFFER_OVERFLOW: return UV_EFAULT;
|
||||
case ERROR_NOACCESS: return UV_EFAULT;
|
||||
case WSAEFAULT: return UV_EFAULT;
|
||||
case ERROR_HOST_UNREACHABLE: return UV_EHOSTUNREACH;
|
||||
case WSAEHOSTUNREACH: return UV_EHOSTUNREACH;
|
||||
@ -126,6 +126,7 @@ int uv_translate_sys_error(int sys_errno) {
|
||||
case ERROR_TOO_MANY_OPEN_FILES: return UV_EMFILE;
|
||||
case WSAEMFILE: return UV_EMFILE;
|
||||
case WSAEMSGSIZE: return UV_EMSGSIZE;
|
||||
case ERROR_BUFFER_OVERFLOW: return UV_ENAMETOOLONG;
|
||||
case ERROR_FILENAME_EXCED_RANGE: return UV_ENAMETOOLONG;
|
||||
case ERROR_NETWORK_UNREACHABLE: return UV_ENETUNREACH;
|
||||
case WSAENETUNREACH: return UV_ENETUNREACH;
|
||||
@ -157,7 +158,6 @@ int uv_translate_sys_error(int sys_errno) {
|
||||
case ERROR_ACCESS_DENIED: return UV_EPERM;
|
||||
case ERROR_PRIVILEGE_NOT_HELD: return UV_EPERM;
|
||||
case ERROR_BAD_PIPE: return UV_EPIPE;
|
||||
case ERROR_NO_DATA: return UV_EPIPE;
|
||||
case ERROR_PIPE_NOT_CONNECTED: return UV_EPIPE;
|
||||
case WSAESHUTDOWN: return UV_EPIPE;
|
||||
case WSAEPROTONOSUPPORT: return UV_EPROTONOSUPPORT;
|
||||
@ -168,6 +168,16 @@ int uv_translate_sys_error(int sys_errno) {
|
||||
case ERROR_INVALID_FUNCTION: return UV_EISDIR;
|
||||
case ERROR_META_EXPANSION_TOO_LONG: return UV_E2BIG;
|
||||
case WSAESOCKTNOSUPPORT: return UV_ESOCKTNOSUPPORT;
|
||||
case ERROR_BAD_EXE_FORMAT: return UV_EFTYPE;
|
||||
default: return UV_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
int uv_translate_write_sys_error(int sys_errno) {
|
||||
switch (sys_errno) {
|
||||
case ERROR_BROKEN_PIPE: return UV_EPIPE;
|
||||
case ERROR_NO_DATA: return UV_EPIPE;
|
||||
default:
|
||||
return uv_translate_sys_error(sys_errno);
|
||||
}
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ static int uv__split_path(const WCHAR* filename, WCHAR** dir,
|
||||
}
|
||||
}
|
||||
|
||||
*file = wcsdup(filename);
|
||||
*file = _wcsdup(filename);
|
||||
} else {
|
||||
if (dir) {
|
||||
*dir = (WCHAR*)uv__malloc((i + 2) * sizeof(WCHAR));
|
||||
@ -253,6 +253,8 @@ short_path_done:
|
||||
}
|
||||
|
||||
dir_to_watch = dir;
|
||||
uv__free(short_path);
|
||||
short_path = NULL;
|
||||
uv__free(pathw);
|
||||
pathw = NULL;
|
||||
}
|
||||
@ -561,7 +563,27 @@ void uv__process_fs_event_req(uv_loop_t* loop, uv_req_t* req,
|
||||
}
|
||||
} else {
|
||||
err = GET_REQ_ERROR(req);
|
||||
handle->cb(handle, NULL, 0, uv_translate_sys_error(err));
|
||||
/*
|
||||
* Check whether the ERROR_ACCESS_DENIED is caused by the watched directory
|
||||
* being actually deleted (not an actual error) or a legit error. Retrieve
|
||||
* FileStandardInfo to check whether the directory is pending deletion.
|
||||
*/
|
||||
FILE_STANDARD_INFO info;
|
||||
if (err == ERROR_ACCESS_DENIED &&
|
||||
handle->dirw != NULL &&
|
||||
GetFileInformationByHandleEx(handle->dir_handle,
|
||||
FileStandardInfo,
|
||||
&info,
|
||||
sizeof(info)) &&
|
||||
info.Directory &&
|
||||
info.DeletePending) {
|
||||
uv__convert_utf16_to_utf8(handle->dirw, -1, &filename);
|
||||
handle->cb(handle, filename, UV_RENAME, 0);
|
||||
uv__free(filename);
|
||||
filename = NULL;
|
||||
} else {
|
||||
handle->cb(handle, NULL, 0, uv_translate_sys_error(err));
|
||||
}
|
||||
}
|
||||
|
||||
if (handle->flags & UV_HANDLE_CLOSING) {
|
||||
|
548
src/win/fs.c
548
src/win/fs.c
@ -46,6 +46,30 @@
|
||||
#define UV_FS_FREE_PTR 0x0008
|
||||
#define UV_FS_CLEANEDUP 0x0010
|
||||
|
||||
#ifndef FILE_DISPOSITION_DELETE
|
||||
#define FILE_DISPOSITION_DELETE 0x0001
|
||||
#endif /* FILE_DISPOSITION_DELETE */
|
||||
|
||||
#ifndef FILE_DISPOSITION_POSIX_SEMANTICS
|
||||
#define FILE_DISPOSITION_POSIX_SEMANTICS 0x0002
|
||||
#endif /* FILE_DISPOSITION_POSIX_SEMANTICS */
|
||||
|
||||
#ifndef FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE
|
||||
#define FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE 0x0010
|
||||
#endif /* FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE */
|
||||
|
||||
NTSTATUS uv__RtlUnicodeStringInit(
|
||||
PUNICODE_STRING DestinationString,
|
||||
PWSTR SourceString,
|
||||
size_t SourceStringLen
|
||||
) {
|
||||
if (SourceStringLen > 0x7FFF)
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
DestinationString->MaximumLength = DestinationString->Length =
|
||||
SourceStringLen * sizeof(SourceString[0]);
|
||||
DestinationString->Buffer = SourceString;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
#define INIT(subtype) \
|
||||
do { \
|
||||
@ -58,7 +82,7 @@
|
||||
#define POST \
|
||||
do { \
|
||||
if (cb != NULL) { \
|
||||
uv__req_register(loop, req); \
|
||||
uv__req_register(loop); \
|
||||
uv__work_submit(loop, \
|
||||
&req->work_req, \
|
||||
UV__WORK_FAST_IO, \
|
||||
@ -97,13 +121,14 @@
|
||||
return; \
|
||||
}
|
||||
|
||||
#define MILLION ((int64_t) 1000 * 1000)
|
||||
#define BILLION ((int64_t) 1000 * 1000 * 1000)
|
||||
#define NSEC_PER_TICK 100
|
||||
#define TICKS_PER_SEC ((int64_t) 1e9 / NSEC_PER_TICK)
|
||||
static const int64_t WIN_TO_UNIX_TICK_OFFSET = 11644473600 * TICKS_PER_SEC;
|
||||
|
||||
static void uv__filetime_to_timespec(uv_timespec_t *ts, int64_t filetime) {
|
||||
filetime -= 116444736 * BILLION;
|
||||
ts->tv_sec = (long) (filetime / (10 * MILLION));
|
||||
ts->tv_nsec = (long) ((filetime - ts->tv_sec * 10 * MILLION) * 100U);
|
||||
filetime -= WIN_TO_UNIX_TICK_OFFSET;
|
||||
ts->tv_sec = filetime / TICKS_PER_SEC;
|
||||
ts->tv_nsec = (filetime % TICKS_PER_SEC) * NSEC_PER_TICK;
|
||||
if (ts->tv_nsec < 0) {
|
||||
ts->tv_sec -= 1;
|
||||
ts->tv_nsec += 1e9;
|
||||
@ -112,7 +137,7 @@ static void uv__filetime_to_timespec(uv_timespec_t *ts, int64_t filetime) {
|
||||
|
||||
#define TIME_T_TO_FILETIME(time, filetime_ptr) \
|
||||
do { \
|
||||
int64_t bigtime = ((time) * 10 * MILLION + 116444736 * BILLION); \
|
||||
int64_t bigtime = ((time) * TICKS_PER_SEC + WIN_TO_UNIX_TICK_OFFSET); \
|
||||
(filetime_ptr)->dwLowDateTime = (uint64_t) bigtime & 0xFFFFFFFF; \
|
||||
(filetime_ptr)->dwHighDateTime = (uint64_t) bigtime >> 32; \
|
||||
} while(0)
|
||||
@ -136,6 +161,16 @@ static int uv__file_symlink_usermode_flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGE
|
||||
|
||||
static DWORD uv__allocation_granularity;
|
||||
|
||||
typedef enum {
|
||||
FS__STAT_PATH_SUCCESS,
|
||||
FS__STAT_PATH_ERROR,
|
||||
FS__STAT_PATH_TRY_SLOW
|
||||
} fs__stat_path_return_t;
|
||||
|
||||
INLINE static void fs__stat_assign_statbuf_null(uv_stat_t* statbuf);
|
||||
INLINE static void fs__stat_assign_statbuf(uv_stat_t* statbuf,
|
||||
FILE_STAT_BASIC_INFORMATION stat_info, int do_lstat);
|
||||
|
||||
|
||||
void uv__fs_init(void) {
|
||||
SYSTEM_INFO system_info;
|
||||
@ -407,8 +442,8 @@ void fs__open(uv_fs_t* req) {
|
||||
|
||||
/* Obtain the active umask. umask() never fails and returns the previous
|
||||
* umask. */
|
||||
current_umask = umask(0);
|
||||
umask(current_umask);
|
||||
current_umask = _umask(0);
|
||||
_umask(current_umask);
|
||||
|
||||
/* convert flags and mode to CreateFile parameters */
|
||||
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;
|
||||
}
|
||||
|
||||
SET_REQ_WIN32_ERROR(req, error);
|
||||
SET_REQ_UV_ERROR(req, uv_translate_write_sys_error(error), error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fs__rmdir(uv_fs_t* req) {
|
||||
int result = _wrmdir(req->file.pathw);
|
||||
if (result == -1)
|
||||
SET_REQ_WIN32_ERROR(req, _doserrno);
|
||||
else
|
||||
SET_REQ_RESULT(req, 0);
|
||||
}
|
||||
|
||||
|
||||
void fs__unlink(uv_fs_t* req) {
|
||||
static void fs__unlink_rmdir(uv_fs_t* req, BOOL isrmdir) {
|
||||
const WCHAR* pathw = req->file.pathw;
|
||||
HANDLE handle;
|
||||
BY_HANDLE_FILE_INFORMATION info;
|
||||
FILE_DISPOSITION_INFORMATION disposition;
|
||||
FILE_DISPOSITION_INFORMATION_EX disposition_ex;
|
||||
IO_STATUS_BLOCK iosb;
|
||||
NTSTATUS status;
|
||||
DWORD error;
|
||||
|
||||
handle = CreateFileW(pathw,
|
||||
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | DELETE,
|
||||
@ -1097,10 +1125,18 @@ void fs__unlink(uv_fs_t* req) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
/* Do not allow deletion of directories, unless it is a symlink. When the
|
||||
* path refers to a non-symlink directory, report EPERM as mandated by
|
||||
* POSIX.1. */
|
||||
if (isrmdir && !(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
/* Error if we're in rmdir mode but it is not a dir.
|
||||
* TODO: change it to UV_NOTDIR in v2. */
|
||||
SET_REQ_UV_ERROR(req, UV_ENOENT, ERROR_DIRECTORY);
|
||||
CloseHandle(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isrmdir && (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
/* If not explicitly allowed, do not allow deletion of directories, unless
|
||||
* it is a symlink. When the path refers to a non-symlink directory, report
|
||||
* EPERM as mandated by POSIX.1. */
|
||||
|
||||
/* Check if it is a reparse point. If it's not, it's a normal directory. */
|
||||
if (!(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
|
||||
@ -1112,7 +1148,7 @@ void fs__unlink(uv_fs_t* req) {
|
||||
/* Read the reparse point and check if it is a valid symlink. If not, don't
|
||||
* unlink. */
|
||||
if (fs__readlink_handle(handle, NULL, NULL) < 0) {
|
||||
DWORD error = GetLastError();
|
||||
error = GetLastError();
|
||||
if (error == ERROR_SYMLINK_NOT_SUPPORTED)
|
||||
error = ERROR_ACCESS_DENIED;
|
||||
SET_REQ_WIN32_ERROR(req, error);
|
||||
@ -1121,42 +1157,77 @@ void fs__unlink(uv_fs_t* req) {
|
||||
}
|
||||
}
|
||||
|
||||
if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
|
||||
/* Remove read-only attribute */
|
||||
FILE_BASIC_INFORMATION basic = { 0 };
|
||||
/* Try posix delete first */
|
||||
disposition_ex.Flags = FILE_DISPOSITION_DELETE | FILE_DISPOSITION_POSIX_SEMANTICS |
|
||||
FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE;
|
||||
|
||||
basic.FileAttributes = (info.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY) |
|
||||
FILE_ATTRIBUTE_ARCHIVE;
|
||||
|
||||
status = pNtSetInformationFile(handle,
|
||||
&iosb,
|
||||
&basic,
|
||||
sizeof basic,
|
||||
FileBasicInformation);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
|
||||
CloseHandle(handle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to set the delete flag. */
|
||||
disposition.DeleteFile = TRUE;
|
||||
status = pNtSetInformationFile(handle,
|
||||
&iosb,
|
||||
&disposition,
|
||||
sizeof disposition,
|
||||
FileDispositionInformation);
|
||||
&disposition_ex,
|
||||
sizeof disposition_ex,
|
||||
FileDispositionInformationEx);
|
||||
if (NT_SUCCESS(status)) {
|
||||
SET_REQ_SUCCESS(req);
|
||||
} else {
|
||||
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
|
||||
/* If status == STATUS_CANNOT_DELETE here, given we set
|
||||
* FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE, STATUS_CANNOT_DELETE can only mean
|
||||
* that there is an existing mapped view to the file, preventing delete.
|
||||
* STATUS_CANNOT_DELETE maps to UV_EACCES so it's not specifically worth handling */
|
||||
error = pRtlNtStatusToDosError(status);
|
||||
if (error == ERROR_NOT_SUPPORTED /* filesystem does not support posix deletion */ ||
|
||||
error == ERROR_INVALID_PARAMETER /* pre Windows 10 error */ ||
|
||||
error == ERROR_INVALID_FUNCTION /* pre Windows 10 1607 error */) {
|
||||
/* posix delete not supported so try fallback */
|
||||
if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
|
||||
/* Remove read-only attribute */
|
||||
FILE_BASIC_INFORMATION basic = { 0 };
|
||||
|
||||
basic.FileAttributes = (info.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY) |
|
||||
FILE_ATTRIBUTE_ARCHIVE;
|
||||
|
||||
status = pNtSetInformationFile(handle,
|
||||
&iosb,
|
||||
&basic,
|
||||
sizeof basic,
|
||||
FileBasicInformation);
|
||||
if (!NT_SUCCESS(status)) {
|
||||
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
|
||||
CloseHandle(handle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to set the delete flag. */
|
||||
disposition.DeleteFile = TRUE;
|
||||
status = pNtSetInformationFile(handle,
|
||||
&iosb,
|
||||
&disposition,
|
||||
sizeof disposition,
|
||||
FileDispositionInformation);
|
||||
if (NT_SUCCESS(status)) {
|
||||
SET_REQ_SUCCESS(req);
|
||||
} else {
|
||||
SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status));
|
||||
}
|
||||
} else {
|
||||
SET_REQ_WIN32_ERROR(req, error);
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(handle);
|
||||
}
|
||||
|
||||
|
||||
static void fs__rmdir(uv_fs_t* req) {
|
||||
fs__unlink_rmdir(req, /*isrmdir*/1);
|
||||
}
|
||||
|
||||
|
||||
static void fs__unlink(uv_fs_t* req) {
|
||||
fs__unlink_rmdir(req, /*isrmdir*/0);
|
||||
}
|
||||
|
||||
|
||||
void fs__mkdir(uv_fs_t* req) {
|
||||
/* TODO: use req->mode. */
|
||||
if (CreateDirectoryW(req->file.pathw, NULL)) {
|
||||
@ -1182,7 +1253,7 @@ void fs__mktemp(uv_fs_t* req, uv__fs_mktemp_func func) {
|
||||
size_t len;
|
||||
uint64_t v;
|
||||
char* path;
|
||||
|
||||
|
||||
path = (char*)req->path;
|
||||
len = wcslen(req->file.pathw);
|
||||
ep = req->file.pathw + len;
|
||||
@ -1593,12 +1664,12 @@ void fs__readdir(uv_fs_t* req) {
|
||||
goto error;
|
||||
|
||||
/* Copy file type. */
|
||||
if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
|
||||
dent.d_type = UV__DT_DIR;
|
||||
if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0)
|
||||
dent.d_type = UV__DT_CHAR;
|
||||
else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0)
|
||||
dent.d_type = UV__DT_LINK;
|
||||
else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0)
|
||||
dent.d_type = UV__DT_CHAR;
|
||||
else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
|
||||
dent.d_type = UV__DT_DIR;
|
||||
else
|
||||
dent.d_type = UV__DT_FILE;
|
||||
|
||||
@ -1627,6 +1698,43 @@ void fs__closedir(uv_fs_t* req) {
|
||||
SET_REQ_RESULT(req, 0);
|
||||
}
|
||||
|
||||
INLINE static fs__stat_path_return_t fs__stat_path(WCHAR* path,
|
||||
uv_stat_t* statbuf, int do_lstat) {
|
||||
FILE_STAT_BASIC_INFORMATION stat_info;
|
||||
|
||||
/* Check if the new fast API is available. */
|
||||
if (!pGetFileInformationByName) {
|
||||
return FS__STAT_PATH_TRY_SLOW;
|
||||
}
|
||||
|
||||
/* Check if the API call fails. */
|
||||
if (!pGetFileInformationByName(path, FileStatBasicByNameInfo, &stat_info,
|
||||
sizeof(stat_info))) {
|
||||
switch(GetLastError()) {
|
||||
case ERROR_FILE_NOT_FOUND:
|
||||
case ERROR_PATH_NOT_FOUND:
|
||||
case ERROR_NOT_READY:
|
||||
case ERROR_BAD_NET_NAME:
|
||||
/* These errors aren't worth retrying with the slow path. */
|
||||
return FS__STAT_PATH_ERROR;
|
||||
}
|
||||
return FS__STAT_PATH_TRY_SLOW;
|
||||
}
|
||||
|
||||
/* A file handle is needed to get st_size for links. */
|
||||
if ((stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
|
||||
return FS__STAT_PATH_TRY_SLOW;
|
||||
}
|
||||
|
||||
if (stat_info.DeviceType == FILE_DEVICE_NULL) {
|
||||
fs__stat_assign_statbuf_null(statbuf);
|
||||
return FS__STAT_PATH_SUCCESS;
|
||||
}
|
||||
|
||||
fs__stat_assign_statbuf(statbuf, stat_info, do_lstat);
|
||||
return FS__STAT_PATH_SUCCESS;
|
||||
}
|
||||
|
||||
INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
|
||||
int do_lstat) {
|
||||
size_t target_length = 0;
|
||||
@ -1635,6 +1743,7 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
|
||||
FILE_FS_VOLUME_INFORMATION volume_info;
|
||||
NTSTATUS nt_status;
|
||||
IO_STATUS_BLOCK io_status;
|
||||
FILE_STAT_BASIC_INFORMATION stat_info;
|
||||
|
||||
nt_status = pNtQueryVolumeInformationFile(handle,
|
||||
&io_status,
|
||||
@ -1650,13 +1759,7 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
|
||||
|
||||
/* If it's NUL device set fields as reasonable as possible and return. */
|
||||
if (device_info.DeviceType == FILE_DEVICE_NULL) {
|
||||
memset(statbuf, 0, sizeof(uv_stat_t));
|
||||
statbuf->st_mode = _S_IFCHR;
|
||||
statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
|
||||
((_S_IREAD | _S_IWRITE) >> 6);
|
||||
statbuf->st_nlink = 1;
|
||||
statbuf->st_blksize = 4096;
|
||||
statbuf->st_rdev = FILE_DEVICE_NULL << 16;
|
||||
fs__stat_assign_statbuf_null(statbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1680,14 +1783,64 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
|
||||
|
||||
/* Buffer overflow (a warning status code) is expected here. */
|
||||
if (io_status.Status == STATUS_NOT_IMPLEMENTED) {
|
||||
statbuf->st_dev = 0;
|
||||
stat_info.VolumeSerialNumber.QuadPart = 0;
|
||||
} else if (NT_ERROR(nt_status)) {
|
||||
SetLastError(pRtlNtStatusToDosError(nt_status));
|
||||
return -1;
|
||||
} else {
|
||||
statbuf->st_dev = volume_info.VolumeSerialNumber;
|
||||
stat_info.VolumeSerialNumber.LowPart = volume_info.VolumeSerialNumber;
|
||||
}
|
||||
|
||||
stat_info.DeviceType = device_info.DeviceType;
|
||||
stat_info.FileAttributes = file_info.BasicInformation.FileAttributes;
|
||||
stat_info.NumberOfLinks = file_info.StandardInformation.NumberOfLinks;
|
||||
stat_info.FileId.QuadPart =
|
||||
file_info.InternalInformation.IndexNumber.QuadPart;
|
||||
stat_info.ChangeTime.QuadPart =
|
||||
file_info.BasicInformation.ChangeTime.QuadPart;
|
||||
stat_info.CreationTime.QuadPart =
|
||||
file_info.BasicInformation.CreationTime.QuadPart;
|
||||
stat_info.LastAccessTime.QuadPart =
|
||||
file_info.BasicInformation.LastAccessTime.QuadPart;
|
||||
stat_info.LastWriteTime.QuadPart =
|
||||
file_info.BasicInformation.LastWriteTime.QuadPart;
|
||||
stat_info.AllocationSize.QuadPart =
|
||||
file_info.StandardInformation.AllocationSize.QuadPart;
|
||||
|
||||
if (do_lstat &&
|
||||
(file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
|
||||
/*
|
||||
* If reading the link fails, the reparse point is not a symlink and needs
|
||||
* to be treated as a regular file. The higher level lstat function will
|
||||
* detect this failure and retry without do_lstat if appropriate.
|
||||
*/
|
||||
if (fs__readlink_handle(handle, NULL, &target_length) != 0) {
|
||||
return -1;
|
||||
}
|
||||
stat_info.EndOfFile.QuadPart = target_length;
|
||||
} else {
|
||||
stat_info.EndOfFile.QuadPart =
|
||||
file_info.StandardInformation.EndOfFile.QuadPart;
|
||||
}
|
||||
|
||||
fs__stat_assign_statbuf(statbuf, stat_info, do_lstat);
|
||||
return 0;
|
||||
}
|
||||
|
||||
INLINE static void fs__stat_assign_statbuf_null(uv_stat_t* statbuf) {
|
||||
memset(statbuf, 0, sizeof(uv_stat_t));
|
||||
statbuf->st_mode = _S_IFCHR;
|
||||
statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
|
||||
((_S_IREAD | _S_IWRITE) >> 6);
|
||||
statbuf->st_nlink = 1;
|
||||
statbuf->st_blksize = 4096;
|
||||
statbuf->st_rdev = FILE_DEVICE_NULL << 16;
|
||||
}
|
||||
|
||||
INLINE static void fs__stat_assign_statbuf(uv_stat_t* statbuf,
|
||||
FILE_STAT_BASIC_INFORMATION stat_info, int do_lstat) {
|
||||
statbuf->st_dev = stat_info.VolumeSerialNumber.LowPart;
|
||||
|
||||
/* Todo: st_mode should probably always be 0666 for everyone. We might also
|
||||
* want to report 0777 if the file is a .exe or a directory.
|
||||
*
|
||||
@ -1719,50 +1872,43 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
|
||||
* target. Otherwise, reparse points must be treated as regular files.
|
||||
*/
|
||||
if (do_lstat &&
|
||||
(file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
|
||||
/*
|
||||
* If reading the link fails, the reparse point is not a symlink and needs
|
||||
* to be treated as a regular file. The higher level lstat function will
|
||||
* detect this failure and retry without do_lstat if appropriate.
|
||||
*/
|
||||
if (fs__readlink_handle(handle, NULL, &target_length) != 0)
|
||||
return -1;
|
||||
(stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
|
||||
statbuf->st_mode |= S_IFLNK;
|
||||
statbuf->st_size = target_length;
|
||||
statbuf->st_size = stat_info.EndOfFile.QuadPart;
|
||||
}
|
||||
|
||||
if (statbuf->st_mode == 0) {
|
||||
if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
if (stat_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
statbuf->st_mode |= _S_IFDIR;
|
||||
statbuf->st_size = 0;
|
||||
} else {
|
||||
statbuf->st_mode |= _S_IFREG;
|
||||
statbuf->st_size = file_info.StandardInformation.EndOfFile.QuadPart;
|
||||
statbuf->st_size = stat_info.EndOfFile.QuadPart;
|
||||
}
|
||||
}
|
||||
|
||||
if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_READONLY)
|
||||
if (stat_info.FileAttributes & FILE_ATTRIBUTE_READONLY)
|
||||
statbuf->st_mode |= _S_IREAD | (_S_IREAD >> 3) | (_S_IREAD >> 6);
|
||||
else
|
||||
statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) |
|
||||
((_S_IREAD | _S_IWRITE) >> 6);
|
||||
|
||||
uv__filetime_to_timespec(&statbuf->st_atim,
|
||||
file_info.BasicInformation.LastAccessTime.QuadPart);
|
||||
stat_info.LastAccessTime.QuadPart);
|
||||
uv__filetime_to_timespec(&statbuf->st_ctim,
|
||||
file_info.BasicInformation.ChangeTime.QuadPart);
|
||||
stat_info.ChangeTime.QuadPart);
|
||||
uv__filetime_to_timespec(&statbuf->st_mtim,
|
||||
file_info.BasicInformation.LastWriteTime.QuadPart);
|
||||
stat_info.LastWriteTime.QuadPart);
|
||||
uv__filetime_to_timespec(&statbuf->st_birthtim,
|
||||
file_info.BasicInformation.CreationTime.QuadPart);
|
||||
stat_info.CreationTime.QuadPart);
|
||||
|
||||
statbuf->st_ino = file_info.InternalInformation.IndexNumber.QuadPart;
|
||||
statbuf->st_ino = stat_info.FileId.QuadPart;
|
||||
|
||||
/* st_blocks contains the on-disk allocation size in 512-byte units. */
|
||||
statbuf->st_blocks =
|
||||
(uint64_t) file_info.StandardInformation.AllocationSize.QuadPart >> 9;
|
||||
(uint64_t) stat_info.AllocationSize.QuadPart >> 9;
|
||||
|
||||
statbuf->st_nlink = file_info.StandardInformation.NumberOfLinks;
|
||||
statbuf->st_nlink = stat_info.NumberOfLinks;
|
||||
|
||||
/* The st_blksize is supposed to be the 'optimal' number of bytes for reading
|
||||
* and writing to the disk. That is, for any definition of 'optimal' - it's
|
||||
@ -1794,8 +1940,6 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
|
||||
statbuf->st_uid = 0;
|
||||
statbuf->st_rdev = 0;
|
||||
statbuf->st_gen = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -1809,6 +1953,179 @@ INLINE static void fs__stat_prepare_path(WCHAR* pathw) {
|
||||
}
|
||||
}
|
||||
|
||||
INLINE static DWORD fs__stat_directory(WCHAR* path, uv_stat_t* statbuf,
|
||||
int do_lstat, DWORD ret_error) {
|
||||
HANDLE handle = INVALID_HANDLE_VALUE;
|
||||
FILE_STAT_BASIC_INFORMATION stat_info;
|
||||
FILE_ID_FULL_DIR_INFORMATION dir_info;
|
||||
FILE_FS_VOLUME_INFORMATION volume_info;
|
||||
FILE_FS_DEVICE_INFORMATION device_info;
|
||||
IO_STATUS_BLOCK io_status;
|
||||
NTSTATUS nt_status;
|
||||
WCHAR* path_dirpath = NULL;
|
||||
WCHAR* path_filename = NULL;
|
||||
UNICODE_STRING FileMask;
|
||||
size_t len;
|
||||
size_t split;
|
||||
WCHAR splitchar;
|
||||
int includes_name;
|
||||
|
||||
/* AKA strtok or wcscspn, in reverse. */
|
||||
len = wcslen(path);
|
||||
split = len;
|
||||
|
||||
includes_name = 0;
|
||||
while (split > 0 && path[split - 1] != L'\\' && path[split - 1] != L'/' &&
|
||||
path[split - 1] != L':') {
|
||||
/* check if the path contains a character other than /,\,:,. */
|
||||
if (path[split-1] != '.') {
|
||||
includes_name = 1;
|
||||
}
|
||||
split--;
|
||||
}
|
||||
/* If the path is a relative path with a file name or a folder name */
|
||||
if (split == 0 && includes_name) {
|
||||
path_dirpath = L".";
|
||||
/* If there is a slash or a backslash */
|
||||
} else if (path[split - 1] == L'\\' || path[split - 1] == L'/') {
|
||||
path_dirpath = path;
|
||||
/* If there is no filename, consider it as a relative folder path */
|
||||
if (!includes_name) {
|
||||
split = len;
|
||||
/* Else, split it */
|
||||
} else {
|
||||
splitchar = path[split - 1];
|
||||
path[split - 1] = L'\0';
|
||||
}
|
||||
/* e.g. "..", "c:" */
|
||||
} else {
|
||||
path_dirpath = path;
|
||||
split = len;
|
||||
}
|
||||
path_filename = &path[split];
|
||||
|
||||
len = 0;
|
||||
while (1) {
|
||||
if (path_filename[len] == L'\0')
|
||||
break;
|
||||
if (path_filename[len] == L'*' || path_filename[len] == L'?' ||
|
||||
path_filename[len] == L'>' || path_filename[len] == L'<' ||
|
||||
path_filename[len] == L'"') {
|
||||
ret_error = ERROR_INVALID_NAME;
|
||||
goto cleanup;
|
||||
}
|
||||
len++;
|
||||
}
|
||||
|
||||
/* Get directory handle */
|
||||
handle = CreateFileW(path_dirpath,
|
||||
FILE_LIST_DIRECTORY,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
ret_error = GetLastError();
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Get files in the directory */
|
||||
nt_status = uv__RtlUnicodeStringInit(&FileMask, path_filename, len);
|
||||
if (!NT_SUCCESS(nt_status)) {
|
||||
ret_error = pRtlNtStatusToDosError(nt_status);
|
||||
goto cleanup;
|
||||
}
|
||||
nt_status = pNtQueryDirectoryFile(handle,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&io_status,
|
||||
&dir_info,
|
||||
sizeof(dir_info),
|
||||
FileIdFullDirectoryInformation,
|
||||
TRUE,
|
||||
&FileMask,
|
||||
TRUE);
|
||||
|
||||
/* Buffer overflow (a warning status code) is expected here since there isn't
|
||||
* enough space to store the FileName, and actually indicates success. */
|
||||
if (!NT_SUCCESS(nt_status) && nt_status != STATUS_BUFFER_OVERFLOW) {
|
||||
if (nt_status == STATUS_NO_MORE_FILES)
|
||||
ret_error = ERROR_PATH_NOT_FOUND;
|
||||
else
|
||||
ret_error = pRtlNtStatusToDosError(nt_status);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Assign values to stat_info */
|
||||
memset(&stat_info, 0, sizeof(FILE_STAT_BASIC_INFORMATION));
|
||||
stat_info.FileAttributes = dir_info.FileAttributes;
|
||||
stat_info.CreationTime.QuadPart = dir_info.CreationTime.QuadPart;
|
||||
stat_info.LastAccessTime.QuadPart = dir_info.LastAccessTime.QuadPart;
|
||||
stat_info.LastWriteTime.QuadPart = dir_info.LastWriteTime.QuadPart;
|
||||
if (stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
||||
/* A file handle is needed to get st_size for the link (from
|
||||
* FSCTL_GET_REPARSE_POINT), which is required by posix, but we are here
|
||||
* because getting the file handle failed. We could get just the
|
||||
* ReparsePointTag by querying FILE_ID_EXTD_DIR_INFORMATION instead to make
|
||||
* sure this really is a link before giving up here on the uv_fs_stat call,
|
||||
* but that doesn't seem essential. */
|
||||
if (!do_lstat)
|
||||
goto cleanup;
|
||||
stat_info.EndOfFile.QuadPart = 0;
|
||||
stat_info.AllocationSize.QuadPart = 0;
|
||||
} else {
|
||||
stat_info.EndOfFile.QuadPart = dir_info.EndOfFile.QuadPart;
|
||||
stat_info.AllocationSize.QuadPart = dir_info.AllocationSize.QuadPart;
|
||||
}
|
||||
stat_info.ChangeTime.QuadPart = dir_info.ChangeTime.QuadPart;
|
||||
stat_info.FileId.QuadPart = dir_info.FileId.QuadPart;
|
||||
|
||||
/* Finish up by getting device info from the directory handle,
|
||||
* since files presumably must live on their device. */
|
||||
nt_status = pNtQueryVolumeInformationFile(handle,
|
||||
&io_status,
|
||||
&volume_info,
|
||||
sizeof volume_info,
|
||||
FileFsVolumeInformation);
|
||||
|
||||
/* Buffer overflow (a warning status code) is expected here. */
|
||||
if (io_status.Status == STATUS_NOT_IMPLEMENTED) {
|
||||
stat_info.VolumeSerialNumber.QuadPart = 0;
|
||||
} else if (NT_ERROR(nt_status)) {
|
||||
ret_error = pRtlNtStatusToDosError(nt_status);
|
||||
goto cleanup;
|
||||
} else {
|
||||
stat_info.VolumeSerialNumber.QuadPart = volume_info.VolumeSerialNumber;
|
||||
}
|
||||
|
||||
nt_status = pNtQueryVolumeInformationFile(handle,
|
||||
&io_status,
|
||||
&device_info,
|
||||
sizeof device_info,
|
||||
FileFsDeviceInformation);
|
||||
|
||||
/* Buffer overflow (a warning status code) is expected here. */
|
||||
if (NT_ERROR(nt_status)) {
|
||||
ret_error = pRtlNtStatusToDosError(nt_status);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
stat_info.DeviceType = device_info.DeviceType;
|
||||
stat_info.NumberOfLinks = 1; /* No way to recover this info. */
|
||||
|
||||
fs__stat_assign_statbuf(statbuf, stat_info, do_lstat);
|
||||
ret_error = 0;
|
||||
|
||||
cleanup:
|
||||
if (split != 0)
|
||||
path[split - 1] = splitchar;
|
||||
if (handle != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(handle);
|
||||
return ret_error;
|
||||
}
|
||||
|
||||
INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
|
||||
int do_lstat,
|
||||
@ -1817,6 +2134,17 @@ INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
|
||||
DWORD flags;
|
||||
DWORD ret;
|
||||
|
||||
/* If new API exists, try to use it. */
|
||||
switch (fs__stat_path(path, statbuf, do_lstat)) {
|
||||
case FS__STAT_PATH_SUCCESS:
|
||||
return 0;
|
||||
case FS__STAT_PATH_ERROR:
|
||||
return GetLastError();
|
||||
case FS__STAT_PATH_TRY_SLOW:
|
||||
break;
|
||||
}
|
||||
|
||||
/* If the new API does not exist, use the old API. */
|
||||
flags = FILE_FLAG_BACKUP_SEMANTICS;
|
||||
if (do_lstat)
|
||||
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
|
||||
@ -1829,8 +2157,12 @@ INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
|
||||
flags,
|
||||
NULL);
|
||||
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
return GetLastError();
|
||||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
ret = GetLastError();
|
||||
if (ret != ERROR_ACCESS_DENIED && ret != ERROR_SHARING_VIOLATION)
|
||||
return ret;
|
||||
return fs__stat_directory(path, statbuf, do_lstat, ret);
|
||||
}
|
||||
|
||||
if (fs__stat_handle(handle, statbuf, do_lstat) != 0)
|
||||
ret = GetLastError();
|
||||
@ -2248,14 +2580,29 @@ fchmod_cleanup:
|
||||
|
||||
|
||||
INLINE static int fs__utime_handle(HANDLE handle, double atime, double mtime) {
|
||||
FILETIME filetime_a, filetime_m;
|
||||
FILETIME filetime_as, *filetime_a = &filetime_as;
|
||||
FILETIME filetime_ms, *filetime_m = &filetime_ms;
|
||||
FILETIME now;
|
||||
|
||||
TIME_T_TO_FILETIME(atime, &filetime_a);
|
||||
TIME_T_TO_FILETIME(mtime, &filetime_m);
|
||||
if (uv__isinf(atime) || uv__isinf(mtime))
|
||||
GetSystemTimeAsFileTime(&now);
|
||||
|
||||
if (!SetFileTime(handle, NULL, &filetime_a, &filetime_m)) {
|
||||
if (uv__isinf(atime))
|
||||
filetime_a = &now;
|
||||
else if (uv__isnan(atime))
|
||||
filetime_a = NULL;
|
||||
else
|
||||
TIME_T_TO_FILETIME(atime, filetime_a);
|
||||
|
||||
if (uv__isinf(mtime))
|
||||
filetime_m = &now;
|
||||
else if (uv__isnan(mtime))
|
||||
filetime_m = NULL;
|
||||
else
|
||||
TIME_T_TO_FILETIME(mtime, filetime_m);
|
||||
|
||||
if (!SetFileTime(handle, NULL, filetime_a, filetime_m))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2423,16 +2770,17 @@ static void fs__create_junction(uv_fs_t* req, const WCHAR* path,
|
||||
|
||||
path_buf[path_buf_len++] = path[i];
|
||||
}
|
||||
path_buf[path_buf_len++] = L'\\';
|
||||
if (add_slash)
|
||||
path_buf[path_buf_len++] = L'\\';
|
||||
len = path_buf_len - start;
|
||||
|
||||
/* Insert null terminator */
|
||||
path_buf[path_buf_len++] = L'\0';
|
||||
|
||||
/* Set the info about the substitute name */
|
||||
buffer->MountPointReparseBuffer.SubstituteNameOffset = start * sizeof(WCHAR);
|
||||
buffer->MountPointReparseBuffer.SubstituteNameLength = len * sizeof(WCHAR);
|
||||
|
||||
/* Insert null terminator */
|
||||
path_buf[path_buf_len++] = L'\0';
|
||||
|
||||
/* Copy the print name of the target path */
|
||||
start = path_buf_len;
|
||||
add_slash = 0;
|
||||
@ -2450,18 +2798,18 @@ static void fs__create_junction(uv_fs_t* req, const WCHAR* path,
|
||||
path_buf[path_buf_len++] = path[i];
|
||||
}
|
||||
len = path_buf_len - start;
|
||||
if (len == 2) {
|
||||
if (len == 2 || add_slash) {
|
||||
path_buf[path_buf_len++] = L'\\';
|
||||
len++;
|
||||
}
|
||||
|
||||
/* Insert another null terminator */
|
||||
path_buf[path_buf_len++] = L'\0';
|
||||
|
||||
/* Set the info about the print name */
|
||||
buffer->MountPointReparseBuffer.PrintNameOffset = start * sizeof(WCHAR);
|
||||
buffer->MountPointReparseBuffer.PrintNameLength = len * sizeof(WCHAR);
|
||||
|
||||
/* Insert another null terminator */
|
||||
path_buf[path_buf_len++] = L'\0';
|
||||
|
||||
/* Calculate how much buffer space was actually used */
|
||||
used_buf_size = FIELD_OFFSET(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer) +
|
||||
path_buf_len * sizeof(WCHAR);
|
||||
@ -2830,7 +3178,7 @@ static void uv__fs_done(struct uv__work* w, int status) {
|
||||
uv_fs_t* req;
|
||||
|
||||
req = container_of(w, uv_fs_t, work_req);
|
||||
uv__req_unregister(req->loop, req);
|
||||
uv__req_unregister(req->loop);
|
||||
|
||||
if (status == UV_ECANCELED) {
|
||||
assert(req->result == 0);
|
||||
|
@ -71,10 +71,9 @@ int uv__getaddrinfo_translate_error(int sys_err) {
|
||||
DECLSPEC_IMPORT void WSAAPI FreeAddrInfoW(PADDRINFOW pAddrInfo);
|
||||
#endif
|
||||
|
||||
|
||||
/* Adjust size value to be multiple of 4. Use to keep pointer aligned.
|
||||
* Do we need different versions of this for different architectures? */
|
||||
#define ALIGNED_SIZE(X) ((((X) + 3) >> 2) << 2)
|
||||
static size_t align_offset(size_t off, size_t alignment) {
|
||||
return ((off + alignment - 1) / alignment) * alignment;
|
||||
}
|
||||
|
||||
#ifndef NDIS_IF_MAX_STRING_SIZE
|
||||
#define NDIS_IF_MAX_STRING_SIZE IF_MAX_STRING_SIZE
|
||||
@ -103,17 +102,7 @@ static void uv__getaddrinfo_work(struct uv__work* w) {
|
||||
* Each size calculation is adjusted to avoid unaligned pointers.
|
||||
*/
|
||||
static void uv__getaddrinfo_done(struct uv__work* w, int status) {
|
||||
uv_getaddrinfo_t* req;
|
||||
size_t addrinfo_len = 0;
|
||||
ssize_t name_len = 0;
|
||||
size_t addrinfo_struct_len = ALIGNED_SIZE(sizeof(struct addrinfo));
|
||||
struct addrinfoW* addrinfow_ptr;
|
||||
struct addrinfo* addrinfo_ptr;
|
||||
char* alloc_ptr = NULL;
|
||||
char* cur_ptr = NULL;
|
||||
int r;
|
||||
|
||||
req = container_of(w, uv_getaddrinfo_t, work_req);
|
||||
uv_getaddrinfo_t* req = container_of(w, uv_getaddrinfo_t, work_req);
|
||||
|
||||
/* release input parameter memory */
|
||||
uv__free(req->alloc);
|
||||
@ -126,34 +115,44 @@ static void uv__getaddrinfo_done(struct uv__work* w, int status) {
|
||||
}
|
||||
|
||||
if (req->retcode == 0) {
|
||||
char* alloc_ptr = NULL;
|
||||
size_t cur_off = 0;
|
||||
size_t addrinfo_len;
|
||||
/* Convert addrinfoW to addrinfo. First calculate required length. */
|
||||
addrinfow_ptr = req->addrinfow;
|
||||
struct addrinfoW* addrinfow_ptr = req->addrinfow;
|
||||
while (addrinfow_ptr != NULL) {
|
||||
addrinfo_len += addrinfo_struct_len +
|
||||
ALIGNED_SIZE(addrinfow_ptr->ai_addrlen);
|
||||
cur_off = align_offset(cur_off, sizeof(void*));
|
||||
cur_off += sizeof(struct addrinfo);
|
||||
/* TODO: This alignment could be smaller, if we could
|
||||
portably get the alignment for sockaddr. */
|
||||
cur_off = align_offset(cur_off, sizeof(void*));
|
||||
cur_off += addrinfow_ptr->ai_addrlen;
|
||||
if (addrinfow_ptr->ai_canonname != NULL) {
|
||||
name_len = uv_utf16_length_as_wtf8(addrinfow_ptr->ai_canonname, -1);
|
||||
ssize_t name_len =
|
||||
uv_utf16_length_as_wtf8(addrinfow_ptr->ai_canonname, -1);
|
||||
if (name_len < 0) {
|
||||
req->retcode = name_len;
|
||||
goto complete;
|
||||
}
|
||||
addrinfo_len += ALIGNED_SIZE(name_len + 1);
|
||||
cur_off += name_len + 1;
|
||||
}
|
||||
addrinfow_ptr = addrinfow_ptr->ai_next;
|
||||
}
|
||||
|
||||
/* allocate memory for addrinfo results */
|
||||
alloc_ptr = (char*)uv__malloc(addrinfo_len);
|
||||
addrinfo_len = cur_off;
|
||||
alloc_ptr = uv__malloc(addrinfo_len);
|
||||
|
||||
/* do conversions */
|
||||
if (alloc_ptr != NULL) {
|
||||
cur_ptr = alloc_ptr;
|
||||
struct addrinfo *addrinfo_ptr = (struct addrinfo *)alloc_ptr;
|
||||
cur_off = 0;
|
||||
addrinfow_ptr = req->addrinfow;
|
||||
|
||||
while (addrinfow_ptr != NULL) {
|
||||
for (;;) {
|
||||
cur_off += sizeof(struct addrinfo);
|
||||
assert(cur_off <= addrinfo_len);
|
||||
/* copy addrinfo struct data */
|
||||
assert(cur_ptr + addrinfo_struct_len <= alloc_ptr + addrinfo_len);
|
||||
addrinfo_ptr = (struct addrinfo*)cur_ptr;
|
||||
addrinfo_ptr->ai_family = addrinfow_ptr->ai_family;
|
||||
addrinfo_ptr->ai_socktype = addrinfow_ptr->ai_socktype;
|
||||
addrinfo_ptr->ai_protocol = addrinfow_ptr->ai_protocol;
|
||||
@ -163,35 +162,38 @@ static void uv__getaddrinfo_done(struct uv__work* w, int status) {
|
||||
addrinfo_ptr->ai_addr = NULL;
|
||||
addrinfo_ptr->ai_next = NULL;
|
||||
|
||||
cur_ptr += addrinfo_struct_len;
|
||||
|
||||
/* copy sockaddr */
|
||||
if (addrinfo_ptr->ai_addrlen > 0) {
|
||||
assert(cur_ptr + addrinfo_ptr->ai_addrlen <=
|
||||
alloc_ptr + addrinfo_len);
|
||||
memcpy(cur_ptr, addrinfow_ptr->ai_addr, addrinfo_ptr->ai_addrlen);
|
||||
addrinfo_ptr->ai_addr = (struct sockaddr*)cur_ptr;
|
||||
cur_ptr += ALIGNED_SIZE(addrinfo_ptr->ai_addrlen);
|
||||
cur_off = align_offset(cur_off, sizeof(void *));
|
||||
addrinfo_ptr->ai_addr = (struct sockaddr *)(alloc_ptr + cur_off);
|
||||
cur_off += addrinfo_ptr->ai_addrlen;
|
||||
assert(cur_off <= addrinfo_len);
|
||||
memcpy(addrinfo_ptr->ai_addr,
|
||||
addrinfow_ptr->ai_addr,
|
||||
addrinfo_ptr->ai_addrlen);
|
||||
}
|
||||
|
||||
/* convert canonical name to UTF-8 */
|
||||
if (addrinfow_ptr->ai_canonname != NULL) {
|
||||
name_len = alloc_ptr + addrinfo_len - cur_ptr;
|
||||
r = uv__copy_utf16_to_utf8(addrinfow_ptr->ai_canonname,
|
||||
-1,
|
||||
cur_ptr,
|
||||
(size_t*)&name_len);
|
||||
ssize_t name_len = addrinfo_len - cur_off;
|
||||
addrinfo_ptr->ai_canonname = alloc_ptr + cur_off;
|
||||
int r = uv__copy_utf16_to_utf8(addrinfow_ptr->ai_canonname,
|
||||
-1,
|
||||
addrinfo_ptr->ai_canonname,
|
||||
(size_t*)&name_len);
|
||||
assert(r == 0);
|
||||
addrinfo_ptr->ai_canonname = cur_ptr;
|
||||
cur_ptr += ALIGNED_SIZE(name_len + 1);
|
||||
cur_off += name_len + 1;
|
||||
assert(cur_off <= addrinfo_len);
|
||||
}
|
||||
assert(cur_ptr <= alloc_ptr + addrinfo_len);
|
||||
|
||||
/* set next ptr */
|
||||
addrinfow_ptr = addrinfow_ptr->ai_next;
|
||||
if (addrinfow_ptr != NULL) {
|
||||
addrinfo_ptr->ai_next = (struct addrinfo*)cur_ptr;
|
||||
}
|
||||
if (addrinfow_ptr == NULL)
|
||||
break;
|
||||
cur_off = align_offset(cur_off, sizeof(void *));
|
||||
struct addrinfo *next_addrinfo_ptr = (struct addrinfo *)(alloc_ptr + cur_off);
|
||||
addrinfo_ptr->ai_next = next_addrinfo_ptr;
|
||||
addrinfo_ptr = next_addrinfo_ptr;
|
||||
}
|
||||
req->addrinfo = (struct addrinfo*)alloc_ptr;
|
||||
} else {
|
||||
@ -206,7 +208,7 @@ static void uv__getaddrinfo_done(struct uv__work* w, int status) {
|
||||
}
|
||||
|
||||
complete:
|
||||
uv__req_unregister(req->loop, req);
|
||||
uv__req_unregister(req->loop);
|
||||
|
||||
/* finally do callback with converted result */
|
||||
if (req->getaddrinfo_cb)
|
||||
@ -242,10 +244,12 @@ int uv_getaddrinfo(uv_loop_t* loop,
|
||||
const char* service,
|
||||
const struct addrinfo* hints) {
|
||||
char hostname_ascii[256];
|
||||
size_t off = 0;
|
||||
size_t nodesize = 0;
|
||||
size_t servicesize = 0;
|
||||
size_t serviceoff = 0;
|
||||
size_t hintssize = 0;
|
||||
char* alloc_ptr = NULL;
|
||||
size_t hintoff = 0;
|
||||
ssize_t rc;
|
||||
|
||||
if (req == NULL || (node == NULL && service == NULL)) {
|
||||
@ -268,6 +272,7 @@ int uv_getaddrinfo(uv_loop_t* loop,
|
||||
return rc;
|
||||
nodesize = strlen(hostname_ascii) + 1;
|
||||
node = hostname_ascii;
|
||||
off += nodesize * sizeof(WCHAR);
|
||||
}
|
||||
|
||||
if (service != NULL) {
|
||||
@ -275,27 +280,28 @@ int uv_getaddrinfo(uv_loop_t* loop,
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
servicesize = rc;
|
||||
off = align_offset(off, sizeof(WCHAR));
|
||||
serviceoff = off;
|
||||
off += servicesize * sizeof(WCHAR);
|
||||
}
|
||||
|
||||
if (hints != NULL) {
|
||||
hintssize = ALIGNED_SIZE(sizeof(struct addrinfoW));
|
||||
off = align_offset(off, sizeof(void *));
|
||||
hintoff = off;
|
||||
hintssize = sizeof(struct addrinfoW);
|
||||
off += hintssize;
|
||||
}
|
||||
|
||||
/* allocate memory for inputs, and partition it as needed */
|
||||
alloc_ptr = uv__malloc(ALIGNED_SIZE(nodesize * sizeof(WCHAR)) +
|
||||
ALIGNED_SIZE(servicesize * sizeof(WCHAR)) +
|
||||
hintssize);
|
||||
if (!alloc_ptr)
|
||||
req->alloc = uv__malloc(off);
|
||||
if (!req->alloc)
|
||||
return UV_ENOMEM;
|
||||
|
||||
/* save alloc_ptr now so we can free if error */
|
||||
req->alloc = (void*) alloc_ptr;
|
||||
|
||||
/* Convert node string to UTF16 into allocated memory and save pointer in the
|
||||
* request. The node here has been converted to ascii. */
|
||||
if (node != NULL) {
|
||||
req->node = (WCHAR*) alloc_ptr;
|
||||
uv_wtf8_to_utf16(node, (WCHAR*) alloc_ptr, nodesize);
|
||||
alloc_ptr += ALIGNED_SIZE(nodesize * sizeof(WCHAR));
|
||||
req->node = (WCHAR*) req->alloc;
|
||||
uv_wtf8_to_utf16(node, req->node, nodesize);
|
||||
} else {
|
||||
req->node = NULL;
|
||||
}
|
||||
@ -303,16 +309,15 @@ int uv_getaddrinfo(uv_loop_t* loop,
|
||||
/* Convert service string to UTF16 into allocated memory and save pointer in
|
||||
* the req. */
|
||||
if (service != NULL) {
|
||||
req->service = (WCHAR*) alloc_ptr;
|
||||
uv_wtf8_to_utf16(service, (WCHAR*) alloc_ptr, servicesize);
|
||||
alloc_ptr += ALIGNED_SIZE(servicesize * sizeof(WCHAR));
|
||||
req->service = (WCHAR*) ((char*) req->alloc + serviceoff);
|
||||
uv_wtf8_to_utf16(service, req->service, servicesize);
|
||||
} else {
|
||||
req->service = NULL;
|
||||
}
|
||||
|
||||
/* copy hints to allocated memory and save pointer in req */
|
||||
if (hints != NULL) {
|
||||
req->addrinfow = (struct addrinfoW*) alloc_ptr;
|
||||
req->addrinfow = (struct addrinfoW*) ((char*) req->alloc + hintoff);
|
||||
req->addrinfow->ai_family = hints->ai_family;
|
||||
req->addrinfow->ai_socktype = hints->ai_socktype;
|
||||
req->addrinfow->ai_protocol = hints->ai_protocol;
|
||||
@ -325,7 +330,7 @@ int uv_getaddrinfo(uv_loop_t* loop,
|
||||
req->addrinfow = NULL;
|
||||
}
|
||||
|
||||
uv__req_register(loop, req);
|
||||
uv__req_register(loop);
|
||||
|
||||
if (getaddrinfo_cb) {
|
||||
uv__work_submit(loop,
|
||||
|
@ -82,7 +82,7 @@ static void uv__getnameinfo_done(struct uv__work* w, int status) {
|
||||
char* service;
|
||||
|
||||
req = container_of(w, uv_getnameinfo_t, work_req);
|
||||
uv__req_unregister(req->loop, req);
|
||||
uv__req_unregister(req->loop);
|
||||
host = service = NULL;
|
||||
|
||||
if (status == UV_ECANCELED) {
|
||||
@ -124,7 +124,7 @@ int uv_getnameinfo(uv_loop_t* loop,
|
||||
}
|
||||
|
||||
UV_REQ_INIT(req, UV_GETNAMEINFO);
|
||||
uv__req_register(loop, req);
|
||||
uv__req_register(loop);
|
||||
|
||||
req->getnameinfo_cb = getnameinfo_cb;
|
||||
req->flags = flags;
|
||||
|
@ -330,4 +330,6 @@ void uv__wake_all_loops(void);
|
||||
*/
|
||||
void uv__init_detect_system_wakeup(void);
|
||||
|
||||
int uv_translate_write_sys_error(int sys_errno);
|
||||
|
||||
#endif /* UV_WIN_INTERNAL_H_ */
|
||||
|
382
src/win/pipe.c
382
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 uv__unique_pipe_name(char* ptr, char* name, size_t size) {
|
||||
snprintf(name, size, "\\\\?\\pipe\\uv\\%p-%lu", ptr, GetCurrentProcessId());
|
||||
/* Does the file path contain embedded nul bytes? */
|
||||
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)
|
||||
CloseHandle(pipe->handle);
|
||||
else
|
||||
close(pipe->u.fd);
|
||||
_close(pipe->u.fd);
|
||||
|
||||
pipe->u.fd = -1;
|
||||
pipe->handle = INVALID_HANDLE_VALUE;
|
||||
@ -200,7 +208,7 @@ static void close_pipe(uv_pipe_t* pipe) {
|
||||
|
||||
static int uv__pipe_server(
|
||||
HANDLE* pipeHandle_ptr, DWORD access,
|
||||
char* name, size_t nameSize, char* random) {
|
||||
char* name, size_t nameSize, unsigned long long random) {
|
||||
HANDLE pipeHandle;
|
||||
int err;
|
||||
|
||||
@ -241,7 +249,7 @@ static int uv__pipe_server(
|
||||
static int uv__create_pipe_pair(
|
||||
HANDLE* server_pipe_ptr, HANDLE* client_pipe_ptr,
|
||||
unsigned int server_flags, unsigned int client_flags,
|
||||
int inherit_client, char* random) {
|
||||
int inherit_client, unsigned long long random) {
|
||||
/* allowed flags are: UV_READABLE_PIPE | UV_WRITABLE_PIPE | UV_NONBLOCK_PIPE */
|
||||
char pipe_name[64];
|
||||
SECURITY_ATTRIBUTES sa;
|
||||
@ -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? */
|
||||
read_flags |= UV_READABLE_PIPE;
|
||||
write_flags |= UV_WRITABLE_PIPE;
|
||||
err = uv__create_pipe_pair(&readh, &writeh, read_flags, write_flags, 0, (char*) &fds[0]);
|
||||
err = uv__create_pipe_pair(&readh,
|
||||
&writeh,
|
||||
read_flags,
|
||||
write_flags,
|
||||
0,
|
||||
(uintptr_t) &fds[0]);
|
||||
if (err != 0)
|
||||
return err;
|
||||
temp[0] = _open_osfhandle((intptr_t) readh, 0);
|
||||
@ -413,7 +426,7 @@ int uv__create_stdio_pipe_pair(uv_loop_t* loop,
|
||||
}
|
||||
|
||||
err = uv__create_pipe_pair(&server_pipe, &client_pipe,
|
||||
server_flags, client_flags, 1, (char*) server_pipe);
|
||||
server_flags, client_flags, 1, (uintptr_t) server_pipe);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
@ -659,15 +672,10 @@ void uv__pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) {
|
||||
}
|
||||
handle->pipe.conn.ipc_xfer_queue_length = 0;
|
||||
|
||||
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
|
||||
if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) {
|
||||
UnregisterWait(handle->read_req.wait_handle);
|
||||
handle->read_req.wait_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
if (handle->read_req.event_handle != NULL) {
|
||||
CloseHandle(handle->read_req.event_handle);
|
||||
handle->read_req.event_handle = NULL;
|
||||
}
|
||||
assert(handle->read_req.wait_handle == INVALID_HANDLE_VALUE);
|
||||
if (handle->read_req.event_handle != NULL) {
|
||||
CloseHandle(handle->read_req.event_handle);
|
||||
handle->read_req.event_handle = NULL;
|
||||
}
|
||||
|
||||
if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)
|
||||
@ -705,6 +713,7 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
||||
uv_loop_t* loop = handle->loop;
|
||||
int i, err;
|
||||
uv_pipe_accept_t* req;
|
||||
char* name_copy;
|
||||
|
||||
if (flags & ~UV_PIPE_NO_TRUNCATE) {
|
||||
return UV_EINVAL;
|
||||
@ -718,16 +727,10 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
if (*name == '\0') {
|
||||
if (includes_nul(name, namelen)) {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
if (flags & UV_PIPE_NO_TRUNCATE) {
|
||||
if (namelen > 256) {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (handle->flags & UV_HANDLE_BOUND) {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
@ -736,14 +739,24 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
||||
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)) {
|
||||
handle->pipe.serv.pending_instances = default_pending_pipe_instances;
|
||||
}
|
||||
|
||||
err = UV_ENOMEM;
|
||||
handle->pipe.serv.accept_reqs = (uv_pipe_accept_t*)
|
||||
uv__malloc(sizeof(uv_pipe_accept_t) * handle->pipe.serv.pending_instances);
|
||||
if (!handle->pipe.serv.accept_reqs)
|
||||
return UV_ENOMEM;
|
||||
if (handle->pipe.serv.accept_reqs == NULL) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0; i < handle->pipe.serv.pending_instances; i++) {
|
||||
req = &handle->pipe.serv.accept_reqs[i];
|
||||
@ -753,9 +766,14 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
||||
req->next_pending = NULL;
|
||||
}
|
||||
|
||||
err = uv__convert_utf8_to_utf16(name, &handle->name);
|
||||
if (err)
|
||||
return err;
|
||||
/* 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) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)) {
|
||||
err = GetLastError();
|
||||
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) {
|
||||
err = WSAEACCES; /* Translates to UV_EACCES. */
|
||||
err = UV_EACCES;
|
||||
} else {
|
||||
err = uv_translate_sys_error(err);
|
||||
}
|
||||
goto error;
|
||||
}
|
||||
@ -781,10 +801,13 @@ int uv_pipe_bind2(uv_pipe_t* handle,
|
||||
return 0;
|
||||
|
||||
error:
|
||||
uv__free(handle->pipe.serv.accept_reqs);
|
||||
uv__free(handle->name);
|
||||
uv__free(name_copy);
|
||||
handle->pipe.serv.accept_reqs = 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,
|
||||
const char* name,
|
||||
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,
|
||||
unsigned int flags,
|
||||
uv_connect_cb cb) {
|
||||
uv_loop_t* loop = handle->loop;
|
||||
uv_loop_t* loop;
|
||||
int err;
|
||||
size_t nameSize;
|
||||
HANDLE pipeHandle = INVALID_HANDLE_VALUE;
|
||||
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) {
|
||||
return UV_EINVAL;
|
||||
@ -862,22 +906,17 @@ int uv_pipe_connect2(uv_connect_t* req,
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
if (*name == '\0') {
|
||||
if (includes_nul(name, namelen)) {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
if (flags & UV_PIPE_NO_TRUNCATE) {
|
||||
if (namelen > 256) {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
name_copy = uv__malloc(namelen + 1);
|
||||
if (name_copy == NULL) {
|
||||
return UV_ENOMEM;
|
||||
}
|
||||
|
||||
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;
|
||||
memcpy(name_copy, name, namelen);
|
||||
name_copy[namelen] = '\0';
|
||||
|
||||
if (handle->flags & UV_HANDLE_PIPESERVER) {
|
||||
err = ERROR_INVALID_PARAMETER;
|
||||
@ -889,7 +928,11 @@ int uv_pipe_connect2(uv_connect_t* req,
|
||||
}
|
||||
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) {
|
||||
err = ERROR_NO_UNICODE_TRANSLATION;
|
||||
goto error;
|
||||
@ -916,7 +959,7 @@ int uv_pipe_connect2(uv_connect_t* req,
|
||||
goto error;
|
||||
}
|
||||
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
handle->reqs_pending++;
|
||||
|
||||
return 0;
|
||||
@ -931,10 +974,12 @@ int uv_pipe_connect2(uv_connect_t* req,
|
||||
SET_REQ_SUCCESS(req);
|
||||
uv__insert_pending_req(loop, (uv_req_t*) req);
|
||||
handle->reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
uv__free(name_copy);
|
||||
|
||||
if (handle->name) {
|
||||
uv__free(handle->name);
|
||||
handle->name = NULL;
|
||||
@ -947,7 +992,7 @@ error:
|
||||
SET_REQ_ERROR(req, err);
|
||||
uv__insert_pending_req(loop, (uv_req_t*) req);
|
||||
handle->reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1116,9 +1161,9 @@ int uv__pipe_accept(uv_pipe_t* server, uv_stream_t* client) {
|
||||
|
||||
err = uv__tcp_xfer_import(
|
||||
(uv_tcp_t*) client, item->xfer_type, &item->xfer_info);
|
||||
|
||||
|
||||
uv__free(item);
|
||||
|
||||
|
||||
if (err != 0)
|
||||
return err;
|
||||
|
||||
@ -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 (req->wait_handle == INVALID_HANDLE_VALUE) {
|
||||
if (!RegisterWaitForSingleObject(&req->wait_handle,
|
||||
req->event_handle, post_completion_read_wait, (void*) req,
|
||||
INFINITE, WT_EXECUTEINWAITTHREAD)) {
|
||||
SET_REQ_ERROR(req, GetLastError());
|
||||
goto error;
|
||||
}
|
||||
assert(req->wait_handle == INVALID_HANDLE_VALUE);
|
||||
if (!RegisterWaitForSingleObject(&req->wait_handle,
|
||||
req->event_handle, post_completion_read_wait, (void*) req,
|
||||
INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) {
|
||||
SET_REQ_ERROR(req, GetLastError());
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1406,16 +1450,16 @@ int uv__pipe_read_start(uv_pipe_t* handle,
|
||||
handle->read_cb = read_cb;
|
||||
handle->alloc_cb = alloc_cb;
|
||||
|
||||
if (handle->read_req.event_handle == NULL) {
|
||||
handle->read_req.event_handle = CreateEvent(NULL, 0, 0, NULL);
|
||||
if (handle->read_req.event_handle == NULL) {
|
||||
uv_fatal_error(GetLastError(), "CreateEvent");
|
||||
}
|
||||
}
|
||||
|
||||
/* If reading was stopped and then started again, there could still be a read
|
||||
* request pending. */
|
||||
if (!(handle->flags & UV_HANDLE_READ_PENDING)) {
|
||||
if (handle->flags & UV_HANDLE_EMULATE_IOCP &&
|
||||
handle->read_req.event_handle == NULL) {
|
||||
handle->read_req.event_handle = CreateEvent(NULL, 0, 0, NULL);
|
||||
if (handle->read_req.event_handle == NULL) {
|
||||
uv_fatal_error(GetLastError(), "CreateEvent");
|
||||
}
|
||||
}
|
||||
uv__pipe_queue_read(loop, handle);
|
||||
}
|
||||
|
||||
@ -1593,7 +1637,7 @@ static int uv__pipe_write_data(uv_loop_t* loop,
|
||||
req->u.io.queued_bytes = 0;
|
||||
}
|
||||
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
handle->reqs_pending++;
|
||||
handle->stream.conn.write_reqs_pending++;
|
||||
POST_COMPLETION_FOR_REQ(loop, req);
|
||||
@ -1641,7 +1685,7 @@ static int uv__pipe_write_data(uv_loop_t* loop,
|
||||
CloseHandle(req->event_handle);
|
||||
req->event_handle = NULL;
|
||||
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
handle->reqs_pending++;
|
||||
handle->stream.conn.write_reqs_pending++;
|
||||
return 0;
|
||||
@ -1668,13 +1712,13 @@ static int uv__pipe_write_data(uv_loop_t* loop,
|
||||
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
|
||||
if (!RegisterWaitForSingleObject(&req->wait_handle,
|
||||
req->event_handle, post_completion_write_wait, (void*) req,
|
||||
INFINITE, WT_EXECUTEINWAITTHREAD)) {
|
||||
INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) {
|
||||
return GetLastError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
handle->reqs_pending++;
|
||||
handle->stream.conn.write_reqs_pending++;
|
||||
|
||||
@ -1694,7 +1738,7 @@ static DWORD uv__pipe_get_ipc_remote_pid(uv_pipe_t* handle) {
|
||||
GetNamedPipeServerProcessId(handle->handle, pid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return *pid;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
int error, uv_buf_t buf) {
|
||||
DWORD error, uv_buf_t buf) {
|
||||
if (error == ERROR_BROKEN_PIPE) {
|
||||
uv__pipe_read_eof(loop, handle, buf);
|
||||
} else {
|
||||
@ -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
|
||||
* encountered before the requested number of bytes are read, an error is
|
||||
* returned. */
|
||||
static int uv__pipe_read_exactly(HANDLE h, void* buffer, DWORD count) {
|
||||
DWORD bytes_read, bytes_read_now;
|
||||
static DWORD uv__pipe_read_exactly(uv_pipe_t* handle, void* buffer, DWORD count) {
|
||||
uv_read_t* req;
|
||||
DWORD bytes_read;
|
||||
DWORD bytes_read_now;
|
||||
|
||||
bytes_read = 0;
|
||||
while (bytes_read < count) {
|
||||
if (!ReadFile(h,
|
||||
req = &handle->read_req;
|
||||
memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
|
||||
req->u.io.overlapped.hEvent = (HANDLE) ((uintptr_t) req->event_handle | 1);
|
||||
if (!ReadFile(handle->handle,
|
||||
(char*) buffer + bytes_read,
|
||||
count - bytes_read,
|
||||
&bytes_read_now,
|
||||
NULL)) {
|
||||
return GetLastError();
|
||||
&req->u.io.overlapped)) {
|
||||
if (GetLastError() != ERROR_IO_PENDING)
|
||||
return GetLastError();
|
||||
if (!GetOverlappedResult(handle->handle, &req->u.io.overlapped, &bytes_read_now, TRUE))
|
||||
return GetLastError();
|
||||
}
|
||||
|
||||
bytes_read += bytes_read_now;
|
||||
@ -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,
|
||||
uv_pipe_t* handle,
|
||||
DWORD suggested_bytes,
|
||||
DWORD max_bytes) {
|
||||
DWORD bytes_read;
|
||||
static int uv__pipe_read_data(uv_loop_t* loop,
|
||||
uv_pipe_t* handle,
|
||||
DWORD* bytes_read, /* inout argument */
|
||||
DWORD max_bytes) {
|
||||
uv_buf_t buf;
|
||||
uv_read_t* req;
|
||||
DWORD r;
|
||||
DWORD bytes_available;
|
||||
int more;
|
||||
|
||||
/* Ask the user for a buffer to read data into. */
|
||||
buf = uv_buf_init(NULL, 0);
|
||||
handle->alloc_cb((uv_handle_t*) handle, suggested_bytes, &buf);
|
||||
handle->alloc_cb((uv_handle_t*) handle, *bytes_read, &buf);
|
||||
if (buf.base == NULL || buf.len == 0) {
|
||||
handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf);
|
||||
return 0; /* Break out of read loop. */
|
||||
@ -1913,33 +1968,77 @@ static DWORD uv__pipe_read_data(uv_loop_t* loop,
|
||||
/* Ensure we read at most the smaller of:
|
||||
* (a) the length of the user-allocated buffer.
|
||||
* (b) the maximum data length as specified by the `max_bytes` argument.
|
||||
* (c) the amount of data that can be read non-blocking
|
||||
*/
|
||||
if (max_bytes > buf.len)
|
||||
max_bytes = buf.len;
|
||||
|
||||
/* Read into the user buffer. */
|
||||
if (!ReadFile(handle->handle, buf.base, max_bytes, &bytes_read, NULL)) {
|
||||
uv__pipe_read_error_or_eof(loop, handle, GetLastError(), buf);
|
||||
return 0; /* Break out of read loop. */
|
||||
if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) {
|
||||
/* The user failed to supply a pipe that can be used non-blocking or with
|
||||
* threads. Try to estimate the amount of data that is safe to read without
|
||||
* blocking, in a race-y way however. */
|
||||
bytes_available = 0;
|
||||
if (!PeekNamedPipe(handle->handle, NULL, 0, NULL, &bytes_available, NULL)) {
|
||||
r = GetLastError();
|
||||
} else {
|
||||
if (max_bytes > bytes_available)
|
||||
max_bytes = bytes_available;
|
||||
*bytes_read = 0;
|
||||
if (max_bytes == 0 || ReadFile(handle->handle, buf.base, max_bytes, bytes_read, NULL))
|
||||
r = ERROR_SUCCESS;
|
||||
else
|
||||
r = GetLastError();
|
||||
}
|
||||
more = max_bytes < bytes_available;
|
||||
} else {
|
||||
/* Read into the user buffer.
|
||||
* Prepare an Event so that we can cancel if it doesn't complete immediately.
|
||||
*/
|
||||
req = &handle->read_req;
|
||||
memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped));
|
||||
req->u.io.overlapped.hEvent = (HANDLE) ((uintptr_t) req->event_handle | 1);
|
||||
if (ReadFile(handle->handle, buf.base, max_bytes, bytes_read, &req->u.io.overlapped)) {
|
||||
r = ERROR_SUCCESS;
|
||||
} else {
|
||||
r = GetLastError();
|
||||
*bytes_read = 0;
|
||||
if (r == ERROR_IO_PENDING) {
|
||||
r = CancelIoEx(handle->handle, &req->u.io.overlapped);
|
||||
assert(r || GetLastError() == ERROR_NOT_FOUND);
|
||||
if (GetOverlappedResult(handle->handle, &req->u.io.overlapped, bytes_read, TRUE)) {
|
||||
r = ERROR_SUCCESS;
|
||||
} else {
|
||||
r = GetLastError();
|
||||
*bytes_read = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
more = *bytes_read == max_bytes;
|
||||
}
|
||||
|
||||
/* Call the read callback. */
|
||||
handle->read_cb((uv_stream_t*) handle, bytes_read, &buf);
|
||||
if (r == ERROR_SUCCESS || r == ERROR_OPERATION_ABORTED)
|
||||
handle->read_cb((uv_stream_t*) handle, *bytes_read, &buf);
|
||||
else
|
||||
uv__pipe_read_error_or_eof(loop, handle, r, buf);
|
||||
|
||||
return bytes_read;
|
||||
return more;
|
||||
}
|
||||
|
||||
|
||||
static DWORD uv__pipe_read_ipc(uv_loop_t* loop, uv_pipe_t* handle) {
|
||||
uint32_t* data_remaining = &handle->pipe.conn.ipc_data_frame.payload_remaining;
|
||||
int err;
|
||||
static int uv__pipe_read_ipc(uv_loop_t* loop, uv_pipe_t* handle) {
|
||||
uint32_t* data_remaining;
|
||||
DWORD err;
|
||||
DWORD more;
|
||||
DWORD bytes_read;
|
||||
|
||||
data_remaining = &handle->pipe.conn.ipc_data_frame.payload_remaining;
|
||||
|
||||
if (*data_remaining > 0) {
|
||||
/* Read frame data payload. */
|
||||
DWORD bytes_read =
|
||||
uv__pipe_read_data(loop, handle, *data_remaining, *data_remaining);
|
||||
bytes_read = *data_remaining;
|
||||
more = uv__pipe_read_data(loop, handle, &bytes_read, bytes_read);
|
||||
*data_remaining -= bytes_read;
|
||||
return bytes_read;
|
||||
|
||||
} else {
|
||||
/* Start of a new IPC frame. */
|
||||
@ -1950,7 +2049,7 @@ static DWORD uv__pipe_read_ipc(uv_loop_t* loop, uv_pipe_t* handle) {
|
||||
|
||||
/* Read the IPC frame header. */
|
||||
err = uv__pipe_read_exactly(
|
||||
handle->handle, &frame_header, sizeof frame_header);
|
||||
handle, &frame_header, sizeof frame_header);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
@ -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
|
||||
* subsequent invocation of uv__pipe_read_ipc(). */
|
||||
if (xfer_type == UV__IPC_SOCKET_XFER_NONE)
|
||||
return sizeof frame_header; /* Number of bytes read. */
|
||||
if (xfer_type != UV__IPC_SOCKET_XFER_NONE) {
|
||||
/* Read transferred socket information. */
|
||||
err = uv__pipe_read_exactly(handle, &xfer_info, sizeof xfer_info);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* Read transferred socket information. */
|
||||
err = uv__pipe_read_exactly(handle->handle, &xfer_info, sizeof xfer_info);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
/* Store the pending socket info. */
|
||||
uv__pipe_queue_ipc_xfer_info(handle, xfer_type, &xfer_info);
|
||||
|
||||
/* Return number of bytes read. */
|
||||
return sizeof frame_header + sizeof xfer_info;
|
||||
/* Store the pending socket info. */
|
||||
uv__pipe_queue_ipc_xfer_info(handle, xfer_type, &xfer_info);
|
||||
}
|
||||
}
|
||||
|
||||
/* Return whether the caller should immediately try another read call to get
|
||||
* more data. Calling uv__pipe_read_exactly will hang if there isn't data
|
||||
* available, so we cannot do this unless we are guaranteed not to reach that.
|
||||
*/
|
||||
more = *data_remaining > 0;
|
||||
return more;
|
||||
|
||||
invalid:
|
||||
/* Invalid frame. */
|
||||
err = WSAECONNABORTED; /* Maps to UV_ECONNABORTED. */
|
||||
@ -2014,12 +2116,20 @@ error:
|
||||
void uv__process_pipe_read_req(uv_loop_t* loop,
|
||||
uv_pipe_t* handle,
|
||||
uv_req_t* req) {
|
||||
DWORD err;
|
||||
DWORD more;
|
||||
DWORD bytes_requested;
|
||||
assert(handle->type == UV_NAMED_PIPE);
|
||||
|
||||
handle->flags &= ~(UV_HANDLE_READ_PENDING | UV_HANDLE_CANCELLATION_PENDING);
|
||||
DECREASE_PENDING_REQ_COUNT(handle);
|
||||
eof_timer_stop(handle);
|
||||
|
||||
if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) {
|
||||
UnregisterWait(handle->read_req.wait_handle);
|
||||
handle->read_req.wait_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
/* At this point, we're done with bookkeeping. If the user has stopped
|
||||
* reading the pipe in the meantime, there is nothing left to do, since there
|
||||
* is no callback that we can call. */
|
||||
@ -2028,7 +2138,7 @@ void uv__process_pipe_read_req(uv_loop_t* loop,
|
||||
|
||||
if (!REQ_SUCCESS(req)) {
|
||||
/* An error occurred doing the zero-read. */
|
||||
DWORD err = GET_REQ_ERROR(req);
|
||||
err = GET_REQ_ERROR(req);
|
||||
|
||||
/* If the read was cancelled by uv__pipe_interrupt_read(), the request may
|
||||
* indicate an ERROR_OPERATION_ABORTED error. This error isn't relevant to
|
||||
@ -2039,34 +2149,18 @@ void uv__process_pipe_read_req(uv_loop_t* loop,
|
||||
} else {
|
||||
/* The zero-read completed without error, indicating there is data
|
||||
* available in the kernel buffer. */
|
||||
DWORD avail;
|
||||
|
||||
/* Get the number of bytes available. */
|
||||
avail = 0;
|
||||
if (!PeekNamedPipe(handle->handle, NULL, 0, NULL, &avail, NULL))
|
||||
uv__pipe_read_error_or_eof(loop, handle, GetLastError(), uv_null_buf_);
|
||||
|
||||
/* Read until we've either read all the bytes available, or the 'reading'
|
||||
* flag is cleared. */
|
||||
while (avail > 0 && handle->flags & UV_HANDLE_READING) {
|
||||
while (handle->flags & UV_HANDLE_READING) {
|
||||
bytes_requested = 65536;
|
||||
/* Depending on the type of pipe, read either IPC frames or raw data. */
|
||||
DWORD bytes_read =
|
||||
handle->ipc ? uv__pipe_read_ipc(loop, handle)
|
||||
: uv__pipe_read_data(loop, handle, avail, (DWORD) -1);
|
||||
if (handle->ipc)
|
||||
more = uv__pipe_read_ipc(loop, handle);
|
||||
else
|
||||
more = uv__pipe_read_data(loop, handle, &bytes_requested, INT32_MAX);
|
||||
|
||||
/* If no bytes were read, treat this as an indication that an error
|
||||
* occurred, and break out of the read loop. */
|
||||
if (bytes_read == 0)
|
||||
if (more == 0)
|
||||
break;
|
||||
|
||||
/* It is possible that more bytes were read than we thought were
|
||||
* available. To prevent `avail` from underflowing, break out of the loop
|
||||
* if this is the case. */
|
||||
if (bytes_read > avail)
|
||||
break;
|
||||
|
||||
/* Recompute the number of bytes available. */
|
||||
avail -= bytes_read;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
handle->write_queue_size -= req->u.io.queued_bytes;
|
||||
|
||||
UNREGISTER_HANDLE_REQ(loop, handle, req);
|
||||
UNREGISTER_HANDLE_REQ(loop, handle);
|
||||
|
||||
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
|
||||
if (req->wait_handle != INVALID_HANDLE_VALUE) {
|
||||
UnregisterWait(req->wait_handle);
|
||||
req->wait_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
if (req->event_handle) {
|
||||
CloseHandle(req->event_handle);
|
||||
req->event_handle = NULL;
|
||||
}
|
||||
if (req->wait_handle != INVALID_HANDLE_VALUE) {
|
||||
UnregisterWait(req->wait_handle);
|
||||
req->wait_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
if (req->event_handle) {
|
||||
CloseHandle(req->event_handle);
|
||||
req->event_handle = NULL;
|
||||
}
|
||||
|
||||
err = GET_REQ_ERROR(req);
|
||||
@ -2174,7 +2266,7 @@ void uv__process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle,
|
||||
|
||||
assert(handle->type == UV_NAMED_PIPE);
|
||||
|
||||
UNREGISTER_HANDLE_REQ(loop, handle, req);
|
||||
UNREGISTER_HANDLE_REQ(loop, handle);
|
||||
|
||||
err = 0;
|
||||
if (REQ_SUCCESS(req)) {
|
||||
@ -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. */
|
||||
handle->stream.conn.shutdown_req = NULL;
|
||||
UNREGISTER_HANDLE_REQ(loop, handle, req);
|
||||
UNREGISTER_HANDLE_REQ(loop, handle);
|
||||
|
||||
if (handle->flags & UV_HANDLE_CLOSING) {
|
||||
/* Already closing. Cancel the shutdown. */
|
||||
@ -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) {
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (handle->flags & UV_HANDLE_BOUND)
|
||||
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) {
|
||||
if (buffer == NULL || size == NULL || *size == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
/* emulate unix behaviour */
|
||||
if (handle->flags & UV_HANDLE_BOUND)
|
||||
return UV_ENOTCONN;
|
||||
|
@ -46,12 +46,12 @@
|
||||
#define CHILD_STDIO_CRT_FLAGS(buffer, fd) \
|
||||
*((unsigned char*) (buffer) + sizeof(int) + fd)
|
||||
|
||||
#define CHILD_STDIO_HANDLE(buffer, fd) \
|
||||
*((HANDLE*) ((unsigned char*) (buffer) + \
|
||||
sizeof(int) + \
|
||||
sizeof(unsigned char) * \
|
||||
CHILD_STDIO_COUNT((buffer)) + \
|
||||
sizeof(HANDLE) * (fd)))
|
||||
#define CHILD_STDIO_HANDLE(buffer, fd) \
|
||||
((void*) ((unsigned char*) (buffer) + \
|
||||
sizeof(int) + \
|
||||
sizeof(unsigned char) * \
|
||||
CHILD_STDIO_COUNT((buffer)) + \
|
||||
sizeof(HANDLE) * (fd)))
|
||||
|
||||
|
||||
/* CRT file descriptor mode flags */
|
||||
@ -194,7 +194,7 @@ int uv__stdio_create(uv_loop_t* loop,
|
||||
CHILD_STDIO_COUNT(buffer) = count;
|
||||
for (i = 0; i < count; i++) {
|
||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
|
||||
CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE;
|
||||
memset(CHILD_STDIO_HANDLE(buffer, i), 0xFF, sizeof(HANDLE));
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
@ -215,14 +215,15 @@ int uv__stdio_create(uv_loop_t* loop,
|
||||
* handles in the stdio buffer are initialized with.
|
||||
* INVALID_HANDLE_VALUE, which should be okay. */
|
||||
if (i <= 2) {
|
||||
HANDLE nul;
|
||||
DWORD access = (i == 0) ? FILE_GENERIC_READ :
|
||||
FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES;
|
||||
|
||||
err = uv__create_nul_handle(&CHILD_STDIO_HANDLE(buffer, i),
|
||||
access);
|
||||
err = uv__create_nul_handle(&nul, access);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
memcpy(CHILD_STDIO_HANDLE(buffer, i), &nul, sizeof(HANDLE));
|
||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FDEV;
|
||||
}
|
||||
break;
|
||||
@ -247,7 +248,7 @@ int uv__stdio_create(uv_loop_t* loop,
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
CHILD_STDIO_HANDLE(buffer, i) = child_pipe;
|
||||
memcpy(CHILD_STDIO_HANDLE(buffer, i), &child_pipe, sizeof(HANDLE));
|
||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE;
|
||||
break;
|
||||
}
|
||||
@ -263,7 +264,7 @@ int uv__stdio_create(uv_loop_t* loop,
|
||||
* error. */
|
||||
if (fdopt.data.fd <= 2 && err == ERROR_INVALID_HANDLE) {
|
||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = 0;
|
||||
CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE;
|
||||
memset(CHILD_STDIO_HANDLE(buffer, i), 0xFF, sizeof(HANDLE));
|
||||
break;
|
||||
}
|
||||
goto error;
|
||||
@ -298,7 +299,7 @@ int uv__stdio_create(uv_loop_t* loop,
|
||||
return -1;
|
||||
}
|
||||
|
||||
CHILD_STDIO_HANDLE(buffer, i) = child_handle;
|
||||
memcpy(CHILD_STDIO_HANDLE(buffer, i), &child_handle, sizeof(HANDLE));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -334,7 +335,7 @@ int uv__stdio_create(uv_loop_t* loop,
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
CHILD_STDIO_HANDLE(buffer, i) = child_handle;
|
||||
memcpy(CHILD_STDIO_HANDLE(buffer, i), &child_handle, sizeof(HANDLE));
|
||||
CHILD_STDIO_CRT_FLAGS(buffer, i) = crt_flags;
|
||||
break;
|
||||
}
|
||||
@ -359,7 +360,7 @@ void uv__stdio_destroy(BYTE* buffer) {
|
||||
|
||||
count = CHILD_STDIO_COUNT(buffer);
|
||||
for (i = 0; i < count; i++) {
|
||||
HANDLE handle = CHILD_STDIO_HANDLE(buffer, i);
|
||||
HANDLE handle = uv__stdio_handle(buffer, i);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(handle);
|
||||
}
|
||||
@ -374,7 +375,7 @@ void uv__stdio_noinherit(BYTE* buffer) {
|
||||
|
||||
count = CHILD_STDIO_COUNT(buffer);
|
||||
for (i = 0; i < count; i++) {
|
||||
HANDLE handle = CHILD_STDIO_HANDLE(buffer, i);
|
||||
HANDLE handle = uv__stdio_handle(buffer, i);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0);
|
||||
}
|
||||
@ -412,5 +413,7 @@ WORD uv__stdio_size(BYTE* buffer) {
|
||||
|
||||
|
||||
HANDLE uv__stdio_handle(BYTE* buffer, int fd) {
|
||||
return CHILD_STDIO_HANDLE(buffer, fd);
|
||||
HANDLE handle;
|
||||
memcpy(&handle, CHILD_STDIO_HANDLE(buffer, fd), sizeof(HANDLE));
|
||||
return handle;
|
||||
}
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include <signal.h>
|
||||
#include <limits.h>
|
||||
#include <wchar.h>
|
||||
#include <malloc.h> /* alloca */
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
@ -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,
|
||||
* then search all path directories.
|
||||
*
|
||||
* - If filename specified has *any* extension, search for the file with the
|
||||
* specified extension first.
|
||||
* - If filename specified has *any* extension, or already contains a path
|
||||
* 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*
|
||||
* (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,
|
||||
WCHAR *cwd,
|
||||
const WCHAR *path) {
|
||||
const WCHAR *path,
|
||||
unsigned int flags) {
|
||||
int file_has_dir;
|
||||
WCHAR* result = NULL;
|
||||
WCHAR *file_name_start;
|
||||
@ -372,16 +373,18 @@ static WCHAR* search_path(const WCHAR *file,
|
||||
file, file_name_start - file,
|
||||
file_name_start, file_len - (file_name_start - file),
|
||||
cwd, cwd_len,
|
||||
name_has_ext);
|
||||
name_has_ext || (flags & UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME));
|
||||
|
||||
} else {
|
||||
dir_end = path;
|
||||
|
||||
/* The file is really only a name; look in cwd first, then scan path */
|
||||
result = path_search_walk_ext(L"", 0,
|
||||
file, file_len,
|
||||
cwd, cwd_len,
|
||||
name_has_ext);
|
||||
if (NeedCurrentDirectoryForExePathW(L"")) {
|
||||
/* The file is really only a name; look in cwd first, then scan path */
|
||||
result = path_search_walk_ext(L"", 0,
|
||||
file, file_len,
|
||||
cwd, cwd_len,
|
||||
name_has_ext);
|
||||
}
|
||||
|
||||
while (result == NULL) {
|
||||
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';
|
||||
wcsrev(start);
|
||||
_wcsrev(start);
|
||||
*(target++) = L'"';
|
||||
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* b_eq;
|
||||
wchar_t* A;
|
||||
wchar_t* B;
|
||||
int nb;
|
||||
int r;
|
||||
|
||||
@ -613,27 +614,8 @@ int env_strncmp(const wchar_t* a, int na, const wchar_t* b) {
|
||||
assert(b_eq);
|
||||
nb = b_eq - b;
|
||||
|
||||
A = alloca((na+1) * sizeof(wchar_t));
|
||||
B = alloca((nb+1) * sizeof(wchar_t));
|
||||
|
||||
r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, a, na, A, na);
|
||||
assert(r==na);
|
||||
A[na] = L'\0';
|
||||
r = LCMapStringW(LOCALE_INVARIANT, LCMAP_UPPERCASE, b, nb, B, nb);
|
||||
assert(r==nb);
|
||||
B[nb] = L'\0';
|
||||
|
||||
for (;;) {
|
||||
wchar_t AA = *A++;
|
||||
wchar_t BB = *B++;
|
||||
if (AA < BB) {
|
||||
return -1;
|
||||
} else if (AA > BB) {
|
||||
return 1;
|
||||
} else if (!AA && !BB) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
r = CompareStringOrdinal(a, na, b, nb, /*case insensitive*/TRUE);
|
||||
return r - CSTR_EQUAL;
|
||||
}
|
||||
|
||||
|
||||
@ -672,6 +654,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) {
|
||||
WCHAR* dst_copy;
|
||||
WCHAR** ptr_copy;
|
||||
WCHAR** env_copy;
|
||||
char* p;
|
||||
size_t required_vars_value_len[ARRAY_SIZE(required_vars)];
|
||||
|
||||
/* first pass: determine size in UTF-16 */
|
||||
@ -687,11 +670,13 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) {
|
||||
}
|
||||
|
||||
/* second pass: copy to UTF-16 environment block */
|
||||
dst_copy = uv__malloc(env_len * sizeof(WCHAR));
|
||||
if (dst_copy == NULL && env_len > 0) {
|
||||
len = env_block_count * sizeof(WCHAR*);
|
||||
p = uv__malloc(len + env_len * sizeof(WCHAR));
|
||||
if (p == NULL) {
|
||||
return UV_ENOMEM;
|
||||
}
|
||||
env_copy = alloca(env_block_count * sizeof(WCHAR*));
|
||||
env_copy = (void*) &p[0];
|
||||
dst_copy = (void*) &p[len];
|
||||
|
||||
ptr = dst_copy;
|
||||
ptr_copy = env_copy;
|
||||
@ -741,7 +726,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) {
|
||||
/* final pass: copy, in sort order, and inserting required variables */
|
||||
dst = uv__malloc((1+env_len) * sizeof(WCHAR));
|
||||
if (!dst) {
|
||||
uv__free(dst_copy);
|
||||
uv__free(p);
|
||||
return UV_ENOMEM;
|
||||
}
|
||||
|
||||
@ -786,7 +771,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) {
|
||||
assert(env_len == (size_t) (ptr - dst));
|
||||
*ptr = L'\0';
|
||||
|
||||
uv__free(dst_copy);
|
||||
uv__free(p);
|
||||
*dst_ptr = dst;
|
||||
return 0;
|
||||
}
|
||||
@ -913,7 +898,7 @@ int uv_spawn(uv_loop_t* loop,
|
||||
*env = NULL, *cwd = NULL;
|
||||
STARTUPINFOW startup;
|
||||
PROCESS_INFORMATION info;
|
||||
DWORD process_flags;
|
||||
DWORD process_flags, cwd_len;
|
||||
BYTE* child_stdio_buffer;
|
||||
|
||||
uv__process_init(loop, process);
|
||||
@ -933,6 +918,7 @@ int uv_spawn(uv_loop_t* loop,
|
||||
assert(!(options->flags & ~(UV_PROCESS_DETACHED |
|
||||
UV_PROCESS_SETGID |
|
||||
UV_PROCESS_SETUID |
|
||||
UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME |
|
||||
UV_PROCESS_WINDOWS_HIDE |
|
||||
UV_PROCESS_WINDOWS_HIDE_CONSOLE |
|
||||
UV_PROCESS_WINDOWS_HIDE_GUI |
|
||||
@ -961,9 +947,10 @@ int uv_spawn(uv_loop_t* loop,
|
||||
if (err)
|
||||
goto done_uv;
|
||||
|
||||
cwd_len = wcslen(cwd);
|
||||
} else {
|
||||
/* Inherit cwd */
|
||||
DWORD cwd_len, r;
|
||||
DWORD r;
|
||||
|
||||
cwd_len = GetCurrentDirectoryW(0, NULL);
|
||||
if (!cwd_len) {
|
||||
@ -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. */
|
||||
path = find_path(env);
|
||||
if (path == NULL) {
|
||||
@ -1012,7 +1008,8 @@ int uv_spawn(uv_loop_t* loop,
|
||||
|
||||
application_path = search_path(application,
|
||||
cwd,
|
||||
path);
|
||||
path,
|
||||
options->flags);
|
||||
if (application_path == NULL) {
|
||||
/* Not found. */
|
||||
err = ERROR_FILE_NOT_FOUND;
|
||||
@ -1210,9 +1207,18 @@ static int uv__kill(HANDLE process_handle, int signum) {
|
||||
(PVOID) dump_folder,
|
||||
&dump_folder_len);
|
||||
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`. */
|
||||
WCHAR* localappdata;
|
||||
SHGetKnownFolderPath(&FOLDERID_LocalAppData, 0, NULL, &localappdata);
|
||||
SHGetKnownFolderPath(&FOLDERID_LocalAppData_libuv,
|
||||
0,
|
||||
NULL,
|
||||
&localappdata);
|
||||
_snwprintf_s(dump_folder,
|
||||
sizeof(dump_folder),
|
||||
_TRUNCATE,
|
||||
@ -1292,19 +1298,35 @@ static int uv__kill(HANDLE process_handle, int signum) {
|
||||
case SIGINT: {
|
||||
/* Unconditionally terminate the process. On Windows, killed processes
|
||||
* normally return 1. */
|
||||
DWORD status;
|
||||
int err;
|
||||
DWORD status;
|
||||
|
||||
if (TerminateProcess(process_handle, 1))
|
||||
return 0;
|
||||
|
||||
/* If the process already exited before TerminateProcess was called,.
|
||||
/* If the process already exited before TerminateProcess was called,
|
||||
* TerminateProcess will fail with ERROR_ACCESS_DENIED. */
|
||||
err = GetLastError();
|
||||
if (err == ERROR_ACCESS_DENIED &&
|
||||
GetExitCodeProcess(process_handle, &status) &&
|
||||
status != STILL_ACTIVE) {
|
||||
return UV_ESRCH;
|
||||
if (err == ERROR_ACCESS_DENIED) {
|
||||
/* First check using GetExitCodeProcess() with status different from
|
||||
* STILL_ACTIVE (259). This check can be set incorrectly by the process,
|
||||
* though that is uncommon. */
|
||||
if (GetExitCodeProcess(process_handle, &status) &&
|
||||
status != STILL_ACTIVE) {
|
||||
return UV_ESRCH;
|
||||
}
|
||||
|
||||
/* But the process could have exited with code == STILL_ACTIVE, use then
|
||||
* WaitForSingleObject with timeout zero. This is prone to a race
|
||||
* condition as it could return WAIT_TIMEOUT because the handle might
|
||||
* not have been signaled yet.That would result in returning the wrong
|
||||
* error code here (UV_EACCES instead of UV_ESRCH), but we cannot fix
|
||||
* the kernel synchronization issue that TerminateProcess is
|
||||
* inconsistent with WaitForSingleObject with just the APIs available to
|
||||
* us in user space. */
|
||||
if (WaitForSingleObject(process_handle, 0) == WAIT_OBJECT_0) {
|
||||
return UV_ESRCH;
|
||||
}
|
||||
}
|
||||
|
||||
return uv_translate_sys_error(err);
|
||||
@ -1320,7 +1342,16 @@ static int uv__kill(HANDLE process_handle, int signum) {
|
||||
if (status != STILL_ACTIVE)
|
||||
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:
|
||||
@ -1355,7 +1386,7 @@ int uv_kill(int pid, int signum) {
|
||||
if (pid == 0) {
|
||||
process_handle = GetCurrentProcess();
|
||||
} else {
|
||||
process_handle = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION,
|
||||
process_handle = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | SYNCHRONIZE,
|
||||
FALSE,
|
||||
pid);
|
||||
}
|
||||
|
@ -53,16 +53,16 @@
|
||||
(uv__ntstatus_to_winsock_error(GET_REQ_STATUS((req))))
|
||||
|
||||
|
||||
#define REGISTER_HANDLE_REQ(loop, handle, req) \
|
||||
#define REGISTER_HANDLE_REQ(loop, handle) \
|
||||
do { \
|
||||
INCREASE_ACTIVE_COUNT((loop), (handle)); \
|
||||
uv__req_register((loop), (req)); \
|
||||
uv__req_register((loop)); \
|
||||
} while (0)
|
||||
|
||||
#define UNREGISTER_HANDLE_REQ(loop, handle, req) \
|
||||
#define UNREGISTER_HANDLE_REQ(loop, handle) \
|
||||
do { \
|
||||
DECREASE_ACTIVE_COUNT((loop), (handle)); \
|
||||
uv__req_unregister((loop), (req)); \
|
||||
uv__req_unregister((loop)); \
|
||||
} while (0)
|
||||
|
||||
|
||||
@ -83,7 +83,7 @@
|
||||
|
||||
|
||||
INLINE static uv_req_t* uv__overlapped_to_req(OVERLAPPED* overlapped) {
|
||||
return CONTAINING_RECORD(overlapped, uv_req_t, u.io.overlapped);
|
||||
return container_of(overlapped, uv_req_t, u.io.overlapped);
|
||||
}
|
||||
|
||||
|
||||
|
@ -91,7 +91,7 @@ int uv__signal_dispatch(int signum) {
|
||||
|
||||
for (handle = RB_NFIND(uv_signal_tree_s, &uv__signal_tree, &lookup);
|
||||
handle != NULL && handle->signum == signum;
|
||||
handle = RB_NEXT(uv_signal_tree_s, &uv__signal_tree, handle)) {
|
||||
handle = RB_NEXT(uv_signal_tree_s, handle)) {
|
||||
unsigned long previous = InterlockedExchange(
|
||||
(volatile LONG*) &handle->pending_signum, signum);
|
||||
|
||||
|
@ -131,7 +131,7 @@ int uv_write(uv_write_t* req,
|
||||
case UV_NAMED_PIPE:
|
||||
err = uv__pipe_write(
|
||||
loop, req, (uv_pipe_t*) handle, bufs, nbufs, NULL, cb);
|
||||
break;
|
||||
return uv_translate_write_sys_error(err);
|
||||
case UV_TTY:
|
||||
err = uv__tty_write(loop, req, (uv_tty_t*) handle, bufs, nbufs, cb);
|
||||
break;
|
||||
@ -164,7 +164,7 @@ int uv_write2(uv_write_t* req,
|
||||
|
||||
err = uv__pipe_write(
|
||||
loop, req, (uv_pipe_t*) handle, bufs, nbufs, send_handle, cb);
|
||||
return uv_translate_sys_error(err);
|
||||
return uv_translate_write_sys_error(err);
|
||||
}
|
||||
|
||||
|
||||
@ -216,7 +216,7 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) {
|
||||
handle->flags &= ~UV_HANDLE_WRITABLE;
|
||||
handle->stream.conn.shutdown_req = req;
|
||||
handle->reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
|
||||
if (handle->stream.conn.write_reqs_pending == 0) {
|
||||
if (handle->type == UV_NAMED_PIPE)
|
||||
|
@ -58,11 +58,17 @@ static int uv__tcp_keepalive(uv_tcp_t* handle, SOCKET socket, int enable, unsign
|
||||
return WSAGetLastError();
|
||||
}
|
||||
|
||||
if (enable && setsockopt(socket,
|
||||
IPPROTO_TCP,
|
||||
TCP_KEEPALIVE,
|
||||
(const char*)&delay,
|
||||
sizeof delay) == -1) {
|
||||
if (!enable)
|
||||
return 0;
|
||||
|
||||
if (delay < 1)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (setsockopt(socket,
|
||||
IPPROTO_TCP,
|
||||
TCP_KEEPALIVE,
|
||||
(const char*)&delay,
|
||||
sizeof delay) == -1) {
|
||||
return WSAGetLastError();
|
||||
}
|
||||
|
||||
@ -206,7 +212,7 @@ void uv__process_tcp_shutdown_req(uv_loop_t* loop, uv_tcp_t* stream, uv_shutdown
|
||||
assert(stream->flags & UV_HANDLE_CONNECTION);
|
||||
|
||||
stream->stream.conn.shutdown_req = NULL;
|
||||
UNREGISTER_HANDLE_REQ(loop, stream, req);
|
||||
UNREGISTER_HANDLE_REQ(loop, stream);
|
||||
|
||||
err = 0;
|
||||
if (stream->flags & UV_HANDLE_CLOSING)
|
||||
@ -286,6 +292,12 @@ static int uv__tcp_try_bind(uv_tcp_t* handle,
|
||||
DWORD err;
|
||||
int r;
|
||||
|
||||
/* There is no SO_REUSEPORT on Windows, Windows only knows SO_REUSEADDR.
|
||||
* so we just return an error directly when UV_TCP_REUSEPORT is requested
|
||||
* for binding the socket. */
|
||||
if (flags & UV_TCP_REUSEPORT)
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
|
||||
if (handle->socket == INVALID_SOCKET) {
|
||||
SOCKET sock;
|
||||
|
||||
@ -822,7 +834,7 @@ out:
|
||||
if (handle->delayed_error != 0) {
|
||||
/* Process the req without IOCP. */
|
||||
handle->reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
uv__insert_pending_req(loop, (uv_req_t*)req);
|
||||
return 0;
|
||||
}
|
||||
@ -838,12 +850,12 @@ out:
|
||||
if (UV_SUCCEEDED_WITHOUT_IOCP(success)) {
|
||||
/* Process the req without IOCP. */
|
||||
handle->reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
uv__insert_pending_req(loop, (uv_req_t*)req);
|
||||
} else if (UV_SUCCEEDED_WITH_IOCP(success)) {
|
||||
/* The req will be processed with IOCP. */
|
||||
handle->reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
} else {
|
||||
return WSAGetLastError();
|
||||
}
|
||||
@ -913,14 +925,14 @@ int uv__tcp_write(uv_loop_t* loop,
|
||||
req->u.io.queued_bytes = 0;
|
||||
handle->reqs_pending++;
|
||||
handle->stream.conn.write_reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
uv__insert_pending_req(loop, (uv_req_t*) req);
|
||||
} else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
|
||||
/* Request queued by the kernel. */
|
||||
req->u.io.queued_bytes = uv__count_bufs(bufs, nbufs);
|
||||
handle->reqs_pending++;
|
||||
handle->stream.conn.write_reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
handle->write_queue_size += req->u.io.queued_bytes;
|
||||
if (handle->flags & UV_HANDLE_EMULATE_IOCP &&
|
||||
!RegisterWaitForSingleObject(&req->wait_handle,
|
||||
@ -934,7 +946,7 @@ int uv__tcp_write(uv_loop_t* loop,
|
||||
req->u.io.queued_bytes = 0;
|
||||
handle->reqs_pending++;
|
||||
handle->stream.conn.write_reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
SET_REQ_ERROR(req, WSAGetLastError());
|
||||
uv__insert_pending_req(loop, (uv_req_t*) req);
|
||||
}
|
||||
@ -1105,7 +1117,7 @@ void uv__process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle,
|
||||
assert(handle->write_queue_size >= req->u.io.queued_bytes);
|
||||
handle->write_queue_size -= req->u.io.queued_bytes;
|
||||
|
||||
UNREGISTER_HANDLE_REQ(loop, handle, req);
|
||||
UNREGISTER_HANDLE_REQ(loop, handle);
|
||||
|
||||
if (handle->flags & UV_HANDLE_EMULATE_IOCP) {
|
||||
if (req->wait_handle != INVALID_HANDLE_VALUE) {
|
||||
@ -1197,7 +1209,7 @@ void uv__process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle,
|
||||
|
||||
assert(handle->type == UV_TCP);
|
||||
|
||||
UNREGISTER_HANDLE_REQ(loop, handle, req);
|
||||
UNREGISTER_HANDLE_REQ(loop, handle);
|
||||
|
||||
err = 0;
|
||||
if (handle->delayed_error) {
|
||||
@ -1551,11 +1563,6 @@ int uv__tcp_connect(uv_connect_t* req,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef WSA_FLAG_NO_HANDLE_INHERIT
|
||||
/* Added in Windows 7 SP1. Specify this to avoid race conditions, */
|
||||
/* but also manually clear the inherit flag in case this failed. */
|
||||
#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
|
||||
#endif
|
||||
|
||||
int uv_socketpair(int type, int protocol, uv_os_sock_t fds[2], int flags0, int flags1) {
|
||||
SOCKET server = INVALID_SOCKET;
|
||||
|
142
src/win/thread.c
142
src/win/thread.c
@ -32,45 +32,23 @@
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
|
||||
static void uv__once_inner(uv_once_t* guard, void (*callback)(void)) {
|
||||
DWORD result;
|
||||
HANDLE existing_event, created_event;
|
||||
typedef void (*uv__once_cb)(void);
|
||||
|
||||
created_event = CreateEvent(NULL, 1, 0, NULL);
|
||||
if (created_event == 0) {
|
||||
/* Could fail in a low-memory situation? */
|
||||
uv_fatal_error(GetLastError(), "CreateEvent");
|
||||
}
|
||||
typedef struct {
|
||||
uv__once_cb callback;
|
||||
} uv__once_data_t;
|
||||
|
||||
existing_event = InterlockedCompareExchangePointer(&guard->event,
|
||||
created_event,
|
||||
NULL);
|
||||
static BOOL WINAPI uv__once_inner(INIT_ONCE *once, void* param, void** context) {
|
||||
uv__once_data_t* data = param;
|
||||
|
||||
if (existing_event == NULL) {
|
||||
/* We won the race */
|
||||
callback();
|
||||
data->callback();
|
||||
|
||||
result = SetEvent(created_event);
|
||||
assert(result);
|
||||
guard->ran = 1;
|
||||
|
||||
} else {
|
||||
/* We lost the race. Destroy the event we created and wait for the existing
|
||||
* one to become signaled. */
|
||||
CloseHandle(created_event);
|
||||
result = WaitForSingleObject(existing_event, INFINITE);
|
||||
assert(result == WAIT_OBJECT_0);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
void uv_once(uv_once_t* guard, void (*callback)(void)) {
|
||||
/* Fast case - avoid WaitForSingleObject. */
|
||||
if (guard->ran) {
|
||||
return;
|
||||
}
|
||||
|
||||
uv__once_inner(guard, callback);
|
||||
void uv_once(uv_once_t* guard, uv__once_cb callback) {
|
||||
uv__once_data_t data = { .callback = callback };
|
||||
InitOnceExecuteOnce(&guard->init_once, uv__once_inner, (void*) &data, NULL);
|
||||
}
|
||||
|
||||
|
||||
@ -79,6 +57,9 @@ STATIC_ASSERT(sizeof(uv_thread_t) <= sizeof(void*));
|
||||
|
||||
static uv_key_t uv__current_thread_key;
|
||||
static uv_once_t uv__current_thread_init_guard = UV_ONCE_INIT;
|
||||
static uv_once_t uv__thread_name_once = UV_ONCE_INIT;
|
||||
HRESULT (WINAPI *pGetThreadDescription)(HANDLE, PWSTR*);
|
||||
HRESULT (WINAPI *pSetThreadDescription)(HANDLE, PCWSTR);
|
||||
|
||||
|
||||
static void uv__init_current_thread_key(void) {
|
||||
@ -117,6 +98,15 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
|
||||
return uv_thread_create_ex(tid, ¶ms, entry, arg);
|
||||
}
|
||||
|
||||
|
||||
int uv_thread_detach(uv_thread_t *tid) {
|
||||
if (CloseHandle(*tid) == 0)
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_thread_create_ex(uv_thread_t* tid,
|
||||
const uv_thread_options_t* params,
|
||||
void (*entry)(void *arg),
|
||||
@ -291,6 +281,92 @@ int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) {
|
||||
}
|
||||
|
||||
|
||||
static void uv__thread_name_init_once(void) {
|
||||
HMODULE m;
|
||||
|
||||
m = GetModuleHandleA("api-ms-win-core-processthreads-l1-1-3.dll");
|
||||
if (m != NULL) {
|
||||
pGetThreadDescription = (void*) GetProcAddress(m, "GetThreadDescription");
|
||||
pSetThreadDescription = (void*) GetProcAddress(m, "SetThreadDescription");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int uv_thread_setname(const char* name) {
|
||||
HRESULT hr;
|
||||
WCHAR* namew;
|
||||
int err;
|
||||
char namebuf[UV_PTHREAD_MAX_NAMELEN_NP];
|
||||
|
||||
uv_once(&uv__thread_name_once, uv__thread_name_init_once);
|
||||
|
||||
if (pSetThreadDescription == NULL)
|
||||
return UV_ENOSYS;
|
||||
|
||||
if (name == NULL)
|
||||
return UV_EINVAL;
|
||||
|
||||
strncpy(namebuf, name, sizeof(namebuf) - 1);
|
||||
namebuf[sizeof(namebuf) - 1] = '\0';
|
||||
|
||||
namew = NULL;
|
||||
err = uv__convert_utf8_to_utf16(namebuf, &namew);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
hr = pSetThreadDescription(GetCurrentThread(), namew);
|
||||
uv__free(namew);
|
||||
if (FAILED(hr))
|
||||
return uv_translate_sys_error(HRESULT_CODE(hr));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_thread_getname(uv_thread_t* tid, char* name, size_t size) {
|
||||
HRESULT hr;
|
||||
WCHAR* namew;
|
||||
char* thread_name;
|
||||
size_t buf_size;
|
||||
int r;
|
||||
DWORD exit_code;
|
||||
|
||||
uv_once(&uv__thread_name_once, uv__thread_name_init_once);
|
||||
|
||||
if (pGetThreadDescription == NULL)
|
||||
return UV_ENOSYS;
|
||||
|
||||
if (name == NULL || size == 0)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (tid == NULL || *tid == NULL)
|
||||
return UV_EINVAL;
|
||||
|
||||
/* Check if the thread handle is valid */
|
||||
if (!GetExitCodeThread(*tid, &exit_code) || exit_code != STILL_ACTIVE)
|
||||
return UV_ENOENT;
|
||||
|
||||
namew = NULL;
|
||||
thread_name = NULL;
|
||||
hr = pGetThreadDescription(*tid, &namew);
|
||||
if (FAILED(hr))
|
||||
return uv_translate_sys_error(HRESULT_CODE(hr));
|
||||
|
||||
buf_size = size;
|
||||
r = uv__copy_utf16_to_utf8(namew, -1, name, &buf_size);
|
||||
if (r == UV_ENOBUFS) {
|
||||
r = uv__convert_utf16_to_utf8(namew, wcslen(namew), &thread_name);
|
||||
if (r == 0) {
|
||||
uv__strscpy(name, thread_name, size);
|
||||
uv__free(thread_name);
|
||||
}
|
||||
}
|
||||
|
||||
LocalFree(namew);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
int uv_mutex_init(uv_mutex_t* mutex) {
|
||||
InitializeCriticalSection(mutex);
|
||||
return 0;
|
||||
|
@ -58,6 +58,9 @@
|
||||
#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
|
||||
#endif
|
||||
#ifndef ENABLE_VIRTUAL_TERMINAL_INPUT
|
||||
#define ENABLE_VIRTUAL_TERMINAL_INPUT 0x0200
|
||||
#endif
|
||||
|
||||
#define CURSOR_SIZE_SMALL 25
|
||||
#define CURSOR_SIZE_LARGE 100
|
||||
@ -119,7 +122,10 @@ static int uv_tty_virtual_width = -1;
|
||||
* handle signalling SIGWINCH
|
||||
*/
|
||||
|
||||
static HANDLE uv__tty_console_handle = INVALID_HANDLE_VALUE;
|
||||
static HANDLE uv__tty_console_handle_out = INVALID_HANDLE_VALUE;
|
||||
static HANDLE uv__tty_console_handle_in = INVALID_HANDLE_VALUE;
|
||||
static DWORD uv__tty_console_in_original_mode = (DWORD)-1;
|
||||
static volatile LONG uv__tty_console_in_need_mode_reset = 0;
|
||||
static int uv__tty_console_height = -1;
|
||||
static int uv__tty_console_width = -1;
|
||||
static HANDLE uv__tty_console_resized = INVALID_HANDLE_VALUE;
|
||||
@ -159,19 +165,21 @@ static uv_tty_vtermstate_t uv__vterm_state = UV_TTY_UNSUPPORTED;
|
||||
static void uv__determine_vterm_state(HANDLE handle);
|
||||
|
||||
void uv__console_init(void) {
|
||||
DWORD dwMode;
|
||||
|
||||
if (uv_sem_init(&uv_tty_output_lock, 1))
|
||||
abort();
|
||||
uv__tty_console_handle = CreateFileW(L"CONOUT$",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_WRITE,
|
||||
0,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
0);
|
||||
if (uv__tty_console_handle != INVALID_HANDLE_VALUE) {
|
||||
uv__tty_console_handle_out = CreateFileW(L"CONOUT$",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_WRITE,
|
||||
0,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
0);
|
||||
if (uv__tty_console_handle_out != INVALID_HANDLE_VALUE) {
|
||||
CONSOLE_SCREEN_BUFFER_INFO sb_info;
|
||||
uv_mutex_init(&uv__tty_console_resize_mutex);
|
||||
if (GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info)) {
|
||||
if (GetConsoleScreenBufferInfo(uv__tty_console_handle_out, &sb_info)) {
|
||||
uv__tty_console_width = sb_info.dwSize.X;
|
||||
uv__tty_console_height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1;
|
||||
}
|
||||
@ -179,6 +187,18 @@ void uv__console_init(void) {
|
||||
NULL,
|
||||
WT_EXECUTELONGFUNCTION);
|
||||
}
|
||||
uv__tty_console_handle_in = CreateFileW(L"CONIN$",
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ,
|
||||
0,
|
||||
OPEN_EXISTING,
|
||||
0,
|
||||
0);
|
||||
if (uv__tty_console_handle_in != INVALID_HANDLE_VALUE) {
|
||||
if (GetConsoleMode(uv__tty_console_handle_in, &dwMode)) {
|
||||
uv__tty_console_in_original_mode = dwMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -253,7 +273,9 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int unused) {
|
||||
/* Initialize TTY input specific fields. */
|
||||
tty->flags |= UV_HANDLE_TTY_READABLE | UV_HANDLE_READABLE;
|
||||
/* TODO: remove me in v2.x. */
|
||||
tty->tty.rd.unused_ = NULL;
|
||||
tty->tty.rd.mode.unused_ = NULL;
|
||||
/* Partially overwrites unused_ again. */
|
||||
tty->tty.rd.mode.mode = 0;
|
||||
tty->tty.rd.read_line_buffer = uv_null_buf_;
|
||||
tty->tty.rd.read_raw_wait = NULL;
|
||||
|
||||
@ -344,6 +366,7 @@ static void uv__tty_capture_initial_style(
|
||||
|
||||
int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
|
||||
DWORD flags;
|
||||
DWORD try_set_flags;
|
||||
unsigned char was_reading;
|
||||
uv_alloc_cb alloc_cb;
|
||||
uv_read_cb read_cb;
|
||||
@ -353,14 +376,19 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
if (!!mode == !!(tty->flags & UV_HANDLE_TTY_RAW)) {
|
||||
if ((int)mode == tty->tty.rd.mode.mode) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
try_set_flags = 0;
|
||||
switch (mode) {
|
||||
case UV_TTY_MODE_NORMAL:
|
||||
flags = ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
|
||||
break;
|
||||
case UV_TTY_MODE_RAW_VT:
|
||||
try_set_flags = ENABLE_VIRTUAL_TERMINAL_INPUT;
|
||||
InterlockedExchange(&uv__tty_console_in_need_mode_reset, 1);
|
||||
/* fallthrough */
|
||||
case UV_TTY_MODE_RAW:
|
||||
flags = ENABLE_WINDOW_INPUT;
|
||||
break;
|
||||
@ -386,16 +414,16 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) {
|
||||
}
|
||||
|
||||
uv_sem_wait(&uv_tty_output_lock);
|
||||
if (!SetConsoleMode(tty->handle, flags)) {
|
||||
if (!SetConsoleMode(tty->handle, flags | try_set_flags) &&
|
||||
!SetConsoleMode(tty->handle, flags)) {
|
||||
err = uv_translate_sys_error(GetLastError());
|
||||
uv_sem_post(&uv_tty_output_lock);
|
||||
return err;
|
||||
}
|
||||
uv_sem_post(&uv_tty_output_lock);
|
||||
|
||||
/* Update flag. */
|
||||
tty->flags &= ~UV_HANDLE_TTY_RAW;
|
||||
tty->flags |= mode ? UV_HANDLE_TTY_RAW : 0;
|
||||
/* Update mode. */
|
||||
tty->tty.rd.mode.mode = mode;
|
||||
|
||||
/* If we just stopped reading, restart. */
|
||||
if (was_reading) {
|
||||
@ -614,7 +642,7 @@ static void uv__tty_queue_read_line(uv_loop_t* loop, uv_tty_t* handle) {
|
||||
|
||||
|
||||
static void uv__tty_queue_read(uv_loop_t* loop, uv_tty_t* handle) {
|
||||
if (handle->flags & UV_HANDLE_TTY_RAW) {
|
||||
if (uv__is_raw_tty_mode(handle->tty.rd.mode.mode)) {
|
||||
uv__tty_queue_read_raw(loop, handle);
|
||||
} else {
|
||||
uv__tty_queue_read_line(loop, handle);
|
||||
@ -695,14 +723,14 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle,
|
||||
|
||||
DWORD records_left, records_read;
|
||||
uv_buf_t buf;
|
||||
off_t buf_used;
|
||||
_off_t buf_used;
|
||||
|
||||
assert(handle->type == UV_TTY);
|
||||
assert(handle->flags & UV_HANDLE_TTY_READABLE);
|
||||
handle->flags &= ~UV_HANDLE_READ_PENDING;
|
||||
|
||||
if (!(handle->flags & UV_HANDLE_READING) ||
|
||||
!(handle->flags & UV_HANDLE_TTY_RAW)) {
|
||||
!(uv__is_raw_tty_mode(handle->tty.rd.mode.mode))) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -1050,7 +1078,7 @@ int uv__tty_read_stop(uv_tty_t* handle) {
|
||||
if (!(handle->flags & UV_HANDLE_READ_PENDING))
|
||||
return 0;
|
||||
|
||||
if (handle->flags & UV_HANDLE_TTY_RAW) {
|
||||
if (uv__is_raw_tty_mode(handle->tty.rd.mode.mode)) {
|
||||
/* Cancel raw read. Write some bullshit event to force the console wait to
|
||||
* return. */
|
||||
memset(&record, 0, sizeof record);
|
||||
@ -2183,7 +2211,7 @@ int uv__tty_write(uv_loop_t* loop,
|
||||
|
||||
handle->reqs_pending++;
|
||||
handle->stream.conn.write_reqs_pending++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
|
||||
req->u.io.queued_bytes = 0;
|
||||
|
||||
@ -2219,7 +2247,7 @@ void uv__process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
|
||||
int err;
|
||||
|
||||
handle->write_queue_size -= req->u.io.queued_bytes;
|
||||
UNREGISTER_HANDLE_REQ(loop, handle, req);
|
||||
UNREGISTER_HANDLE_REQ(loop, handle);
|
||||
|
||||
if (req->cb) {
|
||||
err = GET_REQ_ERROR(req);
|
||||
@ -2246,7 +2274,7 @@ void uv__tty_close(uv_tty_t* handle) {
|
||||
if (handle->u.fd == -1)
|
||||
CloseHandle(handle->handle);
|
||||
else
|
||||
close(handle->u.fd);
|
||||
_close(handle->u.fd);
|
||||
|
||||
handle->u.fd = -1;
|
||||
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);
|
||||
|
||||
stream->stream.conn.shutdown_req = NULL;
|
||||
UNREGISTER_HANDLE_REQ(loop, stream, req);
|
||||
UNREGISTER_HANDLE_REQ(loop, stream);
|
||||
|
||||
/* TTY shutdown is really just a no-op */
|
||||
if (req->cb) {
|
||||
@ -2293,7 +2321,17 @@ void uv__tty_endgame(uv_loop_t* loop, uv_tty_t* handle) {
|
||||
|
||||
|
||||
int uv_tty_reset_mode(void) {
|
||||
/* Not necessary to do anything. */
|
||||
/**
|
||||
* Shells on Windows do know to reset output flags after a program exits,
|
||||
* but not necessarily input flags, so we do that for them.
|
||||
*/
|
||||
if (
|
||||
uv__tty_console_handle_in != INVALID_HANDLE_VALUE &&
|
||||
uv__tty_console_in_original_mode != (DWORD)-1 &&
|
||||
InterlockedExchange(&uv__tty_console_in_need_mode_reset, 0) != 0
|
||||
) {
|
||||
SetConsoleMode(uv__tty_console_handle_in, uv__tty_console_in_original_mode);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2380,8 +2418,8 @@ static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param) {
|
||||
/* Make sure to not overwhelm the system with resize events */
|
||||
Sleep(33);
|
||||
WaitForSingleObject(uv__tty_console_resized, INFINITE);
|
||||
uv__tty_console_signal_resize();
|
||||
ResetEvent(uv__tty_console_resized);
|
||||
uv__tty_console_signal_resize();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -2390,7 +2428,7 @@ static void uv__tty_console_signal_resize(void) {
|
||||
CONSOLE_SCREEN_BUFFER_INFO sb_info;
|
||||
int width, height;
|
||||
|
||||
if (!GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info))
|
||||
if (!GetConsoleScreenBufferInfo(uv__tty_console_handle_out, &sb_info))
|
||||
return;
|
||||
|
||||
width = sb_info.dwSize.X;
|
||||
|
@ -200,6 +200,12 @@ static int uv__udp_maybe_bind(uv_udp_t* handle,
|
||||
if (handle->flags & UV_HANDLE_BOUND)
|
||||
return 0;
|
||||
|
||||
/* There is no SO_REUSEPORT on Windows, Windows only knows SO_REUSEADDR.
|
||||
* so we just return an error directly when UV_UDP_REUSEPORT is requested
|
||||
* for binding the socket. */
|
||||
if (flags & UV_UDP_REUSEPORT)
|
||||
return ERROR_NOT_SUPPORTED;
|
||||
|
||||
if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6) {
|
||||
/* UV_UDP_IPV6ONLY is supported only for IPV6 sockets */
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
@ -376,7 +382,7 @@ static int uv__send(uv_udp_send_t* req,
|
||||
handle->reqs_pending++;
|
||||
handle->send_queue_size += req->u.io.queued_bytes;
|
||||
handle->send_queue_count++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
uv__insert_pending_req(loop, (uv_req_t*)req);
|
||||
} else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) {
|
||||
/* Request queued by the kernel. */
|
||||
@ -384,7 +390,7 @@ static int uv__send(uv_udp_send_t* req,
|
||||
handle->reqs_pending++;
|
||||
handle->send_queue_size += req->u.io.queued_bytes;
|
||||
handle->send_queue_count++;
|
||||
REGISTER_HANDLE_REQ(loop, handle, req);
|
||||
REGISTER_HANDLE_REQ(loop, handle);
|
||||
} else {
|
||||
/* Send failed due to an error. */
|
||||
return WSAGetLastError();
|
||||
@ -527,7 +533,7 @@ void uv__process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle,
|
||||
handle->send_queue_size -= req->u.io.queued_bytes;
|
||||
handle->send_queue_count--;
|
||||
|
||||
UNREGISTER_HANDLE_REQ(loop, handle, req);
|
||||
UNREGISTER_HANDLE_REQ(loop, handle);
|
||||
|
||||
if (req->cb) {
|
||||
err = 0;
|
||||
@ -1095,7 +1101,8 @@ int uv__udp_try_send(uv_udp_t* handle,
|
||||
struct sockaddr_storage converted;
|
||||
int err;
|
||||
|
||||
assert(nbufs > 0);
|
||||
if (nbufs < 1)
|
||||
return UV_EINVAL;
|
||||
|
||||
if (addr != NULL) {
|
||||
err = uv__convert_to_localhost_if_unspecified(addr, &converted);
|
||||
@ -1135,3 +1142,21 @@ int uv__udp_try_send(uv_udp_t* handle,
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
int uv__udp_try_send2(uv_udp_t* handle,
|
||||
unsigned int count,
|
||||
uv_buf_t* bufs[/*count*/],
|
||||
unsigned int nbufs[/*count*/],
|
||||
struct sockaddr* addrs[/*count*/]) {
|
||||
unsigned int i;
|
||||
int r;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
r = uv_udp_try_send(handle, bufs[i], nbufs[i], addrs[i]);
|
||||
if (r < 0)
|
||||
return i > 0 ? i : r; /* Error if first packet, else send count. */
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
227
src/win/util.c
227
src/win/util.c
@ -191,7 +191,7 @@ int uv_cwd(char* buffer, size_t* size) {
|
||||
WCHAR *utf16_buffer;
|
||||
int r;
|
||||
|
||||
if (buffer == NULL || size == NULL) {
|
||||
if (buffer == NULL || size == NULL || *size == 0) {
|
||||
return UV_EINVAL;
|
||||
}
|
||||
|
||||
@ -316,25 +316,19 @@ uv_pid_t uv_os_getpid(void) {
|
||||
|
||||
|
||||
uv_pid_t uv_os_getppid(void) {
|
||||
int parent_pid = -1;
|
||||
HANDLE handle;
|
||||
PROCESSENTRY32 pe;
|
||||
DWORD current_pid = GetCurrentProcessId();
|
||||
NTSTATUS nt_status;
|
||||
PROCESS_BASIC_INFORMATION basic_info;
|
||||
|
||||
pe.dwSize = sizeof(PROCESSENTRY32);
|
||||
handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
|
||||
if (Process32First(handle, &pe)) {
|
||||
do {
|
||||
if (pe.th32ProcessID == current_pid) {
|
||||
parent_pid = pe.th32ParentProcessID;
|
||||
break;
|
||||
}
|
||||
} while( Process32Next(handle, &pe));
|
||||
nt_status = pNtQueryInformationProcess(GetCurrentProcess(),
|
||||
ProcessBasicInformation,
|
||||
&basic_info,
|
||||
sizeof(basic_info),
|
||||
NULL);
|
||||
if (NT_SUCCESS(nt_status)) {
|
||||
return basic_info.InheritedFromUniqueProcessId;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
CloseHandle(handle);
|
||||
return parent_pid;
|
||||
}
|
||||
|
||||
|
||||
@ -512,19 +506,23 @@ int uv_uptime(double* uptime) {
|
||||
|
||||
|
||||
unsigned int uv_available_parallelism(void) {
|
||||
SYSTEM_INFO info;
|
||||
unsigned rc;
|
||||
DWORD_PTR procmask;
|
||||
DWORD_PTR sysmask;
|
||||
int count;
|
||||
int i;
|
||||
|
||||
/* TODO(bnoordhuis) Use GetLogicalProcessorInformationEx() to support systems
|
||||
* with > 64 CPUs? See https://github.com/libuv/libuv/pull/3458
|
||||
*/
|
||||
GetSystemInfo(&info);
|
||||
count = 0;
|
||||
if (GetProcessAffinityMask(GetCurrentProcess(), &procmask, &sysmask))
|
||||
for (i = 0; i < 8 * sizeof(procmask); i++)
|
||||
count += 1 & (procmask >> i);
|
||||
|
||||
rc = info.dwNumberOfProcessors;
|
||||
if (rc < 1)
|
||||
rc = 1;
|
||||
if (count > 0)
|
||||
return count;
|
||||
|
||||
return rc;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@ -876,56 +874,100 @@ void uv_free_interface_addresses(uv_interface_address_t* addresses,
|
||||
|
||||
|
||||
int uv_getrusage(uv_rusage_t *uv_rusage) {
|
||||
FILETIME createTime, exitTime, kernelTime, userTime;
|
||||
SYSTEMTIME kernelSystemTime, userSystemTime;
|
||||
PROCESS_MEMORY_COUNTERS memCounters;
|
||||
IO_COUNTERS ioCounters;
|
||||
FILETIME create_time, exit_time, kernel_time, user_time;
|
||||
SYSTEMTIME kernel_system_time, user_system_time;
|
||||
PROCESS_MEMORY_COUNTERS mem_counters;
|
||||
IO_COUNTERS io_counters;
|
||||
int ret;
|
||||
|
||||
ret = GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime);
|
||||
ret = GetProcessTimes(GetCurrentProcess(),
|
||||
&create_time,
|
||||
&exit_time,
|
||||
&kernel_time,
|
||||
&user_time);
|
||||
if (ret == 0) {
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
}
|
||||
|
||||
ret = FileTimeToSystemTime(&kernelTime, &kernelSystemTime);
|
||||
ret = FileTimeToSystemTime(&kernel_time, &kernel_system_time);
|
||||
if (ret == 0) {
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
}
|
||||
|
||||
ret = FileTimeToSystemTime(&userTime, &userSystemTime);
|
||||
ret = FileTimeToSystemTime(&user_time, &user_system_time);
|
||||
if (ret == 0) {
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
}
|
||||
|
||||
ret = GetProcessMemoryInfo(GetCurrentProcess(),
|
||||
&memCounters,
|
||||
sizeof(memCounters));
|
||||
&mem_counters,
|
||||
sizeof(mem_counters));
|
||||
if (ret == 0) {
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
}
|
||||
|
||||
ret = GetProcessIoCounters(GetCurrentProcess(), &ioCounters);
|
||||
ret = GetProcessIoCounters(GetCurrentProcess(), &io_counters);
|
||||
if (ret == 0) {
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
}
|
||||
|
||||
memset(uv_rusage, 0, sizeof(*uv_rusage));
|
||||
|
||||
uv_rusage->ru_utime.tv_sec = userSystemTime.wHour * 3600 +
|
||||
userSystemTime.wMinute * 60 +
|
||||
userSystemTime.wSecond;
|
||||
uv_rusage->ru_utime.tv_usec = userSystemTime.wMilliseconds * 1000;
|
||||
uv_rusage->ru_utime.tv_sec = user_system_time.wHour * 3600 +
|
||||
user_system_time.wMinute * 60 +
|
||||
user_system_time.wSecond;
|
||||
uv_rusage->ru_utime.tv_usec = user_system_time.wMilliseconds * 1000;
|
||||
|
||||
uv_rusage->ru_stime.tv_sec = kernelSystemTime.wHour * 3600 +
|
||||
kernelSystemTime.wMinute * 60 +
|
||||
kernelSystemTime.wSecond;
|
||||
uv_rusage->ru_stime.tv_usec = kernelSystemTime.wMilliseconds * 1000;
|
||||
uv_rusage->ru_stime.tv_sec = kernel_system_time.wHour * 3600 +
|
||||
kernel_system_time.wMinute * 60 +
|
||||
kernel_system_time.wSecond;
|
||||
uv_rusage->ru_stime.tv_usec = kernel_system_time.wMilliseconds * 1000;
|
||||
|
||||
uv_rusage->ru_majflt = (uint64_t) memCounters.PageFaultCount;
|
||||
uv_rusage->ru_maxrss = (uint64_t) memCounters.PeakWorkingSetSize / 1024;
|
||||
uv_rusage->ru_majflt = (uint64_t) mem_counters.PageFaultCount;
|
||||
uv_rusage->ru_maxrss = (uint64_t) mem_counters.PeakWorkingSetSize / 1024;
|
||||
|
||||
uv_rusage->ru_oublock = (uint64_t) ioCounters.WriteOperationCount;
|
||||
uv_rusage->ru_inblock = (uint64_t) ioCounters.ReadOperationCount;
|
||||
uv_rusage->ru_oublock = (uint64_t) io_counters.WriteOperationCount;
|
||||
uv_rusage->ru_inblock = (uint64_t) io_counters.ReadOperationCount;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int uv_getrusage_thread(uv_rusage_t* uv_rusage) {
|
||||
FILETIME create_time, exit_time, kernel_time, user_time;
|
||||
SYSTEMTIME kernel_system_time, user_system_time;
|
||||
int ret;
|
||||
|
||||
ret = GetThreadTimes(GetCurrentThread(),
|
||||
&create_time,
|
||||
&exit_time,
|
||||
&kernel_time,
|
||||
&user_time);
|
||||
if (ret == 0) {
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
}
|
||||
|
||||
ret = FileTimeToSystemTime(&kernel_time, &kernel_system_time);
|
||||
if (ret == 0) {
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
}
|
||||
|
||||
ret = FileTimeToSystemTime(&user_time, &user_system_time);
|
||||
if (ret == 0) {
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
}
|
||||
|
||||
memset(uv_rusage, 0, sizeof(*uv_rusage));
|
||||
|
||||
uv_rusage->ru_utime.tv_sec = user_system_time.wHour * 3600 +
|
||||
user_system_time.wMinute * 60 +
|
||||
user_system_time.wSecond;
|
||||
uv_rusage->ru_utime.tv_usec = user_system_time.wMilliseconds * 1000;
|
||||
|
||||
uv_rusage->ru_stime.tv_sec = kernel_system_time.wHour * 3600 +
|
||||
kernel_system_time.wMinute * 60 +
|
||||
kernel_system_time.wSecond;
|
||||
uv_rusage->ru_stime.tv_usec = kernel_system_time.wMilliseconds * 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -942,8 +984,13 @@ int uv_os_homedir(char* buffer, size_t* size) {
|
||||
r = uv_os_getenv("USERPROFILE", buffer, size);
|
||||
|
||||
/* Don't return an error if USERPROFILE was not found. */
|
||||
if (r != UV_ENOENT)
|
||||
if (r != UV_ENOENT) {
|
||||
/* USERPROFILE is empty or invalid */
|
||||
if (r == 0 && *size < 3) {
|
||||
return UV_ENOENT;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/* USERPROFILE is not set, so call uv_os_get_passwd() */
|
||||
r = uv_os_get_passwd(&pwd);
|
||||
@ -969,6 +1016,7 @@ int uv_os_homedir(char* buffer, size_t* size) {
|
||||
|
||||
|
||||
int uv_os_tmpdir(char* buffer, size_t* size) {
|
||||
int r;
|
||||
wchar_t *path;
|
||||
size_t len;
|
||||
|
||||
@ -980,6 +1028,12 @@ int uv_os_tmpdir(char* buffer, size_t* size) {
|
||||
if (len == 0) {
|
||||
return uv_translate_sys_error(GetLastError());
|
||||
}
|
||||
|
||||
/* tmp path is empty or invalid */
|
||||
if (len < 3) {
|
||||
return UV_ENOENT;
|
||||
}
|
||||
|
||||
/* Include space for terminating null char. */
|
||||
len += 1;
|
||||
path = uv__malloc(len * sizeof(wchar_t));
|
||||
@ -1001,7 +1055,9 @@ int uv_os_tmpdir(char* buffer, size_t* size) {
|
||||
path[len] = L'\0';
|
||||
}
|
||||
|
||||
return uv__copy_utf16_to_utf8(path, len, buffer, size);
|
||||
r = uv__copy_utf16_to_utf8(path, len, buffer, size);
|
||||
uv__free(path);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
@ -1259,6 +1315,9 @@ int uv_os_getenv(const char* name, char* buffer, size_t* size) {
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
len = GetEnvironmentVariableW(name_w, var, varlen);
|
||||
|
||||
if (len == 0)
|
||||
r = uv_translate_sys_error(GetLastError());
|
||||
|
||||
if (len < varlen)
|
||||
break;
|
||||
|
||||
@ -1280,15 +1339,8 @@ int uv_os_getenv(const char* name, char* buffer, size_t* size) {
|
||||
uv__free(name_w);
|
||||
name_w = NULL;
|
||||
|
||||
if (len == 0) {
|
||||
r = GetLastError();
|
||||
if (r != ERROR_SUCCESS) {
|
||||
r = uv_translate_sys_error(r);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
r = uv__copy_utf16_to_utf8(var, len, buffer, size);
|
||||
if (r == 0)
|
||||
r = uv__copy_utf16_to_utf8(var, len, buffer, size);
|
||||
|
||||
fail:
|
||||
|
||||
@ -1466,6 +1518,48 @@ int uv_os_setpriority(uv_pid_t pid, int priority) {
|
||||
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) {
|
||||
/* Implementation loosely based on
|
||||
@ -1486,20 +1580,7 @@ int uv_os_uname(uv_utsname_t* buffer) {
|
||||
os_info.dwOSVersionInfoSize = sizeof(os_info);
|
||||
os_info.szCSDVersion[0] = L'\0';
|
||||
|
||||
/* Try calling RtlGetVersion(), and fall back to the deprecated GetVersionEx()
|
||||
if RtlGetVersion() is not available. */
|
||||
if (pRtlGetVersion) {
|
||||
pRtlGetVersion(&os_info);
|
||||
} else {
|
||||
/* Silence GetVersionEx() deprecation warning. */
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(suppress : 4996)
|
||||
#endif
|
||||
if (GetVersionExW(&os_info) == 0) {
|
||||
r = uv_translate_sys_error(GetLastError());
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
pRtlGetVersion(&os_info);
|
||||
|
||||
/* Populate the version field. */
|
||||
version_size = 0;
|
||||
@ -1555,7 +1636,7 @@ int uv_os_uname(uv_utsname_t* buffer) {
|
||||
version_size = sizeof(buffer->version) - version_size;
|
||||
r = uv__copy_utf16_to_utf8(os_info.szCSDVersion,
|
||||
-1,
|
||||
buffer->version +
|
||||
buffer->version +
|
||||
sizeof(buffer->version) - version_size,
|
||||
&version_size);
|
||||
if (r)
|
||||
|
@ -36,9 +36,6 @@ sNtQueryDirectoryFile pNtQueryDirectoryFile;
|
||||
sNtQuerySystemInformation pNtQuerySystemInformation;
|
||||
sNtQueryInformationProcess pNtQueryInformationProcess;
|
||||
|
||||
/* Kernel32 function pointers */
|
||||
sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
|
||||
|
||||
/* Powrprof.dll function pointer */
|
||||
sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification;
|
||||
|
||||
@ -48,12 +45,15 @@ sSetWinEventHook pSetWinEventHook;
|
||||
/* ws2_32.dll function pointer */
|
||||
uv_sGetHostNameW pGetHostNameW;
|
||||
|
||||
/* api-ms-win-core-file-l2-1-4.dll function pointer */
|
||||
sGetFileInformationByName pGetFileInformationByName;
|
||||
|
||||
void uv__winapi_init(void) {
|
||||
HMODULE ntdll_module;
|
||||
HMODULE powrprof_module;
|
||||
HMODULE user32_module;
|
||||
HMODULE kernel32_module;
|
||||
HMODULE ws2_32_module;
|
||||
HMODULE api_win_core_file_module;
|
||||
|
||||
ntdll_module = GetModuleHandleA("ntdll.dll");
|
||||
if (ntdll_module == NULL) {
|
||||
@ -99,7 +99,7 @@ void uv__winapi_init(void) {
|
||||
|
||||
pNtQueryDirectoryFile = (sNtQueryDirectoryFile)
|
||||
GetProcAddress(ntdll_module, "NtQueryDirectoryFile");
|
||||
if (pNtQueryVolumeInformationFile == NULL) {
|
||||
if (pNtQueryDirectoryFile == NULL) {
|
||||
uv_fatal_error(GetLastError(), "GetProcAddress");
|
||||
}
|
||||
|
||||
@ -117,15 +117,6 @@ void uv__winapi_init(void) {
|
||||
uv_fatal_error(GetLastError(), "GetProcAddress");
|
||||
}
|
||||
|
||||
kernel32_module = GetModuleHandleA("kernel32.dll");
|
||||
if (kernel32_module == NULL) {
|
||||
uv_fatal_error(GetLastError(), "GetModuleHandleA");
|
||||
}
|
||||
|
||||
pGetQueuedCompletionStatusEx = (sGetQueuedCompletionStatusEx) GetProcAddress(
|
||||
kernel32_module,
|
||||
"GetQueuedCompletionStatusEx");
|
||||
|
||||
powrprof_module = LoadLibraryExA("powrprof.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
if (powrprof_module != NULL) {
|
||||
pPowerRegisterSuspendResumeNotification = (sPowerRegisterSuspendResumeNotification)
|
||||
@ -144,4 +135,10 @@ void uv__winapi_init(void) {
|
||||
ws2_32_module,
|
||||
"GetHostNameW");
|
||||
}
|
||||
|
||||
api_win_core_file_module = GetModuleHandleA("api-ms-win-core-file-l2-1-4.dll");
|
||||
if (api_win_core_file_module != NULL) {
|
||||
pGetFileInformationByName = (sGetFileInformationByName)GetProcAddress(
|
||||
api_win_core_file_module, "GetFileInformationByName");
|
||||
}
|
||||
}
|
||||
|
168
src/win/winapi.h
168
src/win/winapi.h
@ -4125,41 +4125,61 @@ typedef const UNICODE_STRING *PCUNICODE_STRING;
|
||||
# define DEVICE_TYPE DWORD
|
||||
#endif
|
||||
|
||||
/* MinGW already has a definition for REPARSE_DATA_BUFFER, but mingw-w64 does
|
||||
* not.
|
||||
*/
|
||||
#if defined(_MSC_VER) || defined(__MINGW64_VERSION_MAJOR)
|
||||
typedef struct _REPARSE_DATA_BUFFER {
|
||||
ULONG ReparseTag;
|
||||
USHORT ReparseDataLength;
|
||||
USHORT Reserved;
|
||||
union {
|
||||
struct {
|
||||
USHORT SubstituteNameOffset;
|
||||
USHORT SubstituteNameLength;
|
||||
USHORT PrintNameOffset;
|
||||
USHORT PrintNameLength;
|
||||
ULONG Flags;
|
||||
WCHAR PathBuffer[1];
|
||||
} SymbolicLinkReparseBuffer;
|
||||
struct {
|
||||
USHORT SubstituteNameOffset;
|
||||
USHORT SubstituteNameLength;
|
||||
USHORT PrintNameOffset;
|
||||
USHORT PrintNameLength;
|
||||
WCHAR PathBuffer[1];
|
||||
} MountPointReparseBuffer;
|
||||
struct {
|
||||
UCHAR DataBuffer[1];
|
||||
} GenericReparseBuffer;
|
||||
struct {
|
||||
ULONG StringCount;
|
||||
WCHAR StringList[1];
|
||||
} AppExecLinkReparseBuffer;
|
||||
};
|
||||
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
|
||||
#ifndef NTDDI_WIN11_ZN
|
||||
# define NTDDI_WIN11_ZN 0x0A00000E
|
||||
#endif
|
||||
|
||||
/* API is defined in newer SDKS */
|
||||
#if (NTDDI_VERSION < NTDDI_WIN11_ZN)
|
||||
typedef struct _FILE_STAT_BASIC_INFORMATION {
|
||||
LARGE_INTEGER FileId;
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
LARGE_INTEGER AllocationSize;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
ULONG FileAttributes;
|
||||
ULONG ReparseTag;
|
||||
ULONG NumberOfLinks;
|
||||
ULONG DeviceType;
|
||||
ULONG DeviceCharacteristics;
|
||||
ULONG Reserved;
|
||||
LARGE_INTEGER VolumeSerialNumber;
|
||||
FILE_ID_128 FileId128;
|
||||
} FILE_STAT_BASIC_INFORMATION;
|
||||
#endif
|
||||
|
||||
typedef struct _REPARSE_DATA_BUFFER {
|
||||
ULONG ReparseTag;
|
||||
USHORT ReparseDataLength;
|
||||
USHORT Reserved;
|
||||
union {
|
||||
struct {
|
||||
USHORT SubstituteNameOffset;
|
||||
USHORT SubstituteNameLength;
|
||||
USHORT PrintNameOffset;
|
||||
USHORT PrintNameLength;
|
||||
ULONG Flags;
|
||||
WCHAR PathBuffer[1];
|
||||
} SymbolicLinkReparseBuffer;
|
||||
struct {
|
||||
USHORT SubstituteNameOffset;
|
||||
USHORT SubstituteNameLength;
|
||||
USHORT PrintNameOffset;
|
||||
USHORT PrintNameLength;
|
||||
WCHAR PathBuffer[1];
|
||||
} MountPointReparseBuffer;
|
||||
struct {
|
||||
UCHAR DataBuffer[1];
|
||||
} GenericReparseBuffer;
|
||||
struct {
|
||||
ULONG StringCount;
|
||||
WCHAR StringList[1];
|
||||
} AppExecLinkReparseBuffer;
|
||||
};
|
||||
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
|
||||
|
||||
typedef struct _IO_STATUS_BLOCK {
|
||||
union {
|
||||
NTSTATUS Status;
|
||||
@ -4224,6 +4244,15 @@ typedef enum _FILE_INFORMATION_CLASS {
|
||||
FileNumaNodeInformation,
|
||||
FileStandardLinkInformation,
|
||||
FileRemoteProtocolInformation,
|
||||
FileRenameInformationBypassAccessCheck,
|
||||
FileLinkInformationBypassAccessCheck,
|
||||
FileVolumeNameInformation,
|
||||
FileIdInformation,
|
||||
FileIdExtdDirectoryInformation,
|
||||
FileReplaceCompletionInformation,
|
||||
FileHardLinkFullIdInformation,
|
||||
FileIdExtdBothDirectoryInformation,
|
||||
FileDispositionInformationEx, /* based on https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ne-wdm-_file_information_class */
|
||||
FileMaximumInformation
|
||||
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
|
||||
|
||||
@ -4258,6 +4287,22 @@ typedef struct _FILE_BOTH_DIR_INFORMATION {
|
||||
WCHAR FileName[1];
|
||||
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;
|
||||
|
||||
typedef struct _FILE_ID_FULL_DIR_INFORMATION {
|
||||
ULONG NextEntryOffset;
|
||||
ULONG FileIndex;
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
LARGE_INTEGER LastWriteTime;
|
||||
LARGE_INTEGER ChangeTime;
|
||||
LARGE_INTEGER EndOfFile;
|
||||
LARGE_INTEGER AllocationSize;
|
||||
ULONG FileAttributes;
|
||||
ULONG FileNameLength;
|
||||
ULONG EaSize;
|
||||
LARGE_INTEGER FileId;
|
||||
WCHAR FileName[1];
|
||||
} FILE_ID_FULL_DIR_INFORMATION, *PFILE_ID_FULL_DIR_INFORMATION;
|
||||
|
||||
typedef struct _FILE_BASIC_INFORMATION {
|
||||
LARGE_INTEGER CreationTime;
|
||||
LARGE_INTEGER LastAccessTime;
|
||||
@ -4323,6 +4368,10 @@ typedef struct _FILE_DISPOSITION_INFORMATION {
|
||||
BOOLEAN DeleteFile;
|
||||
} FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION;
|
||||
|
||||
typedef struct _FILE_DISPOSITION_INFORMATION_EX {
|
||||
DWORD Flags;
|
||||
} FILE_DISPOSITION_INFORMATION_EX, *PFILE_DISPOSITION_INFORMATION_EX;
|
||||
|
||||
typedef struct _FILE_PIPE_LOCAL_INFORMATION {
|
||||
ULONG NamedPipeType;
|
||||
ULONG NamedPipeConfiguration;
|
||||
@ -4427,6 +4476,14 @@ typedef struct _FILE_FS_SECTOR_SIZE_INFORMATION {
|
||||
ULONG ByteOffsetForPartitionAlignment;
|
||||
} FILE_FS_SECTOR_SIZE_INFORMATION, *PFILE_FS_SECTOR_SIZE_INFORMATION;
|
||||
|
||||
typedef struct _PROCESS_BASIC_INFORMATION {
|
||||
PVOID Reserved1;
|
||||
PVOID PebBaseAddress;
|
||||
PVOID Reserved2[2];
|
||||
ULONG_PTR UniqueProcessId;
|
||||
ULONG_PTR InheritedFromUniqueProcessId;
|
||||
} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION;
|
||||
|
||||
typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION {
|
||||
LARGE_INTEGER IdleTime;
|
||||
LARGE_INTEGER KernelTime;
|
||||
@ -4440,6 +4497,10 @@ typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION {
|
||||
# define SystemProcessorPerformanceInformation 8
|
||||
#endif
|
||||
|
||||
#ifndef ProcessBasicInformation
|
||||
# define ProcessBasicInformation 0
|
||||
#endif
|
||||
|
||||
#ifndef ProcessConsoleHostProcess
|
||||
# define ProcessConsoleHostProcess 49
|
||||
#endif
|
||||
@ -4611,15 +4672,6 @@ typedef NTSTATUS (NTAPI *sNtQueryInformationProcess)
|
||||
# define SYMBOLIC_LINK_FLAG_DIRECTORY 0x1
|
||||
#endif
|
||||
|
||||
#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
|
||||
typedef struct _OVERLAPPED_ENTRY {
|
||||
ULONG_PTR lpCompletionKey;
|
||||
LPOVERLAPPED lpOverlapped;
|
||||
ULONG_PTR Internal;
|
||||
DWORD dwNumberOfBytesTransferred;
|
||||
} OVERLAPPED_ENTRY, *LPOVERLAPPED_ENTRY;
|
||||
#endif
|
||||
|
||||
/* from wincon.h */
|
||||
#ifndef ENABLE_INSERT_MODE
|
||||
# define ENABLE_INSERT_MODE 0x20
|
||||
@ -4666,14 +4718,6 @@ typedef NTSTATUS (NTAPI *sNtQueryInformationProcess)
|
||||
# define ERROR_MUI_FILE_NOT_LOADED 15105
|
||||
#endif
|
||||
|
||||
typedef BOOL (WINAPI *sGetQueuedCompletionStatusEx)
|
||||
(HANDLE CompletionPort,
|
||||
LPOVERLAPPED_ENTRY lpCompletionPortEntries,
|
||||
ULONG ulCount,
|
||||
PULONG ulNumEntriesRemoved,
|
||||
DWORD dwMilliseconds,
|
||||
BOOL fAlertable);
|
||||
|
||||
/* from powerbase.h */
|
||||
#ifndef DEVICE_NOTIFY_CALLBACK
|
||||
# define DEVICE_NOTIFY_CALLBACK 2
|
||||
@ -4739,6 +4783,24 @@ typedef struct _TCP_INITIAL_RTO_PARAMETERS {
|
||||
# define SIO_TCP_INITIAL_RTO _WSAIOW(IOC_VENDOR,17)
|
||||
#endif
|
||||
|
||||
/* from winnt.h */
|
||||
/* API is defined in newer SDKS */
|
||||
#if (NTDDI_VERSION < NTDDI_WIN11_ZN)
|
||||
typedef enum _FILE_INFO_BY_NAME_CLASS {
|
||||
FileStatByNameInfo,
|
||||
FileStatLxByNameInfo,
|
||||
FileCaseSensitiveByNameInfo,
|
||||
FileStatBasicByNameInfo,
|
||||
MaximumFileInfoByNameClass
|
||||
} FILE_INFO_BY_NAME_CLASS;
|
||||
#endif
|
||||
|
||||
typedef BOOL(WINAPI* sGetFileInformationByName)(
|
||||
PCWSTR FileName,
|
||||
FILE_INFO_BY_NAME_CLASS FileInformationClass,
|
||||
PVOID FileInfoBuffer,
|
||||
ULONG FileInfoBufferSize);
|
||||
|
||||
/* Ntdll function pointers */
|
||||
extern sRtlGetVersion pRtlGetVersion;
|
||||
extern sRtlNtStatusToDosError pRtlNtStatusToDosError;
|
||||
@ -4750,15 +4812,15 @@ extern sNtQueryDirectoryFile pNtQueryDirectoryFile;
|
||||
extern sNtQuerySystemInformation pNtQuerySystemInformation;
|
||||
extern sNtQueryInformationProcess pNtQueryInformationProcess;
|
||||
|
||||
/* Kernel32 function pointers */
|
||||
extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx;
|
||||
|
||||
/* Powrprof.dll function pointer */
|
||||
extern sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification;
|
||||
|
||||
/* User32.dll function pointer */
|
||||
extern sSetWinEventHook pSetWinEventHook;
|
||||
|
||||
/* api-ms-win-core-file-l2-1-4.dll function pointers */
|
||||
extern sGetFileInformationByName pGetFileInformationByName;
|
||||
|
||||
/* ws2_32.dll function pointer */
|
||||
/* mingw doesn't have this definition, so let's declare it here locally */
|
||||
typedef int (WINAPI *uv_sGetHostNameW)
|
||||
|
@ -154,47 +154,6 @@ typedef struct _AFD_RECV_INFO {
|
||||
#define IOCTL_AFD_POLL \
|
||||
_AFD_CONTROL_CODE(AFD_POLL, METHOD_BUFFERED)
|
||||
|
||||
#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
|
||||
typedef struct _IP_ADAPTER_UNICAST_ADDRESS_XP {
|
||||
/* FIXME: __C89_NAMELESS was removed */
|
||||
/* __C89_NAMELESS */ union {
|
||||
ULONGLONG Alignment;
|
||||
/* __C89_NAMELESS */ struct {
|
||||
ULONG Length;
|
||||
DWORD Flags;
|
||||
};
|
||||
};
|
||||
struct _IP_ADAPTER_UNICAST_ADDRESS_XP *Next;
|
||||
SOCKET_ADDRESS Address;
|
||||
IP_PREFIX_ORIGIN PrefixOrigin;
|
||||
IP_SUFFIX_ORIGIN SuffixOrigin;
|
||||
IP_DAD_STATE DadState;
|
||||
ULONG ValidLifetime;
|
||||
ULONG PreferredLifetime;
|
||||
ULONG LeaseLifetime;
|
||||
} IP_ADAPTER_UNICAST_ADDRESS_XP,*PIP_ADAPTER_UNICAST_ADDRESS_XP;
|
||||
|
||||
typedef struct _IP_ADAPTER_UNICAST_ADDRESS_LH {
|
||||
union {
|
||||
ULONGLONG Alignment;
|
||||
struct {
|
||||
ULONG Length;
|
||||
DWORD Flags;
|
||||
};
|
||||
};
|
||||
struct _IP_ADAPTER_UNICAST_ADDRESS_LH *Next;
|
||||
SOCKET_ADDRESS Address;
|
||||
IP_PREFIX_ORIGIN PrefixOrigin;
|
||||
IP_SUFFIX_ORIGIN SuffixOrigin;
|
||||
IP_DAD_STATE DadState;
|
||||
ULONG ValidLifetime;
|
||||
ULONG PreferredLifetime;
|
||||
ULONG LeaseLifetime;
|
||||
UINT8 OnLinkPrefixLength;
|
||||
} IP_ADAPTER_UNICAST_ADDRESS_LH,*PIP_ADAPTER_UNICAST_ADDRESS_LH;
|
||||
|
||||
#endif
|
||||
|
||||
int uv__convert_to_localhost_if_unspecified(const struct sockaddr* addr,
|
||||
struct sockaddr_storage* storage);
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user