diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..700707ced32 --- /dev/null +++ b/.github/dependabot.yml @@ -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" diff --git a/.github/stale.yml b/.github/stale.yml deleted file mode 100644 index 6f2fb663c62..00000000000 --- a/.github/stale.yml +++ /dev/null @@ -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 diff --git a/.github/workflows/CI-docs.yml b/.github/workflows/CI-docs.yml new file mode 100644 index 00000000000..f414448814c --- /dev/null +++ b/.github/workflows/CI-docs.yml @@ -0,0 +1,25 @@ +name: CI-docs + +on: + pull_request: + paths: + - 'docs/**' + - '!docs/code/**' + - '.github/workflows/CI-docs.yml' + +jobs: + docs-src: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.9' + cache: 'pip' # caching pip dependencies + - run: pip install -r docs/requirements.txt + - name: html + run: | + make -C docs html + - name: linkcheck + run: | + make -C docs linkcheck diff --git a/.github/workflows/CI-sample.yml b/.github/workflows/CI-sample.yml new file mode 100644 index 00000000000..14ffd4cb6b4 --- /dev/null +++ b/.github/workflows/CI-sample.yml @@ -0,0 +1,33 @@ +name: ci-sample + +on: + pull_request: + paths: + - '**' + - '!docs/**' + - '!.**' + - 'docs/code/**' + - '.github/workflows/CI-sample.yml' + push: + branches: + - v[0-9].* + - master + +jobs: + build: + strategy: + fail-fast: false + matrix: + os: [macos-latest, ubuntu-latest, windows-latest] + runs-on: ${{matrix.os}} + steps: + - uses: actions/checkout@v4 + - name: setup + run: cmake -E make_directory ${{runner.workspace}}/libuv/docs/code/build + - name: configure + # you may like use Ninja on unix-like OS, but for windows, the only easy way is to use Visual Studio if you want Ninja + run: cmake .. + working-directory: ${{runner.workspace}}/libuv/docs/code/build + - name: build + run: cmake --build . + working-directory: ${{runner.workspace}}/libuv/docs/code/build diff --git a/.github/workflows/CI-unix.yml b/.github/workflows/CI-unix.yml new file mode 100644 index 00000000000..298dca21400 --- /dev/null +++ b/.github/workflows/CI-unix.yml @@ -0,0 +1,182 @@ +name: CI-unix + +on: + pull_request: + paths: + - '**' + - '!docs/**' + - '!src/win/**' + - '!.**' + - '.github/workflows/CI-unix.yml' + push: + branches: + - v[0-9].* + - master + +jobs: + build-linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: configure + run: | + ./autogen.sh + mkdir build + (cd build && ../configure) + - name: distcheck + run: | + make -C build distcheck + + build-android: + runs-on: ubuntu-latest + env: + ANDROID_AVD_HOME: /root/.android/avd + steps: + - uses: actions/checkout@v4 + - name: Envinfo + run: npx envinfo + - name: Enable KVM + run: | + 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: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [macos-13, macos-14] + steps: + - 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 + - name: Configure + run: | + mkdir build + cd build + cmake .. -DBUILD_TESTING=ON -G Ninja + - name: Build + run: | + cmake --build build + ls -lh + - name: platform_output + run: | + ./build/uv_run_tests platform_output + - name: platform_output_a + run: | + ./build/uv_run_tests_a platform_output + - name: Test + run: | + cd build && ctest -V + - name: Autotools configure + if: always() + run: | + ./autogen.sh + mkdir build-auto + (cd build-auto && ../configure) + make -C build-auto -j4 + + build-ios: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [macos-13, macos-14] + steps: + - uses: actions/checkout@v4 + - name: Configure + run: | + mkdir build-ios + cd build-ios + cmake .. -GXcode -DCMAKE_SYSTEM_NAME:STRING=iOS -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED:BOOL=NO -DCMAKE_CONFIGURATION_TYPES:STRING=Release + - name: Build + run: | + cmake --build build-ios + ls -lh build-ios + + build-cross-qemu: + runs-on: ubuntu-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 } + - {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@v4 + - name: Install qemu and ${{ matrix.config.toolchain }} + run: | + sudo apt update + sudo apt install qemu-user qemu-user-binfmt ${{ matrix.config.toolchain }} -y + - name: Configure with ${{ matrix.config.cc }} + run: | + mkdir build + cd build + cmake .. -DBUILD_TESTING=ON -DQEMU=ON -DCMAKE_C_COMPILER=${{ matrix.config.cc }} + - name: Build + run: | + cmake --build build + ls -lh build + - name: Test + run: | + ${{ matrix.config.qemu }} build/uv_run_tests_a diff --git a/.github/workflows/CI-win.yml b/.github/workflows/CI-win.yml new file mode 100644 index 00000000000..b845139de64 --- /dev/null +++ b/.github/workflows/CI-win.yml @@ -0,0 +1,129 @@ +name: CI-win + +on: + pull_request: + paths: + - '**' + - '!docs/**' + - '!src/unix/**' + - '!.**' + - '.github/workflows/CI-win.yml' + push: + branches: + - v[0-9].* + - master + +jobs: + build-windows: + runs-on: windows-${{ matrix.config.server }} + name: build-${{ join(matrix.config.*, '-') }} + strategy: + fail-fast: false + matrix: + config: + - {toolchain: Visual Studio 16 2019, arch: Win32, server: 2019} + - {toolchain: Visual Studio 16 2019, arch: x64, server: 2019} + - {toolchain: Visual Studio 17 2022, arch: Win32, server: 2022} + - {toolchain: Visual Studio 17 2022, arch: x64, server: 2022} + - {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} + steps: + - uses: actions/checkout@v4 + - name: Build + run: + cmake -S . -B build -DBUILD_TESTING=ON + -G "${{ matrix.config.toolchain }}" -A ${{ matrix.config.arch }} + ${{ matrix.config.config == 'ASAN' && '-DASAN=on -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded' || '' }} + + cmake --build build --config RelWithDebInfo + + ${{ matrix.config.config == 'ASAN' && 'Copy-Item -Path "build\\*.exe" -Destination "build\\RelWithDebInfo\\"' || '' }} + + ${{ matrix.config.config == 'ASAN' && 'Copy-Item -Path "build\\*.dll" -Destination "build\\RelWithDebInfo\\"' || '' }} + + ls -l build + + ls -l build\\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 + if: ${{ matrix.config.config != 'ASAN' && matrix.config.arch != 'arm64' }} + shell: cmd + run: + cd build + + ctest -C RelWithDebInfo -V + - name: Test only static + if: ${{ matrix.config.config == 'ASAN' && matrix.config.arch != 'arm64' }} + shell: cmd + run: + build\\RelWithDebInfo\\uv_run_tests_a.exe + + build-mingw: + runs-on: ubuntu-latest + name: build-mingw-${{ matrix.config.arch }} + strategy: + fail-fast: false + matrix: + config: + - {arch: i686, server: 2022, libgcc: dw2 } + - {arch: x86_64, server: 2022, libgcc: seh } + steps: + - uses: actions/checkout@v4 + - name: Install mingw32 environment + run: | + sudo apt update + sudo apt install mingw-w64 ninja-build -y + - name: Build + run: | + cmake -S . -B build -G Ninja -DHOST_ARCH=${{ matrix.config.arch }} -DBUILD_TESTING=ON -DCMAKE_TOOLCHAIN_FILE=cmake-toolchains/cross-mingw32.cmake + cmake --build build + 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 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@v4 + with: + name: mingw-${{ matrix.config.arch }} + path: build/usr/**/* + retention-days: 2 + + test-mingw: + runs-on: windows-${{ matrix.config.server }} + name: test-mingw-${{ matrix.config.arch }} + needs: build-mingw + strategy: + fail-fast: false + matrix: + config: + - {arch: i686, server: 2022} + - {arch: x86_64, server: 2022} + steps: + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: mingw-${{ matrix.config.arch }} + - name: Test + shell: cmd + run: | + bin\uv_run_tests_a.exe + - name: Test + shell: cmd + run: | + bin\uv_run_tests.exe diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml deleted file mode 100644 index c8af0199c7d..00000000000 --- a/.github/workflows/CI.yml +++ /dev/null @@ -1,118 +0,0 @@ -name: CI - -on: [push, pull_request] - -jobs: - build-windows: - runs-on: windows-${{ matrix.config.server }} - name: build-${{ matrix.config.toolchain}}-${{ matrix.config.arch}} - strategy: - fail-fast: false - matrix: - config: - - {toolchain: Visual Studio 15 2017, arch: Win32, server: 2016} - - {toolchain: Visual Studio 15 2017, arch: x64, server: 2016} - - {toolchain: Visual Studio 16 2019, arch: Win32, server: 2019} - - {toolchain: Visual Studio 16 2019, arch: x64, server: 2019} - - {toolchain: Visual Studio 17 2022, arch: Win32, server: 2022} - - {toolchain: Visual Studio 17 2022, arch: x64, server: 2022} - steps: - - uses: actions/checkout@v2 - - name: Envinfo - run: npx envinfo - - name: Build - shell: cmd - run: | - mkdir -p build - cd build - cmake .. -DBUILD_TESTING=ON -G "${{ matrix.config.toolchain }}" -A ${{ matrix.config.arch }} - cmake --build . - - name: Test - shell: cmd - run: | - cd build - ctest -C Debug --output-on-failure - - build-android: - runs-on: ubuntu-latest - container: reactnativecommunity/react-native-android:2020-5-20 - steps: - - uses: actions/checkout@v2 - - name: Envinfo - run: npx envinfo - - name: Build android arm64 - # see build options you can use in https://developer.android.com/ndk/guides/cmake - run: | - mkdir build && cd build - $ANDROID_HOME/cmake/3.10.2.4988404/bin/cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_HOME/ndk/20.0.5594570/build/cmake/android.toolchain.cmake -DCMAKE_BUILD_TYPE=Release -DANDROID_ABI="arm64-v8a" -DANDROID_PLATFORM=android-21 .. - $ANDROID_HOME/cmake/3.10.2.4988404/bin/cmake --build . - - build-macos: - runs-on: macos-10.15 - steps: - - uses: actions/checkout@v2 - - name: Envinfo - run: npx envinfo - - name: Setup - run: | - brew install ninja - - name: Build - run: | - mkdir build - cd build && cmake .. -DBUILD_TESTING=ON -G Ninja - cmake --build . - ls -lh - - name: Test - run: | - cd build && ctest -V - - build-cross-qemu: - runs-on: ubuntu-latest - name: build-cross-qemu-${{ matrix.config.target }} - - strategy: - fail-fast: false - matrix: - config: - - {target: arm, toolchain: gcc-arm-linux-gnueabi, cc: arm-linux-gnueabi-gcc, qemu: qemu-arm-static } - - {target: armhf, toolchain: gcc-arm-linux-gnueabihf, cc: arm-linux-gnueabihf-gcc, qemu: qemu-arm-static } - - {target: aarch64, toolchain: gcc-aarch64-linux-gnu, cc: aarch64-linux-gnu-gcc, qemu: qemu-aarch64-static } - - {target: riscv64, toolchain: gcc-riscv64-linux-gnu, cc: riscv64-linux-gnu-gcc, qemu: qemu-riscv64-static } - - {target: ppc, toolchain: gcc-powerpc-linux-gnu, cc: powerpc-linux-gnu-gcc, qemu: qemu-ppc-static } - - {target: ppc64, toolchain: gcc-powerpc64-linux-gnu, cc: powerpc64-linux-gnu-gcc, qemu: qemu-ppc64-static } - - {target: ppc64le, toolchain: gcc-powerpc64le-linux-gnu, cc: powerpc64le-linux-gnu-gcc, qemu: qemu-ppc64le-static } - - {target: s390x, toolchain: gcc-s390x-linux-gnu, cc: s390x-linux-gnu-gcc, qemu: qemu-s390x-static } - - {target: mips, toolchain: gcc-mips-linux-gnu, cc: mips-linux-gnu-gcc, qemu: qemu-mips-static } - - {target: mips64, toolchain: gcc-mips64-linux-gnuabi64, cc: mips64-linux-gnuabi64-gcc, qemu: qemu-mips64-static } - - {target: mipsel, toolchain: gcc-mipsel-linux-gnu, cc: mipsel-linux-gnu-gcc, qemu: qemu-mipsel-static } - - {target: mips64el,toolchain: gcc-mips64el-linux-gnuabi64, cc: mips64el-linux-gnuabi64-gcc,qemu: qemu-mips64el-static } - - {target: alpha, toolchain: gcc-alpha-linux-gnu, cc: alpha-linux-gnu-gcc, qemu: qemu-alpha-static } - - {target: arm (u64 slots), toolchain: gcc-arm-linux-gnueabi, cc: arm-linux-gnueabi-gcc, qemu: qemu-arm-static} - - {target: aarch64 (u64 slots), toolchain: gcc-aarch64-linux-gnu, cc: aarch64-linux-gnu-gcc, qemu: qemu-aarch64-static} - - {target: ppc (u64 slots), toolchain: gcc-powerpc-linux-gnu, cc: powerpc-linux-gnu-gcc, qemu: qemu-ppc-static} - - {target: ppc64 (u64 slots), toolchain: gcc-powerpc64-linux-gnu, cc: powerpc64-linux-gnu-gcc, qemu: qemu-ppc64-static} - - steps: - - uses: actions/checkout@v2 - - name: Install QEMU - # this ensure install latest qemu on ubuntu, apt get version is old - env: - QEMU_SRC: "http://archive.ubuntu.com/ubuntu/pool/universe/q/qemu" - QEMU_VER: "qemu-user-static_4\\.2-.*_amd64.deb$" - run: | - DEB=`curl -s $QEMU_SRC/ | grep -o -E 'href="([^"#]+)"' | cut -d'"' -f2 | grep $QEMU_VER | tail -1` - wget $QEMU_SRC/$DEB - sudo dpkg -i $DEB - - name: Install ${{ matrix.config.toolchain }} - run: | - sudo apt update - sudo apt install ${{ matrix.config.toolchain }} -y - - name: Build - run: | - mkdir build - cd build && cmake .. -DBUILD_TESTING=ON -DQEMU=ON -DCMAKE_C_COMPILER=${{ matrix.config.cc }} - cmake --build . - ls -lh - - name: Test - run: | - ${{ matrix.config.qemu }} build/uv_run_tests_a diff --git a/.github/workflows/sanitizer.yml b/.github/workflows/sanitizer.yml index c0a54b28235..58254327d2a 100644 --- a/.github/workflows/sanitizer.yml +++ b/.github/workflows/sanitizer.yml @@ -1,26 +1,128 @@ name: Sanitizer checks -on: [push, pull_request] +on: + pull_request: + paths: + - '**' + - '!docs/**' + - '!.**' + - '.github/workflows/sanitizer.yml' + push: + branches: + - v[0-9].* + - master jobs: - sanitizers: - runs-on: ubuntu-latest + 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 - - name: TSAN + + # [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 + (cd build-asan && cmake .. -G Ninja -DBUILD_TESTING=ON -DASAN=ON -DCMAKE_BUILD_TYPE=Debug) + cmake --build build-asan + - name: ASAN Test + run: | + ./build-asan/uv_run_tests_a + + - name: MSAN Build + run: | + mkdir build-msan + (cd build-msan && cmake .. -G Ninja -DBUILD_TESTING=ON -DMSAN=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=clang) + cmake --build build-msan + - name: MSAN Test + run: | + ./build-msan/uv_run_tests_a + + - name: TSAN Build run: | mkdir build-tsan (cd build-tsan && cmake .. -G Ninja -DBUILD_TESTING=ON -DTSAN=ON -DCMAKE_BUILD_TYPE=Release) cmake --build build-tsan - ./build-tsan/uv_run_tests_a || true # currently permit failures - - name: ASAN + - name: TSAN Test + # Note: path must be absolute because some tests chdir. + # TSan exits with an error when it can't find the file. + run: | + env TSAN_OPTIONS="suppressions=$PWD/tsansupp.txt" ./build-tsan/uv_run_tests_a + + - name: UBSAN Build + run: | + mkdir build-ubsan + (cd build-ubsan && cmake .. -G Ninja -DBUILD_TESTING=ON -DUBSAN=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=clang) + cmake --build build-ubsan + - name: UBSAN Test + run: | + ./build-ubsan/uv_run_tests_a + + sanitizers-macos: + runs-on: macos-13 + steps: + - uses: actions/checkout@v4 + + - name: Envinfo + run: npx envinfo + + - name: ASAN Build run: | mkdir build-asan - (cd build-asan && cmake .. -G Ninja -DBUILD_TESTING=ON -DASAN=ON -DCMAKE_BUILD_TYPE=Debug) + (cd build-asan && cmake .. -DBUILD_TESTING=ON -DASAN=ON -DCMAKE_BUILD_TYPE=Debug) cmake --build build-asan + - name: ASAN Test + run: | ./build-asan/uv_run_tests_a + + - name: TSAN Build + run: | + mkdir build-tsan + (cd build-tsan && cmake .. -DBUILD_TESTING=ON -DTSAN=ON -DCMAKE_BUILD_TYPE=Release) + cmake --build build-tsan + - name: TSAN Test + run: | + ./build-tsan/uv_run_tests_a + + - name: UBSAN Build + run: | + mkdir build-ubsan + (cd build-ubsan && cmake .. -DBUILD_TESTING=ON -DUBSAN=ON -DCMAKE_BUILD_TYPE=Debug) + cmake --build build-ubsan + - name: UBSAN Test + run: | + ./build-ubsan/uv_run_tests_a + + sanitizers-windows: + runs-on: windows-2022 + steps: + - 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@v2 + with: + version: "17" + + - name: Envinfo + run: npx envinfo + + - name: UBSAN Build + run: | + mkdir build-ubsan + cmake -B build-ubsan -G Ninja -DBUILD_TESTING=ON -DUBSAN=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=clang + cmake --build build-ubsan + - name: UBSAN Test + run: | + ./build-ubsan/uv_run_tests_a diff --git a/.gitignore b/.gitignore index 7eb49322af0..6d396efb49f 100644 --- a/.gitignore +++ b/.gitignore @@ -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/ diff --git a/.mailmap b/.mailmap index b23377c6151..f5d5375e044 100644 --- a/.mailmap +++ b/.mailmap @@ -4,6 +4,7 @@ Aaron Bieber Alan Gutierrez Andrius Bentkus Andy Fiddaman +Andy Pan Bert Belder Bert Belder Bert Belder @@ -18,6 +19,7 @@ David Carlier Devchandra Meetei Leishangthem Fedor Indutny Frank Denis +Hüseyin Açacak <110401522+huseyinacacak-janea@users.noreply.github.com> Imran Iqbal Isaac Z. Schlueter Jason Williams @@ -29,6 +31,7 @@ Keno Fischer Keno Fischer Leith Bade Leonard Hecker +Lewis Russell Maciej Małecki Marc Schlaich Michael @@ -36,6 +39,7 @@ Michael Neumann Michael Penick Nicholas Vavilov Nick Logan +Olivier Valentin Rasmus Christian Pedersen Rasmus Christian Pedersen Richard Lau @@ -46,7 +50,8 @@ Sakthipriyan Vairamani Sam Roberts San-Tai Hsu Santiago Gimeno -Saúl Ibarra Corretgé +Saúl Ibarra Corretgé +Saúl Ibarra Corretgé Saúl Ibarra Corretgé Shigeki Ohtsu Shuowang (Wayne) Zhang @@ -60,5 +65,7 @@ gengjiawen jBarz jBarz ptlomholt +theanarkh <2923878201@qq.com> tjarlama <59913901+tjarlama@users.noreply.github.com> +ywave620 <60539365+ywave620@users.noreply.github.com> zlargon diff --git a/.readthedocs.yaml b/.readthedocs.yaml index e53b9f3e84b..5290ec33e89 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -2,10 +2,14 @@ version: 2 sphinx: builder: html - configuration: null + configuration: docs/src/conf.py fail_on_warning: false +build: + os: "ubuntu-22.04" + tools: + python: "3.9" + python: - version: 3.8 install: - requirements: docs/requirements.txt diff --git a/AUTHORS b/AUTHORS index a18d09668b9..39550bbc535 100644 --- a/AUTHORS +++ b/AUTHORS @@ -496,3 +496,99 @@ Jesper Storm Bache Campbell He Andrey Hohutkin deal +David Machaj <46852402+dmachaj@users.noreply.github.com> +Jessica Clarke +Jeremy Rose +woclass +Luca Adrian L +WenTao Ou +jonilaitinen +UMU +Paul Evans +wyckster +Vittore F. Scolari +roflcopter4 <15476346+roflcopter4@users.noreply.github.com> +V-for-Vasili +Denny C. Dai +Hannah Shi +tuftedocelot +blogdaren +chucksilvers +Sergey Fedorov +theanarkh <2923878201@qq.com> +Samuel Cabrero +自发对称破缺 <429839446@qq.com> +Luan Devecchi +Steven Schveighoffer +number201724 +Daniel +Christian Clason +ywave620 +jensbjorgensen +daomingq +Qix +Edward Humes <29870961+aurxenon@users.noreply.github.com> +Tim Besard +Sergey Rubanov +Stefan Stojanovic +Zvicii +dundargoc <33953936+dundargoc@users.noreply.github.com> +Jack·Boos·Yu <47264268+JackBoosY@users.noreply.github.com> +panran <310762957@qq.com> +Tamás Bálint Misius +Bruno Passeri +Jason Zhang +Lewis Russell +sivadeilra +cui fliter +Mohammed Keyvanzadeh +Niklas Mischkulnig <4586894+mischnic@users.noreply.github.com> +Stefan Karpinski +liuxiang88 <94350585+liuxiang88@users.noreply.github.com> +Jeffrey H. Johnson +Abdirahim Musse <33973272+abmusse@users.noreply.github.com> +小明 <7737673+caobug@users.noreply.github.com> +Shuduo Sang +Keith Winstein +michalbiesek +Alois Klink +SmorkalovG +Pleuvens +jolai <58589285+laijonathan@users.noreply.github.com> +Julien Roncaglia +prubel +Per Allansson <65364157+per-allansson@users.noreply.github.com> +Matheus Izvekov +Christian Heimlich +Hao Hu <33607772+hhu8@users.noreply.github.com> +matoro <12038583+matoro@users.noreply.github.com> +Bo Anderson +Ardi Nugraha <33378542+ardi-nugraha@users.noreply.github.com> +Anton Bachin +Trevor Flynn +Andy Pan +Viacheslav Muravyev +Anthony Alayo +Thomas Walter <31201229+waltoss@users.noreply.github.com> +hiiizxf <385122613@qq.com> +Geddy +Farzin Monsef +tgolang <154592711+tgolang@users.noreply.github.com> +josedelinux +Hüseyin Açacak <110401522+huseyinacacak-janea@users.noreply.github.com> +Uilian Ries +Olivier Valentin +郑苏波 (Super Zheng) +zeertzjq +Ian Butterworth +握猫猫 <164346864@qq.com> +Zuohui Yang <274048862@qq.com> +Edigleysson Silva (Edy) +Raihaan Shouhell +Rialbat +Adam +Poul T Lomholt +Thad House +Julian A Avar C <28635807+julian-a-avar-c@users.noreply.github.com> +amcgoogan <105525867+amcgoogan@users.noreply.github.com> +Rafael Gonzaga diff --git a/CMakeLists.txt b/CMakeLists.txt index b41e5462dd6..af89db2dfc2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,13 @@ -cmake_minimum_required(VERSION 3.4) -project(libuv LANGUAGES C) +cmake_minimum_required(VERSION 3.10) -cmake_policy(SET CMP0057 NEW) # Enable IN_LIST operator -cmake_policy(SET CMP0064 NEW) # Support if (TEST) operator +if(POLICY CMP0091) + cmake_policy(SET CMP0091 NEW) # Enable MSVC_RUNTIME_LIBRARY setting +endif() +if(POLICY CMP0092) + cmake_policy(SET CMP0092 NEW) # disable /W3 warning, if possible +endif() + +project(libuv LANGUAGES C) list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") @@ -17,9 +22,13 @@ set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS ON) set(CMAKE_C_STANDARD 90) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +option(LIBUV_BUILD_SHARED "Build shared lib" ON) + cmake_dependent_option(LIBUV_BUILD_TESTS "Build the unit tests when BUILD_TESTING is enabled and we are the root project" ON - "BUILD_TESTING;CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR" OFF) + "BUILD_TESTING;LIBUV_BUILD_SHARED;CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR" OFF) cmake_dependent_option(LIBUV_BUILD_BENCH "Build the benchmarks when building unit tests and we are the root project" ON "LIBUV_BUILD_TESTS" OFF) @@ -27,28 +36,66 @@ cmake_dependent_option(LIBUV_BUILD_BENCH # Qemu Build option(QEMU "build for qemu" OFF) if(QEMU) - add_definitions(-D__QEMU__=1) + list(APPEND uv_defines __QEMU__=1) endif() +# Note: these are mutually exclusive. option(ASAN "Enable AddressSanitizer (ASan)" OFF) +option(MSAN "Enable MemorySanitizer (MSan)" OFF) option(TSAN "Enable ThreadSanitizer (TSan)" OFF) +option(UBSAN "Enable UndefinedBehaviorSanitizer (UBSan)" OFF) -if((ASAN OR TSAN) AND NOT (CMAKE_C_COMPILER_ID MATCHES "AppleClang|GNU|Clang")) - message(SEND_ERROR "Sanitizer support requires clang or gcc. Try again with -DCMAKE_C_COMPILER.") +if(MSAN AND NOT CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang") + message(SEND_ERROR "MemorySanitizer requires clang. Try again with -DCMAKE_C_COMPILER=clang") endif() if(ASAN) - add_definitions(-D__ASAN__=1) - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer -fsanitize=address") - set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=address") - set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=address") + list(APPEND uv_defines __ASAN__=1) + if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|GNU|Clang") + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=address") + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=address") + elseif(MSVC) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address") + else() + message(SEND_ERROR "AddressSanitizer support requires clang, gcc, or msvc. Try again with -DCMAKE_C_COMPILER.") + endif() +endif() + +if(MSAN) + list(APPEND uv_defines __MSAN__=1) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer -fsanitize=memory") + set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=memory") + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=memory") endif() if(TSAN) - add_definitions(-D__TSAN__=1) - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer -fsanitize=thread") - set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=thread") - set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=thread") + list(APPEND uv_defines __TSAN__=1) + if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|GNU|Clang") + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer -fsanitize=thread") + set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=thread") + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=thread") + else() + message(SEND_ERROR "ThreadSanitizer support requires clang or gcc. Try again with -DCMAKE_C_COMPILER.") + endif() +endif() + +if(UBSAN) + cmake_minimum_required(VERSION 3.13) + list(APPEND uv_defines __UBSAN__=1) + if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|GNU|Clang") + 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 or gcc. Try again with -DCMAKE_C_COMPILER.") + endif() endif() # Compiler check @@ -119,12 +166,19 @@ list(APPEND uv_cflags ${lint-utf8-msvc} ) check_c_compiler_flag(-fno-strict-aliasing UV_F_STRICT_ALIASING) list(APPEND uv_cflags $<$:-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 src/inet.c src/random.c src/strscpy.c + src/strtok.c + src/thread-common.c src/threadpool.c src/timer.c src/uv-common.c @@ -132,14 +186,17 @@ 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 advapi32 iphlpapi userenv - ws2_32) + ws2_32 + dbghelp + ole32 + shell32) list(APPEND uv_sources src/win/async.c src/win/core.c @@ -215,16 +272,11 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Android") list(APPEND uv_defines _GNU_SOURCE) list(APPEND uv_libraries dl) list(APPEND uv_sources - src/unix/android-ifaddrs.c - src/unix/linux-core.c - src/unix/linux-inotify.c - src/unix/linux-syscalls.c + src/unix/linux.c src/unix/procfs-exepath.c - src/unix/pthread-fixes.c src/unix/random-getentropy.c src/unix/random-getrandom.c - src/unix/random-sysctl-linux.c - src/unix/epoll.c) + src/unix/random-sysctl-linux.c) endif() if(APPLE OR CMAKE_SYSTEM_NAME MATCHES "Android|Linux") @@ -259,17 +311,26 @@ if(APPLE) src/unix/fsevents.c) 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 + src/unix/no-fsevents.c + src/unix/no-proctitle.c + src/unix/posix-hrtime.c + src/unix/posix-poll.c + src/unix/hurd.c) +endif() + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") list(APPEND uv_defines _GNU_SOURCE _POSIX_C_SOURCE=200112) list(APPEND uv_libraries dl rt) list(APPEND uv_sources - src/unix/linux-core.c - src/unix/linux-inotify.c - src/unix/linux-syscalls.c + src/unix/linux.c src/unix/procfs-exepath.c src/unix/random-getrandom.c - src/unix/random-sysctl-linux.c - src/unix/epoll.c) + src/unix/random-sysctl-linux.c) endif() if(CMAKE_SYSTEM_NAME STREQUAL "NetBSD") @@ -300,7 +361,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL "OS390") list(APPEND uv_defines _XOPEN_SOURCE=600) list(APPEND uv_defines _XOPEN_SOURCE_EXTENDED) list(APPEND uv_sources - src/unix/pthread-fixes.c src/unix/os390.c src/unix/os390-syscalls.c src/unix/os390-proctitle.c) @@ -338,6 +398,10 @@ if(CMAKE_SYSTEM_NAME STREQUAL "OS400") endif() if(CMAKE_SYSTEM_NAME STREQUAL "SunOS") + if(CMAKE_SYSTEM_VERSION STREQUAL "5.10") + list(APPEND uv_defines SUNOS_NO_IFADDRS) + list(APPEND uv_libraries rt) + endif() list(APPEND uv_defines __EXTENSIONS__ _XOPEN_SOURCE=500 _REENTRANT) list(APPEND uv_libraries kstat nsl sendfile socket) list(APPEND uv_sources @@ -372,25 +436,42 @@ if(APPLE OR CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD|Linux|NetBSD|OpenBSD") list(APPEND uv_test_libraries util) endif() -add_library(uv SHARED ${uv_sources}) -target_compile_definitions(uv - INTERFACE - USING_UV_SHARED=1 - PRIVATE - BUILDING_UV_SHARED=1 - ${uv_defines}) -target_compile_options(uv PRIVATE ${uv_cflags}) -target_include_directories(uv - PUBLIC - $ - $ - PRIVATE - $) -if(CMAKE_SYSTEM_NAME STREQUAL "OS390") - target_include_directories(uv PUBLIC $) - set_target_properties(uv PROPERTIES LINKER_LANGUAGE CXX) +if(CYGWIN OR MSYS) + list(APPEND uv_defines _GNU_SOURCE) + list(APPEND uv_sources + src/unix/cygwin.c + src/unix/bsd-ifaddrs.c + src/unix/no-fsevents.c + src/unix/no-proctitle.c + src/unix/posix-hrtime.c + src/unix/posix-poll.c + src/unix/procfs-exepath.c + src/unix/sysinfo-loadavg.c + src/unix/sysinfo-memory.c) +endif() + +if(LIBUV_BUILD_SHARED) + add_library(uv SHARED ${uv_sources}) + target_compile_definitions(uv + INTERFACE + USING_UV_SHARED=1 + PRIVATE + BUILDING_UV_SHARED=1 + ${uv_defines}) + target_compile_options(uv PRIVATE ${uv_cflags}) + target_include_directories(uv + PUBLIC + $ + $ + PRIVATE + $) + if(CMAKE_SYSTEM_NAME STREQUAL "OS390") + target_include_directories(uv PUBLIC $) + set_target_properties(uv PROPERTIES LINKER_LANGUAGE CXX) + endif() + target_link_libraries(uv ${uv_libraries}) + set_target_properties(uv PROPERTIES OUTPUT_NAME "uv") endif() -target_link_libraries(uv ${uv_libraries}) add_library(uv_a STATIC ${uv_sources}) target_compile_definitions(uv_a PRIVATE ${uv_defines}) @@ -406,6 +487,10 @@ if(CMAKE_SYSTEM_NAME STREQUAL "OS390") set_target_properties(uv_a PROPERTIES LINKER_LANGUAGE CXX) endif() target_link_libraries(uv_a ${uv_libraries}) +set_target_properties(uv_a PROPERTIES OUTPUT_NAME "uv") +if(WIN32) + set_target_properties(uv_a PROPERTIES PREFIX "lib") +endif() if(LIBUV_BUILD_TESTS) # Small hack: use ${uv_test_sources} now to get the runner skeleton, @@ -418,6 +503,7 @@ if(LIBUV_BUILD_TESTS) test/benchmark-fs-stat.c test/benchmark-getaddrinfo.c test/benchmark-loop-count.c + test/benchmark-queue-work.c test/benchmark-million-async.c test/benchmark-million-timers.c test/benchmark-multi-accept.c @@ -447,7 +533,6 @@ if(LIBUV_BUILD_TESTS) test/test-async-null-cb.c test/test-async.c test/test-barrier.c - test/test-callback-order.c test/test-callback-stack.c test/test-close-fd.c test/test-close-order.c @@ -487,6 +572,7 @@ if(LIBUV_BUILD_TESTS) test/test-hrtime.c test/test-idle.c test/test-idna.c + test/test-iouring-pollhup.c test/test-ip4-addr.c test/test-ip6-addr.c test/test-ip-name.c @@ -546,10 +632,12 @@ if(LIBUV_BUILD_TESTS) test/test-spawn.c test/test-stdio-over-pipes.c test/test-strscpy.c + test/test-strtok.c test/test-tcp-alloc-cb-fail.c test/test-tcp-bind-error.c test/test-tcp-bind6-error.c test/test-tcp-close-accept.c + test/test-tcp-close-after-read-timeout.c test/test-tcp-close-while-connecting.c test/test-tcp-close.c test/test-tcp-close-reset.c @@ -562,9 +650,12 @@ 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 test/test-tcp-try-write.c + test/test-tcp-write-in-a-row.c test/test-tcp-try-write-error.c test/test-tcp-unexpected-read.c test/test-tcp-write-after-connect.c @@ -573,8 +664,11 @@ if(LIBUV_BUILD_TESTS) test/test-tcp-write-to-half-open-connection.c test/test-tcp-writealot.c test/test-test-macros.c + 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 @@ -605,6 +699,8 @@ if(LIBUV_BUILD_TESTS) test/test-udp-sendmmsg-error.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) @@ -621,6 +717,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 + "$" + "$/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}) @@ -637,6 +739,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 + "$" + "$/uv_run_tests_a_no_ext") + endif() endif() # Now for some gibbering horrors from beyond the stars... @@ -648,30 +756,55 @@ string(REPLACE ";" " " LIBS "${LIBS}") file(STRINGS configure.ac configure_ac REGEX ^AC_INIT) string(REGEX MATCH "([0-9]+)[.][0-9]+[.][0-9]+" PACKAGE_VERSION "${configure_ac}") set(UV_VERSION_MAJOR "${CMAKE_MATCH_1}") -# The version in the filename is mirroring the behaviour of autotools. -set_target_properties(uv PROPERTIES - VERSION ${UV_VERSION_MAJOR}.0.0 - SOVERSION ${UV_VERSION_MAJOR}) + set(includedir ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}) set(libdir ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}) set(prefix ${CMAKE_INSTALL_PREFIX}) -configure_file(libuv.pc.in libuv.pc @ONLY) configure_file(libuv-static.pc.in libuv-static.pc @ONLY) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR}) -install(FILES ${PROJECT_BINARY_DIR}/libuv.pc ${PROJECT_BINARY_DIR}/libuv-static.pc +install(FILES LICENSE-extra DESTINATION ${CMAKE_INSTALL_DOCDIR}) +install(FILES ${PROJECT_BINARY_DIR}/libuv-static.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) -install(TARGETS uv EXPORT libuvConfig - RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) -install(TARGETS uv_a ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) -install(EXPORT libuvConfig DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libuv) +install(TARGETS uv_a EXPORT libuvConfig + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +install(EXPORT libuvConfig + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libuv + NAMESPACE libuv::) + +if(LIBUV_BUILD_SHARED) + # The version in the filename is mirroring the behaviour of autotools. + set_target_properties(uv PROPERTIES + VERSION ${UV_VERSION_MAJOR}.0.0 + SOVERSION ${UV_VERSION_MAJOR}) + configure_file(libuv.pc.in libuv.pc @ONLY) + install(FILES ${PROJECT_BINARY_DIR}/libuv.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + install(TARGETS uv EXPORT libuvConfig + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +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} diff --git a/ChangeLog b/ChangeLog index 0a41c5760bc..b2770e60004 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,1137 @@ +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: + +* test: fix license blurb (Ben Noordhuis) + +* linux: fix harmless warn_unused_result warning (Shuduo Sang) + +* darwin: fix build warnings (小明) + +* linux: don't use io_uring on pre-5.10.186 kernels (Ben Noordhuis) + +* fs: fix WTF-8 decoding issue (Jameson Nash) + +* test: enable disabled tcp_connect6_error_fault (Ben Noordhuis) + +* test: enable disabled fs_link (Ben Noordhuis) + +* test: enable disabled spawn_same_stdout_stderr (Ben Noordhuis) + +* linux: handle UNAME26 personality (Ben Noordhuis) + +* build: move cmake_minimum_required version to 3.9 (Keith Winstein) + +* unix: set ipv6 scope id for link-local addresses (Ben Noordhuis) + +* unix: match kqueue and epoll code (Trevor Norris) + +* win,spawn: allow `%PATH%` to be unset (Kyle Edwards) + +* doc: switch to Furo, a more modern Sphinx theme (Saúl Ibarra Corretgé) + +* darwin: make TCP_KEEPINTVL and TCP_KEEPCNT available (小明) + +* win,fs: avoid winapi macro redefinition (Brad King) + +* linux: add missing riscv syscall numbers (michalbiesek) + +* doc: fix broken "Shared library" Wikipedia link (Alois Klink) + +* unix: get mainline kernel version in Ubuntu (Santiago Gimeno) + +* unix: get mainline kernel version in Debian (Ben Noordhuis) + +* build: fix qemu install in CI-unix workflow (Santiago Gimeno) + +* unix: disable io_uring close on selected kernels (Santiago Gimeno) + +* test: skip tests when ipv6 is not available (Santiago Gimeno) + +* ibmi: implement ifaddrs, getifaddrs, freeifaddrs (Abdirahim Musse) + +* unix: reset signal counters after fork (SmorkalovG) + +* win,process: avoid assert after spawning Store app (Jameson Nash) + +* unix: remove pread/preadv conditionals (Ben Noordhuis) + +* unix: remove pwrite/pwritev conditionals (Ben Noordhuis) + +* darwin: remove workaround for data corruption bug (Ben Noordhuis) + +* src: default to stream=stderr in handle printer (Ben Noordhuis) + +* test: switch to new-style ASSERT_EQ macros (Pleuvens) + +* zos: correctly get cpu model in uv_cpu_info() (jolai) + +* test: fix get_passwd2 on IBM i (Abdirahim Musse) + +* unix: don't malloc on sync uv_fs_read (Ben Noordhuis) + +* freebsd: get fs event path with fcntl(F_KINFO) (David Carlier) + +* test: switch from ASSERT_* to ASSERT_PTR_* (Pleuvens) + +* darwin: workaround apple pthread_cond_wait bug (Julien Roncaglia) + +* doc: uv_close should be called after exit callback (Pleuvens) + +* test: 192.0.2.0/24 is the actual -TEST-NET-1 (prubel) + +* unix: add back preadv/pwritev fallback (Ben Noordhuis) + +* unix: rename variable for consistency (Ben Noordhuis) + +* unix: merge read/write code into single functions (Ben Noordhuis) + +* doc: filename arg to uv_fs_event_cb can be NULL (Ben Noordhuis) + +* build,win: we need to link against shell32.lib (Per Allansson) + +* unix: no preadv/pwritev workaround if not needed (Jeffrey H. Johnson) + +* build: add CI for Windows ARM64 (build only) (Per Allansson) + +* linux: disable io_uring on 32 bits arm systems (Ben Noordhuis) + +* build: run sanitizers on macos ci (Ben Noordhuis) + +* misc: export WTF8 conversion utilities (Jameson Nash) + +* build: fix libuv.a file name for cmake (Jameson Nash) + +* build: add windows ubsan and clang ci (Matheus Izvekov) + +* win: improve accuracy of ProductName between arch (Christian Heimlich) + + +2023.06.30, Version 1.46.0 (Stable), f0bb7e40f0508bedf6fad33769b3f87bb8aedfa6 + +Changes since version 1.45.0: + +* Add SHA to ChangeLog (Santiago Gimeno) + +* misc: update readthedocs config (Jameson Nash) + +* test: remove erroneous RETURN_SKIP (Ben Noordhuis) + +* android: disable io_uring support (Ben Noordhuis) + +* linux: add some more iouring backed fs ops (Santiago Gimeno) + +* build: add autoconf option for disable-maintainer-mode (Jameson Nash) + +* fs: use WTF-8 on Windows (Stefan Karpinski) + +* unix,win: replace QUEUE with struct uv__queue (Ben Noordhuis) + +* linux: fs_read to use io_uring if iovcnt > IOV_MAX (Santiago Gimeno) + +* ios: fix uv_getrusage() ru_maxrss calculation (Ben Noordhuis) + +* include: update outdated code comment (Ben Noordhuis) + +* linux: support abstract unix sockets (Ben Noordhuis) + +* unix,win: add UV_PIPE_NO_TRUNCATE flag (Ben Noordhuis) + +* unix: add loongarch support (liuxiang88) + +* doc: add DPS8M to LINKS.md (Jeffrey H. Johnson) + +* include: add EUNATCH errno mapping (Abdirahim Musse) + +* src: don't run timers if loop is stopped/unref'd (Trevor Norris) + +* win: fix -Wpointer-to-int-cast warning (Ben Noordhuis) + +* test,win: fix -Wunused-variable warning (Ben Noordhuis) + +* test,win: fix -Wformat warning (Ben Noordhuis) + +* linux: work around io_uring IORING_OP_CLOSE bug (Ben Noordhuis) + +* win: remove unused functions (Ben Noordhuis) + +* bench: add bench to check uv_loop_alive (Trevor Norris) + +* test: add uv_cancel test for threadpool (Trevor Norris) + +* unix: skip prohibited syscalls on tvOS and watchOS (小明) + +* unix,fs: make no_pwritev access thread-safe (Santiago Gimeno) + +* unix: fix build for lower versions of Android (小明) + + +2023.05.19, Version 1.45.0 (Stable), 96e05543f53b19d9642b4b0dd73b86ad3cea313e + +Changes since version 1.44.2: + +* win: remove stdint-msvc2008.h (Ben Noordhuis) + +* android: remove pthread-fixes.c (Ben Noordhuis) + +* build: enable MSVC_RUNTIME_LIBRARY setting (自发对称破缺) + +* unix: switch to c11 atomics (Ben Noordhuis) + +* unix: don't accept() connections in a loop (Ben Noordhuis) + +* win: fix off-by-1 buffer overrun in uv_exepath() (Ben Noordhuis) + +* build: switch ci from macos-10.15 to macos-11 (Ben Noordhuis) + +* win: fix thread race in uv_cwd() and uv_chdir() (Ben Noordhuis) + +* unix,win: remove UV_HANDLE_SHUTTING flag (Santiago Gimeno) + +* win: support Windows 11 in uv_os_uname() (Luan Devecchi) + +* unix: fix uv_getrusage() ru_maxrss reporting (Ben Noordhuis) + +* doc: add note about offset -1 in uv_fs_read/write (Steven Schveighoffer) + +* test: fix musl libc.a dlerror() test expectation (Ben Noordhuis) + +* kqueue: DRY file descriptor deletion logic (Ben Noordhuis) + +* linux: teach uv_get_constrained_memory() cgroupsv2 (Ben Noordhuis) + +* build: upgrade qemu-user-static package (Ben Noordhuis) + +* linux: move epoll.c back into linux-core.c (Ben Noordhuis) + +* unix: remove pre-macos 10.8 compatibility hack (Ben Noordhuis) + +* unix,win: fix memory leak in uv_fs_scandir() (Ben Noordhuis) + +* build: restore qemu download logic (Ben Noordhuis) + +* win: fix uv__pipe_accept memory leak (number201724) + +* doc: update LINKS.md (Daniel) + +* unix: simplify atomic op in uv_tty_reset_mode() (Ben Noordhuis) + +* build: add LIBUV_BUILD_SHARED cmake option (Christian Clason) + +* linux: remove unused or obsolete syscall wrappers (Ben Noordhuis) + +* linux: merge files back into single file (Ben Noordhuis) + +* stream: process more than one write req per loop tick (ywave620) + +* unix,win: give thread pool threads an 8 MB stack (Ben Noordhuis) + +* build: add MemorySanitizer (MSAN) support (Ben Noordhuis) + +* doc: add uv_poll_cb status==UV_EBADF note (jensbjorgensen) + +* build: support AddressSanitizer on MSVC (Jameson Nash) + +* win,pipe: improve method of obtaining pid for ipc (number201724) + +* thread: add support for affinity (daomingq) + +* include: map ENODATA error code (Ben Noordhuis) + +* build: remove bashism from autogen.sh (Santiago Gimeno) + +* win,tcp,udp: remove "active streams" optimization (Saúl Ibarra Corretgé) + +* win: drop code checking for Windows XP / Server 2k3 (Saúl Ibarra Corretgé) + +* unix,win: fix 'sprintf' is deprecated warning (twosee) + +* doc: mention close_cb can be NULL (Qix) + +* win: optimize udp receive performance (ywave620) + +* win: fix an incompatible types warning (twosee) + +* doc: document 0 return value for free/total memory (Ben Noordhuis) + +* darwin: use hw.cpufrequency again for frequency info (Jameson Nash) + +* win,test: change format of TEST_PIPENAME's (Santiago Gimeno) + +* win,pipe: fixes in uv_pipe_connect() (Santiago Gimeno) + +* misc: fix return value of memory functions (theanarkh) + +* src: add new metrics APIs (Trevor Norris) + +* thread: add uv_thread_getcpu() (daomingq) + +* build: don't use ifaddrs.h on solaris 10 (Edward Humes) + +* unix,win: add uv_get_available_memory() (Tim Besard) + +* test: fix -Wunused-but-set-variable warnings (Ben Noordhuis) + +* doc: bump min supported linux and freebsd versions (Ben Noordhuis) + +* Add Socket Runtime to the LINKS.md (Sergey Rubanov) + +* unix: drop kfreebsd support (Ben Noordhuis) + +* win: fix fstat for pipes and character files (Stefan Stojanovic) + +* win: fix -Wunused-variable warning (Ben Noordhuis) + +* win: fix -Wunused-function warning (Ben Noordhuis) + +* build: drop qemu-alpha from ci matrix (Ben Noordhuis) + +* win: move child_stdio_buffer out of uv_process_t (Santiago Gimeno) + +* test: fix some unreachable code warnings (Santiago Gimeno) + +* linux: simplify uv_uptime() (Ben Noordhuis) + +* test: unflake fs_event_watch_dir test (Ben Noordhuis) + +* darwin: remove unused fsevents symbol lookups (Ben Noordhuis) + +* build: add define guard around UV_EXTERN (Zvicii) + +* build: add UndefinedBehaviorSanitizer support (Ben Noordhuis) + +* build: enable platform_output test on qemu (Ben Noordhuis) + +* linux: handle cpu hotplugging in uv_cpu_info() (Ben Noordhuis) + +* build: remove unnecessary policy setting (dundargoc) + +* docs: add vcpkg instruction step (Jack·Boos·Yu) + +* win,fs: fix readlink errno for a non-symlink file (Darshan Sen) + +* misc: extend getpw to take uid as an argument (Jameson Nash) + +* unix,win: use static_assert when available (Ben Noordhuis) + +* docs: delete code Makefile (Jameson Nash) + +* docs: add CI for docs PRs (Jameson Nash) + +* docs: update Sphinx version on RTD (Jameson Nash) + +* doc: clean up license file (Ben Noordhuis) + +* test: fix some warnings when compiling tests (panran) + +* build,win: add mingw-w64 CI configuration (Jameson Nash) + +* build: add CI for distcheck (Jameson Nash) + +* unix: remove busy loop from uv_async_send (Jameson Nash) + +* doc: document uv_fs_cb type (Tamás Bálint Misius) + +* build: Improve build by cmake for Cygwin (erw7) + +* build: add libuv:: namespace to libuvConfig.cmake (AJ Heller) + +* test: fix ThreadSanitizer thread leak warning (Ben Noordhuis) + +* test: fix ThreadSanitizer data race warning (Ben Noordhuis) + +* test: fix ThreadSanitizer data race warning (Ben Noordhuis) + +* test: fix ThreadSanitizer data race warning (Ben Noordhuis) + +* test: cond-skip fork_threadpool_queue_work_simple (Ben Noordhuis) + +* test: cond-skip signal_multiple_loops (Ben Noordhuis) + +* test: cond-skip tcp_writealot (Ben Noordhuis) + +* build: promote tsan ci to must-pass (Ben Noordhuis) + +* build: add CI for OpenBSD and FreeBSD (James McCoy) + +* build,test: fix distcheck errors (Jameson Nash) + +* test: remove bad tty window size assumption (Ben Noordhuis) + +* darwin,process: feed kevent the signal to reap children (Jameson Nash) + +* unix: abort on clock_gettime() error (Ben Noordhuis) + +* test: remove timing-sensitive check (Ben Noordhuis) + +* unix: DRY and fix tcp bind error path (Jameson Nash) + +* macos: fix fsevents thread race conditions (Ben Noordhuis) + +* win: fix leak in uv_chdir (Trevor Norris) + +* test: make valgrind happy (Trevor Norris) + +* barrier: wait for prior out before next in (Jameson Nash) + +* test: fix visual studio 2015 build error (Ben Noordhuis) + +* linux: fix ceph copy error truncating readonly files (Bruno Passeri) + +* test: silence more valgrind warnings (Trevor Norris) + +* doc: add entries to LINKS.md (Trevor Norris) + +* win,unix: change execution order of timers (Trevor Norris) + +* doc: add trevnorris to maintainers (Trevor Norris) + +* linux: remove epoll_pwait() emulation code path (Ben Noordhuis) + +* linux: replace unsafe macro with inline function (Ben Noordhuis) + +* linux: remove arm oabi support (Ben Noordhuis) + +* unix,sunos: SO_REUSEPORT not valid on all sockets (Stacey Marshall) + +* doc: consistent single backquote in misc.rst (Jason Zhang) + +* src: switch to use C11 atomics where available (Trevor Norris) + +* test: don't use static buffer for formatting (Ben Noordhuis) + +* linux: introduce io_uring support (Ben Noordhuis) + +* linux: fix academic valgrind warning (Ben Noordhuis) + +* test: disable signal test under ASan and MSan (Ben Noordhuis) + +* linux: add IORING_OP_OPENAT support (Ben Noordhuis) + +* linux: add IORING_OP_CLOSE support (Ben Noordhuis) + +* linux: remove bug workaround for obsolete kernels (Ben Noordhuis) + +* doc: update active maintainers list (Ben Noordhuis) + +* test: add ASSERT_OK (Trevor Norris) + +* src: fix events/events_waiting metrics counter (Trevor Norris) + +* unix,win: add uv_clock_gettime() (Ben Noordhuis) + +* build: remove freebsd and openbsd buildbots (Ben Noordhuis) + +* win: fix race condition in uv__init_console() (sivadeilra) + +* linux: fix logic bug in sqe ring space check (Ben Noordhuis) + +* linux: use io_uring to batch epoll_ctl calls (Ben Noordhuis) + +* macos: update minimum supported version (Santiago Gimeno) + +* docs: fix some typos (cui fliter) + +* unix: use memcpy() instead of type punning (Ben Noordhuis) + +* test: add additional assert (Mohammed Keyvanzadeh) + +* build: export compile_commands.json (Lewis Russell) + +* win,process: write minidumps when sending SIGQUIT (Elliot Saba) + +* unix: constrained_memory should return UINT64_MAX (Tim Besard) + +* unix: handle CQ overflow in iou ring (Santiago Gimeno) + +* unix: remove clang compiler warning pragmas (Ben Noordhuis) + +* win: fix mingw build (gengjiawen) + +* test: fix -Wbool-compare compiler warning (Ben Noordhuis) + +* win: define MiniDumpWithAvxXStateContext always (Santiago Gimeno) + +* freebsd: hard-code UV_ENODATA definition (Santiago Gimeno) + +* linux: work around EOWNERDEAD io_uring kernel bug (Ben Noordhuis) + +* linux: fix WRITEV with lots of bufs using io_uring (Santiago Gimeno) + + +2022.07.12, Version 1.44.2 (Stable), 0c1fa696aa502eb749c2c4735005f41ba00a27b8 + +Changes since version 1.44.1: + +* Add SHA to ChangeLog (Jameson Nash) + +* aix, ibmi: handle server hang when remote sends TCP RST (V-for-Vasili) + +* build: make CI a bit noisier (Jameson Nash) + +* process: reset the signal mask if the fork fails (Jameson Nash) + +* zos: implement cmpxchgi() using assembly (Shuowang (Wayne) Zhang) + +* build: AC_SUBST for AM_CFLAGS (Claes Nästén) + +* ibmi: Implement UDP disconnect (V-for-Vasili) + +* doc: update active maintainers list (Ben Noordhuis) + +* build: fix kFreeBSD build (James McCoy) + +* build: remove Windows 2016 workflows (Darshan Sen) + +* Revert "win,errors: remap ERROR_ACCESS_DENIED to UV_EACCES" (Darshan Sen) + +* unix: simplify getpwuid call (Jameson Nash) + +* build: filter CI by paths and branches (Jameson Nash) + +* build: add iOS to macos CI (Jameson Nash) + +* build: re-enable CI for windows changes (Jameson Nash) + +* process,iOS: fix build breakage in process.c (Denny C. Dai) + +* test: remove unused declarations in tcp_rst test (V-for-Vasili) + +* core: add thread-safe strtok implementation (Guilherme Íscaro) + +* win: fix incompatible-types warning (twosee) + +* test: fix flaky file watcher test (Ben Noordhuis) + +* build: fix AIX xlc autotools build (V-for-Vasili) + +* unix,win: fix UV_RUN_ONCE + uv_idle_stop loop hang (Ben Noordhuis) + +* win: fix unexpected ECONNRESET error on TCP socket (twosee) + +* doc: make sample cross-platform build (gengjiawen) + +* test: separate some static variables by test cases (Hannah Shi) + +* sunos: fs-event callback can be called after uv_close() (Andy Fiddaman) + +* uv: re-register interest in a file after change (Shuowang (Wayne) Zhang) + +* uv: register UV_RENAME event for _RFIM_UNLINK (Shuowang (Wayne) Zhang) + +* uv: register __rfim_event 156 as UV_RENAME (Shuowang (Wayne) Zhang) + +* doc: remove smartos from supported platforms (Ben Noordhuis) + +* macos: avoid posix_spawnp() cwd bug (Jameson Nash) + +* release: check versions of autogen scripts are newer (Jameson Nash) + +* test: rewrite embed test (Ben Noordhuis) + +* openbsd: use utimensat instead of lutimes (tuftedocelot) + +* doc: fix link to uvwget example main() function (blogdaren) + +* unix: use MSG_CMSG_CLOEXEC where supported (Ben Noordhuis) + +* test: remove disabled callback_order test (Ben Noordhuis) + +* win,pipe: fix bugs with pipe resource lifetime management (Jameson Nash) + +* loop: better align order-of-events behavior between platforms (Jameson Nash) + +* aix,test: uv_backend_fd is not supported by poll (V-for-Vasili) + +* kqueue: skip EVFILT_PROC when invalidating fds (chucksilvers) + +* darwin: fix atomic-ops.h ppc64 build (Sergey Fedorov) + +* zos: don't err when killing a zombie process (Shuowang (Wayne) Zhang) + +* zos: avoid fs event callbacks after uv_close() (Shuowang (Wayne) Zhang) + +* zos: correctly format interface addresses names (Shuowang (Wayne) Zhang) + +* zos: add uv_interface_addresses() netmask support (Shuowang (Wayne) Zhang) + +* zos: improve memory management of ip addresses (Shuowang (Wayne) Zhang) + +* tcp,pipe: fail `bind` or `listen` after `close` (theanarkh) + +* zos: implement uv_available_parallelism() (Shuowang (Wayne) Zhang) + +* udp,win: fix UDP compiler warning (Jameson Nash) + +* zos: fix early exit of epoll_wait() (Shuowang (Wayne) Zhang) + +* unix,tcp: fix errno handling in uv__tcp_bind() (Samuel Cabrero) + +* shutdown,unix: reduce code duplication (Jameson Nash) + +* unix: fix c99 comments (Ben Noordhuis) + +* unix: retry tcgetattr/tcsetattr() on EINTR (Ben Noordhuis) + +* docs: update introduction.rst (Ikko Ashimine) + +* unix,stream: optimize uv_shutdown() codepath (Jameson Nash) + +* zos: delay signal handling until after normal i/o (Shuowang (Wayne) Zhang) + +* stream: uv__drain() always needs to stop POLLOUT (Jameson Nash) + +* unix,tcp: allow EINVAL errno from setsockopt in uv_tcp_close_reset() (Stacey + Marshall) + +* win,shutdown: improve how shutdown is dispatched (Jameson Nash) + + +2022.03.09, Version 1.44.1 (Stable), e8b7eb6908a847ffbe6ab2eec7428e43a0aa53a2 + +Changes since version 1.44.0: + +* process: simplify uv__write_int calls (Jameson Nash) + +* macos: don't use thread-unsafe strtok() (Ben Noordhuis) + +* process: fix hang after NOTE_EXIT (Jameson Nash) + + +2022.03.07, Version 1.44.0 (Stable), d2bff508457336d808ba7148b33088f6acbfe0a6 + +Changes since version 1.43.0: + +* darwin: remove EPROTOTYPE error workaround (Ben Noordhuis) + +* doc: fix v1.43.0 changelog entries (cjihrig) + +* win: replace CRITICAL_SECTION+Semaphore with SRWLock (David Machaj) + +* darwin: translate EPROTOTYPE to ECONNRESET (Ben Noordhuis) + +* android: use libc getifaddrs() (Ben Noordhuis) + +* unix: fix STATIC_ASSERT to check what it means to check (Jessica Clarke) + +* unix: ensure struct msghdr is zeroed in recvmmsg (Ondřej Surý) + +* test: test with maximum recvmmsg buffer (Ondřej Surý) + +* unix: don't allow too small thread stack size (Ben Noordhuis) + +* bsd: ensure mutex is initialized (Ben Noordhuis) + +* doc: add gengjiawen as maintainer (gengjiawen) + +* process: monitor for exit with kqueue on BSDs (Jeremy Rose) + +* test: fix flaky uv_fs_lutime test (Momtchil Momtchev) + +* build: fix cmake install locations (Jameson Nash) + +* thread,win: fix C90 style nit (ssrlive) + +* build: rename CFLAGS to AM_CFLAGS (Jameson Nash) + +* doc/guide: update content and sample code (woclass) + +* process,bsd: handle kevent NOTE_EXIT failure (Jameson Nash) + +* test: remove flaky test ipc_closed_handle (Ben Noordhuis) + +* darwin: bump minimum supported version to 10.15 (Ben Noordhuis) + +* win: return fractional seconds in uv_uptime() (Luca Adrian L) + +* build: export uv_a for cmake (WenTao Ou) + +* loop: add pending work to loop-alive check (Jameson Nash) + +* win: use GetTickCount64 for uptime again (Jameson Nash) + +* win: restrict system DLL load paths (jonilaitinen) + +* win,errors: remap ERROR_ACCESS_DENIED to UV_EACCES (Darshan Sen) + +* bench: add `uv_queue_work` ping-pong measurement (Momtchil Momtchev) + +* build: fix error C4146 on MSVC (UMU) + +* test: fix benchmark-ping-udp (Ryan Liptak) + +* win,fs: consider broken pipe error a normal EOF (Momtchil Momtchev) + +* document the values of enum uv_stdio_flags (Paul Evans) + +* win,loop: add missing uv_update_time (twosee) + +* win,fs: avoid closing an invalid handle (Jameson Nash) + +* fix oopsie from + +* doc: clarify android api level (Ben Noordhuis) + +* win: fix style nits [NFC] (Jameson Nash) + +* test: fix flaky udp_mmsg test (Santiago Gimeno) + +* test: fix ipc_send_recv_pipe flakiness (Ben Noordhuis) + +* doc: checkout -> check out (wyckster) + +* core: change uv_get_password uid/gid to unsigned (Jameson Nash) + +* hurd: unbreak build on GNU/Hurd (Vittore F. Scolari) + +* freebsd: use copy_file_range() in uv_fs_sendfile() (David Carlier) + +* test: use closefd in runner-unix.c (Guilherme Íscaro) + +* Reland "macos: use posix_spawn instead of fork" (Jameson Nash) + +* android: fix build error when no ifaddrs.h (ssrlive) + +* unix,win: add uv_available_parallelism() (Ben Noordhuis) + +* process: remove OpenBSD from kevent list (Jameson Nash) + +* zos: fix build breakage (Ben Noordhuis) + +* process: only use F_DUPFD_CLOEXEC if it is defined (Jameson Nash) + +* win,poll: add the MSAFD GUID for AF_UNIX (roflcopter4) + +* unix: simplify uv__cloexec_fcntl() (Ben Noordhuis) + +* doc: add secondary GPG ID for vtjnash (Jameson Nash) + +* unix: remove uv__cloexec_ioctl() (Jameson Nash) + + 2022.01.05, Version 1.43.0 (Stable), 988f2bfc4defb9a85a536a3e645834c161143ee0 Changes since version 1.42.0: @@ -109,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) @@ -2615,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) @@ -4718,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) diff --git a/LICENSE b/LICENSE index 28f17339e29..6566365d4f2 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,3 @@ -libuv is licensed for use as follows: - -==== Copyright (c) 2015-present libuv project contributors. Permission is hereby granted, free of charge, to any person obtaining a copy @@ -20,51 +17,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -==== - -This license applies to parts of libuv originating from the -https://github.com/joyent/libuv repository: - -==== - -Copyright Joyent, Inc. and other Node contributors. All rights reserved. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to -deal in the Software without restriction, including without limitation the -rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -IN THE SOFTWARE. - -==== - -This license applies to all parts of libuv that are not externally -maintained libraries. - -The externally maintained libraries used by libuv are: - - - tree.h (from FreeBSD), copyright Niels Provos. Two clause BSD license. - - - inet_pton and inet_ntop implementations, contained in src/inet.c, are - copyright the Internet Systems Consortium, Inc., and licensed under the ISC - license. - - - stdint-msvc2008.h (from msinttypes), copyright Alexander Chemeris. Three - clause BSD license. - - - pthread-fixes.c, copyright Google Inc. and Sony Mobile Communications AB. - Three clause BSD license. - - - android-ifaddrs.h, android-ifaddrs.c, copyright Berkeley Software Design - Inc, Kenneth MacKay and Emergya (Cloud4all, FP7/2007-2013, grant agreement - n° 289016). Three clause BSD license. diff --git a/LICENSE-extra b/LICENSE-extra new file mode 100644 index 00000000000..7d8ee65fce6 --- /dev/null +++ b/LICENSE-extra @@ -0,0 +1,36 @@ +This license applies to parts of libuv originating from the +https://github.com/joyent/libuv repository: + +==== + +Copyright Joyent, Inc. and other Node contributors. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. + +==== + +This license applies to all parts of libuv that are not externally +maintained libraries. + +The externally maintained libraries used by libuv are: + + - tree.h (from FreeBSD), copyright Niels Provos. Two clause BSD license. + + - inet_pton and inet_ntop implementations, contained in src/inet.c, are + copyright the Internet Systems Consortium, Inc., and licensed under the ISC + license. diff --git a/LINKS.md b/LINKS.md index b8204e56e16..743935cebb8 100644 --- a/LINKS.md +++ b/LINKS.md @@ -1,16 +1,20 @@ ### Apps / VM +* [AliceO2](https://github.com/AliceO2Group/AliceO2): The framework and detector specific code for the reconstruction, calibration and simulation for the ALICE experiment at CERN. +* [Beam](https://github.com/BeamMW/beam): A scalable, confidential cryptocurrency based on the Mimblewimble protocol. * [BIND 9](https://bind.isc.org/): DNS software system including an authoritative server, a recursive resolver and related utilities. * [cjdns](https://github.com/cjdelisle/cjdns): Encrypted self-configuring network/VPN routing engine * [clearskies_core](https://github.com/larroy/clearskies_core): Clearskies file synchronization program. (C++11) * [CMake](https://cmake.org) open-source, cross-platform family of tools designed to build, test and package software -* [Coherence](https://github.com/liesware/coherence/): Cryptographic server for modern web apps. +* [Cocos-Engine](https://github.com/cocos/cocos-engine): The runtime framework for Cocos Creator editor. +* [Coherence](https://github.com/liesware/coherence/): Cryptographic server for modern web apps. +* [DPS8M](https://dps8m.gitlab.io): GE ∕ Honeywell ∕ Bull DPS‑8/M and 6180/L68 mainframe simulator. * [DPS-For-IoT](https://github.com/intel/dps-for-iot/wiki): Fully distributed publish/subscribe protocol. * [HashLink](https://github.com/HaxeFoundation/hashlink): Haxe run-time with libuv support included. * [Haywire](https://github.com/kellabyte/Haywire): Asynchronous HTTP server. * [H2O](https://github.com/h2o/h2o): An optimized HTTP server with support for HTTP/1.x and HTTP/2. * [Igropyr](https://github.com/guenchi/Igropyr): a async Scheme http server base on libuv. * [Julia](http://julialang.org/): Scientific computing programming language -* [Kestrel](https://github.com/aspnet/AspNetCore/tree/master/src/Servers/Kestrel): web server (C# + libuv + [ASP.NET Core](http://github.com/aspnet)) +* [Kestrel](https://github.com/dotnet/aspnetcore/tree/main/src/Servers/Kestrel): web server (C# + libuv + [ASP.NET Core](http://github.com/aspnet)) * [Knot DNS Resolver](https://www.knot-resolver.cz/): A minimalistic DNS caching resolver * [Lever](http://leverlanguage.com): runtime, libuv at the 0.9.0 release * [libnode](https://github.com/plenluno/libnode): C++ implementation of Node.js @@ -30,8 +34,11 @@ * [phastlight](https://github.com/phastlight/phastlight): Command line tool and web server written in PHP 5.3+ inspired by Node.js * [pilight](https://www.pilight.org/): home automation ("domotica") * [pixie](https://github.com/pixie-lang/pixie): clojure-inspired lisp with a tracing JIT +* [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 * [Swish](https://github.com/becls/swish/): Concurrency engine with Erlang-like concepts. Includes a web server. @@ -39,6 +46,7 @@ * [Urbit](http://urbit.org): runtime * [uv_callback](https://github.com/litesync/uv_callback) libuv thread communication * [uvloop](https://github.com/MagicStack/uvloop): Ultra fast implementation of python's asyncio event loop on top of libuv +* [WPILib](https://github.com/wpilibsuite/allwpilib): Libraries for creating robot programs for the roboRIO. * [Wren CLI](https://github.com/wren-lang/wren-cli): For io, process, scheduler and timer modules ### Other @@ -59,6 +67,7 @@ * [lluv](https://github.com/moteus/lua-lluv) * C++11 * [uvpp](https://github.com/larroy/uvpp) - Not complete, exposes very few aspects of `libuv` + * [nsuv](https://github.com/nodesource/nsuv) - Template wrapper focused on enforcing compile-time type safety when propagating data * C++17 * [uvw](https://github.com/skypjack/uvw) - Header-only, event based, tiny and easy to use *libuv* wrapper in modern C++. * Python diff --git a/MAINTAINERS.md b/MAINTAINERS.md index fb7e5ef3417..ff8be88b7b7 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -1,23 +1,16 @@ - # Project Maintainers libuv is currently managed by the following individuals: -* **Anna Henningsen** ([@addaleax](https://github.com/addaleax)) -* **Bartosz Sosnowski** ([@bzoz](https://github.com/bzoz)) * **Ben Noordhuis** ([@bnoordhuis](https://github.com/bnoordhuis)) - GPG key: D77B 1E34 243F BAF0 5F8E 9CC3 4F55 C8C8 46AB 89B9 (pubkey-bnoordhuis) -* **Bert Belder** ([@piscisaureus](https://github.com/piscisaureus)) * **Colin Ihrig** ([@cjihrig](https://github.com/cjihrig)) - GPG key: 94AE 3667 5C46 4D64 BAFA 68DD 7434 390B DBE9 B9C5 (pubkey-cjihrig) - GPG key: 5735 3E0D BDAA A7E8 39B6 6A1A FF47 D5E4 AD8B 4FDC (pubkey-cjihrig-kb) -* **Fedor Indutny** ([@indutny](https://github.com/indutny)) - - GPG key: AF2E EA41 EC34 47BF DD86 FED9 D706 3CCE 19B7 E890 (pubkey-indutny) -* **Imran Iqbal** ([@imran-iq](https://github.com/imran-iq)) - - GPG key: 9DFE AA5F 481B BF77 2D90 03CE D592 4925 2F8E C41A (pubkey-iwuzhere) * **Jameson Nash** ([@vtjnash](https://github.com/vtjnash)) - GPG key: AEAD 0A4B 6867 6775 1A0E 4AEF 34A2 5FB1 2824 6514 (pubkey-vtjnash) -* **John Barboza** ([@jbarz](https://github.com/jbarz)) + - GPG key: CFBB 9CA9 A5BE AFD7 0E2B 3C5A 79A6 7C55 A367 9C8B (pubkey2022-vtjnash) +* **Jiawen Geng** ([@gengjiawen](https://github.com/gengjiawen)) * **Kaoru Takanashi** ([@erw7](https://github.com/erw7)) - GPG Key: 5804 F999 8A92 2AFB A398 47A0 7183 5090 6134 887F (pubkey-erw7) * **Richard Lau** ([@richardlau](https://github.com/richardlau)) @@ -26,6 +19,18 @@ libuv is currently managed by the following individuals: - GPG key: 612F 0EAD 9401 6223 79DF 4402 F28C 3C8D A33C 03BE (pubkey-santigimeno) * **Saúl Ibarra Corretgé** ([@saghul](https://github.com/saghul)) - GPG key: FDF5 1936 4458 319F A823 3DC9 410E 5553 AE9B C059 (pubkey-saghul) +* **Trevor Norris** ([@trevnorris](https://github.com/trevnorris)) + - GPG key: AEFC 279A 0C93 0676 7E58 29A1 251C A676 820D C7F3 (pubkey-trevnorris) + +## Project Maintainers emeriti + +* **Anna Henningsen** ([@addaleax](https://github.com/addaleax)) +* **Bartosz Sosnowski** ([@bzoz](https://github.com/bzoz)) +* **Bert Belder** ([@piscisaureus](https://github.com/piscisaureus)) +* **Fedor Indutny** ([@indutny](https://github.com/indutny)) + - GPG key: AF2E EA41 EC34 47BF DD86 FED9 D706 3CCE 19B7 E890 (pubkey-indutny) +* **Imran Iqbal** ([@imran-iq](https://github.com/imran-iq)) +* **John Barboza** ([@jbarz](https://github.com/jbarz)) ## Storing a maintainer key in Git diff --git a/Makefile.am b/Makefile.am index e91cfb8f03e..9b9e6be7178 100644 --- a/Makefile.am +++ b/Makefile.am @@ -27,8 +27,8 @@ uvinclude_HEADERS = include/uv/errno.h \ CLEANFILES = lib_LTLIBRARIES = libuv.la -libuv_la_CFLAGS = @CFLAGS@ -libuv_la_LDFLAGS = -no-undefined -version-info 1:0:0 +libuv_la_CFLAGS = $(AM_CFLAGS) +libuv_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined -version-info 1:0:0 libuv_la_SOURCES = src/fs-poll.c \ src/heap-inl.h \ src/idna.c \ @@ -38,12 +38,15 @@ libuv_la_SOURCES = src/fs-poll.c \ src/random.c \ src/strscpy.c \ src/strscpy.h \ + src/thread-common.c \ src/threadpool.c \ src/timer.c \ src/uv-data-getter-setters.c \ src/uv-common.c \ src/uv-common.h \ - src/version.c + src/version.c \ + src/strtok.c \ + src/strtok.h if SUNOS # Can't be turned into a CC_CHECK_CFLAGS in configure.ac, it makes compilers @@ -56,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 \ @@ -94,7 +97,6 @@ else # WINNT uvinclude_HEADERS += include/uv/unix.h AM_CPPFLAGS += -I$(top_srcdir)/src/unix libuv_la_SOURCES += src/unix/async.c \ - src/unix/atomic-ops.h \ src/unix/core.c \ src/unix/dl.c \ src/unix/fs.c \ @@ -108,7 +110,6 @@ libuv_la_SOURCES += src/unix/async.c \ src/unix/process.c \ src/unix/random-devurandom.c \ src/unix/signal.c \ - src/unix/spinlock.h \ src/unix/stream.c \ src/unix/tcp.c \ src/unix/thread.c \ @@ -120,18 +121,26 @@ endif # WINNT EXTRA_DIST = test/fixtures/empty_file \ test/fixtures/load_error.node \ test/fixtures/lorem_ipsum.txt \ + test/fixtures/one_file/one_file \ include \ docs \ img \ CONTRIBUTING.md \ LICENSE \ + LICENSE-extra \ README.md TESTS = test/run-tests check_PROGRAMS = test/run-tests -test_run_tests_CFLAGS = +test_run_tests_CFLAGS = $(AM_CFLAGS) + +if WINNT +check-am: test/run-tests_no_ext +test/run-tests_no_ext: test/run-tests$(EXEEXT) + cp test/run-tests$(EXEEXT) test/run-tests_no_ext +endif if SUNOS # Can't be turned into a CC_CHECK_CFLAGS in configure.ac, it makes compilers @@ -139,7 +148,7 @@ if SUNOS test_run_tests_CFLAGS += -pthreads endif -test_run_tests_LDFLAGS = +test_run_tests_LDFLAGS = $(AM_LDFLAGS) test_run_tests_SOURCES = test/blackhole-server.c \ test/echo-server.c \ test/run-tests.c \ @@ -150,7 +159,6 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-async.c \ test/test-async-null-cb.c \ test/test-barrier.c \ - test/test-callback-order.c \ test/test-callback-stack.c \ test/test-close-fd.c \ test/test-close-order.c \ @@ -190,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 \ @@ -250,11 +259,13 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-spawn.c \ test/test-stdio-over-pipes.c \ test/test-strscpy.c \ + test/test-strtok.c \ test/test-tcp-alloc-cb-fail.c \ test/test-tcp-bind-error.c \ test/test-tcp-bind6-error.c \ test/test-tcp-close-accept.c \ test/test-tcp-close-while-connecting.c \ + test/test-tcp-close-after-read-timeout.c \ test/test-tcp-close.c \ test/test-tcp-close-reset.c \ test/test-tcp-create-socket-early.c \ @@ -265,7 +276,9 @@ 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 \ test/test-tcp-unexpected-read.c \ test/test-tcp-oob.c \ @@ -274,11 +287,15 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-tcp-writealot.c \ test/test-tcp-write-fail.c \ test/test-tcp-try-write.c \ + test/test-tcp-write-in-a-row.c \ test/test-tcp-try-write-error.c \ test/test-tcp-write-queue-order.c \ test/test-test-macros.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 \ @@ -309,6 +326,8 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-udp-sendmmsg-error.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 @@ -388,10 +407,7 @@ libuv_la_SOURCES += src/unix/aix-common.c \ endif if ANDROID -uvinclude_HEADERS += include/uv/android-ifaddrs.h libuv_la_CFLAGS += -D_GNU_SOURCE -libuv_la_SOURCES += src/unix/android-ifaddrs.c \ - src/unix/pthread-fixes.c endif if CYGWIN @@ -415,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 \ @@ -457,23 +474,22 @@ endif if HURD uvinclude_HEADERS += include/uv/posix.h -libuv_la_SOURCES += src/unix/no-fsevents.c \ +libuv_la_SOURCES += src/unix/bsd-ifaddrs.c \ + src/unix/no-fsevents.c \ + src/unix/no-proctitle.c \ src/unix/posix-hrtime.c \ - src/unix/posix-poll.c + src/unix/posix-poll.c \ + src/unix/hurd.c endif if LINUX uvinclude_HEADERS += include/uv/linux.h libuv_la_CFLAGS += -D_GNU_SOURCE -libuv_la_SOURCES += src/unix/linux-core.c \ - src/unix/linux-inotify.c \ - src/unix/linux-syscalls.c \ - src/unix/linux-syscalls.h \ +libuv_la_SOURCES += src/unix/linux.c \ src/unix/procfs-exepath.c \ src/unix/proctitle.c \ src/unix/random-getrandom.c \ - src/unix/random-sysctl-linux.c \ - src/unix/epoll.c + src/unix/random-sysctl-linux.c test_run_tests_LDFLAGS += -lutil endif @@ -537,8 +553,7 @@ libuv_la_CFLAGS += -D_UNIX03_THREADS \ -qXPLINK \ -qFLOAT=IEEE libuv_la_LDFLAGS += -qXPLINK -libuv_la_SOURCES += src/unix/pthread-fixes.c \ - src/unix/os390.c \ +libuv_la_SOURCES += src/unix/os390.c \ src/unix/os390-syscalls.c \ src/unix/proctitle.c endif diff --git a/README.md b/README.md index 06486febc28..12c3061a894 100644 --- a/README.md +++ b/README.md @@ -43,8 +43,11 @@ The ABI/API changes can be tracked [here](http://abi-laboratory.pro/tracker/time ## Licensing -libuv is licensed under the MIT license. Check the [LICENSE file](LICENSE). -The documentation is licensed under the CC BY 4.0 license. Check the [LICENSE-docs file](LICENSE-docs). +libuv is licensed under the MIT license. Check the [LICENSE](LICENSE) and +[LICENSE-extra](LICENSE-extra) files. + +The documentation is licensed under the CC BY 4.0 license. Check the +[LICENSE-docs file](LICENSE-docs). ## Community @@ -220,6 +223,27 @@ Make sure that you specify the architecture you wish to build for in the "ARCHS" flag. You can specify more than one by delimiting with a space (e.g. "x86_64 i386"). +### Install with vcpkg + +```bash +$ git clone https://github.com/microsoft/vcpkg.git +$ ./bootstrap-vcpkg.bat # for powershell +$ ./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 diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..32abba81544 --- /dev/null +++ b/SECURITY.md @@ -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. \ No newline at end of file diff --git a/SUPPORTED_PLATFORMS.md b/SUPPORTED_PLATFORMS.md index 87e23823ad6..9597801b919 100644 --- a/SUPPORTED_PLATFORMS.md +++ b/SUPPORTED_PLATFORMS.md @@ -2,17 +2,16 @@ | System | Support type | Supported versions | Notes | |---|---|---|---| -| GNU/Linux | Tier 1 | Linux >= 2.6.32 with glibc >= 2.12 | | -| macOS | Tier 1 | macOS >= 10.7 | | -| Windows | Tier 1 | >= Windows 8 | VS 2015 and later are supported | -| FreeBSD | Tier 1 | >= 10 | | +| GNU/Linux | Tier 1 | Linux >= 3.10 with glibc >= 2.17 | | +| macOS | Tier 1 | macOS >= 11 | Currently supported macOS releases | +| Windows | Tier 1 | >= Windows 10 | VS 2015 and later are supported | +| FreeBSD | Tier 2 | >= 12 | | | AIX | Tier 2 | >= 6 | Maintainers: @libuv/aix | | IBM i | Tier 2 | >= IBM i 7.2 | Maintainers: @libuv/ibmi | | z/OS | Tier 2 | >= V2R2 | Maintainers: @libuv/zos | | Linux with musl | Tier 2 | musl >= 1.0 | | -| SmartOS | Tier 3 | >= 14.4 | | -| Android | Tier 3 | NDK >= r15b | | -| MinGW | Tier 3 | MinGW32 and MinGW-w64 | | +| Android | Tier 3 | NDK >= r15b | Android 7.0, `-DANDROID_PLATFORM=android-24` | +| MinGW | Tier 3 | MinGW-w64 | | | SunOS | Tier 3 | Solaris 121 and later | | | Other | Tier 3 | N/A | | diff --git a/autogen.sh b/autogen.sh index 271c2ee8c33..cf82cc634d4 100755 --- a/autogen.sh +++ b/autogen.sh @@ -14,9 +14,16 @@ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +set -eu cd `dirname "$0"` -if [ "$LIBTOOLIZE" = "" ] && [ "`uname`" = "Darwin" ]; then +if [ "${1:-dev}" = "release" ]; then + export LIBUV_RELEASE=true +else + export LIBUV_RELEASE=false +fi + +if [ "${LIBTOOLIZE:-}" = "" ] && [ "`uname`" = "Darwin" ]; then LIBTOOLIZE=glibtoolize fi @@ -25,9 +32,17 @@ AUTOCONF=${AUTOCONF:-autoconf} AUTOMAKE=${AUTOMAKE:-automake} LIBTOOLIZE=${LIBTOOLIZE:-libtoolize} +aclocal_version=`"$ACLOCAL" --version | head -n 1 | sed 's/[^.0-9]//g'` +autoconf_version=`"$AUTOCONF" --version | head -n 1 | sed 's/[^.0-9]//g'` automake_version=`"$AUTOMAKE" --version | head -n 1 | sed 's/[^.0-9]//g'` automake_version_major=`echo "$automake_version" | cut -d. -f1` automake_version_minor=`echo "$automake_version" | cut -d. -f2` +libtoolize_version=`"$LIBTOOLIZE" --version | head -n 1 | sed 's/[^.0-9]//g'` + +if [ $aclocal_version != $automake_version ]; then + echo "FATAL: aclocal version appears not to be from the same as automake" + exit 1 +fi UV_EXTRA_AUTOMAKE_FLAGS= if test "$automake_version_major" -gt 1 || \ @@ -39,8 +54,22 @@ fi echo "m4_define([UV_EXTRA_AUTOMAKE_FLAGS], [$UV_EXTRA_AUTOMAKE_FLAGS])" \ > m4/libuv-extra-automake-flags.m4 -set -ex -"$LIBTOOLIZE" --copy +(set -x +"$LIBTOOLIZE" --copy --force "$ACLOCAL" -I m4 +) +if $LIBUV_RELEASE; then + "$AUTOCONF" -o /dev/null m4/libuv-check-versions.m4 + echo " +AC_PREREQ($autoconf_version) +AC_INIT([libuv-release-check], [0.0]) +AM_INIT_AUTOMAKE([$automake_version]) +LT_PREREQ($libtoolize_version) +AC_OUTPUT +" > m4/libuv-check-versions.m4 +fi +( +set -x "$AUTOCONF" "$AUTOMAKE" --add-missing --copy +) diff --git a/cmake-toolchains/cross-mingw32.cmake b/cmake-toolchains/cross-mingw32.cmake new file mode 100644 index 00000000000..3fe1dd69ec5 --- /dev/null +++ b/cmake-toolchains/cross-mingw32.cmake @@ -0,0 +1,17 @@ +if(NOT HOST_ARCH) + message(SEND_ERROR "-DHOST_ARCH required to be specified") +endif() + +list(APPEND CMAKE_TRY_COMPILE_PLATFORM_VARIABLES + HOST_ARCH + ) + +SET(CMAKE_SYSTEM_NAME Windows) +set(COMPILER_PREFIX "${HOST_ARCH}-w64-mingw32") +find_program(CMAKE_RC_COMPILER NAMES ${COMPILER_PREFIX}-windres) +find_program(CMAKE_C_COMPILER NAMES ${COMPILER_PREFIX}-gcc) +find_program(CMAKE_CXX_COMPILER NAMES ${COMPILER_PREFIX}-g++) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/configure.ac b/configure.ac index bdb0c75ea50..95a32149ca1 100644 --- a/configure.ac +++ b/configure.ac @@ -13,12 +13,13 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. AC_PREREQ(2.57) -AC_INIT([libuv], [1.43.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]) m4_include([m4/libuv-check-flags.m4]) AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects] UV_EXTRA_AUTOMAKE_FLAGS) +AM_MAINTAINER_MODE([enable]) # pass --disable-maintainer-mode if autotools may be unavailable AC_CANONICAL_HOST AC_ENABLE_SHARED AC_ENABLE_STATIC @@ -28,7 +29,9 @@ AM_PROG_CC_C_O CC_ATTRIBUTE_VISIBILITY([default], [ CC_FLAG_VISIBILITY([CFLAGS="${CFLAGS} -fvisibility=hidden"]) ]) -CC_CHECK_CFLAGS_APPEND([-fno-strict-aliasing]) +# Xlc has a flag "-f". Need to use CC_CHECK_FLAG_SUPPORTED_APPEND so +# we exclude -fno-strict-aliasing for xlc +CC_CHECK_FLAG_SUPPORTED_APPEND([-fno-strict-aliasing]) CC_CHECK_CFLAGS_APPEND([-g]) CC_CHECK_CFLAGS_APPEND([-std=gnu89]) CC_CHECK_CFLAGS_APPEND([-Wall]) @@ -59,7 +62,7 @@ AM_CONDITIONAL([ANDROID], [AS_CASE([$host_os],[linux-android*],[true], [false]) AM_CONDITIONAL([CYGWIN], [AS_CASE([$host_os],[cygwin*], [true], [false])]) AM_CONDITIONAL([DARWIN], [AS_CASE([$host_os],[darwin*], [true], [false])]) AM_CONDITIONAL([DRAGONFLY],[AS_CASE([$host_os],[dragonfly*], [true], [false])]) -AM_CONDITIONAL([FREEBSD], [AS_CASE([$host_os],[*freebsd*], [true], [false])]) +AM_CONDITIONAL([FREEBSD], [AS_CASE([$host_os],[freebsd*], [true], [false])]) AM_CONDITIONAL([HAIKU], [AS_CASE([$host_os],[haiku], [true], [false])]) AM_CONDITIONAL([HURD], [AS_CASE([$host_os],[gnu*], [true], [false])]) AM_CONDITIONAL([LINUX], [AS_CASE([$host_os],[linux*], [true], [false])]) @@ -71,12 +74,12 @@ 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" + LIBS="$LIBS -lws2_32 -lpsapi -liphlpapi -luserenv -luser32 -ldbghelp -lole32 -lshell32" ]) -AS_CASE([$host_os], [netbsd*], [AC_CHECK_LIB([kvm], [kvm_open])]) -AS_CASE([$host_os], [kfreebsd*], [ - LIBS="$LIBS -lfreebsd-glue" +AS_CASE([$host_os], [solaris2.10], [ + CFLAGS="$CFLAGS -DSUNOS_NO_IFADDRS" ]) +AS_CASE([$host_os], [netbsd*], [AC_CHECK_LIB([kvm], [kvm_open])]) AS_CASE([$host_os], [haiku], [ LIBS="$LIBS -lnetwork" ]) @@ -85,4 +88,5 @@ AC_CONFIG_FILES([Makefile libuv.pc]) AC_CONFIG_LINKS([test/fixtures/empty_file:test/fixtures/empty_file]) AC_CONFIG_LINKS([test/fixtures/load_error.node:test/fixtures/load_error.node]) AC_CONFIG_LINKS([test/fixtures/lorem_ipsum.txt:test/fixtures/lorem_ipsum.txt]) +AC_CONFIG_LINKS([test/fixtures/one_file/one_file:test/fixtures/one_file/one_file]) AC_OUTPUT diff --git a/docs/code/.gitignore b/docs/code/.gitignore new file mode 100644 index 00000000000..c46ecde4c6f --- /dev/null +++ b/docs/code/.gitignore @@ -0,0 +1,3 @@ +*/* +!*.c +!*.h diff --git a/docs/code/CMakeLists.txt b/docs/code/CMakeLists.txt new file mode 100644 index 00000000000..3d01991b268 --- /dev/null +++ b/docs/code/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.5) + +project(libuv_sample) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +add_subdirectory("../../" build) + +set(SIMPLE_SAMPLES + cgi + helloworld + dns + detach + default-loop + idle-basic + idle-compute + interfaces + locks + onchange + pipe-echo-server + ref-timer + spawn + tcp-echo-server + thread-create + udp-dhcp + uvcat + uvstop + uvtee + ) +IF (NOT WIN32) + list(APPEND SIMPLE_SAMPLES + signal + progress + queue-cancel + queue-work + tty + tty-gravity + ) +ENDIF() + +foreach (X IN LISTS SIMPLE_SAMPLES) + add_executable(${X} ${X}/main.c) + target_link_libraries(${X} uv_a) +endforeach () + + +FIND_PACKAGE(CURL) +IF(CURL_FOUND) + add_executable(uvwget uvwget/main.c) + target_link_libraries(uvwget uv_a ${CURL_LIBRARIES}) +ENDIF(CURL_FOUND) diff --git a/docs/code/cgi/main.c b/docs/code/cgi/main.c index d2e34265a0a..97422110d35 100644 --- a/docs/code/cgi/main.c +++ b/docs/code/cgi/main.c @@ -15,8 +15,8 @@ void cleanup_handles(uv_process_t *req, int64_t exit_status, int term_signal) { } void invoke_cgi_script(uv_tcp_t *client) { - size_t size = 500; - char path[size]; + char path[500]; + size_t size = sizeof(path); uv_exepath(path, &size); strcpy(path + (strlen(path) - strlen("cgi")), "tick"); diff --git a/docs/code/default-loop/main.c b/docs/code/default-loop/main.c new file mode 100644 index 00000000000..e00a4d2bbe0 --- /dev/null +++ b/docs/code/default-loop/main.c @@ -0,0 +1,12 @@ +#include +#include + +int main() { + uv_loop_t *loop = uv_default_loop(); + + printf("Default loop.\n"); + uv_run(loop, UV_RUN_DEFAULT); + + uv_loop_close(loop); + return 0; +} diff --git a/docs/code/interfaces/main.c b/docs/code/interfaces/main.c index cac12c26617..744a47f26a4 100644 --- a/docs/code/interfaces/main.c +++ b/docs/code/interfaces/main.c @@ -11,17 +11,17 @@ int main() { printf("Number of interfaces: %d\n", count); while (i--) { - uv_interface_address_t interface = info[i]; + uv_interface_address_t interface_a = info[i]; - printf("Name: %s\n", interface.name); - printf("Internal? %s\n", interface.is_internal ? "Yes" : "No"); + printf("Name: %s\n", interface_a.name); + printf("Internal? %s\n", interface_a.is_internal ? "Yes" : "No"); - if (interface.address.address4.sin_family == AF_INET) { - uv_ip4_name(&interface.address.address4, buf, sizeof(buf)); + if (interface_a.address.address4.sin_family == AF_INET) { + uv_ip4_name(&interface_a.address.address4, buf, sizeof(buf)); printf("IPv4 address: %s\n", buf); } - else if (interface.address.address4.sin_family == AF_INET6) { - uv_ip6_name(&interface.address.address6, buf, sizeof(buf)); + else if (interface_a.address.address4.sin_family == AF_INET6) { + uv_ip6_name(&interface_a.address.address6, buf, sizeof(buf)); printf("IPv6 address: %s\n", buf); } diff --git a/docs/code/thread-create/main.c b/docs/code/thread-create/main.c index 70224c1e202..7e345ef0836 100644 --- a/docs/code/thread-create/main.c +++ b/docs/code/thread-create/main.c @@ -1,5 +1,4 @@ #include -#include #include @@ -7,7 +6,7 @@ void hare(void *arg) { int tracklen = *((int *) arg); while (tracklen) { tracklen--; - sleep(1); + uv_sleep(1000); fprintf(stderr, "Hare ran another step\n"); } fprintf(stderr, "Hare done running!\n"); @@ -18,7 +17,7 @@ void tortoise(void *arg) { while (tracklen) { tracklen--; fprintf(stderr, "Tortoise ran another step\n"); - sleep(3); + uv_sleep(3000); } fprintf(stderr, "Tortoise done running!\n"); } diff --git a/docs/code/tty/main.c b/docs/code/tty/main.c index d44ec62ce98..ce3607982a5 100644 --- a/docs/code/tty/main.c +++ b/docs/code/tty/main.c @@ -5,25 +5,27 @@ uv_loop_t *loop; uv_tty_t tty; + int main() { - loop = uv_default_loop(); + 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); } diff --git a/docs/code/udp-dhcp/main.c b/docs/code/udp-dhcp/main.c index fc2ca0c8a8d..4dc28390308 100644 --- a/docs/code/udp-dhcp/main.c +++ b/docs/code/udp-dhcp/main.c @@ -53,7 +53,8 @@ uv_buf_t make_discover_msg() { // HOPS buffer.base[3] = 0x0; // XID 4 bytes - buffer.base[4] = (unsigned int) random(); + if (uv_random(NULL, NULL, &buffer.base[4], 4, 0, NULL)) + abort(); // SECS buffer.base[8] = 0x0; // FLAGS diff --git a/docs/code/uvcat/main.c b/docs/code/uvcat/main.c index b03b09449ac..01923f2ae26 100644 --- a/docs/code/uvcat/main.c +++ b/docs/code/uvcat/main.c @@ -1,7 +1,6 @@ #include #include #include -#include #include void on_read(uv_fs_t *req); diff --git a/docs/code/uvtee/main.c b/docs/code/uvtee/main.c index 6216c2eb4df..be307b9a67f 100644 --- a/docs/code/uvtee/main.c +++ b/docs/code/uvtee/main.c @@ -1,6 +1,5 @@ #include #include -#include #include #include diff --git a/docs/requirements.txt b/docs/requirements.txt index 8386e0178fa..2e310ebe728 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,42 +1,36 @@ # primary -Sphinx==3.5.4 +furo==2023.5.20 +Sphinx==6.1.3 # dependencies -alabaster==0.7.12 -appdirs==1.4.3 -Babel==2.9.0 -CacheControl==0.12.6 -certifi==2019.11.28 -chardet==3.0.4 -colorama==0.4.3 -contextlib2==0.6.0 -distlib==0.3.0 -distro==1.4.0 -docutils==0.16 -html5lib==1.0.1 -idna==2.8 -imagesize==1.2.0 -ipaddr==2.2.0 -Jinja2==2.11.3 -lockfile==0.12.2 -MarkupSafe==1.1.1 -msgpack==0.6.2 -packaging==20.3 -pep517==0.8.2 -progress==1.5 -Pygments==2.8.1 -pyparsing==2.4.6 -pytoml==0.1.21 -pytz==2021.1 -requests==2.22.0 -retrying==1.3.3 -six==1.14.0 -snowballstemmer==2.1.0 -sphinxcontrib-applehelp==1.0.2 +alabaster==0.7.13 +Babel==2.11.0 +beautifulsoup4==4.12.2 +certifi==2022.12.7 +charset-normalizer==3.0.1 +colorama==0.4.6 +docutils==0.19 +idna==3.4 +imagesize==1.4.1 +importlib-metadata==6.0.0 +Jinja2==3.1.2 +livereload==2.6.3 +MarkupSafe==2.1.2 +packaging==23.0 +Pygments==2.14.0 +pytz==2022.7.1 +requests==2.28.2 +six==1.16.0 +snowballstemmer==2.2.0 +soupsieve==2.4.1 +sphinx-autobuild==2021.3.14 +sphinx-basic-ng==1.0.0b2 sphinxcontrib-devhelp==1.0.2 -sphinxcontrib-htmlhelp==1.0.3 +sphinxcontrib-htmlhelp==2.0.0 sphinxcontrib-jsmath==1.0.1 sphinxcontrib-qthelp==1.0.3 -sphinxcontrib-serializinghtml==1.1.4 -urllib3==1.25.8 -webencodings==0.5.1 +sphinxcontrib-serializinghtml==1.1.5 +sphinxcontrib.applehelp==1.0.3 +tornado==6.3.2 +urllib3==1.26.14 +zipp==3.11.0 diff --git a/docs/src/conf.py b/docs/src/conf.py index f6f43253d36..354759a23c5 100644 --- a/docs/src/conf.py +++ b/docs/src/conf.py @@ -118,7 +118,7 @@ def get_libuv_version(): # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'nature' +html_theme = 'furo' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the diff --git a/docs/src/design.rst b/docs/src/design.rst index a23e33a214a..5a20595c3b4 100644 --- a/docs/src/design.rst +++ b/docs/src/design.rst @@ -60,16 +60,15 @@ stages of a loop iteration: :align: center -#. The loop concept of 'now' is updated. The event loop caches the current time at the start of - the event loop tick in order to reduce the number of time-related system calls. +#. The loop concept of 'now' is initially set. + +#. Due timers are run if the loop was run with ``UV_RUN_DEFAULT``. All active timers scheduled + for a time before the loop's concept of *now* get their callbacks called. #. If the loop is *alive* an iteration is started, otherwise the loop will exit immediately. So, when is a loop considered to be *alive*? If a loop has active and ref'd handles, active requests or closing handles it's considered to be *alive*. -#. Due timers are run. All active timers scheduled for a time before the loop's concept of *now* - get their callbacks called. - #. Pending callbacks are called. All I/O callbacks are called right after polling for I/O, for the most part. There are cases, however, in which calling such a callback is deferred for the next loop iteration. If the previous iteration deferred any I/O callback it will be run at this point. @@ -101,9 +100,11 @@ stages of a loop iteration: #. Close callbacks are called. If a handle was closed by calling :c:func:`uv_close` it will get the close callback called. -#. Special case in case the loop was run with ``UV_RUN_ONCE``, as it implies forward progress. - It's possible that no I/O callbacks were fired after blocking for I/O, but some time has passed - so there might be timers which are due, those timers get their callbacks called. +#. The loop concept of 'now' is updated. + +#. Due timers are run. Note that 'now' is not updated again until the next loop iteration. + So if a timer became due while other timers were being processed, it won't be run until + the following event loop iteration. #. Iteration ends. If the loop was run with ``UV_RUN_NOWAIT`` or ``UV_RUN_ONCE`` modes the iteration ends and :c:func:`uv_run` will return. If the loop was run with ``UV_RUN_DEFAULT`` @@ -125,7 +126,7 @@ File I/O Unlike network I/O, there are no platform-specific file I/O primitives libuv could rely on, so the current approach is to run blocking file I/O operations in a thread pool. -For a thorough explanation of the cross-platform file I/O landscape, checkout +For a thorough explanation of the cross-platform file I/O landscape, check out `this post `_. libuv currently uses a global thread pool on which all loops can queue work. 3 types of diff --git a/docs/src/errors.rst b/docs/src/errors.rst index c7240f3546f..a2e94d96a6e 100644 --- a/docs/src/errors.rst +++ b/docs/src/errors.rst @@ -339,6 +339,9 @@ Error constants socket type not supported +.. c:macro:: UV_EUNATCH + + protocol driver not attached API --- diff --git a/docs/src/fs.rst b/docs/src/fs.rst index 0bf2abed5e1..7bc8d0cbfd1 100644 --- a/docs/src/fs.rst +++ b/docs/src/fs.rst @@ -12,6 +12,15 @@ otherwise it will be performed asynchronously. All file operations are run on the threadpool. See :ref:`threadpool` for information on the threadpool size. +Starting with libuv v1.45.0, some file operations on Linux are handed off to +`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. 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. @@ -24,7 +33,8 @@ Data types .. c:type:: uv_timespec_t - Portable equivalent of ``struct timespec``. + Y2K38-unsafe data type for storing times with nanosecond resolution. + Will be replaced with :c:type:`uv_timespec64_t` in libuv v2.0. :: @@ -122,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. :: @@ -140,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; @@ -160,6 +177,10 @@ Data types size_t nentries; } uv_dir_t; +.. c:type:: void (*uv_fs_cb)(uv_fs_t* req) + + Callback called when a request is completed asynchronously. + Public members ^^^^^^^^^^^^^^ @@ -218,7 +239,8 @@ API .. c:function:: int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, uv_fs_cb cb) - Equivalent to :man:`preadv(2)`. + Equivalent to :man:`preadv(2)`. If the `offset` argument is `-1`, then + the current file offset is used and updated. .. warning:: On Windows, under non-MSVC environments (e.g. when GCC or Clang is used @@ -231,7 +253,8 @@ API .. c:function:: int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, uv_fs_cb cb) - Equivalent to :man:`pwritev(2)`. + Equivalent to :man:`pwritev(2)`. If the `offset` argument is `-1`, then + the current file offset is used and updated. .. warning:: On Windows, under non-MSVC environments (e.g. when GCC or Clang is used @@ -441,7 +464,7 @@ API .. c:function:: int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) - Equivalent to :man:`realpath(3)` on Unix. Windows uses `GetFinalPathNameByHandle `_. + Equivalent to :man:`realpath(3)` on Unix. Windows uses `GetFinalPathNameByHandleW `_. The resulting string is stored in `req->ptr`. .. warning:: @@ -463,10 +486,6 @@ API The background story and some more details on these issues can be checked `here `_. - .. note:: - This function is not implemented on Windows XP and Windows Server 2003. - On these systems, UV_ENOSYS is returned. - .. versionadded:: 1.8.0 .. c:function:: int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb) @@ -644,7 +663,7 @@ File open constants .. note:: `UV_FS_O_RANDOM` is only supported on Windows via - `FILE_FLAG_RANDOM_ACCESS `_. + `FILE_FLAG_RANDOM_ACCESS `_. .. c:macro:: UV_FS_O_RDONLY @@ -661,7 +680,7 @@ File open constants .. note:: `UV_FS_O_SEQUENTIAL` is only supported on Windows via - `FILE_FLAG_SEQUENTIAL_SCAN `_. + `FILE_FLAG_SEQUENTIAL_SCAN `_. .. c:macro:: UV_FS_O_SHORT_LIVED @@ -669,7 +688,7 @@ File open constants .. note:: `UV_FS_O_SHORT_LIVED` is only supported on Windows via - `FILE_ATTRIBUTE_TEMPORARY `_. + `FILE_ATTRIBUTE_TEMPORARY `_. .. c:macro:: UV_FS_O_SYMLINK @@ -690,7 +709,7 @@ File open constants .. note:: `UV_FS_O_TEMPORARY` is only supported on Windows via - `FILE_ATTRIBUTE_TEMPORARY `_. + `FILE_ATTRIBUTE_TEMPORARY `_. .. c:macro:: UV_FS_O_TRUNC diff --git a/docs/src/fs_event.rst b/docs/src/fs_event.rst index e28ec625ed6..bfdecdd7329 100644 --- a/docs/src/fs_event.rst +++ b/docs/src/fs_event.rst @@ -39,11 +39,20 @@ Data types .. c:type:: void (*uv_fs_event_cb)(uv_fs_event_t* handle, const char* filename, int events, int status) Callback passed to :c:func:`uv_fs_event_start` which will be called repeatedly - after the handle is started. If the handle was started with a directory the - `filename` parameter will be a relative path to a file contained in the directory. - The `events` parameter is an ORed mask of :c:type:`uv_fs_event` elements. + after the handle is started. -.. c:type:: uv_fs_event + If the handle was started with a directory the `filename` parameter will + 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:enum:`uv_fs_event` elements. + +.. 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. @@ -54,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. @@ -105,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) diff --git a/docs/src/guide/basics.rst b/docs/src/guide/basics.rst index 457fb15cbe8..2b21d730c30 100644 --- a/docs/src/guide/basics.rst +++ b/docs/src/guide/basics.rst @@ -71,7 +71,7 @@ architecture of libuv and its background. If you have no prior experience with either libuv or libev, it is a quick, useful watch. libuv's event loop is explained in more detail in the `documentation -`_. +`_. .. raw:: html @@ -109,6 +109,11 @@ A default loop is provided by libuv and can be accessed using ``uv_default_loop()``. You should use this loop if you only want a single loop. +.. rubric:: default-loop/main.c +.. literalinclude:: ../../code/default-loop/main.c + :language: c + :linenos: + .. note:: node.js uses the default loop as its main loop. If you are writing bindings @@ -119,9 +124,9 @@ loop. Error handling -------------- -Initialization functions or synchronous functions which may fail return a negative number on error. Async functions that may fail will pass a status parameter to their callbacks. The error messages are defined as ``UV_E*`` `constants`_. +Initialization functions or synchronous functions which may fail return a negative number on error. Async functions that may fail will pass a status parameter to their callbacks. The error messages are defined as ``UV_E*`` `constants`_. -.. _constants: http://docs.libuv.org/en/v1.x/errors.html#error-constants +.. _constants: https://docs.libuv.org/en/v1.x/errors.html#error-constants You can use the ``uv_strerror(int)`` and ``uv_err_name(int)`` functions to get a ``const char *`` describing the error or the error name respectively. @@ -134,7 +139,7 @@ Handles and Requests libuv works by the user expressing interest in particular events. This is usually done by creating a **handle** to an I/O device, timer or process. Handles are opaque structs named as ``uv_TYPE_t`` where type signifies what the -handle is used for. +handle is used for. .. rubric:: libuv watchers .. code-block:: c @@ -169,6 +174,16 @@ handle is used for. typedef struct uv_udp_send_s uv_udp_send_t; typedef struct uv_fs_s uv_fs_t; typedef struct uv_work_s uv_work_t; + typedef struct uv_random_s uv_random_t; + + /* None of the above. */ + typedef struct uv_env_item_s uv_env_item_t; + typedef struct uv_cpu_info_s uv_cpu_info_t; + typedef struct uv_interface_address_s uv_interface_address_t; + typedef struct uv_dirent_s uv_dirent_t; + typedef struct uv_passwd_s uv_passwd_t; + typedef struct uv_utsname_s uv_utsname_t; + typedef struct uv_statfs_s uv_statfs_t; Handles represent long-lived objects. Async operations on such handles are diff --git a/docs/src/guide/filesystem.rst b/docs/src/guide/filesystem.rst index 2d5f6cb9250..c0bfbf5b54e 100644 --- a/docs/src/guide/filesystem.rst +++ b/docs/src/guide/filesystem.rst @@ -13,7 +13,7 @@ Simple filesystem read/write is achieved using the ``uv_fs_*`` functions and the watchers registered with the event loop when application interaction is required. -.. _thread pool: http://docs.libuv.org/en/v1.x/threadpool.html#thread-pool-work-scheduling +.. _thread pool: https://docs.libuv.org/en/v1.x/threadpool.html#thread-pool-work-scheduling All filesystem functions have two forms - *synchronous* and *asynchronous*. @@ -66,7 +66,7 @@ The ``result`` field of a ``uv_fs_t`` is the file descriptor in case of the .. literalinclude:: ../../code/uvcat/main.c :language: c :linenos: - :lines: 26-40 + :lines: 26-39 :emphasize-lines: 2,8,12 In the case of a read call, you should pass an *initialized* buffer which will @@ -91,7 +91,7 @@ callbacks. .. literalinclude:: ../../code/uvcat/main.c :language: c :linenos: - :lines: 16-24 + :lines: 17-24 :emphasize-lines: 6 .. warning:: @@ -132,6 +132,7 @@ same patterns as the read/write/open calls, returning the result in the int uv_fs_copyfile(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, uv_fs_cb cb); int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb); int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, uv_fs_cb cb); + int uv_fs_mkstemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, uv_fs_cb cb); int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, uv_fs_cb cb); int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent); @@ -149,6 +150,7 @@ same patterns as the read/write/open calls, returning the result in the int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb); int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb); int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, double mtime, uv_fs_cb cb); + int uv_fs_lutime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb); int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb); int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, uv_fs_cb cb); @@ -158,6 +160,7 @@ same patterns as the read/write/open calls, returning the result in the int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb); int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb); int uv_fs_lchown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb); + int uv_fs_statfs(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); .. _buffers-and-streams: @@ -190,7 +193,7 @@ and freed by the application. .. ERROR:: - THIS PROGRAM DOES NOT ALWAYS WORK, NEED SOMETHING BETTER** + **THIS PROGRAM DOES NOT ALWAYS WORK, NEED SOMETHING BETTER** To demonstrate streams we will need to use ``uv_pipe_t``. This allows streaming local files [#]_. Here is a simple tee utility using libuv. Doing all operations @@ -209,7 +212,7 @@ opened as bidirectional by default. .. literalinclude:: ../../code/uvtee/main.c :language: c :linenos: - :lines: 61-80 + :lines: 62-80 :emphasize-lines: 4,5,15 The third argument of ``uv_pipe_init()`` should be set to 1 for IPC using named @@ -285,6 +288,13 @@ a command whenever any of the watched files change:: ./onchange [file2] ... +.. note:: + + Currently this example only works on OSX and Windows. + Refer to the `notes of uv_fs_event_start`_ function. + +.. _notes of uv_fs_event_start: https://docs.libuv.org/en/v1.x/fs_event.html#c.uv_fs_event_start + The file change notification is started using ``uv_fs_event_init()``: .. rubric:: onchange/main.c - The setup @@ -300,8 +310,8 @@ argument, ``flags``, can be: .. code-block:: c /* - * Flags to be passed to uv_fs_event_start(). - */ + * Flags to be passed to uv_fs_event_start(). + */ enum uv_fs_event_flags { UV_FS_EVENT_WATCH_ENTRY = 1, UV_FS_EVENT_STAT = 2, @@ -319,9 +329,9 @@ The callback will receive the following arguments: #. ``const char *filename`` - If a directory is being monitored, this is the file which was changed. Only non-``null`` on Linux and Windows. May be ``null`` even on those platforms. - #. ``int flags`` - one of ``UV_RENAME`` or ``UV_CHANGE``, or a bitwise OR of - both. - #. ``int status`` - Currently 0. + #. ``int events`` - one of ``UV_RENAME`` or ``UV_CHANGE``, or a bitwise OR of + both. + #. ``int status`` - If ``status < 0``, there is an :ref:`libuv error`. In our example we simply print the arguments and run the command using ``system()``. diff --git a/docs/src/guide/introduction.rst b/docs/src/guide/introduction.rst index 0656e4d852f..91f0fa55818 100644 --- a/docs/src/guide/introduction.rst +++ b/docs/src/guide/introduction.rst @@ -8,7 +8,7 @@ It is meant to cover the main areas of libuv, but is not a comprehensive reference discussing every function and data structure. The `official libuv documentation`_ may be consulted for full details. -.. _official libuv documentation: http://docs.libuv.org/en/v1.x/ +.. _official libuv documentation: https://docs.libuv.org/en/v1.x/ This book is still a work in progress, so sections may be incomplete, but I hope you will enjoy it as it grows. @@ -47,25 +47,23 @@ Since then libuv has continued to mature and become a high quality standalone library for system programming. Users outside of node.js include Mozilla's Rust_ programming language, and a variety_ of language bindings. -This book and the code is based on libuv version `v1.3.0`_. +This book and the code is based on libuv version `v1.42.0`_. Code ---- -All the code from this book is included as part of the source of the book on -Github. `Clone`_/`Download`_ the book, then build libuv:: +All the example code and the source of the book is included as part of +the libuv_ project on GitHub. +Clone or Download libuv_, then build it:: - cd libuv - ./autogen.sh + sh autogen.sh ./configure make There is no need to ``make install``. To build the examples run ``make`` in the -``code/`` directory. +``docs/code/`` directory. -.. _Clone: https://github.com/nikhilm/uvbook -.. _Download: https://github.com/nikhilm/uvbook/downloads -.. _v1.3.0: https://github.com/libuv/libuv/tags +.. _v1.42.0: https://github.com/libuv/libuv/releases/tag/v1.42.0 .. _V8: https://v8.dev .. _libev: http://software.schmorp.de/pkg/libev.html .. _libuv: https://github.com/libuv/libuv diff --git a/docs/src/guide/networking.rst b/docs/src/guide/networking.rst index dcb5643137c..892ade0023e 100644 --- a/docs/src/guide/networking.rst +++ b/docs/src/guide/networking.rst @@ -164,7 +164,7 @@ IPv6 stack only IPv6 sockets can be used for both IPv4 and IPv6 communication. If you want to restrict the socket to IPv6 only, pass the ``UV_UDP_IPV6ONLY`` flag to -``uv_udp_bind`` [#]_. +``uv_udp_bind``. Multicast ~~~~~~~~~ @@ -250,7 +250,6 @@ times, with each address being reported once. ---- .. [#] https://beej.us/guide/bgnet/html/#broadcast-packetshello-world -.. [#] on Windows only supported on Windows Vista and later. .. [#] https://www.tldp.org/HOWTO/Multicast-HOWTO-6.html#ss6.1 .. [#] libuv use the system ``getaddrinfo`` in the libuv threadpool. libuv v0.8.0 and earlier also included c-ares_ as an alternative, but this has been diff --git a/docs/src/guide/processes.rst b/docs/src/guide/processes.rst index c1278f17f13..024af1db486 100644 --- a/docs/src/guide/processes.rst +++ b/docs/src/guide/processes.rst @@ -53,6 +53,8 @@ ID of the child process. The exit callback will be invoked with the *exit status* and the type of *signal* which caused the exit. +Note that it is important **not** to call ``uv_close`` before the exit callback. + .. rubric:: spawn/main.c .. literalinclude:: ../../code/spawn/main.c :language: c @@ -126,7 +128,8 @@ of ``uv_kill`` is:: For processes started using libuv, you may use ``uv_process_kill`` instead, which accepts the ``uv_process_t`` watcher as the first argument, rather than -the pid. In this case, **remember to call** ``uv_close`` on the watcher. +the pid. In this case, **remember to call** ``uv_close`` on the watcher _after_ +the exit callback has been called. Signals ------- @@ -330,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. diff --git a/docs/src/guide/utilities.rst b/docs/src/guide/utilities.rst index 4657b1b0bf6..44e4370dff1 100644 --- a/docs/src/guide/utilities.rst +++ b/docs/src/guide/utilities.rst @@ -220,7 +220,7 @@ progress with the download whenever libuv notifies of I/O readiness. .. literalinclude:: ../../code/uvwget/main.c :language: c :linenos: - :lines: 1-9,140- + :lines: 1-9,142- :emphasize-lines: 7,21,24-25 The way each library is integrated with libuv will vary. In the case of @@ -235,7 +235,7 @@ Our downloader is to be invoked as:: $ ./uvwget [url1] [url2] ... -So we add each argument as an URL +So we add each argument as a URL .. rubric:: uvwget/main.c - Adding urls .. literalinclude:: ../../code/uvwget/main.c @@ -363,7 +363,7 @@ to get the error message. argument. ``init_plugin_function`` is a function pointer to the sort of function we are looking for in the application's plugins. -.. _shared libraries: https://en.wikipedia.org/wiki/Shared_library#Shared_libraries +.. _shared libraries: https://en.wikipedia.org/wiki/Shared_library TTY --- diff --git a/docs/src/handle.rst b/docs/src/handle.rst index 0edb7d7adf2..2b1b8eec968 100644 --- a/docs/src/handle.rst +++ b/docs/src/handle.rst @@ -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 @@ -153,6 +153,9 @@ API In-progress requests, like uv_connect_t or uv_write_t, are cancelled and have their callbacks called asynchronously with status=UV_ECANCELED. + `close_cb` can be `NULL` in cases where no cleanup or deallocation is + necessary. + .. c:function:: void uv_ref(uv_handle_t* handle) Reference the given handle. References are idempotent, that is, if a handle @@ -245,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`. diff --git a/docs/src/loop.rst b/docs/src/loop.rst index 0f5ddfb3ca2..d1f41e1c9f4 100644 --- a/docs/src/loop.rst +++ b/docs/src/loop.rst @@ -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`. diff --git a/docs/src/metrics.rst b/docs/src/metrics.rst index 696c620d192..0141d03286b 100644 --- a/docs/src/metrics.rst +++ b/docs/src/metrics.rst @@ -4,8 +4,46 @@ Metrics operations ====================== -libuv provides a metrics API to track the amount of time the event loop has -spent idle in the kernel's event provider. +libuv provides a metrics API to track various internal operations of the event +loop. + + +Data types +---------- + +.. c:type:: uv_metrics_t + + The struct that contains event loop metrics. It is recommended to retrieve + these metrics in a :c:type:`uv_prepare_cb` in order to make sure there are + no inconsistencies with the metrics counters. + + :: + + typedef struct { + uint64_t loop_count; + uint64_t events; + uint64_t events_waiting; + /* private */ + uint64_t* reserved[13]; + } uv_metrics_t; + + +Public members +^^^^^^^^^^^^^^ + +.. c:member:: uint64_t uv_metrics_t.loop_count + + Number of event loop iterations. + +.. c:member:: uint64_t uv_metrics_t.events + + Number of events that have been processed by the event handler. + +.. c:member:: uint64_t uv_metrics_t.events_waiting + + Number of events that were waiting to be processed when the event provider + was called. + API --- @@ -25,3 +63,9 @@ API :c:type:`UV_METRICS_IDLE_TIME`. .. versionadded:: 1.39.0 + +.. c:function:: int uv_metrics_info(uv_loop_t* loop, uv_metrics_t* metrics) + + Copy the current set of event loop metrics to the ``metrics`` pointer. + + .. versionadded:: 1.45.0 diff --git a/docs/src/misc.rst b/docs/src/misc.rst index 8017e8e1596..db95e2dde83 100644 --- a/docs/src/misc.rst +++ b/docs/src/misc.rst @@ -73,7 +73,8 @@ Data types .. c:type:: uv_timeval_t - Data type for storing times. + Y2K38-unsafe data type for storing times with microsecond resolution. + Will be replaced with :c:type:`uv_timeval64_t` in libuv v2.0. :: @@ -84,7 +85,7 @@ Data types .. c:type:: uv_timeval64_t - Alternative data type for storing times. + Y2K38-safe data type for storing times with microsecond resolution. :: @@ -93,6 +94,28 @@ Data types int32_t tv_usec; } uv_timeval64_t; +.. c:type:: uv_timespec64_t + + Y2K38-safe data type for storing times with nanosecond resolution. + + :: + + typedef struct { + int64_t tv_sec; + int32_t tv_nsec; + } uv_timespec64_t; + +.. c:enum:: uv_clock_id + + Clock source for :c:func:`uv_clock_gettime`. + + :: + + typedef enum { + UV_CLOCK_MONOTONIC, + UV_CLOCK_REALTIME + } uv_clock_id; + .. c:type:: uv_rusage_t Data type for resource usage results. @@ -119,7 +142,10 @@ Data types } uv_rusage_t; Members marked with `(X)` are unsupported on Windows. - See :man:`getrusage(2)` for supported fields on Unix + See :man:`getrusage(2)` for supported fields on UNIX-like platforms. + + The maximum resident set size is reported in kilobytes, the unit most + platforms use natively. .. c:type:: uv_cpu_info_t @@ -173,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. @@ -211,7 +249,7 @@ API type of the stdio streams. For :man:`isatty(3)` equivalent functionality use this function and test - for ``UV_TTY``. + for `UV_TTY`. .. c:function:: int uv_replace_allocator(uv_malloc_func malloc_func, uv_realloc_func realloc_func, uv_calloc_func calloc_func, uv_free_func free_func) @@ -225,8 +263,8 @@ API after all resources have been freed and thus libuv doesn't reference any allocated memory chunk. - On success, it returns 0, if any of the function pointers is NULL it - returns UV_EINVAL. + On success, it returns 0, if any of the function pointers is `NULL` it + returns `UV_EINVAL`. .. warning:: There is no protection against changing the allocator multiple times. If the user changes it they are responsible for making @@ -312,7 +350,7 @@ API .. c:function:: int uv_uptime(double* uptime) - Gets the current system uptime. + Gets the current system uptime. Depending on the system full or fractional seconds are returned. .. c:function:: int uv_getrusage(uv_rusage_t* rusage) @@ -322,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. @@ -334,15 +383,41 @@ API .. versionadded:: 1.16.0 +.. c:function:: unsigned int uv_available_parallelism(void) + + Returns an estimate of the default amount of parallelism a program should + use. Always returns a non-zero value. + + On Linux, inspects the calling thread's CPU affinity mask to determine if + it has been pinned to specific CPUs. + + On Windows, the available parallelism may be underreported on systems with + more than 64 logical CPUs. + + On other platforms, reports the number of CPUs that the operating system + considers to be online. + + .. versionadded:: 1.44.0 + .. c:function:: int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) Gets information about the CPUs on the system. The `cpu_infos` array will have `count` elements and needs to be freed with :c:func:`uv_free_cpu_info`. + Use :c:func:`uv_available_parallelism` if you need to know how many CPUs + are available for threads or child processes. + .. c:function:: void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) Frees the `cpu_infos` array previously allocated with :c:func:`uv_cpu_info`. +.. c:function:: int uv_cpumask_size(void) + + Returns the maximum size of the mask used for process/thread affinities, + or `UV_ENOTSUP` if affinities are not supported on the current platform. + + .. versionadded:: 1.45.0 + .. c:function:: int uv_interface_addresses(uv_interface_address_t** addresses, int* count) Gets address information about the network interfaces on the system. An @@ -514,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`. @@ -522,18 +626,21 @@ API .. c:function:: uint64_t uv_get_free_memory(void) - Gets the amount of free memory available in the system, as reported by the kernel (in bytes). + Gets the amount of free memory available in the system, as reported by + the kernel (in bytes). Returns 0 when unknown. .. c:function:: uint64_t uv_get_total_memory(void) Gets the total amount of physical memory in the system (in bytes). + Returns 0 when unknown. .. c:function:: uint64_t uv_get_constrained_memory(void) - Gets the amount of memory available to the process (in bytes) based on + Gets the total amount of memory available to the process (in bytes) based on limits imposed by the OS. If there is no such constraint, or the constraint - is unknown, `0` is returned. Note that it is not unusual for this value to - be less than or greater than :c:func:`uv_get_total_memory`. + is unknown, `0` is returned. If there is a constraining mechanism, but there + is no constraint set, `UINT64_MAX` is returned. Note that it is not unusual + for this value to be less than or greater than :c:func:`uv_get_total_memory`. .. note:: This function currently only returns a non-zero value on Linux, based @@ -541,9 +648,23 @@ API .. versionadded:: 1.29.0 +.. c:function:: uint64_t uv_get_available_memory(void) + + Gets the amount of free memory that is still available to the process (in bytes). + This differs from :c:func:`uv_get_free_memory` in that it takes into account any + limits imposed by the OS. If there is no such constraint, or the constraint + is unknown, the amount returned will be identical to :c:func:`uv_get_free_memory`. + + .. note:: + This function currently only returns a value that is different from + what :c:func:`uv_get_free_memory` reports on Linux, based + on cgroups if it is present. + + .. versionadded:: 1.45.0 + .. c:function:: uint64_t uv_hrtime(void) - Returns the current high-resolution real time. This is expressed in + Returns the current high-resolution timestamp. This is expressed in nanoseconds. It is relative to an arbitrary time in the past. It is not related to the time of day and therefore not subject to clock drift. The primary use is for measuring performance between intervals. @@ -552,6 +673,19 @@ API Not every platform can support nanosecond resolution; however, this value will always be in nanoseconds. +.. c:function:: int uv_clock_gettime(uv_clock_id clock_id, uv_timespec64_t* ts) + + Obtain the current system time from a high-resolution real-time or monotonic + clock source. + + The real-time clock counts from the UNIX epoch (1970-01-01) and is subject + to time adjustments; it can jump back in time. + + The monotonic clock counts from an arbitrary point in the past and never + jumps back in time. + + .. versionadded:: 1.45.0 + .. c:function:: void uv_print_all_handles(uv_loop_t* loop, FILE* stream) Prints all handles associated with the given `loop` to the given `stream`. @@ -757,3 +891,50 @@ API Causes the calling thread to sleep for `msec` milliseconds. .. versionadded:: 1.34.0 + +String manipulation functions +----------------------------- + +These string utilities are needed internally for dealing with Windows, and are +exported to allow clients to work uniformly with this data when the libuv API +is not complete. + +.. c:function:: size_t uv_utf16_length_as_wtf8(const uint16_t* utf16, ssize_t utf16_len) + + Get the length of a UTF-16 (or UCS-2) `utf16` value after converting it to + WTF-8. If `utf16` is NUL terminated, `utf16_len` can be set to -1, + otherwise it must be specified. + + .. versionadded:: 1.47.0 + +.. c:function:: int uv_utf16_to_wtf8(const uint16_t* utf16, ssize_t utf16_len, char** wtf8_ptr, size_t* wtf8_len_ptr) + + Convert UTF-16 (or UCS-2) data in `utf16` to WTF-8 data in `*wtf8_ptr`. The + `utf16_len` count (in characters) gives the length of `utf16`. If `utf16` + is NUL terminated, `utf16_len` can be set to -1, otherwise it must be + specified. If `wtf8_ptr` is `NULL`, no result will be computed, but the + length (equal to `uv_utf16_length_as_wtf8`) will be stored in `wtf8_ptr`. + If `*wtf8_ptr` is `NULL`, space for the conversion will be allocated and + returned in `wtf8_ptr` and the length will be returned in `wtf8_len_ptr`. + Otherwise, the length of `*wtf8_ptr` must be passed in `wtf8_len_ptr`. The + `wtf8_ptr` must contain an extra space for an extra NUL after the result. + If the result is truncated, `UV_ENOBUFS` will be returned and + `wtf8_len_ptr` will be the length of the required `wtf8_ptr` to contain the + whole result. + + .. versionadded:: 1.47.0 + +.. c:function:: ssize_t uv_wtf8_length_as_utf16(const char* wtf8) + + Get the length in characters of a NUL-terminated WTF-8 `wtf8` value + after converting it to UTF-16 (or UCS-2), including NUL terminator. + + .. versionadded:: 1.47.0 + +.. c:function:: void uv_wtf8_to_utf16(const char* utf8, uint16_t* utf16, size_t utf16_len) + + Convert NUL-terminated WTF-8 data in `wtf8` to UTF-16 (or UCS-2) data + in `utf16`. The `utf16_len` count (in characters) must include space + for the NUL terminator. + + .. versionadded:: 1.47.0 diff --git a/docs/src/pipe.rst b/docs/src/pipe.rst index 5fa83b80d36..4abdc65e715 100644 --- a/docs/src/pipe.rst +++ b/docs/src/pipe.rst @@ -55,17 +55,61 @@ API Bind the pipe to a file path (Unix) or a name (Windows). + Does not support Linux abstract namespace sockets, + unlike :c:func:`uv_pipe_bind2`. + + Alias for ``uv_pipe_bind2(handle, name, strlen(name), 0)``. + + .. note:: + Paths on Unix get truncated to ``sizeof(sockaddr_un.sun_path)`` bytes, + typically between 92 and 108 bytes. + +.. c:function:: int uv_pipe_bind2(uv_pipe_t* handle, const char* name, size_t namelen, unsigned int flags) + + Bind the pipe to a file path (Unix) or a name (Windows). + + ``flags`` must be zero or ``UV_PIPE_NO_TRUNCATE``. Returns ``UV_EINVAL`` + for unsupported flags without performing the bind operation. + + Supports Linux abstract namespace sockets. ``namelen`` must include + the leading nul byte but not the trailing nul byte. + + .. versionadded:: 1.46.0 + .. note:: - Paths on Unix get truncated to ``sizeof(sockaddr_un.sun_path)`` bytes, typically between - 92 and 108 bytes. + Paths on Unix get truncated to ``sizeof(sockaddr_un.sun_path)`` bytes, + typically between 92 and 108 bytes, unless the ``UV_PIPE_NO_TRUNCATE`` + flag is specified, in which case an ``UV_EINVAL`` error is returned. .. c:function:: void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, const char* name, uv_connect_cb cb) - Connect to the Unix domain socket or the named pipe. + Connect to the Unix domain socket or the Windows named pipe. + + Does not support Linux abstract namespace sockets, + unlike :c:func:`uv_pipe_connect2`. + + Alias for ``uv_pipe_connect2(req, handle, name, strlen(name), 0, cb)``. + + .. note:: + Paths on Unix get truncated to ``sizeof(sockaddr_un.sun_path)`` bytes, + typically between 92 and 108 bytes. + +.. c:function:: void uv_pipe_connect2(uv_connect_t* req, uv_pipe_t* handle, const char* name, size_t namelen, unsigned int flags, uv_connect_cb cb) + + Connect to the Unix domain socket or the Windows named pipe. + + ``flags`` must be zero or ``UV_PIPE_NO_TRUNCATE``. Returns ``UV_EINVAL`` + for unsupported flags without performing the connect operation. + + Supports Linux abstract namespace sockets. ``namelen`` must include + the leading nul byte but not the trailing nul byte. + + .. versionadded:: 1.46.0 .. note:: - Paths on Unix get truncated to ``sizeof(sockaddr_un.sun_path)`` bytes, typically between - 92 and 108 bytes. + Paths on Unix get truncated to ``sizeof(sockaddr_un.sun_path)`` bytes, + typically between 92 and 108 bytes, unless the ``UV_PIPE_NO_TRUNCATE`` + flag is specified, in which case an ``UV_EINVAL`` error is returned. .. c:function:: int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size) diff --git a/docs/src/poll.rst b/docs/src/poll.rst index 93a101ec686..b598f0737be 100644 --- a/docs/src/poll.rst +++ b/docs/src/poll.rst @@ -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 @@ -101,7 +101,9 @@ API with one of the `UV_E*` error codes (see :ref:`errors`). The user should not close the socket while the handle is active. If the user does that anyway, the callback *may* be called reporting an error status, but this is - **not** guaranteed. + **not** guaranteed. If `status == UV_EBADF` polling is discontinued for the + file handle and no further events will be reported. The user should + then call :c:func:`uv_close` on the handle. .. note:: Calling :c:func:`uv_poll_start` on a handle that is already active is diff --git a/docs/src/process.rst b/docs/src/process.rst index ea6c4b9ad28..f15dcf610f5 100644 --- a/docs/src/process.rst +++ b/docs/src/process.rst @@ -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 @@ -109,10 +116,39 @@ Data types :: typedef enum { + /* + * The following four options are mutually-exclusive, and define + * the operation to perform for the corresponding file descriptor + * in the child process: + */ + + /* + * No file descriptor will be provided (or redirected to + * `/dev/null` if it is fd 0, 1 or 2). + */ UV_IGNORE = 0x00, + + /* + * Open a new pipe into `data.stream`, per the flags below. The + * `data.stream` field must point to a uv_pipe_t object that has + * been initialized with `uv_pipe_init(loop, data.stream, ipc);`, + * but not yet opened or connected. + /* UV_CREATE_PIPE = 0x01, + + /* + * The child process will be given a duplicate of the parent's + * file descriptor given by `data.fd`. + */ UV_INHERIT_FD = 0x02, + + /* + * The child process will be given a duplicate of the parent's + * file descriptor being used by the stream handle given by + * `data.stream`. + */ UV_INHERIT_STREAM = 0x04, + /* * When UV_CREATE_PIPE is specified, UV_READABLE_PIPE and UV_WRITABLE_PIPE * determine the direction of flow, from the child process' perspective. Both @@ -120,6 +156,7 @@ Data types */ UV_READABLE_PIPE = 0x10, UV_WRITABLE_PIPE = 0x20, + /* * When UV_CREATE_PIPE is specified, specifying UV_NONBLOCK_PIPE opens the * handle in non-blocking mode in the child. This may cause loss of data, @@ -153,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 @@ -166,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 @@ -232,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 diff --git a/docs/src/request.rst b/docs/src/request.rst index a0414431b0e..aacabe026a7 100644 --- a/docs/src/request.rst +++ b/docs/src/request.rst @@ -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`. diff --git a/docs/src/static/loop_iteration.png b/docs/src/static/loop_iteration.png index e769cf338b4..1545f84a8dc 100644 Binary files a/docs/src/static/loop_iteration.png and b/docs/src/static/loop_iteration.png differ diff --git a/docs/src/tcp.rst b/docs/src/tcp.rst index cccc86bbfc0..f9b203c4199 100644 --- a/docs/src/tcp.rst +++ b/docs/src/tcp.rst @@ -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. + + :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. - `flags` can contain ``UV_TCP_IPV6ONLY``, in which case dual-stack support - is disabled and only IPv6 is used. + :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) diff --git a/docs/src/threading.rst b/docs/src/threading.rst index 7ca1d4b7a58..2edf3a89938 100644 --- a/docs/src/threading.rst +++ b/docs/src/threading.rst @@ -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. @@ -88,10 +96,86 @@ Threads .. versionadded:: 1.26.0 +.. c:function:: int uv_thread_setaffinity(uv_thread_t* tid, char* cpumask, char* oldmask, size_t mask_size) + + Sets the specified thread's affinity to cpumask, which is specified in + bytes. Optionally returning the previous affinity setting in oldmask. + On Unix, uses :man:`pthread_getaffinity_np(3)` to get the affinity setting + and maps the cpu_set_t to bytes in oldmask. Then maps the bytes in cpumask + to a cpu_set_t and uses :man:`pthread_setaffinity_np(3)`. On Windows, maps + the bytes in cpumask to a bitmask and uses SetThreadAffinityMask() which + returns the previous affinity setting. + + The mask_size specifies the number of entries (bytes) in cpumask / oldmask, + and must be greater-than-or-equal-to :c:func:`uv_cpumask_size`. + + .. note:: + Thread affinity setting is not atomic on Windows. Unsupported on macOS. + + .. versionadded:: 1.45.0 + +.. c:function:: int uv_thread_getaffinity(uv_thread_t* tid, char* cpumask, size_t mask_size) + + Gets the specified thread's affinity setting. On Unix, this maps the + cpu_set_t returned by :man:`pthread_getaffinity_np(3)` to bytes in cpumask. + + The mask_size specifies the number of entries (bytes) in cpumask, + and must be greater-than-or-equal-to :c:func:`uv_cpumask_size`. + + .. note:: + Thread affinity getting is not atomic on Windows. Unsupported on macOS. + + .. versionadded:: 1.45.0 + +.. c:function:: int uv_thread_getcpu(void) + + Gets the CPU number on which the calling thread is running. + + .. note:: + Currently only implemented on Windows, Linux and FreeBSD. + + .. versionadded:: 1.45.0 + .. c:function:: uv_thread_t uv_thread_self(void) .. 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 ^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/src/threadpool.rst b/docs/src/threadpool.rst index cf6cdc1be0f..05f31d2ccf3 100644 --- a/docs/src/threadpool.rst +++ b/docs/src/threadpool.rst @@ -14,6 +14,11 @@ is 1024). .. versionchanged:: 1.30.0 the maximum UV_THREADPOOL_SIZE allowed was increased from 128 to 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 diff --git a/docs/src/timer.rst b/docs/src/timer.rst index 070fa79da9d..474c6b8c4cd 100644 --- a/docs/src/timer.rst +++ b/docs/src/timer.rst @@ -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, diff --git a/docs/src/tty.rst b/docs/src/tty.rst index f1acfdc1372..7a2235210bf 100644 --- a/docs/src/tty.rst +++ b/docs/src/tty.rst @@ -98,7 +98,7 @@ API .. c:function:: int uv_tty_set_mode(uv_tty_t* handle, uv_tty_mode_t mode) .. versionchanged:: 1.2.0: the mode is specified as a - :c:type:`uv_tty_mode_t` value. + :c:enum:`uv_tty_mode_t` value. Set the TTY using the specified terminal mode. diff --git a/docs/src/udp.rst b/docs/src/udp.rst index 009767d55dc..5f225e5cda4 100644 --- a/docs/src/udp.rst +++ b/docs/src/udp.rst @@ -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 @@ -56,14 +58,26 @@ Data types /* * Indicates if IP_RECVERR/IPV6_RECVERR will be set when binding the handle. * This sets IP_RECVERR for IPv4 and IPV6_RECVERR for IPv6 UDP sockets on - * Linux. This stops the Linux kernel from supressing some ICMP error messages + * Linux. This stops the Linux kernel from suppressing some ICMP error messages * and enables full ICMP error reporting for faster failover. * This flag is no-op on platforms other than Linux. */ 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 diff --git a/include/uv.h b/include/uv.h index 606083c87de..f0ec376b607 100644 --- a/include/uv.h +++ b/include/uv.h @@ -31,6 +31,7 @@ extern "C" { #error "Define either BUILDING_UV_SHARED or USING_UV_SHARED, not both." #endif +#ifndef UV_EXTERN #ifdef _WIN32 /* Windows - set up dll import/export decorators. */ # if defined(BUILDING_UV_SHARED) @@ -50,17 +51,19 @@ extern "C" { #else # define UV_EXTERN /* nothing */ #endif +#endif /* UV_EXTERN */ #include "uv/errno.h" #include "uv/version.h" #include #include +#include -#if defined(_MSC_VER) && _MSC_VER < 1600 -# include "uv/stdint-msvc2008.h" -#else -# include -#endif +/* Internal type, do not use. */ +struct uv__queue { + struct uv__queue* next; + struct uv__queue* prev; +}; #if defined(_WIN32) # include "uv/win.h" @@ -152,6 +155,9 @@ extern "C" { XX(EFTYPE, "inappropriate file type or format") \ XX(EILSEQ, "illegal byte sequence") \ 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) \ @@ -247,12 +253,17 @@ typedef struct uv_cpu_info_s uv_cpu_info_t; typedef struct uv_interface_address_s uv_interface_address_t; typedef struct uv_dirent_s uv_dirent_t; typedef struct uv_passwd_s uv_passwd_t; +typedef struct uv_group_s uv_group_t; typedef struct uv_utsname_s uv_utsname_t; typedef struct uv_statfs_s uv_statfs_t; +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 { @@ -282,13 +293,13 @@ UV_EXTERN int uv_loop_init(uv_loop_t* loop); UV_EXTERN int uv_loop_close(uv_loop_t* loop); /* * NOTE: - * This function is DEPRECATED (to be removed after 0.12), users should + * This function is DEPRECATED, users should * allocate the loop manually and use uv_loop_init instead. */ UV_EXTERN uv_loop_t* uv_loop_new(void); /* * NOTE: - * This function is DEPRECATED (to be removed after 0.12). Users should use + * This function is DEPRECATED. Users should use * uv_loop_close and free the memory manually instead. */ UV_EXTERN void uv_loop_delete(uv_loop_t*); @@ -344,11 +355,32 @@ typedef void (*uv_random_cb)(uv_random_t* req, void* buf, size_t buflen); +typedef enum { + UV_CLOCK_MONOTONIC, + UV_CLOCK_REALTIME +} uv_clock_id; + +/* XXX(bnoordhuis) not 2038-proof, https://github.com/libuv/libuv/issues/3864 */ typedef struct { long tv_sec; long tv_nsec; } uv_timespec_t; +typedef struct { + int64_t tv_sec; + int32_t tv_nsec; +} uv_timespec64_t; + +/* XXX(bnoordhuis) not 2038-proof, https://github.com/libuv/libuv/issues/3864 */ +typedef struct { + long tv_sec; + long tv_usec; +} uv_timeval_t; + +typedef struct { + int64_t tv_sec; + int32_t tv_usec; +} uv_timeval64_t; typedef struct { uint64_t st_dev; @@ -437,7 +469,7 @@ struct uv_shutdown_s { uv_handle_type type; \ /* private */ \ uv_close_cb close_cb; \ - void* handle_queue[2]; \ + struct uv__queue handle_queue; \ union { \ int fd; \ void* reserved[4]; \ @@ -575,7 +607,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, @@ -616,10 +659,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, @@ -642,6 +688,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. */ @@ -718,6 +776,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); @@ -779,6 +843,10 @@ inline int uv_tty_set_mode(uv_tty_t* handle, int mode) { UV_EXTERN uv_handle_type uv_guess_handle(uv_file file); +enum { + UV_PIPE_NO_TRUNCATE = 1u << 0 +}; + /* * uv_pipe_t is a subclass of uv_stream_t. * @@ -795,10 +863,20 @@ struct uv_pipe_s { UV_EXTERN int uv_pipe_init(uv_loop_t*, uv_pipe_t* handle, int ipc); UV_EXTERN int uv_pipe_open(uv_pipe_t*, uv_file file); UV_EXTERN int uv_pipe_bind(uv_pipe_t* handle, const char* name); +UV_EXTERN int uv_pipe_bind2(uv_pipe_t* handle, + const char* name, + size_t namelen, + unsigned int flags); UV_EXTERN void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, const char* name, uv_connect_cb cb); +UV_EXTERN int uv_pipe_connect2(uv_connect_t* req, + uv_pipe_t* handle, + const char* name, + size_t namelen, + unsigned int flags, + uv_connect_cb cb); UV_EXTERN int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size); @@ -1063,7 +1141,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) }; /* @@ -1133,12 +1218,18 @@ struct uv_interface_address_s { struct uv_passwd_s { char* username; - long uid; - long gid; + unsigned long uid; + unsigned long gid; char* shell; char* homedir; }; +struct uv_group_s { + char* groupname; + unsigned long gid; + char** members; +}; + struct uv_utsname_s { char sysname[256]; char release[256]; @@ -1184,16 +1275,6 @@ UV_EXTERN int uv_uptime(double* uptime); UV_EXTERN uv_os_fd_t uv_get_osfhandle(int fd); UV_EXTERN int uv_open_osfhandle(uv_os_fd_t os_fd); -typedef struct { - long tv_sec; - long tv_usec; -} uv_timeval_t; - -typedef struct { - int64_t tv_sec; - int32_t tv_usec; -} uv_timeval64_t; - typedef struct { uv_timeval_t ru_utime; /* user CPU time used */ uv_timeval_t ru_stime; /* system CPU time used */ @@ -1214,11 +1295,15 @@ 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); UV_EXTERN int uv_os_get_passwd(uv_passwd_t* pwd); UV_EXTERN void uv_os_free_passwd(uv_passwd_t* pwd); +UV_EXTERN int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid); +UV_EXTERN int uv_os_get_group(uv_group_t* grp, uv_uid_t gid); +UV_EXTERN void uv_os_free_group(uv_group_t* grp); UV_EXTERN uv_pid_t uv_os_getpid(void); UV_EXTERN uv_pid_t uv_os_getppid(void); @@ -1242,8 +1327,21 @@ 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); +UV_EXTERN int uv_cpumask_size(void); UV_EXTERN int uv_interface_addresses(uv_interface_address_t** addresses, int* count); @@ -1276,6 +1374,15 @@ UV_EXTERN int uv_os_gethostname(char* buffer, size_t* size); UV_EXTERN int uv_os_uname(uv_utsname_t* buffer); +struct uv_metrics_s { + uint64_t loop_count; + uint64_t events; + uint64_t events_waiting; + /* private */ + uint64_t* reserved[13]; +}; + +UV_EXTERN int uv_metrics_info(uv_loop_t* loop, uv_metrics_t* metrics); UV_EXTERN uint64_t uv_metrics_idle_time(uv_loop_t* loop); typedef enum { @@ -1709,7 +1816,9 @@ UV_EXTERN int uv_chdir(const char* dir); UV_EXTERN uint64_t uv_get_free_memory(void); UV_EXTERN uint64_t uv_get_total_memory(void); UV_EXTERN uint64_t uv_get_constrained_memory(void); +UV_EXTERN uint64_t uv_get_available_memory(void); +UV_EXTERN int uv_clock_gettime(uv_clock_id clock_id, uv_timespec64_t* ts); UV_EXTERN uint64_t uv_hrtime(void); UV_EXTERN void uv_sleep(unsigned int msec); @@ -1768,6 +1877,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, @@ -1786,9 +1896,20 @@ UV_EXTERN int uv_thread_create_ex(uv_thread_t* tid, const uv_thread_options_t* params, uv_thread_cb entry, void* arg); +UV_EXTERN int uv_thread_setaffinity(uv_thread_t* tid, + char* cpumask, + char* oldmask, + size_t mask_size); +UV_EXTERN int uv_thread_getaffinity(uv_thread_t* tid, + char* cpumask, + size_t mask_size); +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; @@ -1807,7 +1928,7 @@ struct uv_loop_s { void* data; /* Loop reference counting. */ unsigned int active_handles; - void* handle_queue[2]; + struct uv__queue handle_queue; union { void* unused; unsigned int count; @@ -1822,6 +1943,18 @@ 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); +/* 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 #undef UV_REQ_TYPE_PRIVATE diff --git a/include/uv/android-ifaddrs.h b/include/uv/android-ifaddrs.h deleted file mode 100644 index 9cd19fec129..00000000000 --- a/include/uv/android-ifaddrs.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 1995, 1999 - * Berkeley Software Design, Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * BSDI ifaddrs.h,v 2.5 2000/02/23 14:51:59 dab Exp - */ - -#ifndef _IFADDRS_H_ -#define _IFADDRS_H_ - -struct ifaddrs { - struct ifaddrs *ifa_next; - char *ifa_name; - unsigned int ifa_flags; - struct sockaddr *ifa_addr; - struct sockaddr *ifa_netmask; - struct sockaddr *ifa_dstaddr; - void *ifa_data; -}; - -/* - * This may have been defined in . Note that if is - * to be included it must be included before this header file. - */ -#ifndef ifa_broadaddr -#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ -#endif - -#include - -__BEGIN_DECLS -extern int getifaddrs(struct ifaddrs **ifap); -extern void freeifaddrs(struct ifaddrs *ifa); -__END_DECLS - -#endif diff --git a/include/uv/darwin.h b/include/uv/darwin.h index d226415820b..06962bfda80 100644 --- a/include/uv/darwin.h +++ b/include/uv/darwin.h @@ -40,7 +40,7 @@ void* cf_state; \ uv_mutex_t cf_mutex; \ uv_sem_t cf_sem; \ - void* cf_signals[2]; \ + struct uv__queue cf_signals; \ #define UV_PLATFORM_FS_EVENT_FIELDS \ uv__io_t event_watcher; \ @@ -48,8 +48,8 @@ int realpath_len; \ int cf_flags; \ uv_async_t* cf_cb; \ - void* cf_events[2]; \ - void* cf_member[2]; \ + struct uv__queue cf_events; \ + struct uv__queue cf_member; \ int cf_error; \ uv_mutex_t cf_mutex; \ diff --git a/include/uv/errno.h b/include/uv/errno.h index 71906b3f5e6..ac00778cfc5 100644 --- a/include/uv/errno.h +++ b/include/uv/errno.h @@ -413,7 +413,6 @@ #elif defined(__APPLE__) || \ defined(__DragonFly__) || \ defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ defined(__NetBSD__) || \ defined(__OpenBSD__) # define UV__EHOSTDOWN (-64) @@ -457,4 +456,28 @@ # define UV__ESOCKTNOSUPPORT (-4025) #endif +/* FreeBSD defines ENODATA in /usr/include/c++/v1/errno.h which is only visible + * if C++ is being used. Define it directly to avoid problems when integrating + * libuv in a C++ project. + */ +#if defined(ENODATA) && !defined(_WIN32) +# define UV__ENODATA UV__ERR(ENODATA) +#elif defined(__FreeBSD__) +# define UV__ENODATA (-9919) +#else +# define UV__ENODATA (-4024) +#endif + +#if defined(EUNATCH) && !defined(_WIN32) +# define UV__EUNATCH UV__ERR(EUNATCH) +#else +# 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_ */ diff --git a/include/uv/linux.h b/include/uv/linux.h index 9b38405a190..9f22f8cf726 100644 --- a/include/uv/linux.h +++ b/include/uv/linux.h @@ -28,7 +28,7 @@ int inotify_fd; \ #define UV_PLATFORM_FS_EVENT_FIELDS \ - void* watchers[2]; \ + struct uv__queue watchers; \ int wd; \ #endif /* UV_LINUX_H */ diff --git a/include/uv/stdint-msvc2008.h b/include/uv/stdint-msvc2008.h deleted file mode 100644 index d02608a5972..00000000000 --- a/include/uv/stdint-msvc2008.h +++ /dev/null @@ -1,247 +0,0 @@ -// ISO C9x compliant stdint.h for Microsoft Visual Studio -// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 -// -// Copyright (c) 2006-2008 Alexander Chemeris -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are met: -// -// 1. Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// 3. The name of the author may be used to endorse or promote products -// derived from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED -// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO -// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR -// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF -// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef _MSC_VER // [ -#error "Use this header only with Microsoft Visual C++ compilers!" -#endif // _MSC_VER ] - -#ifndef _MSC_STDINT_H_ // [ -#define _MSC_STDINT_H_ - -#if _MSC_VER > 1000 -#pragma once -#endif - -#include - -// For Visual Studio 6 in C++ mode and for many Visual Studio versions when -// compiling for ARM we should wrap include with 'extern "C++" {}' -// or compiler give many errors like this: -// error C2733: second C linkage of overloaded function 'wmemchr' not allowed -#ifdef __cplusplus -extern "C" { -#endif -# include -#ifdef __cplusplus -} -#endif - -// Define _W64 macros to mark types changing their size, like intptr_t. -#ifndef _W64 -# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 -# define _W64 __w64 -# else -# define _W64 -# endif -#endif - - -// 7.18.1 Integer types - -// 7.18.1.1 Exact-width integer types - -// Visual Studio 6 and Embedded Visual C++ 4 doesn't -// realize that, e.g. char has the same size as __int8 -// so we give up on __intX for them. -#if (_MSC_VER < 1300) - typedef signed char int8_t; - typedef signed short int16_t; - typedef signed int int32_t; - typedef unsigned char uint8_t; - typedef unsigned short uint16_t; - typedef unsigned int uint32_t; -#else - typedef signed __int8 int8_t; - typedef signed __int16 int16_t; - typedef signed __int32 int32_t; - typedef unsigned __int8 uint8_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int32 uint32_t; -#endif -typedef signed __int64 int64_t; -typedef unsigned __int64 uint64_t; - - -// 7.18.1.2 Minimum-width integer types -typedef int8_t int_least8_t; -typedef int16_t int_least16_t; -typedef int32_t int_least32_t; -typedef int64_t int_least64_t; -typedef uint8_t uint_least8_t; -typedef uint16_t uint_least16_t; -typedef uint32_t uint_least32_t; -typedef uint64_t uint_least64_t; - -// 7.18.1.3 Fastest minimum-width integer types -typedef int8_t int_fast8_t; -typedef int16_t int_fast16_t; -typedef int32_t int_fast32_t; -typedef int64_t int_fast64_t; -typedef uint8_t uint_fast8_t; -typedef uint16_t uint_fast16_t; -typedef uint32_t uint_fast32_t; -typedef uint64_t uint_fast64_t; - -// 7.18.1.4 Integer types capable of holding object pointers -#ifdef _WIN64 // [ - typedef signed __int64 intptr_t; - typedef unsigned __int64 uintptr_t; -#else // _WIN64 ][ - typedef _W64 signed int intptr_t; - typedef _W64 unsigned int uintptr_t; -#endif // _WIN64 ] - -// 7.18.1.5 Greatest-width integer types -typedef int64_t intmax_t; -typedef uint64_t uintmax_t; - - -// 7.18.2 Limits of specified-width integer types - -#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 - -// 7.18.2.1 Limits of exact-width integer types -#define INT8_MIN ((int8_t)_I8_MIN) -#define INT8_MAX _I8_MAX -#define INT16_MIN ((int16_t)_I16_MIN) -#define INT16_MAX _I16_MAX -#define INT32_MIN ((int32_t)_I32_MIN) -#define INT32_MAX _I32_MAX -#define INT64_MIN ((int64_t)_I64_MIN) -#define INT64_MAX _I64_MAX -#define UINT8_MAX _UI8_MAX -#define UINT16_MAX _UI16_MAX -#define UINT32_MAX _UI32_MAX -#define UINT64_MAX _UI64_MAX - -// 7.18.2.2 Limits of minimum-width integer types -#define INT_LEAST8_MIN INT8_MIN -#define INT_LEAST8_MAX INT8_MAX -#define INT_LEAST16_MIN INT16_MIN -#define INT_LEAST16_MAX INT16_MAX -#define INT_LEAST32_MIN INT32_MIN -#define INT_LEAST32_MAX INT32_MAX -#define INT_LEAST64_MIN INT64_MIN -#define INT_LEAST64_MAX INT64_MAX -#define UINT_LEAST8_MAX UINT8_MAX -#define UINT_LEAST16_MAX UINT16_MAX -#define UINT_LEAST32_MAX UINT32_MAX -#define UINT_LEAST64_MAX UINT64_MAX - -// 7.18.2.3 Limits of fastest minimum-width integer types -#define INT_FAST8_MIN INT8_MIN -#define INT_FAST8_MAX INT8_MAX -#define INT_FAST16_MIN INT16_MIN -#define INT_FAST16_MAX INT16_MAX -#define INT_FAST32_MIN INT32_MIN -#define INT_FAST32_MAX INT32_MAX -#define INT_FAST64_MIN INT64_MIN -#define INT_FAST64_MAX INT64_MAX -#define UINT_FAST8_MAX UINT8_MAX -#define UINT_FAST16_MAX UINT16_MAX -#define UINT_FAST32_MAX UINT32_MAX -#define UINT_FAST64_MAX UINT64_MAX - -// 7.18.2.4 Limits of integer types capable of holding object pointers -#ifdef _WIN64 // [ -# define INTPTR_MIN INT64_MIN -# define INTPTR_MAX INT64_MAX -# define UINTPTR_MAX UINT64_MAX -#else // _WIN64 ][ -# define INTPTR_MIN INT32_MIN -# define INTPTR_MAX INT32_MAX -# define UINTPTR_MAX UINT32_MAX -#endif // _WIN64 ] - -// 7.18.2.5 Limits of greatest-width integer types -#define INTMAX_MIN INT64_MIN -#define INTMAX_MAX INT64_MAX -#define UINTMAX_MAX UINT64_MAX - -// 7.18.3 Limits of other integer types - -#ifdef _WIN64 // [ -# define PTRDIFF_MIN _I64_MIN -# define PTRDIFF_MAX _I64_MAX -#else // _WIN64 ][ -# define PTRDIFF_MIN _I32_MIN -# define PTRDIFF_MAX _I32_MAX -#endif // _WIN64 ] - -#define SIG_ATOMIC_MIN INT_MIN -#define SIG_ATOMIC_MAX INT_MAX - -#ifndef SIZE_MAX // [ -# ifdef _WIN64 // [ -# define SIZE_MAX _UI64_MAX -# else // _WIN64 ][ -# define SIZE_MAX _UI32_MAX -# endif // _WIN64 ] -#endif // SIZE_MAX ] - -// WCHAR_MIN and WCHAR_MAX are also defined in -#ifndef WCHAR_MIN // [ -# define WCHAR_MIN 0 -#endif // WCHAR_MIN ] -#ifndef WCHAR_MAX // [ -# define WCHAR_MAX _UI16_MAX -#endif // WCHAR_MAX ] - -#define WINT_MIN 0 -#define WINT_MAX _UI16_MAX - -#endif // __STDC_LIMIT_MACROS ] - - -// 7.18.4 Limits of other integer types - -#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 - -// 7.18.4.1 Macros for minimum-width integer constants - -#define INT8_C(val) val##i8 -#define INT16_C(val) val##i16 -#define INT32_C(val) val##i32 -#define INT64_C(val) val##i64 - -#define UINT8_C(val) val##ui8 -#define UINT16_C(val) val##ui16 -#define UINT32_C(val) val##ui32 -#define UINT64_C(val) val##ui64 - -// 7.18.4.2 Macros for greatest-width integer constants -#define INTMAX_C INT64_C -#define UINTMAX_C UINT64_C - -#endif // __STDC_CONSTANT_MACROS ] - - -#endif // _MSC_STDINT_H_ ] diff --git a/include/uv/threadpool.h b/include/uv/threadpool.h index 9708ebdd530..24ce916fda4 100644 --- a/include/uv/threadpool.h +++ b/include/uv/threadpool.h @@ -31,7 +31,7 @@ struct uv__work { void (*work)(struct uv__work *w); void (*done)(struct uv__work *w, int status); struct uv_loop_s* loop; - void* wq[2]; + struct uv__queue wq; }; #endif /* UV_THREADPOOL_H_ */ diff --git a/include/uv/tree.h b/include/uv/tree.h index 2b28835fded..06bba084f38 100644 --- a/include/uv/tree.h +++ b/include/uv/tree.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) diff --git a/include/uv/unix.h b/include/uv/unix.h index ea37d787686..7c972026f68 100644 --- a/include/uv/unix.h +++ b/include/uv/unix.h @@ -59,7 +59,6 @@ # include "uv/darwin.h" #elif defined(__DragonFly__) || \ defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ defined(__OpenBSD__) || \ defined(__NetBSD__) # include "uv/bsd.h" @@ -93,8 +92,8 @@ typedef struct uv__io_s uv__io_t; struct uv__io_s { uv__io_cb cb; - void* pending_queue[2]; - void* watcher_queue[2]; + struct uv__queue pending_queue; + struct uv__queue watcher_queue; unsigned int pevents; /* Pending event mask i.e. mask at next tick. */ unsigned int events; /* Current event mask. */ int fd; @@ -221,21 +220,21 @@ typedef struct { #define UV_LOOP_PRIVATE_FIELDS \ unsigned long flags; \ int backend_fd; \ - void* pending_queue[2]; \ - void* watcher_queue[2]; \ + struct uv__queue pending_queue; \ + struct uv__queue watcher_queue; \ uv__io_t** watchers; \ unsigned int nwatchers; \ unsigned int nfds; \ - void* wq[2]; \ + struct uv__queue wq; \ uv_mutex_t wq_mutex; \ uv_async_t wq_async; \ uv_rwlock_t cloexec_lock; \ uv_handle_t* closing_handles; \ - void* process_handles[2]; \ - void* prepare_handles[2]; \ - void* check_handles[2]; \ - void* idle_handles[2]; \ - void* async_handles[2]; \ + struct uv__queue process_handles; \ + struct uv__queue prepare_handles; \ + struct uv__queue check_handles; \ + struct uv__queue idle_handles; \ + struct uv__queue async_handles; \ void (*async_unused)(void); /* TODO(bnoordhuis) Remove in libuv v2. */ \ uv__io_t async_io_watcher; \ int async_wfd; \ @@ -258,7 +257,7 @@ typedef struct { #define UV_PRIVATE_REQ_TYPES /* empty */ #define UV_WRITE_PRIVATE_FIELDS \ - void* queue[2]; \ + struct uv__queue queue; \ unsigned int write_index; \ uv_buf_t* bufs; \ unsigned int nbufs; \ @@ -266,13 +265,16 @@ typedef struct { uv_buf_t bufsml[4]; \ #define UV_CONNECT_PRIVATE_FIELDS \ - void* queue[2]; \ + struct uv__queue queue; \ #define UV_SHUTDOWN_PRIVATE_FIELDS /* empty */ #define UV_UDP_SEND_PRIVATE_FIELDS \ - void* queue[2]; \ - struct sockaddr_storage addr; \ + struct uv__queue queue; \ + union { \ + struct sockaddr addr; \ + struct sockaddr_storage storage; \ + } u; \ unsigned int nbufs; \ uv_buf_t* bufs; \ ssize_t status; \ @@ -287,8 +289,8 @@ typedef struct { uv_connect_t *connect_req; \ uv_shutdown_t *shutdown_req; \ uv__io_t io_watcher; \ - void* write_queue[2]; \ - void* write_completed_queue[2]; \ + struct uv__queue write_queue; \ + struct uv__queue write_completed_queue; \ uv_connection_cb connection_cb; \ int delayed_error; \ int accepted_fd; \ @@ -301,35 +303,38 @@ typedef struct { uv_alloc_cb alloc_cb; \ uv_udp_recv_cb recv_cb; \ uv__io_t io_watcher; \ - void* write_queue[2]; \ - void* write_completed_queue[2]; \ + struct uv__queue write_queue; \ + struct uv__queue write_completed_queue; \ #define UV_PIPE_PRIVATE_FIELDS \ - const char* pipe_fname; /* strdup'ed */ + const char* pipe_fname; /* NULL or strdup'ed */ #define UV_POLL_PRIVATE_FIELDS \ uv__io_t io_watcher; #define UV_PREPARE_PRIVATE_FIELDS \ uv_prepare_cb prepare_cb; \ - void* queue[2]; \ + struct uv__queue queue; \ #define UV_CHECK_PRIVATE_FIELDS \ uv_check_cb check_cb; \ - void* queue[2]; \ + struct uv__queue queue; \ #define UV_IDLE_PRIVATE_FIELDS \ uv_idle_cb idle_cb; \ - void* queue[2]; \ + struct uv__queue queue; \ #define UV_ASYNC_PRIVATE_FIELDS \ uv_async_cb async_cb; \ - void* queue[2]; \ + struct uv__queue queue; \ int pending; \ #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; @@ -353,7 +358,7 @@ typedef struct { int retcode; #define UV_PROCESS_PRIVATE_FIELDS \ - void* queue[2]; \ + struct uv__queue queue; \ int status; \ #define UV_FS_PRIVATE_FIELDS \ @@ -418,6 +423,8 @@ typedef struct { # define UV_FS_O_DIRECT 0x04000 #elif defined(__linux__) && defined(__x86_64__) # define UV_FS_O_DIRECT 0x04000 +#elif defined(__linux__) && defined(__loongarch__) +# define UV_FS_O_DIRECT 0x04000 #elif defined(O_DIRECT) # define UV_FS_O_DIRECT O_DIRECT #else diff --git a/include/uv/version.h b/include/uv/version.h index 1934f390dc5..6356e1ee44c 100644 --- a/include/uv/version.h +++ b/include/uv/version.h @@ -31,7 +31,7 @@ */ #define UV_VERSION_MAJOR 1 -#define UV_VERSION_MINOR 43 +#define UV_VERSION_MINOR 50 #define UV_VERSION_PATCH 1 #define UV_VERSION_IS_RELEASE 0 #define UV_VERSION_SUFFIX "dev" diff --git a/include/uv/win.h b/include/uv/win.h index 59a5d86e736..58d10b8d07f 100644 --- a/include/uv/win.h +++ b/include/uv/win.h @@ -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 -#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 -// 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 @@ -59,12 +51,7 @@ typedef struct pollfd { #include #include #include - -#if defined(_MSC_VER) && _MSC_VER < 1600 -# include "uv/stdint-msvc2008.h" -#else -# include -#endif +#include #include "uv/tree.h" #include "uv/threadpool.h" @@ -75,6 +62,11 @@ typedef struct pollfd { # define S_IFLNK 0xA000 #endif +/* 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 + /* Additional signals supported by uv_signal and or uv_kill. The CRT defines * the following signals already: * @@ -91,6 +83,7 @@ typedef struct pollfd { * variants (Linux and Darwin) */ #define SIGHUP 1 +#define SIGQUIT 3 #define SIGKILL 9 #define SIGWINCH 28 @@ -223,7 +216,7 @@ typedef struct _AFD_POLL_INFO { AFD_POLL_HANDLE_INFO Handles[1]; } AFD_POLL_INFO, *PAFD_POLL_INFO; -#define UV_MSAFD_PROVIDER_COUNT 3 +#define UV_MSAFD_PROVIDER_COUNT 4 /** @@ -274,11 +267,12 @@ typedef struct { } uv_rwlock_t; typedef struct { - unsigned int n; - unsigned int count; + unsigned threshold; + unsigned in; uv_mutex_t mutex; - uv_sem_t turnstile1; - uv_sem_t turnstile2; + /* TODO: in v2 make this a uv_cond_t, without unused_ */ + CONDITION_VARIABLE cond; + unsigned out; } uv_barrier_t; typedef struct { @@ -288,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. */ @@ -348,14 +342,14 @@ typedef struct { uv_idle_t* next_idle_handle; \ /* This handle holds the peer sockets for the fast variant of uv_poll_t */ \ SOCKET poll_peer_sockets[UV_MSAFD_PROVIDER_COUNT]; \ - /* Counter to keep track of active tcp streams */ \ + /* No longer used. */ \ unsigned int active_tcp_streams; \ - /* Counter to keep track of active udp streams */ \ + /* No longer used. */ \ unsigned int active_udp_streams; \ /* Counter to started timer */ \ uint64_t timer_counter; \ /* Threadpool */ \ - void* wq[2]; \ + struct uv__queue wq; \ uv_mutex_t wq_mutex; \ uv_async_t wq_async; @@ -377,6 +371,13 @@ typedef struct { OVERLAPPED overlapped; \ size_t queued_bytes; \ } io; \ + /* in v2, we can move these to the UV_CONNECT_PRIVATE_FIELDS */ \ + struct { \ + ULONG_PTR result; /* overlapped.Internal is reused to hold the result */\ + HANDLE pipeHandle; \ + DWORD duplex_flags; \ + WCHAR* name; \ + } connect; \ } u; \ struct uv_req_s* next_req; @@ -477,7 +478,7 @@ typedef struct { uint32_t payload_remaining; \ uint64_t dummy; /* TODO: retained for ABI compat; remove this in v2.x. */ \ } ipc_data_frame; \ - void* ipc_xfer_queue[2]; \ + struct uv__queue ipc_xfer_queue; \ int ipc_xfer_queue_length; \ uv_write_t* non_overlapped_writes_tail; \ CRITICAL_SECTION readfile_thread_lock; \ @@ -491,7 +492,7 @@ typedef struct { struct { uv_pipe_connection_fields } conn; \ } pipe; -/* TODO: put the parser states in an union - TTY handles are always half-duplex +/* TODO: put the parser states in a union - TTY handles are always half-duplex * so read-state can safely overlap write-state. */ #define UV_TTY_PRIVATE_FIELDS \ HANDLE handle; \ @@ -541,7 +542,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; \ @@ -599,7 +603,7 @@ typedef struct { struct uv_process_exit_s { \ UV_REQ_FIELDS \ } exit_req; \ - BYTE* child_stdio_buffer; \ + void* unused; /* TODO: retained for ABI compat; remove this in v2.x. */ \ int exit_signal; \ HANDLE wait_handle; \ HANDLE process_handle; \ diff --git a/libuv-static.pc.in b/libuv-static.pc.in index ea625482d5e..639058c8e08 100644 --- a/libuv-static.pc.in +++ b/libuv-static.pc.in @@ -8,5 +8,5 @@ Version: @PACKAGE_VERSION@ Description: multi-platform support library with a focus on asynchronous I/O. URL: http://libuv.org/ -Libs: -L${libdir} -luv_a @LIBS@ +Libs: -L${libdir} -l:libuv.a @LIBS@ Cflags: -I${includedir} diff --git a/libuv.pc.in b/libuv.pc.in index 1d7b86f7517..0f569146697 100644 --- a/libuv.pc.in +++ b/libuv.pc.in @@ -2,6 +2,7 @@ prefix=@prefix@ exec_prefix=${prefix} libdir=@libdir@ includedir=@includedir@ +LIBUV_STATIC=-L${libdir} -l:libuv.a @LIBS@ Name: libuv Version: @PACKAGE_VERSION@ diff --git a/m4/libuv-check-flags.m4 b/m4/libuv-check-flags.m4 index e347056ae2e..2a01308d37a 100644 --- a/m4/libuv-check-flags.m4 +++ b/m4/libuv-check-flags.m4 @@ -1,6 +1,7 @@ dnl Macros to check the presence of generic (non-typed) symbols. dnl Copyright (c) 2006-2008 Diego Pettenò dnl Copyright (c) 2006-2008 xine project +dnl Copyright (c) 2021 libuv project dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by @@ -63,7 +64,7 @@ AC_DEFUN([CC_CHECK_CFLAGS], [ ]) dnl CC_CHECK_CFLAG_APPEND(FLAG, [action-if-found], [action-if-not-found]) -dnl Check for CFLAG and appends them to CFLAGS if supported +dnl Check for CFLAG and appends them to AM_CFLAGS if supported AC_DEFUN([CC_CHECK_CFLAG_APPEND], [ AC_CACHE_CHECK([if $CC supports $1 flag], AS_TR_SH([cc_cv_cflags_$1]), @@ -71,7 +72,9 @@ AC_DEFUN([CC_CHECK_CFLAG_APPEND], [ ) AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes], - [CFLAGS="$CFLAGS $1"; DEBUG_CFLAGS="$DEBUG_CFLAGS $1"; $2], [$3]) + [AM_CFLAGS="$AM_CFLAGS $1"; DEBUG_CFLAGS="$DEBUG_CFLAGS $1"; $2], [$3]) + + AC_SUBST([AM_CFLAGS]) ]) dnl CC_CHECK_CFLAGS_APPEND([FLAG1 FLAG2], [action-if-found], [action-if-not]) @@ -101,6 +104,20 @@ AC_DEFUN([CC_CHECK_LDFLAGS], [ [$2], [$3]) ]) +dnl Check if flag is supported by both compiler and linker +dnl If so, append it to AM_CFLAGS +dnl CC_CHECK_FLAG_SUPPORTED_APPEND([FLAG]) + +AC_DEFUN([CC_CHECK_FLAG_SUPPORTED_APPEND], [ + CC_CHECK_CFLAGS([$1], + [CC_CHECK_LDFLAGS([$1], + [AM_CFLAGS="$AM_CFLAGS $1"; + DEBUG_CFLAGS="$DEBUG_CFLAGS $1"; + AC_SUBST([AM_CFLAGS]) + ]) + ]) +]) + dnl define the LDFLAGS_NOUNDEFINED variable with the correct value for dnl the current linker to avoid undefined references in a shared object. AC_DEFUN([CC_NOUNDEFINED], [ diff --git a/m4/libuv-check-versions.m4 b/m4/libuv-check-versions.m4 new file mode 100644 index 00000000000..795b07571fc --- /dev/null +++ b/m4/libuv-check-versions.m4 @@ -0,0 +1,7 @@ + +AC_PREREQ(2.71) +AC_INIT([libuv-release-check], [0.0]) +AM_INIT_AUTOMAKE([1.16.5]) +LT_PREREQ(2.4.7) +AC_OUTPUT + diff --git a/src/fs-poll.c b/src/fs-poll.c index 89864e23fbc..44f6263a583 100644 --- a/src/fs-poll.c +++ b/src/fs-poll.c @@ -25,7 +25,7 @@ #ifdef _WIN32 #include "win/internal.h" #include "win/handle-inl.h" -#define uv__make_close_pending(h) uv_want_endgame((h)->loop, (h)) +#define uv__make_close_pending(h) uv__want_endgame((h)->loop, (h)) #else #include "unix/internal.h" #endif @@ -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; diff --git a/src/idna.c b/src/idna.c index b44cb16a1ee..5fcaf64c974 100644 --- a/src/idna.c +++ b/src/idna.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, 2018 Ben Noordhuis +/* Copyright libuv contributors. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -18,9 +18,55 @@ */ #include "uv.h" +#include "uv-common.h" #include "idna.h" #include #include +#include /* UINT_MAX */ + + +static int32_t uv__wtf8_decode1(const char** input) { + uint32_t code_point; + uint8_t b1; + uint8_t b2; + uint8_t b3; + uint8_t b4; + + b1 = **input; + if (b1 <= 0x7F) + return b1; /* ASCII code point */ + if (b1 < 0xC2) + return -1; /* invalid: continuation byte */ + code_point = b1; + + b2 = *++*input; + if ((b2 & 0xC0) != 0x80) + return -1; /* invalid: not a continuation byte */ + code_point = (code_point << 6) | (b2 & 0x3F); + if (b1 <= 0xDF) + return 0x7FF & code_point; /* two-byte character */ + + b3 = *++*input; + if ((b3 & 0xC0) != 0x80) + return -1; /* invalid: not a continuation byte */ + code_point = (code_point << 6) | (b3 & 0x3F); + if (b1 <= 0xEF) + return 0xFFFF & code_point; /* three-byte character */ + + b4 = *++*input; + if ((b4 & 0xC0) != 0x80) + return -1; /* invalid: not a continuation byte */ + code_point = (code_point << 6) | (b4 & 0x3F); + if (b1 <= 0xF4) { + code_point &= 0x1FFFFF; + if (code_point <= 0x10FFFF) + return code_point; /* four-byte character */ + } + + /* code point too large */ + return -1; +} + static unsigned uv__utf8_decode1_slow(const char** p, const char* pe, @@ -88,6 +134,7 @@ static unsigned uv__utf8_decode1_slow(const char** p, return a; } + unsigned uv__utf8_decode1(const char** p, const char* pe) { unsigned a; @@ -101,6 +148,7 @@ unsigned uv__utf8_decode1(const char** p, const char* pe) { return uv__utf8_decode1_slow(p, pe, a); } + static int uv__idna_toascii_label(const char* s, const char* se, char** d, char* de) { static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz0123456789"; @@ -129,7 +177,7 @@ static int uv__idna_toascii_label(const char* s, const char* se, while (s < se) { c = uv__utf8_decode1(&s, se); - if (c == -1u) + if (c == UINT_MAX) return UV_EINVAL; if (c < 128) @@ -151,7 +199,7 @@ static int uv__idna_toascii_label(const char* s, const char* se, s = ss; while (s < se) { c = uv__utf8_decode1(&s, se); - assert(c != -1u); + assert(c != UINT_MAX); if (c > 127) continue; @@ -182,7 +230,7 @@ static int uv__idna_toascii_label(const char* s, const char* se, while (s < se) { c = uv__utf8_decode1(&s, se); - assert(c != -1u); + assert(c != UINT_MAX); if (c >= n) if (c < m) @@ -201,7 +249,7 @@ static int uv__idna_toascii_label(const char* s, const char* se, s = ss; while (s < se) { c = uv__utf8_decode1(&s, se); - assert(c != -1u); + assert(c != UINT_MAX); if (c < n) if (++delta == 0) @@ -266,13 +314,17 @@ static int uv__idna_toascii_label(const char* s, const char* se, return 0; } -long uv__idna_toascii(const char* s, const char* se, char* d, char* de) { + +ssize_t uv__idna_toascii(const char* s, const char* se, char* d, char* de) { const char* si; const char* st; unsigned c; char* ds; int rc; + if (s == se) + return UV_EINVAL; + ds = d; si = s; @@ -280,7 +332,7 @@ long uv__idna_toascii(const char* s, const char* se, char* d, char* de) { st = si; c = uv__utf8_decode1(&si, se); - if (c == -1u) + if (c == UINT_MAX) return UV_EINVAL; if (c != '.') @@ -307,8 +359,202 @@ long 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. */ } + + +ssize_t uv_wtf8_length_as_utf16(const char* source_ptr) { + size_t w_target_len = 0; + int32_t code_point; + + do { + code_point = uv__wtf8_decode1(&source_ptr); + if (code_point < 0) + return -1; + if (code_point > 0xFFFF) + w_target_len++; + w_target_len++; + } while (*source_ptr++); + + return w_target_len; +} + + +void uv_wtf8_to_utf16(const char* source_ptr, + uint16_t* w_target, + size_t w_target_len) { + int32_t code_point; + + do { + 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 > 0xFFFF) { + assert(code_point < 0x10FFFF); + *w_target++ = (((code_point - 0x10000) >> 10) + 0xD800); + *w_target++ = ((code_point - 0x10000) & 0x3FF) + 0xDC00; + w_target_len -= 2; + } else { + *w_target++ = code_point; + w_target_len -= 1; + } + } while (*source_ptr++); + + (void)w_target_len; + assert(w_target_len == 0); +} + + +static int32_t uv__get_surrogate_value(const uint16_t* w_source_ptr, + ssize_t w_source_len) { + uint16_t u; + uint16_t next; + + u = w_source_ptr[0]; + if (u >= 0xD800 && u <= 0xDBFF && w_source_len != 1) { + next = w_source_ptr[1]; + if (next >= 0xDC00 && next <= 0xDFFF) + return 0x10000 + ((u - 0xD800) << 10) + (next - 0xDC00); + } + return u; +} + + +size_t uv_utf16_length_as_wtf8(const uint16_t* w_source_ptr, + ssize_t w_source_len) { + size_t target_len; + int32_t code_point; + + target_len = 0; + while (w_source_len) { + code_point = uv__get_surrogate_value(w_source_ptr, w_source_len); + /* Can be invalid UTF-8 but must be valid WTF-8. */ + assert(code_point >= 0); + if (w_source_len < 0 && code_point == 0) + break; + if (code_point < 0x80) + target_len += 1; + else if (code_point < 0x800) + target_len += 2; + else if (code_point < 0x10000) + target_len += 3; + else { + target_len += 4; + w_source_ptr++; + if (w_source_len > 0) + w_source_len--; + } + w_source_ptr++; + if (w_source_len > 0) + w_source_len--; + } + + return target_len; +} + + +int uv_utf16_to_wtf8(const uint16_t* w_source_ptr, + ssize_t w_source_len, + char** target_ptr, + size_t* target_len_ptr) { + size_t target_len; + char* target; + char* target_end; + int32_t code_point; + + /* If *target_ptr is provided, then *target_len_ptr must be its length + * (excluding space for NUL), otherwise we will compute the target_len_ptr + * length and may return a new allocation in *target_ptr if target_ptr is + * provided. */ + if (target_ptr == NULL || *target_ptr == NULL) { + target_len = uv_utf16_length_as_wtf8(w_source_ptr, w_source_len); + if (target_len_ptr != NULL) + *target_len_ptr = target_len; + } else { + target_len = *target_len_ptr; + } + + if (target_ptr == NULL) + return 0; + + if (*target_ptr == NULL) { + target = uv__malloc(target_len + 1); + if (target == NULL) { + return UV_ENOMEM; + } + *target_ptr = target; + } else { + target = *target_ptr; + } + + target_end = target + target_len; + + while (target != target_end && w_source_len) { + code_point = uv__get_surrogate_value(w_source_ptr, w_source_len); + /* Can be invalid UTF-8 but must be valid WTF-8. */ + assert(code_point >= 0); + if (w_source_len < 0 && code_point == 0) { + w_source_len = 0; + break; + } + if (code_point < 0x80) { + *target++ = code_point; + } else if (code_point < 0x800) { + *target++ = 0xC0 | (code_point >> 6); + if (target == target_end) + break; + *target++ = 0x80 | (code_point & 0x3F); + } else if (code_point < 0x10000) { + *target++ = 0xE0 | (code_point >> 12); + if (target == target_end) + break; + *target++ = 0x80 | ((code_point >> 6) & 0x3F); + if (target == target_end) + break; + *target++ = 0x80 | (code_point & 0x3F); + } else { + *target++ = 0xF0 | (code_point >> 18); + if (target == target_end) + break; + *target++ = 0x80 | ((code_point >> 12) & 0x3F); + if (target == target_end) + break; + *target++ = 0x80 | ((code_point >> 6) & 0x3F); + if (target == target_end) + break; + *target++ = 0x80 | (code_point & 0x3F); + /* uv__get_surrogate_value consumed 2 input characters */ + w_source_ptr++; + if (w_source_len > 0) + w_source_len--; + } + target_len = target - *target_ptr; + w_source_ptr++; + if (w_source_len > 0) + w_source_len--; + } + + if (target != target_end && target_len_ptr != NULL) + /* Did not fill all of the provided buffer, so update the target_len_ptr + * output with the space used. */ + *target_len_ptr = target - *target_ptr; + + /* Check if input fit into target exactly. */ + if (w_source_len < 0 && target == target_end && w_source_ptr[0] == 0) + w_source_len = 0; + + *target++ = '\0'; + + /* Characters remained after filling the buffer, compute the remaining length now. */ + if (w_source_len) { + if (target_len_ptr != NULL) + *target_len_ptr = target_len + uv_utf16_length_as_wtf8(w_source_ptr, w_source_len); + return UV_ENOBUFS; + } + + return 0; +} diff --git a/src/idna.h b/src/idna.h index 8e0c592fe13..ea6b4df9671 100644 --- a/src/idna.h +++ b/src/idna.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011, 2018 Ben Noordhuis +/* Copyright libuv contributors. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -26,6 +26,6 @@ unsigned uv__utf8_decode1(const char** p, const char* pe); * is the number of bytes written to |d|, including the trailing nul byte. * A return value < 0 is a libuv error code. |s| and |d| can not overlap. */ -long uv__idna_toascii(const char* s, const char* se, char* d, char* de); +ssize_t uv__idna_toascii(const char* s, const char* se, char* d, char* de); #endif /* UV_SRC_IDNA_H_ */ diff --git a/src/inet.c b/src/inet.c index ddabf22fa52..cd77496846e 100644 --- a/src/inet.c +++ b/src/inet.c @@ -17,12 +17,7 @@ #include #include - -#if defined(_MSC_VER) && _MSC_VER < 1600 -# include "uv/stdint-msvc2008.h" -#else -# include -#endif +#include #include "uv.h" #include "uv-common.h" @@ -135,7 +130,7 @@ static int inet_ntop6(const unsigned char *src, char *dst, size_t size) { tp += strlen(tp); break; } - tp += sprintf(tp, "%x", words[i]); + tp += snprintf(tp, sizeof tmp - (tp - tmp), "%x", words[i]); } /* Was it a trailing run of 0x00's? */ if (best.base != -1 && (best.base + best.len) == ARRAY_SIZE(words)) diff --git a/src/queue.h b/src/queue.h index ff3540a0a51..5f8489e9bc5 100644 --- a/src/queue.h +++ b/src/queue.h @@ -18,91 +18,73 @@ #include -typedef void *QUEUE[2]; - -/* Private macros. */ -#define QUEUE_NEXT(q) (*(QUEUE **) &((*(q))[0])) -#define QUEUE_PREV(q) (*(QUEUE **) &((*(q))[1])) -#define QUEUE_PREV_NEXT(q) (QUEUE_NEXT(QUEUE_PREV(q))) -#define QUEUE_NEXT_PREV(q) (QUEUE_PREV(QUEUE_NEXT(q))) - -/* Public macros. */ -#define QUEUE_DATA(ptr, type, field) \ - ((type *) ((char *) (ptr) - offsetof(type, field))) - -/* Important note: mutating the list while QUEUE_FOREACH is - * iterating over its elements results in undefined behavior. - */ -#define QUEUE_FOREACH(q, h) \ - for ((q) = QUEUE_NEXT(h); (q) != (h); (q) = QUEUE_NEXT(q)) - -#define QUEUE_EMPTY(q) \ - ((const QUEUE *) (q) == (const QUEUE *) QUEUE_NEXT(q)) - -#define QUEUE_HEAD(q) \ - (QUEUE_NEXT(q)) - -#define QUEUE_INIT(q) \ - do { \ - QUEUE_NEXT(q) = (q); \ - QUEUE_PREV(q) = (q); \ - } \ - while (0) - -#define QUEUE_ADD(h, n) \ - do { \ - QUEUE_PREV_NEXT(h) = QUEUE_NEXT(n); \ - QUEUE_NEXT_PREV(n) = QUEUE_PREV(h); \ - QUEUE_PREV(h) = QUEUE_PREV(n); \ - QUEUE_PREV_NEXT(h) = (h); \ - } \ - while (0) - -#define QUEUE_SPLIT(h, q, n) \ - do { \ - QUEUE_PREV(n) = QUEUE_PREV(h); \ - QUEUE_PREV_NEXT(n) = (n); \ - QUEUE_NEXT(n) = (q); \ - QUEUE_PREV(h) = QUEUE_PREV(q); \ - QUEUE_PREV_NEXT(h) = (h); \ - QUEUE_PREV(q) = (n); \ - } \ - while (0) - -#define QUEUE_MOVE(h, n) \ - do { \ - if (QUEUE_EMPTY(h)) \ - QUEUE_INIT(n); \ - else { \ - QUEUE* q = QUEUE_HEAD(h); \ - QUEUE_SPLIT(h, q, n); \ - } \ - } \ - while (0) - -#define QUEUE_INSERT_HEAD(h, q) \ - do { \ - QUEUE_NEXT(q) = QUEUE_NEXT(h); \ - QUEUE_PREV(q) = (h); \ - QUEUE_NEXT_PREV(q) = (q); \ - QUEUE_NEXT(h) = (q); \ - } \ - while (0) - -#define QUEUE_INSERT_TAIL(h, q) \ - do { \ - QUEUE_NEXT(q) = (h); \ - QUEUE_PREV(q) = QUEUE_PREV(h); \ - QUEUE_PREV_NEXT(q) = (q); \ - QUEUE_PREV(h) = (q); \ - } \ - while (0) - -#define QUEUE_REMOVE(q) \ - do { \ - QUEUE_PREV_NEXT(q) = QUEUE_NEXT(q); \ - QUEUE_NEXT_PREV(q) = QUEUE_PREV(q); \ - } \ - while (0) +#define uv__queue_data(pointer, type, field) \ + ((type*) ((char*) (pointer) - offsetof(type, field))) + +#define uv__queue_foreach(q, h) \ + for ((q) = (h)->next; (q) != (h); (q) = (q)->next) + +static inline void uv__queue_init(struct uv__queue* q) { + q->next = q; + q->prev = q; +} + +static inline int uv__queue_empty(const struct uv__queue* q) { + return q == q->next; +} + +static inline struct uv__queue* uv__queue_head(const struct uv__queue* q) { + return q->next; +} + +static inline struct uv__queue* uv__queue_next(const struct uv__queue* q) { + return q->next; +} + +static inline void uv__queue_add(struct uv__queue* h, struct uv__queue* n) { + h->prev->next = n->next; + n->next->prev = h->prev; + h->prev = n->prev; + h->prev->next = h; +} + +static inline void uv__queue_split(struct uv__queue* h, + struct uv__queue* q, + struct uv__queue* n) { + n->prev = h->prev; + n->prev->next = n; + n->next = q; + h->prev = q->prev; + h->prev->next = h; + q->prev = n; +} + +static inline void uv__queue_move(struct uv__queue* h, struct uv__queue* n) { + if (uv__queue_empty(h)) + uv__queue_init(n); + else + uv__queue_split(h, h->next, n); +} + +static inline void uv__queue_insert_head(struct uv__queue* h, + struct uv__queue* q) { + q->next = h->next; + q->prev = h; + q->next->prev = q; + h->next = q; +} + +static inline void uv__queue_insert_tail(struct uv__queue* h, + struct uv__queue* q) { + q->next = h; + q->prev = h->prev; + q->prev->next = q; + h->prev = q; +} + +static inline void uv__queue_remove(struct uv__queue* q) { + q->prev->next = q->next; + q->next->prev = q->prev; +} #endif /* QUEUE_H_ */ diff --git a/src/random.c b/src/random.c index e75f77deb2b..57fc0d911da 100644 --- a/src/random.c +++ b/src/random.c @@ -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; diff --git a/src/strscpy.h b/src/strscpy.h index cc78149db5f..e8d47247f0d 100644 --- a/src/strscpy.h +++ b/src/strscpy.h @@ -28,7 +28,7 @@ */ #include "uv.h" -/* Copies up to |n-1| bytes from |d| to |s| and always zero-terminates +/* Copies up to |n-1| bytes from |s| to |d| and always zero-terminates * the result, except when |n==0|. Returns the number of bytes copied * or UV_E2BIG if |d| is too small. * diff --git a/src/strtok.c b/src/strtok.c new file mode 100644 index 00000000000..45ddea50b1a --- /dev/null +++ b/src/strtok.c @@ -0,0 +1,52 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include "strtok.h" + +char* uv__strtok(char* str, const char* sep, char** itr) { + const char* sep_itr; + char* tmp; + char* start; + + if (str == NULL) + start = tmp = *itr; + else + start = tmp = str; + + if (tmp == NULL) + return NULL; + + while (*tmp != '\0') { + sep_itr = sep; + while (*sep_itr != '\0') { + if (*tmp == *sep_itr) { + *itr = tmp + 1; + *tmp = '\0'; + return start; + } + sep_itr++; + } + tmp++; + } + *itr = NULL; + return start; +} diff --git a/src/strtok.h b/src/strtok.h new file mode 100644 index 00000000000..3799ff55436 --- /dev/null +++ b/src/strtok.h @@ -0,0 +1,27 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_STRTOK_H_ +#define UV_STRTOK_H_ + +char* uv__strtok(char* str, const char* sep, char** itr); + +#endif /* UV_STRTOK_H_ */ diff --git a/src/thread-common.c b/src/thread-common.c new file mode 100644 index 00000000000..c67c0a7dd72 --- /dev/null +++ b/src/thread-common.c @@ -0,0 +1,175 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "uv-common.h" + +#include +#ifndef _WIN32 +#include +#endif + +#if defined(PTHREAD_BARRIER_SERIAL_THREAD) +STATIC_ASSERT(sizeof(uv_barrier_t) == sizeof(pthread_barrier_t)); +#endif + +/* Note: guard clauses should match uv_barrier_t's in include/uv/unix.h. */ +#if defined(_AIX) || \ + defined(__OpenBSD__) || \ + !defined(PTHREAD_BARRIER_SERIAL_THREAD) +int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) { + int rc; +#ifdef _WIN32 + uv_barrier_t* b; + b = barrier; + + if (barrier == NULL || count == 0) + return UV_EINVAL; +#else + struct _uv_barrier* b; + + if (barrier == NULL || count == 0) + return UV_EINVAL; + + b = uv__malloc(sizeof(*b)); + if (b == NULL) + return UV_ENOMEM; +#endif + + b->in = 0; + b->out = 0; + b->threshold = count; + + rc = uv_mutex_init(&b->mutex); + if (rc != 0) + goto error2; + + /* TODO(vjnash): remove these uv_cond_t casts in v2. */ + rc = uv_cond_init((uv_cond_t*) &b->cond); + if (rc != 0) + goto error; + +#ifndef _WIN32 + barrier->b = b; +#endif + return 0; + +error: + uv_mutex_destroy(&b->mutex); +error2: +#ifndef _WIN32 + uv__free(b); +#endif + return rc; +} + + +int uv_barrier_wait(uv_barrier_t* barrier) { + int last; +#ifdef _WIN32 + uv_barrier_t* b; + b = barrier; +#else + struct _uv_barrier* b; + + if (barrier == NULL || barrier->b == NULL) + return UV_EINVAL; + + b = barrier->b; +#endif + + uv_mutex_lock(&b->mutex); + + while (b->out != 0) + uv_cond_wait((uv_cond_t*) &b->cond, &b->mutex); + + if (++b->in == b->threshold) { + b->in = 0; + b->out = b->threshold; + uv_cond_broadcast((uv_cond_t*) &b->cond); + } else { + do + uv_cond_wait((uv_cond_t*) &b->cond, &b->mutex); + while (b->in != 0); + } + + last = (--b->out == 0); + if (last) + uv_cond_broadcast((uv_cond_t*) &b->cond); + + uv_mutex_unlock(&b->mutex); + return last; +} + + +void uv_barrier_destroy(uv_barrier_t* barrier) { +#ifdef _WIN32 + uv_barrier_t* b; + b = barrier; +#else + struct _uv_barrier* b; + b = barrier->b; +#endif + + uv_mutex_lock(&b->mutex); + + assert(b->in == 0); + while (b->out != 0) + uv_cond_wait((uv_cond_t*) &b->cond, &b->mutex); + + if (b->in != 0) + abort(); + + uv_mutex_unlock(&b->mutex); + uv_mutex_destroy(&b->mutex); + uv_cond_destroy((uv_cond_t*) &b->cond); + +#ifndef _WIN32 + uv__free(barrier->b); + barrier->b = NULL; +#endif +} + +#else + +int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) { + return UV__ERR(pthread_barrier_init(barrier, NULL, count)); +} + + +int uv_barrier_wait(uv_barrier_t* barrier) { + int rc; + + rc = pthread_barrier_wait(barrier); + if (rc != 0) + if (rc != PTHREAD_BARRIER_SERIAL_THREAD) + abort(); + + return rc == PTHREAD_BARRIER_SERIAL_THREAD; +} + + +void uv_barrier_destroy(uv_barrier_t* barrier) { + if (pthread_barrier_destroy(barrier)) + abort(); +} + +#endif diff --git a/src/threadpool.c b/src/threadpool.c index e804c7c4b6f..98d81cc7b6a 100644 --- a/src/threadpool.c +++ b/src/threadpool.c @@ -37,10 +37,10 @@ static unsigned int slow_io_work_running; static unsigned int nthreads; static uv_thread_t* threads; static uv_thread_t default_threads[4]; -static QUEUE exit_message; -static QUEUE wq; -static QUEUE run_slow_work_message; -static QUEUE slow_io_pending_wq; +static struct uv__queue exit_message; +static struct uv__queue wq; +static struct uv__queue run_slow_work_message; +static struct uv__queue slow_io_pending_wq; static unsigned int slow_work_thread_threshold(void) { return (nthreads + 1) / 2; @@ -56,9 +56,10 @@ static void uv__cancelled(struct uv__work* w) { */ static void worker(void* arg) { struct uv__work* w; - QUEUE* q; + struct uv__queue* q; int is_slow_work; + uv_thread_setname("libuv-worker"); uv_sem_post((uv_sem_t*) arg); arg = NULL; @@ -68,49 +69,49 @@ static void worker(void* arg) { /* Keep waiting while either no work is present or only slow I/O and we're at the threshold for that. */ - while (QUEUE_EMPTY(&wq) || - (QUEUE_HEAD(&wq) == &run_slow_work_message && - QUEUE_NEXT(&run_slow_work_message) == &wq && + while (uv__queue_empty(&wq) || + (uv__queue_head(&wq) == &run_slow_work_message && + uv__queue_next(&run_slow_work_message) == &wq && slow_io_work_running >= slow_work_thread_threshold())) { idle_threads += 1; uv_cond_wait(&cond, &mutex); idle_threads -= 1; } - q = QUEUE_HEAD(&wq); + q = uv__queue_head(&wq); if (q == &exit_message) { uv_cond_signal(&cond); uv_mutex_unlock(&mutex); break; } - QUEUE_REMOVE(q); - QUEUE_INIT(q); /* Signal uv_cancel() that the work req is executing. */ + uv__queue_remove(q); + uv__queue_init(q); /* Signal uv_cancel() that the work req is executing. */ is_slow_work = 0; if (q == &run_slow_work_message) { /* If we're at the slow I/O threshold, re-schedule until after all other work in the queue is done. */ if (slow_io_work_running >= slow_work_thread_threshold()) { - QUEUE_INSERT_TAIL(&wq, q); + uv__queue_insert_tail(&wq, q); continue; } /* If we encountered a request to run slow I/O work but there is none to run, that means it's cancelled => Start over. */ - if (QUEUE_EMPTY(&slow_io_pending_wq)) + if (uv__queue_empty(&slow_io_pending_wq)) continue; is_slow_work = 1; slow_io_work_running++; - q = QUEUE_HEAD(&slow_io_pending_wq); - QUEUE_REMOVE(q); - QUEUE_INIT(q); + q = uv__queue_head(&slow_io_pending_wq); + uv__queue_remove(q); + uv__queue_init(q); /* If there is more slow I/O work, schedule it to be run as well. */ - if (!QUEUE_EMPTY(&slow_io_pending_wq)) { - QUEUE_INSERT_TAIL(&wq, &run_slow_work_message); + if (!uv__queue_empty(&slow_io_pending_wq)) { + uv__queue_insert_tail(&wq, &run_slow_work_message); if (idle_threads > 0) uv_cond_signal(&cond); } @@ -118,13 +119,13 @@ static void worker(void* arg) { uv_mutex_unlock(&mutex); - w = QUEUE_DATA(q, struct uv__work, wq); + w = uv__queue_data(q, struct uv__work, wq); w->work(w); uv_mutex_lock(&w->loop->wq_mutex); w->work = NULL; /* Signal uv_cancel() that the work req is done executing. */ - QUEUE_INSERT_TAIL(&w->loop->wq, &w->wq); + uv__queue_insert_tail(&w->loop->wq, &w->wq); uv_async_send(&w->loop->wq_async); uv_mutex_unlock(&w->loop->wq_mutex); @@ -139,12 +140,12 @@ static void worker(void* arg) { } -static void post(QUEUE* q, enum uv__work_kind kind) { +static void post(struct uv__queue* q, enum uv__work_kind kind) { uv_mutex_lock(&mutex); if (kind == UV__WORK_SLOW_IO) { /* Insert into a separate queue. */ - QUEUE_INSERT_TAIL(&slow_io_pending_wq, q); - if (!QUEUE_EMPTY(&run_slow_work_message)) { + uv__queue_insert_tail(&slow_io_pending_wq, q); + if (!uv__queue_empty(&run_slow_work_message)) { /* Running slow I/O tasks is already scheduled => Nothing to do here. The worker that runs said other task will schedule this one as well. */ uv_mutex_unlock(&mutex); @@ -153,7 +154,7 @@ static void post(QUEUE* q, enum uv__work_kind kind) { q = &run_slow_work_message; } - QUEUE_INSERT_TAIL(&wq, q); + uv__queue_insert_tail(&wq, q); if (idle_threads > 0) uv_cond_signal(&cond); uv_mutex_unlock(&mutex); @@ -191,6 +192,7 @@ void uv__threadpool_cleanup(void) { static void init_threads(void) { + uv_thread_options_t config; unsigned int i; const char* val; uv_sem_t sem; @@ -219,15 +221,18 @@ static void init_threads(void) { if (uv_mutex_init(&mutex)) abort(); - QUEUE_INIT(&wq); - QUEUE_INIT(&slow_io_pending_wq); - QUEUE_INIT(&run_slow_work_message); + uv__queue_init(&wq); + uv__queue_init(&slow_io_pending_wq); + uv__queue_init(&run_slow_work_message); if (uv_sem_init(&sem, 0)) abort(); + config.flags = UV_THREAD_HAS_STACK_SIZE; + config.stack_size = 8u << 20; /* 8 MB */ + for (i = 0; i < nthreads; i++) - if (uv_thread_create(threads + i, worker, &sem)) + if (uv_thread_create_ex(threads + i, &config, worker, &sem)) abort(); for (i = 0; i < nthreads; i++) @@ -271,15 +276,19 @@ void uv__work_submit(uv_loop_t* loop, } +/* TODO(bnoordhuis) teach libuv how to cancel file operations + * that go through io_uring instead of the thread pool. + */ static int uv__work_cancel(uv_loop_t* loop, uv_req_t* req, struct uv__work* w) { int cancelled; + uv_once(&once, init_once); /* Ensure |mutex| is initialized. */ uv_mutex_lock(&mutex); uv_mutex_lock(&w->loop->wq_mutex); - cancelled = !QUEUE_EMPTY(&w->wq) && w->work != NULL; + cancelled = !uv__queue_empty(&w->wq) && w->work != NULL; if (cancelled) - QUEUE_REMOVE(&w->wq); + uv__queue_remove(&w->wq); uv_mutex_unlock(&w->loop->wq_mutex); uv_mutex_unlock(&mutex); @@ -289,7 +298,7 @@ static int uv__work_cancel(uv_loop_t* loop, uv_req_t* req, struct uv__work* w) { w->work = uv__cancelled; uv_mutex_lock(&loop->wq_mutex); - QUEUE_INSERT_TAIL(&loop->wq, &w->wq); + uv__queue_insert_tail(&loop->wq, &w->wq); uv_async_send(&loop->wq_async); uv_mutex_unlock(&loop->wq_mutex); @@ -300,22 +309,39 @@ static int uv__work_cancel(uv_loop_t* loop, uv_req_t* req, struct uv__work* w) { void uv__work_done(uv_async_t* handle) { struct uv__work* w; uv_loop_t* loop; - QUEUE* q; - QUEUE wq; + struct uv__queue* q; + struct uv__queue wq; int err; + int nevents; loop = container_of(handle, uv_loop_t, wq_async); uv_mutex_lock(&loop->wq_mutex); - QUEUE_MOVE(&loop->wq, &wq); + uv__queue_move(&loop->wq, &wq); uv_mutex_unlock(&loop->wq_mutex); - while (!QUEUE_EMPTY(&wq)) { - q = QUEUE_HEAD(&wq); - QUEUE_REMOVE(q); + nevents = 0; + + while (!uv__queue_empty(&wq)) { + q = uv__queue_head(&wq); + uv__queue_remove(q); w = container_of(q, struct uv__work, wq); err = (w->work == uv__cancelled) ? UV_ECANCELED : 0; w->done(w, err); + nevents++; + } + + /* This check accomplishes 2 things: + * 1. Even if the queue was empty, the call to uv__work_done() should count + * as an event. Which will have been added by the event loop when + * calling this callback. + * 2. Prevents accidental wrap around in case nevents == 0 events == 0. + */ + if (nevents > 1) { + /* Subtract 1 to counter the call to uv__work_done(). */ + uv__metrics_inc_events(loop, nevents - 1); + if (uv__get_internal_fields(loop)->current_timeout == 0) + uv__metrics_inc_events_waiting(loop, nevents - 1); } } @@ -331,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; diff --git a/src/timer.c b/src/timer.c index bc680e71a9e..4525199ddc1 100644 --- a/src/timer.c +++ b/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); } diff --git a/src/unix/aix.c b/src/unix/aix.c index 6a013d43e3a..3af3009a216 100644 --- a/src/unix/aix.c +++ b/src/unix/aix.c @@ -131,11 +131,12 @@ int uv__io_check_fd(uv_loop_t* loop, int fd) { void uv__io_poll(uv_loop_t* loop, int timeout) { + uv__loop_internal_fields_t* lfields; struct pollfd events[1024]; struct pollfd pqry; struct pollfd* pe; struct poll_ctl pc; - QUEUE* q; + struct uv__queue* q; uv__io_t* w; uint64_t base; uint64_t diff; @@ -150,16 +151,18 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int reset_timeout; if (loop->nfds == 0) { - assert(QUEUE_EMPTY(&loop->watcher_queue)); + assert(uv__queue_empty(&loop->watcher_queue)); return; } - while (!QUEUE_EMPTY(&loop->watcher_queue)) { - q = QUEUE_HEAD(&loop->watcher_queue); - QUEUE_REMOVE(q); - QUEUE_INIT(q); + lfields = uv__get_internal_fields(loop); - w = QUEUE_DATA(q, uv__io_t, watcher_queue); + while (!uv__queue_empty(&loop->watcher_queue)) { + q = uv__queue_head(&loop->watcher_queue); + uv__queue_remove(q); + uv__queue_init(q); + + w = uv__queue_data(q, uv__io_t, watcher_queue); assert(w->pevents != 0); assert(w->fd >= 0); assert(w->fd < (int) loop->nwatchers); @@ -217,7 +220,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { base = loop->time; count = 48; /* Benchmarks suggest this gives the best throughput. */ - if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + if (lfields->flags & UV_METRICS_IDLE_TIME) { reset_timeout = 1; user_timeout = timeout; timeout = 0; @@ -232,6 +235,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { 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; + nfds = pollset_poll(loop->backend_fd, events, ARRAY_SIZE(events), @@ -321,9 +330,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { nevents++; } + uv__metrics_inc_events(loop, nevents); if (reset_timeout != 0) { timeout = user_timeout; reset_timeout = 0; + uv__metrics_inc_events_waiting(loop, nevents); } if (have_signals != 0) { @@ -389,6 +400,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + void uv_loadavg(double avg[3]) { perfstat_cpu_total_t ps_total; int result = perfstat_cpu_total(NULL, &ps_total, sizeof(ps_total), 1); @@ -425,7 +441,7 @@ static char* uv__rawname(const char* cp, char (*dst)[FILENAME_MAX+1]) { static int uv__path_is_a_directory(char* filename) { struct stat statbuf; - if (stat(filename, &statbuf) < 0) + if (uv__stat(filename, &statbuf) < 0) return -1; /* failed: not a directory, assume it is a file */ if (statbuf.st_type == VDIR) diff --git a/src/unix/android-ifaddrs.c b/src/unix/android-ifaddrs.c deleted file mode 100644 index 4765cc06b57..00000000000 --- a/src/unix/android-ifaddrs.c +++ /dev/null @@ -1,713 +0,0 @@ -/* -Copyright (c) 2013, Kenneth MacKay -Copyright (c) 2014, Emergya (Cloud4all, FP7/2007-2013 grant agreement #289016) -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "uv/android-ifaddrs.h" -#include "uv-common.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -typedef struct NetlinkList -{ - struct NetlinkList *m_next; - struct nlmsghdr *m_data; - unsigned int m_size; -} NetlinkList; - -static int netlink_socket(pid_t *p_pid) -{ - struct sockaddr_nl l_addr; - socklen_t l_len; - - int l_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if(l_socket < 0) - { - return -1; - } - - memset(&l_addr, 0, sizeof(l_addr)); - l_addr.nl_family = AF_NETLINK; - if(bind(l_socket, (struct sockaddr *)&l_addr, sizeof(l_addr)) < 0) - { - close(l_socket); - return -1; - } - - l_len = sizeof(l_addr); - if(getsockname(l_socket, (struct sockaddr *)&l_addr, &l_len) < 0) - { - close(l_socket); - return -1; - } - *p_pid = l_addr.nl_pid; - - return l_socket; -} - -static int netlink_send(int p_socket, int p_request) -{ - char l_buffer[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct rtgenmsg))]; - - struct nlmsghdr *l_hdr; - struct rtgenmsg *l_msg; - struct sockaddr_nl l_addr; - - memset(l_buffer, 0, sizeof(l_buffer)); - - l_hdr = (struct nlmsghdr *)l_buffer; - l_msg = (struct rtgenmsg *)NLMSG_DATA(l_hdr); - - l_hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*l_msg)); - l_hdr->nlmsg_type = p_request; - l_hdr->nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; - l_hdr->nlmsg_pid = 0; - l_hdr->nlmsg_seq = p_socket; - l_msg->rtgen_family = AF_UNSPEC; - - memset(&l_addr, 0, sizeof(l_addr)); - l_addr.nl_family = AF_NETLINK; - return (sendto(p_socket, l_hdr, l_hdr->nlmsg_len, 0, (struct sockaddr *)&l_addr, sizeof(l_addr))); -} - -static int netlink_recv(int p_socket, void *p_buffer, size_t p_len) -{ - struct sockaddr_nl l_addr; - struct msghdr l_msg; - - struct iovec l_iov; - l_iov.iov_base = p_buffer; - l_iov.iov_len = p_len; - - for(;;) - { - int l_result; - l_msg.msg_name = (void *)&l_addr; - l_msg.msg_namelen = sizeof(l_addr); - l_msg.msg_iov = &l_iov; - l_msg.msg_iovlen = 1; - l_msg.msg_control = NULL; - l_msg.msg_controllen = 0; - l_msg.msg_flags = 0; - l_result = recvmsg(p_socket, &l_msg, 0); - - if(l_result < 0) - { - if(errno == EINTR) - { - continue; - } - return -2; - } - - /* Buffer was too small */ - if(l_msg.msg_flags & MSG_TRUNC) - { - return -1; - } - return l_result; - } -} - -static struct nlmsghdr *getNetlinkResponse(int p_socket, pid_t p_pid, int *p_size, int *p_done) -{ - size_t l_size = 4096; - void *l_buffer = NULL; - - for(;;) - { - int l_read; - - uv__free(l_buffer); - l_buffer = uv__malloc(l_size); - if (l_buffer == NULL) - { - return NULL; - } - - l_read = netlink_recv(p_socket, l_buffer, l_size); - *p_size = l_read; - if(l_read == -2) - { - uv__free(l_buffer); - return NULL; - } - if(l_read >= 0) - { - struct nlmsghdr *l_hdr; - for(l_hdr = (struct nlmsghdr *)l_buffer; NLMSG_OK(l_hdr, (unsigned int)l_read); l_hdr = (struct nlmsghdr *)NLMSG_NEXT(l_hdr, l_read)) - { - if((pid_t)l_hdr->nlmsg_pid != p_pid || (int)l_hdr->nlmsg_seq != p_socket) - { - continue; - } - - if(l_hdr->nlmsg_type == NLMSG_DONE) - { - *p_done = 1; - break; - } - - if(l_hdr->nlmsg_type == NLMSG_ERROR) - { - uv__free(l_buffer); - return NULL; - } - } - return l_buffer; - } - - l_size *= 2; - } -} - -static NetlinkList *newListItem(struct nlmsghdr *p_data, unsigned int p_size) -{ - NetlinkList *l_item = uv__malloc(sizeof(NetlinkList)); - if (l_item == NULL) - { - return NULL; - } - - l_item->m_next = NULL; - l_item->m_data = p_data; - l_item->m_size = p_size; - return l_item; -} - -static void freeResultList(NetlinkList *p_list) -{ - NetlinkList *l_cur; - while(p_list) - { - l_cur = p_list; - p_list = p_list->m_next; - uv__free(l_cur->m_data); - uv__free(l_cur); - } -} - -static NetlinkList *getResultList(int p_socket, int p_request, pid_t p_pid) -{ - int l_size; - int l_done; - NetlinkList *l_list; - NetlinkList *l_end; - - if(netlink_send(p_socket, p_request) < 0) - { - return NULL; - } - - l_list = NULL; - l_end = NULL; - - l_done = 0; - while(!l_done) - { - NetlinkList *l_item; - - struct nlmsghdr *l_hdr = getNetlinkResponse(p_socket, p_pid, &l_size, &l_done); - /* Error */ - if(!l_hdr) - { - freeResultList(l_list); - return NULL; - } - - l_item = newListItem(l_hdr, l_size); - if (!l_item) - { - freeResultList(l_list); - return NULL; - } - if(!l_list) - { - l_list = l_item; - } - else - { - l_end->m_next = l_item; - } - l_end = l_item; - } - return l_list; -} - -static size_t maxSize(size_t a, size_t b) -{ - return (a > b ? a : b); -} - -static size_t calcAddrLen(sa_family_t p_family, int p_dataSize) -{ - switch(p_family) - { - case AF_INET: - return sizeof(struct sockaddr_in); - case AF_INET6: - return sizeof(struct sockaddr_in6); - case AF_PACKET: - return maxSize(sizeof(struct sockaddr_ll), offsetof(struct sockaddr_ll, sll_addr) + p_dataSize); - default: - return maxSize(sizeof(struct sockaddr), offsetof(struct sockaddr, sa_data) + p_dataSize); - } -} - -static void makeSockaddr(sa_family_t p_family, struct sockaddr *p_dest, void *p_data, size_t p_size) -{ - switch(p_family) - { - case AF_INET: - memcpy(&((struct sockaddr_in*)p_dest)->sin_addr, p_data, p_size); - break; - case AF_INET6: - memcpy(&((struct sockaddr_in6*)p_dest)->sin6_addr, p_data, p_size); - break; - case AF_PACKET: - memcpy(((struct sockaddr_ll*)p_dest)->sll_addr, p_data, p_size); - ((struct sockaddr_ll*)p_dest)->sll_halen = p_size; - break; - default: - memcpy(p_dest->sa_data, p_data, p_size); - break; - } - p_dest->sa_family = p_family; -} - -static void addToEnd(struct ifaddrs **p_resultList, struct ifaddrs *p_entry) -{ - if(!*p_resultList) - { - *p_resultList = p_entry; - } - else - { - struct ifaddrs *l_cur = *p_resultList; - while(l_cur->ifa_next) - { - l_cur = l_cur->ifa_next; - } - l_cur->ifa_next = p_entry; - } -} - -static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList) -{ - struct ifaddrs *l_entry; - - char *l_index; - char *l_name; - char *l_addr; - char *l_data; - - struct ifinfomsg *l_info = (struct ifinfomsg *)NLMSG_DATA(p_hdr); - - size_t l_nameSize = 0; - size_t l_addrSize = 0; - size_t l_dataSize = 0; - - size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg)); - struct rtattr *l_rta; - for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) - { - size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); - switch(l_rta->rta_type) - { - case IFLA_ADDRESS: - case IFLA_BROADCAST: - l_addrSize += NLMSG_ALIGN(calcAddrLen(AF_PACKET, l_rtaDataSize)); - break; - case IFLA_IFNAME: - l_nameSize += NLMSG_ALIGN(l_rtaSize + 1); - break; - case IFLA_STATS: - l_dataSize += NLMSG_ALIGN(l_rtaSize); - break; - default: - break; - } - } - - l_entry = uv__malloc(sizeof(struct ifaddrs) + sizeof(int) + l_nameSize + l_addrSize + l_dataSize); - if (l_entry == NULL) - { - return -1; - } - memset(l_entry, 0, sizeof(struct ifaddrs)); - l_entry->ifa_name = ""; - - l_index = ((char *)l_entry) + sizeof(struct ifaddrs); - l_name = l_index + sizeof(int); - l_addr = l_name + l_nameSize; - l_data = l_addr + l_addrSize; - - /* Save the interface index so we can look it up when handling the - * addresses. - */ - memcpy(l_index, &l_info->ifi_index, sizeof(int)); - - l_entry->ifa_flags = l_info->ifi_flags; - - l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg)); - for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) - { - void *l_rtaData = RTA_DATA(l_rta); - size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); - switch(l_rta->rta_type) - { - case IFLA_ADDRESS: - case IFLA_BROADCAST: - { - size_t l_addrLen = calcAddrLen(AF_PACKET, l_rtaDataSize); - makeSockaddr(AF_PACKET, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize); - ((struct sockaddr_ll *)l_addr)->sll_ifindex = l_info->ifi_index; - ((struct sockaddr_ll *)l_addr)->sll_hatype = l_info->ifi_type; - if(l_rta->rta_type == IFLA_ADDRESS) - { - l_entry->ifa_addr = (struct sockaddr *)l_addr; - } - else - { - l_entry->ifa_broadaddr = (struct sockaddr *)l_addr; - } - l_addr += NLMSG_ALIGN(l_addrLen); - break; - } - case IFLA_IFNAME: - strncpy(l_name, l_rtaData, l_rtaDataSize); - l_name[l_rtaDataSize] = '\0'; - l_entry->ifa_name = l_name; - break; - case IFLA_STATS: - memcpy(l_data, l_rtaData, l_rtaDataSize); - l_entry->ifa_data = l_data; - break; - default: - break; - } - } - - addToEnd(p_resultList, l_entry); - return 0; -} - -static struct ifaddrs *findInterface(int p_index, struct ifaddrs **p_links, int p_numLinks) -{ - int l_num = 0; - struct ifaddrs *l_cur = *p_links; - while(l_cur && l_num < p_numLinks) - { - char *l_indexPtr = ((char *)l_cur) + sizeof(struct ifaddrs); - int l_index; - memcpy(&l_index, l_indexPtr, sizeof(int)); - if(l_index == p_index) - { - return l_cur; - } - - l_cur = l_cur->ifa_next; - ++l_num; - } - return NULL; -} - -static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList, int p_numLinks) -{ - struct ifaddrmsg *l_info = (struct ifaddrmsg *)NLMSG_DATA(p_hdr); - struct ifaddrs *l_interface = findInterface(l_info->ifa_index, p_resultList, p_numLinks); - - size_t l_nameSize = 0; - size_t l_addrSize = 0; - - int l_addedNetmask = 0; - - size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg)); - struct rtattr *l_rta; - struct ifaddrs *l_entry; - - char *l_name; - char *l_addr; - - for(l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) - { - size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); - if(l_info->ifa_family == AF_PACKET) - { - continue; - } - - switch(l_rta->rta_type) - { - case IFA_ADDRESS: - case IFA_LOCAL: - l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); - if((l_info->ifa_family == AF_INET || l_info->ifa_family == AF_INET6) && !l_addedNetmask) - { - /* Make room for netmask */ - l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); - l_addedNetmask = 1; - } - break; - case IFA_BROADCAST: - l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); - break; - case IFA_LABEL: - l_nameSize += NLMSG_ALIGN(l_rtaDataSize + 1); - break; - default: - break; - } - } - - l_entry = uv__malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize); - if (l_entry == NULL) - { - return -1; - } - memset(l_entry, 0, sizeof(struct ifaddrs)); - l_entry->ifa_name = (l_interface ? l_interface->ifa_name : ""); - - l_name = ((char *)l_entry) + sizeof(struct ifaddrs); - l_addr = l_name + l_nameSize; - - l_entry->ifa_flags = l_info->ifa_flags; - if(l_interface) - { - l_entry->ifa_flags |= l_interface->ifa_flags; - } - - l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg)); - for(l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) - { - void *l_rtaData = RTA_DATA(l_rta); - size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); - switch(l_rta->rta_type) - { - case IFA_ADDRESS: - case IFA_BROADCAST: - case IFA_LOCAL: - { - size_t l_addrLen = calcAddrLen(l_info->ifa_family, l_rtaDataSize); - makeSockaddr(l_info->ifa_family, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize); - if(l_info->ifa_family == AF_INET6) - { - if(IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)l_rtaData) || IN6_IS_ADDR_MC_LINKLOCAL((struct in6_addr *)l_rtaData)) - { - ((struct sockaddr_in6 *)l_addr)->sin6_scope_id = l_info->ifa_index; - } - } - - /* Apparently in a point-to-point network IFA_ADDRESS contains - * the dest address and IFA_LOCAL contains the local address - */ - if(l_rta->rta_type == IFA_ADDRESS) - { - if(l_entry->ifa_addr) - { - l_entry->ifa_dstaddr = (struct sockaddr *)l_addr; - } - else - { - l_entry->ifa_addr = (struct sockaddr *)l_addr; - } - } - else if(l_rta->rta_type == IFA_LOCAL) - { - if(l_entry->ifa_addr) - { - l_entry->ifa_dstaddr = l_entry->ifa_addr; - } - l_entry->ifa_addr = (struct sockaddr *)l_addr; - } - else - { - l_entry->ifa_broadaddr = (struct sockaddr *)l_addr; - } - l_addr += NLMSG_ALIGN(l_addrLen); - break; - } - case IFA_LABEL: - strncpy(l_name, l_rtaData, l_rtaDataSize); - l_name[l_rtaDataSize] = '\0'; - l_entry->ifa_name = l_name; - break; - default: - break; - } - } - - if(l_entry->ifa_addr && (l_entry->ifa_addr->sa_family == AF_INET || l_entry->ifa_addr->sa_family == AF_INET6)) - { - unsigned l_maxPrefix = (l_entry->ifa_addr->sa_family == AF_INET ? 32 : 128); - unsigned l_prefix = (l_info->ifa_prefixlen > l_maxPrefix ? l_maxPrefix : l_info->ifa_prefixlen); - unsigned char l_mask[16] = {0}; - unsigned i; - for(i=0; i<(l_prefix/8); ++i) - { - l_mask[i] = 0xff; - } - if(l_prefix % 8) - { - l_mask[i] = 0xff << (8 - (l_prefix % 8)); - } - - makeSockaddr(l_entry->ifa_addr->sa_family, (struct sockaddr *)l_addr, l_mask, l_maxPrefix / 8); - l_entry->ifa_netmask = (struct sockaddr *)l_addr; - } - - addToEnd(p_resultList, l_entry); - return 0; -} - -static int interpretLinks(int p_socket, pid_t p_pid, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList) -{ - - int l_numLinks = 0; - for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next) - { - unsigned int l_nlsize = p_netlinkList->m_size; - struct nlmsghdr *l_hdr; - for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize)) - { - if((pid_t)l_hdr->nlmsg_pid != p_pid || (int)l_hdr->nlmsg_seq != p_socket) - { - continue; - } - - if(l_hdr->nlmsg_type == NLMSG_DONE) - { - break; - } - - if(l_hdr->nlmsg_type == RTM_NEWLINK) - { - if(interpretLink(l_hdr, p_resultList) == -1) - { - return -1; - } - ++l_numLinks; - } - } - } - return l_numLinks; -} - -static int interpretAddrs(int p_socket, pid_t p_pid, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList, int p_numLinks) -{ - for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next) - { - unsigned int l_nlsize = p_netlinkList->m_size; - struct nlmsghdr *l_hdr; - for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize)) - { - if((pid_t)l_hdr->nlmsg_pid != p_pid || (int)l_hdr->nlmsg_seq != p_socket) - { - continue; - } - - if(l_hdr->nlmsg_type == NLMSG_DONE) - { - break; - } - - if(l_hdr->nlmsg_type == RTM_NEWADDR) - { - if (interpretAddr(l_hdr, p_resultList, p_numLinks) == -1) - { - return -1; - } - } - } - } - return 0; -} - -int getifaddrs(struct ifaddrs **ifap) -{ - int l_socket; - int l_result; - int l_numLinks; - pid_t l_pid; - NetlinkList *l_linkResults; - NetlinkList *l_addrResults; - - if(!ifap) - { - return -1; - } - *ifap = NULL; - - l_socket = netlink_socket(&l_pid); - if(l_socket < 0) - { - return -1; - } - - l_linkResults = getResultList(l_socket, RTM_GETLINK, l_pid); - if(!l_linkResults) - { - close(l_socket); - return -1; - } - - l_addrResults = getResultList(l_socket, RTM_GETADDR, l_pid); - if(!l_addrResults) - { - close(l_socket); - freeResultList(l_linkResults); - return -1; - } - - l_result = 0; - l_numLinks = interpretLinks(l_socket, l_pid, l_linkResults, ifap); - if(l_numLinks == -1 || interpretAddrs(l_socket, l_pid, l_addrResults, ifap, l_numLinks) == -1) - { - l_result = -1; - } - - freeResultList(l_linkResults); - freeResultList(l_addrResults); - close(l_socket); - return l_result; -} - -void freeifaddrs(struct ifaddrs *ifa) -{ - struct ifaddrs *l_cur; - while(ifa) - { - l_cur = ifa; - ifa = ifa->ifa_next; - uv__free(l_cur); - } -} diff --git a/src/unix/async.c b/src/unix/async.c index e1805c32379..8265a43ab47 100644 --- a/src/unix/async.c +++ b/src/unix/async.c @@ -24,9 +24,9 @@ #include "uv.h" #include "internal.h" -#include "atomic-ops.h" #include +#include #include /* snprintf() */ #include #include @@ -38,8 +38,37 @@ #include #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); int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) { @@ -52,8 +81,9 @@ int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) { uv__handle_init(loop, (uv_handle_t*)handle, UV_ASYNC); handle->async_cb = async_cb; handle->pending = 0; + handle->u.fd = 0; /* This will be used as a busy flag. */ - QUEUE_INSERT_TAIL(&loop->async_handles, &handle->queue); + uv__queue_insert_tail(&loop->async_handles, &handle->queue); uv__handle_start(handle); return 0; @@ -61,46 +91,54 @@ int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) { int uv_async_send(uv_async_t* handle) { + _Atomic int* pending; + _Atomic int* busy; + + pending = (_Atomic int*) &handle->pending; + busy = (_Atomic int*) &handle->u.fd; + /* Do a cheap read first. */ - if (ACCESS_ONCE(int, handle->pending) != 0) + if (atomic_load_explicit(pending, memory_order_relaxed) != 0) return 0; - /* Tell the other thread we're busy with the handle. */ - if (cmpxchgi(&handle->pending, 0, 1) != 0) - return 0; + /* Set the loop to busy. */ + atomic_fetch_add(busy, 1); /* Wake up the other thread's event loop. */ - uv__async_send(handle->loop); + if (atomic_exchange(pending, 1) == 0) + uv__async_send(handle->loop); - /* Tell the other thread we're done. */ - if (cmpxchgi(&handle->pending, 1, 2) != 1) - abort(); + /* Set the loop to not-busy. */ + atomic_fetch_add(busy, -1); return 0; } -/* Only call this from the event loop thread. */ -static int uv__async_spin(uv_async_t* handle) { +/* Wait for the busy flag to clear before closing. + * Only call this from the event loop thread. */ +static void uv__async_spin(uv_async_t* handle) { + _Atomic int* pending; + _Atomic int* busy; int i; - int rc; + + pending = (_Atomic int*) &handle->pending; + busy = (_Atomic int*) &handle->u.fd; + + /* Set the pending flag first, so no new events will be added by other + * threads after this function returns. */ + atomic_store(pending, 1); for (;;) { - /* 997 is not completely chosen at random. It's a prime number, acyclical - * by nature, and should therefore hopefully dampen sympathetic resonance. + /* 997 is not completely chosen at random. It's a prime number, acyclic by + * nature, and should therefore hopefully dampen sympathetic resonance. */ for (i = 0; i < 997; i++) { - /* rc=0 -- handle is not pending. - * rc=1 -- handle is pending, other thread is still working with it. - * rc=2 -- handle is pending, other thread is done. - */ - rc = cmpxchgi(&handle->pending, 2, 0); - - if (rc != 1) - return rc; + if (atomic_load(busy) == 0) + return; /* Other thread is busy with this handle, spin until it's done. */ - cpu_relax(); + uv__cpu_relax(); } /* Yield the CPU. We may have preempted the other thread while it's @@ -114,7 +152,7 @@ static int uv__async_spin(uv_async_t* handle) { void uv__async_close(uv_async_t* handle) { uv__async_spin(handle); - QUEUE_REMOVE(&handle->queue); + uv__queue_remove(&handle->queue); uv__handle_stop(handle); } @@ -122,13 +160,18 @@ void uv__async_close(uv_async_t* handle) { static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { char buf[1024]; ssize_t r; - QUEUE queue; - QUEUE* q; + struct uv__queue queue; + struct uv__queue* q; uv_async_t* h; + _Atomic int *pending; 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)) @@ -146,16 +189,18 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { abort(); } - QUEUE_MOVE(&loop->async_handles, &queue); - while (!QUEUE_EMPTY(&queue)) { - q = QUEUE_HEAD(&queue); - h = QUEUE_DATA(q, uv_async_t, queue); + uv__queue_move(&loop->async_handles, &queue); + while (!uv__queue_empty(&queue)) { + q = uv__queue_head(&queue); + h = uv__queue_data(q, uv_async_t, queue); - QUEUE_REMOVE(q); - QUEUE_INSERT_TAIL(&loop->async_handles, q); + uv__queue_remove(q); + uv__queue_insert_tail(&loop->async_handles, q); - if (0 == uv__async_spin(h)) - continue; /* Not pending. */ + /* Atomically fetch and clear pending flag */ + pending = (_Atomic int*) &h->pending; + if (atomic_exchange(pending, 0) == 0) + continue; if (h->async_cb == NULL) continue; @@ -182,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 @@ -202,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; @@ -213,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("/dev/null", O_RDONLY); + if (err < 0) + return err; + + pipefd[0] = err; + pipefd[1] = -1; + + /* When using EVFILT_USER event to wake up the kqueue, this event must be + * registered beforehand. Otherwise, calling kevent() to issue an + * unregistered EVFILT_USER event will get an ENOENT. + * Since uv__async_send() may happen before uv__io_poll() with multi-threads, + * we can't defer this registration of EVFILT_USER event as we did for other + * events, but must perform it right away. */ + EV_SET(&ev, err, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, 0); + err = kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL); + if (err < 0) + return UV__ERR(errno); + } else { + err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE); + if (err < 0) + return err; + } #else err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE); if (err < 0) @@ -223,24 +312,79 @@ 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; } +void uv__async_stop(uv_loop_t* loop) { + struct uv__queue queue; + struct uv__queue* q; + uv_async_t* h; + + if (loop->async_io_watcher.fd == -1) + return; + + /* Make sure no other thread is accessing the async handle fd after the loop + * cleanup. + */ + uv__queue_move(&loop->async_handles, &queue); + while (!uv__queue_empty(&queue)) { + q = uv__queue_head(&queue); + h = uv__queue_data(q, uv_async_t, queue); + + uv__queue_remove(q); + uv__queue_insert_tail(&loop->async_handles, q); + + uv__async_spin(h); + } + + if (loop->async_wfd != -1) { + if (loop->async_wfd != loop->async_io_watcher.fd) + uv__close(loop->async_wfd); + loop->async_wfd = -1; + } + + uv__io_stop(loop, &loop->async_io_watcher, POLLIN); + uv__close(loop->async_io_watcher.fd); + loop->async_io_watcher.fd = -1; +} + + int uv__async_fork(uv_loop_t* loop) { + struct uv__queue queue; + struct uv__queue* q; + uv_async_t* h; + if (loop->async_io_watcher.fd == -1) /* never started */ return 0; - uv__async_stop(loop); + uv__queue_move(&loop->async_handles, &queue); + while (!uv__queue_empty(&queue)) { + q = uv__queue_head(&queue); + h = uv__queue_data(q, uv_async_t, queue); - return uv__async_start(loop); -} + uv__queue_remove(q); + uv__queue_insert_tail(&loop->async_handles, q); + /* The state of any thread that set pending is now likely corrupt in this + * child because the user called fork, so just clear these flags and move + * on. Calling most libc functions after `fork` is declared to be undefined + * behavior anyways, unless async-signal-safe, for multithreaded programs + * like libuv, and nothing interesting in pthreads is async-signal-safe. + */ + h->pending = 0; + /* This is the busy flag, and we just abruptly lost all other threads. */ + h->u.fd = 0; + } -void uv__async_stop(uv_loop_t* loop) { - if (loop->async_io_watcher.fd == -1) - return; - + /* Recreate these, since they still exist, but belong to the wrong pid now. */ if (loop->async_wfd != -1) { if (loop->async_wfd != loop->async_io_watcher.fd) uv__close(loop->async_wfd); @@ -250,4 +394,19 @@ void uv__async_stop(uv_loop_t* loop) { uv__io_stop(loop, &loop->async_io_watcher, POLLIN); uv__close(loop->async_io_watcher.fd); loop->async_io_watcher.fd = -1; + + return uv__async_start(loop); +} + + +static void uv__cpu_relax(void) { +#if defined(__i386__) || defined(__x86_64__) + __asm__ __volatile__ ("rep; nop" ::: "memory"); /* a.k.a. PAUSE */ +#elif (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__) + __asm__ __volatile__ ("yield" ::: "memory"); +#elif (defined(__ppc__) || defined(__ppc64__)) && defined(__APPLE__) + __asm volatile ("" : : : "memory"); +#elif !defined(__APPLE__) && (defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__)) + __asm__ __volatile__ ("or 1,1,1; or 2,2,2" ::: "memory"); +#endif } diff --git a/src/unix/atomic-ops.h b/src/unix/atomic-ops.h deleted file mode 100644 index c48d058435a..00000000000 --- a/src/unix/atomic-ops.h +++ /dev/null @@ -1,63 +0,0 @@ -/* Copyright (c) 2013, Ben Noordhuis - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef UV_ATOMIC_OPS_H_ -#define UV_ATOMIC_OPS_H_ - -#include "internal.h" /* UV_UNUSED */ - -#if defined(__SUNPRO_C) || defined(__SUNPRO_CC) -#include -#endif - -UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)); -UV_UNUSED(static void cpu_relax(void)); - -/* Prefer hand-rolled assembly over the gcc builtins because the latter also - * issue full memory barriers. - */ -UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) { -#if defined(__i386__) || defined(__x86_64__) - int out; - __asm__ __volatile__ ("lock; cmpxchg %2, %1;" - : "=a" (out), "+m" (*(volatile int*) ptr) - : "r" (newval), "0" (oldval) - : "memory"); - return out; -#elif defined(__MVS__) - unsigned int op4; - if (__plo_CSST(ptr, (unsigned int*) &oldval, newval, - (unsigned int*) ptr, *ptr, &op4)) - return oldval; - else - return op4; -#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) - return atomic_cas_uint((uint_t *)ptr, (uint_t)oldval, (uint_t)newval); -#else - return __sync_val_compare_and_swap(ptr, oldval, newval); -#endif -} - -UV_UNUSED(static void cpu_relax(void)) { -#if defined(__i386__) || defined(__x86_64__) - __asm__ __volatile__ ("rep; nop" ::: "memory"); /* a.k.a. PAUSE */ -#elif (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__) - __asm__ __volatile__ ("yield" ::: "memory"); -#elif defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) - __asm__ __volatile__ ("or 1,1,1; or 2,2,2" ::: "memory"); -#endif -} - -#endif /* UV_ATOMIC_OPS_H_ */ diff --git a/src/unix/bsd-ifaddrs.c b/src/unix/bsd-ifaddrs.c index e48934bce2b..11ca95591fc 100644 --- a/src/unix/bsd-ifaddrs.c +++ b/src/unix/bsd-ifaddrs.c @@ -27,7 +27,7 @@ #include #include -#if !defined(__CYGWIN__) && !defined(__MSYS__) +#if !defined(__CYGWIN__) && !defined(__MSYS__) && !defined(__GNU__) #include #endif @@ -40,7 +40,7 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { return 1; if (ent->ifa_addr == NULL) return 1; -#if !defined(__CYGWIN__) && !defined(__MSYS__) +#if !defined(__CYGWIN__) && !defined(__MSYS__) && !defined(__GNU__) /* * If `exclude_type` is `UV__EXCLUDE_IFPHYS`, return whether `sa_family` * equals `AF_LINK`. Otherwise, the result depends on the operating @@ -69,7 +69,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { struct ifaddrs* addrs; struct ifaddrs* ent; uv_interface_address_t* address; -#if !(defined(__CYGWIN__) || defined(__MSYS__)) +#if !(defined(__CYGWIN__) || defined(__MSYS__)) && !defined(__GNU__) int i; #endif @@ -126,7 +126,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { address++; } -#if !(defined(__CYGWIN__) || defined(__MSYS__)) +#if !(defined(__CYGWIN__) || defined(__MSYS__)) && !defined(__GNU__) /* Fill in physical addresses for each interface */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS)) diff --git a/src/unix/bsd-proctitle.c b/src/unix/bsd-proctitle.c index 4f4e9e5183f..b0c01e2cb85 100644 --- a/src/unix/bsd-proctitle.c +++ b/src/unix/bsd-proctitle.c @@ -38,6 +38,7 @@ static void init_process_title_mutex_once(void) { void uv__process_title_cleanup(void) { + uv_once(&process_title_mutex_once, init_process_title_mutex_once); uv_mutex_destroy(&process_title_mutex); } diff --git a/src/unix/core.c b/src/unix/core.c index 71e9c525c4a..61cbc0d027f 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -20,6 +20,7 @@ #include "uv.h" #include "internal.h" +#include "strtok.h" #include /* NULL */ #include /* printf */ @@ -40,18 +41,22 @@ #include /* writev */ #include /* getrusage */ #include +#include #include #include +#include /* clock_gettime */ #ifdef __sun # include -# include # include #endif #if defined(__APPLE__) +# include +# include # include -# endif /* defined(__APPLE__) */ +# include +#endif /* defined(__APPLE__) */ #if defined(__APPLE__) && !TARGET_OS_IPHONE @@ -65,13 +70,14 @@ extern char** environ; #if defined(__DragonFly__) || \ defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ defined(__NetBSD__) || \ defined(__OpenBSD__) # include # include # include +# include # if defined(__FreeBSD__) +# include # define uv__accept4 accept4 # endif # if defined(__NetBSD__) @@ -80,30 +86,71 @@ extern char** environ; #endif #if defined(__MVS__) -#include +# include +# include "zos-sys-info.h" #endif #if defined(__linux__) +# include # include +# define gettid() syscall(SYS_gettid) # define uv__accept4 accept4 #endif +#if defined(__FreeBSD__) +# include +# include +#endif + +#if defined(__NetBSD__) +# include +#endif + #if defined(__linux__) && defined(__SANITIZE_THREAD__) && defined(__clang__) # include #endif -static int uv__run_pending(uv_loop_t* loop); +static void uv__run_pending(uv_loop_t* loop); /* Verify that uv_buf_t is ABI-compatible with struct iovec. */ STATIC_ASSERT(sizeof(uv_buf_t) == sizeof(struct iovec)); -STATIC_ASSERT(sizeof(&((uv_buf_t*) 0)->base) == +STATIC_ASSERT(sizeof(((uv_buf_t*) 0)->base) == sizeof(((struct iovec*) 0)->iov_base)); -STATIC_ASSERT(sizeof(&((uv_buf_t*) 0)->len) == +STATIC_ASSERT(sizeof(((uv_buf_t*) 0)->len) == sizeof(((struct iovec*) 0)->iov_len)); STATIC_ASSERT(offsetof(uv_buf_t, base) == offsetof(struct iovec, iov_base)); STATIC_ASSERT(offsetof(uv_buf_t, len) == offsetof(struct iovec, iov_len)); +/* https://github.com/libuv/libuv/issues/1674 */ +int uv_clock_gettime(uv_clock_id clock_id, uv_timespec64_t* ts) { + struct timespec t; + int r; + + if (ts == NULL) + return UV_EFAULT; + + switch (clock_id) { + default: + return UV_EINVAL; + case UV_CLOCK_MONOTONIC: + r = clock_gettime(CLOCK_MONOTONIC, &t); + break; + case UV_CLOCK_REALTIME: + r = clock_gettime(CLOCK_REALTIME, &t); + break; + } + + if (r) + return UV__ERR(errno); + + ts->tv_sec = t.tv_sec; + ts->tv_nsec = t.tv_nsec; + + return 0; +} + + uint64_t uv_hrtime(void) { return uv__hrtime(UV_CLOCK_PRECISE); } @@ -121,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: @@ -158,6 +205,15 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { case UV_FS_EVENT: uv__fs_event_close((uv_fs_event_t*)handle); +#if defined(__sun) || defined(__MVS__) + /* + * On Solaris, illumos, and z/OS we will not be able to dissociate the + * watcher for an event which is pending delivery, so we cannot always call + * uv__make_close_pending() straight away. The backend will call the + * function once the event has cleared. + */ + return; +#endif break; case UV_POLL: @@ -220,10 +276,10 @@ int uv__getiovmax(void) { #if defined(IOV_MAX) return IOV_MAX; #elif defined(_SC_IOV_MAX) - static int iovmax_cached = -1; + static _Atomic int iovmax_cached = -1; int iovmax; - iovmax = uv__load_relaxed(&iovmax_cached); + iovmax = atomic_load_explicit(&iovmax_cached, memory_order_relaxed); if (iovmax != -1) return iovmax; @@ -235,7 +291,7 @@ int uv__getiovmax(void) { if (iovmax == -1) iovmax = 1; - uv__store_relaxed(&iovmax_cached, iovmax); + atomic_store_explicit(&iovmax_cached, iovmax, memory_order_relaxed); return iovmax; #else @@ -301,7 +357,7 @@ static void uv__finish_close(uv_handle_t* handle) { } uv__handle_unref(handle); - QUEUE_REMOVE(&handle->handle_queue); + uv__queue_remove(&handle->handle_queue); if (handle->close_cb) { handle->close_cb(handle); @@ -334,60 +390,80 @@ int uv_backend_fd(const uv_loop_t* loop) { } -int uv_backend_timeout(const uv_loop_t* loop) { - if (loop->stop_flag != 0) - return 0; - - if (!uv__has_active_handles(loop) && !uv__has_active_reqs(loop)) - return 0; - - if (!QUEUE_EMPTY(&loop->idle_handles)) - return 0; - - if (!QUEUE_EMPTY(&loop->pending_queue)) - return 0; +static int uv__loop_alive(const uv_loop_t* loop) { + return uv__has_active_handles(loop) || + uv__has_active_reqs(loop) || + !uv__queue_empty(&loop->pending_queue) || + loop->closing_handles != NULL; +} - if (loop->closing_handles) - return 0; - return uv__next_timeout(loop); +static int uv__backend_timeout(const uv_loop_t* loop) { + if (loop->stop_flag == 0 && + /* uv__loop_alive(loop) && */ + (uv__has_active_handles(loop) || uv__has_active_reqs(loop)) && + uv__queue_empty(&loop->pending_queue) && + uv__queue_empty(&loop->idle_handles) && + (loop->flags & UV_LOOP_REAP_CHILDREN) == 0 && + loop->closing_handles == NULL) + return uv__next_timeout(loop); + return 0; } -static int uv__loop_alive(const uv_loop_t* loop) { - return uv__has_active_handles(loop) || - uv__has_active_reqs(loop) || - loop->closing_handles != NULL; +int uv_backend_timeout(const uv_loop_t* loop) { + if (uv__queue_empty(&loop->watcher_queue)) + return uv__backend_timeout(loop); + /* Need to call uv_run to update the backend fd state. */ + return 0; } int uv_loop_alive(const uv_loop_t* loop) { - return uv__loop_alive(loop); + return uv__loop_alive(loop); } int uv_run(uv_loop_t* loop, uv_run_mode mode) { int timeout; int r; - int ran_pending; + int can_sleep; r = uv__loop_alive(loop); if (!r) uv__update_time(loop); - while (r != 0 && loop->stop_flag == 0) { + /* Maintain backwards compatibility by processing timers before entering the + * while loop for UV_RUN_DEFAULT. Otherwise timers only need to be executed + * once, which should be done after polling in order to maintain proper + * execution order of the conceptual event loop. */ + if (mode == UV_RUN_DEFAULT && r != 0 && loop->stop_flag == 0) { uv__update_time(loop); uv__run_timers(loop); - ran_pending = uv__run_pending(loop); + } + + while (r != 0 && loop->stop_flag == 0) { + can_sleep = + uv__queue_empty(&loop->pending_queue) && + uv__queue_empty(&loop->idle_handles); + + uv__run_pending(loop); uv__run_idle(loop); uv__run_prepare(loop); timeout = 0; - if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT) - timeout = uv_backend_timeout(loop); + if ((mode == UV_RUN_ONCE && can_sleep) || mode == UV_RUN_DEFAULT) + timeout = uv__backend_timeout(loop); + + uv__metrics_inc_loop_count(loop); uv__io_poll(loop, timeout); + /* Process immediate callbacks (e.g. write_cb) a small fixed number of + * times to avoid loop starvation.*/ + for (r = 0; r < 8 && !uv__queue_empty(&loop->pending_queue); r++) + uv__run_pending(loop); + /* Run one final update on the provider_idle_time in case uv__io_poll * returned because the timeout expired, but no events were received. This * call will be ignored if the provider_entry_time was either never set (if @@ -398,18 +474,8 @@ int uv_run(uv_loop_t* loop, uv_run_mode mode) { uv__run_check(loop); uv__run_closing_handles(loop); - if (mode == UV_RUN_ONCE) { - /* UV_RUN_ONCE implies forward progress: at least one callback must have - * been invoked when it returns. uv__io_poll() can return without doing - * I/O (meaning: no callbacks) when its timeout expires - which means we - * have pending timers that satisfy the forward progress constraint. - * - * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from - * the check. - */ - uv__update_time(loop); - uv__run_timers(loop); - } + uv__update_time(loop); + uv__run_timers(loop); r = uv__loop_alive(loop); if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT) @@ -597,20 +663,6 @@ int uv__nonblock_ioctl(int fd, int set) { return 0; } - - -int uv__cloexec_ioctl(int fd, int set) { - int r; - - do - r = ioctl(fd, set ? FIOCLEX : FIONCLEX); - while (r == -1 && errno == EINTR); - - if (r) - return UV__ERR(errno); - - return 0; -} #endif @@ -645,25 +697,13 @@ int uv__nonblock_fcntl(int fd, int set) { } -int uv__cloexec_fcntl(int fd, int set) { +int uv__cloexec(int fd, int set) { int flags; int r; - do - r = fcntl(fd, F_GETFD); - while (r == -1 && errno == EINTR); - - if (r == -1) - return UV__ERR(errno); - - /* Bail out now if already set/clear. */ - if (!!(r & FD_CLOEXEC) == !!set) - return 0; - + flags = 0; if (set) - flags = r | FD_CLOEXEC; - else - flags = r & ~FD_CLOEXEC; + flags = FD_CLOEXEC; do r = fcntl(fd, F_SETFD, flags); @@ -677,28 +717,23 @@ int uv__cloexec_fcntl(int fd, int set) { ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) { - struct cmsghdr* cmsg; +#if defined(__ANDROID__) || \ + defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__NetBSD__) || \ + defined(__OpenBSD__) || \ + defined(__linux__) ssize_t rc; + rc = recvmsg(fd, msg, flags | MSG_CMSG_CLOEXEC); + if (rc == -1) + return UV__ERR(errno); + return rc; +#else + struct cmsghdr* cmsg; int* pfd; int* end; -#if defined(__linux__) - static int no_msg_cmsg_cloexec; - if (0 == uv__load_relaxed(&no_msg_cmsg_cloexec)) { - rc = recvmsg(fd, msg, flags | 0x40000000); /* MSG_CMSG_CLOEXEC */ - if (rc != -1) - return rc; - if (errno != EINVAL) - return UV__ERR(errno); - rc = recvmsg(fd, msg, flags); - if (rc == -1) - return UV__ERR(errno); - uv__store_relaxed(&no_msg_cmsg_cloexec, 1); - } else { - rc = recvmsg(fd, msg, flags); - } -#else + ssize_t rc; rc = recvmsg(fd, msg, flags); -#endif if (rc == -1) return UV__ERR(errno); if (msg->msg_controllen == 0) @@ -711,13 +746,14 @@ ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) { pfd += 1) uv__cloexec(*pfd, 1); return rc; +#endif } 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... */ @@ -803,25 +839,20 @@ int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd) { } -static int uv__run_pending(uv_loop_t* loop) { - QUEUE* q; - QUEUE pq; +static void uv__run_pending(uv_loop_t* loop) { + struct uv__queue* q; + struct uv__queue pq; uv__io_t* w; - if (QUEUE_EMPTY(&loop->pending_queue)) - return 0; - - QUEUE_MOVE(&loop->pending_queue, &pq); + uv__queue_move(&loop->pending_queue, &pq); - while (!QUEUE_EMPTY(&pq)) { - q = QUEUE_HEAD(&pq); - QUEUE_REMOVE(q); - QUEUE_INIT(q); - w = QUEUE_DATA(q, uv__io_t, pending_queue); + while (!uv__queue_empty(&pq)) { + q = uv__queue_head(&pq); + uv__queue_remove(q); + uv__queue_init(q); + w = uv__queue_data(q, uv__io_t, pending_queue); w->cb(loop, w, POLLOUT); } - - return 1; } @@ -874,17 +905,12 @@ static void maybe_resize(uv_loop_t* loop, unsigned int len) { void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd) { assert(cb != NULL); assert(fd >= -1); - QUEUE_INIT(&w->pending_queue); - QUEUE_INIT(&w->watcher_queue); + uv__queue_init(&w->pending_queue); + uv__queue_init(&w->watcher_queue); w->cb = cb; w->fd = fd; w->events = 0; w->pevents = 0; - -#if defined(UV_HAVE_KQUEUE) - w->rcount = 0; - w->wcount = 0; -#endif /* defined(UV_HAVE_KQUEUE) */ } @@ -906,8 +932,8 @@ void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) { return; #endif - if (QUEUE_EMPTY(&w->watcher_queue)) - QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); + if (uv__queue_empty(&w->watcher_queue)) + uv__queue_insert_tail(&loop->watcher_queue, &w->watcher_queue); if (loop->watchers[w->fd] == NULL) { loop->watchers[w->fd] = w; @@ -932,8 +958,8 @@ void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) { w->pevents &= ~events; if (w->pevents == 0) { - QUEUE_REMOVE(&w->watcher_queue); - QUEUE_INIT(&w->watcher_queue); + uv__queue_remove(&w->watcher_queue); + uv__queue_init(&w->watcher_queue); w->events = 0; if (w == loop->watchers[w->fd]) { @@ -942,14 +968,14 @@ void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) { loop->nfds--; } } - else if (QUEUE_EMPTY(&w->watcher_queue)) - QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); + else if (uv__queue_empty(&w->watcher_queue)) + uv__queue_insert_tail(&loop->watcher_queue, &w->watcher_queue); } void uv__io_close(uv_loop_t* loop, uv__io_t* w) { uv__io_stop(loop, w, POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI); - QUEUE_REMOVE(&w->pending_queue); + uv__queue_remove(&w->pending_queue); /* Remove stale events for this file descriptor */ if (w->fd != -1) @@ -958,8 +984,8 @@ void uv__io_close(uv_loop_t* loop, uv__io_t* w) { void uv__io_feed(uv_loop_t* loop, uv__io_t* w) { - if (QUEUE_EMPTY(&w->pending_queue)) - QUEUE_INSERT_TAIL(&loop->pending_queue, &w->pending_queue); + if (uv__queue_empty(&w->pending_queue)) + uv__queue_insert_tail(&loop->pending_queue, &w->pending_queue); } @@ -975,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; @@ -1004,10 +1030,61 @@ int uv_getrusage(uv_rusage_t* rusage) { rusage->ru_nivcsw = usage.ru_nivcsw; #endif + /* Most platforms report ru_maxrss in kilobytes; macOS and Solaris are + * the outliers because of course they are. + */ +#if defined(__APPLE__) + rusage->ru_maxrss /= 1024; /* macOS and iOS report bytes. */ +#elif defined(__sun) + 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_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; @@ -1036,6 +1113,32 @@ int uv__open_cloexec(const char* path, int flags) { } +int uv__slurp(const char* filename, char* buf, size_t len) { + ssize_t n; + int fd; + + assert(len > 0); + + fd = uv__open_cloexec(filename, O_RDONLY); + if (fd < 0) + return fd; + + do + n = read(fd, buf, len - 1); + while (n == -1 && errno == EINTR); + + if (uv__close_nocheckstdio(fd)) + abort(); + + if (n < 0) + return UV__ERR(errno); + + buf[n] = '\0'; + + return 0; +} + + int uv__dup2_cloexec(int oldfd, int newfd) { #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__linux__) int r; @@ -1077,8 +1180,8 @@ int uv_os_homedir(char* buffer, size_t* size) { if (r != UV_ENOENT) return r; - /* HOME is not set, so call uv__getpwuid_r() */ - r = uv__getpwuid_r(&pwd); + /* HOME is not set, so call uv_os_get_passwd() */ + r = uv_os_get_passwd(&pwd); if (r != 0) { return r; @@ -1151,33 +1254,23 @@ int uv_os_tmpdir(char* buffer, size_t* size) { } -int uv__getpwuid_r(uv_passwd_t* pwd) { +static int uv__getpwuid_r(uv_passwd_t *pwd, uid_t uid) { struct passwd pw; struct passwd* result; char* buf; - uid_t uid; size_t bufsize; size_t name_size; size_t homedir_size; size_t shell_size; - long initsize; int r; if (pwd == NULL) return UV_EINVAL; - initsize = sysconf(_SC_GETPW_R_SIZE_MAX); - - if (initsize <= 0) - bufsize = 4096; - else - bufsize = (size_t) initsize; - - uid = geteuid(); - buf = NULL; - - for (;;) { - uv__free(buf); + /* Calling sysconf(_SC_GETPW_R_SIZE_MAX) would get the suggested size, but it + * is frequently 1024 or 4096, so we can just use that directly. The pwent + * will not usually be large. */ + for (bufsize = 2000;; bufsize *= 2) { buf = uv__malloc(bufsize); if (buf == NULL) @@ -1187,21 +1280,18 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { r = getpwuid_r(uid, &pw, buf, bufsize, &result); while (r == EINTR); + if (r != 0 || result == NULL) + uv__free(buf); + if (r != ERANGE) break; - - bufsize *= 2; } - if (r != 0) { - uv__free(buf); + if (r != 0) return UV__ERR(r); - } - if (result == NULL) { - uv__free(buf); + if (result == NULL) return UV_ENOENT; - } /* Allocate memory for the username, shell, and home directory */ name_size = strlen(pw.pw_name) + 1; @@ -1235,24 +1325,98 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { } -void uv_os_free_passwd(uv_passwd_t* pwd) { - if (pwd == NULL) - return; +int uv_os_get_group(uv_group_t* grp, uv_uid_t gid) { +#if defined(__ANDROID__) && __ANDROID_API__ < 24 + /* This function getgrgid_r() was added in Android N (level 24) */ + return UV_ENOSYS; +#else + struct group gp; + struct group* result; + char* buf; + char* gr_mem; + size_t bufsize; + size_t name_size; + long members; + size_t mem_size; + int r; - /* - The memory for name, shell, and homedir are allocated in a single - uv__malloc() call. The base of the pointer is stored in pwd->username, so - that is the field that needs to be freed. - */ - uv__free(pwd->username); - pwd->username = NULL; - pwd->shell = NULL; - pwd->homedir = NULL; + if (grp == NULL) + return UV_EINVAL; + + /* Calling sysconf(_SC_GETGR_R_SIZE_MAX) would get the suggested size, but it + * is frequently 1024 or 4096, so we can just use that directly. The pwent + * will not usually be large. */ + for (bufsize = 2000;; bufsize *= 2) { + buf = uv__malloc(bufsize); + + if (buf == NULL) + return UV_ENOMEM; + + do + r = getgrgid_r(gid, &gp, buf, bufsize, &result); + while (r == EINTR); + + if (r != 0 || result == NULL) + uv__free(buf); + + if (r != ERANGE) + break; + } + + if (r != 0) + return UV__ERR(r); + + if (result == NULL) + return UV_ENOENT; + + /* Allocate memory for the groupname and members. */ + name_size = strlen(gp.gr_name) + 1; + members = 0; + mem_size = sizeof(char*); + for (r = 0; gp.gr_mem[r] != NULL; r++) { + mem_size += strlen(gp.gr_mem[r]) + 1 + sizeof(char*); + members++; + } + + gr_mem = uv__malloc(name_size + mem_size); + if (gr_mem == NULL) { + uv__free(buf); + return UV_ENOMEM; + } + + /* Copy the members */ + grp->members = (char**) gr_mem; + grp->members[members] = NULL; + gr_mem = (char*) &grp->members[members + 1]; + for (r = 0; r < members; r++) { + grp->members[r] = gr_mem; + strcpy(gr_mem, gp.gr_mem[r]); + gr_mem += strlen(gr_mem) + 1; + } + assert(gr_mem == (char*)grp->members + mem_size); + + /* Copy the groupname */ + grp->groupname = gr_mem; + memcpy(grp->groupname, gp.gr_name, name_size); + gr_mem += name_size; + + /* Copy the gid */ + grp->gid = gp.gr_gid; + + uv__free(buf); + + return 0; +#endif } int uv_os_get_passwd(uv_passwd_t* pwd) { - return uv__getpwuid_r(pwd); + return uv__getpwuid_r(pwd, geteuid()); +} + + +int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid) { + return uv__getpwuid_r(pwd, uid); } @@ -1413,6 +1577,13 @@ uv_pid_t uv_os_getppid(void) { return getppid(); } +int uv_cpumask_size(void) { +#if UV__CPU_AFFINITY_SUPPORTED + return CPU_SETSIZE; +#else + return UV_ENOTSUP; +#endif +} int uv_os_getpriority(uv_pid_t pid, int* priority) { int r; @@ -1441,6 +1612,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; @@ -1554,6 +1854,7 @@ int uv__search_path(const char* prog, char* buf, size_t* buflen) { char* cloned_path; char* path_env; char* token; + char* itr; if (buf == NULL || buflen == NULL || *buflen == 0) return UV_EINVAL; @@ -1595,7 +1896,7 @@ int uv__search_path(const char* prog, char* buf, size_t* buflen) { if (cloned_path == NULL) return UV_ENOMEM; - token = strtok(cloned_path, ":"); + token = uv__strtok(cloned_path, ":", &itr); while (token != NULL) { snprintf(trypath, sizeof(trypath) - 1, "%s/%s", token, prog); if (realpath(trypath, abspath) == abspath) { @@ -1614,10 +1915,168 @@ int uv__search_path(const char* prog, char* buf, size_t* buflen) { return 0; } } - token = strtok(NULL, ":"); + token = uv__strtok(NULL, ":", &itr); } uv__free(cloned_path); /* Out of tokens (path entries), and no match found */ 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; + + memset(&set, 0, sizeof(set)); + + /* sysconf(_SC_NPROCESSORS_ONLN) in musl calls sched_getaffinity() but in + * glibc it's... complicated... so for consistency try sched_getaffinity() + * before falling back to sysconf(_SC_NPROCESSORS_ONLN). + */ + if (0 == sched_getaffinity(0, sizeof(set), &set)) + rc = uv__cpu_count(&set); +#elif defined(__MVS__) + rc = __get_num_online_cpus(); + if (rc < 1) + rc = 1; + + return (unsigned) 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__ */ + + if (rc < 1) + rc = 1; + + return (unsigned) rc; +} + +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; +} diff --git a/src/unix/cygwin.c b/src/unix/cygwin.c index 169958d55f2..4913108223f 100644 --- a/src/unix/cygwin.c +++ b/src/unix/cygwin.c @@ -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) { @@ -51,3 +87,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { uint64_t uv_get_constrained_memory(void) { return 0; /* Memory constraints are unknown. */ } + +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} diff --git a/src/unix/darwin-proctitle.c b/src/unix/darwin-proctitle.c index 5288083ef04..5e5642972a4 100644 --- a/src/unix/darwin-proctitle.c +++ b/src/unix/darwin-proctitle.c @@ -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: diff --git a/src/unix/darwin-stub.h b/src/unix/darwin-stub.h index 433e3efa730..b93cf67c596 100644 --- a/src/unix/darwin-stub.h +++ b/src/unix/darwin-stub.h @@ -27,7 +27,6 @@ struct CFArrayCallBacks; struct CFRunLoopSourceContext; struct FSEventStreamContext; -struct CFRange; typedef double CFAbsoluteTime; typedef double CFTimeInterval; @@ -43,23 +42,13 @@ typedef unsigned CFStringEncoding; typedef void* CFAllocatorRef; typedef void* CFArrayRef; typedef void* CFBundleRef; -typedef void* CFDataRef; typedef void* CFDictionaryRef; -typedef void* CFMutableDictionaryRef; -typedef struct CFRange CFRange; typedef void* CFRunLoopRef; typedef void* CFRunLoopSourceRef; typedef void* CFStringRef; typedef void* CFTypeRef; typedef void* FSEventStreamRef; -typedef uint32_t IOOptionBits; -typedef unsigned int io_iterator_t; -typedef unsigned int io_object_t; -typedef unsigned int io_service_t; -typedef unsigned int io_registry_entry_t; - - typedef void (*FSEventStreamCallback)(const FSEventStreamRef, void*, size_t, @@ -80,11 +69,6 @@ struct FSEventStreamContext { void* pad[3]; }; -struct CFRange { - CFIndex location; - CFIndex length; -}; - static const CFStringEncoding kCFStringEncodingUTF8 = 0x8000100; static const OSStatus noErr = 0; diff --git a/src/unix/darwin-syscalls.h b/src/unix/darwin-syscalls.h new file mode 100644 index 00000000000..dc2d1bd234b --- /dev/null +++ b/src/unix/darwin-syscalls.h @@ -0,0 +1,17 @@ +#ifndef UV_DARWIN_SYSCALLS_H_ +#define UV_DARWIN_SYSCALLS_H_ + +#include +#include + +/* 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_ */ diff --git a/src/unix/darwin.c b/src/unix/darwin.c index 62f04d31542..009efbefaa7 100644 --- a/src/unix/darwin.c +++ b/src/unix/darwin.c @@ -25,7 +25,6 @@ #include #include -#include #include #include #include /* _NSGetExecutablePath */ @@ -33,13 +32,9 @@ #include #include /* sysconf */ -#include "darwin-stub.h" - static uv_once_t once = UV_ONCE_INIT; -static uint64_t (*time_func)(void); static mach_timebase_info_data_t timebase; -typedef unsigned char UInt8; int uv__platform_loop_init(uv_loop_t* loop) { loop->cf_state = NULL; @@ -59,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; } @@ -110,7 +101,7 @@ uint64_t uv_get_free_memory(void) { if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&info, &count) != KERN_SUCCESS) { - return UV_EINVAL; /* FIXME(bnoordhuis) Translate error. */ + return 0; } return (uint64_t) info.free_count * sysconf(_SC_PAGESIZE); @@ -123,7 +114,7 @@ uint64_t uv_get_total_memory(void) { size_t size = sizeof(info); if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) - return UV__ERR(errno); + return 0; return (uint64_t) info; } @@ -134,6 +125,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); @@ -183,159 +179,17 @@ int uv_uptime(double* uptime) { return 0; } -static int uv__get_cpu_speed(uint64_t* speed) { - /* IOKit */ - void (*pIOObjectRelease)(io_object_t); - kern_return_t (*pIOMasterPort)(mach_port_t, mach_port_t*); - CFMutableDictionaryRef (*pIOServiceMatching)(const char*); - kern_return_t (*pIOServiceGetMatchingServices)(mach_port_t, - CFMutableDictionaryRef, - io_iterator_t*); - io_service_t (*pIOIteratorNext)(io_iterator_t); - CFTypeRef (*pIORegistryEntryCreateCFProperty)(io_registry_entry_t, - CFStringRef, - CFAllocatorRef, - IOOptionBits); - - /* CoreFoundation */ - CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef, - const char*, - CFStringEncoding); - CFStringEncoding (*pCFStringGetSystemEncoding)(void); - UInt8 *(*pCFDataGetBytePtr)(CFDataRef); - CFIndex (*pCFDataGetLength)(CFDataRef); - void (*pCFDataGetBytes)(CFDataRef, CFRange, UInt8*); - void (*pCFRelease)(CFTypeRef); - - void* core_foundation_handle; - void* iokit_handle; - int err; - - kern_return_t kr; - mach_port_t mach_port; - io_iterator_t it; - io_object_t service; - - mach_port = 0; - - err = UV_ENOENT; - core_foundation_handle = dlopen("/System/Library/Frameworks/" - "CoreFoundation.framework/" - "CoreFoundation", - RTLD_LAZY | RTLD_LOCAL); - iokit_handle = dlopen("/System/Library/Frameworks/IOKit.framework/" - "IOKit", - RTLD_LAZY | RTLD_LOCAL); - - if (core_foundation_handle == NULL || iokit_handle == NULL) - goto out; - -#define V(handle, symbol) \ - do { \ - *(void **)(&p ## symbol) = dlsym((handle), #symbol); \ - if (p ## symbol == NULL) \ - goto out; \ - } \ - while (0) - V(iokit_handle, IOMasterPort); - V(iokit_handle, IOServiceMatching); - V(iokit_handle, IOServiceGetMatchingServices); - V(iokit_handle, IOIteratorNext); - V(iokit_handle, IOObjectRelease); - V(iokit_handle, IORegistryEntryCreateCFProperty); - V(core_foundation_handle, CFStringCreateWithCString); - V(core_foundation_handle, CFStringGetSystemEncoding); - V(core_foundation_handle, CFDataGetBytePtr); - V(core_foundation_handle, CFDataGetLength); - V(core_foundation_handle, CFDataGetBytes); - V(core_foundation_handle, CFRelease); -#undef V - -#define S(s) pCFStringCreateWithCString(NULL, (s), kCFStringEncodingUTF8) - - kr = pIOMasterPort(MACH_PORT_NULL, &mach_port); - assert(kr == KERN_SUCCESS); - CFMutableDictionaryRef classes_to_match - = pIOServiceMatching("IOPlatformDevice"); - kr = pIOServiceGetMatchingServices(mach_port, classes_to_match, &it); - assert(kr == KERN_SUCCESS); - service = pIOIteratorNext(it); - - CFStringRef device_type_str = S("device_type"); - CFStringRef clock_frequency_str = S("clock-frequency"); - - while (service != 0) { - CFDataRef data; - data = pIORegistryEntryCreateCFProperty(service, - device_type_str, - NULL, - 0); - if (data) { - const UInt8* raw = pCFDataGetBytePtr(data); - if (strncmp((char*)raw, "cpu", 3) == 0 || - strncmp((char*)raw, "processor", 9) == 0) { - CFDataRef freq_ref; - freq_ref = pIORegistryEntryCreateCFProperty(service, - clock_frequency_str, - NULL, - 0); - if (freq_ref) { - const UInt8* freq_ref_ptr = pCFDataGetBytePtr(freq_ref); - CFIndex len = pCFDataGetLength(freq_ref); - if (len == 8) - memcpy(speed, freq_ref_ptr, 8); - else if (len == 4) { - uint32_t v; - memcpy(&v, freq_ref_ptr, 4); - *speed = v; - } else { - *speed = 0; - } - - pCFRelease(freq_ref); - pCFRelease(data); - break; - } - } - pCFRelease(data); - } - - service = pIOIteratorNext(it); - } - - pIOObjectRelease(it); - - err = 0; - - if (device_type_str != NULL) - pCFRelease(device_type_str); - if (clock_frequency_str != NULL) - pCFRelease(clock_frequency_str); - -out: - if (core_foundation_handle != NULL) - dlclose(core_foundation_handle); - - if (iokit_handle != NULL) - dlclose(iokit_handle); - - mach_port_deallocate(mach_task_self(), mach_port); - - return err; -} - int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), multiplier = ((uint64_t)1000L / ticks); char model[512]; + uint64_t cpuspeed; size_t size; unsigned int i; natural_t numcpus; mach_msg_type_number_t msg_type; processor_cpu_load_info_data_t *info; uv_cpu_info_t* cpu_info; - uint64_t cpuspeed; - int err; size = sizeof(model); if (sysctlbyname("machdep.cpu.brand_string", &model, &size, NULL, 0) && @@ -343,9 +197,13 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { return UV__ERR(errno); } - err = uv__get_cpu_speed(&cpuspeed); - if (err < 0) - return err; + cpuspeed = 0; + size = sizeof(cpuspeed); + sysctlbyname("hw.cpufrequency", &cpuspeed, &size, NULL, 0); + if (cpuspeed == 0) + /* If sysctl hw.cputype == CPU_TYPE_ARM64, the correct value is unavailable + * from Apple, but we can hard-code it here to a plausible value. */ + cpuspeed = 2400000000U; if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numcpus, (processor_info_array_t*)&info, @@ -371,7 +229,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { cpu_info->cpu_times.irq = 0; cpu_info->model = uv__strdup(model); - cpu_info->speed = cpuspeed/1000000; + cpu_info->speed = (int)(cpuspeed / 1000000); } vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type); diff --git a/src/unix/epoll.c b/src/unix/epoll.c deleted file mode 100644 index 97348e254b4..00000000000 --- a/src/unix/epoll.c +++ /dev/null @@ -1,422 +0,0 @@ -/* Copyright libuv contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include "uv.h" -#include "internal.h" -#include -#include - -int uv__epoll_init(uv_loop_t* loop) { - int fd; - fd = epoll_create1(O_CLOEXEC); - - /* epoll_create1() can fail either because it's not implemented (old kernel) - * or because it doesn't understand the O_CLOEXEC flag. - */ - if (fd == -1 && (errno == ENOSYS || errno == EINVAL)) { - fd = epoll_create(256); - - if (fd != -1) - uv__cloexec(fd, 1); - } - - loop->backend_fd = fd; - if (fd == -1) - return UV__ERR(errno); - - return 0; -} - - -void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { - struct epoll_event* events; - struct epoll_event dummy; - uintptr_t i; - uintptr_t nfds; - - assert(loop->watchers != NULL); - assert(fd >= 0); - - events = (struct epoll_event*) loop->watchers[loop->nwatchers]; - nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; - if (events != NULL) - /* Invalidate events with same file descriptor */ - for (i = 0; i < nfds; i++) - if (events[i].data.fd == fd) - events[i].data.fd = -1; - - /* Remove the file descriptor from the epoll. - * This avoids a problem where the same file description remains open - * in another process, causing repeated junk epoll events. - * - * We pass in a dummy epoll_event, to work around a bug in old kernels. - */ - if (loop->backend_fd >= 0) { - /* 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)); - epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &dummy); - } -} - - -int uv__io_check_fd(uv_loop_t* loop, int fd) { - struct epoll_event e; - int rc; - - memset(&e, 0, sizeof(e)); - e.events = POLLIN; - e.data.fd = -1; - - rc = 0; - if (epoll_ctl(loop->backend_fd, EPOLL_CTL_ADD, fd, &e)) - if (errno != EEXIST) - rc = UV__ERR(errno); - - if (rc == 0) - if (epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &e)) - abort(); - - return rc; -} - - -void uv__io_poll(uv_loop_t* loop, int timeout) { - /* A bug in kernels < 2.6.37 makes timeouts larger than ~30 minutes - * effectively infinite on 32 bits architectures. To avoid blocking - * indefinitely, we cap the timeout and poll again if necessary. - * - * Note that "30 minutes" is a simplification because it depends on - * the value of CONFIG_HZ. The magic constant assumes CONFIG_HZ=1200, - * that being the largest value I have seen in the wild (and only once.) - */ - static const int max_safe_timeout = 1789569; - static int no_epoll_pwait_cached; - static int no_epoll_wait_cached; - int no_epoll_pwait; - int no_epoll_wait; - struct epoll_event events[1024]; - struct epoll_event* pe; - struct epoll_event e; - int real_timeout; - QUEUE* q; - uv__io_t* w; - sigset_t sigset; - uint64_t sigmask; - uint64_t base; - int have_signals; - int nevents; - int count; - int nfds; - int fd; - int op; - int i; - int user_timeout; - int reset_timeout; - - if (loop->nfds == 0) { - assert(QUEUE_EMPTY(&loop->watcher_queue)); - return; - } - - memset(&e, 0, sizeof(e)); - - while (!QUEUE_EMPTY(&loop->watcher_queue)) { - q = QUEUE_HEAD(&loop->watcher_queue); - QUEUE_REMOVE(q); - QUEUE_INIT(q); - - w = QUEUE_DATA(q, uv__io_t, watcher_queue); - assert(w->pevents != 0); - assert(w->fd >= 0); - assert(w->fd < (int) loop->nwatchers); - - e.events = w->pevents; - e.data.fd = w->fd; - - if (w->events == 0) - op = EPOLL_CTL_ADD; - else - op = EPOLL_CTL_MOD; - - /* XXX Future optimization: do EPOLL_CTL_MOD lazily if we stop watching - * events, skip the syscall and squelch the events after epoll_wait(). - */ - if (epoll_ctl(loop->backend_fd, op, w->fd, &e)) { - if (errno != EEXIST) - abort(); - - assert(op == EPOLL_CTL_ADD); - - /* We've reactivated a file descriptor that's been watched before. */ - if (epoll_ctl(loop->backend_fd, EPOLL_CTL_MOD, w->fd, &e)) - abort(); - } - - w->events = w->pevents; - } - - sigmask = 0; - if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { - sigemptyset(&sigset); - sigaddset(&sigset, SIGPROF); - sigmask |= 1 << (SIGPROF - 1); - } - - assert(timeout >= -1); - base = loop->time; - count = 48; /* Benchmarks suggest this gives the best throughput. */ - real_timeout = timeout; - - if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { - reset_timeout = 1; - user_timeout = timeout; - timeout = 0; - } else { - reset_timeout = 0; - user_timeout = 0; - } - - /* You could argue there is a dependency between these two but - * ultimately we don't care about their ordering with respect - * to one another. Worst case, we make a few system calls that - * could have been avoided because another thread already knows - * they fail with ENOSYS. Hardly the end of the world. - */ - no_epoll_pwait = uv__load_relaxed(&no_epoll_pwait_cached); - no_epoll_wait = uv__load_relaxed(&no_epoll_wait_cached); - - for (;;) { - /* 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); - - /* See the comment for max_safe_timeout for an explanation of why - * this is necessary. Executive summary: kernel bug workaround. - */ - if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout) - timeout = max_safe_timeout; - - if (sigmask != 0 && no_epoll_pwait != 0) - if (pthread_sigmask(SIG_BLOCK, &sigset, NULL)) - abort(); - - if (no_epoll_wait != 0 || (sigmask != 0 && no_epoll_pwait == 0)) { - nfds = epoll_pwait(loop->backend_fd, - events, - ARRAY_SIZE(events), - timeout, - &sigset); - if (nfds == -1 && errno == ENOSYS) { - uv__store_relaxed(&no_epoll_pwait_cached, 1); - no_epoll_pwait = 1; - } - } else { - nfds = epoll_wait(loop->backend_fd, - events, - ARRAY_SIZE(events), - timeout); - if (nfds == -1 && errno == ENOSYS) { - uv__store_relaxed(&no_epoll_wait_cached, 1); - no_epoll_wait = 1; - } - } - - if (sigmask != 0 && no_epoll_pwait != 0) - if (pthread_sigmask(SIG_UNBLOCK, &sigset, NULL)) - abort(); - - /* Update loop->time unconditionally. It's tempting to skip the update when - * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the - * operating system didn't reschedule our process while in the syscall. - */ - SAVE_ERRNO(uv__update_time(loop)); - - if (nfds == 0) { - assert(timeout != -1); - - if (reset_timeout != 0) { - timeout = user_timeout; - reset_timeout = 0; - } - - if (timeout == -1) - continue; - - if (timeout == 0) - return; - - /* We may have been inside the system call for longer than |timeout| - * milliseconds so we need to update the timestamp to avoid drift. - */ - goto update_timeout; - } - - if (nfds == -1) { - if (errno == ENOSYS) { - /* epoll_wait() or epoll_pwait() failed, try the other system call. */ - assert(no_epoll_wait == 0 || no_epoll_pwait == 0); - continue; - } - - if (errno != EINTR) - abort(); - - if (reset_timeout != 0) { - timeout = user_timeout; - reset_timeout = 0; - } - - if (timeout == -1) - continue; - - if (timeout == 0) - return; - - /* Interrupted by a signal. Update timeout and poll again. */ - goto update_timeout; - } - - have_signals = 0; - nevents = 0; - - { - /* Squelch a -Waddress-of-packed-member warning with gcc >= 9. */ - union { - struct epoll_event* events; - uv__io_t* watchers; - } x; - - x.events = events; - assert(loop->watchers != NULL); - loop->watchers[loop->nwatchers] = x.watchers; - loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; - } - - for (i = 0; i < nfds; i++) { - pe = events + i; - fd = pe->data.fd; - - /* Skip invalidated events, see uv__platform_invalidate_fd */ - if (fd == -1) - continue; - - assert(fd >= 0); - assert((unsigned) fd < loop->nwatchers); - - w = loop->watchers[fd]; - - if (w == NULL) { - /* File descriptor that we've stopped watching, disarm it. - * - * Ignore all errors because we may be racing with another thread - * when the file descriptor is closed. - */ - epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, pe); - continue; - } - - /* Give users only events they're interested in. Prevents spurious - * callbacks when previous callback invocation in this loop has stopped - * the current watcher. Also, filters out events that users has not - * requested us to watch. - */ - pe->events &= w->pevents | POLLERR | POLLHUP; - - /* Work around an epoll quirk where it sometimes reports just the - * EPOLLERR or EPOLLHUP event. In order to force the event loop to - * move forward, we merge in the read/write events that the watcher - * is interested in; uv__read() and uv__write() will then deal with - * the error or hangup in the usual fashion. - * - * Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user - * reads the available data, calls uv_read_stop(), then sometime later - * calls uv_read_start() again. By then, libuv has forgotten about the - * hangup and the kernel won't report EPOLLIN again because there's - * nothing left to read. If anything, libuv is to blame here. The - * current hack is just a quick bandaid; to properly fix it, libuv - * needs to remember the error/hangup event. We should get that for - * free when we switch over to edge-triggered I/O. - */ - if (pe->events == POLLERR || pe->events == POLLHUP) - pe->events |= - w->pevents & (POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI); - - if (pe->events != 0) { - /* Run signal watchers last. This also affects child process watchers - * because those are implemented in terms of signal watchers. - */ - if (w == &loop->signal_io_watcher) { - have_signals = 1; - } else { - uv__metrics_update_idle_time(loop); - w->cb(loop, w, pe->events); - } - - nevents++; - } - } - - if (reset_timeout != 0) { - timeout = user_timeout; - reset_timeout = 0; - } - - if (have_signals != 0) { - uv__metrics_update_idle_time(loop); - loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); - } - - loop->watchers[loop->nwatchers] = NULL; - loop->watchers[loop->nwatchers + 1] = NULL; - - if (have_signals != 0) - return; /* Event loop should cycle now so don't poll again. */ - - if (nevents != 0) { - if (nfds == ARRAY_SIZE(events) && --count != 0) { - /* Poll for more events but don't block this time. */ - timeout = 0; - continue; - } - return; - } - - if (timeout == 0) - return; - - if (timeout == -1) - continue; - -update_timeout: - assert(timeout > 0); - - real_timeout -= (loop->time - base); - if (real_timeout <= 0) - return; - - timeout = real_timeout; - } -} - diff --git a/src/unix/freebsd.c b/src/unix/freebsd.c index 170b897e202..a6de29c558c 100644 --- a/src/unix/freebsd.c +++ b/src/unix/freebsd.c @@ -26,7 +26,12 @@ #include #include -#include +#if defined(__DragonFly__) +# include +# include +#else +# include +#endif #include #include #include @@ -91,7 +96,7 @@ uint64_t uv_get_free_memory(void) { size_t size = sizeof(freecount); if (sysctlbyname("vm.stats.vm.v_free_count", &freecount, &size, NULL, 0)) - return UV__ERR(errno); + return 0; return (uint64_t) freecount * sysconf(_SC_PAGESIZE); @@ -105,7 +110,7 @@ uint64_t uv_get_total_memory(void) { size_t size = sizeof(info); if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) - return UV__ERR(errno); + return 0; return (uint64_t) info; } @@ -116,6 +121,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); @@ -264,26 +274,17 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { } -int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { -#if __FreeBSD__ >= 11 && !defined(__DragonFly__) - return sendmmsg(fd, - (struct mmsghdr*) mmsg, - vlen, - 0 /* flags */); -#else - return errno = ENOSYS, -1; -#endif -} - - -int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { -#if __FreeBSD__ >= 11 && !defined(__DragonFly__) - return recvmmsg(fd, - (struct mmsghdr*) mmsg, - vlen, - 0 /* flags */, - NULL /* timeout */); +ssize_t +uv__fs_copy_file_range(int fd_in, + off_t* off_in, + int fd_out, + off_t* off_out, + size_t len, + unsigned int flags) +{ +#if __FreeBSD__ >= 13 && !defined(__DragonFly__) + return copy_file_range(fd_in, off_in, fd_out, off_out, len, flags); #else - return errno = ENOSYS, -1; + return errno = ENOSYS, -1; #endif } diff --git a/src/unix/fs.c b/src/unix/fs.c index 362c36c6a0c..1631d9340bc 100644 --- a/src/unix/fs.c +++ b/src/unix/fs.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -41,26 +42,15 @@ #include #include #include -#include #include #include #include -#if defined(__DragonFly__) || \ - defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ - defined(__OpenBSD__) || \ - defined(__NetBSD__) -# define HAVE_PREADV 1 -#else -# define HAVE_PREADV 0 -#endif - #if defined(__linux__) -# include "sys/utsname.h" +# include #endif -#if defined(__linux__) || defined(__sun) +#if defined(__sun) # include # include #endif @@ -79,7 +69,6 @@ #if defined(__APPLE__) || \ defined(__DragonFly__) || \ defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ defined(__OpenBSD__) || \ defined(__NetBSD__) # include @@ -150,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, \ @@ -247,7 +236,8 @@ UV_UNUSED(static struct timeval uv__fs_to_timeval(double time)) { static ssize_t uv__fs_futime(uv_fs_t* req) { #if defined(__linux__) \ || defined(_AIX71) \ - || defined(__HAIKU__) + || defined(__HAIKU__) \ + || defined(__GNU__) struct timespec ts[2]; ts[0] = uv__fs_to_timespec(req->atime); ts[1] = uv__fs_to_timespec(req->mtime); @@ -255,7 +245,6 @@ static ssize_t uv__fs_futime(uv_fs_t* req) { #elif defined(__APPLE__) \ || defined(__DragonFly__) \ || defined(__FreeBSD__) \ - || defined(__FreeBSD_kernel__) \ || defined(__NetBSD__) \ || defined(__OpenBSD__) \ || defined(__sun) @@ -310,7 +299,7 @@ static int uv__fs_mkstemp(uv_fs_t* req) { static uv_once_t once = UV_ONCE_INIT; int r; #ifdef O_CLOEXEC - static int no_cloexec_support; + static _Atomic int no_cloexec_support; #endif static const char pattern[] = "XXXXXX"; static const size_t pattern_size = sizeof(pattern) - 1; @@ -335,7 +324,8 @@ static int uv__fs_mkstemp(uv_fs_t* req) { uv_once(&once, uv__mkostemp_initonce); #ifdef O_CLOEXEC - if (uv__load_relaxed(&no_cloexec_support) == 0 && uv__mkostemp != NULL) { + if (atomic_load_explicit(&no_cloexec_support, memory_order_relaxed) == 0 && + uv__mkostemp != NULL) { r = uv__mkostemp(path, O_CLOEXEC); if (r >= 0) @@ -348,7 +338,7 @@ static int uv__fs_mkstemp(uv_fs_t* req) { /* We set the static variable so that next calls don't even try to use mkostemp. */ - uv__store_relaxed(&no_cloexec_support, 1); + atomic_store_explicit(&no_cloexec_support, 1, memory_order_relaxed); } #endif /* O_CLOEXEC */ @@ -406,139 +396,180 @@ static ssize_t uv__fs_open(uv_fs_t* req) { } -#if !HAVE_PREADV -static ssize_t uv__fs_preadv(uv_file fd, - uv_buf_t* bufs, - unsigned int nbufs, - off_t off) { - uv_buf_t* buf; - uv_buf_t* end; - ssize_t result; - ssize_t rc; - size_t pos; - - assert(nbufs > 0); +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; - result = 0; - pos = 0; - buf = bufs + 0; - end = bufs + nbufs; + total = 0; + for (i = 0; i < (size_t) nbufs; i++) { + p = bufs[i].iov_base; + n = bufs[i].iov_len; - for (;;) { do - rc = pread(fd, buf->base + pos, buf->len - pos, off + result); - while (rc == -1 && errno == EINTR); + if (is_pread) + r = pread(fd, p, n, off); + else + r = pwrite(fd, p, n, off); + while (r == -1 && errno == EINTR); - if (rc == 0) - break; + if (r == -1) { + if (total > 0) + return total; + return -1; + } - if (rc == -1 && result == 0) - return UV__ERR(errno); + off += r; + total += r; - if (rc == -1) - break; /* We read some data so return that, ignore the error. */ + if ((size_t) r < n) + return total; + } - pos += rc; - result += rc; + return total; +} - if (pos < buf->len) - continue; - pos = 0; - buf += 1; +#ifdef __linux__ +typedef int uv__iovcnt; +#else +typedef size_t uv__iovcnt; +#endif - if (buf == end) - break; + +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); } - return result; + 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); } -#endif static ssize_t uv__fs_read(uv_fs_t* req) { -#if defined(__linux__) - static int no_preadv; -#endif + const struct iovec* bufs; unsigned int iovmax; - ssize_t result; + size_t nbufs; + ssize_t r; + off_t off; + int fd; + + fd = req->file; + off = req->off; + bufs = (const struct iovec*) req->bufs; + nbufs = req->nbufs; iovmax = uv__getiovmax(); - if (req->nbufs > iovmax) - req->nbufs = iovmax; - - if (req->off < 0) { - if (req->nbufs == 1) - result = read(req->file, req->bufs[0].base, req->bufs[0].len); - else - result = readv(req->file, (struct iovec*) req->bufs, req->nbufs); + if (nbufs > iovmax) + nbufs = iovmax; + + r = 0; + if (off < 0) { + if (nbufs == 1) + r = read(fd, bufs->iov_base, bufs->iov_len); + else if (nbufs > 1) + r = readv(fd, bufs, nbufs); } else { - if (req->nbufs == 1) { - result = pread(req->file, req->bufs[0].base, req->bufs[0].len, req->off); - goto done; - } - -#if HAVE_PREADV - result = preadv(req->file, (struct iovec*) req->bufs, req->nbufs, req->off); -#else -# if defined(__linux__) - if (uv__load_relaxed(&no_preadv)) retry: -# endif - { - result = uv__fs_preadv(req->file, req->bufs, req->nbufs, req->off); - } -# if defined(__linux__) - else { - result = uv__preadv(req->file, - (struct iovec*)req->bufs, - req->nbufs, - req->off); - if (result == -1 && errno == ENOSYS) { - uv__store_relaxed(&no_preadv, 1); - goto retry; - } - } -# endif -#endif + if (nbufs == 1) + r = pread(fd, bufs->iov_base, bufs->iov_len, off); + else if (nbufs > 1) + r = uv__preadv(fd, bufs, nbufs, off); } -done: - /* Early cleanup of bufs allocation, since we're done with it. */ - if (req->bufs != req->bufsml) - uv__free(req->bufs); - - req->bufs = NULL; - req->nbufs = 0; - #ifdef __PASE__ /* PASE returns EOPNOTSUPP when reading a directory, convert to EISDIR */ - if (result == -1 && errno == EOPNOTSUPP) { + if (r == -1 && errno == EOPNOTSUPP) { struct stat buf; ssize_t rc; - rc = fstat(req->file, &buf); + rc = uv__fstat(fd, &buf); if (rc == 0 && S_ISDIR(buf.st_mode)) { errno = EISDIR; } } #endif - return result; -} + /* We don't own the buffer list in the synchronous case. */ + if (req->cb != NULL) + if (req->bufs != req->bufsml) + uv__free(req->bufs); + req->bufs = NULL; + req->nbufs = 0; -#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_8) -#define UV_CONST_DIRENT uv__dirent_t -#else -#define UV_CONST_DIRENT const uv__dirent_t -#endif + return r; +} -static int uv__fs_scandir_filter(UV_CONST_DIRENT* dent) { +static int uv__fs_scandir_filter(const uv__dirent_t* dent) { return strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0; } -static int uv__fs_scandir_sort(UV_CONST_DIRENT** a, UV_CONST_DIRENT** b) { +static int uv__fs_scandir_sort(const uv__dirent_t** a, const uv__dirent_t** b) { return strcmp((*a)->d_name, (*b)->d_name); } @@ -714,7 +745,7 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) { /* We may not have a real PATH_MAX. Read size of link. */ struct stat st; int ret; - ret = lstat(req->path, &st); + ret = uv__lstat(req->path, &st); if (ret != 0) return -1; if (!S_ISLNK(st.st_mode)) { @@ -764,14 +795,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); @@ -906,31 +946,6 @@ static ssize_t uv__fs_sendfile_emul(uv_fs_t* req) { #ifdef __linux__ -static unsigned uv__kernel_version(void) { - static unsigned cached_version; - struct utsname u; - unsigned version; - unsigned major; - unsigned minor; - unsigned patch; - - version = uv__load_relaxed(&cached_version); - if (version != 0) - return version; - - if (-1 == uname(&u)) - return 0; - - if (3 != sscanf(u.release, "%u.%u.%u", &major, &minor, &patch)) - return 0; - - version = major * 65536 + minor * 256 + patch; - uv__store_relaxed(&cached_version, version); - - return version; -} - - /* Pre-4.20 kernels have a bug where CephFS uses the RADOS copy-from command * in copy_file_range() when it shouldn't. There is no workaround except to * fall back to a regular copy. @@ -967,10 +982,10 @@ static int uv__is_cifs_or_smb(int fd) { static ssize_t uv__fs_try_copy_file_range(int in_fd, off_t* off, int out_fd, size_t len) { - static int no_copy_file_range_support; + static _Atomic int no_copy_file_range_support; ssize_t r; - if (uv__load_relaxed(&no_copy_file_range_support)) { + if (atomic_load_explicit(&no_copy_file_range_support, memory_order_relaxed)) { errno = ENOSYS; return -1; } @@ -989,7 +1004,7 @@ static ssize_t uv__fs_try_copy_file_range(int in_fd, off_t* off, errno = ENOSYS; /* Use fallback. */ break; case ENOSYS: - uv__store_relaxed(&no_copy_file_range_support, 1); + atomic_store_explicit(&no_copy_file_range_support, 1, memory_order_relaxed); break; case EPERM: /* It's been reported that CIFS spuriously fails. @@ -1060,10 +1075,10 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) { return -1; } -#elif defined(__APPLE__) || \ - defined(__DragonFly__) || \ - defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) +/* 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; @@ -1074,17 +1089,19 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) { */ #if defined(__FreeBSD__) || defined(__DragonFly__) +#if defined(__FreeBSD__) + off_t off; + + off = req->off; + r = uv__fs_copy_file_range(in_fd, &off, out_fd, NULL, req->bufsml[0].len, 0); + if (r >= 0) { + r = off - req->off; + req->off = off; + return r; + } +#endif len = 0; r = sendfile(in_fd, out_fd, req->off, req->bufsml[0].len, NULL, &len, 0); -#elif defined(__FreeBSD_kernel__) - len = 0; - r = bsd_sendfile(in_fd, - out_fd, - req->off, - req->bufsml[0].len, - NULL, - &len, - 0); #else /* The darwin sendfile takes len as an input for the length to send, * so make sure to initialize it with the caller's value. */ @@ -1136,7 +1153,6 @@ static ssize_t uv__fs_utime(uv_fs_t* req) { #elif defined(__APPLE__) \ || defined(__DragonFly__) \ || defined(__FreeBSD__) \ - || defined(__FreeBSD_kernel__) \ || defined(__NetBSD__) \ || defined(__OpenBSD__) struct timeval tv[2]; @@ -1168,7 +1184,9 @@ static ssize_t uv__fs_lutime(uv_fs_t* req) { #if defined(__linux__) || \ defined(_AIX71) || \ defined(__sun) || \ - defined(__HAIKU__) + defined(__HAIKU__) || \ + defined(__GNU__) || \ + defined(__OpenBSD__) struct timespec ts[2]; ts[0] = uv__fs_to_timespec(req->atime); ts[1] = uv__fs_to_timespec(req->mtime); @@ -1176,7 +1194,6 @@ static ssize_t uv__fs_lutime(uv_fs_t* req) { #elif defined(__APPLE__) || \ defined(__DragonFly__) || \ defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ defined(__NetBSD__) struct timeval tv[2]; tv[0] = uv__fs_to_timeval(req->atime); @@ -1190,71 +1207,41 @@ static ssize_t uv__fs_lutime(uv_fs_t* req) { static ssize_t uv__fs_write(uv_fs_t* req) { -#if defined(__linux__) - static int no_pwritev; -#endif + const struct iovec* bufs; + size_t nbufs; ssize_t r; + off_t off; + int fd; - /* Serialize writes on OS X, concurrent write() and pwrite() calls result in - * data loss. We can't use a per-file descriptor lock, the descriptor may be - * a dup(). - */ -#if defined(__APPLE__) - static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; - - if (pthread_mutex_lock(&lock)) - abort(); -#endif + fd = req->file; + off = req->off; + bufs = (const struct iovec*) req->bufs; + nbufs = req->nbufs; - if (req->off < 0) { - if (req->nbufs == 1) - r = write(req->file, req->bufs[0].base, req->bufs[0].len); - else - r = writev(req->file, (struct iovec*) req->bufs, req->nbufs); + r = 0; + if (off < 0) { + if (nbufs == 1) + r = write(fd, bufs->iov_base, bufs->iov_len); + else if (nbufs > 1) + r = writev(fd, bufs, nbufs); } else { - if (req->nbufs == 1) { - r = pwrite(req->file, req->bufs[0].base, req->bufs[0].len, req->off); - goto done; - } -#if HAVE_PREADV - r = pwritev(req->file, (struct iovec*) req->bufs, req->nbufs, req->off); -#else -# if defined(__linux__) - if (no_pwritev) retry: -# endif - { - r = pwrite(req->file, req->bufs[0].base, req->bufs[0].len, req->off); - } -# if defined(__linux__) - else { - r = uv__pwritev(req->file, - (struct iovec*) req->bufs, - req->nbufs, - req->off); - if (r == -1 && errno == ENOSYS) { - no_pwritev = 1; - goto retry; - } - } -# endif -#endif + if (nbufs == 1) + r = pwrite(fd, bufs->iov_base, bufs->iov_len, off); + else if (nbufs > 1) + r = uv__pwritev(fd, bufs, nbufs, off); } -done: -#if defined(__APPLE__) - if (pthread_mutex_unlock(&lock)) - abort(); -#endif - return r; } + static ssize_t uv__fs_copyfile(uv_fs_t* req) { uv_fs_t fs_req; uv_file srcfd; uv_file dstfd; struct stat src_statsbuf; struct stat dst_statsbuf; + struct timespec times[2]; int dst_flags; int result; int err; @@ -1274,7 +1261,7 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { return srcfd; /* Get the source file's mode. */ - if (fstat(srcfd, &src_statsbuf)) { + if (uv__fstat(srcfd, &src_statsbuf)) { err = UV__ERR(errno); goto out; } @@ -1302,7 +1289,7 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { destination are not the same file. If they are the same, bail out early. */ if ((req->flags & UV_FS_COPYFILE_EXCL) == 0) { /* Get the destination file's mode. */ - if (fstat(dstfd, &dst_statsbuf)) { + if (uv__fstat(dstfd, &dst_statsbuf)) { err = UV__ERR(errno); goto out; } @@ -1316,10 +1303,51 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { /* Truncate the file in case the destination already existed. */ if (ftruncate(dstfd, 0) != 0) { err = UV__ERR(errno); - goto out; + + /* ftruncate() on ceph-fuse fails with EACCES when the file is created + * with read only permissions. Since ftruncate() on a newly created + * file is a meaningless operation anyway, detect that condition + * and squelch the error. + */ + if (err != UV_EACCES) + goto out; + + if (dst_statsbuf.st_size > 0) + goto out; + + err = 0; } } + /** + * 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__ @@ -1500,14 +1528,14 @@ static int uv__fs_statx(int fd, uv_stat_t* buf) { STATIC_ASSERT(UV_ENOSYS != -1); #ifdef __linux__ - static int no_statx; + static _Atomic int no_statx; struct uv__statx statxbuf; int dirfd; int flags; int mode; int rc; - if (uv__load_relaxed(&no_statx)) + if (atomic_load_explicit(&no_statx, memory_order_relaxed)) return UV_ENOSYS; dirfd = AT_FDCWD; @@ -1541,30 +1569,11 @@ static int uv__fs_statx(int fd, * implemented, rc might return 1 with 0 set as the error code in which * case we return ENOSYS. */ - uv__store_relaxed(&no_statx, 1); + atomic_store_explicit(&no_statx, 1, memory_order_relaxed); return UV_ENOSYS; } - buf->st_dev = makedev(statxbuf.stx_dev_major, statxbuf.stx_dev_minor); - buf->st_mode = statxbuf.stx_mode; - buf->st_nlink = statxbuf.stx_nlink; - buf->st_uid = statxbuf.stx_uid; - buf->st_gid = statxbuf.stx_gid; - buf->st_rdev = makedev(statxbuf.stx_rdev_major, statxbuf.stx_rdev_minor); - buf->st_ino = statxbuf.stx_ino; - buf->st_size = statxbuf.stx_size; - buf->st_blksize = statxbuf.stx_blksize; - buf->st_blocks = statxbuf.stx_blocks; - buf->st_atim.tv_sec = statxbuf.stx_atime.tv_sec; - buf->st_atim.tv_nsec = statxbuf.stx_atime.tv_nsec; - buf->st_mtim.tv_sec = statxbuf.stx_mtime.tv_sec; - buf->st_mtim.tv_nsec = statxbuf.stx_mtime.tv_nsec; - buf->st_ctim.tv_sec = statxbuf.stx_ctime.tv_sec; - buf->st_ctim.tv_nsec = statxbuf.stx_ctime.tv_nsec; - buf->st_birthtim.tv_sec = statxbuf.stx_btime.tv_sec; - buf->st_birthtim.tv_nsec = statxbuf.stx_btime.tv_nsec; - buf->st_flags = 0; - buf->st_gen = 0; + uv__statx_to_stat(&statxbuf, buf); return 0; #else @@ -1581,7 +1590,7 @@ static int uv__fs_stat(const char *path, uv_stat_t *buf) { if (ret != UV_ENOSYS) return ret; - ret = stat(path, &pbuf); + ret = uv__stat(path, &pbuf); if (ret == 0) uv__to_stat(&pbuf, buf); @@ -1597,7 +1606,7 @@ static int uv__fs_lstat(const char *path, uv_stat_t *buf) { if (ret != UV_ENOSYS) return ret; - ret = lstat(path, &pbuf); + ret = uv__lstat(path, &pbuf); if (ret == 0) uv__to_stat(&pbuf, buf); @@ -1613,7 +1622,7 @@ static int uv__fs_fstat(int fd, uv_stat_t *buf) { if (ret != UV_ENOSYS) return ret; - ret = fstat(fd, &pbuf); + ret = uv__fstat(fd, &pbuf); if (ret == 0) uv__to_stat(&pbuf, buf); @@ -1756,7 +1765,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); @@ -1767,6 +1776,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, @@ -1808,6 +1827,9 @@ int uv_fs_chown(uv_loop_t* loop, int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { INIT(CLOSE); req->file = file; + if (cb != NULL) + if (uv__iou_fs_close(loop, req)) + return 0; POST; } @@ -1855,6 +1877,9 @@ int uv_fs_lchown(uv_loop_t* loop, int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { INIT(FDATASYNC); req->file = file; + if (cb != NULL) + if (uv__iou_fs_fsync_or_fdatasync(loop, req, /* IORING_FSYNC_DATASYNC */ 1)) + return 0; POST; } @@ -1862,6 +1887,9 @@ int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { INIT(FSTAT); req->file = file; + if (cb != NULL) + if (uv__iou_fs_statx(loop, req, /* is_fstat */ 1, /* is_lstat */ 0)) + return 0; POST; } @@ -1869,6 +1897,9 @@ int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { INIT(FSYNC); req->file = file; + if (cb != NULL) + if (uv__iou_fs_fsync_or_fdatasync(loop, req, /* no flags */ 0)) + return 0; POST; } @@ -1881,6 +1912,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; } @@ -1915,6 +1949,9 @@ int uv_fs_lutime(uv_loop_t* loop, int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { INIT(LSTAT); PATH; + if (cb != NULL) + if (uv__iou_fs_statx(loop, req, /* is_fstat */ 0, /* is_lstat */ 1)) + return 0; POST; } @@ -1926,6 +1963,9 @@ int uv_fs_link(uv_loop_t* loop, uv_fs_cb cb) { INIT(LINK); PATH2; + if (cb != NULL) + if (uv__iou_fs_link(loop, req)) + return 0; POST; } @@ -1938,6 +1978,9 @@ int uv_fs_mkdir(uv_loop_t* loop, INIT(MKDIR); PATH; req->mode = mode; + if (cb != NULL) + if (uv__iou_fs_mkdir(loop, req)) + return 0; POST; } @@ -1976,6 +2019,9 @@ int uv_fs_open(uv_loop_t* loop, PATH; req->flags = flags; req->mode = mode; + if (cb != NULL) + if (uv__iou_fs_open(loop, req)) + return 0; POST; } @@ -1991,9 +2037,14 @@ int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, if (bufs == NULL || nbufs == 0) return UV_EINVAL; + req->off = off; req->file = file; - + req->bufs = (uv_buf_t*) bufs; /* Safe, doesn't mutate |bufs| */ req->nbufs = nbufs; + + if (cb == NULL) + goto post; + req->bufs = req->bufsml; if (nbufs > ARRAY_SIZE(req->bufsml)) req->bufs = uv__malloc(nbufs * sizeof(*bufs)); @@ -2003,7 +2054,10 @@ int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, memcpy(req->bufs, bufs, nbufs * sizeof(*bufs)); - req->off = off; + if (uv__iou_fs_read_or_write(loop, req, /* is_read */ 1)) + return 0; + +post: POST; } @@ -2081,6 +2135,9 @@ int uv_fs_rename(uv_loop_t* loop, uv_fs_cb cb) { INIT(RENAME); PATH2; + if (cb != NULL) + if (uv__iou_fs_rename(loop, req)) + return 0; POST; } @@ -2111,6 +2168,9 @@ int uv_fs_sendfile(uv_loop_t* loop, int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { INIT(STAT); PATH; + if (cb != NULL) + if (uv__iou_fs_statx(loop, req, /* is_fstat */ 0, /* is_lstat */ 0)) + return 0; POST; } @@ -2124,6 +2184,9 @@ int uv_fs_symlink(uv_loop_t* loop, INIT(SYMLINK); PATH2; req->flags = flags; + if (cb != NULL) + if (uv__iou_fs_symlink(loop, req)) + return 0; POST; } @@ -2131,6 +2194,9 @@ int uv_fs_symlink(uv_loop_t* loop, int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { INIT(UNLINK); PATH; + if (cb != NULL) + if (uv__iou_fs_unlink(loop, req)) + return 0; POST; } @@ -2174,6 +2240,11 @@ int uv_fs_write(uv_loop_t* loop, memcpy(req->bufs, bufs, nbufs * sizeof(*bufs)); req->off = off; + + if (cb != NULL) + if (uv__iou_fs_read_or_write(loop, req, /* is_read */ 0)) + return 0; + POST; } @@ -2182,7 +2253,7 @@ void uv_fs_req_cleanup(uv_fs_t* req) { if (req == NULL) return; - /* Only necessary for asychronous requests, i.e., requests with a callback. + /* Only necessary for asynchronous requests, i.e., requests with a callback. * Synchronous ones don't copy their arguments and have req->path and * req->new_path pointing to user-owned memory. UV_FS_MKDTEMP and * UV_FS_MKSTEMP are the exception to the rule, they always allocate memory. diff --git a/src/unix/fsevents.c b/src/unix/fsevents.c index bf4f1f6a518..7fb6bb2ec36 100644 --- a/src/unix/fsevents.c +++ b/src/unix/fsevents.c @@ -80,13 +80,13 @@ enum uv__cf_loop_signal_type_e { typedef enum uv__cf_loop_signal_type_e uv__cf_loop_signal_type_t; struct uv__cf_loop_signal_s { - QUEUE member; + struct uv__queue member; uv_fs_event_t* handle; uv__cf_loop_signal_type_t type; }; struct uv__fsevents_event_s { - QUEUE member; + struct uv__queue member; int events; char path[1]; }; @@ -98,7 +98,7 @@ struct uv__cf_loop_state_s { FSEventStreamRef fsevent_stream; uv_sem_t fsevent_sem; uv_mutex_t fsevent_mutex; - void* fsevent_handles[2]; + struct uv__queue fsevent_handles; unsigned int fsevent_handle_count; }; @@ -132,7 +132,6 @@ static void (*pCFRunLoopWakeUp)(CFRunLoopRef); static CFStringRef (*pCFStringCreateWithFileSystemRepresentation)( CFAllocatorRef, const char*); -static CFStringEncoding (*pCFStringGetSystemEncoding)(void); static CFStringRef (*pkCFRunLoopDefaultMode); static FSEventStreamRef (*pFSEventStreamCreate)(CFAllocatorRef, FSEventStreamCallback, @@ -141,7 +140,6 @@ static FSEventStreamRef (*pFSEventStreamCreate)(CFAllocatorRef, FSEventStreamEventId, CFTimeInterval, FSEventStreamCreateFlags); -static void (*pFSEventStreamFlushSync)(FSEventStreamRef); static void (*pFSEventStreamInvalidate)(FSEventStreamRef); static void (*pFSEventStreamRelease)(FSEventStreamRef); static void (*pFSEventStreamScheduleWithRunLoop)(FSEventStreamRef, @@ -152,22 +150,22 @@ static void (*pFSEventStreamStop)(FSEventStreamRef); #define UV__FSEVENTS_PROCESS(handle, block) \ do { \ - QUEUE events; \ - QUEUE* q; \ + struct uv__queue events; \ + struct uv__queue* q; \ uv__fsevents_event_t* event; \ int err; \ uv_mutex_lock(&(handle)->cf_mutex); \ /* Split-off all events and empty original queue */ \ - QUEUE_MOVE(&(handle)->cf_events, &events); \ + uv__queue_move(&(handle)->cf_events, &events); \ /* Get error (if any) and zero original one */ \ err = (handle)->cf_error; \ (handle)->cf_error = 0; \ uv_mutex_unlock(&(handle)->cf_mutex); \ /* Loop through events, deallocating each after processing */ \ - while (!QUEUE_EMPTY(&events)) { \ - q = QUEUE_HEAD(&events); \ - event = QUEUE_DATA(q, uv__fsevents_event_t, member); \ - QUEUE_REMOVE(q); \ + while (!uv__queue_empty(&events)) { \ + q = uv__queue_head(&events); \ + event = uv__queue_data(q, uv__fsevents_event_t, member); \ + uv__queue_remove(q); \ /* NOTE: Checking uv__is_active() is required here, because handle \ * callback may close handle and invoking it after it will lead to \ * incorrect behaviour */ \ @@ -195,14 +193,14 @@ static void uv__fsevents_cb(uv_async_t* cb) { /* Runs in CF thread, pushed event into handle's event list */ static void uv__fsevents_push_event(uv_fs_event_t* handle, - QUEUE* events, + struct uv__queue* events, int err) { assert(events != NULL || err != 0); uv_mutex_lock(&handle->cf_mutex); /* Concatenate two queues */ if (events != NULL) - QUEUE_ADD(&handle->cf_events, events); + uv__queue_add(&handle->cf_events, events); /* Propagate error */ if (err != 0) @@ -226,12 +224,12 @@ static void uv__fsevents_event_cb(const FSEventStreamRef streamRef, char* path; char* pos; uv_fs_event_t* handle; - QUEUE* q; + struct uv__queue* q; uv_loop_t* loop; uv__cf_loop_state_t* state; uv__fsevents_event_t* event; FSEventStreamEventFlags flags; - QUEUE head; + struct uv__queue head; loop = info; state = loop->cf_state; @@ -240,9 +238,9 @@ static void uv__fsevents_event_cb(const FSEventStreamRef streamRef, /* For each handle */ uv_mutex_lock(&state->fsevent_mutex); - QUEUE_FOREACH(q, &state->fsevent_handles) { - handle = QUEUE_DATA(q, uv_fs_event_t, cf_member); - QUEUE_INIT(&head); + uv__queue_foreach(q, &state->fsevent_handles) { + handle = uv__queue_data(q, uv_fs_event_t, cf_member); + uv__queue_init(&head); /* Process and filter out events */ for (i = 0; i < numEvents; i++) { @@ -278,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 @@ -320,10 +314,10 @@ static void uv__fsevents_event_cb(const FSEventStreamRef streamRef, event->events = UV_CHANGE; } - QUEUE_INSERT_TAIL(&head, &event->member); + uv__queue_insert_tail(&head, &event->member); } - if (!QUEUE_EMPTY(&head)) + if (!uv__queue_empty(&head)) uv__fsevents_push_event(handle, &head, 0); } uv_mutex_unlock(&state->fsevent_mutex); @@ -331,8 +325,9 @@ static void uv__fsevents_event_cb(const FSEventStreamRef streamRef, /* Runs in CF thread */ -static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) { - uv__cf_loop_state_t* state; +static int uv__fsevents_create_stream(uv__cf_loop_state_t* state, + uv_loop_t* loop, + CFArrayRef paths) { FSEventStreamContext ctx; FSEventStreamRef ref; CFAbsoluteTime latency; @@ -373,10 +368,7 @@ static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) { flags); assert(ref != NULL); - state = loop->cf_state; - pFSEventStreamScheduleWithRunLoop(ref, - state->loop, - *pkCFRunLoopDefaultMode); + pFSEventStreamScheduleWithRunLoop(ref, state->loop, *pkCFRunLoopDefaultMode); if (!pFSEventStreamStart(ref)) { pFSEventStreamInvalidate(ref); pFSEventStreamRelease(ref); @@ -389,11 +381,7 @@ static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) { /* Runs in CF thread */ -static void uv__fsevents_destroy_stream(uv_loop_t* loop) { - uv__cf_loop_state_t* state; - - state = loop->cf_state; - +static void uv__fsevents_destroy_stream(uv__cf_loop_state_t* state) { if (state->fsevent_stream == NULL) return; @@ -408,10 +396,10 @@ static void uv__fsevents_destroy_stream(uv_loop_t* loop) { /* Runs in CF thread, when there're new fsevent handles to add to stream */ -static void uv__fsevents_reschedule(uv_fs_event_t* handle, +static void uv__fsevents_reschedule(uv__cf_loop_state_t* state, + uv_loop_t* loop, uv__cf_loop_signal_type_t type) { - uv__cf_loop_state_t* state; - QUEUE* q; + struct uv__queue* q; uv_fs_event_t* curr; CFArrayRef cf_paths; CFStringRef* paths; @@ -419,7 +407,6 @@ static void uv__fsevents_reschedule(uv_fs_event_t* handle, int err; unsigned int path_count; - state = handle->loop->cf_state; paths = NULL; cf_paths = NULL; err = 0; @@ -438,7 +425,7 @@ static void uv__fsevents_reschedule(uv_fs_event_t* handle, uv_mutex_unlock(&state->fsevent_mutex); /* Destroy previous FSEventStream */ - uv__fsevents_destroy_stream(handle->loop); + uv__fsevents_destroy_stream(state); /* Any failure below will be a memory failure */ err = UV_ENOMEM; @@ -455,9 +442,9 @@ static void uv__fsevents_reschedule(uv_fs_event_t* handle, q = &state->fsevent_handles; for (; i < path_count; i++) { - q = QUEUE_NEXT(q); + q = uv__queue_next(q); assert(q != &state->fsevent_handles); - curr = QUEUE_DATA(q, uv_fs_event_t, cf_member); + curr = uv__queue_data(q, uv_fs_event_t, cf_member); assert(curr->realpath != NULL); paths[i] = @@ -478,7 +465,7 @@ static void uv__fsevents_reschedule(uv_fs_event_t* handle, err = UV_ENOMEM; goto final; } - err = uv__fsevents_create_stream(handle->loop, cf_paths); + err = uv__fsevents_create_stream(state, loop, cf_paths); } final: @@ -495,8 +482,8 @@ static void uv__fsevents_reschedule(uv_fs_event_t* handle, /* Broadcast error to all handles */ uv_mutex_lock(&state->fsevent_mutex); - QUEUE_FOREACH(q, &state->fsevent_handles) { - curr = QUEUE_DATA(q, uv_fs_event_t, cf_member); + uv__queue_foreach(q, &state->fsevent_handles) { + curr = uv__queue_data(q, uv_fs_event_t, cf_member); uv__fsevents_push_event(curr, NULL, err); } uv_mutex_unlock(&state->fsevent_mutex); @@ -563,10 +550,8 @@ static int uv__fsevents_global_init(void) { V(core_foundation_handle, CFRunLoopStop); V(core_foundation_handle, CFRunLoopWakeUp); V(core_foundation_handle, CFStringCreateWithFileSystemRepresentation); - V(core_foundation_handle, CFStringGetSystemEncoding); V(core_foundation_handle, kCFRunLoopDefaultMode); V(core_services_handle, FSEventStreamCreate); - V(core_services_handle, FSEventStreamFlushSync); V(core_services_handle, FSEventStreamInvalidate); V(core_services_handle, FSEventStreamRelease); V(core_services_handle, FSEventStreamScheduleWithRunLoop); @@ -617,7 +602,7 @@ static int uv__fsevents_loop_init(uv_loop_t* loop) { if (err) goto fail_sem_init; - QUEUE_INIT(&loop->cf_signals); + uv__queue_init(&loop->cf_signals); err = uv_sem_init(&state->fsevent_sem, 0); if (err) @@ -627,7 +612,7 @@ static int uv__fsevents_loop_init(uv_loop_t* loop) { if (err) goto fail_fsevent_mutex_init; - QUEUE_INIT(&state->fsevent_handles); + uv__queue_init(&state->fsevent_handles); state->fsevent_need_reschedule = 0; state->fsevent_handle_count = 0; @@ -686,7 +671,7 @@ static int uv__fsevents_loop_init(uv_loop_t* loop) { void uv__fsevents_loop_delete(uv_loop_t* loop) { uv__cf_loop_signal_t* s; uv__cf_loop_state_t* state; - QUEUE* q; + struct uv__queue* q; if (loop->cf_state == NULL) return; @@ -699,10 +684,10 @@ void uv__fsevents_loop_delete(uv_loop_t* loop) { uv_mutex_destroy(&loop->cf_mutex); /* Free any remaining data */ - while (!QUEUE_EMPTY(&loop->cf_signals)) { - q = QUEUE_HEAD(&loop->cf_signals); - s = QUEUE_DATA(q, uv__cf_loop_signal_t, member); - QUEUE_REMOVE(q); + while (!uv__queue_empty(&loop->cf_signals)) { + q = uv__queue_head(&loop->cf_signals); + s = uv__queue_data(q, uv__cf_loop_signal_t, member); + uv__queue_remove(q); uv__free(s); } @@ -746,28 +731,28 @@ static void* uv__cf_loop_runner(void* arg) { static void uv__cf_loop_cb(void* arg) { uv_loop_t* loop; uv__cf_loop_state_t* state; - QUEUE* item; - QUEUE split_head; + struct uv__queue* item; + struct uv__queue split_head; uv__cf_loop_signal_t* s; loop = arg; state = loop->cf_state; uv_mutex_lock(&loop->cf_mutex); - QUEUE_MOVE(&loop->cf_signals, &split_head); + uv__queue_move(&loop->cf_signals, &split_head); uv_mutex_unlock(&loop->cf_mutex); - while (!QUEUE_EMPTY(&split_head)) { - item = QUEUE_HEAD(&split_head); - QUEUE_REMOVE(item); + while (!uv__queue_empty(&split_head)) { + item = uv__queue_head(&split_head); + uv__queue_remove(item); - s = QUEUE_DATA(item, uv__cf_loop_signal_t, member); + s = uv__queue_data(item, uv__cf_loop_signal_t, member); /* This was a termination signal */ if (s->handle == NULL) pCFRunLoopStop(state->loop); else - uv__fsevents_reschedule(s->handle, s->type); + uv__fsevents_reschedule(state, loop, s->type); uv__free(s); } @@ -789,7 +774,7 @@ int uv__cf_loop_signal(uv_loop_t* loop, item->type = type; uv_mutex_lock(&loop->cf_mutex); - QUEUE_INSERT_TAIL(&loop->cf_signals, &item->member); + uv__queue_insert_tail(&loop->cf_signals, &item->member); state = loop->cf_state; assert(state != NULL); @@ -804,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; @@ -812,13 +798,17 @@ 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 */ - QUEUE_INIT(&handle->cf_events); + uv__queue_init(&handle->cf_events); handle->cf_error = 0; /* @@ -843,7 +833,7 @@ int uv__fsevents_init(uv_fs_event_t* handle) { /* Insert handle into the list */ state = handle->loop->cf_state; uv_mutex_lock(&state->fsevent_mutex); - QUEUE_INSERT_TAIL(&state->fsevent_handles, &handle->cf_member); + uv__queue_insert_tail(&state->fsevent_handles, &handle->cf_member); state->fsevent_handle_count++; state->fsevent_need_reschedule = 1; uv_mutex_unlock(&state->fsevent_mutex); @@ -883,7 +873,7 @@ int uv__fsevents_close(uv_fs_event_t* handle) { /* Remove handle from the list */ state = handle->loop->cf_state; uv_mutex_lock(&state->fsevent_mutex); - QUEUE_REMOVE(&handle->cf_member); + uv__queue_remove(&handle->cf_member); state->fsevent_handle_count--; state->fsevent_need_reschedule = 1; uv_mutex_unlock(&state->fsevent_mutex); diff --git a/src/unix/getaddrinfo.c b/src/unix/getaddrinfo.c index 77337ace945..b7075343666 100644 --- a/src/unix/getaddrinfo.c +++ b/src/unix/getaddrinfo.c @@ -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) diff --git a/src/unix/getnameinfo.c b/src/unix/getnameinfo.c index 991002a67d7..959b4c6a821 100644 --- a/src/unix/getnameinfo.c +++ b/src/unix/getnameinfo.c @@ -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) { diff --git a/src/unix/haiku.c b/src/unix/haiku.c index cf17d836b4c..31284b66dc3 100644 --- a/src/unix/haiku.c +++ b/src/unix/haiku.c @@ -84,6 +84,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + int uv_resident_set_memory(size_t* rss) { area_info area; ssize_t cookie; diff --git a/src/unix/hurd.c b/src/unix/hurd.c new file mode 100644 index 00000000000..63c878123f1 --- /dev/null +++ b/src/unix/hurd.c @@ -0,0 +1,172 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#define _GNU_SOURCE 1 + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +int uv_exepath(char* buffer, size_t* size) { + kern_return_t err; + /* XXX in current Hurd, strings are char arrays of 1024 elements */ + string_t exepath; + ssize_t copied; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + if (*size - 1 > 0) { + /* XXX limited length of buffer in current Hurd, this API will probably + * evolve in the future */ + err = proc_get_exe(getproc(), getpid(), exepath); + + if (err) + return UV__ERR(err); + } + + copied = uv__strscpy(buffer, exepath, *size); + + /* do not return error on UV_E2BIG failure */ + *size = copied < 0 ? strlen(buffer) : (size_t) copied; + + return 0; +} + +int uv_resident_set_memory(size_t* rss) { + kern_return_t err; + struct task_basic_info bi; + mach_msg_type_number_t count; + + count = TASK_BASIC_INFO_COUNT; + err = task_info(mach_task_self(), TASK_BASIC_INFO, + (task_info_t) &bi, &count); + + if (err) + return UV__ERR(err); + + *rss = bi.resident_size; + + return 0; +} + +uint64_t uv_get_free_memory(void) { + kern_return_t err; + struct vm_statistics vmstats; + + err = vm_statistics(mach_task_self(), &vmstats); + + if (err) + return 0; + + return vmstats.free_count * vm_page_size; +} + + +uint64_t uv_get_total_memory(void) { + kern_return_t err; + host_basic_info_data_t hbi; + mach_msg_type_number_t cnt; + + cnt = HOST_BASIC_INFO_COUNT; + err = host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t) &hbi, &cnt); + + if (err) + return 0; + + return hbi.memory_size; +} + + +int uv_uptime(double* uptime) { + char buf[128]; + + /* Try /proc/uptime first */ + if (0 == uv__slurp("/proc/uptime", buf, sizeof(buf))) + if (1 == sscanf(buf, "%lf", uptime)) + return 0; + + /* Reimplement here code from procfs to calculate uptime if not mounted? */ + + return UV__ERR(EIO); +} + +void uv_loadavg(double avg[3]) { + char buf[128]; /* Large enough to hold all of /proc/loadavg. */ + + if (0 == uv__slurp("/proc/loadavg", buf, sizeof(buf))) + if (3 == sscanf(buf, "%lf %lf %lf", &avg[0], &avg[1], &avg[2])) + return; + + /* Reimplement here code from procfs to calculate loadavg if not mounted? */ +} + + +int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + kern_return_t err; + host_basic_info_data_t hbi; + mach_msg_type_number_t cnt; + + /* Get count of cpus */ + cnt = HOST_BASIC_INFO_COUNT; + err = host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t) &hbi, &cnt); + + if (err) { + err = UV__ERR(err); + goto abort; + } + + /* XXX not implemented on the Hurd */ + *cpu_infos = uv__calloc(hbi.avail_cpus, sizeof(**cpu_infos)); + if (*cpu_infos == NULL) { + err = UV_ENOMEM; + goto abort; + } + + *count = hbi.avail_cpus; + + return 0; + + abort: + *cpu_infos = NULL; + *count = 0; + return err; +} + +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} diff --git a/src/unix/ibmi.c b/src/unix/ibmi.c index 8c6ae636329..837bba6e2fe 100644 --- a/src/unix/ibmi.c +++ b/src/unix/ibmi.c @@ -249,6 +249,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + void uv_loadavg(double avg[3]) { SSTS0200 rcvr; diff --git a/src/unix/internal.h b/src/unix/internal.h index 12d4da93686..b1d2b21756d 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -26,21 +26,38 @@ #include #include /* _POSIX_PATH_MAX, PATH_MAX */ +#include #include /* abort */ #include /* strrchr */ #include /* O_CLOEXEC and O_NONBLOCK, if supported. */ #include #include #include +#include +#include +#if defined(__APPLE__) || defined(__DragonFly__) || \ + defined(__FreeBSD__) || defined(__NetBSD__) +#include +#endif + +#define uv__msan_unpoison(p, n) \ + do { \ + (void) (p); \ + (void) (n); \ + } while (0) + +#if defined(__has_feature) +# if __has_feature(memory_sanitizer) +# include +# undef uv__msan_unpoison +# define uv__msan_unpoison __msan_unpoison +# endif +#endif #if defined(__STRICT_ANSI__) # define inline __inline #endif -#if defined(__linux__) -# include "linux-syscalls.h" -#endif /* __linux__ */ - #if defined(__MVS__) # include "os390-syscalls.h" #endif /* __MVS__ */ @@ -58,8 +75,11 @@ # include #endif /* _AIX */ -#if defined(__APPLE__) && !TARGET_OS_IPHONE -# include +#if defined(__APPLE__) +# include "darwin-syscalls.h" +# if !TARGET_OS_IPHONE +# include +# endif #endif /* @@ -79,13 +99,11 @@ # define UV__PATH_MAX 8192 #endif -#if defined(__ANDROID__) -int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset); -# ifdef pthread_sigmask -# undef pthread_sigmask -# endif -# define pthread_sigmask(how, set, oldset) uv__pthread_sigmask(how, set, oldset) -#endif +union uv__sockaddr { + struct sockaddr_in6 in6; + struct sockaddr_in in; + struct sockaddr addr; +}; #define ACCESS_ONCE(type, var) \ (*(volatile type*) &(var)) @@ -145,7 +163,9 @@ typedef struct uv__stream_queued_fds_s uv__stream_queued_fds_t; /* loop flags */ enum { - UV_LOOP_BLOCK_SIGPROF = 1 + UV_LOOP_BLOCK_SIGPROF = 0x1, + UV_LOOP_REAP_CHILDREN = 0x2, + UV_LOOP_ENABLE_IO_URING_SQPOLL = 0x4 }; /* flags of excluding ifaddr */ @@ -165,20 +185,48 @@ struct uv__stream_queued_fds_s { int fds[1]; }; +#ifdef __linux__ +struct uv__statx_timestamp { + int64_t tv_sec; + uint32_t tv_nsec; + int32_t unused0; +}; + +struct uv__statx { + uint32_t stx_mask; + uint32_t stx_blksize; + uint64_t stx_attributes; + uint32_t stx_nlink; + uint32_t stx_uid; + uint32_t stx_gid; + uint16_t stx_mode; + uint16_t unused0; + uint64_t stx_ino; + uint64_t stx_size; + uint64_t stx_blocks; + uint64_t stx_attributes_mask; + struct uv__statx_timestamp stx_atime; + struct uv__statx_timestamp stx_btime; + struct uv__statx_timestamp stx_ctime; + struct uv__statx_timestamp stx_mtime; + uint32_t stx_rdev_major; + uint32_t stx_rdev_minor; + uint32_t stx_dev_major; + uint32_t stx_dev_minor; + uint64_t unused1[14]; +}; +#endif /* __linux__ */ #if defined(_AIX) || \ defined(__APPLE__) || \ defined(__DragonFly__) || \ defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ defined(__linux__) || \ defined(__OpenBSD__) || \ defined(__NetBSD__) -#define uv__cloexec uv__cloexec_ioctl #define uv__nonblock uv__nonblock_ioctl #define UV__NONBLOCK_IS_IOCTL 1 #else -#define uv__cloexec uv__cloexec_fcntl #define uv__nonblock uv__nonblock_fcntl #define UV__NONBLOCK_IS_IOCTL 0 #endif @@ -196,14 +244,14 @@ struct uv__stream_queued_fds_s { #endif /* core */ -int uv__cloexec_ioctl(int fd, int set); -int uv__cloexec_fcntl(int fd, int set); +int uv__cloexec(int fd, int set); int uv__nonblock_ioctl(int fd, int set); int uv__nonblock_fcntl(int fd, int set); 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); @@ -241,14 +289,18 @@ void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events); int uv__accept(int sockfd); int uv__dup2_cloexec(int oldfd, int newfd); int uv__open_cloexec(const char* path, int flags); +int uv__slurp(const char* filename, char* buf, size_t len); /* tcp */ -int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb); +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); +int uv__pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); /* signal */ void uv__signal_close(uv_signal_t* handle); @@ -259,10 +311,10 @@ int uv__signal_loop_fork(uv_loop_t* loop); /* platform specific */ uint64_t uv__hrtime(uv_clocktype_t type); int uv__kqueue_init(uv_loop_t* loop); -int uv__epoll_init(uv_loop_t* loop); int uv__platform_loop_init(uv_loop_t* loop); void uv__platform_loop_delete(uv_loop_t* loop); void uv__platform_invalidate_fd(uv_loop_t* loop, int fd); +int uv__process_init(uv_loop_t* loop); /* various */ void uv__async_close(uv_async_t* handle); @@ -275,13 +327,14 @@ 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); -uv_handle_type uv__handle_type(int fd); FILE* uv__open_file(const char* path); -int uv__getpwuid_r(uv_passwd_t* pwd); int uv__search_path(const char* prog, char* buf, size_t* buflen); +void uv__wait_children(uv_loop_t* loop); /* random */ int uv__random_devurandom(void* buf, size_t buflen); @@ -290,6 +343,40 @@ int uv__random_getentropy(void* buf, size_t buflen); int uv__random_readpath(const char* path, void* buf, size_t buflen); 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); +int uv__iou_fs_link(uv_loop_t* loop, uv_fs_t* req); +int uv__iou_fs_mkdir(uv_loop_t* loop, uv_fs_t* req); +int uv__iou_fs_open(uv_loop_t* loop, uv_fs_t* req); +int uv__iou_fs_read_or_write(uv_loop_t* loop, + uv_fs_t* req, + int is_read); +int uv__iou_fs_rename(uv_loop_t* loop, uv_fs_t* req); +int uv__iou_fs_statx(uv_loop_t* loop, + uv_fs_t* req, + int is_fstat, + int is_lstat); +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 +#define uv__iou_fs_open(loop, req) 0 +#define uv__iou_fs_read_or_write(loop, req, is_read) 0 +#define uv__iou_fs_rename(loop, req) 0 +#define uv__iou_fs_statx(loop, req, is_fstat, is_lstat) 0 +#define uv__iou_fs_symlink(loop, req) 0 +#define uv__iou_fs_unlink(loop, req) 0 +#endif + #if defined(__APPLE__) int uv___stream_fd(const uv_stream_t* handle); #define uv__stream_fd(handle) (uv___stream_fd((const uv_stream_t*) (handle))) @@ -323,8 +410,53 @@ UV_UNUSED(static char* uv__basename_r(const char* path)) { return s + 1; } +UV_UNUSED(static int uv__fstat(int fd, struct stat* s)) { + int rc; + + rc = fstat(fd, s); + if (rc >= 0) + uv__msan_unpoison(s, sizeof(*s)); + + return rc; +} + +UV_UNUSED(static int uv__lstat(const char* path, struct stat* s)) { + int rc; + + rc = lstat(path, s); + if (rc >= 0) + uv__msan_unpoison(s, sizeof(*s)); + + return rc; +} + +UV_UNUSED(static int uv__stat(const char* path, struct stat* s)) { + int rc; + + rc = stat(path, s); + if (rc >= 0) + uv__msan_unpoison(s, sizeof(*s)); + + return rc; +} + #if defined(__linux__) -int uv__inotify_fork(uv_loop_t* loop, void* old_watchers); +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, + int fd_out, + off_t* off_out, + size_t len, + unsigned int flags); +int uv__statx(int dirfd, + const char* path, + int flags, + unsigned int mask, + struct uv__statx* statxbuf); +void uv__statx_to_stat(const struct uv__statx* statxbuf, uv_stat_t* buf); +ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags); +unsigned uv__kernel_version(void); #endif typedef int (*uv__peersockfunc)(int, struct sockaddr*, socklen_t*); @@ -334,27 +466,66 @@ int uv__getsockpeername(const uv_handle_t* handle, struct sockaddr* name, int* namelen); -#if defined(__linux__) || \ - defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ - defined(__DragonFly__) -#define HAVE_MMSG 1 -struct uv__mmsghdr { - struct msghdr msg_hdr; - unsigned int msg_len; -}; +#if defined(__sun) +#if !defined(_POSIX_VERSION) || _POSIX_VERSION < 200809L +size_t strnlen(const char* s, size_t maxlen); +#endif +#endif -int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen); -int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen); +#if defined(__FreeBSD__) +ssize_t +uv__fs_copy_file_range(int fd_in, + off_t* off_in, + int fd_out, + off_t* off_out, + size_t len, + unsigned int flags); +#endif + +#if defined(__linux__) || (defined(__FreeBSD__) && __FreeBSD_version >= 1301000) +#define UV__CPU_AFFINITY_SUPPORTED 1 #else -#define HAVE_MMSG 0 +#define UV__CPU_AFFINITY_SUPPORTED 0 #endif -#if defined(__sun) -#if !defined(_POSIX_VERSION) || _POSIX_VERSION < 200809L -size_t strnlen(const char* s, size_t maxlen); +#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_ */ diff --git a/src/unix/kqueue.c b/src/unix/kqueue.c index 75e9110709d..e0166c344b0 100644 --- a/src/unix/kqueue.c +++ b/src/unix/kqueue.c @@ -30,6 +30,9 @@ #include #include #include +#if defined(__FreeBSD__) +#include +#endif #include #include #include @@ -60,7 +63,7 @@ int uv__kqueue_init(uv_loop_t* loop) { #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 -static int uv__has_forked_with_cfrunloop; +static _Atomic int uv__has_forked_with_cfrunloop; #endif int uv__io_fork(uv_loop_t* loop) { @@ -82,7 +85,9 @@ int uv__io_fork(uv_loop_t* loop) { process. So we sidestep the issue by pretending like we never started it in the first place. */ - uv__store_relaxed(&uv__has_forked_with_cfrunloop, 1); + atomic_store_explicit(&uv__has_forked_with_cfrunloop, + 1, + memory_order_relaxed); uv__free(loop->cf_state); loop->cf_state = NULL; } @@ -92,31 +97,75 @@ 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); + + /* On FreeBSD, kqueue only supports EVFILT_READ notification for regular files + * and always reports ready events for writing, resulting in busy-looping. + * + * On Darwin, DragonFlyBSD, NetBSD and OpenBSD, kqueue reports ready events for + * regular files as readable and writable only once, acting like an EV_ONESHOT. + * + * Neither of the above cases should be added to the kqueue. + */ + if (S_ISREG(sb.st_mode) || S_ISDIR(sb.st_mode)) + return UV_EINVAL; + +#ifdef __APPLE__ + /* On Darwin (both macOS and iOS), in addition to regular files, FIFOs also don't + * work properly with kqueue: the disconnection from the last writer won't trigger + * an event for kqueue in spite of what the man pages say. Thus, we also disallow + * the case of S_IFIFO. */ + if (S_ISFIFO(sb.st_mode)) { + /* File descriptors of FIFO, pipe and kqueue share the same type of file, + * therefore there is no way to tell them apart via stat.st_mode&S_IFMT. + * Fortunately, FIFO is the only one that has a persisted file on filesystem, + * from which we're able to make the distinction for it. */ + if (!fcntl(fd, F_GETPATH, path)) + return UV_EINVAL; + } +#endif + + 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; +} - 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(); - return rc; +static void uv__kqueue_delete(int kqfd, const struct kevent *ev) { + struct kevent change; + + EV_SET(&change, ev->ident, ev->filter, EV_DELETE, 0, 0, 0); + + if (0 == kevent(kqfd, &change, 1, NULL, 0, NULL)) + return; + + if (errno == EBADF || errno == ENOENT) + return; + + abort(); } void uv__io_poll(uv_loop_t* loop, int timeout) { + uv__loop_internal_fields_t* lfields; struct kevent events[1024]; struct kevent* ev; struct timespec spec; unsigned int nevents; unsigned int revents; - QUEUE* q; + struct uv__queue* q; uv__io_t* w; + uv_process_t* process; sigset_t* pset; sigset_t set; uint64_t base; @@ -133,18 +182,19 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int reset_timeout; if (loop->nfds == 0) { - assert(QUEUE_EMPTY(&loop->watcher_queue)); + assert(uv__queue_empty(&loop->watcher_queue)); return; } + lfields = uv__get_internal_fields(loop); nevents = 0; - while (!QUEUE_EMPTY(&loop->watcher_queue)) { - q = QUEUE_HEAD(&loop->watcher_queue); - QUEUE_REMOVE(q); - QUEUE_INIT(q); + while (!uv__queue_empty(&loop->watcher_queue)) { + q = uv__queue_head(&loop->watcher_queue); + uv__queue_remove(q); + uv__queue_init(q); - w = QUEUE_DATA(q, uv__io_t, watcher_queue); + w = uv__queue_data(q, uv__io_t, watcher_queue); assert(w->pevents != 0); assert(w->fd >= 0); assert(w->fd < (int) loop->nwatchers); @@ -204,7 +254,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { base = loop->time; count = 48; /* Benchmarks suggest this gives the best throughput. */ - if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + if (lfields->flags & UV_METRICS_IDLE_TIME) { reset_timeout = 1; user_timeout = timeout; timeout = 0; @@ -227,6 +277,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (pset != NULL) pthread_sigmask(SIG_BLOCK, pset, NULL); + /* 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; + nfds = kevent(loop->backend_fd, events, nevents, @@ -234,6 +290,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { ARRAY_SIZE(events), timeout == -1 ? NULL : &spec); + if (nfds == -1) + assert(errno == EINTR); + else if (nfds == 0) + /* Unlimited timeout should only return with events or signal. */ + assert(timeout != -1); + if (pset != NULL) pthread_sigmask(SIG_UNBLOCK, pset, NULL); @@ -241,36 +303,24 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the * operating system didn't reschedule our process while in the syscall. */ - SAVE_ERRNO(uv__update_time(loop)); - - if (nfds == 0) { - if (reset_timeout != 0) { - timeout = user_timeout; - reset_timeout = 0; - if (timeout == -1) - continue; - if (timeout > 0) - goto update_timeout; + uv__update_time(loop); + + if (nfds == 0 || nfds == -1) { + /* If kqueue is empty or interrupted, we might still have children ready + * to reap immediately. */ + if (loop->flags & UV_LOOP_REAP_CHILDREN) { + loop->flags &= ~UV_LOOP_REAP_CHILDREN; + uv__wait_children(loop); + assert((reset_timeout == 0 ? timeout : user_timeout) == 0); + return; /* Equivalent to fall-through behavior. */ } - assert(timeout != -1); - return; - } - - if (nfds == -1) { - if (errno != EINTR) - abort(); - if (reset_timeout != 0) { timeout = user_timeout; reset_timeout = 0; - } - - if (timeout == 0) + } else if (nfds == 0) { return; - - if (timeout == -1) - continue; + } /* Interrupted by a signal. Update timeout and poll again. */ goto update_timeout; @@ -285,23 +335,42 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { for (i = 0; i < nfds; i++) { ev = events + i; fd = ev->ident; + + /* Handle kevent NOTE_EXIT results */ + if (ev->filter == EVFILT_PROC) { + uv__queue_foreach(q, &loop->process_handles) { + process = uv__queue_data(q, uv_process_t, queue); + if (process->pid == fd) { + process->flags |= UV_HANDLE_REAP; + loop->flags |= UV_LOOP_REAP_CHILDREN; + break; + } + } + nevents++; + continue; + } + /* Skip invalidated events, see uv__platform_invalidate_fd */ if (fd == -1) continue; w = loop->watchers[fd]; if (w == NULL) { - /* File descriptor that we've stopped watching, disarm it. - * TODO: batch up. */ - struct kevent events[1]; - - EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0); - if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL)) - if (errno != EBADF && errno != ENOENT) - abort(); + /* File descriptor that we've stopped watching, disarm it. */ + uv__kqueue_delete(loop->backend_fd, ev); + 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); @@ -315,47 +384,27 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { revents = 0; if (ev->filter == EVFILT_READ) { - if (w->pevents & POLLIN) { + if (w->pevents & POLLIN) revents |= POLLIN; - w->rcount = ev->data; - } else { - /* TODO batch up */ - struct kevent events[1]; - EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0); - if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL)) - if (errno != ENOENT) - abort(); - } + else + uv__kqueue_delete(loop->backend_fd, ev); + if ((ev->flags & EV_EOF) && (w->pevents & UV__POLLRDHUP)) revents |= UV__POLLRDHUP; } if (ev->filter == EV_OOBAND) { - if (w->pevents & UV__POLLPRI) { + if (w->pevents & UV__POLLPRI) revents |= UV__POLLPRI; - w->rcount = ev->data; - } else { - /* TODO batch up */ - struct kevent events[1]; - EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0); - if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL)) - if (errno != ENOENT) - abort(); - } + else + uv__kqueue_delete(loop->backend_fd, ev); } if (ev->filter == EVFILT_WRITE) { - if (w->pevents & POLLOUT) { + if (w->pevents & POLLOUT) revents |= POLLOUT; - w->wcount = ev->data; - } else { - /* TODO batch up */ - struct kevent events[1]; - EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0); - if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL)) - if (errno != ENOENT) - abort(); - } + else + uv__kqueue_delete(loop->backend_fd, ev); } if (ev->flags & EV_ERROR) @@ -377,9 +426,16 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { nevents++; } + if (loop->flags & UV_LOOP_REAP_CHILDREN) { + loop->flags &= ~UV_LOOP_REAP_CHILDREN; + uv__wait_children(loop); + } + + uv__metrics_inc_events(loop, nevents); if (reset_timeout != 0) { timeout = user_timeout; reset_timeout = 0; + uv__metrics_inc_events_waiting(loop, nevents); } if (have_signals != 0) { @@ -402,13 +458,13 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { return; } +update_timeout: if (timeout == 0) return; if (timeout == -1) continue; -update_timeout: assert(timeout > 0); diff = loop->time - base; @@ -435,7 +491,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { /* Invalidate events with same file descriptor */ for (i = 0; i < nfds; i++) - if ((int) events[i].ident == fd) + if ((int) events[i].ident == fd && events[i].filter != EVFILT_PROC) events[i].ident = -1; } @@ -465,6 +521,26 @@ static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) { */ if (fcntl(handle->event_watcher.fd, F_GETPATH, pathbuf) == 0) path = uv__basename_r(pathbuf); +#elif defined(F_KINFO) + /* We try to get the file info reference from the file descriptor. + * 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; + + 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); @@ -520,13 +596,14 @@ int uv_fs_event_start(uv_fs_event_t* handle, handle->realpath_len = 0; handle->cf_flags = flags; - if (fstat(fd, &statbuf)) + if (uv__fstat(fd, &statbuf)) goto fallback; /* FSEvents works only with directories */ if (!(statbuf.st_mode & S_IFDIR)) goto fallback; - if (0 == uv__load_relaxed(&uv__has_forked_with_cfrunloop)) { + if (0 == atomic_load_explicit(&uv__has_forked_with_cfrunloop, + memory_order_relaxed)) { int r; /* The fallback fd is no longer needed */ uv__close_nocheckstdio(fd); @@ -561,7 +638,8 @@ int uv_fs_event_stop(uv_fs_event_t* handle) { uv__handle_stop(handle); #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 - if (0 == uv__load_relaxed(&uv__has_forked_with_cfrunloop)) + if (0 == atomic_load_explicit(&uv__has_forked_with_cfrunloop, + memory_order_relaxed)) if (handle->cf_cb != NULL) r = uv__fsevents_close(handle); #endif diff --git a/src/unix/linux-core.c b/src/unix/linux-core.c deleted file mode 100644 index 413d0dd3f09..00000000000 --- a/src/unix/linux-core.c +++ /dev/null @@ -1,857 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -/* We lean on the fact that POLL{IN,OUT,ERR,HUP} correspond with their - * EPOLL* counterparts. We use the POLL* variants in this file because that - * is what libuv uses elsewhere. - */ - -#include "uv.h" -#include "internal.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#define HAVE_IFADDRS_H 1 - -#ifdef __UCLIBC__ -# if __UCLIBC_MAJOR__ < 0 && __UCLIBC_MINOR__ < 9 && __UCLIBC_SUBLEVEL__ < 32 -# undef HAVE_IFADDRS_H -# endif -#endif - -#ifdef HAVE_IFADDRS_H -# if defined(__ANDROID__) -# include "uv/android-ifaddrs.h" -# else -# include -# endif -# include -# include -# include -#endif /* HAVE_IFADDRS_H */ - -/* Available from 2.6.32 onwards. */ -#ifndef CLOCK_MONOTONIC_COARSE -# define CLOCK_MONOTONIC_COARSE 6 -#endif - -/* This is rather annoying: CLOCK_BOOTTIME lives in but we can't - * include that file because it conflicts with . We'll just have to - * define it ourselves. - */ -#ifndef CLOCK_BOOTTIME -# define CLOCK_BOOTTIME 7 -#endif - -static int read_models(unsigned int numcpus, uv_cpu_info_t* ci); -static int read_times(FILE* statfile_fp, - unsigned int numcpus, - uv_cpu_info_t* ci); -static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci); -static uint64_t read_cpufreq(unsigned int cpunum); - -int uv__platform_loop_init(uv_loop_t* loop) { - - loop->inotify_fd = -1; - loop->inotify_watchers = NULL; - - return uv__epoll_init(loop); -} - - -int uv__io_fork(uv_loop_t* loop) { - int err; - void* old_watchers; - - old_watchers = loop->inotify_watchers; - - uv__close(loop->backend_fd); - loop->backend_fd = -1; - uv__platform_loop_delete(loop); - - err = uv__platform_loop_init(loop); - if (err) - return err; - - return uv__inotify_fork(loop, old_watchers); -} - - -void uv__platform_loop_delete(uv_loop_t* loop) { - if (loop->inotify_fd == -1) return; - uv__io_stop(loop, &loop->inotify_read_watcher, POLLIN); - uv__close(loop->inotify_fd); - loop->inotify_fd = -1; -} - - - -uint64_t uv__hrtime(uv_clocktype_t type) { - static clock_t fast_clock_id = -1; - struct timespec t; - clock_t clock_id; - - /* Prefer CLOCK_MONOTONIC_COARSE if available but only when it has - * millisecond granularity or better. CLOCK_MONOTONIC_COARSE is - * serviced entirely from the vDSO, whereas CLOCK_MONOTONIC may - * decide to make a costly system call. - */ - /* TODO(bnoordhuis) Use CLOCK_MONOTONIC_COARSE for UV_CLOCK_PRECISE - * when it has microsecond granularity or better (unlikely). - */ - clock_id = CLOCK_MONOTONIC; - if (type != UV_CLOCK_FAST) - goto done; - - clock_id = uv__load_relaxed(&fast_clock_id); - if (clock_id != -1) - goto done; - - clock_id = CLOCK_MONOTONIC; - if (0 == clock_getres(CLOCK_MONOTONIC_COARSE, &t)) - if (t.tv_nsec <= 1 * 1000 * 1000) - clock_id = CLOCK_MONOTONIC_COARSE; - - uv__store_relaxed(&fast_clock_id, clock_id); - -done: - - if (clock_gettime(clock_id, &t)) - return 0; /* Not really possible. */ - - return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec; -} - - -int uv_resident_set_memory(size_t* rss) { - char buf[1024]; - const char* s; - ssize_t n; - long val; - int fd; - int i; - - do - fd = open("/proc/self/stat", O_RDONLY); - while (fd == -1 && errno == EINTR); - - 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, ')'); - 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 (errno != 0) - goto err; - if (val < 0) - goto err; - - *rss = val * getpagesize(); - return 0; - -err: - return UV_EINVAL; -} - -static int uv__slurp(const char* filename, char* buf, size_t len) { - ssize_t n; - int fd; - - assert(len > 0); - - fd = uv__open_cloexec(filename, O_RDONLY); - if (fd < 0) - return fd; - - do - n = read(fd, buf, len - 1); - while (n == -1 && errno == EINTR); - - if (uv__close_nocheckstdio(fd)) - abort(); - - if (n < 0) - return UV__ERR(errno); - - buf[n] = '\0'; - - return 0; -} - -int uv_uptime(double* uptime) { - static volatile int no_clock_boottime; - char buf[128]; - struct timespec now; - int r; - - /* Try /proc/uptime first, then fallback to clock_gettime(). */ - - if (0 == uv__slurp("/proc/uptime", buf, sizeof(buf))) - if (1 == sscanf(buf, "%lf", uptime)) - return 0; - - /* Try CLOCK_BOOTTIME first, fall back to CLOCK_MONOTONIC if not available - * (pre-2.6.39 kernels). CLOCK_MONOTONIC doesn't increase when the system - * is suspended. - */ - if (no_clock_boottime) { - retry_clock_gettime: r = clock_gettime(CLOCK_MONOTONIC, &now); - } - else if ((r = clock_gettime(CLOCK_BOOTTIME, &now)) && errno == EINVAL) { - no_clock_boottime = 1; - goto retry_clock_gettime; - } - - if (r) - return UV__ERR(errno); - - *uptime = now.tv_sec; - return 0; -} - - -static int uv__cpu_num(FILE* statfile_fp, unsigned int* numcpus) { - unsigned int num; - char buf[1024]; - - if (!fgets(buf, sizeof(buf), statfile_fp)) - return UV_EIO; - - num = 0; - while (fgets(buf, sizeof(buf), statfile_fp)) { - if (strncmp(buf, "cpu", 3)) - break; - num++; - } - - if (num == 0) - return UV_EIO; - - *numcpus = num; - return 0; -} - - -int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { - unsigned int numcpus; - uv_cpu_info_t* ci; - int err; - FILE* statfile_fp; - - *cpu_infos = NULL; - *count = 0; - - statfile_fp = uv__open_file("/proc/stat"); - if (statfile_fp == NULL) - return UV__ERR(errno); - - err = uv__cpu_num(statfile_fp, &numcpus); - if (err < 0) - goto out; - - err = UV_ENOMEM; - ci = uv__calloc(numcpus, sizeof(*ci)); - if (ci == NULL) - goto out; - - err = read_models(numcpus, ci); - if (err == 0) - err = read_times(statfile_fp, numcpus, ci); - - if (err) { - uv_free_cpu_info(ci, numcpus); - goto out; - } - - /* read_models() on x86 also reads the CPU speed from /proc/cpuinfo. - * We don't check for errors here. Worst case, the field is left zero. - */ - if (ci[0].speed == 0) - read_speeds(numcpus, ci); - - *cpu_infos = ci; - *count = numcpus; - err = 0; - -out: - - if (fclose(statfile_fp)) - if (errno != EINTR && errno != EINPROGRESS) - abort(); - - return err; -} - - -static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci) { - unsigned int num; - - for (num = 0; num < numcpus; num++) - ci[num].speed = read_cpufreq(num) / 1000; -} - - -/* Also reads the CPU frequency on ppc and x86. The other architectures only - * have a BogoMIPS field, which may not be very accurate. - * - * Note: Simply returns on error, uv_cpu_info() takes care of the cleanup. - */ -static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) { -#if defined(__PPC__) - static const char model_marker[] = "cpu\t\t: "; - static const char speed_marker[] = "clock\t\t: "; -#else - static const char model_marker[] = "model name\t: "; - static const char speed_marker[] = "cpu MHz\t\t: "; -#endif - const char* inferred_model; - unsigned int model_idx; - unsigned int speed_idx; - unsigned int part_idx; - char buf[1024]; - char* model; - FILE* fp; - int model_id; - - /* Most are unused on non-ARM, non-MIPS and non-x86 architectures. */ - (void) &model_marker; - (void) &speed_marker; - (void) &speed_idx; - (void) &part_idx; - (void) &model; - (void) &buf; - (void) &fp; - (void) &model_id; - - model_idx = 0; - speed_idx = 0; - part_idx = 0; - -#if defined(__arm__) || \ - defined(__i386__) || \ - defined(__mips__) || \ - defined(__aarch64__) || \ - defined(__PPC__) || \ - defined(__x86_64__) - fp = uv__open_file("/proc/cpuinfo"); - if (fp == NULL) - return UV__ERR(errno); - - while (fgets(buf, sizeof(buf), fp)) { - if (model_idx < numcpus) { - if (strncmp(buf, model_marker, sizeof(model_marker) - 1) == 0) { - model = buf + sizeof(model_marker) - 1; - model = uv__strndup(model, strlen(model) - 1); /* Strip newline. */ - if (model == NULL) { - fclose(fp); - return UV_ENOMEM; - } - ci[model_idx++].model = model; - continue; - } - } -#if defined(__arm__) || defined(__mips__) || defined(__aarch64__) - if (model_idx < numcpus) { -#if defined(__arm__) - /* Fallback for pre-3.8 kernels. */ - static const char model_marker[] = "Processor\t: "; -#elif defined(__aarch64__) - static const char part_marker[] = "CPU part\t: "; - - /* Adapted from: https://github.com/karelzak/util-linux */ - struct vendor_part { - const int id; - const char* name; - }; - - static const struct vendor_part arm_chips[] = { - { 0x811, "ARM810" }, - { 0x920, "ARM920" }, - { 0x922, "ARM922" }, - { 0x926, "ARM926" }, - { 0x940, "ARM940" }, - { 0x946, "ARM946" }, - { 0x966, "ARM966" }, - { 0xa20, "ARM1020" }, - { 0xa22, "ARM1022" }, - { 0xa26, "ARM1026" }, - { 0xb02, "ARM11 MPCore" }, - { 0xb36, "ARM1136" }, - { 0xb56, "ARM1156" }, - { 0xb76, "ARM1176" }, - { 0xc05, "Cortex-A5" }, - { 0xc07, "Cortex-A7" }, - { 0xc08, "Cortex-A8" }, - { 0xc09, "Cortex-A9" }, - { 0xc0d, "Cortex-A17" }, /* Originally A12 */ - { 0xc0f, "Cortex-A15" }, - { 0xc0e, "Cortex-A17" }, - { 0xc14, "Cortex-R4" }, - { 0xc15, "Cortex-R5" }, - { 0xc17, "Cortex-R7" }, - { 0xc18, "Cortex-R8" }, - { 0xc20, "Cortex-M0" }, - { 0xc21, "Cortex-M1" }, - { 0xc23, "Cortex-M3" }, - { 0xc24, "Cortex-M4" }, - { 0xc27, "Cortex-M7" }, - { 0xc60, "Cortex-M0+" }, - { 0xd01, "Cortex-A32" }, - { 0xd03, "Cortex-A53" }, - { 0xd04, "Cortex-A35" }, - { 0xd05, "Cortex-A55" }, - { 0xd06, "Cortex-A65" }, - { 0xd07, "Cortex-A57" }, - { 0xd08, "Cortex-A72" }, - { 0xd09, "Cortex-A73" }, - { 0xd0a, "Cortex-A75" }, - { 0xd0b, "Cortex-A76" }, - { 0xd0c, "Neoverse-N1" }, - { 0xd0d, "Cortex-A77" }, - { 0xd0e, "Cortex-A76AE" }, - { 0xd13, "Cortex-R52" }, - { 0xd20, "Cortex-M23" }, - { 0xd21, "Cortex-M33" }, - { 0xd41, "Cortex-A78" }, - { 0xd42, "Cortex-A78AE" }, - { 0xd4a, "Neoverse-E1" }, - { 0xd4b, "Cortex-A78C" }, - }; - - if (strncmp(buf, part_marker, sizeof(part_marker) - 1) == 0) { - model = buf + sizeof(part_marker) - 1; - - errno = 0; - model_id = strtol(model, NULL, 16); - if ((errno != 0) || model_id < 0) { - fclose(fp); - return UV_EINVAL; - } - - for (part_idx = 0; part_idx < ARRAY_SIZE(arm_chips); part_idx++) { - if (model_id == arm_chips[part_idx].id) { - model = uv__strdup(arm_chips[part_idx].name); - if (model == NULL) { - fclose(fp); - return UV_ENOMEM; - } - ci[model_idx++].model = model; - break; - } - } - } -#else /* defined(__mips__) */ - static const char model_marker[] = "cpu model\t\t: "; -#endif - if (strncmp(buf, model_marker, sizeof(model_marker) - 1) == 0) { - model = buf + sizeof(model_marker) - 1; - model = uv__strndup(model, strlen(model) - 1); /* Strip newline. */ - if (model == NULL) { - fclose(fp); - return UV_ENOMEM; - } - ci[model_idx++].model = model; - continue; - } - } -#else /* !__arm__ && !__mips__ && !__aarch64__ */ - if (speed_idx < numcpus) { - if (strncmp(buf, speed_marker, sizeof(speed_marker) - 1) == 0) { - ci[speed_idx++].speed = atoi(buf + sizeof(speed_marker) - 1); - continue; - } - } -#endif /* __arm__ || __mips__ || __aarch64__ */ - } - - fclose(fp); -#endif /* __arm__ || __i386__ || __mips__ || __PPC__ || __x86_64__ || __aarch__ */ - - /* Now we want to make sure that all the models contain *something* because - * it's not safe to leave them as null. Copy the last entry unless there - * isn't one, in that case we simply put "unknown" into everything. - */ - inferred_model = "unknown"; - if (model_idx > 0) - inferred_model = ci[model_idx - 1].model; - - while (model_idx < numcpus) { - model = uv__strndup(inferred_model, strlen(inferred_model)); - if (model == NULL) - return UV_ENOMEM; - ci[model_idx++].model = model; - } - - return 0; -} - - -static int read_times(FILE* statfile_fp, - unsigned int numcpus, - uv_cpu_info_t* ci) { - struct uv_cpu_times_s ts; - unsigned int ticks; - unsigned int multiplier; - uint64_t user; - uint64_t nice; - uint64_t sys; - uint64_t idle; - uint64_t dummy; - uint64_t irq; - uint64_t num; - uint64_t len; - char buf[1024]; - - ticks = (unsigned int)sysconf(_SC_CLK_TCK); - assert(ticks != (unsigned int) -1); - assert(ticks != 0); - multiplier = ((uint64_t)1000L / ticks); - - rewind(statfile_fp); - - if (!fgets(buf, sizeof(buf), statfile_fp)) - abort(); - - num = 0; - - while (fgets(buf, sizeof(buf), statfile_fp)) { - if (num >= numcpus) - break; - - if (strncmp(buf, "cpu", 3)) - break; - - /* skip "cpu " marker */ - { - unsigned int n; - int r = sscanf(buf, "cpu%u ", &n); - assert(r == 1); - (void) r; /* silence build warning */ - for (len = sizeof("cpu0"); n /= 10; len++); - } - - /* Line contains user, nice, system, idle, iowait, irq, softirq, steal, - * guest, guest_nice but we're only interested in the first four + irq. - * - * Don't use %*s to skip fields or %ll to read straight into the uint64_t - * fields, they're not allowed in C89 mode. - */ - if (6 != sscanf(buf + len, - "%" PRIu64 " %" PRIu64 " %" PRIu64 - "%" PRIu64 " %" PRIu64 " %" PRIu64, - &user, - &nice, - &sys, - &idle, - &dummy, - &irq)) - abort(); - - ts.user = user * multiplier; - ts.nice = nice * multiplier; - ts.sys = sys * multiplier; - ts.idle = idle * multiplier; - ts.irq = irq * multiplier; - ci[num++].cpu_times = ts; - } - assert(num == numcpus); - - return 0; -} - - -static uint64_t read_cpufreq(unsigned int cpunum) { - uint64_t val; - char buf[1024]; - FILE* fp; - - snprintf(buf, - sizeof(buf), - "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", - cpunum); - - fp = uv__open_file(buf); - if (fp == NULL) - return 0; - - if (fscanf(fp, "%" PRIu64, &val) != 1) - val = 0; - - fclose(fp); - - return val; -} - - -static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { - if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING))) - return 1; - if (ent->ifa_addr == NULL) - return 1; - /* - * On Linux getifaddrs returns information related to the raw underlying - * devices. We're not interested in this information yet. - */ - if (ent->ifa_addr->sa_family == PF_PACKET) - return exclude_type; - return !exclude_type; -} - -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; - int i; - struct sockaddr_ll *sll; - - *count = 0; - *addresses = NULL; - - if (getifaddrs(&addrs)) - return UV__ERR(errno); - - /* Count the number of interfaces */ - for (ent = addrs; ent != NULL; ent = ent->ifa_next) { - if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR)) - continue; - - (*count)++; - } - - if (*count == 0) { - freeifaddrs(addrs); - return 0; - } - - /* Make sure the memory is initiallized to zero using calloc() */ - *addresses = uv__calloc(*count, sizeof(**addresses)); - if (!(*addresses)) { - freeifaddrs(addrs); - return UV_ENOMEM; - } - - 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); - - if (ent->ifa_addr->sa_family == AF_INET6) { - address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr); - } else { - address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr); - } - - if (ent->ifa_netmask->sa_family == AF_INET6) { - address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask); - } else { - address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask); - } - - address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK); - - address++; - } - - /* Fill in physical addresses for each interface */ - for (ent = addrs; ent != NULL; ent = ent->ifa_next) { - if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS)) - continue; - - address = *addresses; - - for (i = 0; i < (*count); i++) { - size_t namelen = strlen(ent->ifa_name); - /* Alias interface share the same physical address */ - if (strncmp(address->name, ent->ifa_name, namelen) == 0 && - (address->name[namelen] == 0 || address->name[namelen] == ':')) { - sll = (struct sockaddr_ll*)ent->ifa_addr; - memcpy(address->phys_addr, sll->sll_addr, sizeof(address->phys_addr)); - } - address++; - } - } - - freeifaddrs(addrs); - - return 0; -#endif -} - - -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); -} - - -void uv__set_process_title(const char* title) { -#if defined(PR_SET_NAME) - prctl(PR_SET_NAME, title); /* Only copies first 16 characters. */ -#endif -} - - -static uint64_t uv__read_proc_meminfo(const char* what) { - uint64_t rc; - char* p; - char buf[4096]; /* Large enough to hold all of /proc/meminfo. */ - - if (uv__slurp("/proc/meminfo", buf, sizeof(buf))) - return 0; - - p = strstr(buf, what); - - if (p == NULL) - return 0; - - p += strlen(what); - - rc = 0; - sscanf(p, "%" PRIu64 " kB", &rc); - - return rc * 1024; -} - - -uint64_t uv_get_free_memory(void) { - struct sysinfo info; - uint64_t rc; - - rc = uv__read_proc_meminfo("MemAvailable:"); - - if (rc != 0) - return rc; - - if (0 == sysinfo(&info)) - return (uint64_t) info.freeram * info.mem_unit; - - return 0; -} - - -uint64_t uv_get_total_memory(void) { - struct sysinfo info; - uint64_t rc; - - rc = uv__read_proc_meminfo("MemTotal:"); - - if (rc != 0) - return rc; - - if (0 == sysinfo(&info)) - return (uint64_t) info.totalram * info.mem_unit; - - return 0; -} - - -static uint64_t uv__read_cgroups_uint64(const char* cgroup, const char* param) { - char filename[256]; - char buf[32]; /* Large enough to hold an encoded uint64_t. */ - uint64_t rc; - - rc = 0; - snprintf(filename, sizeof(filename), "/sys/fs/cgroup/%s/%s", cgroup, param); - if (0 == uv__slurp(filename, buf, sizeof(buf))) - sscanf(buf, "%" PRIu64, &rc); - - return rc; -} - - -uint64_t uv_get_constrained_memory(void) { - /* - * This might return 0 if there was a problem getting the memory limit from - * cgroups. This is OK because a return value of 0 signifies that the memory - * limit is unknown. - */ - return uv__read_cgroups_uint64("memory", "memory.limit_in_bytes"); -} - - -void uv_loadavg(double avg[3]) { - struct sysinfo info; - char buf[128]; /* Large enough to hold all of /proc/loadavg. */ - - if (0 == uv__slurp("/proc/loadavg", buf, sizeof(buf))) - if (3 == sscanf(buf, "%lf %lf %lf", &avg[0], &avg[1], &avg[2])) - return; - - if (sysinfo(&info) < 0) - return; - - avg[0] = (double) info.loads[0] / 65536.0; - avg[1] = (double) info.loads[1] / 65536.0; - avg[2] = (double) info.loads[2] / 65536.0; -} diff --git a/src/unix/linux-inotify.c b/src/unix/linux-inotify.c deleted file mode 100644 index c1bd260e16e..00000000000 --- a/src/unix/linux-inotify.c +++ /dev/null @@ -1,327 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include "uv.h" -#include "uv/tree.h" -#include "internal.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -struct watcher_list { - RB_ENTRY(watcher_list) entry; - QUEUE watchers; - int iterating; - char* path; - int wd; -}; - -struct watcher_root { - struct watcher_list* rbh_root; -}; -#define CAST(p) ((struct watcher_root*)(p)) - - -static int compare_watchers(const struct watcher_list* a, - const struct watcher_list* b) { - if (a->wd < b->wd) return -1; - if (a->wd > b->wd) return 1; - return 0; -} - - -RB_GENERATE_STATIC(watcher_root, watcher_list, entry, compare_watchers) - - -static void uv__inotify_read(uv_loop_t* loop, - uv__io_t* w, - unsigned int revents); - -static void maybe_free_watcher_list(struct watcher_list* w, - uv_loop_t* loop); - -static int init_inotify(uv_loop_t* loop) { - int fd; - - if (loop->inotify_fd != -1) - return 0; - - fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); - if (fd < 0) - return UV__ERR(errno); - - loop->inotify_fd = fd; - uv__io_init(&loop->inotify_read_watcher, uv__inotify_read, loop->inotify_fd); - uv__io_start(loop, &loop->inotify_read_watcher, POLLIN); - - return 0; -} - - -int uv__inotify_fork(uv_loop_t* loop, void* old_watchers) { - /* Open the inotify_fd, and re-arm all the inotify watchers. */ - int err; - struct watcher_list* tmp_watcher_list_iter; - struct watcher_list* watcher_list; - struct watcher_list tmp_watcher_list; - QUEUE queue; - QUEUE* q; - uv_fs_event_t* handle; - char* tmp_path; - - if (old_watchers != NULL) { - /* We must restore the old watcher list to be able to close items - * out of it. - */ - loop->inotify_watchers = old_watchers; - - QUEUE_INIT(&tmp_watcher_list.watchers); - /* Note that the queue we use is shared with the start and stop() - * functions, making QUEUE_FOREACH unsafe to use. So we use the - * QUEUE_MOVE trick to safely iterate. Also don't free the watcher - * list until we're done iterating. c.f. uv__inotify_read. - */ - RB_FOREACH_SAFE(watcher_list, watcher_root, - CAST(&old_watchers), tmp_watcher_list_iter) { - watcher_list->iterating = 1; - QUEUE_MOVE(&watcher_list->watchers, &queue); - while (!QUEUE_EMPTY(&queue)) { - q = QUEUE_HEAD(&queue); - handle = QUEUE_DATA(q, uv_fs_event_t, watchers); - /* It's critical to keep a copy of path here, because it - * will be set to NULL by stop() and then deallocated by - * maybe_free_watcher_list - */ - tmp_path = uv__strdup(handle->path); - assert(tmp_path != NULL); - QUEUE_REMOVE(q); - QUEUE_INSERT_TAIL(&watcher_list->watchers, q); - uv_fs_event_stop(handle); - - QUEUE_INSERT_TAIL(&tmp_watcher_list.watchers, &handle->watchers); - handle->path = tmp_path; - } - watcher_list->iterating = 0; - maybe_free_watcher_list(watcher_list, loop); - } - - QUEUE_MOVE(&tmp_watcher_list.watchers, &queue); - while (!QUEUE_EMPTY(&queue)) { - q = QUEUE_HEAD(&queue); - QUEUE_REMOVE(q); - handle = QUEUE_DATA(q, uv_fs_event_t, watchers); - tmp_path = handle->path; - handle->path = NULL; - err = uv_fs_event_start(handle, handle->cb, tmp_path, 0); - uv__free(tmp_path); - if (err) - return err; - } - } - - return 0; -} - - -static struct watcher_list* find_watcher(uv_loop_t* loop, int wd) { - struct watcher_list w; - w.wd = wd; - return RB_FIND(watcher_root, CAST(&loop->inotify_watchers), &w); -} - -static void maybe_free_watcher_list(struct watcher_list* w, uv_loop_t* loop) { - /* if the watcher_list->watchers is being iterated over, we can't free it. */ - if ((!w->iterating) && QUEUE_EMPTY(&w->watchers)) { - /* No watchers left for this path. Clean up. */ - RB_REMOVE(watcher_root, CAST(&loop->inotify_watchers), w); - inotify_rm_watch(loop->inotify_fd, w->wd); - uv__free(w); - } -} - -static void uv__inotify_read(uv_loop_t* loop, - uv__io_t* dummy, - unsigned int events) { - const struct inotify_event* e; - struct watcher_list* w; - uv_fs_event_t* h; - QUEUE queue; - QUEUE* q; - const char* path; - ssize_t size; - const char *p; - /* needs to be large enough for sizeof(inotify_event) + strlen(path) */ - char buf[4096]; - - for (;;) { - do - size = read(loop->inotify_fd, buf, sizeof(buf)); - while (size == -1 && errno == EINTR); - - if (size == -1) { - assert(errno == EAGAIN || errno == EWOULDBLOCK); - break; - } - - assert(size > 0); /* pre-2.6.21 thing, size=0 == read buffer too small */ - - /* Now we have one or more inotify_event structs. */ - for (p = buf; p < buf + size; p += sizeof(*e) + e->len) { - e = (const struct inotify_event*) p; - - events = 0; - if (e->mask & (IN_ATTRIB|IN_MODIFY)) - events |= UV_CHANGE; - if (e->mask & ~(IN_ATTRIB|IN_MODIFY)) - events |= UV_RENAME; - - w = find_watcher(loop, e->wd); - if (w == NULL) - continue; /* Stale event, no watchers left. */ - - /* inotify does not return the filename when monitoring a single file - * for modifications. Repurpose the filename for API compatibility. - * I'm not convinced this is a good thing, maybe it should go. - */ - path = e->len ? (const char*) (e + 1) : uv__basename_r(w->path); - - /* We're about to iterate over the queue and call user's callbacks. - * What can go wrong? - * A callback could call uv_fs_event_stop() - * and the queue can change under our feet. - * So, we use QUEUE_MOVE() trick to safely iterate over the queue. - * And we don't free the watcher_list until we're done iterating. - * - * First, - * tell uv_fs_event_stop() (that could be called from a user's callback) - * not to free watcher_list. - */ - w->iterating = 1; - QUEUE_MOVE(&w->watchers, &queue); - while (!QUEUE_EMPTY(&queue)) { - q = QUEUE_HEAD(&queue); - h = QUEUE_DATA(q, uv_fs_event_t, watchers); - - QUEUE_REMOVE(q); - QUEUE_INSERT_TAIL(&w->watchers, q); - - h->cb(h, path, events, 0); - } - /* done iterating, time to (maybe) free empty watcher_list */ - w->iterating = 0; - maybe_free_watcher_list(w, loop); - } - } -} - - -int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { - uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); - return 0; -} - - -int uv_fs_event_start(uv_fs_event_t* handle, - uv_fs_event_cb cb, - const char* path, - unsigned int flags) { - struct watcher_list* w; - size_t len; - int events; - int err; - int wd; - - if (uv__is_active(handle)) - return UV_EINVAL; - - err = init_inotify(handle->loop); - if (err) - return err; - - events = IN_ATTRIB - | IN_CREATE - | IN_MODIFY - | IN_DELETE - | IN_DELETE_SELF - | IN_MOVE_SELF - | IN_MOVED_FROM - | IN_MOVED_TO; - - wd = inotify_add_watch(handle->loop->inotify_fd, path, events); - if (wd == -1) - return UV__ERR(errno); - - w = find_watcher(handle->loop, wd); - if (w) - goto no_insert; - - len = strlen(path) + 1; - w = uv__malloc(sizeof(*w) + len); - if (w == NULL) - return UV_ENOMEM; - - w->wd = wd; - w->path = memcpy(w + 1, path, len); - QUEUE_INIT(&w->watchers); - w->iterating = 0; - RB_INSERT(watcher_root, CAST(&handle->loop->inotify_watchers), w); - -no_insert: - uv__handle_start(handle); - QUEUE_INSERT_TAIL(&w->watchers, &handle->watchers); - handle->path = w->path; - handle->cb = cb; - handle->wd = wd; - - return 0; -} - - -int uv_fs_event_stop(uv_fs_event_t* handle) { - struct watcher_list* w; - - if (!uv__is_active(handle)) - return 0; - - w = find_watcher(handle->loop, handle->wd); - assert(w != NULL); - - handle->wd = -1; - handle->path = NULL; - uv__handle_stop(handle); - QUEUE_REMOVE(&handle->watchers); - - maybe_free_watcher_list(w, handle->loop); - - return 0; -} - - -void uv__fs_event_close(uv_fs_event_t* handle) { - uv_fs_event_stop(handle); -} diff --git a/src/unix/linux-syscalls.c b/src/unix/linux-syscalls.c deleted file mode 100644 index 5071cd56d1f..00000000000 --- a/src/unix/linux-syscalls.c +++ /dev/null @@ -1,264 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include "linux-syscalls.h" -#include -#include -#include -#include -#include - -#if defined(__arm__) -# if defined(__thumb__) || defined(__ARM_EABI__) -# define UV_SYSCALL_BASE 0 -# else -# define UV_SYSCALL_BASE 0x900000 -# endif -#endif /* __arm__ */ - -#ifndef __NR_recvmmsg -# if defined(__x86_64__) -# define __NR_recvmmsg 299 -# elif defined(__arm__) -# define __NR_recvmmsg (UV_SYSCALL_BASE + 365) -# endif -#endif /* __NR_recvmsg */ - -#ifndef __NR_sendmmsg -# if defined(__x86_64__) -# define __NR_sendmmsg 307 -# elif defined(__arm__) -# define __NR_sendmmsg (UV_SYSCALL_BASE + 374) -# endif -#endif /* __NR_sendmmsg */ - -#ifndef __NR_utimensat -# if defined(__x86_64__) -# define __NR_utimensat 280 -# elif defined(__i386__) -# define __NR_utimensat 320 -# elif defined(__arm__) -# define __NR_utimensat (UV_SYSCALL_BASE + 348) -# endif -#endif /* __NR_utimensat */ - -#ifndef __NR_preadv -# if defined(__x86_64__) -# define __NR_preadv 295 -# elif defined(__i386__) -# define __NR_preadv 333 -# elif defined(__arm__) -# define __NR_preadv (UV_SYSCALL_BASE + 361) -# endif -#endif /* __NR_preadv */ - -#ifndef __NR_pwritev -# if defined(__x86_64__) -# define __NR_pwritev 296 -# elif defined(__i386__) -# define __NR_pwritev 334 -# elif defined(__arm__) -# define __NR_pwritev (UV_SYSCALL_BASE + 362) -# endif -#endif /* __NR_pwritev */ - -#ifndef __NR_dup3 -# if defined(__x86_64__) -# define __NR_dup3 292 -# elif defined(__i386__) -# define __NR_dup3 330 -# elif defined(__arm__) -# define __NR_dup3 (UV_SYSCALL_BASE + 358) -# endif -#endif /* __NR_pwritev */ - -#ifndef __NR_copy_file_range -# if defined(__x86_64__) -# define __NR_copy_file_range 326 -# elif defined(__i386__) -# define __NR_copy_file_range 377 -# elif defined(__s390__) -# define __NR_copy_file_range 375 -# elif defined(__arm__) -# define __NR_copy_file_range (UV_SYSCALL_BASE + 391) -# elif defined(__aarch64__) -# define __NR_copy_file_range 285 -# elif defined(__powerpc__) -# define __NR_copy_file_range 379 -# elif defined(__arc__) -# define __NR_copy_file_range 285 -# endif -#endif /* __NR_copy_file_range */ - -#ifndef __NR_statx -# if defined(__x86_64__) -# define __NR_statx 332 -# elif defined(__i386__) -# define __NR_statx 383 -# elif defined(__aarch64__) -# define __NR_statx 397 -# elif defined(__arm__) -# define __NR_statx (UV_SYSCALL_BASE + 397) -# elif defined(__ppc__) -# define __NR_statx 383 -# elif defined(__s390__) -# define __NR_statx 379 -# endif -#endif /* __NR_statx */ - -#ifndef __NR_getrandom -# if defined(__x86_64__) -# define __NR_getrandom 318 -# elif defined(__i386__) -# define __NR_getrandom 355 -# elif defined(__aarch64__) -# define __NR_getrandom 384 -# elif defined(__arm__) -# define __NR_getrandom (UV_SYSCALL_BASE + 384) -# elif defined(__ppc__) -# define __NR_getrandom 359 -# elif defined(__s390__) -# define __NR_getrandom 349 -# endif -#endif /* __NR_getrandom */ - -struct uv__mmsghdr; - -int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { -#if defined(__i386__) - unsigned long args[4]; - int rc; - - args[0] = (unsigned long) fd; - args[1] = (unsigned long) mmsg; - args[2] = (unsigned long) vlen; - args[3] = /* flags */ 0; - - /* socketcall() raises EINVAL when SYS_SENDMMSG is not supported. */ - rc = syscall(/* __NR_socketcall */ 102, 20 /* SYS_SENDMMSG */, args); - if (rc == -1) - if (errno == EINVAL) - errno = ENOSYS; - - return rc; -#elif defined(__NR_sendmmsg) - return syscall(__NR_sendmmsg, fd, mmsg, vlen, /* flags */ 0); -#else - return errno = ENOSYS, -1; -#endif -} - - -int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { -#if defined(__i386__) - unsigned long args[5]; - int rc; - - args[0] = (unsigned long) fd; - args[1] = (unsigned long) mmsg; - args[2] = (unsigned long) vlen; - args[3] = /* flags */ 0; - args[4] = /* timeout */ 0; - - /* socketcall() raises EINVAL when SYS_RECVMMSG is not supported. */ - rc = syscall(/* __NR_socketcall */ 102, 19 /* SYS_RECVMMSG */, args); - if (rc == -1) - if (errno == EINVAL) - errno = ENOSYS; - - return rc; -#elif defined(__NR_recvmmsg) - return syscall(__NR_recvmmsg, fd, mmsg, vlen, /* flags */ 0, /* timeout */ 0); -#else - return errno = ENOSYS, -1; -#endif -} - - -ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset) { -#if !defined(__NR_preadv) || defined(__ANDROID_API__) && __ANDROID_API__ < 24 - return errno = ENOSYS, -1; -#else - return syscall(__NR_preadv, fd, iov, iovcnt, (long)offset, (long)(offset >> 32)); -#endif -} - - -ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset) { -#if !defined(__NR_pwritev) || defined(__ANDROID_API__) && __ANDROID_API__ < 24 - return errno = ENOSYS, -1; -#else - return syscall(__NR_pwritev, fd, iov, iovcnt, (long)offset, (long)(offset >> 32)); -#endif -} - - -int uv__dup3(int oldfd, int newfd, int flags) { -#if !defined(__NR_dup3) || defined(__ANDROID_API__) && __ANDROID_API__ < 21 - return errno = ENOSYS, -1; -#else - return syscall(__NR_dup3, oldfd, newfd, flags); -#endif -} - - -ssize_t -uv__fs_copy_file_range(int fd_in, - off_t* off_in, - int fd_out, - off_t* off_out, - size_t len, - unsigned int flags) -{ -#ifdef __NR_copy_file_range - return syscall(__NR_copy_file_range, - fd_in, - off_in, - fd_out, - off_out, - len, - flags); -#else - return errno = ENOSYS, -1; -#endif -} - - -int uv__statx(int dirfd, - const char* path, - int flags, - unsigned int mask, - struct uv__statx* statxbuf) { -#if !defined(__NR_statx) || defined(__ANDROID_API__) && __ANDROID_API__ < 30 - return errno = ENOSYS, -1; -#else - return syscall(__NR_statx, dirfd, path, flags, mask, statxbuf); -#endif -} - - -ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags) { -#if !defined(__NR_getrandom) || defined(__ANDROID_API__) && __ANDROID_API__ < 28 - return errno = ENOSYS, -1; -#else - return syscall(__NR_getrandom, buf, buflen, flags); -#endif -} diff --git a/src/unix/linux-syscalls.h b/src/unix/linux-syscalls.h deleted file mode 100644 index b4d9082d46f..00000000000 --- a/src/unix/linux-syscalls.h +++ /dev/null @@ -1,78 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef UV_LINUX_SYSCALL_H_ -#define UV_LINUX_SYSCALL_H_ - -#include -#include -#include -#include -#include - -struct uv__statx_timestamp { - int64_t tv_sec; - uint32_t tv_nsec; - int32_t unused0; -}; - -struct uv__statx { - uint32_t stx_mask; - uint32_t stx_blksize; - uint64_t stx_attributes; - uint32_t stx_nlink; - uint32_t stx_uid; - uint32_t stx_gid; - uint16_t stx_mode; - uint16_t unused0; - uint64_t stx_ino; - uint64_t stx_size; - uint64_t stx_blocks; - uint64_t stx_attributes_mask; - struct uv__statx_timestamp stx_atime; - struct uv__statx_timestamp stx_btime; - struct uv__statx_timestamp stx_ctime; - struct uv__statx_timestamp stx_mtime; - uint32_t stx_rdev_major; - uint32_t stx_rdev_minor; - uint32_t stx_dev_major; - uint32_t stx_dev_minor; - uint64_t unused1[14]; -}; - -ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset); -ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset); -int uv__dup3(int oldfd, int newfd, int flags); -ssize_t -uv__fs_copy_file_range(int fd_in, - off_t* off_in, - int fd_out, - off_t* off_out, - size_t len, - unsigned int flags); -int uv__statx(int dirfd, - const char* path, - int flags, - unsigned int mask, - struct uv__statx* statxbuf); -ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags); - -#endif /* UV_LINUX_SYSCALL_H_ */ diff --git a/src/unix/linux.c b/src/unix/linux.c new file mode 100644 index 00000000000..763f5dd5917 --- /dev/null +++ b/src/unix/linux.c @@ -0,0 +1,2723 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* We lean on the fact that POLL{IN,OUT,ERR,HUP} correspond with their + * EPOLL* counterparts. We use the POLL* variants in this file because that + * is what libuv uses elsewhere. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include /* offsetof */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __NR_io_uring_setup +# define __NR_io_uring_setup 425 +#endif + +#ifndef __NR_io_uring_enter +# define __NR_io_uring_enter 426 +#endif + +#ifndef __NR_io_uring_register +# define __NR_io_uring_register 427 +#endif + +#ifndef __NR_copy_file_range +# if defined(__x86_64__) +# define __NR_copy_file_range 326 +# elif defined(__i386__) +# define __NR_copy_file_range 377 +# elif defined(__s390__) +# define __NR_copy_file_range 375 +# elif defined(__arm__) +# define __NR_copy_file_range 391 +# elif defined(__aarch64__) +# define __NR_copy_file_range 285 +# elif defined(__powerpc__) +# define __NR_copy_file_range 379 +# elif defined(__arc__) +# define __NR_copy_file_range 285 +# elif defined(__riscv) +# define __NR_copy_file_range 285 +# endif +#endif /* __NR_copy_file_range */ + +#ifndef __NR_statx +# if defined(__x86_64__) +# define __NR_statx 332 +# elif defined(__i386__) +# define __NR_statx 383 +# elif defined(__aarch64__) +# define __NR_statx 397 +# elif defined(__arm__) +# define __NR_statx 397 +# elif defined(__ppc__) +# define __NR_statx 383 +# elif defined(__s390__) +# define __NR_statx 379 +# elif defined(__riscv) +# define __NR_statx 291 +# endif +#endif /* __NR_statx */ + +#ifndef __NR_getrandom +# if defined(__x86_64__) +# define __NR_getrandom 318 +# elif defined(__i386__) +# define __NR_getrandom 355 +# elif defined(__aarch64__) +# define __NR_getrandom 384 +# elif defined(__arm__) +# define __NR_getrandom 384 +# elif defined(__ppc__) +# define __NR_getrandom 359 +# elif defined(__s390__) +# define __NR_getrandom 349 +# elif defined(__riscv) +# define __NR_getrandom 278 +# endif +#endif /* __NR_getrandom */ + +enum { + UV__IORING_SETUP_SQPOLL = 2u, + UV__IORING_SETUP_NO_SQARRAY = 0x10000u, +}; + +enum { + UV__IORING_FEAT_SINGLE_MMAP = 1u, + UV__IORING_FEAT_NODROP = 2u, + UV__IORING_FEAT_RSRC_TAGS = 1024u, /* linux v5.13 */ +}; + +enum { + UV__IORING_OP_READV = 1, + UV__IORING_OP_WRITEV = 2, + UV__IORING_OP_FSYNC = 3, + UV__IORING_OP_OPENAT = 18, + UV__IORING_OP_CLOSE = 19, + UV__IORING_OP_STATX = 21, + UV__IORING_OP_EPOLL_CTL = 29, + UV__IORING_OP_RENAMEAT = 35, + UV__IORING_OP_UNLINKAT = 36, + UV__IORING_OP_MKDIRAT = 37, + UV__IORING_OP_SYMLINKAT = 38, + UV__IORING_OP_LINKAT = 39, + UV__IORING_OP_FTRUNCATE = 55, +}; + +enum { + UV__IORING_ENTER_GETEVENTS = 1u, + UV__IORING_ENTER_SQ_WAKEUP = 2u, +}; + +enum { + UV__IORING_SQ_NEED_WAKEUP = 1u, + UV__IORING_SQ_CQ_OVERFLOW = 2u, +}; + +struct uv__io_cqring_offsets { + uint32_t head; + uint32_t tail; + uint32_t ring_mask; + uint32_t ring_entries; + uint32_t overflow; + uint32_t cqes; + uint64_t reserved0; + uint64_t reserved1; +}; + +STATIC_ASSERT(40 == sizeof(struct uv__io_cqring_offsets)); + +struct uv__io_sqring_offsets { + uint32_t head; + uint32_t tail; + uint32_t ring_mask; + uint32_t ring_entries; + uint32_t flags; + uint32_t dropped; + uint32_t array; + uint32_t reserved0; + uint64_t reserved1; +}; + +STATIC_ASSERT(40 == sizeof(struct uv__io_sqring_offsets)); + +struct uv__io_uring_cqe { + uint64_t user_data; + int32_t res; + uint32_t flags; +}; + +STATIC_ASSERT(16 == sizeof(struct uv__io_uring_cqe)); + +struct uv__io_uring_sqe { + uint8_t opcode; + uint8_t flags; + uint16_t ioprio; + int32_t fd; + union { + uint64_t off; + uint64_t addr2; + }; + union { + uint64_t addr; + }; + uint32_t len; + union { + uint32_t rw_flags; + uint32_t fsync_flags; + uint32_t open_flags; + uint32_t statx_flags; + }; + uint64_t user_data; + union { + uint16_t buf_index; + uint64_t pad[3]; + }; +}; + +STATIC_ASSERT(64 == sizeof(struct uv__io_uring_sqe)); +STATIC_ASSERT(0 == offsetof(struct uv__io_uring_sqe, opcode)); +STATIC_ASSERT(1 == offsetof(struct uv__io_uring_sqe, flags)); +STATIC_ASSERT(2 == offsetof(struct uv__io_uring_sqe, ioprio)); +STATIC_ASSERT(4 == offsetof(struct uv__io_uring_sqe, fd)); +STATIC_ASSERT(8 == offsetof(struct uv__io_uring_sqe, off)); +STATIC_ASSERT(16 == offsetof(struct uv__io_uring_sqe, addr)); +STATIC_ASSERT(24 == offsetof(struct uv__io_uring_sqe, len)); +STATIC_ASSERT(28 == offsetof(struct uv__io_uring_sqe, rw_flags)); +STATIC_ASSERT(32 == offsetof(struct uv__io_uring_sqe, user_data)); +STATIC_ASSERT(40 == offsetof(struct uv__io_uring_sqe, buf_index)); + +struct uv__io_uring_params { + uint32_t sq_entries; + uint32_t cq_entries; + uint32_t flags; + uint32_t sq_thread_cpu; + uint32_t sq_thread_idle; + uint32_t features; + uint32_t reserved[4]; + struct uv__io_sqring_offsets sq_off; /* 40 bytes */ + struct uv__io_cqring_offsets cq_off; /* 40 bytes */ +}; + +STATIC_ASSERT(40 + 40 + 40 == sizeof(struct uv__io_uring_params)); +STATIC_ASSERT(40 == offsetof(struct uv__io_uring_params, sq_off)); +STATIC_ASSERT(80 == offsetof(struct uv__io_uring_params, cq_off)); + +STATIC_ASSERT(EPOLL_CTL_ADD < 4); +STATIC_ASSERT(EPOLL_CTL_DEL < 4); +STATIC_ASSERT(EPOLL_CTL_MOD < 4); + +struct watcher_list { + RB_ENTRY(watcher_list) entry; + struct uv__queue watchers; + int iterating; + char* path; + int wd; +}; + +struct watcher_root { + struct watcher_list* rbh_root; +}; + +static int uv__inotify_fork(uv_loop_t* loop, struct watcher_list* root); +static void uv__inotify_read(uv_loop_t* loop, + uv__io_t* w, + unsigned int revents); +static int compare_watchers(const struct watcher_list* a, + const struct watcher_list* b); +static void maybe_free_watcher_list(struct watcher_list* w, + uv_loop_t* loop); + +static void uv__epoll_ctl_flush(int epollfd, + struct uv__iou* ctl, + struct epoll_event (*events)[256]); + +static void uv__epoll_ctl_prep(int epollfd, + struct uv__iou* ctl, + struct epoll_event (*events)[256], + int op, + int fd, + struct epoll_event* e); + +RB_GENERATE_STATIC(watcher_root, watcher_list, entry, compare_watchers) + + +static struct watcher_root* uv__inotify_watchers(uv_loop_t* loop) { + /* This cast works because watcher_root is a struct with a pointer as its + * sole member. Such type punning is unsafe in the presence of strict + * pointer aliasing (and is just plain nasty) but that is why libuv + * is compiled with -fno-strict-aliasing. + */ + return (struct watcher_root*) &loop->inotify_watchers; +} + + +unsigned uv__kernel_version(void) { + static _Atomic unsigned cached_version; + struct utsname u; + unsigned version; + unsigned major; + unsigned minor; + unsigned patch; + char v_sig[256]; + char* needle; + + version = atomic_load_explicit(&cached_version, memory_order_relaxed); + if (version != 0) + return version; + + /* Check /proc/version_signature first as it's the way to get the mainline + * kernel version in Ubuntu. The format is: + * Ubuntu ubuntu_kernel_version mainline_kernel_version + * For example: + * Ubuntu 5.15.0-79.86-generic 5.15.111 + */ + if (0 == uv__slurp("/proc/version_signature", v_sig, sizeof(v_sig))) + if (3 == sscanf(v_sig, "Ubuntu %*s %u.%u.%u", &major, &minor, &patch)) + goto calculate_version; + + if (-1 == uname(&u)) + return 0; + + /* In Debian we need to check `version` instead of `release` to extract the + * mainline kernel version. This is an example of how it looks like: + * #1 SMP Debian 5.10.46-4 (2021-08-03) + */ + needle = strstr(u.version, "Debian "); + if (needle != NULL) + if (3 == sscanf(needle, "Debian %u.%u.%u", &major, &minor, &patch)) + goto calculate_version; + + if (3 != sscanf(u.release, "%u.%u.%u", &major, &minor, &patch)) + return 0; + + /* Handle it when the process runs under the UNAME26 personality: + * + * - kernels >= 3.x identify as 2.6.40+x + * - kernels >= 4.x identify as 2.6.60+x + * + * UNAME26 is a poorly conceived hack that doesn't let us distinguish + * between 4.x kernels and 5.x/6.x kernels so we conservatively assume + * that 2.6.60+x means 4.x. + * + * Fun fact of the day: it's technically possible to observe the actual + * kernel version for a brief moment because uname() first copies out the + * real release string before overwriting it with the backcompat string. + */ + if (major == 2 && minor == 6) { + if (patch >= 60) { + major = 4; + minor = patch - 60; + patch = 0; + } else if (patch >= 40) { + major = 3; + minor = patch - 40; + patch = 0; + } + } + +calculate_version: + version = major * 65536 + minor * 256 + patch; + atomic_store_explicit(&cached_version, version, memory_order_relaxed); + + return version; +} + + +ssize_t +uv__fs_copy_file_range(int fd_in, + off_t* off_in, + int fd_out, + off_t* off_out, + size_t len, + unsigned int flags) +{ +#ifdef __NR_copy_file_range + return syscall(__NR_copy_file_range, + fd_in, + off_in, + fd_out, + off_out, + len, + flags); +#else + return errno = ENOSYS, -1; +#endif +} + + +int uv__statx(int dirfd, + const char* path, + int flags, + unsigned int mask, + struct uv__statx* statxbuf) { +#if !defined(__NR_statx) || defined(__ANDROID_API__) && __ANDROID_API__ < 30 + return errno = ENOSYS, -1; +#else + int rc; + + rc = syscall(__NR_statx, dirfd, path, flags, mask, statxbuf); + if (rc >= 0) + uv__msan_unpoison(statxbuf, sizeof(*statxbuf)); + + return rc; +#endif +} + + +ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags) { +#if !defined(__NR_getrandom) || defined(__ANDROID_API__) && __ANDROID_API__ < 28 + return errno = ENOSYS, -1; +#else + ssize_t rc; + + rc = syscall(__NR_getrandom, buf, buflen, flags); + if (rc >= 0) + uv__msan_unpoison(buf, buflen); + + return rc; +#endif +} + + +int uv__io_uring_setup(int entries, struct uv__io_uring_params* params) { + return syscall(__NR_io_uring_setup, entries, params); +} + + +int uv__io_uring_enter(int fd, + unsigned to_submit, + unsigned min_complete, + unsigned flags) { + /* io_uring_enter used to take a sigset_t but it's unused + * in newer kernels unless IORING_ENTER_EXT_ARG is set, + * in which case it takes a struct io_uring_getevents_arg. + */ + return syscall(__NR_io_uring_enter, + fd, + to_submit, + min_complete, + flags, + NULL, + 0L); +} + + +int uv__io_uring_register(int fd, unsigned opcode, void* arg, unsigned nargs) { + return syscall(__NR_io_uring_register, fd, opcode, arg, nargs); +} + + +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) { + val = getenv("UV_USE_IO_URING"); + use = val != NULL && atoi(val) > 0 ? 1 : -1; + atomic_store_explicit(&use_io_uring, use, memory_order_relaxed); + } + + return use > 0; +#endif +} + + +static void uv__iou_init(int epollfd, + struct uv__iou* iou, + uint32_t entries, + uint32_t flags) { + struct uv__io_uring_params params; + struct epoll_event e; + size_t cqlen; + 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(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 | no_sqarray; + + if (flags & UV__IORING_SETUP_SQPOLL) + params.sq_thread_idle = 10; /* milliseconds */ + + /* Kernel returns a file descriptor with O_CLOEXEC flag set. */ + ringfd = uv__io_uring_setup(entries, ¶ms); + if (ringfd == -1) + return; + + /* IORING_FEAT_RSRC_TAGS is used to detect linux v5.13 but what we're + * actually detecting is whether IORING_OP_STATX works with SQPOLL. + */ + if (!(params.features & UV__IORING_FEAT_RSRC_TAGS)) + goto fail; + + /* Implied by IORING_FEAT_RSRC_TAGS but checked explicitly anyway. */ + if (!(params.features & UV__IORING_FEAT_SINGLE_MMAP)) + goto fail; + + /* Implied by IORING_FEAT_RSRC_TAGS but checked explicitly anyway. */ + if (!(params.features & UV__IORING_FEAT_NODROP)) + goto fail; + + sqlen = params.sq_off.array + params.sq_entries * sizeof(uint32_t); + cqlen = + params.cq_off.cqes + params.cq_entries * sizeof(struct uv__io_uring_cqe); + maxlen = sqlen < cqlen ? cqlen : sqlen; + sqelen = params.sq_entries * sizeof(struct uv__io_uring_sqe); + + sq = mmap(0, + maxlen, + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_POPULATE, + ringfd, + 0); /* IORING_OFF_SQ_RING */ + + sqe = mmap(0, + sqelen, + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_POPULATE, + ringfd, + 0x10000000ull); /* IORING_OFF_SQES */ + + if (sq == MAP_FAILED || sqe == MAP_FAILED) + goto fail; + + if (flags & UV__IORING_SETUP_SQPOLL) { + /* Only interested in completion events. To get notified when + * the kernel pulls items from the submission ring, add POLLOUT. + */ + memset(&e, 0, sizeof(e)); + e.events = POLLIN; + e.data.fd = ringfd; + + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ringfd, &e)) + goto fail; + } + + 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->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); + iou->cqmask = *(uint32_t*) (sq + params.cq_off.ring_mask); + iou->sq = sq; + iou->cqe = sq + params.cq_off.cqes; + iou->sqe = sqe; + iou->sqlen = sqlen; + iou->cqlen = cqlen; + iou->maxlen = maxlen; + iou->sqelen = sqelen; + iou->ringfd = ringfd; + iou->in_flight = 0; + + if (no_sqarray) + return; + + sqarray = (uint32_t*) (sq + params.sq_off.array); + for (i = 0; i <= iou->sqmask; i++) + sqarray[i] = i; /* Slot -> sqe identity mapping. */ + + return; + +fail: + if (sq != MAP_FAILED) + munmap(sq, maxlen); + + if (sqe != MAP_FAILED) + munmap(sqe, sqelen); + + uv__close(ringfd); +} + + +static void uv__iou_delete(struct uv__iou* iou) { + if (iou->ringfd > -1) { + munmap(iou->sq, iou->maxlen); + munmap(iou->sqe, iou->sqelen); + uv__close(iou->ringfd); + iou->ringfd = -1; + } +} + + +int uv__platform_loop_init(uv_loop_t* loop) { + uv__loop_internal_fields_t* lfields; + + lfields = uv__get_internal_fields(loop); + lfields->ctl.ringfd = -1; + lfields->iou.ringfd = -2; /* "uninitialized" */ + + loop->inotify_watchers = NULL; + loop->inotify_fd = -1; + loop->backend_fd = epoll_create1(O_CLOEXEC); + + if (loop->backend_fd == -1) + return UV__ERR(errno); + + uv__iou_init(loop->backend_fd, &lfields->ctl, 256, 0); + + return 0; +} + + +int uv__io_fork(uv_loop_t* loop) { + int err; + struct watcher_list* root; + + root = uv__inotify_watchers(loop)->rbh_root; + + uv__close(loop->backend_fd); + loop->backend_fd = -1; + + /* TODO(bnoordhuis) Loses items from the submission and completion rings. */ + uv__platform_loop_delete(loop); + + err = uv__platform_loop_init(loop); + if (err) + return err; + + return uv__inotify_fork(loop, root); +} + + +void uv__platform_loop_delete(uv_loop_t* loop) { + uv__loop_internal_fields_t* lfields; + + lfields = uv__get_internal_fields(loop); + uv__iou_delete(&lfields->ctl); + uv__iou_delete(&lfields->iou); + + if (loop->inotify_fd != -1) { + uv__io_stop(loop, &loop->inotify_read_watcher, POLLIN); + uv__close(loop->inotify_fd); + loop->inotify_fd = -1; + } +} + + +struct uv__invalidate { + struct epoll_event (*prep)[256]; + struct epoll_event* events; + int nfds; +}; + + +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + uv__loop_internal_fields_t* lfields; + struct uv__invalidate* inv; + struct epoll_event dummy; + int i; + + lfields = uv__get_internal_fields(loop); + inv = lfields->inv; + + /* Invalidate events with same file descriptor */ + if (inv != NULL) + for (i = 0; i < inv->nfds; i++) + if (inv->events[i].data.fd == fd) + inv->events[i].data.fd = -1; + + /* Remove the file descriptor from the epoll. + * 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)); + epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &dummy); +} + + +int uv__io_check_fd(uv_loop_t* loop, int fd) { + struct epoll_event e; + int rc; + + memset(&e, 0, sizeof(e)); + e.events = POLLIN; + e.data.fd = -1; + + rc = 0; + if (epoll_ctl(loop->backend_fd, EPOLL_CTL_ADD, fd, &e)) + if (errno != EEXIST) + rc = UV__ERR(errno); + + if (rc == 0) + if (epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &e)) + abort(); + + return rc; +} + + +/* Caller must initialize SQE and call uv__iou_submit(). */ +static struct uv__io_uring_sqe* uv__iou_get_sqe(struct uv__iou* iou, + uv_loop_t* loop, + uv_fs_t* req) { + struct uv__io_uring_sqe* sqe; + uint32_t head; + uint32_t tail; + 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; + + head = atomic_load_explicit((_Atomic uint32_t*) iou->sqhead, + memory_order_acquire); + tail = *iou->sqtail; + mask = iou->sqmask; + + if ((head & mask) == ((tail + 1) & mask)) + return NULL; /* No room in ring buffer. TODO(bnoordhuis) maybe flush it? */ + + slot = tail & mask; + sqe = iou->sqe; + sqe = &sqe[slot]; + memset(sqe, 0, sizeof(*sqe)); + sqe->user_data = (uintptr_t) req; + + /* Pacify uv_cancel(). */ + req->work_req.loop = loop; + req->work_req.work = NULL; + req->work_req.done = NULL; + uv__queue_init(&req->work_req.wq); + + uv__req_register(loop); + iou->in_flight++; + + return sqe; +} + + +static void uv__iou_submit(struct uv__iou* iou) { + uint32_t flags; + + atomic_store_explicit((_Atomic uint32_t*) iou->sqtail, + *iou->sqtail + 1, + memory_order_release); + + flags = atomic_load_explicit((_Atomic uint32_t*) iou->sqflags, + memory_order_acquire); + + if (flags & UV__IORING_SQ_NEED_WAKEUP) + if (uv__io_uring_enter(iou->ringfd, 0, 0, UV__IORING_ENTER_SQ_WAKEUP)) + if (errno != EOWNERDEAD) /* Kernel bug. Harmless, ignore. */ + perror("libuv: io_uring_enter(wakeup)"); /* Can't happen. */ +} + + +int uv__iou_fs_close(uv_loop_t* loop, uv_fs_t* req) { + struct uv__io_uring_sqe* sqe; + struct uv__iou* iou; + int kv; + + kv = uv__kernel_version(); + /* Work around a poorly understood bug in older kernels where closing a file + * descriptor pointing to /foo/bar results in ETXTBSY errors when trying to + * execve("/foo/bar") later on. The bug seems to have been fixed somewhere + * between 5.15.85 and 5.15.90. I couldn't pinpoint the responsible commit + * but good candidates are the several data race fixes. Interestingly, it + * seems to manifest only when running under Docker so the possibility of + * a Docker bug can't be completely ruled out either. Yay, computers. + * Also, disable on non-longterm versions between 5.16.0 (non-longterm) and + * 6.1.0 (longterm). Starting with longterm 6.1.x, the issue seems to be + * solved. + */ + if (kv < /* 5.15.90 */ 0x050F5A) + return 0; + + if (kv >= /* 5.16.0 */ 0x050A00 && kv < /* 6.1.0 */ 0x060100) + 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->opcode = UV__IORING_OP_CLOSE; + + uv__iou_submit(iou); + + return 1; +} + + +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) { + struct uv__io_uring_sqe* sqe; + struct uv__iou* iou; + + iou = &uv__get_internal_fields(loop)->iou; + + sqe = uv__iou_get_sqe(iou, loop, req); + if (sqe == NULL) + return 0; + + /* Little known fact: setting seq->off and seq->len turns + * it into an asynchronous sync_file_range() operation. + */ + sqe->fd = req->file; + sqe->fsync_flags = fsync_flags; + sqe->opcode = UV__IORING_OP_FSYNC; + + uv__iou_submit(iou); + + return 1; +} + + +int uv__iou_fs_link(uv_loop_t* loop, uv_fs_t* req) { + struct uv__io_uring_sqe* sqe; + struct uv__iou* iou; + + 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; + + sqe->addr = (uintptr_t) req->path; + sqe->fd = AT_FDCWD; + sqe->addr2 = (uintptr_t) req->new_path; + sqe->len = AT_FDCWD; + sqe->opcode = UV__IORING_OP_LINKAT; + + uv__iou_submit(iou); + + return 1; +} + + +int uv__iou_fs_mkdir(uv_loop_t* loop, uv_fs_t* req) { + struct uv__io_uring_sqe* sqe; + struct uv__iou* iou; + + 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; + + sqe->addr = (uintptr_t) req->path; + sqe->fd = AT_FDCWD; + sqe->len = req->mode; + sqe->opcode = UV__IORING_OP_MKDIRAT; + + uv__iou_submit(iou); + + return 1; +} + + +int uv__iou_fs_open(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; + + sqe = uv__iou_get_sqe(iou, loop, req); + if (sqe == NULL) + return 0; + + sqe->addr = (uintptr_t) req->path; + sqe->fd = AT_FDCWD; + sqe->len = req->mode; + sqe->opcode = UV__IORING_OP_OPENAT; + sqe->open_flags = req->flags | O_CLOEXEC; + + uv__iou_submit(iou); + + return 1; +} + + +int uv__iou_fs_rename(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; + + sqe = uv__iou_get_sqe(iou, loop, req); + if (sqe == NULL) + return 0; + + sqe->addr = (uintptr_t) req->path; + sqe->fd = AT_FDCWD; + sqe->addr2 = (uintptr_t) req->new_path; + sqe->len = AT_FDCWD; + sqe->opcode = UV__IORING_OP_RENAMEAT; + + uv__iou_submit(iou); + + return 1; +} + + +int uv__iou_fs_symlink(uv_loop_t* loop, uv_fs_t* req) { + struct uv__io_uring_sqe* sqe; + struct uv__iou* iou; + + 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; + + sqe->addr = (uintptr_t) req->path; + sqe->fd = AT_FDCWD; + sqe->addr2 = (uintptr_t) req->new_path; + sqe->opcode = UV__IORING_OP_SYMLINKAT; + + uv__iou_submit(iou); + + return 1; +} + + +int uv__iou_fs_unlink(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; + + sqe = uv__iou_get_sqe(iou, loop, req); + if (sqe == NULL) + return 0; + + sqe->addr = (uintptr_t) req->path; + sqe->fd = AT_FDCWD; + sqe->opcode = UV__IORING_OP_UNLINKAT; + + uv__iou_submit(iou); + + return 1; +} + + +int uv__iou_fs_read_or_write(uv_loop_t* loop, + uv_fs_t* req, + int is_read) { + struct uv__io_uring_sqe* sqe; + struct uv__iou* iou; + + /* If iovcnt is greater than IOV_MAX, cap it to IOV_MAX on reads and fallback + * to the threadpool on writes */ + if (req->nbufs > IOV_MAX) { + if (is_read) + req->nbufs = IOV_MAX; + else + return 0; + } + + iou = &uv__get_internal_fields(loop)->iou; + + sqe = uv__iou_get_sqe(iou, loop, req); + if (sqe == NULL) + return 0; + + sqe->addr = (uintptr_t) req->bufs; + sqe->fd = req->file; + sqe->len = req->nbufs; + sqe->off = req->off < 0 ? -1 : req->off; + sqe->opcode = is_read ? UV__IORING_OP_READV : UV__IORING_OP_WRITEV; + + uv__iou_submit(iou); + + return 1; +} + + +int uv__iou_fs_statx(uv_loop_t* loop, + uv_fs_t* req, + int is_fstat, + int is_lstat) { + struct uv__io_uring_sqe* sqe; + struct uv__statx* statxbuf; + struct uv__iou* iou; + + statxbuf = uv__malloc(sizeof(*statxbuf)); + if (statxbuf == NULL) + return 0; + + iou = &uv__get_internal_fields(loop)->iou; + + sqe = uv__iou_get_sqe(iou, loop, req); + if (sqe == NULL) { + uv__free(statxbuf); + return 0; + } + + req->ptr = statxbuf; + + sqe->addr = (uintptr_t) req->path; + sqe->addr2 = (uintptr_t) statxbuf; + sqe->fd = AT_FDCWD; + sqe->len = 0xFFF; /* STATX_BASIC_STATS + STATX_BTIME */ + sqe->opcode = UV__IORING_OP_STATX; + + if (is_fstat) { + sqe->addr = (uintptr_t) ""; + sqe->fd = req->file; + sqe->statx_flags |= 0x1000; /* AT_EMPTY_PATH */ + } + + if (is_lstat) + sqe->statx_flags |= AT_SYMLINK_NOFOLLOW; + + uv__iou_submit(iou); + + return 1; +} + + +void uv__statx_to_stat(const struct uv__statx* statxbuf, uv_stat_t* buf) { + buf->st_dev = makedev(statxbuf->stx_dev_major, statxbuf->stx_dev_minor); + buf->st_mode = statxbuf->stx_mode; + buf->st_nlink = statxbuf->stx_nlink; + buf->st_uid = statxbuf->stx_uid; + buf->st_gid = statxbuf->stx_gid; + buf->st_rdev = makedev(statxbuf->stx_rdev_major, statxbuf->stx_rdev_minor); + buf->st_ino = statxbuf->stx_ino; + buf->st_size = statxbuf->stx_size; + buf->st_blksize = statxbuf->stx_blksize; + buf->st_blocks = statxbuf->stx_blocks; + buf->st_atim.tv_sec = statxbuf->stx_atime.tv_sec; + buf->st_atim.tv_nsec = statxbuf->stx_atime.tv_nsec; + buf->st_mtim.tv_sec = statxbuf->stx_mtime.tv_sec; + buf->st_mtim.tv_nsec = statxbuf->stx_mtime.tv_nsec; + buf->st_ctim.tv_sec = statxbuf->stx_ctime.tv_sec; + buf->st_ctim.tv_nsec = statxbuf->stx_ctime.tv_nsec; + buf->st_birthtim.tv_sec = statxbuf->stx_btime.tv_sec; + buf->st_birthtim.tv_nsec = statxbuf->stx_btime.tv_nsec; + buf->st_flags = 0; + buf->st_gen = 0; +} + + +static void uv__iou_fs_statx_post(uv_fs_t* req) { + struct uv__statx* statxbuf; + uv_stat_t* buf; + + buf = &req->statbuf; + statxbuf = req->ptr; + req->ptr = NULL; + + if (req->result == 0) { + uv__msan_unpoison(statxbuf, sizeof(*statxbuf)); + uv__statx_to_stat(statxbuf, buf); + req->ptr = buf; + } + + uv__free(statxbuf); +} + + +static void uv__poll_io_uring(uv_loop_t* loop, struct uv__iou* iou) { + struct uv__io_uring_cqe* cqe; + struct uv__io_uring_cqe* e; + uv_fs_t* req; + uint32_t head; + uint32_t tail; + uint32_t mask; + uint32_t i; + uint32_t flags; + int nevents; + int rc; + + head = *iou->cqhead; + tail = atomic_load_explicit((_Atomic uint32_t*) iou->cqtail, + memory_order_acquire); + mask = iou->cqmask; + cqe = iou->cqe; + nevents = 0; + + for (i = head; i != tail; i++) { + e = &cqe[i & mask]; + + req = (uv_fs_t*) (uintptr_t) e->user_data; + assert(req->type == UV_FS); + + 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; + + switch (req->fs_type) { + case UV_FS_FSTAT: + case UV_FS_LSTAT: + case UV_FS_STAT: + uv__iou_fs_statx_post(req); + break; + default: /* Squelch -Wswitch warnings. */ + break; + } + + uv__metrics_update_idle_time(loop); + req->cb(req); + nevents++; + } + + atomic_store_explicit((_Atomic uint32_t*) iou->cqhead, + tail, + memory_order_release); + + /* Check whether CQE's overflowed, if so enter the kernel to make them + * available. Don't grab them immediately but in the next loop iteration to + * avoid loop starvation. */ + flags = atomic_load_explicit((_Atomic uint32_t*) iou->sqflags, + memory_order_acquire); + + if (flags & UV__IORING_SQ_CQ_OVERFLOW) { + do + rc = uv__io_uring_enter(iou->ringfd, 0, 0, UV__IORING_ENTER_GETEVENTS); + while (rc == -1 && errno == EINTR); + + if (rc < 0) + perror("libuv: io_uring_enter(getevents)"); /* Can't happen. */ + } + + uv__metrics_inc_events(loop, nevents); + if (uv__get_internal_fields(loop)->current_timeout == 0) + uv__metrics_inc_events_waiting(loop, nevents); +} + + +/* 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], + int op, + int fd, + struct epoll_event* e) { + struct uv__io_uring_sqe* sqe; + struct epoll_event* pe; + uint32_t mask; + uint32_t slot; + + assert(op == EPOLL_CTL_ADD || op == EPOLL_CTL_MOD); + assert(ctl->ringfd != -1); + + 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); +} + + +static void uv__epoll_ctl_flush(int epollfd, + struct uv__iou* ctl, + struct epoll_event (*events)[256]) { + struct epoll_event oldevents[256]; + struct uv__io_uring_cqe* cqe; + uint32_t oldslot; + uint32_t slot; + uint32_t n; + int fd; + int op; + int rc; + + STATIC_ASSERT(sizeof(oldevents) == sizeof(*events)); + assert(ctl->ringfd != -1); + assert(*ctl->sqhead != *ctl->sqtail); + + n = *ctl->sqtail - *ctl->sqhead; + do + rc = uv__io_uring_enter(ctl->ringfd, n, n, UV__IORING_ENTER_GETEVENTS); + while (rc == -1 && errno == EINTR); + + if (rc < 0) + perror("libuv: io_uring_enter(getevents)"); /* Can't happen. */ + + if (rc != (int) n) + abort(); + + assert(*ctl->sqhead == *ctl->sqtail); + + memcpy(oldevents, *events, sizeof(*events)); + + /* Failed submissions are either EPOLL_CTL_DEL commands for file descriptors + * that have been closed, or EPOLL_CTL_ADD commands for file descriptors + * that we are already watching. Ignore the former and retry the latter + * with EPOLL_CTL_MOD. + */ + while (*ctl->cqhead != *ctl->cqtail) { + slot = (*ctl->cqhead)++ & ctl->cqmask; + + cqe = ctl->cqe; + cqe = &cqe[slot]; + + if (cqe->res == 0) + continue; + + fd = cqe->user_data >> 32; + op = 3 & cqe->user_data; + oldslot = 255 & (cqe->user_data >> 2); + + if (op == EPOLL_CTL_DEL) + continue; + + if (op != EPOLL_CTL_ADD) + abort(); + + if (cqe->res != -EEXIST) + abort(); + + uv__epoll_ctl_prep(epollfd, + ctl, + events, + EPOLL_CTL_MOD, + fd, + &oldevents[oldslot]); + } +} + + +void uv__io_poll(uv_loop_t* loop, int timeout) { + uv__loop_internal_fields_t* lfields; + struct epoll_event events[1024]; + struct epoll_event prep[256]; + struct uv__invalidate inv; + struct epoll_event* pe; + struct epoll_event e; + struct uv__iou* ctl; + struct uv__iou* iou; + int real_timeout; + struct uv__queue* q; + uv__io_t* w; + sigset_t* sigmask; + sigset_t sigset; + uint64_t base; + int have_iou_events; + int have_signals; + int nevents; + int epollfd; + int count; + int nfds; + int fd; + int op; + int i; + int user_timeout; + int reset_timeout; + + lfields = uv__get_internal_fields(loop); + ctl = &lfields->ctl; + iou = &lfields->iou; + + sigmask = NULL; + if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGPROF); + sigmask = &sigset; + } + + assert(timeout >= -1); + base = loop->time; + count = 48; /* Benchmarks suggest this gives the best throughput. */ + real_timeout = timeout; + + if (lfields->flags & UV_METRICS_IDLE_TIME) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + user_timeout = 0; + } + + epollfd = loop->backend_fd; + + memset(&e, 0, sizeof(e)); + + while (!uv__queue_empty(&loop->watcher_queue)) { + q = uv__queue_head(&loop->watcher_queue); + w = uv__queue_data(q, uv__io_t, watcher_queue); + uv__queue_remove(q); + uv__queue_init(q); + + op = EPOLL_CTL_MOD; + if (w->events == 0) + op = EPOLL_CTL_ADD; + + w->events = w->pevents; + e.events = w->pevents; + e.data.fd = w->fd; + fd = w->fd; + + if (ctl->ringfd != -1) { + uv__epoll_ctl_prep(epollfd, ctl, &prep, op, fd, &e); + continue; + } + + if (!epoll_ctl(epollfd, op, fd, &e)) + continue; + + assert(op == EPOLL_CTL_ADD); + assert(errno == EEXIST); + + /* File descriptor that's been watched before, update event mask. */ + if (epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &e)) + abort(); + } + + inv.events = events; + inv.prep = &prep; + inv.nfds = -1; + + for (;;) { + if (loop->nfds == 0) + if (iou->in_flight == 0) + break; + + /* All event mask mutations should be visible to the kernel before + * we enter epoll_pwait(). + */ + if (ctl->ringfd != -1) + while (*ctl->sqhead != *ctl->sqtail) + uv__epoll_ctl_flush(epollfd, ctl, &prep); + + /* 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; + + nfds = epoll_pwait(epollfd, events, ARRAY_SIZE(events), timeout, sigmask); + + /* Update loop->time unconditionally. It's tempting to skip the update when + * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the + * operating system didn't reschedule our process while in the syscall. + */ + SAVE_ERRNO(uv__update_time(loop)); + + if (nfds == -1) + assert(errno == EINTR); + else if (nfds == 0) + /* Unlimited timeout should only return with events or signal. */ + assert(timeout != -1); + + if (nfds == 0 || nfds == -1) { + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } else if (nfds == 0) { + return; + } + + /* Interrupted by a signal. Update timeout and poll again. */ + goto update_timeout; + } + + have_iou_events = 0; + have_signals = 0; + nevents = 0; + + inv.nfds = nfds; + lfields->inv = &inv; + + for (i = 0; i < nfds; i++) { + pe = events + i; + fd = pe->data.fd; + + /* Skip invalidated events, see uv__platform_invalidate_fd */ + if (fd == -1) + continue; + + if (fd == iou->ringfd) { + uv__poll_io_uring(loop, iou); + have_iou_events = 1; + continue; + } + + assert(fd >= 0); + assert((unsigned) fd < loop->nwatchers); + + w = loop->watchers[fd]; + + if (w == NULL) { + /* File descriptor that we've stopped watching, disarm it. + * + * 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. + */ + epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, pe); + continue; + } + + /* Give users only events they're interested in. Prevents spurious + * callbacks when previous callback invocation in this loop has stopped + * the current watcher. Also, filters out events that users has not + * requested us to watch. + */ + pe->events &= w->pevents | POLLERR | POLLHUP; + + /* Work around an epoll quirk where it sometimes reports just the + * EPOLLERR or EPOLLHUP event. In order to force the event loop to + * move forward, we merge in the read/write events that the watcher + * is interested in; uv__read() and uv__write() will then deal with + * the error or hangup in the usual fashion. + * + * Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user + * reads the available data, calls uv_read_stop(), then sometime later + * calls uv_read_start() again. By then, libuv has forgotten about the + * hangup and the kernel won't report EPOLLIN again because there's + * nothing left to read. If anything, libuv is to blame here. The + * current hack is just a quick bandaid; to properly fix it, libuv + * needs to remember the error/hangup event. We should get that for + * free when we switch over to edge-triggered I/O. + */ + if (pe->events == POLLERR || pe->events == POLLHUP) + pe->events |= + w->pevents & (POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI); + + if (pe->events != 0) { + /* Run signal watchers last. This also affects child process watchers + * because those are implemented in terms of signal watchers. + */ + if (w == &loop->signal_io_watcher) { + have_signals = 1; + } else { + uv__metrics_update_idle_time(loop); + w->cb(loop, w, pe->events); + } + + nevents++; + } + } + + uv__metrics_inc_events(loop, nevents); + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + uv__metrics_inc_events_waiting(loop, nevents); + } + + if (have_signals != 0) { + uv__metrics_update_idle_time(loop); + loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); + } + + lfields->inv = NULL; + + if (have_iou_events != 0) + break; /* Event loop should cycle now so don't poll again. */ + + if (have_signals != 0) + break; /* Event loop should cycle now so don't poll again. */ + + if (nevents != 0) { + if (nfds == ARRAY_SIZE(events) && --count != 0) { + /* Poll for more events but don't block this time. */ + timeout = 0; + continue; + } + break; + } + +update_timeout: + if (timeout == 0) + break; + + if (timeout == -1) + continue; + + assert(timeout > 0); + + real_timeout -= (loop->time - base); + if (real_timeout <= 0) + break; + + timeout = real_timeout; + } + + if (ctl->ringfd != -1) + while (*ctl->sqhead != *ctl->sqtail) + uv__epoll_ctl_flush(epollfd, ctl, &prep); +} + +uint64_t uv__hrtime(uv_clocktype_t type) { + static _Atomic clock_t fast_clock_id = -1; + struct timespec t; + clock_t clock_id; + + /* Prefer CLOCK_MONOTONIC_COARSE if available but only when it has + * millisecond granularity or better. CLOCK_MONOTONIC_COARSE is + * serviced entirely from the vDSO, whereas CLOCK_MONOTONIC may + * decide to make a costly system call. + */ + /* TODO(bnoordhuis) Use CLOCK_MONOTONIC_COARSE for UV_CLOCK_PRECISE + * when it has microsecond granularity or better (unlikely). + */ + clock_id = CLOCK_MONOTONIC; + if (type != UV_CLOCK_FAST) + goto done; + + clock_id = atomic_load_explicit(&fast_clock_id, memory_order_relaxed); + if (clock_id != -1) + goto done; + + clock_id = CLOCK_MONOTONIC; + if (0 == clock_getres(CLOCK_MONOTONIC_COARSE, &t)) + if (t.tv_nsec <= 1 * 1000 * 1000) + clock_id = CLOCK_MONOTONIC_COARSE; + + atomic_store_explicit(&fast_clock_id, clock_id, memory_order_relaxed); + +done: + + if (clock_gettime(clock_id, &t)) + return 0; /* Not really possible. */ + + return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec; +} + + +int uv_resident_set_memory(size_t* rss) { + char buf[1024]; + const char* s; + long val; + int rc; + int i; + + /* 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; + + *rss = val * getpagesize(); + return 0; + +err: + return UV_EINVAL; +} + +int uv_uptime(double* uptime) { + struct timespec now; + char buf[128]; + + /* Consult /proc/uptime when present (common case), or fall back to + * clock_gettime. Why not always clock_gettime? It doesn't always return the + * right result under OpenVZ and possibly other containerized environments. + */ + if (0 == uv__slurp("/proc/uptime", buf, sizeof(buf))) + if (1 == sscanf(buf, "%lf", uptime)) + return 0; + + if (clock_gettime(CLOCK_BOOTTIME, &now)) + return UV__ERR(errno); + + *uptime = now.tv_sec; + return 0; +} + + +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[] = "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__ + "0x811\nARM810\n" "0x920\nARM920\n" "0x922\nARM922\n" + "0x926\nARM926\n" "0x940\nARM940\n" "0x946\nARM946\n" + "0x966\nARM966\n" "0xa20\nARM1020\n" "0xa22\nARM1022\n" + "0xa26\nARM1026\n" "0xb02\nARM11 MPCore\n" "0xb36\nARM1136\n" + "0xb56\nARM1156\n" "0xb76\nARM1176\n" "0xc05\nCortex-A5\n" + "0xc07\nCortex-A7\n" "0xc08\nCortex-A8\n" "0xc09\nCortex-A9\n" + "0xc0d\nCortex-A17\n" /* Originally A12 */ + "0xc0f\nCortex-A15\n" "0xc0e\nCortex-A17\n" "0xc14\nCortex-R4\n" + "0xc15\nCortex-R5\n" "0xc17\nCortex-R7\n" "0xc18\nCortex-R8\n" + "0xc20\nCortex-M0\n" "0xc21\nCortex-M1\n" "0xc23\nCortex-M3\n" + "0xc24\nCortex-M4\n" "0xc27\nCortex-M7\n" "0xc60\nCortex-M0+\n" + "0xd01\nCortex-A32\n" "0xd03\nCortex-A53\n" "0xd04\nCortex-A35\n" + "0xd05\nCortex-A55\n" "0xd06\nCortex-A65\n" "0xd07\nCortex-A57\n" + "0xd08\nCortex-A72\n" "0xd09\nCortex-A73\n" "0xd0a\nCortex-A75\n" + "0xd0b\nCortex-A76\n" "0xd0c\nNeoverse-N1\n" "0xd0d\nCortex-A77\n" + "0xd0e\nCortex-A76AE\n" "0xd13\nCortex-R52\n" "0xd20\nCortex-M23\n" + "0xd21\nCortex-M33\n" "0xd41\nCortex-A78\n" "0xd42\nCortex-A78AE\n" + "0xd4a\nNeoverse-E1\n" "0xd4b\nCortex-A78C\n" +#endif + ""; + struct cpu { + unsigned long long freq, user, nice, sys, idle, irq; + unsigned model; + }; + FILE* fp; + char* p; + int found; + int n; + unsigned i; + unsigned cpu; + unsigned maxcpu; + unsigned size; + unsigned long long skip; + struct cpu (*cpus)[8192]; /* Kernel maximum. */ + struct cpu* c; + struct cpu t; + char (*model)[64]; + unsigned char bitmap[ARRAY_SIZE(*cpus) / 8]; + /* Assumption: even big.LITTLE systems will have only a handful + * of different CPU models. Most systems will just have one. + */ + char models[8][64]; + char buf[1024]; + + memset(bitmap, 0, sizeof(bitmap)); + memset(models, 0, sizeof(models)); + snprintf(*models, sizeof(*models), "unknown"); + maxcpu = 0; + + cpus = uv__calloc(ARRAY_SIZE(*cpus), sizeof(**cpus)); + if (cpus == NULL) + return UV_ENOMEM; + + fp = uv__open_file("/proc/stat"); + if (fp == NULL) { + uv__free(cpus); + return UV__ERR(errno); + } + + if (NULL == fgets(buf, sizeof(buf), fp)) + abort(); + + for (;;) { + memset(&t, 0, sizeof(t)); + + n = fscanf(fp, "cpu%u %llu %llu %llu %llu %llu %llu", + &cpu, &t.user, &t.nice, &t.sys, &t.idle, &skip, &t.irq); + + if (n != 7) + break; + + if (NULL == fgets(buf, sizeof(buf), fp)) + abort(); + + if (cpu >= ARRAY_SIZE(*cpus)) + continue; + + (*cpus)[cpu] = t; + + bitmap[cpu >> 3] |= 1 << (cpu & 7); + + if (cpu >= maxcpu) + maxcpu = cpu + 1; + } + + fclose(fp); + + fp = uv__open_file("/proc/cpuinfo"); + if (fp == NULL) + goto nocpuinfo; + + for (;;) { + if (1 != fscanf(fp, "processor\t: %u\n", &cpu)) + break; /* Parse error. */ + + 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; + } + } + + goto next; /* Not found. */ + +parts: + n = (int) strcspn(p, "\n"); + + /* arm64: translate CPU part code to model name. */ + if (*parts) { + p = memmem(parts, sizeof(parts) - 1, p, n + 1); + if (p == NULL) + p = "unknown"; + else + p += n + 1; + n = (int) strcspn(p, "\n"); + } + + found = 0; + for (model = models; !found && model < ARRAY_END(models); model++) + found = !strncmp(p, *model, strlen(*model)); + + if (!found) + goto next; + + if (**model == '\0') + snprintf(*model, sizeof(*model), "%.*s", n, p); + + if (cpu < maxcpu) + (*cpus)[cpu].model = model - models; + +next: + while (fgets(buf, sizeof(buf), fp)) + if (*buf == '\n') + break; + } + + fclose(fp); + fp = NULL; + +nocpuinfo: + + n = 0; + for (cpu = 0; cpu < maxcpu; cpu++) { + if (!(bitmap[cpu >> 3] & (1 << (cpu & 7)))) + continue; + + n++; + snprintf(buf, sizeof(buf), + "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", cpu); + + fp = uv__open_file(buf); + if (fp == NULL) + continue; + + if (1 != fscanf(fp, "%llu", &(*cpus)[cpu].freq)) + abort(); + fclose(fp); + fp = NULL; + } + + size = n * sizeof(**ci) + sizeof(models); + *ci = uv__malloc(size); + *count = 0; + + if (*ci == NULL) { + uv__free(cpus); + return UV_ENOMEM; + } + + *count = n; + p = memcpy(*ci + n, models, sizeof(models)); + + i = 0; + for (cpu = 0; cpu < maxcpu; cpu++) { + if (!(bitmap[cpu >> 3] & (1 << (cpu & 7)))) + continue; + + c = *cpus + cpu; + + (*ci)[i++] = (uv_cpu_info_t) { + .model = p + c->model * sizeof(*model), + .speed = c->freq / 1000, + /* Note: sysconf(_SC_CLK_TCK) is fixed at 100 Hz, + * therefore the multiplier is always 1000/100 = 10. + */ + .cpu_times = (struct uv_cpu_times_s) { + .user = 10 * c->user, + .nice = 10 * c->nice, + .sys = 10 * c->sys, + .idle = 10 * c->idle, + .irq = 10 * c->irq, + }, + }; + } + + uv__free(cpus); + + return 0; +} + + +static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { + if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING))) + return 1; + if (ent->ifa_addr == NULL) + return 1; + /* + * On Linux getifaddrs returns information related to the raw underlying + * devices. We're not interested in this information yet. + */ + if (ent->ifa_addr->sa_family == PF_PACKET) + return exclude_type; + return !exclude_type; +} + +int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { + struct ifaddrs *addrs, *ent; + uv_interface_address_t* address; + int i; + struct sockaddr_ll *sll; + + *count = 0; + *addresses = NULL; + + if (getifaddrs(&addrs)) + return UV__ERR(errno); + + /* Count the number of interfaces */ + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR)) + continue; + + (*count)++; + } + + if (*count == 0) { + freeifaddrs(addrs); + return 0; + } + + /* Make sure the memory is initiallized to zero using calloc() */ + *addresses = uv__calloc(*count, sizeof(**addresses)); + if (!(*addresses)) { + freeifaddrs(addrs); + return UV_ENOMEM; + } + + 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); + + if (ent->ifa_addr->sa_family == AF_INET6) { + address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr); + } else { + address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr); + } + + if (ent->ifa_netmask->sa_family == AF_INET6) { + address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask); + } else { + address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask); + } + + address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK); + + address++; + } + + /* Fill in physical addresses for each interface */ + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS)) + continue; + + address = *addresses; + + for (i = 0; i < (*count); i++) { + size_t namelen = strlen(ent->ifa_name); + /* Alias interface share the same physical address */ + if (strncmp(address->name, ent->ifa_name, namelen) == 0 && + (address->name[namelen] == 0 || address->name[namelen] == ':')) { + sll = (struct sockaddr_ll*)ent->ifa_addr; + memcpy(address->phys_addr, sll->sll_addr, sizeof(address->phys_addr)); + } + address++; + } + } + + freeifaddrs(addrs); + + return 0; +} + + +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); +} + + +void uv__set_process_title(const char* title) { +#if defined(PR_SET_NAME) + prctl(PR_SET_NAME, title); /* Only copies first 16 characters. */ +#endif +} + + +static uint64_t uv__read_proc_meminfo(const char* what) { + uint64_t rc; + char* p; + char buf[4096]; /* Large enough to hold all of /proc/meminfo. */ + + if (uv__slurp("/proc/meminfo", buf, sizeof(buf))) + return 0; + + p = strstr(buf, what); + + if (p == NULL) + return 0; + + p += strlen(what); + + rc = 0; + sscanf(p, "%" PRIu64 " kB", &rc); + + return rc * 1024; +} + + +uint64_t uv_get_free_memory(void) { + struct sysinfo info; + uint64_t rc; + + rc = uv__read_proc_meminfo("MemAvailable:"); + + if (rc != 0) + return rc; + + if (0 == sysinfo(&info)) + return (uint64_t) info.freeram * info.mem_unit; + + return 0; +} + + +uint64_t uv_get_total_memory(void) { + struct sysinfo info; + uint64_t rc; + + rc = uv__read_proc_meminfo("MemTotal:"); + + if (rc != 0) + return rc; + + if (0 == sysinfo(&info)) + return (uint64_t) info.totalram * info.mem_unit; + + return 0; +} + + +static uint64_t uv__read_uint64(const char* filename) { + char buf[32]; /* Large enough to hold an encoded uint64_t. */ + uint64_t rc; + + rc = 0; + if (0 == uv__slurp(filename, buf, sizeof(buf))) + if (1 != sscanf(buf, "%" PRIu64, &rc)) + if (0 == strcmp(buf, "max\n")) + rc = UINT64_MAX; + + return rc; +} + + +/* Given a buffer with the contents of a cgroup1 /proc/self/cgroups, + * finds the location and length of the memory controller mount path. + * This disregards the leading / for easy concatenation of paths. + * Returns NULL if the memory controller wasn't found. */ +static char* uv__cgroup1_find_memory_controller(char buf[static 1024], + int* n) { + char* p; + + /* Seek to the memory controller line. */ + p = strchr(buf, ':'); + while (p != NULL && strncmp(p, ":memory:", 8)) { + p = strchr(p, '\n'); + if (p != NULL) + p = strchr(p, ':'); + } + + if (p != NULL) { + /* Determine the length of the mount path. */ + p = p + strlen(":memory:/"); + *n = (int) strcspn(p, "\n"); + } + + return p; +} + +static void uv__get_cgroup1_memory_limits(char buf[static 1024], uint64_t* high, + uint64_t* max) { + char filename[4097]; + char* p; + int n; + uint64_t cgroup1_max; + + /* Find out where the controller is mounted. */ + p = uv__cgroup1_find_memory_controller(buf, &n); + if (p != NULL) { + snprintf(filename, sizeof(filename), + "/sys/fs/cgroup/memory/%.*s/memory.soft_limit_in_bytes", n, p); + *high = uv__read_uint64(filename); + + snprintf(filename, sizeof(filename), + "/sys/fs/cgroup/memory/%.*s/memory.limit_in_bytes", n, p); + *max = uv__read_uint64(filename); + + /* If the controller wasn't mounted, the reads above will have failed, + * as indicated by uv__read_uint64 returning 0. + */ + if (*high != 0 && *max != 0) + goto update_limits; + } + + /* Fall back to the limits of the global memory controller. */ + *high = uv__read_uint64("/sys/fs/cgroup/memory/memory.soft_limit_in_bytes"); + *max = uv__read_uint64("/sys/fs/cgroup/memory/memory.limit_in_bytes"); + + /* uv__read_uint64 detects cgroup2's "max", so we need to separately detect + * cgroup1's maximum value (which is derived from LONG_MAX and PAGE_SIZE). + */ +update_limits: + cgroup1_max = LONG_MAX & ~(sysconf(_SC_PAGESIZE) - 1); + if (*high == cgroup1_max) + *high = UINT64_MAX; + if (*max == cgroup1_max) + *max = UINT64_MAX; +} + +static void uv__get_cgroup2_memory_limits(char buf[static 1024], uint64_t* high, + uint64_t* max) { + char filename[4097]; + char* p; + int n; + + /* Find out where the controller is mounted. */ + p = buf + strlen("0::/"); + n = (int) strcspn(p, "\n"); + + /* Read the memory limits of the controller. */ + snprintf(filename, sizeof(filename), "/sys/fs/cgroup/%.*s/memory.max", n, p); + *max = uv__read_uint64(filename); + snprintf(filename, sizeof(filename), "/sys/fs/cgroup/%.*s/memory.high", n, p); + *high = uv__read_uint64(filename); +} + +static uint64_t uv__get_cgroup_constrained_memory(char buf[static 1024]) { + uint64_t high; + uint64_t max; + + /* In the case of cgroupv2, we'll only have a single entry. */ + if (strncmp(buf, "0::/", 4)) + uv__get_cgroup1_memory_limits(buf, &high, &max); + else + uv__get_cgroup2_memory_limits(buf, &high, &max); + + if (high == 0 || max == 0) + return 0; + + return high < max ? high : max; +} + +uint64_t uv_get_constrained_memory(void) { + char buf[1024]; + + if (uv__slurp("/proc/self/cgroup", buf, sizeof(buf))) + return 0; + + return uv__get_cgroup_constrained_memory(buf); +} + + +static uint64_t uv__get_cgroup1_current_memory(char buf[static 1024]) { + char filename[4097]; + uint64_t current; + char* p; + int n; + + /* Find out where the controller is mounted. */ + p = uv__cgroup1_find_memory_controller(buf, &n); + if (p != NULL) { + snprintf(filename, sizeof(filename), + "/sys/fs/cgroup/memory/%.*s/memory.usage_in_bytes", n, p); + current = uv__read_uint64(filename); + + /* If the controller wasn't mounted, the reads above will have failed, + * as indicated by uv__read_uint64 returning 0. + */ + if (current != 0) + return current; + } + + /* Fall back to the usage of the global memory controller. */ + return uv__read_uint64("/sys/fs/cgroup/memory/memory.usage_in_bytes"); +} + +static uint64_t uv__get_cgroup2_current_memory(char buf[static 1024]) { + char filename[4097]; + char* p; + int n; + + /* Find out where the controller is mounted. */ + p = buf + strlen("0::/"); + n = (int) strcspn(p, "\n"); + + snprintf(filename, sizeof(filename), + "/sys/fs/cgroup/%.*s/memory.current", n, p); + return uv__read_uint64(filename); +} + +uint64_t uv_get_available_memory(void) { + char buf[1024]; + uint64_t constrained; + uint64_t current; + uint64_t total; + + if (uv__slurp("/proc/self/cgroup", buf, sizeof(buf))) + return 0; + + constrained = uv__get_cgroup_constrained_memory(buf); + if (constrained == 0) + return uv_get_free_memory(); + + total = uv_get_total_memory(); + if (constrained > total) + return uv_get_free_memory(); + + /* In the case of cgroupv2, we'll only have a single entry. */ + if (strncmp(buf, "0::/", 4)) + current = uv__get_cgroup1_current_memory(buf); + else + current = uv__get_cgroup2_current_memory(buf); + + /* memory usage can be higher than the limit (for short bursts of time) */ + if (constrained < current) + return 0; + + return constrained - current; +} + + +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. */ + + if (0 == uv__slurp("/proc/loadavg", buf, sizeof(buf))) + if (3 == sscanf(buf, "%lf %lf %lf", &avg[0], &avg[1], &avg[2])) + return; + + if (sysinfo(&info) < 0) + return; + + avg[0] = (double) info.loads[0] / 65536.0; + avg[1] = (double) info.loads[1] / 65536.0; + avg[2] = (double) info.loads[2] / 65536.0; +} + + +static int compare_watchers(const struct watcher_list* a, + const struct watcher_list* b) { + if (a->wd < b->wd) return -1; + if (a->wd > b->wd) return 1; + return 0; +} + + +static int init_inotify(uv_loop_t* loop) { + int fd; + + if (loop->inotify_fd != -1) + return 0; + + fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (fd < 0) + return UV__ERR(errno); + + loop->inotify_fd = fd; + uv__io_init(&loop->inotify_read_watcher, uv__inotify_read, loop->inotify_fd); + uv__io_start(loop, &loop->inotify_read_watcher, POLLIN); + + return 0; +} + + +static int uv__inotify_fork(uv_loop_t* loop, struct watcher_list* root) { + /* Open the inotify_fd, and re-arm all the inotify watchers. */ + int err; + struct watcher_list* tmp_watcher_list_iter; + struct watcher_list* watcher_list; + struct watcher_list tmp_watcher_list; + struct uv__queue queue; + struct uv__queue* q; + uv_fs_event_t* handle; + char* tmp_path; + + if (root == NULL) + return 0; + + /* We must restore the old watcher list to be able to close items + * out of it. + */ + loop->inotify_watchers = root; + + uv__queue_init(&tmp_watcher_list.watchers); + /* Note that the queue we use is shared with the start and stop() + * functions, making uv__queue_foreach unsafe to use. So we use the + * uv__queue_move trick to safely iterate. Also don't free the watcher + * list until we're done iterating. c.f. uv__inotify_read. + */ + RB_FOREACH_SAFE(watcher_list, watcher_root, + uv__inotify_watchers(loop), tmp_watcher_list_iter) { + watcher_list->iterating = 1; + uv__queue_move(&watcher_list->watchers, &queue); + while (!uv__queue_empty(&queue)) { + q = uv__queue_head(&queue); + handle = uv__queue_data(q, uv_fs_event_t, watchers); + /* It's critical to keep a copy of path here, because it + * will be set to NULL by stop() and then deallocated by + * maybe_free_watcher_list + */ + tmp_path = uv__strdup(handle->path); + assert(tmp_path != NULL); + uv__queue_remove(q); + uv__queue_insert_tail(&watcher_list->watchers, q); + uv_fs_event_stop(handle); + + uv__queue_insert_tail(&tmp_watcher_list.watchers, &handle->watchers); + handle->path = tmp_path; + } + watcher_list->iterating = 0; + maybe_free_watcher_list(watcher_list, loop); + } + + uv__queue_move(&tmp_watcher_list.watchers, &queue); + while (!uv__queue_empty(&queue)) { + q = uv__queue_head(&queue); + uv__queue_remove(q); + handle = uv__queue_data(q, uv_fs_event_t, watchers); + tmp_path = handle->path; + handle->path = NULL; + err = uv_fs_event_start(handle, handle->cb, tmp_path, 0); + uv__free(tmp_path); + if (err) + return err; + } + + return 0; +} + + +static struct watcher_list* find_watcher(uv_loop_t* loop, int wd) { + struct watcher_list w; + w.wd = wd; + return RB_FIND(watcher_root, uv__inotify_watchers(loop), &w); +} + + +static void maybe_free_watcher_list(struct watcher_list* w, uv_loop_t* loop) { + /* if the watcher_list->watchers is being iterated over, we can't free it. */ + if ((!w->iterating) && uv__queue_empty(&w->watchers)) { + /* No watchers left for this path. Clean up. */ + RB_REMOVE(watcher_root, uv__inotify_watchers(loop), w); + inotify_rm_watch(loop->inotify_fd, w->wd); + uv__free(w); + } +} + + +static void uv__inotify_read(uv_loop_t* loop, + uv__io_t* dummy, + unsigned int events) { + const struct inotify_event* e; + struct watcher_list* w; + uv_fs_event_t* h; + struct uv__queue queue; + struct uv__queue* q; + const char* path; + ssize_t size; + const char *p; + /* needs to be large enough for sizeof(inotify_event) + strlen(path) */ + char buf[4096]; + + for (;;) { + do + size = read(loop->inotify_fd, buf, sizeof(buf)); + while (size == -1 && errno == EINTR); + + if (size == -1) { + assert(errno == EAGAIN || errno == EWOULDBLOCK); + break; + } + + assert(size > 0); /* pre-2.6.21 thing, size=0 == read buffer too small */ + + /* Now we have one or more inotify_event structs. */ + for (p = buf; p < buf + size; p += sizeof(*e) + e->len) { + e = (const struct inotify_event*) p; + + events = 0; + if (e->mask & (IN_ATTRIB|IN_MODIFY)) + events |= UV_CHANGE; + if (e->mask & ~(IN_ATTRIB|IN_MODIFY)) + events |= UV_RENAME; + + w = find_watcher(loop, e->wd); + if (w == NULL) + continue; /* Stale event, no watchers left. */ + + /* inotify does not return the filename when monitoring a single file + * for modifications. Repurpose the filename for API compatibility. + * I'm not convinced this is a good thing, maybe it should go. + */ + path = e->len ? (const char*) (e + 1) : uv__basename_r(w->path); + + /* We're about to iterate over the queue and call user's callbacks. + * What can go wrong? + * A callback could call uv_fs_event_stop() + * and the queue can change under our feet. + * So, we use uv__queue_move() trick to safely iterate over the queue. + * And we don't free the watcher_list until we're done iterating. + * + * First, + * tell uv_fs_event_stop() (that could be called from a user's callback) + * not to free watcher_list. + */ + w->iterating = 1; + uv__queue_move(&w->watchers, &queue); + while (!uv__queue_empty(&queue)) { + q = uv__queue_head(&queue); + h = uv__queue_data(q, uv_fs_event_t, watchers); + + uv__queue_remove(q); + uv__queue_insert_tail(&w->watchers, q); + + h->cb(h, path, events, 0); + } + /* done iterating, time to (maybe) free empty watcher_list */ + w->iterating = 0; + maybe_free_watcher_list(w, loop); + } + } +} + + +int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { + uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); + return 0; +} + + +int uv_fs_event_start(uv_fs_event_t* handle, + uv_fs_event_cb cb, + const char* path, + unsigned int flags) { + struct watcher_list* w; + uv_loop_t* loop; + size_t len; + int events; + int err; + int wd; + + if (uv__is_active(handle)) + return UV_EINVAL; + + loop = handle->loop; + + err = init_inotify(loop); + if (err) + return err; + + events = IN_ATTRIB + | IN_CREATE + | IN_MODIFY + | IN_DELETE + | IN_DELETE_SELF + | IN_MOVE_SELF + | IN_MOVED_FROM + | IN_MOVED_TO; + + wd = inotify_add_watch(loop->inotify_fd, path, events); + if (wd == -1) + return UV__ERR(errno); + + w = find_watcher(loop, wd); + if (w) + goto no_insert; + + len = strlen(path) + 1; + w = uv__malloc(sizeof(*w) + len); + if (w == NULL) + return UV_ENOMEM; + + w->wd = wd; + w->path = memcpy(w + 1, path, len); + uv__queue_init(&w->watchers); + w->iterating = 0; + RB_INSERT(watcher_root, uv__inotify_watchers(loop), w); + +no_insert: + uv__handle_start(handle); + uv__queue_insert_tail(&w->watchers, &handle->watchers); + handle->path = w->path; + handle->cb = cb; + handle->wd = wd; + + return 0; +} + + +int uv_fs_event_stop(uv_fs_event_t* handle) { + struct watcher_list* w; + + if (!uv__is_active(handle)) + return 0; + + w = find_watcher(handle->loop, handle->wd); + assert(w != NULL); + + handle->wd = -1; + handle->path = NULL; + uv__handle_stop(handle); + uv__queue_remove(&handle->watchers); + + maybe_free_watcher_list(w, handle->loop); + + return 0; +} + + +void uv__fs_event_close(uv_fs_event_t* handle) { + uv_fs_event_stop(handle); +} diff --git a/src/unix/loop-watcher.c b/src/unix/loop-watcher.c index b8c1c2a7102..2db8b515df7 100644 --- a/src/unix/loop-watcher.c +++ b/src/unix/loop-watcher.c @@ -32,7 +32,7 @@ int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) { \ if (uv__is_active(handle)) return 0; \ if (cb == NULL) return UV_EINVAL; \ - QUEUE_INSERT_HEAD(&handle->loop->name##_handles, &handle->queue); \ + uv__queue_insert_head(&handle->loop->name##_handles, &handle->queue); \ handle->name##_cb = cb; \ uv__handle_start(handle); \ return 0; \ @@ -40,21 +40,21 @@ \ int uv_##name##_stop(uv_##name##_t* handle) { \ if (!uv__is_active(handle)) return 0; \ - QUEUE_REMOVE(&handle->queue); \ + uv__queue_remove(&handle->queue); \ uv__handle_stop(handle); \ return 0; \ } \ \ void uv__run_##name(uv_loop_t* loop) { \ uv_##name##_t* h; \ - QUEUE queue; \ - QUEUE* q; \ - QUEUE_MOVE(&loop->name##_handles, &queue); \ - while (!QUEUE_EMPTY(&queue)) { \ - q = QUEUE_HEAD(&queue); \ - h = QUEUE_DATA(q, uv_##name##_t, queue); \ - QUEUE_REMOVE(q); \ - QUEUE_INSERT_TAIL(&loop->name##_handles, q); \ + struct uv__queue queue; \ + struct uv__queue* q; \ + uv__queue_move(&loop->name##_handles, &queue); \ + while (!uv__queue_empty(&queue)) { \ + q = uv__queue_head(&queue); \ + h = uv__queue_data(q, uv_##name##_t, queue); \ + uv__queue_remove(q); \ + uv__queue_insert_tail(&loop->name##_handles, q); \ h->name##_cb(h); \ } \ } \ diff --git a/src/unix/loop.c b/src/unix/loop.c index a88e71c3393..179ee999d80 100644 --- a/src/unix/loop.c +++ b/src/unix/loop.c @@ -45,22 +45,25 @@ int uv_loop_init(uv_loop_t* loop) { err = uv_mutex_init(&lfields->loop_metrics.lock); if (err) goto fail_metrics_mutex_init; + memset(&lfields->loop_metrics.metrics, + 0, + sizeof(lfields->loop_metrics.metrics)); heap_init((struct heap*) &loop->timer_heap); - QUEUE_INIT(&loop->wq); - QUEUE_INIT(&loop->idle_handles); - QUEUE_INIT(&loop->async_handles); - QUEUE_INIT(&loop->check_handles); - QUEUE_INIT(&loop->prepare_handles); - QUEUE_INIT(&loop->handle_queue); + uv__queue_init(&loop->wq); + uv__queue_init(&loop->idle_handles); + uv__queue_init(&loop->async_handles); + uv__queue_init(&loop->check_handles); + uv__queue_init(&loop->prepare_handles); + uv__queue_init(&loop->handle_queue); loop->active_handles = 0; loop->active_reqs.count = 0; loop->nfds = 0; loop->watchers = NULL; loop->nwatchers = 0; - QUEUE_INIT(&loop->pending_queue); - QUEUE_INIT(&loop->watcher_queue); + uv__queue_init(&loop->pending_queue); + uv__queue_init(&loop->watcher_queue); loop->closing_handles = NULL; uv__update_time(loop); @@ -79,13 +82,10 @@ int uv_loop_init(uv_loop_t* loop) { goto fail_platform_init; uv__signal_global_once_init(); - err = uv_signal_init(loop, &loop->child_watcher); + err = uv__process_init(loop); if (err) goto fail_signal_init; - - uv__handle_unref(&loop->child_watcher); - loop->child_watcher.flags |= UV_HANDLE_INTERNAL; - QUEUE_INIT(&loop->process_handles); + uv__queue_init(&loop->process_handles); err = uv_rwlock_init(&loop->cloexec_lock); if (err) @@ -152,9 +152,9 @@ int uv_loop_fork(uv_loop_t* loop) { if (w == NULL) continue; - if (w->pevents != 0 && QUEUE_EMPTY(&w->watcher_queue)) { + if (w->pevents != 0 && uv__queue_empty(&w->watcher_queue)) { w->events = 0; /* Force re-registration in uv__io_poll. */ - QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); + uv__queue_insert_tail(&loop->watcher_queue, &w->watcher_queue); } } @@ -180,7 +180,7 @@ void uv__loop_close(uv_loop_t* loop) { } uv_mutex_lock(&loop->wq_mutex); - assert(QUEUE_EMPTY(&loop->wq) && "thread pool work queue not empty!"); + assert(uv__queue_empty(&loop->wq) && "thread pool work queue not empty!"); assert(!uv__has_active_reqs(loop)); uv_mutex_unlock(&loop->wq_mutex); uv_mutex_destroy(&loop->wq_mutex); @@ -192,8 +192,8 @@ void uv__loop_close(uv_loop_t* loop) { uv_rwlock_destroy(&loop->cloexec_lock); #if 0 - assert(QUEUE_EMPTY(&loop->pending_queue)); - assert(QUEUE_EMPTY(&loop->watcher_queue)); + assert(uv__queue_empty(&loop->pending_queue)); + assert(uv__queue_empty(&loop->watcher_queue)); assert(loop->nfds == 0); #endif @@ -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; diff --git a/src/unix/netbsd.c b/src/unix/netbsd.c index c66333f522c..fa21e98e41a 100644 --- a/src/unix/netbsd.c +++ b/src/unix/netbsd.c @@ -103,7 +103,7 @@ uint64_t uv_get_free_memory(void) { int which[] = {CTL_VM, VM_UVMEXP}; if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) - return UV__ERR(errno); + return 0; return (uint64_t) info.free * sysconf(_SC_PAGESIZE); } @@ -120,7 +120,7 @@ uint64_t uv_get_total_memory(void) { size_t size = sizeof(info); if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) - return UV__ERR(errno); + return 0; return (uint64_t) info; } @@ -131,6 +131,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + int uv_resident_set_memory(size_t* rss) { kvm_t *kd = NULL; struct kinfo_proc2 *kinfo = NULL; diff --git a/src/unix/openbsd.c b/src/unix/openbsd.c index f32a94df387..9c863b6c90d 100644 --- a/src/unix/openbsd.c +++ b/src/unix/openbsd.c @@ -116,7 +116,7 @@ uint64_t uv_get_free_memory(void) { int which[] = {CTL_VM, VM_UVMEXP}; if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) - return UV__ERR(errno); + return 0; return (uint64_t) info.free * sysconf(_SC_PAGESIZE); } @@ -128,7 +128,7 @@ uint64_t uv_get_total_memory(void) { size_t size = sizeof(info); if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) - return UV__ERR(errno); + return 0; return (uint64_t) info; } @@ -139,6 +139,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + int uv_resident_set_memory(size_t* rss) { struct kinfo_proc kinfo; size_t page_size = getpagesize(); diff --git a/src/unix/os390-syscalls.c b/src/unix/os390-syscalls.c index a74112701a4..7f90c270906 100644 --- a/src/unix/os390-syscalls.c +++ b/src/unix/os390-syscalls.c @@ -27,7 +27,7 @@ #include #include -static QUEUE global_epoll_queue; +static struct uv__queue global_epoll_queue; static uv_mutex_t global_epoll_lock; static uv_once_t once = UV_ONCE_INIT; @@ -178,18 +178,18 @@ static void after_fork(void) { static void child_fork(void) { - QUEUE* q; + struct uv__queue* q; uv_once_t child_once = UV_ONCE_INIT; /* reset once */ memcpy(&once, &child_once, sizeof(child_once)); /* reset epoll list */ - while (!QUEUE_EMPTY(&global_epoll_queue)) { + while (!uv__queue_empty(&global_epoll_queue)) { uv__os390_epoll* lst; - q = QUEUE_HEAD(&global_epoll_queue); - QUEUE_REMOVE(q); - lst = QUEUE_DATA(q, uv__os390_epoll, member); + q = uv__queue_head(&global_epoll_queue); + uv__queue_remove(q); + lst = uv__queue_data(q, uv__os390_epoll, member); uv__free(lst->items); lst->items = NULL; lst->size = 0; @@ -201,7 +201,7 @@ static void child_fork(void) { static void epoll_init(void) { - QUEUE_INIT(&global_epoll_queue); + uv__queue_init(&global_epoll_queue); if (uv_mutex_init(&global_epoll_lock)) abort(); @@ -225,7 +225,7 @@ uv__os390_epoll* epoll_create1(int flags) { lst->items[lst->size - 1].revents = 0; uv_once(&once, epoll_init); uv_mutex_lock(&global_epoll_lock); - QUEUE_INSERT_TAIL(&global_epoll_queue, &lst->member); + uv__queue_insert_tail(&global_epoll_queue, &lst->member); uv_mutex_unlock(&global_epoll_lock); } @@ -284,6 +284,8 @@ int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events, nmsgsfds_t size; struct pollfd* pfds; int pollret; + int pollfdret; + int pollmsgret; int reventcount; int nevents; struct pollfd msg_fd; @@ -304,24 +306,24 @@ int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events, return -1; } - if (lst->size > 0) - _SET_FDS_MSGS(size, 1, lst->size - 1); - else - _SET_FDS_MSGS(size, 0, 0); + assert(lst->size > 0); + _SET_FDS_MSGS(size, 1, lst->size - 1); pfds = lst->items; pollret = poll(pfds, size, timeout); if (pollret <= 0) return pollret; - assert(lst->size > 0); - - pollret = _NFDS(pollret) + _NMSGS(pollret); + pollfdret = _NFDS(pollret); + pollmsgret = _NMSGS(pollret); reventcount = 0; nevents = 0; - msg_fd = pfds[lst->size - 1]; + msg_fd = pfds[lst->size - 1]; /* message queue is always last entry */ + maxevents = maxevents - pollmsgret; /* allow spot for message queue */ for (i = 0; - i < lst->size && i < maxevents && reventcount < pollret; ++i) { + i < lst->size - 1 && + nevents < maxevents && + reventcount < pollfdret; ++i) { struct epoll_event ev; struct pollfd* pfd; @@ -332,32 +334,32 @@ int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events, ev.fd = pfd->fd; ev.events = pfd->revents; ev.is_msg = 0; - if (pfd->revents & POLLIN && pfd->revents & POLLOUT) - reventcount += 2; - else if (pfd->revents & (POLLIN | POLLOUT)) - ++reventcount; - pfd->revents = 0; + reventcount++; events[nevents++] = ev; } - if (msg_fd.revents != 0 && msg_fd.fd != -1) - if (i == lst->size) - events[nevents - 1].is_msg = 1; + if (pollmsgret > 0 && msg_fd.revents != 0 && msg_fd.fd != -1) { + struct epoll_event ev; + ev.fd = msg_fd.fd; + ev.events = msg_fd.revents; + ev.is_msg = 1; + events[nevents++] = ev; + } return nevents; } int epoll_file_close(int fd) { - QUEUE* q; + struct uv__queue* q; uv_once(&once, epoll_init); uv_mutex_lock(&global_epoll_lock); - QUEUE_FOREACH(q, &global_epoll_queue) { + uv__queue_foreach(q, &global_epoll_queue) { uv__os390_epoll* lst; - lst = QUEUE_DATA(q, uv__os390_epoll, member); + lst = uv__queue_data(q, uv__os390_epoll, member); if (fd < lst->size && lst->items != NULL && lst->items[fd].fd != -1) lst->items[fd].fd = -1; } @@ -369,7 +371,7 @@ int epoll_file_close(int fd) { void epoll_queue_close(uv__os390_epoll* lst) { /* Remove epoll instance from global queue */ uv_mutex_lock(&global_epoll_lock); - QUEUE_REMOVE(&lst->member); + uv__queue_remove(&lst->member); uv_mutex_unlock(&global_epoll_lock); /* Free resources */ diff --git a/src/unix/os390-syscalls.h b/src/unix/os390-syscalls.h index 9f504171d85..d5f3bcf8b1c 100644 --- a/src/unix/os390-syscalls.h +++ b/src/unix/os390-syscalls.h @@ -45,7 +45,7 @@ struct epoll_event { }; typedef struct { - QUEUE member; + struct uv__queue member; struct pollfd* items; unsigned long size; int msg_queue; diff --git a/src/unix/os390.c b/src/unix/os390.c index bf0448b5190..1b277292aeb 100644 --- a/src/unix/os390.c +++ b/src/unix/os390.c @@ -19,6 +19,7 @@ * IN THE SOFTWARE. */ +#include "uv.h" #include "internal.h" #include #include @@ -30,6 +31,7 @@ #include #include #include "zos-base.h" +#include "zos-sys-info.h" #if defined(__clang__) #include "csrsic.h" #else @@ -66,9 +68,6 @@ /* Total number of frames currently on all available frame queues. */ #define RCEAFC_OFFSET 0x088 -/* CPC model length from the CSRSI Service. */ -#define CPCMODEL_LENGTH 16 - /* Pointer to the home (current) ASCB. */ #define PSAAOLD 0x224 @@ -198,6 +197,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + int uv_resident_set_memory(size_t* rss) { char* ascb; char* rax; @@ -253,9 +257,12 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { idx = 0; while (idx < *count) { cpu_info->speed = *(int*)(info.siv1v2si22v1.si22v1cpucapability); - cpu_info->model = uv__malloc(CPCMODEL_LENGTH + 1); - memset(cpu_info->model, '\0', CPCMODEL_LENGTH + 1); - memcpy(cpu_info->model, info.siv1v2si11v1.si11v1cpcmodel, CPCMODEL_LENGTH); + cpu_info->model = uv__malloc(ZOSCPU_MODEL_LENGTH + 1); + if (cpu_info->model == NULL) { + uv_free_cpu_info(*cpu_infos, idx); + return UV_ENOMEM; + } + __get_cpu_model(cpu_info->model, ZOSCPU_MODEL_LENGTH + 1); cpu_info->cpu_times.user = cpu_usage_avg; /* TODO: implement the following */ cpu_info->cpu_times.sys = 0; @@ -278,7 +285,9 @@ static int uv__interface_addresses_v6(uv_interface_address_t** addresses, __net_ifconf6header_t ifc; __net_ifconf6entry_t* ifr; __net_ifconf6entry_t* p; - __net_ifconf6entry_t flg; + unsigned int i; + int count_names; + unsigned char netmask[16] = {0}; *count = 0; /* Assume maximum buffer size allowable */ @@ -287,24 +296,33 @@ static int uv__interface_addresses_v6(uv_interface_address_t** addresses, if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) return UV__ERR(errno); + ifc.__nif6h_buffer = uv__calloc(1, maxsize); + + if (ifc.__nif6h_buffer == NULL) { + uv__close(sockfd); + return UV_ENOMEM; + } + ifc.__nif6h_version = 1; ifc.__nif6h_buflen = maxsize; - ifc.__nif6h_buffer = uv__calloc(1, maxsize);; if (ioctl(sockfd, SIOCGIFCONF6, &ifc) == -1) { + /* This will error on a system that does not support IPv6. However, we want + * to treat this as there being 0 interfaces so we can continue to get IPv4 + * interfaces in uv_interface_addresses(). So return 0 instead of the error. + */ + uv__free(ifc.__nif6h_buffer); uv__close(sockfd); - return UV__ERR(errno); + errno = 0; + return 0; } - - *count = 0; ifr = (__net_ifconf6entry_t*)(ifc.__nif6h_buffer); while ((char*)ifr < (char*)ifc.__nif6h_buffer + ifc.__nif6h_buflen) { p = ifr; ifr = (__net_ifconf6entry_t*)((char*)ifr + ifc.__nif6h_entrylen); - if (!(p->__nif6e_addr.sin6_family == AF_INET6 || - p->__nif6e_addr.sin6_family == AF_INET)) + if (!(p->__nif6e_addr.sin6_family == AF_INET6)) continue; if (!(p->__nif6e_flags & _NIF6E_FLAGS_ON_LINK_ACTIVE)) @@ -313,21 +331,28 @@ static int uv__interface_addresses_v6(uv_interface_address_t** addresses, ++(*count); } + if ((*count) == 0) { + uv__free(ifc.__nif6h_buffer); + uv__close(sockfd); + return 0; + } + /* Alloc the return interface structs */ - *addresses = uv__malloc(*count * sizeof(uv_interface_address_t)); + *addresses = uv__calloc(1, *count * sizeof(uv_interface_address_t)); if (!(*addresses)) { + uv__free(ifc.__nif6h_buffer); uv__close(sockfd); return UV_ENOMEM; } address = *addresses; + count_names = 0; ifr = (__net_ifconf6entry_t*)(ifc.__nif6h_buffer); while ((char*)ifr < (char*)ifc.__nif6h_buffer + ifc.__nif6h_buflen) { p = ifr; ifr = (__net_ifconf6entry_t*)((char*)ifr + ifc.__nif6h_entrylen); - if (!(p->__nif6e_addr.sin6_family == AF_INET6 || - p->__nif6e_addr.sin6_family == AF_INET)) + if (!(p->__nif6e_addr.sin6_family == AF_INET6)) continue; if (!(p->__nif6e_flags & _NIF6E_FLAGS_ON_LINK_ACTIVE)) @@ -335,20 +360,41 @@ static int uv__interface_addresses_v6(uv_interface_address_t** addresses, /* All conditions above must match count loop */ - address->name = uv__strdup(p->__nif6e_name); + i = 0; + /* Ignore EBCDIC space (0x40) padding in name */ + while (i < ARRAY_SIZE(p->__nif6e_name) && + p->__nif6e_name[i] != 0x40 && + p->__nif6e_name[i] != 0) + ++i; + address->name = uv__malloc(i + 1); + if (address->name == NULL) { + uv_free_interface_addresses(*addresses, count_names); + uv__free(ifc.__nif6h_buffer); + uv__close(sockfd); + return UV_ENOMEM; + } + memcpy(address->name, p->__nif6e_name, i); + address->name[i] = '\0'; + __e2a_s(address->name); + count_names++; + + address->address.address6 = *((struct sockaddr_in6*) &p->__nif6e_addr); - if (p->__nif6e_addr.sin6_family == AF_INET6) - address->address.address6 = *((struct sockaddr_in6*) &p->__nif6e_addr); - else - address->address.address4 = *((struct sockaddr_in*) &p->__nif6e_addr); + for (i = 0; i < (p->__nif6e_prefixlen / 8); i++) + netmask[i] = 0xFF; - /* TODO: Retrieve netmask using SIOCGIFNETMASK ioctl */ + if (p->__nif6e_prefixlen % 8) + netmask[i] = 0xFF << (8 - (p->__nif6e_prefixlen % 8)); - address->is_internal = flg.__nif6e_flags & _NIF6E_FLAGS_LOOPBACK ? 1 : 0; - memset(address->phys_addr, 0, sizeof(address->phys_addr)); + address->netmask.netmask6.sin6_len = p->__nif6e_prefixlen; + memcpy(&(address->netmask.netmask6.sin6_addr), netmask, 16); + address->netmask.netmask6.sin6_family = AF_INET6; + + address->is_internal = p->__nif6e_flags & _NIF6E_FLAGS_LOOPBACK ? 1 : 0; address++; } + uv__free(ifc.__nif6h_buffer); uv__close(sockfd); return 0; } @@ -362,14 +408,18 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { struct ifreq flg; struct ifreq* ifr; struct ifreq* p; + uv_interface_address_t* addresses_v6; int count_v6; + unsigned int i; + int rc; + int count_names; *count = 0; *addresses = NULL; /* get the ipv6 addresses first */ - uv_interface_address_t* addresses_v6; - uv__interface_addresses_v6(&addresses_v6, &count_v6); + if ((rc = uv__interface_addresses_v6(&addresses_v6, &count_v6)) != 0) + return rc; /* now get the ipv4 addresses */ @@ -377,12 +427,27 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { maxsize = 16384; sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - if (0 > sockfd) + if (0 > sockfd) { + if (count_v6) + uv_free_interface_addresses(addresses_v6, count_v6); return UV__ERR(errno); + } ifc.ifc_req = uv__calloc(1, maxsize); + + if (ifc.ifc_req == NULL) { + if (count_v6) + uv_free_interface_addresses(addresses_v6, count_v6); + uv__close(sockfd); + return UV_ENOMEM; + } + ifc.ifc_len = maxsize; + if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) { + if (count_v6) + uv_free_interface_addresses(addresses_v6, count_v6); + uv__free(ifc.ifc_req); uv__close(sockfd); return UV__ERR(errno); } @@ -403,6 +468,9 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { + if (count_v6) + uv_free_interface_addresses(addresses_v6, count_v6); + uv__free(ifc.ifc_req); uv__close(sockfd); return UV__ERR(errno); } @@ -413,27 +481,35 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { (*count)++; } - if (*count == 0) { + if (*count == 0 && count_v6 == 0) { + uv__free(ifc.ifc_req); uv__close(sockfd); return 0; } /* Alloc the return interface structs */ - *addresses = uv__malloc((*count + count_v6) * + *addresses = uv__calloc(1, (*count + count_v6) * sizeof(uv_interface_address_t)); if (!(*addresses)) { + if (count_v6) + uv_free_interface_addresses(addresses_v6, count_v6); + uv__free(ifc.ifc_req); uv__close(sockfd); return UV_ENOMEM; } address = *addresses; - /* copy over the ipv6 addresses */ - memcpy(address, addresses_v6, count_v6 * sizeof(uv_interface_address_t)); - address += count_v6; - *count += count_v6; - uv__free(addresses_v6); + /* copy over the ipv6 addresses if any are found */ + if (count_v6) { + memcpy(address, addresses_v6, count_v6 * sizeof(uv_interface_address_t)); + address += count_v6; + *count += count_v6; + /* free ipv6 addresses, but keep address names */ + uv__free(addresses_v6); + } + count_names = *count; ifr = ifc.ifc_req; while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { p = ifr; @@ -446,6 +522,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { + uv_free_interface_addresses(*addresses, count_names); + uv__free(ifc.ifc_req); uv__close(sockfd); return UV_ENOSYS; } @@ -455,22 +533,43 @@ 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); + i = 0; + /* Ignore EBCDIC space (0x40) padding in name */ + while (i < ARRAY_SIZE(p->ifr_name) && + p->ifr_name[i] != 0x40 && + p->ifr_name[i] != 0) + ++i; + address->name = uv__malloc(i + 1); + if (address->name == NULL) { + uv_free_interface_addresses(*addresses, count_names); + uv__free(ifc.ifc_req); + uv__close(sockfd); + return UV_ENOMEM; + } + memcpy(address->name, p->ifr_name, i); + address->name[i] = '\0'; + __e2a_s(address->name); + count_names++; - if (p->ifr_addr.sa_family == AF_INET6) { - address->address.address6 = *((struct sockaddr_in6*) &p->ifr_addr); - } else { - address->address.address4 = *((struct sockaddr_in*) &p->ifr_addr); + address->address.address4 = *((struct sockaddr_in*) &p->ifr_addr); + + if (ioctl(sockfd, SIOCGIFNETMASK, p) == -1) { + uv_free_interface_addresses(*addresses, count_names); + uv__free(ifc.ifc_req); + uv__close(sockfd); + return UV__ERR(errno); } + address->netmask.netmask4 = *((struct sockaddr_in*) &p->ifr_addr); + address->netmask.netmask4.sin_family = AF_INET; address->is_internal = flg.ifr_flags & IFF_LOOPBACK ? 1 : 0; - memset(address->phys_addr, 0, sizeof(address->phys_addr)); address++; } #undef ADDR_SIZE #undef MAX + uv__free(ifc.ifc_req); uv__close(sockfd); return 0; } @@ -529,27 +628,17 @@ int uv__io_check_fd(uv_loop_t* loop, int fd) { } -void uv__fs_event_close(uv_fs_event_t* handle) { - uv_fs_event_stop(handle); -} - - int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); return 0; } -int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, - const char* filename, unsigned int flags) { +static int os390_regfileint(uv_fs_event_t* handle, char* path) { uv__os390_epoll* ep; _RFIS reg_struct; - char* path; int rc; - if (uv__is_active(handle)) - return UV_EINVAL; - ep = handle->loop->ep; assert(ep->msg_queue != -1); @@ -558,25 +647,44 @@ int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, reg_struct.__rfis_type = 1; memcpy(reg_struct.__rfis_utok, &handle, sizeof(handle)); + rc = __w_pioctl(path, _IOCC_REGFILEINT, sizeof(reg_struct), ®_struct); + if (rc != 0) + return UV__ERR(errno); + + memcpy(handle->rfis_rftok, reg_struct.__rfis_rftok, + sizeof(handle->rfis_rftok)); + + return 0; +} + + +int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, + const char* filename, unsigned int flags) { + char* path; + int rc; + + if (uv__is_active(handle)) + return UV_EINVAL; + path = uv__strdup(filename); if (path == NULL) return UV_ENOMEM; - rc = __w_pioctl(path, _IOCC_REGFILEINT, sizeof(reg_struct), ®_struct); - if (rc != 0) - return UV__ERR(errno); + rc = os390_regfileint(handle, path); + if (rc != 0) { + uv__free(path); + return rc; + } uv__handle_start(handle); handle->path = path; handle->cb = cb; - memcpy(handle->rfis_rftok, reg_struct.__rfis_rftok, - sizeof(handle->rfis_rftok)); return 0; } -int uv_fs_event_stop(uv_fs_event_t* handle) { +int uv__fs_event_stop(uv_fs_event_t* handle) { uv__os390_epoll* ep; _RFIS reg_struct; int rc; @@ -602,12 +710,40 @@ int uv_fs_event_stop(uv_fs_event_t* handle) { if (rc != 0 && errno != EALREADY && errno != ENOENT) abort(); + if (handle->path != NULL) { + uv__free(handle->path); + handle->path = NULL; + } + + if (rc != 0 && errno == EALREADY) + return -1; + uv__handle_stop(handle); return 0; } +int uv_fs_event_stop(uv_fs_event_t* handle) { + uv__fs_event_stop(handle); + return 0; +} + + +void uv__fs_event_close(uv_fs_event_t* handle) { + /* + * If we were unable to unregister file interest here, then it is most likely + * that there is a pending queued change notification. When this happens, we + * don't want to complete the close as it will free the underlying memory for + * the handle, causing a use-after-free problem when the event is processed. + * We defer the final cleanup until after the event is consumed in + * os390_message_queue_handler(). + */ + if (uv__fs_event_stop(handle) == 0) + uv__make_close_pending((uv_handle_t*) handle); +} + + static int os390_message_queue_handler(uv__os390_epoll* ep) { uv_fs_event_t* handle; int msglen; @@ -628,7 +764,15 @@ static int os390_message_queue_handler(uv__os390_epoll* ep) { events = 0; if (msg.__rfim_event == _RFIM_ATTR || msg.__rfim_event == _RFIM_WRITE) events = UV_CHANGE; - else if (msg.__rfim_event == _RFIM_RENAME) + else if (msg.__rfim_event == _RFIM_RENAME || msg.__rfim_event == _RFIM_UNLINK) + events = UV_RENAME; + else if (msg.__rfim_event == 156) + /* TODO(gabylb): zos - this event should not happen, need to investigate. + * + * This event seems to occur when the watched file is [re]moved, or an + * editor (like vim) renames then creates the file on save (for vim, that's + * when backupcopy=no|auto). + */ events = UV_RENAME; else /* Some event that we are not interested in. */ @@ -639,6 +783,26 @@ static int os390_message_queue_handler(uv__os390_epoll* ep) { */ __a2e_l(msg.__rfim_utok, sizeof(msg.__rfim_utok)); handle = *(uv_fs_event_t**)(msg.__rfim_utok); + assert(handle != NULL); + + assert((handle->flags & UV_HANDLE_CLOSED) == 0); + if (uv__is_closing(handle)) { + uv__handle_stop(handle); + uv__make_close_pending((uv_handle_t*) handle); + return 0; + } else if (handle->path == NULL) { + /* _RFIS_UNREG returned EALREADY. */ + uv__handle_stop(handle); + return 0; + } + + /* The file is implicitly unregistered when the change notification is + * sent, only one notification is sent per registration. So we need to + * re-register interest in a file after each change notification we + * receive. + */ + assert(handle->path != NULL); + os390_regfileint(handle, handle->path); handle->cb(handle, uv__basename_r(handle->path), events, 0); return 1; } @@ -646,12 +810,14 @@ static int os390_message_queue_handler(uv__os390_epoll* ep) { void uv__io_poll(uv_loop_t* loop, int timeout) { static const int max_safe_timeout = 1789569; + uv__loop_internal_fields_t* lfields; struct epoll_event events[1024]; struct epoll_event* pe; struct epoll_event e; uv__os390_epoll* ep; + int have_signals; int real_timeout; - QUEUE* q; + struct uv__queue* q; uv__io_t* w; uint64_t base; int count; @@ -663,17 +829,19 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int reset_timeout; if (loop->nfds == 0) { - assert(QUEUE_EMPTY(&loop->watcher_queue)); + assert(uv__queue_empty(&loop->watcher_queue)); return; } - while (!QUEUE_EMPTY(&loop->watcher_queue)) { + lfields = uv__get_internal_fields(loop); + + while (!uv__queue_empty(&loop->watcher_queue)) { uv_stream_t* stream; - q = QUEUE_HEAD(&loop->watcher_queue); - QUEUE_REMOVE(q); - QUEUE_INIT(q); - w = QUEUE_DATA(q, uv__io_t, watcher_queue); + q = uv__queue_head(&loop->watcher_queue); + uv__queue_remove(q); + uv__queue_init(q); + w = uv__queue_data(q, uv__io_t, watcher_queue); assert(w->pevents != 0); assert(w->fd >= 0); @@ -712,8 +880,9 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { count = 48; /* Benchmarks suggest this gives the best throughput. */ real_timeout = timeout; int nevents = 0; + have_signals = 0; - if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + if (lfields->flags & UV_METRICS_IDLE_TIME) { reset_timeout = 1; user_timeout = timeout; timeout = 0; @@ -732,6 +901,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout) timeout = max_safe_timeout; + /* 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; + nfds = epoll_wait(loop->ep, events, ARRAY_SIZE(events), timeout); @@ -796,6 +971,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { ep = loop->ep; if (pe->is_msg) { os390_message_queue_handler(ep); + nevents++; continue; } @@ -825,19 +1001,37 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { pe->events |= w->pevents & (POLLIN | POLLOUT); if (pe->events != 0) { - uv__metrics_update_idle_time(loop); - w->cb(loop, w, pe->events); + /* Run signal watchers last. This also affects child process watchers + * because those are implemented in terms of signal watchers. + */ + if (w == &loop->signal_io_watcher) { + have_signals = 1; + } else { + uv__metrics_update_idle_time(loop); + w->cb(loop, w, pe->events); + } nevents++; } } - loop->watchers[loop->nwatchers] = NULL; - loop->watchers[loop->nwatchers + 1] = NULL; + uv__metrics_inc_events(loop, nevents); if (reset_timeout != 0) { timeout = user_timeout; reset_timeout = 0; + uv__metrics_inc_events_waiting(loop, nevents); + } + + if (have_signals != 0) { + uv__metrics_update_idle_time(loop); + loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); } + loop->watchers[loop->nwatchers] = NULL; + loop->watchers[loop->nwatchers + 1] = NULL; + + if (have_signals != 0) + return; /* Event loop should cycle now so don't poll again. */ + if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { /* Poll for more events but don't block this time. */ @@ -872,6 +1066,5 @@ int uv__io_fork(uv_loop_t* loop) { */ loop->ep = NULL; - uv__platform_loop_delete(loop); return uv__platform_loop_init(loop); } diff --git a/src/unix/pipe.c b/src/unix/pipe.c index 788e038e8aa..bd57b17fb03 100644 --- a/src/unix/pipe.c +++ b/src/unix/pipe.c @@ -30,6 +30,19 @@ #include +/* Does the file path contain embedded nul bytes? */ +static int includes_nul(const char *s, size_t n) { + if (n == 0) + return 0; +#ifdef __linux__ + /* Accept abstract socket namespace path ("\0/virtual/path"). */ + s++; + n--; +#endif + return NULL != memchr(s, '\0', n); +} + + int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) { uv__stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE); handle->shutdown_req = NULL; @@ -41,24 +54,68 @@ int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) { int uv_pipe_bind(uv_pipe_t* handle, const char* name) { + return uv_pipe_bind2(handle, name, strlen(name), 0); +} + + +int uv_pipe_bind2(uv_pipe_t* handle, + const char* name, + size_t namelen, + unsigned int flags) { struct sockaddr_un saddr; - const char* pipe_fname; + char* pipe_fname; int sockfd; int err; + socklen_t addrlen; pipe_fname = NULL; + if (flags & ~UV_PIPE_NO_TRUNCATE) + return UV_EINVAL; + + if (name == NULL) + return UV_EINVAL; + + /* namelen==0 on Linux means autobind the listen socket in the abstract + * socket namespace, see `man 7 unix` for details. + */ +#if !defined(__linux__) + if (namelen == 0) + return UV_EINVAL; +#endif + + if (includes_nul(name, namelen)) + return UV_EINVAL; + + if (flags & UV_PIPE_NO_TRUNCATE) + if (namelen > sizeof(saddr.sun_path)) + return UV_EINVAL; + + /* Truncate long paths. Documented behavior. */ + if (namelen > sizeof(saddr.sun_path)) + namelen = sizeof(saddr.sun_path); + /* Already bound? */ if (uv__stream_fd(handle) >= 0) return UV_EINVAL; - /* Make a copy of the file name, it outlives this function's scope. */ - pipe_fname = uv__strdup(name); - if (pipe_fname == NULL) - return UV_ENOMEM; + if (uv__is_closing(handle)) + return UV_EINVAL; - /* We've got a copy, don't touch the original any more. */ - name = NULL; + /* Make a copy of the file path unless it is an abstract socket. + * We unlink the file later but abstract sockets disappear + * automatically since they're not real file system entities. + */ + 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); if (err < 0) @@ -66,10 +123,10 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { sockfd = err; memset(&saddr, 0, sizeof saddr); - uv__strscpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path)); + 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) @@ -81,17 +138,17 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { /* Success. */ handle->flags |= UV_HANDLE_BOUND; - handle->pipe_fname = pipe_fname; /* Is a strdup'ed copy. */ + handle->pipe_fname = pipe_fname; /* NULL or a copy of |name| */ handle->io_watcher.fd = sockfd; return 0; err_socket: - uv__free((void*)pipe_fname); + uv__free(pipe_fname); return err; } -int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { +int uv__pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { if (uv__stream_fd(handle) == -1) return UV_EINVAL; @@ -174,10 +231,56 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, const char* name, uv_connect_cb 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); + } +} + + +int uv_pipe_connect2(uv_connect_t* req, + uv_pipe_t* handle, + const char* name, + size_t namelen, + unsigned int flags, + uv_connect_cb cb) { struct sockaddr_un saddr; int new_sock; int err; int r; + socklen_t addrlen; + + if (flags & ~UV_PIPE_NO_TRUNCATE) + return UV_EINVAL; + + if (name == NULL) + return UV_EINVAL; + + if (namelen == 0) + return UV_EINVAL; + + if (includes_nul(name, namelen)) + return UV_EINVAL; + + if (flags & UV_PIPE_NO_TRUNCATE) + if (namelen > sizeof(saddr.sun_path)) + return UV_EINVAL; + + /* Truncate long paths. Documented behavior. */ + if (namelen > sizeof(saddr.sun_path)) + namelen = sizeof(saddr.sun_path); new_sock = (uv__stream_fd(handle) == -1); @@ -189,12 +292,16 @@ void uv_pipe_connect(uv_connect_t* req, } memset(&saddr, 0, sizeof saddr); - uv__strscpy(saddr.sun_path, name, sizeof(saddr.sun_path)); + 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); @@ -226,14 +333,15 @@ void uv_pipe_connect(uv_connect_t* req, 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; - QUEUE_INIT(&req->queue); + uv__queue_init(&req->queue); /* Force callback to run on next tick in case of error. */ if (err) uv__io_feed(handle->loop, &handle->io_watcher); + return 0; } @@ -241,10 +349,20 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle, uv__peersockfunc func, char* buffer, size_t* size) { +#if defined(__linux__) + static const int is_linux = 1; +#else + static const int is_linux = 0; +#endif struct sockaddr_un sa; socklen_t addrlen; + size_t slop; + char* p; int err; + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + addrlen = sizeof(sa); memset(&sa, 0, addrlen); err = uv__getsockpeername((const uv_handle_t*) handle, @@ -256,17 +374,20 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle, return err; } -#if defined(__linux__) - if (sa.sun_path[0] == 0) - /* Linux abstract namespace */ + slop = 1; + if (is_linux && sa.sun_path[0] == '\0') { + /* Linux abstract namespace. Not zero-terminated. */ + slop = 0; addrlen -= offsetof(struct sockaddr_un, sun_path); - else -#endif - addrlen = strlen(sa.sun_path); - + } else { + p = memchr(sa.sun_path, '\0', sizeof(sa.sun_path)); + if (p == NULL) + p = ARRAY_END(sa.sun_path); + addrlen = p - sa.sun_path; + } - if ((size_t)addrlen >= *size) { - *size = addrlen + 1; + if ((size_t)addrlen + slop > *size) { + *size = addrlen + slop; return UV_ENOBUFS; } @@ -319,14 +440,14 @@ uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) { if (handle->accepted_fd == -1) return UV_UNKNOWN_HANDLE; else - return uv__handle_type(handle->accepted_fd); + return uv_guess_handle(handle->accepted_fd); } 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; @@ -339,26 +460,14 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) { return UV_EINVAL; /* Unfortunately fchmod does not work on all platforms, we will use chmod. */ - name_len = 0; - r = uv_pipe_getsockname(handle, NULL, &name_len); - if (r != UV_ENOBUFS) - return r; - - name_buffer = uv__malloc(name_len); - if (name_buffer == NULL) - return UV_ENOMEM; - + name_len = sizeof(name_buffer); r = uv_pipe_getsockname(handle, name_buffer, &name_len); - if (r != 0) { - uv__free(name_buffer); + if (r != 0) return r; - } /* stat must be used as fstat has a bug on Darwin */ - if (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) @@ -367,15 +476,12 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) { desired_mode |= S_IWUSR | S_IWGRP | S_IWOTH; /* Exit early if pipe already has desired mode. */ - if ((pipe_stat.st_mode & desired_mode) == desired_mode) { - uv__free(name_buffer); + if ((pipe_stat.st_mode & desired_mode) == desired_mode) return 0; - } pipe_stat.st_mode |= desired_mode; r = chmod(name_buffer, pipe_stat.st_mode); - uv__free(name_buffer); return r != -1 ? 0 : UV__ERR(errno); } @@ -384,7 +490,11 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) { int uv_pipe(uv_os_fd_t fds[2], int read_flags, int write_flags) { uv_os_fd_t temp[2]; int err; -#if defined(__FreeBSD__) || defined(__linux__) +#if defined(__linux__) || \ + defined(__FreeBSD__) || \ + defined(__OpenBSD__) || \ + defined(__DragonFly__) || \ + defined(__NetBSD__) int flags = O_CLOEXEC; if ((read_flags & UV_NONBLOCK_PIPE) && (write_flags & UV_NONBLOCK_PIPE)) diff --git a/src/unix/posix-hrtime.c b/src/unix/posix-hrtime.c index 323dfc20392..7b45c01a4d0 100644 --- a/src/unix/posix-hrtime.c +++ b/src/unix/posix-hrtime.c @@ -23,13 +23,14 @@ #include "internal.h" #include +#include #include -#undef NANOSEC -#define NANOSEC ((uint64_t) 1e9) - uint64_t uv__hrtime(uv_clocktype_t type) { - struct timespec ts; - clock_gettime(CLOCK_MONOTONIC, &ts); - return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec); + struct timespec t; + + if (clock_gettime(CLOCK_MONOTONIC, &t)) + abort(); + + return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec; } diff --git a/src/unix/posix-poll.c b/src/unix/posix-poll.c index 0f4bf93874b..2e016c2fbae 100644 --- a/src/unix/posix-poll.c +++ b/src/unix/posix-poll.c @@ -132,11 +132,12 @@ static void uv__pollfds_del(uv_loop_t* loop, int fd) { void uv__io_poll(uv_loop_t* loop, int timeout) { + uv__loop_internal_fields_t* lfields; sigset_t* pset; sigset_t set; uint64_t time_base; uint64_t time_diff; - QUEUE* q; + struct uv__queue* q; uv__io_t* w; size_t i; unsigned int nevents; @@ -148,17 +149,19 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int reset_timeout; if (loop->nfds == 0) { - assert(QUEUE_EMPTY(&loop->watcher_queue)); + assert(uv__queue_empty(&loop->watcher_queue)); return; } + lfields = uv__get_internal_fields(loop); + /* Take queued watchers and add their fds to our poll fds array. */ - while (!QUEUE_EMPTY(&loop->watcher_queue)) { - q = QUEUE_HEAD(&loop->watcher_queue); - QUEUE_REMOVE(q); - QUEUE_INIT(q); + while (!uv__queue_empty(&loop->watcher_queue)) { + q = uv__queue_head(&loop->watcher_queue); + uv__queue_remove(q); + uv__queue_init(q); - w = QUEUE_DATA(q, uv__io_t, watcher_queue); + w = uv__queue_data(q, uv__io_t, watcher_queue); assert(w->pevents != 0); assert(w->fd >= 0); assert(w->fd < (int) loop->nwatchers); @@ -179,7 +182,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { assert(timeout >= -1); time_base = loop->time; - if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + if (lfields->flags & UV_METRICS_IDLE_TIME) { reset_timeout = 1; user_timeout = timeout; timeout = 0; @@ -198,6 +201,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { 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; + if (pset != NULL) if (pthread_sigmask(SIG_BLOCK, pset, NULL)) abort(); @@ -292,9 +301,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { } } + uv__metrics_inc_events(loop, nevents); if (reset_timeout != 0) { timeout = user_timeout; reset_timeout = 0; + uv__metrics_inc_events_waiting(loop, nevents); } if (have_signals != 0) { diff --git a/src/unix/process.c b/src/unix/process.c index 91bf3c50702..f2038f2c0e8 100644 --- a/src/unix/process.c +++ b/src/unix/process.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -34,14 +35,28 @@ #include #include -#if defined(__APPLE__) && !TARGET_OS_IPHONE +#if defined(__APPLE__) +# include +# include +# include +# include +# include +# include # include +# include # define environ (*_NSGetEnviron()) + +/* macOS 10.14 back does not define this constant */ +# ifndef POSIX_SPAWN_SETSID +# define POSIX_SPAWN_SETSID 1024 +# endif + #else extern char **environ; #endif -#if defined(__linux__) || defined(__GLIBC__) +#if defined(__linux__) || \ + defined(__GNU__) # include #endif @@ -49,55 +64,100 @@ extern char **environ; # include "zos-base.h" #endif +#ifdef UV_HAVE_KQUEUE +#include +#else +#define UV_USE_SIGCHLD +#endif + +#ifdef UV_USE_SIGCHLD static void uv__chld(uv_signal_t* handle, int signum) { + assert(signum == SIGCHLD); + uv__wait_children(handle->loop); +} + + +int uv__process_init(uv_loop_t* loop) { + int err; + + err = uv_signal_init(loop, &loop->child_watcher); + if (err) + return err; + uv__handle_unref(&loop->child_watcher); + loop->child_watcher.flags |= UV_HANDLE_INTERNAL; + return 0; +} + + +#else +int uv__process_init(uv_loop_t* loop) { + memset(&loop->child_watcher, 0, sizeof(loop->child_watcher)); + return 0; +} +#endif + + +void uv__wait_children(uv_loop_t* loop) { uv_process_t* process; - uv_loop_t* loop; int exit_status; int term_signal; int status; + int options; pid_t pid; - QUEUE pending; - QUEUE* q; - QUEUE* h; + struct uv__queue pending; + struct uv__queue* q; + struct uv__queue* h; - assert(signum == SIGCHLD); - - QUEUE_INIT(&pending); - loop = handle->loop; + uv__queue_init(&pending); h = &loop->process_handles; - q = QUEUE_HEAD(h); + q = uv__queue_head(h); while (q != h) { - process = QUEUE_DATA(q, uv_process_t, queue); - q = QUEUE_NEXT(q); + process = uv__queue_data(q, uv_process_t, queue); + q = uv__queue_next(q); + +#ifndef UV_USE_SIGCHLD + if ((process->flags & UV_HANDLE_REAP) == 0) + continue; + options = 0; + process->flags &= ~UV_HANDLE_REAP; + loop->nfds--; +#else + options = WNOHANG; +#endif do - pid = waitpid(process->pid, &status, WNOHANG); + pid = waitpid(process->pid, &status, options); while (pid == -1 && errno == EINTR); - if (pid == 0) +#ifdef UV_USE_SIGCHLD + if (pid == 0) /* Not yet exited */ continue; +#endif if (pid == -1) { if (errno != ECHILD) abort(); + /* The child died, and we missed it. This probably means someone else + * stole the waitpid from us. Handle this by not handling it at all. */ continue; } + assert(pid == process->pid); process->status = status; - QUEUE_REMOVE(&process->queue); - QUEUE_INSERT_TAIL(&pending, &process->queue); + uv__queue_remove(&process->queue); + uv__queue_insert_tail(&pending, &process->queue); } h = &pending; - q = QUEUE_HEAD(h); + q = uv__queue_head(h); while (q != h) { - process = QUEUE_DATA(q, uv_process_t, queue); - q = QUEUE_NEXT(q); + process = uv__queue_data(q, uv_process_t, queue); + q = uv__queue_next(q); - QUEUE_REMOVE(&process->queue); - QUEUE_INIT(&process->queue); + uv__queue_remove(&process->queue); + uv__queue_init(&process->queue); uv__handle_stop(process); if (process->exit_cb == NULL) @@ -113,13 +173,18 @@ static void uv__chld(uv_signal_t* handle, int signum) { process->exit_cb(process, exit_status, term_signal); } - assert(QUEUE_EMPTY(&pending)); + assert(uv__queue_empty(&pending)); } /* * Used for initializing stdio streams like options.stdin_stream. Returns * zero on success. See also the cleanup section in uv_spawn(). */ +#if !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH)) +/* execvp is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED, so must be + * avoided. Since this isn't called on those targets, the function + * doesn't even need to be defined for them. + */ static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) { int mask; int fd; @@ -195,24 +260,17 @@ static void uv__write_int(int fd, int val) { n = write(fd, &val, sizeof(val)); while (n == -1 && errno == EINTR); - if (n == -1 && errno == EPIPE) - return; /* parent process has quit */ - - assert(n == sizeof(val)); + /* The write might have failed (e.g. if the parent process has died), + * but we have nothing left but to _exit ourself now too. */ + _exit(127); } static void uv__write_errno(int error_fd) { uv__write_int(error_fd, UV__ERR(errno)); - _exit(127); } -#if !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH)) -/* execvp is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED, so must be - * avoided. Since this isn't called on those targets, the function - * doesn't even need to be defined for them. - */ static void uv__process_child_init(const uv_process_options_t* options, int stdio_count, int (*pipes)[2], @@ -254,22 +312,31 @@ static void uv__process_child_init(const uv_process_options_t* options, use_fd = pipes[fd][1]; if (use_fd < 0 || use_fd >= fd) continue; +#ifdef F_DUPFD_CLOEXEC /* POSIX 2008 */ + pipes[fd][1] = fcntl(use_fd, F_DUPFD_CLOEXEC, stdio_count); +#else pipes[fd][1] = fcntl(use_fd, F_DUPFD, stdio_count); +#endif if (pipes[fd][1] == -1) uv__write_errno(error_fd); +#ifndef F_DUPFD_CLOEXEC /* POSIX 2008 */ + n = uv__cloexec(pipes[fd][1], 1); + if (n) + uv__write_int(error_fd, n); +#endif } for (fd = 0; fd < stdio_count; fd++) { - close_fd = pipes[fd][0]; + close_fd = -1; use_fd = pipes[fd][1]; if (use_fd < 0) { if (fd >= 3) continue; else { - /* redirect stdin, stdout and stderr to /dev/null even if UV_IGNORE is - * set - */ + /* Redirect stdin, stdout and stderr to /dev/null even if UV_IGNORE is + * set. */ + uv__close_nocheckstdio(fd); /* Free up fd, if it happens to be open. */ use_fd = open("/dev/null", fd == 0 ? O_RDONLY : O_RDWR); close_fd = use_fd; @@ -278,28 +345,27 @@ static void uv__process_child_init(const uv_process_options_t* options, } } - if (fd == use_fd) - uv__cloexec_fcntl(use_fd, 0); - else + if (fd == use_fd) { + if (close_fd == -1) { + n = uv__cloexec(use_fd, 0); + if (n) + uv__write_int(error_fd, n); + } + } + else { fd = dup2(use_fd, fd); + } if (fd == -1) uv__write_errno(error_fd); - if (fd <= 2) + if (fd <= 2 && close_fd == -1) uv__nonblock_fcntl(fd, 0); if (close_fd >= stdio_count) uv__close(close_fd); } - for (fd = 0; fd < stdio_count; fd++) { - use_fd = pipes[fd][1]; - - if (use_fd >= stdio_count) - uv__close(use_fd); - } - if (options->cwd != NULL && chdir(options->cwd)) uv__write_errno(error_fd); @@ -320,9 +386,8 @@ static void uv__process_child_init(const uv_process_options_t* options, if ((options->flags & UV_PROCESS_SETUID) && setuid(options->uid)) uv__write_errno(error_fd); - if (options->env != NULL) { + if (options->env != NULL) environ = options->env; - } /* Reset signal mask just before exec. */ sigemptyset(&signewset); @@ -336,10 +401,554 @@ static void uv__process_child_init(const uv_process_options_t* options, #endif uv__write_errno(error_fd); - abort(); } + + +#if defined(__APPLE__) +typedef struct uv__posix_spawn_fncs_tag { + struct { + int (*addchdir_np)(const posix_spawn_file_actions_t *, const char *); + } file_actions; +} uv__posix_spawn_fncs_t; + + +static uv_once_t posix_spawn_init_once = UV_ONCE_INIT; +static uv__posix_spawn_fncs_t posix_spawn_fncs; +static int posix_spawn_can_use_setsid; + + +static void uv__spawn_init_posix_spawn_fncs(void) { + /* Try to locate all non-portable functions at runtime */ + posix_spawn_fncs.file_actions.addchdir_np = + dlsym(RTLD_DEFAULT, "posix_spawn_file_actions_addchdir_np"); +} + + +static void uv__spawn_init_can_use_setsid(void) { + int which[] = {CTL_KERN, KERN_OSRELEASE}; + unsigned major; + unsigned minor; + unsigned patch; + char buf[256]; + size_t len; + + len = sizeof(buf); + if (sysctl(which, ARRAY_SIZE(which), buf, &len, NULL, 0)) + return; + + /* NULL specifies to use LC_C_LOCALE */ + if (3 != sscanf_l(buf, NULL, "%u.%u.%u", &major, &minor, &patch)) + return; + + posix_spawn_can_use_setsid = (major >= 19); /* macOS Catalina */ +} + + +static void uv__spawn_init_posix_spawn(void) { + /* Init handles to all potentially non-defined functions */ + uv__spawn_init_posix_spawn_fncs(); + + /* Init feature detection for POSIX_SPAWN_SETSID flag */ + uv__spawn_init_can_use_setsid(); +} + + +static int uv__spawn_set_posix_spawn_attrs( + posix_spawnattr_t* attrs, + const uv__posix_spawn_fncs_t* posix_spawn_fncs, + const uv_process_options_t* options) { + int err; + unsigned int flags; + sigset_t signal_set; + + err = posix_spawnattr_init(attrs); + if (err != 0) { + /* If initialization fails, no need to de-init, just return */ + return err; + } + + if (options->flags & (UV_PROCESS_SETUID | UV_PROCESS_SETGID)) { + /* kauth_cred_issuser currently requires exactly uid == 0 for these + * posixspawn_attrs (set_groups_np, setuid_np, setgid_np), which deviates + * from the normal specification of setuid (which also uses euid), and they + * are also undocumented syscalls, so we do not use them. */ + err = ENOSYS; + goto error; + } + + /* Set flags for spawn behavior + * 1) POSIX_SPAWN_CLOEXEC_DEFAULT: (Apple Extension) All descriptors in the + * parent will be treated as if they had been created with O_CLOEXEC. The + * only fds that will be passed on to the child are those manipulated by + * the file actions + * 2) POSIX_SPAWN_SETSIGDEF: Signals mentioned in spawn-sigdefault in the + * spawn attributes will be reset to behave as their default + * 3) POSIX_SPAWN_SETSIGMASK: Signal mask will be set to the value of + * spawn-sigmask in attributes + * 4) POSIX_SPAWN_SETSID: Make the process a new session leader if a detached + * session was requested. */ + flags = POSIX_SPAWN_CLOEXEC_DEFAULT | + POSIX_SPAWN_SETSIGDEF | + POSIX_SPAWN_SETSIGMASK; + if (options->flags & UV_PROCESS_DETACHED) { + /* If running on a version of macOS where this flag is not supported, + * revert back to the fork/exec flow. Otherwise posix_spawn will + * silently ignore the flag. */ + if (!posix_spawn_can_use_setsid) { + err = ENOSYS; + goto error; + } + + flags |= POSIX_SPAWN_SETSID; + } + err = posix_spawnattr_setflags(attrs, flags); + if (err != 0) + goto error; + + /* Reset all signal the child to their default behavior */ + sigfillset(&signal_set); + err = posix_spawnattr_setsigdefault(attrs, &signal_set); + if (err != 0) + goto error; + + /* Reset the signal mask for all signals */ + sigemptyset(&signal_set); + err = posix_spawnattr_setsigmask(attrs, &signal_set); + if (err != 0) + goto error; + + return err; + +error: + (void) posix_spawnattr_destroy(attrs); + return err; +} + + +static int uv__spawn_set_posix_spawn_file_actions( + posix_spawn_file_actions_t* actions, + const uv__posix_spawn_fncs_t* posix_spawn_fncs, + const uv_process_options_t* options, + int stdio_count, + int (*pipes)[2]) { + int fd; + int fd2; + int use_fd; + int err; + + err = posix_spawn_file_actions_init(actions); + if (err != 0) { + /* If initialization fails, no need to de-init, just return */ + return err; + } + + /* Set the current working directory if requested */ + if (options->cwd != NULL) { + if (posix_spawn_fncs->file_actions.addchdir_np == NULL) { + err = ENOSYS; + goto error; + } + + err = posix_spawn_fncs->file_actions.addchdir_np(actions, options->cwd); + if (err != 0) + goto error; + } + + /* Do not return ENOSYS after this point, as we may mutate pipes. */ + + /* First duplicate low numbered fds, since it's not safe to duplicate them, + * they could get replaced. Example: swapping stdout and stderr; without + * this fd 2 (stderr) would be duplicated into fd 1, thus making both + * stdout and stderr go to the same fd, which was not the intention. */ + for (fd = 0; fd < stdio_count; fd++) { + use_fd = pipes[fd][1]; + if (use_fd < 0 || use_fd >= fd) + continue; + use_fd = stdio_count; + for (fd2 = 0; fd2 < stdio_count; fd2++) { + /* If we were not setting POSIX_SPAWN_CLOEXEC_DEFAULT, we would need to + * also consider whether fcntl(fd, F_GETFD) returned without the + * FD_CLOEXEC flag set. */ + if (pipes[fd2][1] == use_fd) { + use_fd++; + fd2 = 0; + } + } + err = posix_spawn_file_actions_adddup2( + actions, + pipes[fd][1], + use_fd); + assert(err != ENOSYS); + if (err != 0) + goto error; + pipes[fd][1] = use_fd; + } + + /* Second, move the descriptors into their respective places */ + for (fd = 0; fd < stdio_count; fd++) { + use_fd = pipes[fd][1]; + if (use_fd < 0) { + if (fd >= 3) + continue; + else { + /* If ignored, redirect to (or from) /dev/null, */ + err = posix_spawn_file_actions_addopen( + actions, + fd, + "/dev/null", + fd == 0 ? O_RDONLY : O_RDWR, + 0); + assert(err != ENOSYS); + if (err != 0) + goto error; + continue; + } + } + + if (fd == use_fd) + err = posix_spawn_file_actions_addinherit_np(actions, fd); + else + err = posix_spawn_file_actions_adddup2(actions, use_fd, fd); + assert(err != ENOSYS); + if (err != 0) + goto error; + + /* Make sure the fd is marked as non-blocking (state shared between child + * and parent). */ + uv__nonblock_fcntl(use_fd, 0); + } + + /* Finally, close all the superfluous descriptors */ + for (fd = 0; fd < stdio_count; fd++) { + use_fd = pipes[fd][1]; + if (use_fd < stdio_count) + continue; + + /* Check if we already closed this. */ + for (fd2 = 0; fd2 < fd; fd2++) { + if (pipes[fd2][1] == use_fd) + break; + } + if (fd2 < fd) + continue; + + err = posix_spawn_file_actions_addclose(actions, use_fd); + assert(err != ENOSYS); + if (err != 0) + goto error; + } + + return 0; + +error: + (void) posix_spawn_file_actions_destroy(actions); + return err; +} + +char* uv__spawn_find_path_in_env(char** env) { + char** env_iterator; + const char path_var[] = "PATH="; + + /* Look for an environment variable called PATH in the + * provided env array, and return its value if found */ + for (env_iterator = env; *env_iterator != NULL; env_iterator++) { + if (strncmp(*env_iterator, path_var, sizeof(path_var) - 1) == 0) { + /* Found "PATH=" at the beginning of the string */ + return *env_iterator + sizeof(path_var) - 1; + } + } + + return NULL; +} + + +static int uv__spawn_resolve_and_spawn(const uv_process_options_t* options, + posix_spawnattr_t* attrs, + posix_spawn_file_actions_t* actions, + pid_t* pid) { + const char *p; + const char *z; + const char *path; + size_t l; + size_t k; + int err; + int seen_eacces; + + path = NULL; + err = -1; + seen_eacces = 0; + + /* Short circuit for erroneous case */ + if (options->file == NULL) + return ENOENT; + + /* The environment for the child process is that of the parent unless overridden + * by options->env */ + char** env = environ; + if (options->env != NULL) + env = options->env; + + /* If options->file contains a slash, posix_spawn/posix_spawnp should behave + * the same, and do not involve PATH resolution at all. The libc + * `posix_spawnp` provided by Apple is buggy (since 10.15), so we now emulate it + * here, per https://github.com/libuv/libuv/pull/3583. */ + if (strchr(options->file, '/') != NULL) { + do + err = posix_spawn(pid, options->file, actions, attrs, options->args, env); + while (err == EINTR); + return err; + } + + /* Look for the definition of PATH in the provided env */ + path = uv__spawn_find_path_in_env(env); + + /* The following resolution logic (execvpe emulation) is copied from + * https://git.musl-libc.org/cgit/musl/tree/src/process/execvp.c + * and adapted to work for our specific usage */ + + /* If no path was provided in env, use the default value + * to look for the executable */ + if (path == NULL) + path = _PATH_DEFPATH; + + k = strnlen(options->file, NAME_MAX + 1); + if (k > NAME_MAX) + return ENAMETOOLONG; + + l = strnlen(path, PATH_MAX - 1) + 1; + + for (p = path;; p = z) { + /* Compose the new process file from the entry in the PATH + * environment variable and the actual file name */ + char b[PATH_MAX + NAME_MAX]; + z = strchr(p, ':'); + if (!z) + z = p + strlen(p); + if ((size_t)(z - p) >= l) { + if (!*z++) + break; + + continue; + } + memcpy(b, p, z - p); + b[z - p] = '/'; + memcpy(b + (z - p) + (z > p), options->file, k + 1); + + /* Try to spawn the new process file. If it fails with ENOENT, the + * new process file is not in this PATH entry, continue with the next + * PATH entry. */ + do + err = posix_spawn(pid, b, actions, attrs, options->args, env); + while (err == EINTR); + + switch (err) { + case EACCES: + seen_eacces = 1; + break; /* continue search */ + case ENOENT: + case ENOTDIR: + break; /* continue search */ + default: + return err; + } + + if (!*z++) + break; + } + + if (seen_eacces) + return EACCES; + return err; +} + + +static int uv__spawn_and_init_child_posix_spawn( + const uv_process_options_t* options, + int stdio_count, + int (*pipes)[2], + pid_t* pid, + const uv__posix_spawn_fncs_t* posix_spawn_fncs) { + int err; + posix_spawnattr_t attrs; + posix_spawn_file_actions_t actions; + + err = uv__spawn_set_posix_spawn_attrs(&attrs, posix_spawn_fncs, options); + if (err != 0) + goto error; + + /* This may mutate pipes. */ + err = uv__spawn_set_posix_spawn_file_actions(&actions, + posix_spawn_fncs, + options, + stdio_count, + pipes); + if (err != 0) { + (void) posix_spawnattr_destroy(&attrs); + goto error; + } + + /* Try to spawn options->file resolving in the provided environment + * if any */ + err = uv__spawn_resolve_and_spawn(options, &attrs, &actions, pid); + assert(err != ENOSYS); + + /* Destroy the actions/attributes */ + (void) posix_spawn_file_actions_destroy(&actions); + (void) posix_spawnattr_destroy(&attrs); + +error: + /* In an error situation, the attributes and file actions are + * already destroyed, only the happy path requires cleanup */ + return UV__ERR(err); +} +#endif + +static int uv__spawn_and_init_child_fork(const uv_process_options_t* options, + int stdio_count, + int (*pipes)[2], + int error_fd, + pid_t* pid) { + sigset_t signewset; + sigset_t sigoldset; + + /* Start the child with most signals blocked, to avoid any issues before we + * can reset them, but allow program failures to exit (and not hang). */ + sigfillset(&signewset); + sigdelset(&signewset, SIGKILL); + sigdelset(&signewset, SIGSTOP); + sigdelset(&signewset, SIGTRAP); + sigdelset(&signewset, SIGSEGV); + sigdelset(&signewset, SIGBUS); + sigdelset(&signewset, SIGILL); + sigdelset(&signewset, SIGSYS); + sigdelset(&signewset, SIGABRT); + if (pthread_sigmask(SIG_BLOCK, &signewset, &sigoldset) != 0) + abort(); + + *pid = fork(); + + if (*pid == 0) { + /* Fork succeeded, in the child process */ + uv__process_child_init(options, stdio_count, pipes, error_fd); + abort(); + } + + if (pthread_sigmask(SIG_SETMASK, &sigoldset, NULL) != 0) + abort(); + + if (*pid == -1) + /* Failed to fork */ + return UV__ERR(errno); + + /* Fork succeeded, in the parent process */ + return 0; +} + +static int uv__spawn_and_init_child( + uv_loop_t* loop, + const uv_process_options_t* options, + int stdio_count, + int (*pipes)[2], + pid_t* pid) { + int signal_pipe[2] = { -1, -1 }; + int status; + int err; + int exec_errorno; + ssize_t r; + +#if defined(__APPLE__) + uv_once(&posix_spawn_init_once, uv__spawn_init_posix_spawn); + + /* Special child process spawn case for macOS Big Sur (11.0) onwards + * + * Big Sur introduced a significant performance degradation on a call to + * fork/exec when the process has many pages mmaped in with MAP_JIT, like, say + * a javascript interpreter. Electron-based applications, for example, + * are impacted; though the magnitude of the impact depends on how much the + * app relies on subprocesses. + * + * On macOS, though, posix_spawn is implemented in a way that does not + * exhibit the problem. This block implements the forking and preparation + * logic with posix_spawn and its related primitives. It also takes advantage of + * the macOS extension POSIX_SPAWN_CLOEXEC_DEFAULT that makes impossible to + * leak descriptors to the child process. */ + err = uv__spawn_and_init_child_posix_spawn(options, + stdio_count, + pipes, + pid, + &posix_spawn_fncs); + + /* The posix_spawn flow will return UV_ENOSYS if any of the posix_spawn_x_np + * non-standard functions is both _needed_ and _undefined_. In those cases, + * default back to the fork/execve strategy. For all other errors, just fail. */ + if (err != UV_ENOSYS) + return err; + #endif + /* This pipe is used by the parent to wait until + * the child has called `execve()`. We need this + * to avoid the following race condition: + * + * if ((pid = fork()) > 0) { + * kill(pid, SIGTERM); + * } + * else if (pid == 0) { + * execve("/bin/cat", argp, envp); + * } + * + * The parent sends a signal immediately after forking. + * Since the child may not have called `execve()` yet, + * there is no telling what process receives the signal, + * our fork or /bin/cat. + * + * To avoid ambiguity, we create a pipe with both ends + * marked close-on-exec. Then, after the call to `fork()`, + * the parent polls the read end until it EOFs or errors with EPIPE. + */ + err = uv__make_pipe(signal_pipe, 0); + if (err) + return err; + + /* Acquire write lock to prevent opening new fds in worker threads */ + uv_rwlock_wrlock(&loop->cloexec_lock); + + err = uv__spawn_and_init_child_fork(options, stdio_count, pipes, signal_pipe[1], pid); + + /* Release lock in parent process */ + uv_rwlock_wrunlock(&loop->cloexec_lock); + + uv__close(signal_pipe[1]); + + if (err == 0) { + do + r = read(signal_pipe[0], &exec_errorno, sizeof(exec_errorno)); + while (r == -1 && errno == EINTR); + + if (r == 0) + ; /* okay, EOF */ + else if (r == sizeof(exec_errorno)) { + do + err = waitpid(*pid, &status, 0); /* okay, read errorno */ + while (err == -1 && errno == EINTR); + assert(err == *pid); + err = exec_errorno; + } else if (r == -1 && errno == EPIPE) { + /* Something unknown happened to our child before spawn */ + do + err = waitpid(*pid, &status, 0); /* okay, got EPIPE */ + while (err == -1 && errno == EINTR); + assert(err == *pid); + err = UV_EPIPE; + } else + abort(); + } + + uv__close_nocheckstdio(signal_pipe[0]); + + return err; +} +#endif /* ISN'T TARGET_OS_TV || TARGET_OS_WATCH */ int uv_spawn(uv_loop_t* loop, uv_process_t* process, @@ -348,30 +957,27 @@ int uv_spawn(uv_loop_t* loop, /* fork is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED. */ return UV_ENOSYS; #else - sigset_t signewset; - sigset_t sigoldset; - int signal_pipe[2] = { -1, -1 }; int pipes_storage[8][2]; int (*pipes)[2]; int stdio_count; - ssize_t r; pid_t pid; int err; int exec_errorno; int i; - int status; assert(options->file != NULL); 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 | UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS))); uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS); - QUEUE_INIT(&process->queue); + uv__queue_init(&process->queue); + process->status = 0; stdio_count = options->stdio_count; if (stdio_count < 3) @@ -396,92 +1002,47 @@ int uv_spawn(uv_loop_t* loop, goto error; } - /* This pipe is used by the parent to wait until - * the child has called `execve()`. We need this - * to avoid the following race condition: - * - * if ((pid = fork()) > 0) { - * kill(pid, SIGTERM); - * } - * else if (pid == 0) { - * execve("/bin/cat", argp, envp); - * } - * - * The parent sends a signal immediately after forking. - * Since the child may not have called `execve()` yet, - * there is no telling what process receives the signal, - * our fork or /bin/cat. - * - * To avoid ambiguity, we create a pipe with both ends - * marked close-on-exec. Then, after the call to `fork()`, - * the parent polls the read end until it EOFs or errors with EPIPE. - */ - err = uv__make_pipe(signal_pipe, 0); - if (err) - goto error; - +#ifdef UV_USE_SIGCHLD uv_signal_start(&loop->child_watcher, uv__chld, SIGCHLD); +#endif - /* Acquire write lock to prevent opening new fds in worker threads */ - uv_rwlock_wrlock(&loop->cloexec_lock); - - /* Start the child with most signals blocked, to avoid any issues before we - * can reset them, but allow program failures to exit (and not hang). */ - sigfillset(&signewset); - sigdelset(&signewset, SIGKILL); - sigdelset(&signewset, SIGSTOP); - sigdelset(&signewset, SIGTRAP); - sigdelset(&signewset, SIGSEGV); - sigdelset(&signewset, SIGBUS); - sigdelset(&signewset, SIGILL); - sigdelset(&signewset, SIGSYS); - sigdelset(&signewset, SIGABRT); - if (pthread_sigmask(SIG_BLOCK, &signewset, &sigoldset) != 0) - abort(); - - pid = fork(); - if (pid == -1) - err = UV__ERR(errno); - - if (pid == 0) - uv__process_child_init(options, stdio_count, pipes, signal_pipe[1]); - - if (pthread_sigmask(SIG_SETMASK, &sigoldset, NULL) != 0) - abort(); + /* Spawn the child */ + exec_errorno = uv__spawn_and_init_child(loop, options, stdio_count, pipes, &pid); - /* Release lock in parent process */ - uv_rwlock_wrunlock(&loop->cloexec_lock); +#if 0 + /* This runs into a nodejs issue (it expects initialized streams, even if the + * exec failed). + * See https://github.com/libuv/libuv/pull/3107#issuecomment-782482608 */ + if (exec_errorno != 0) + goto error; +#endif - uv__close(signal_pipe[1]); + /* Activate this handle if exec() happened successfully, even if we later + * fail to open a stdio handle. This ensures we can eventually reap the child + * with waitpid. */ + if (exec_errorno == 0) { +#ifndef UV_USE_SIGCHLD + struct kevent event; + EV_SET(&event, pid, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT, 0, 0); + if (kevent(loop->backend_fd, &event, 1, NULL, 0, NULL)) { + if (errno != ESRCH) + abort(); + /* Process already exited. Call waitpid on the next loop iteration. */ + process->flags |= UV_HANDLE_REAP; + loop->flags |= UV_LOOP_REAP_CHILDREN; + } + /* This prevents uv__io_poll() from bailing out prematurely, being unaware + * that we added an event here for it to react to. We will decrement this + * again after the waitpid call succeeds. */ + loop->nfds++; +#endif - if (pid == -1) { - uv__close(signal_pipe[0]); - goto error; + process->pid = pid; + process->exit_cb = options->exit_cb; + uv__queue_insert_tail(&loop->process_handles, &process->queue); + uv__handle_start(process); } - process->status = 0; - exec_errorno = 0; - do - r = read(signal_pipe[0], &exec_errorno, sizeof(exec_errorno)); - while (r == -1 && errno == EINTR); - - if (r == 0) - ; /* okay, EOF */ - else if (r == sizeof(exec_errorno)) { - do - err = waitpid(pid, &status, 0); /* okay, read errorno */ - while (err == -1 && errno == EINTR); - assert(err == pid); - } else if (r == -1 && errno == EPIPE) { - do - err = waitpid(pid, &status, 0); /* okay, got EPIPE */ - while (err == -1 && errno == EINTR); - assert(err == pid); - } else - abort(); - - uv__close_nocheckstdio(signal_pipe[0]); - for (i = 0; i < options->stdio_count; i++) { err = uv__process_open_stream(options->stdio + i, pipes[i]); if (err == 0) @@ -493,15 +1054,6 @@ int uv_spawn(uv_loop_t* loop, goto error; } - /* Only activate this handle if exec() happened successfully */ - if (exec_errorno == 0) { - QUEUE_INSERT_TAIL(&loop->process_handles, &process->queue); - uv__handle_start(process); - } - - process->pid = pid; - process->exit_cb = options->exit_cb; - if (pipes != pipes_storage) uv__free(pipes); @@ -534,16 +1086,25 @@ int uv_process_kill(uv_process_t* process, int signum) { int uv_kill(int pid, int signum) { - if (kill(pid, signum)) + if (kill(pid, signum)) { +#if defined(__MVS__) + /* EPERM is returned if the process is a zombie. */ + siginfo_t infop; + if (errno == EPERM && + waitid(P_PID, pid, &infop, WNOHANG | WNOWAIT | WEXITED) == 0) + return 0; +#endif return UV__ERR(errno); - else + } else return 0; } void uv__process_close(uv_process_t* handle) { - QUEUE_REMOVE(&handle->queue); + uv__queue_remove(&handle->queue); uv__handle_stop(handle); - if (QUEUE_EMPTY(&handle->loop->process_handles)) +#ifdef UV_USE_SIGCHLD + if (uv__queue_empty(&handle->loop->process_handles)) uv_signal_stop(&handle->loop->child_watcher); +#endif } diff --git a/src/unix/pthread-fixes.c b/src/unix/pthread-fixes.c deleted file mode 100644 index 022d79c4e21..00000000000 --- a/src/unix/pthread-fixes.c +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright (c) 2013, Sony Mobile Communications AB - * Copyright (c) 2012, Google Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are - met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the - distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -/* Android versions < 4.1 have a broken pthread_sigmask. */ -#include "uv-common.h" - -#include -#include -#include - -int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset) { - static int workaround; - int err; - - if (uv__load_relaxed(&workaround)) { - return sigprocmask(how, set, oset); - } else { - err = pthread_sigmask(how, set, oset); - if (err) { - if (err == EINVAL && sigprocmask(how, set, oset) == 0) { - uv__store_relaxed(&workaround, 1); - return 0; - } else { - return -1; - } - } - } - - return 0; -} diff --git a/src/unix/qnx.c b/src/unix/qnx.c index ca148d349f8..57ea9dfd9cc 100644 --- a/src/unix/qnx.c +++ b/src/unix/qnx.c @@ -88,6 +88,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + int uv_resident_set_memory(size_t* rss) { int fd; procfs_asinfo asinfo; diff --git a/src/unix/random-devurandom.c b/src/unix/random-devurandom.c index 05e52a56a36..d6336f2c98c 100644 --- a/src/unix/random-devurandom.c +++ b/src/unix/random-devurandom.c @@ -40,7 +40,7 @@ int uv__random_readpath(const char* path, void* buf, size_t buflen) { if (fd < 0) return fd; - if (fstat(fd, &s)) { + if (uv__fstat(fd, &s)) { uv__close(fd); return UV__ERR(errno); } diff --git a/src/unix/random-getrandom.c b/src/unix/random-getrandom.c index bcc94089bcb..054eccf1664 100644 --- a/src/unix/random-getrandom.c +++ b/src/unix/random-getrandom.c @@ -24,8 +24,6 @@ #ifdef __linux__ -#include "linux-syscalls.h" - #define uv__random_getrandom_init() 0 #else /* !__linux__ */ diff --git a/src/unix/signal.c b/src/unix/signal.c index 1133c73a955..f23c887d0d6 100644 --- a/src/unix/signal.c +++ b/src/unix/signal.c @@ -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; @@ -279,26 +279,43 @@ static int uv__signal_loop_once_init(uv_loop_t* loop) { int uv__signal_loop_fork(uv_loop_t* loop) { + struct uv__queue* q; + + if (loop->signal_pipefd[0] == -1) + return 0; uv__io_stop(loop, &loop->signal_io_watcher, POLLIN); uv__close(loop->signal_pipefd[0]); uv__close(loop->signal_pipefd[1]); loop->signal_pipefd[0] = -1; loop->signal_pipefd[1] = -1; + + uv__queue_foreach(q, &loop->handle_queue) { + uv_handle_t* handle = uv__queue_data(q, uv_handle_t, handle_queue); + uv_signal_t* sh; + + if (handle->type != UV_SIGNAL) + continue; + + sh = (uv_signal_t*) handle; + sh->caught_signals = 0; + sh->dispatched_signals = 0; + } + return uv__signal_loop_once_init(loop); } void uv__signal_loop_cleanup(uv_loop_t* loop) { - QUEUE* q; + struct uv__queue* q; /* Stop all the signal watchers that are still attached to this loop. This * ensures that the (shared) signal tree doesn't contain any invalid entries * entries, and that signal handlers are removed when appropriate. - * It's safe to use QUEUE_FOREACH here because the handles and the handle + * It's safe to use uv__queue_foreach here because the handles and the handle * queue are not modified by uv__signal_stop(). */ - QUEUE_FOREACH(q, &loop->handle_queue) { - uv_handle_t* handle = QUEUE_DATA(q, uv_handle_t, handle_queue); + uv__queue_foreach(q, &loop->handle_queue) { + uv_handle_t* handle = uv__queue_data(q, uv_handle_t, handle_queue); if (handle->type == UV_SIGNAL) uv__signal_stop((uv_signal_t*) handle); diff --git a/src/unix/spinlock.h b/src/unix/spinlock.h deleted file mode 100644 index a20c83cc601..00000000000 --- a/src/unix/spinlock.h +++ /dev/null @@ -1,53 +0,0 @@ -/* Copyright (c) 2013, Ben Noordhuis - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef UV_SPINLOCK_H_ -#define UV_SPINLOCK_H_ - -#include "internal.h" /* ACCESS_ONCE, UV_UNUSED */ -#include "atomic-ops.h" - -#define UV_SPINLOCK_INITIALIZER { 0 } - -typedef struct { - int lock; -} uv_spinlock_t; - -UV_UNUSED(static void uv_spinlock_init(uv_spinlock_t* spinlock)); -UV_UNUSED(static void uv_spinlock_lock(uv_spinlock_t* spinlock)); -UV_UNUSED(static void uv_spinlock_unlock(uv_spinlock_t* spinlock)); -UV_UNUSED(static int uv_spinlock_trylock(uv_spinlock_t* spinlock)); - -UV_UNUSED(static void uv_spinlock_init(uv_spinlock_t* spinlock)) { - ACCESS_ONCE(int, spinlock->lock) = 0; -} - -UV_UNUSED(static void uv_spinlock_lock(uv_spinlock_t* spinlock)) { - while (!uv_spinlock_trylock(spinlock)) cpu_relax(); -} - -UV_UNUSED(static void uv_spinlock_unlock(uv_spinlock_t* spinlock)) { - ACCESS_ONCE(int, spinlock->lock) = 0; -} - -UV_UNUSED(static int uv_spinlock_trylock(uv_spinlock_t* spinlock)) { - /* TODO(bnoordhuis) Maybe change to a ticket lock to guarantee fair queueing. - * Not really critical until we have locks that are (frequently) contended - * for by several threads. - */ - return 0 == cmpxchgi(&spinlock->lock, 0, 1); -} - -#endif /* UV_SPINLOCK_H_ */ diff --git a/src/unix/stream.c b/src/unix/stream.c index b5b05a6a4a7..18763b4744c 100644 --- a/src/unix/stream.c +++ b/src/unix/stream.c @@ -60,12 +60,23 @@ struct uv__stream_select_s { }; #endif /* defined(__APPLE__) */ +union uv__cmsg { + struct cmsghdr hdr; + /* This cannot be larger because of the IBMi PASE limitation that + * the total size of control messages cannot exceed 256 bytes. + */ + char pad[256]; +}; + +STATIC_ASSERT(256 == sizeof(union uv__cmsg)); + static void uv__stream_connect(uv_stream_t*); static void uv__write(uv_stream_t* stream); static void uv__read(uv_stream_t* stream); static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events); static void uv__write_callbacks(uv_stream_t* stream); static size_t uv__write_req_size(uv_write_t* req); +static void uv__drain(uv_stream_t* stream); void uv__stream_init(uv_loop_t* loop, @@ -83,8 +94,8 @@ void uv__stream_init(uv_loop_t* loop, stream->accepted_fd = -1; stream->queued_fds = NULL; stream->delayed_error = 0; - QUEUE_INIT(&stream->write_queue); - QUEUE_INIT(&stream->write_completed_queue); + uv__queue_init(&stream->write_queue); + uv__queue_init(&stream->write_completed_queue); stream->write_queue_size = 0; if (loop->emfile_fd == -1) { @@ -428,15 +439,15 @@ int uv__stream_open(uv_stream_t* stream, int fd, int flags) { void uv__stream_flush_write_queue(uv_stream_t* stream, int error) { uv_write_t* req; - QUEUE* q; - while (!QUEUE_EMPTY(&stream->write_queue)) { - q = QUEUE_HEAD(&stream->write_queue); - QUEUE_REMOVE(q); + struct uv__queue* q; + while (!uv__queue_empty(&stream->write_queue)) { + q = uv__queue_head(&stream->write_queue); + uv__queue_remove(q); - req = QUEUE_DATA(q, uv_write_t, queue); + req = uv__queue_data(q, uv_write_t, queue); req->error = error; - QUEUE_INSERT_TAIL(&stream->write_completed_queue, &req->queue); + uv__queue_insert_tail(&stream->write_completed_queue, &req->queue); } } @@ -446,24 +457,14 @@ 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; } uv__stream_flush_write_queue(stream, UV_ECANCELED); uv__write_callbacks(stream); - - if (stream->shutdown_req) { - /* The ECANCELED error code is a lie, the shutdown(2) syscall is a - * fait accompli at this point. Maybe we should revisit this in v0.11. - * A possible reason for leaving it unchanged is that it informs the - * callee that the handle has been destroyed. - */ - uv__req_unregister(stream->loop, stream->shutdown_req); - stream->shutdown_req->cb(stream->shutdown_req, UV_ECANCELED); - stream->shutdown_req = NULL; - } + uv__drain(stream); assert(stream->write_queue_size == 0); } @@ -504,76 +505,34 @@ static int uv__emfile_trick(uv_loop_t* loop, int accept_fd) { } -#if defined(UV_HAVE_KQUEUE) -# define UV_DEC_BACKLOG(w) w->rcount--; -#else -# define UV_DEC_BACKLOG(w) /* no-op */ -#endif /* defined(UV_HAVE_KQUEUE) */ - - void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { uv_stream_t* stream; int err; + int fd; stream = container_of(w, uv_stream_t, io_watcher); assert(events & POLLIN); assert(stream->accepted_fd == -1); assert(!(stream->flags & UV_HANDLE_CLOSING)); - uv__io_start(stream->loop, &stream->io_watcher, POLLIN); + fd = uv__stream_fd(stream); + err = uv__accept(fd); - /* connection_cb can close the server socket while we're - * in the loop so check it on each iteration. - */ - while (uv__stream_fd(stream) != -1) { - assert(stream->accepted_fd == -1); - -#if defined(UV_HAVE_KQUEUE) - if (w->rcount <= 0) - return; -#endif /* defined(UV_HAVE_KQUEUE) */ - - err = uv__accept(uv__stream_fd(stream)); - if (err < 0) { - if (err == UV_EAGAIN || err == UV__ERR(EWOULDBLOCK)) - return; /* Not an error. */ - - if (err == UV_ECONNABORTED) - continue; /* Ignore. Nothing we can do about that. */ - - if (err == UV_EMFILE || err == UV_ENFILE) { - err = uv__emfile_trick(loop, uv__stream_fd(stream)); - if (err == UV_EAGAIN || err == UV__ERR(EWOULDBLOCK)) - break; - } - - stream->connection_cb(stream, err); - continue; - } + if (err == UV_EMFILE || err == UV_ENFILE) + err = uv__emfile_trick(loop, fd); /* Shed load. */ - UV_DEC_BACKLOG(w) - stream->accepted_fd = err; - stream->connection_cb(stream, 0); + if (err < 0) + return; - if (stream->accepted_fd != -1) { - /* The user hasn't yet accepted called uv_accept() */ - uv__io_stop(loop, &stream->io_watcher, POLLIN); - return; - } + stream->accepted_fd = err; + stream->connection_cb(stream, 0); - if (stream->type == UV_TCP && - (stream->flags & UV_HANDLE_TCP_SINGLE_ACCEPT)) { - /* Give other processes a chance to accept connections. */ - struct timespec timeout = { 0, 1 }; - nanosleep(&timeout, NULL); - } - } + if (stream->accepted_fd != -1) + /* The user hasn't yet accepted called uv_accept() */ + uv__io_stop(loop, &stream->io_watcher, POLLIN); } -#undef UV_DEC_BACKLOG - - int uv_accept(uv_stream_t* server, uv_stream_t* client) { int err; @@ -641,14 +600,16 @@ int uv_accept(uv_stream_t* server, uv_stream_t* client) { int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) { int err; - + if (uv__is_closing(stream)) { + return UV_EINVAL; + } switch (stream->type) { case UV_TCP: - err = uv_tcp_listen((uv_tcp_t*)stream, backlog, cb); + err = uv__tcp_listen((uv_tcp_t*)stream, backlog, cb); break; case UV_NAMED_PIPE: - err = uv_pipe_listen((uv_pipe_t*)stream, backlog, cb); + err = uv__pipe_listen((uv_pipe_t*)stream, backlog, cb); break; default: @@ -666,26 +627,30 @@ static void uv__drain(uv_stream_t* stream) { uv_shutdown_t* req; int err; - assert(QUEUE_EMPTY(&stream->write_queue)); - uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT); - uv__stream_osx_interrupt_select(stream); + assert(uv__queue_empty(&stream->write_queue)); + if (!(stream->flags & UV_HANDLE_CLOSING)) { + uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT); + uv__stream_osx_interrupt_select(stream); + } - /* Shutdown? */ - if ((stream->flags & UV_HANDLE_SHUTTING) && - !(stream->flags & UV_HANDLE_CLOSING) && - !(stream->flags & UV_HANDLE_SHUT)) { - assert(stream->shutdown_req); + if (!uv__is_stream_shutting(stream)) + return; - req = stream->shutdown_req; + req = stream->shutdown_req; + assert(req); + + if ((stream->flags & UV_HANDLE_CLOSING) || + !(stream->flags & UV_HANDLE_SHUT)) { stream->shutdown_req = NULL; - stream->flags &= ~UV_HANDLE_SHUTTING; - uv__req_unregister(stream->loop, req); + uv__req_unregister(stream->loop); err = 0; - if (shutdown(uv__stream_fd(stream), SHUT_WR)) + if (stream->flags & UV_HANDLE_CLOSING) + /* The user destroyed the stream before we got to do the shutdown. */ + err = UV_ECANCELED; + else if (shutdown(uv__stream_fd(stream), SHUT_WR)) err = UV__ERR(errno); - - if (err == 0) + else /* Success. */ stream->flags |= UV_HANDLE_SHUT; if (req->cb != NULL) @@ -733,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; @@ -749,7 +715,7 @@ static void uv__write_req_finish(uv_write_t* req) { uv_stream_t* stream = req->handle; /* Pop the req off tcp->write_queue. */ - QUEUE_REMOVE(&req->queue); + uv__queue_remove(&req->queue); /* Only free when there was no error. On error, we touch up write_queue_size * right before making the callback. The reason we don't do that right away @@ -766,7 +732,7 @@ static void uv__write_req_finish(uv_write_t* req) { /* Add it to the write_completed_queue where it will have its * callback called in the near future. */ - QUEUE_INSERT_TAIL(&stream->write_completed_queue, &req->queue); + uv__queue_insert_tail(&stream->write_completed_queue, &req->queue); uv__io_feed(stream->loop, &stream->io_watcher); } @@ -814,18 +780,14 @@ static int uv__try_write(uv_stream_t* stream, if (send_handle != NULL) { int fd_to_send; struct msghdr msg; - struct cmsghdr *cmsg; - union { - char data[64]; - struct cmsghdr alias; - } scratch; + union uv__cmsg cmsg; if (uv__is_closing(send_handle)) return UV_EBADF; fd_to_send = uv__handle_fd((uv_handle_t*) send_handle); - memset(&scratch, 0, sizeof(scratch)); + memset(&cmsg, 0, sizeof(cmsg)); assert(fd_to_send >= 0); @@ -835,20 +797,13 @@ static int uv__try_write(uv_stream_t* stream, msg.msg_iovlen = iovcnt; msg.msg_flags = 0; - msg.msg_control = &scratch.alias; + msg.msg_control = &cmsg.hdr; msg.msg_controllen = CMSG_SPACE(sizeof(fd_to_send)); - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof(fd_to_send)); - - /* silence aliasing warning */ - { - void* pv = CMSG_DATA(cmsg); - int* pi = pv; - *pi = fd_to_send; - } + cmsg.hdr.cmsg_level = SOL_SOCKET; + cmsg.hdr.cmsg_type = SCM_RIGHTS; + cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(fd_to_send)); + memcpy(CMSG_DATA(&cmsg.hdr), &fd_to_send, sizeof(fd_to_send)); do n = sendmsg(uv__stream_fd(stream), &msg, 0); @@ -883,18 +838,25 @@ static int uv__try_write(uv_stream_t* stream, } static void uv__write(uv_stream_t* stream) { - QUEUE* q; + struct uv__queue* q; uv_write_t* req; ssize_t n; + int count; assert(uv__stream_fd(stream) >= 0); + /* Prevent loop starvation when the consumer of this stream read as fast as + * (or faster than) we can write it. This `count` mechanism does not need to + * change even if we switch to edge-triggered I/O. + */ + count = 32; + for (;;) { - if (QUEUE_EMPTY(&stream->write_queue)) + if (uv__queue_empty(&stream->write_queue)) return; - q = QUEUE_HEAD(&stream->write_queue); - req = QUEUE_DATA(q, uv_write_t, queue); + q = uv__queue_head(&stream->write_queue); + req = uv__queue_data(q, uv_write_t, queue); assert(req->handle == stream); n = uv__try_write(stream, @@ -907,10 +869,13 @@ static void uv__write(uv_stream_t* stream) { req->send_handle = NULL; if (uv__write_req_update(stream, req, n)) { uv__write_req_finish(req); - return; /* TODO(bnoordhuis) Start trying to write the next request. */ + if (count-- > 0) + continue; /* Start trying to write the next request. */ + + return; } } else if (n != UV_EAGAIN) - break; + goto error; /* If this is a blocking stream, try again. */ if (stream->flags & UV_HANDLE_BLOCKING_WRITES) @@ -925,8 +890,8 @@ static void uv__write(uv_stream_t* stream) { return; } +error: req->error = n; - // XXX(jwn): this must call uv__stream_flush_write_queue(stream, n) here, since we won't generate any more events uv__write_req_finish(req); uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT); uv__stream_osx_interrupt_select(stream); @@ -935,20 +900,20 @@ static void uv__write(uv_stream_t* stream) { static void uv__write_callbacks(uv_stream_t* stream) { uv_write_t* req; - QUEUE* q; - QUEUE pq; + struct uv__queue* q; + struct uv__queue pq; - if (QUEUE_EMPTY(&stream->write_completed_queue)) + if (uv__queue_empty(&stream->write_completed_queue)) return; - QUEUE_MOVE(&stream->write_completed_queue, &pq); + uv__queue_move(&stream->write_completed_queue, &pq); - while (!QUEUE_EMPTY(&pq)) { + while (!uv__queue_empty(&pq)) { /* Pop a req off write_completed_queue. */ - q = QUEUE_HEAD(&pq); - req = QUEUE_DATA(q, uv_write_t, queue); - QUEUE_REMOVE(q); - uv__req_unregister(stream->loop, req); + q = uv__queue_head(&pq); + req = uv__queue_data(q, uv_write_t, queue); + uv__queue_remove(q); + uv__req_unregister(stream->loop); if (req->bufs != NULL) { stream->write_queue_size -= uv__write_req_size(req); @@ -964,49 +929,6 @@ static void uv__write_callbacks(uv_stream_t* stream) { } -uv_handle_type uv__handle_type(int fd) { - struct sockaddr_storage ss; - socklen_t sslen; - socklen_t len; - int type; - - memset(&ss, 0, sizeof(ss)); - sslen = sizeof(ss); - - if (getsockname(fd, (struct sockaddr*)&ss, &sslen)) - return UV_UNKNOWN_HANDLE; - - len = sizeof type; - - if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &len)) - return UV_UNKNOWN_HANDLE; - - if (type == SOCK_STREAM) { -#if defined(_AIX) || defined(__DragonFly__) - /* on AIX/DragonFly the getsockname call returns an empty sa structure - * for sockets of type AF_UNIX. For all other types it will - * return a properly filled in structure. - */ - if (sslen == 0) - return UV_NAMED_PIPE; -#endif - switch (ss.ss_family) { - case AF_UNIX: - return UV_NAMED_PIPE; - case AF_INET: - case AF_INET6: - return UV_TCP; - } - } - - if (type == SOCK_DGRAM && - (ss.ss_family == AF_INET || ss.ss_family == AF_INET6)) - return UV_UDP; - - return UV_UNKNOWN_HANDLE; -} - - static void uv__stream_eof(uv_stream_t* stream, const uv_buf_t* buf) { stream->flags |= UV_HANDLE_READ_EOF; stream->flags &= ~UV_HANDLE_READING; @@ -1056,76 +978,55 @@ static int uv__stream_queue_fd(uv_stream_t* stream, int fd) { } -#if defined(__PASE__) -/* on IBMi PASE the control message length can not exceed 256. */ -# define UV__CMSG_FD_COUNT 60 -#else -# define UV__CMSG_FD_COUNT 64 -#endif -#define UV__CMSG_FD_SIZE (UV__CMSG_FD_COUNT * sizeof(int)) - - 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 count; + err = 0; for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) { - char* start; - char* end; - int err; - void* pv; - int* pi; - unsigned int i; - unsigned int count; - if (cmsg->cmsg_type != SCM_RIGHTS) { fprintf(stderr, "ignoring non-SCM_RIGHTS ancillary data: %d\n", cmsg->cmsg_type); continue; } - /* silence aliasing warning */ - pv = CMSG_DATA(cmsg); - pi = pv; - - /* Count available fds */ - start = (char*) cmsg; - end = (char*) cmsg + cmsg->cmsg_len; - count = 0; - while (start + CMSG_LEN(count * sizeof(*pi)) < end) - count++; - assert(start + CMSG_LEN(count * sizeof(*pi)) == end); - - for (i = 0; i < count; i++) { - /* Already has accepted fd, queue now */ - if (stream->accepted_fd != -1) { - err = uv__stream_queue_fd(stream, pi[i]); - if (err != 0) { - /* Close rest */ - for (; i < count; i++) - uv__close(pi[i]); - return err; - } - } else { - stream->accepted_fd = pi[i]; + assert(cmsg->cmsg_len >= CMSG_LEN(0)); + count = cmsg->cmsg_len - CMSG_LEN(0); + assert(count % sizeof(fd) == 0); + count /= sizeof(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; } -#ifdef __clang__ -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wgnu-folding-constant" -# pragma clang diagnostic ignored "-Wvla-extension" -#endif - static void uv__read(uv_stream_t* stream) { uv_buf_t buf; ssize_t nread; struct msghdr msg; - char cmsg_space[CMSG_SPACE(UV__CMSG_FD_SIZE)]; + union uv__cmsg cmsg; int count; int err; int is_ipc; @@ -1171,8 +1072,8 @@ static void uv__read(uv_stream_t* stream) { msg.msg_name = NULL; msg.msg_namelen = 0; /* Set up to receive a descriptor even if one isn't in the message */ - msg.msg_controllen = sizeof(cmsg_space); - msg.msg_control = cmsg_space; + msg.msg_controllen = sizeof(cmsg); + msg.msg_control = &cmsg.hdr; do { nread = uv__recvmsg(uv__stream_fd(stream), &msg, 0); @@ -1256,14 +1157,6 @@ static void uv__read(uv_stream_t* stream) { } -#ifdef __clang__ -# pragma clang diagnostic pop -#endif - -#undef UV__CMSG_FD_COUNT -#undef UV__CMSG_FD_SIZE - - int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) { assert(stream->type == UV_TCP || stream->type == UV_TTY || @@ -1271,23 +1164,23 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) { if (!(stream->flags & UV_HANDLE_WRITABLE) || stream->flags & UV_HANDLE_SHUT || - stream->flags & UV_HANDLE_SHUTTING || + uv__is_stream_shutting(stream) || uv__is_closing(stream)) { return UV_ENOTCONN; } assert(uv__stream_fd(stream) >= 0); - /* Initialize request */ + /* Initialize request. The `shutdown(2)` call will always be deferred until + * `uv__drain`, just before the callback is run. */ uv__req_init(stream->loop, req, UV_SHUTDOWN); req->handle = stream; req->cb = cb; stream->shutdown_req = req; - stream->flags |= UV_HANDLE_SHUTTING; stream->flags &= ~UV_HANDLE_WRITABLE; - uv__io_start(stream->loop, &stream->io_watcher, POLLOUT); - uv__stream_osx_interrupt_select(stream); + if (uv__queue_empty(&stream->write_queue)) + uv__io_feed(stream->loop, &stream->io_watcher); return 0; } @@ -1339,7 +1232,7 @@ static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { uv__write_callbacks(stream); /* Write queue drained. */ - if (QUEUE_EMPTY(&stream->write_queue)) + if (uv__queue_empty(&stream->write_queue)) uv__drain(stream); } } @@ -1380,9 +1273,9 @@ 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 || QUEUE_EMPTY(&stream->write_queue)) { + if (error < 0 || uv__queue_empty(&stream->write_queue)) { uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT); } @@ -1464,7 +1357,7 @@ int uv_write2(uv_write_t* req, req->handle = stream; req->error = 0; req->send_handle = send_handle; - QUEUE_INIT(&req->queue); + uv__queue_init(&req->queue); req->bufs = req->bufsml; if (nbufs > ARRAY_SIZE(req->bufsml)) @@ -1479,7 +1372,7 @@ int uv_write2(uv_write_t* req, stream->write_queue_size += uv__count_bufs(bufs, nbufs); /* Append the request to write_queue. */ - QUEUE_INSERT_TAIL(&stream->write_queue, &req->queue); + uv__queue_insert_tail(&stream->write_queue, &req->queue); /* If the queue was empty when this function began, we should attempt to * do the write immediately. Otherwise start the write_watcher and wait diff --git a/src/unix/sunos.c b/src/unix/sunos.c index 2bf297e5d96..2d6bae79604 100644 --- a/src/unix/sunos.c +++ b/src/unix/sunos.c @@ -148,13 +148,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { struct port_event events[1024]; struct port_event* pe; struct timespec spec; - QUEUE* q; + struct uv__queue* q; uv__io_t* w; sigset_t* pset; sigset_t set; uint64_t base; uint64_t diff; - uint64_t idle_poll; unsigned int nfds; unsigned int i; int saved_errno; @@ -167,16 +166,16 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int reset_timeout; if (loop->nfds == 0) { - assert(QUEUE_EMPTY(&loop->watcher_queue)); + assert(uv__queue_empty(&loop->watcher_queue)); return; } - while (!QUEUE_EMPTY(&loop->watcher_queue)) { - q = QUEUE_HEAD(&loop->watcher_queue); - QUEUE_REMOVE(q); - QUEUE_INIT(q); + while (!uv__queue_empty(&loop->watcher_queue)) { + q = uv__queue_head(&loop->watcher_queue); + uv__queue_remove(q); + uv__queue_init(q); - w = QUEUE_DATA(q, uv__io_t, watcher_queue); + w = uv__queue_data(q, uv__io_t, watcher_queue); assert(w->pevents != 0); if (port_associate(loop->backend_fd, @@ -317,13 +316,15 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { continue; /* Disabled by callback. */ /* Events Ports operates in oneshot mode, rearm timer on next run. */ - if (w->pevents != 0 && QUEUE_EMPTY(&w->watcher_queue)) - QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); + if (w->pevents != 0 && uv__queue_empty(&w->watcher_queue)) + uv__queue_insert_tail(&loop->watcher_queue, &w->watcher_queue); } + uv__metrics_inc_events(loop, nevents); if (reset_timeout != 0) { timeout = user_timeout; reset_timeout = 0; + uv__metrics_inc_events_waiting(loop, nevents); } if (have_signals != 0) { @@ -416,6 +417,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + void uv_loadavg(double avg[3]) { (void) getloadavg(avg, 3); } @@ -424,7 +430,7 @@ void uv_loadavg(double avg[3]) { #if defined(PORT_SOURCE_FILE) static int uv__fs_event_rearm(uv_fs_event_t *handle) { - if (handle->fd == -1) + if (handle->fd == PORT_DELETED) return UV_EBADF; if (port_associate(handle->loop->fs_fd, @@ -475,6 +481,12 @@ static void uv__fs_event_read(uv_loop_t* loop, handle = (uv_fs_event_t*) pe.portev_user; assert((r == 0) && "unexpected port_get() error"); + if (uv__is_closing(handle)) { + uv__handle_stop(handle); + uv__make_close_pending((uv_handle_t*) handle); + break; + } + events = 0; if (pe.portev_events & (FILE_ATTRIB | FILE_MODIFIED)) events |= UV_CHANGE; @@ -542,12 +554,14 @@ int uv_fs_event_start(uv_fs_event_t* handle, } -int uv_fs_event_stop(uv_fs_event_t* handle) { +static int uv__fs_event_stop(uv_fs_event_t* handle) { + int ret = 0; + if (!uv__is_active(handle)) return 0; - if (handle->fd == PORT_FIRED || handle->fd == PORT_LOADED) { - port_dissociate(handle->loop->fs_fd, + if (handle->fd == PORT_LOADED) { + ret = port_dissociate(handle->loop->fs_fd, PORT_SOURCE_FILE, (uintptr_t) &handle->fo); } @@ -556,13 +570,28 @@ int uv_fs_event_stop(uv_fs_event_t* handle) { uv__free(handle->path); handle->path = NULL; handle->fo.fo_name = NULL; - uv__handle_stop(handle); + if (ret == 0) + uv__handle_stop(handle); + return ret; +} + +int uv_fs_event_stop(uv_fs_event_t* handle) { + (void) uv__fs_event_stop(handle); return 0; } void uv__fs_event_close(uv_fs_event_t* handle) { - uv_fs_event_stop(handle); + /* + * If we were unable to dissociate the port here, then it is most likely + * that there is a pending queued event. When this happens, we don't want + * to complete the close as it will free the underlying memory for the + * handle, causing a use-after-free problem when the event is processed. + * We defer the final cleanup until after the event is consumed in + * uv__fs_event_read(). + */ + if (uv__fs_event_stop(handle) == 0) + uv__make_close_pending((uv_handle_t*) handle); } #else /* !defined(PORT_SOURCE_FILE) */ diff --git a/src/unix/tcp.c b/src/unix/tcp.c index bc0fb661f1c..98970d75278 100644 --- a/src/unix/tcp.c +++ b/src/unix/tcp.c @@ -27,17 +27,47 @@ #include #include +#include +#include -static int new_socket(uv_tcp_t* handle, int domain, unsigned long flags) { - struct sockaddr_storage saddr; +/* ifaddrs is not implemented on AIX and IBM i PASE */ +#if !defined(_AIX) +#include +#endif + +static int maybe_bind_socket(int fd) { + union uv__sockaddr s; socklen_t slen; + + slen = sizeof(s); + memset(&s, 0, sizeof(s)); + + if (getsockname(fd, &s.addr, &slen)) + return UV__ERR(errno); + + if (s.addr.sa_family == AF_INET) + if (s.in.sin_port != 0) + return 0; /* Already bound to a port. */ + + if (s.addr.sa_family == AF_INET6) + if (s.in6.sin6_port != 0) + return 0; /* Already bound to a port. */ + + /* Bind to an arbitrary port. */ + if (bind(fd, &s.addr, slen)) + return UV__ERR(errno); + + return 0; +} + + +static int new_socket(uv_tcp_t* handle, int domain, unsigned int flags) { int sockfd; int err; - err = uv__socket(domain, SOCK_STREAM, 0); - if (err < 0) - return err; - sockfd = err; + sockfd = uv__socket(domain, SOCK_STREAM, 0); + if (sockfd < 0) + return sockfd; err = uv__stream_open((uv_stream_t*) handle, sockfd, flags); if (err) { @@ -45,74 +75,44 @@ static int new_socket(uv_tcp_t* handle, int domain, unsigned long flags) { return err; } - if (flags & UV_HANDLE_BOUND) { - /* Bind this new socket to an arbitrary port */ - slen = sizeof(saddr); - memset(&saddr, 0, sizeof(saddr)); - if (getsockname(uv__stream_fd(handle), (struct sockaddr*) &saddr, &slen)) { - uv__close(sockfd); - return UV__ERR(errno); - } - - if (bind(uv__stream_fd(handle), (struct sockaddr*) &saddr, slen)) { - uv__close(sockfd); - return UV__ERR(errno); - } - } + if (flags & UV_HANDLE_BOUND) + return maybe_bind_socket(sockfd); return 0; } -static int maybe_new_socket(uv_tcp_t* handle, int domain, unsigned long flags) { - struct sockaddr_storage saddr; - socklen_t slen; +static int maybe_new_socket(uv_tcp_t* handle, int domain, unsigned int flags) { + int sockfd; + int err; - if (domain == AF_UNSPEC) { - handle->flags |= flags; - return 0; - } + if (domain == AF_UNSPEC) + goto out; - if (uv__stream_fd(handle) != -1) { + sockfd = uv__stream_fd(handle); + if (sockfd == -1) + return new_socket(handle, domain, flags); - if (flags & UV_HANDLE_BOUND) { - - if (handle->flags & UV_HANDLE_BOUND) { - /* It is already bound to a port. */ - handle->flags |= flags; - return 0; - } - - /* Query to see if tcp socket is bound. */ - slen = sizeof(saddr); - memset(&saddr, 0, sizeof(saddr)); - if (getsockname(uv__stream_fd(handle), (struct sockaddr*) &saddr, &slen)) - return UV__ERR(errno); - - if ((saddr.ss_family == AF_INET6 && - ((struct sockaddr_in6*) &saddr)->sin6_port != 0) || - (saddr.ss_family == AF_INET && - ((struct sockaddr_in*) &saddr)->sin_port != 0)) { - /* Handle is already bound to a port. */ - handle->flags |= flags; - return 0; - } - - /* Bind to arbitrary port */ - if (bind(uv__stream_fd(handle), (struct sockaddr*) &saddr, slen)) - return UV__ERR(errno); - } + if (!(flags & UV_HANDLE_BOUND)) + goto out; - handle->flags |= flags; - return 0; - } + if (handle->flags & UV_HANDLE_BOUND) + goto out; /* Already bound to a port. */ + + err = maybe_bind_socket(sockfd); + if (err) + return err; + +out: - return new_socket(handle, domain, flags); + handle->flags |= flags; + return 0; } int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* tcp, unsigned int flags) { int domain; + int err; /* Use the lower 8 bits for the domain */ domain = flags & 0xFF; @@ -129,9 +129,12 @@ int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* tcp, unsigned int flags) { */ if (domain != AF_UNSPEC) { - int err = maybe_new_socket(tcp, domain, 0); + err = new_socket(tcp, domain, 0); if (err) { - QUEUE_REMOVE(&tcp->handle_queue); + uv__queue_remove(&tcp->handle_queue); + if (tcp->io_watcher.fd != -1) + uv__close(tcp->io_watcher.fd); + tcp->io_watcher.fd = -1; return err; } } @@ -164,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) { @@ -184,14 +193,15 @@ int uv__tcp_bind(uv_tcp_t* tcp, #endif errno = 0; - if (bind(tcp->io_watcher.fd, addr, addrlen) && errno != EADDRINUSE) { + err = bind(tcp->io_watcher.fd, addr, addrlen); + if (err == -1 && errno != EADDRINUSE) { if (errno == EAFNOSUPPORT) /* OSX, other BSDs and SunoS fail with EAFNOSUPPORT when binding a * socket created with AF_INET to an AF_INET6 address or vice versa. */ return UV_EINVAL; return UV__ERR(errno); } - tcp->delayed_error = UV__ERR(errno); + tcp->delayed_error = (err == -1) ? UV__ERR(errno) : 0; tcp->flags |= UV_HANDLE_BOUND; if (addr->sa_family == AF_INET6) @@ -201,11 +211,75 @@ int uv__tcp_bind(uv_tcp_t* tcp, } +static int uv__is_ipv6_link_local(const struct sockaddr* addr) { + const struct sockaddr_in6* a6; + uint8_t b[2]; + + if (addr->sa_family != AF_INET6) + return 0; + + a6 = (const struct sockaddr_in6*) addr; + memcpy(b, &a6->sin6_addr, sizeof(b)); + + return b[0] == 0xFE && b[1] == 0x80; +} + + +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; + + if (getifaddrs(&ifa)) + return 0; + + for (p = ifa; p != NULL; p = p->ifa_next) + if (p->ifa_addr != NULL) + if (uv__is_ipv6_link_local(p->ifa_addr)) + break; + + rv = 0; + if (p != NULL) { + a6 = (struct sockaddr_in6*) p->ifa_addr; + rv = a6->sin6_scope_id; + } + + freeifaddrs(ifa); +#endif /* defined(_AIX) */ + + return rv; +} + + int uv__tcp_connect(uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr, unsigned int addrlen, uv_connect_cb cb) { + struct sockaddr_in6 tmp6; int err; int r; @@ -223,6 +297,14 @@ int uv__tcp_connect(uv_connect_t* req, if (err) return err; + if (uv__is_ipv6_link_local(addr)) { + memcpy(&tmp6, addr, sizeof(tmp6)); + if (tmp6.sin6_scope_id == 0) { + tmp6.sin6_scope_id = uv__ipv6_link_local_scope_id(); + addr = (void*) &tmp6; + } + } + do { errno = 0; r = connect(uv__stream_fd(handle), addr, addrlen); @@ -255,7 +337,7 @@ int uv__tcp_connect(uv_connect_t* req, uv__req_init(handle->loop, req, UV_CONNECT); req->cb = cb; req->handle = (uv_stream_t*) handle; - QUEUE_INIT(&req->queue); + uv__queue_init(&req->queue); handle->connect_req = req; uv__io_start(handle->loop, &handle->io_watcher, POLLOUT); @@ -316,37 +398,33 @@ int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb) { struct linger l = { 1, 0 }; /* Disallow setting SO_LINGER to zero due to some platform inconsistencies */ - if (handle->flags & UV_HANDLE_SHUTTING) + if (uv__is_stream_shutting(handle)) return UV_EINVAL; fd = uv__stream_fd(handle); - if (0 != setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l))) - return UV__ERR(errno); + if (0 != setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l))) { + if (errno == EINVAL) { + /* Open Group Specifications Issue 7, 2018 edition states that + * EINVAL may mean the socket has been shut down already. + * Behavior observed on Solaris, illumos and macOS. */ + errno = 0; + } else { + return UV__ERR(errno); + } + } uv_close((uv_handle_t*) handle, close_cb); return 0; } -int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { - static int single_accept_cached = -1; - unsigned long flags; - int single_accept; +int uv__tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { + unsigned int flags; int err; if (tcp->delayed_error) return tcp->delayed_error; - single_accept = uv__load_relaxed(&single_accept_cached); - if (single_accept == -1) { - const char* val = getenv("UV_TCP_SINGLE_ACCEPT"); - single_accept = (val != NULL && atoi(val) != 0); /* Off by default. */ - uv__store_relaxed(&single_accept_cached, single_accept); - } - - if (single_accept) - tcp->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT; - flags = 0; #if defined(__MVS__) /* on zOS the listen call does not bind automatically @@ -380,32 +458,121 @@ 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; + + 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); + + 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 (on) { - int intvl = 1; /* 1 second; same as default on Win32 */ - int cnt = 10; /* 10 retries; same as hardcoded on Win32 */ - if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay))) - return UV__ERR(errno); - if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl))) - return UV__ERR(errno); - if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt))) - return UV__ERR(errno); - } + 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 - /* 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. */ -#if defined(TCP_KEEPALIVE) && !defined(__sun) - if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay))) +#ifdef TCP_KEEPINTVL + 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 +#ifdef TCP_KEEPCNT + cnt = 10; /* 10 retries; same as hardcoded on Win32 */ + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt))) + return UV__ERR(errno); +#endif + +#endif /* !defined(__sun) */ return 0; } @@ -451,10 +618,6 @@ int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay) { int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) { - if (enable) - handle->flags &= ~UV_HANDLE_TCP_SINGLE_ACCEPT; - else - handle->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT; return 0; } @@ -467,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; diff --git a/src/unix/thread.c b/src/unix/thread.c index c46450cc661..e51c290466d 100644 --- a/src/unix/thread.c +++ b/src/unix/thread.c @@ -23,6 +23,9 @@ #include "internal.h" #include +#ifdef __OpenBSD__ +#include +#endif #include #include @@ -41,132 +44,59 @@ #include /* gnu_get_libc_version() */ #endif -#undef NANOSEC -#define NANOSEC ((uint64_t) 1e9) - -#if defined(PTHREAD_BARRIER_SERIAL_THREAD) -STATIC_ASSERT(sizeof(uv_barrier_t) == sizeof(pthread_barrier_t)); +#if defined(__linux__) +# include +# define uv__cpu_set_t cpu_set_t +#elif defined(__FreeBSD__) +# include +# include +# include +# define uv__cpu_set_t cpuset_t #endif -/* Note: guard clauses should match uv_barrier_t's in include/uv/unix.h. */ -#if defined(_AIX) || \ - defined(__OpenBSD__) || \ - !defined(PTHREAD_BARRIER_SERIAL_THREAD) -int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) { - struct _uv_barrier* b; - int rc; - - if (barrier == NULL || count == 0) - return UV_EINVAL; - - b = uv__malloc(sizeof(*b)); - if (b == NULL) - return UV_ENOMEM; - - b->in = 0; - b->out = 0; - b->threshold = count; - - rc = uv_mutex_init(&b->mutex); - if (rc != 0) - goto error2; - - rc = uv_cond_init(&b->cond); - if (rc != 0) - goto error; - - barrier->b = b; - return 0; - -error: - uv_mutex_destroy(&b->mutex); -error2: - uv__free(b); - return rc; -} - -int uv_barrier_wait(uv_barrier_t* barrier) { - struct _uv_barrier* b; - int last; - - if (barrier == NULL || barrier->b == NULL) - return UV_EINVAL; - - b = barrier->b; - uv_mutex_lock(&b->mutex); +#undef NANOSEC +#define NANOSEC ((uint64_t) 1e9) - if (++b->in == b->threshold) { - b->in = 0; - b->out = b->threshold; - uv_cond_signal(&b->cond); - } else { - do - uv_cond_wait(&b->cond, &b->mutex); - while (b->in != 0); - } +/* Musl's PTHREAD_STACK_MIN is 2 KB on all architectures, which is + * too small to safely receive signals on. + * + * Musl's PTHREAD_STACK_MIN + MINSIGSTKSZ == 8192 on arm64 (which has + * the largest MINSIGSTKSZ of the architectures that musl supports) so + * let's use that as a lower bound. + * + * We use a hardcoded value because PTHREAD_STACK_MIN + MINSIGSTKSZ + * is between 28 and 133 KB when compiling against glibc, depending + * on the architecture. + */ +static size_t uv__min_stack_size(void) { + static const size_t min = 8192; - last = (--b->out == 0); - uv_cond_signal(&b->cond); +#ifdef PTHREAD_STACK_MIN /* Not defined on NetBSD. */ + if (min < (size_t) PTHREAD_STACK_MIN) + return PTHREAD_STACK_MIN; +#endif /* PTHREAD_STACK_MIN */ - uv_mutex_unlock(&b->mutex); - return last; + return min; } -void uv_barrier_destroy(uv_barrier_t* barrier) { - struct _uv_barrier* b; - - b = barrier->b; - uv_mutex_lock(&b->mutex); - - assert(b->in == 0); - while (b->out != 0) - uv_cond_wait(&b->cond, &b->mutex); - - if (b->in != 0) - abort(); - - uv_mutex_unlock(&b->mutex); - uv_mutex_destroy(&b->mutex); - uv_cond_destroy(&b->cond); - - uv__free(barrier->b); - barrier->b = NULL; -} - +/* On Linux, threads created by musl have a much smaller stack than threads + * created by glibc (80 vs. 2048 or 4096 kB.) Follow glibc for consistency. + */ +static size_t uv__default_stack_size(void) { +#if !defined(__linux__) + return 0; +#elif defined(__PPC__) || defined(__ppc__) || defined(__powerpc__) + return 4 << 20; /* glibc default. */ #else - -int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) { - return UV__ERR(pthread_barrier_init(barrier, NULL, count)); -} - - -int uv_barrier_wait(uv_barrier_t* barrier) { - int rc; - - rc = pthread_barrier_wait(barrier); - if (rc != 0) - if (rc != PTHREAD_BARRIER_SERIAL_THREAD) - abort(); - - return rc == PTHREAD_BARRIER_SERIAL_THREAD; -} - - -void uv_barrier_destroy(uv_barrier_t* barrier) { - if (pthread_barrier_destroy(barrier)) - abort(); -} - + return 2 << 20; /* glibc default. */ #endif +} /* On MacOS, threads other than the main thread are created with a reduced * stack size by default. Adjust to RLIMIT_STACK aligned to the page size. - * - * On Linux, threads created by musl have a much smaller stack than threads - * created by glibc (80 vs. 2048 or 4096 kB.) Follow glibc for consistency. */ size_t uv__thread_stack_size(void) { #if defined(__APPLE__) || defined(__linux__) @@ -176,34 +106,20 @@ size_t uv__thread_stack_size(void) { * the system call wrapper invokes the wrong system call. Don't treat * that as fatal, just use the default stack size instead. */ - if (0 == getrlimit(RLIMIT_STACK, &lim) && lim.rlim_cur != RLIM_INFINITY) { - /* pthread_attr_setstacksize() expects page-aligned values. */ - lim.rlim_cur -= lim.rlim_cur % (rlim_t) getpagesize(); - - /* Musl's PTHREAD_STACK_MIN is 2 KB on all architectures, which is - * too small to safely receive signals on. - * - * Musl's PTHREAD_STACK_MIN + MINSIGSTKSZ == 8192 on arm64 (which has - * the largest MINSIGSTKSZ of the architectures that musl supports) so - * let's use that as a lower bound. - * - * We use a hardcoded value because PTHREAD_STACK_MIN + MINSIGSTKSZ - * is between 28 and 133 KB when compiling against glibc, depending - * on the architecture. - */ - if (lim.rlim_cur >= 8192) - if (lim.rlim_cur >= PTHREAD_STACK_MIN) - return lim.rlim_cur; - } -#endif + if (getrlimit(RLIMIT_STACK, &lim)) + return uv__default_stack_size(); -#if !defined(__linux__) - return 0; -#elif defined(__PPC__) || defined(__ppc__) || defined(__powerpc__) - return 4 << 20; /* glibc default. */ -#else - return 2 << 20; /* glibc default. */ + if (lim.rlim_cur == RLIM_INFINITY) + return uv__default_stack_size(); + + /* pthread_attr_setstacksize() expects page-aligned values. */ + lim.rlim_cur -= lim.rlim_cur % (rlim_t) getpagesize(); + + if (lim.rlim_cur >= (rlim_t) uv__min_stack_size()) + return lim.rlim_cur; #endif + + return uv__default_stack_size(); } @@ -213,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), @@ -222,6 +144,7 @@ int uv_thread_create_ex(uv_thread_t* tid, pthread_attr_t attr_storage; size_t pagesize; size_t stack_size; + size_t min_stack_size; /* Used to squelch a -Wcast-function-type warning. */ union { @@ -239,10 +162,9 @@ int uv_thread_create_ex(uv_thread_t* tid, pagesize = (size_t)getpagesize(); /* Round up to the nearest page boundary. */ stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1); -#ifdef PTHREAD_STACK_MIN - if (stack_size < PTHREAD_STACK_MIN) - stack_size = PTHREAD_STACK_MIN; -#endif + min_stack_size = uv__min_stack_size(); + if (stack_size < min_stack_size) + stack_size = min_stack_size; } if (stack_size > 0) { @@ -264,6 +186,106 @@ int uv_thread_create_ex(uv_thread_t* tid, return UV__ERR(err); } +#if UV__CPU_AFFINITY_SUPPORTED + +int uv_thread_setaffinity(uv_thread_t* tid, + char* cpumask, + char* oldmask, + size_t mask_size) { + int i; + int r; + uv__cpu_set_t cpuset; + int cpumasksize; + + cpumasksize = uv_cpumask_size(); + if (cpumasksize < 0) + return cpumasksize; + if (mask_size < (size_t)cpumasksize) + return UV_EINVAL; + + if (oldmask != NULL) { + r = uv_thread_getaffinity(tid, oldmask, mask_size); + if (r < 0) + return r; + } + + CPU_ZERO(&cpuset); + for (i = 0; i < cpumasksize; i++) + if (cpumask[i]) + CPU_SET(i, &cpuset); + +#if defined(__ANDROID__) + if (sched_setaffinity(pthread_gettid_np(*tid), sizeof(cpuset), &cpuset)) + r = errno; + else + r = 0; +#else + r = pthread_setaffinity_np(*tid, sizeof(cpuset), &cpuset); +#endif + + return UV__ERR(r); +} + + +int uv_thread_getaffinity(uv_thread_t* tid, + char* cpumask, + size_t mask_size) { + int r; + int i; + uv__cpu_set_t cpuset; + int cpumasksize; + + cpumasksize = uv_cpumask_size(); + if (cpumasksize < 0) + return cpumasksize; + if (mask_size < (size_t)cpumasksize) + return UV_EINVAL; + + CPU_ZERO(&cpuset); +#if defined(__ANDROID__) + if (sched_getaffinity(pthread_gettid_np(*tid), sizeof(cpuset), &cpuset)) + r = errno; + else + r = 0; +#else + r = pthread_getaffinity_np(*tid, sizeof(cpuset), &cpuset); +#endif + if (r) + return UV__ERR(r); + for (i = 0; i < cpumasksize; i++) + cpumask[i] = !!CPU_ISSET(i, &cpuset); + + return 0; +} +#else +int uv_thread_setaffinity(uv_thread_t* tid, + char* cpumask, + char* oldmask, + size_t mask_size) { + return UV_ENOTSUP; +} + + +int uv_thread_getaffinity(uv_thread_t* tid, + char* cpumask, + size_t mask_size) { + return UV_ENOTSUP; +} +#endif /* defined(__linux__) || defined(UV_BSD_H) */ + +int uv_thread_getcpu(void) { +#if UV__CPU_AFFINITY_SUPPORTED + int cpu; + + cpu = sched_getcpu(); + if (cpu < 0) + return UV__ERR(errno); + + return cpu; +#else + return UV_ENOTSUP; +#endif +} uv_thread_t uv_thread_self(void) { return pthread_self(); @@ -278,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) @@ -565,7 +599,7 @@ static void uv__custom_sem_post(uv_sem_t* sem_) { uv_mutex_lock(&sem->mutex); sem->value++; if (sem->value == 1) - uv_cond_signal(&sem->cond); + uv_cond_signal(&sem->cond); /* Release one to replace us. */ uv_mutex_unlock(&sem->mutex); } @@ -776,11 +810,33 @@ void uv_cond_broadcast(uv_cond_t* cond) { abort(); } +#if defined(__APPLE__) && defined(__MACH__) + +void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) { + int r; + + errno = 0; + r = pthread_cond_wait(cond, mutex); + + /* Workaround for a bug in OS X at least up to 13.6 + * See https://github.com/libuv/libuv/issues/4165 + */ + if (r == EINVAL) + if (errno == EBUSY) + return; + + if (r) + abort(); +} + +#else /* !(defined(__APPLE__) && defined(__MACH__)) */ + void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) { if (pthread_cond_wait(cond, mutex)) abort(); } +#endif int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { int r; @@ -840,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 diff --git a/src/unix/tty.c b/src/unix/tty.c index 9442cf16360..793054ba5a9 100644 --- a/src/unix/tty.c +++ b/src/unix/tty.c @@ -21,8 +21,8 @@ #include "uv.h" #include "internal.h" -#include "spinlock.h" +#include #include #include #include @@ -64,11 +64,24 @@ static int isreallyatty(int file) { static int orig_termios_fd = -1; static struct termios orig_termios; -static uv_spinlock_t termios_spinlock = UV_SPINLOCK_INITIALIZER; +static _Atomic int termios_spinlock; + +int uv__tcsetattr(int fd, int how, const struct termios *term) { + int rc; + + do + rc = tcsetattr(fd, how, term); + while (rc == -1 && errno == EINTR); + + if (rc == -1) + return UV__ERR(errno); + + return 0; +} static int uv__tty_is_slave(const int fd) { int result; -#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#if defined(__linux__) || defined(__FreeBSD__) int dummy; result = ioctl(fd, TIOCGPTN, &dummy) != 0; @@ -100,7 +113,7 @@ static int uv__tty_is_slave(const int fd) { } /* Lookup stat structure behind the file descriptor. */ - if (fstat(fd, &sb) != 0) + if (uv__fstat(fd, &sb) != 0) abort(); /* Assert character device. */ @@ -209,7 +222,7 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int unused) { int rc = r; if (newfd != -1) uv__close(newfd); - QUEUE_REMOVE(&tty->handle_queue); + uv__queue_remove(&tty->handle_queue); do r = fcntl(fd, F_SETFL, saved_flags); while (r == -1 && errno == EINTR); @@ -267,23 +280,33 @@ static void uv__tty_make_raw(struct termios* tio) { int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) { struct termios tmp; + int expected; int fd; + int rc; if (tty->mode == (int) mode) return 0; fd = uv__stream_fd(tty); if (tty->mode == UV_TTY_MODE_NORMAL && mode != UV_TTY_MODE_NORMAL) { - if (tcgetattr(fd, &tty->orig_termios)) + do + rc = tcgetattr(fd, &tty->orig_termios); + while (rc == -1 && errno == EINTR); + + if (rc == -1) return UV__ERR(errno); /* This is used for uv_tty_reset_mode() */ - uv_spinlock_lock(&termios_spinlock); + do + expected = 0; + while (!atomic_compare_exchange_strong(&termios_spinlock, &expected, 1)); + if (orig_termios_fd == -1) { orig_termios = tty->orig_termios; orig_termios_fd = fd; } - uv_spinlock_unlock(&termios_spinlock); + + atomic_store(&termios_spinlock, 0); } tmp = tty->orig_termios; @@ -304,11 +327,42 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) { } /* Apply changes after draining */ - if (tcsetattr(fd, TCSADRAIN, &tmp)) - return UV__ERR(errno); + rc = uv__tcsetattr(fd, TCSADRAIN, &tmp); + if (rc == 0) + tty->mode = mode; - tty->mode = mode; - return 0; + return rc; +} + + +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); } @@ -331,7 +385,7 @@ int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) { uv_handle_type uv_guess_handle(uv_file file) { - struct sockaddr sa; + struct sockaddr_storage ss; struct stat s; socklen_t len; int type; @@ -342,8 +396,24 @@ uv_handle_type uv_guess_handle(uv_file file) { if (isatty(file)) return UV_TTY; - if (fstat(file, &s)) + if (uv__fstat(file, &s)) { +#if defined(__PASE__) + /* On ibmi receiving RST from TCP instead of FIN immediately puts fd into + * an error state. fstat will return EINVAL, getsockname will also return + * EINVAL, even if sockaddr_storage is valid. (If file does not refer to a + * socket, ENOTSOCK is returned instead.) + * In such cases, we will permit the user to open the connection as uv_tcp + * still, so that the user can get immediately notified of the error in + * their read callback and close this fd. + */ + len = sizeof(ss); + if (getsockname(file, (struct sockaddr*) &ss, &len)) { + if (errno == EINVAL) + return UV_TCP; + } +#endif return UV_UNKNOWN_HANDLE; + } if (S_ISREG(s.st_mode)) return UV_FILE; @@ -357,16 +427,29 @@ uv_handle_type uv_guess_handle(uv_file file) { if (!S_ISSOCK(s.st_mode)) return UV_UNKNOWN_HANDLE; - len = sizeof(type); - if (getsockopt(file, SOL_SOCKET, SO_TYPE, &type, &len)) + len = sizeof(ss); + if (getsockname(file, (struct sockaddr*) &ss, &len)) { +#if defined(_AIX) + /* On aix receiving RST from TCP instead of FIN immediately puts fd into + * an error state. In such case getsockname will return EINVAL, even if + * sockaddr_storage is valid. + * In such cases, we will permit the user to open the connection as uv_tcp + * still, so that the user can get immediately notified of the error in + * their read callback and close this fd. + */ + if (errno == EINVAL) { + return UV_TCP; + } +#endif return UV_UNKNOWN_HANDLE; + } - len = sizeof(sa); - if (getsockname(file, &sa, &len)) + len = sizeof(type); + if (getsockopt(file, SOL_SOCKET, SO_TYPE, &type, &len)) return UV_UNKNOWN_HANDLE; if (type == SOCK_DGRAM) - if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6) + if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6) return UV_UDP; if (type == SOCK_STREAM) { @@ -379,9 +462,9 @@ uv_handle_type uv_guess_handle(uv_file file) { return UV_NAMED_PIPE; #endif /* defined(_AIX) || defined(__DragonFly__) */ - if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6) + if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6) return UV_TCP; - if (sa.sa_family == AF_UNIX) + if (ss.ss_family == AF_UNIX) return UV_NAMED_PIPE; } @@ -398,15 +481,15 @@ int uv_tty_reset_mode(void) { int err; saved_errno = errno; - if (!uv_spinlock_trylock(&termios_spinlock)) - return UV_EBUSY; /* In uv_tty_set_mode(). */ + + if (atomic_exchange(&termios_spinlock, 1)) + return UV_EBUSY; /* In uv_tty_set_mode() or uv__tty_close(). */ err = 0; if (orig_termios_fd != -1) - if (tcsetattr(orig_termios_fd, TCSANOW, &orig_termios)) - err = UV__ERR(errno); + err = uv__tcsetattr(orig_termios_fd, TCSANOW, &orig_termios); - uv_spinlock_unlock(&termios_spinlock); + atomic_store(&termios_spinlock, 0); errno = saved_errno; return err; diff --git a/src/unix/udp.c b/src/unix/udp.c index aee8d639344..67c01f7dce8 100644 --- a/src/unix/udp.c +++ b/src/unix/udp.c @@ -40,12 +40,6 @@ # define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP #endif -union uv__sockaddr { - struct sockaddr_in6 in6; - struct sockaddr_in in; - struct sockaddr addr; -}; - static void uv__udp_run_completed(uv_udp_t* handle); static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents); static void uv__udp_recvmsg(uv_udp_t* handle); @@ -53,37 +47,11 @@ 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); -#if HAVE_MMSG - -#define UV__MMSG_MAXWIDTH 20 - -static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf); -static void uv__udp_sendmmsg(uv_udp_t* handle); - -static int uv__recvmmsg_avail; -static int uv__sendmmsg_avail; -static uv_once_t once = UV_ONCE_INIT; - -static void uv__udp_mmsg_init(void) { - int ret; - int s; - s = uv__socket(AF_INET, SOCK_DGRAM, 0); - if (s < 0) - return; - ret = uv__sendmmsg(s, NULL, 0); - if (ret == 0 || errno != ENOSYS) { - uv__sendmmsg_avail = 1; - uv__recvmmsg_avail = 1; - } else { - ret = uv__recvmmsg(s, NULL, 0); - if (ret == 0 || errno != ENOSYS) - uv__recvmmsg_avail = 1; - } - uv__close(s); -} - -#endif void uv__udp_close(uv_udp_t* handle) { uv__io_close(handle->loop, &handle->io_watcher); @@ -98,18 +66,18 @@ void uv__udp_close(uv_udp_t* handle) { void uv__udp_finish_close(uv_udp_t* handle) { uv_udp_send_t* req; - QUEUE* q; + struct uv__queue* q; assert(!uv__io_active(&handle->io_watcher, POLLIN | POLLOUT)); assert(handle->io_watcher.fd == -1); - while (!QUEUE_EMPTY(&handle->write_queue)) { - q = QUEUE_HEAD(&handle->write_queue); - QUEUE_REMOVE(q); + while (!uv__queue_empty(&handle->write_queue)) { + q = uv__queue_head(&handle->write_queue); + uv__queue_remove(q); - req = QUEUE_DATA(q, uv_udp_send_t, queue); + req = uv__queue_data(q, uv_udp_send_t, queue); req->status = UV_ECANCELED; - QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue); + uv__queue_insert_tail(&handle->write_completed_queue, &req->queue); } uv__udp_run_completed(handle); @@ -126,17 +94,17 @@ void uv__udp_finish_close(uv_udp_t* handle) { static void uv__udp_run_completed(uv_udp_t* handle) { uv_udp_send_t* req; - QUEUE* q; + struct uv__queue* q; assert(!(handle->flags & UV_HANDLE_UDP_PROCESSING)); handle->flags |= UV_HANDLE_UDP_PROCESSING; - while (!QUEUE_EMPTY(&handle->write_completed_queue)) { - q = QUEUE_HEAD(&handle->write_completed_queue); - QUEUE_REMOVE(q); + while (!uv__queue_empty(&handle->write_completed_queue)) { + q = uv__queue_head(&handle->write_completed_queue); + uv__queue_remove(q); - req = QUEUE_DATA(q, uv_udp_send_t, queue); - uv__req_unregister(handle->loop, req); + req = uv__queue_data(q, uv_udp_send_t, queue); + uv__req_unregister(handle->loop); handle->send_queue_size -= uv__count_bufs(req->bufs, req->nbufs); handle->send_queue_count--; @@ -157,7 +125,7 @@ static void uv__udp_run_completed(uv_udp_t* handle) { req->send_cb(req, req->status); } - if (QUEUE_EMPTY(&handle->write_queue)) { + if (uv__queue_empty(&handle->write_queue)) { /* Pending queue and completion queue empty, stop watcher. */ uv__io_stop(handle->loop, &handle->io_watcher, POLLOUT); if (!uv__io_active(&handle->io_watcher, POLLIN)) @@ -177,17 +145,17 @@ 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); } } -#if HAVE_MMSG static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) { - struct sockaddr_in6 peers[UV__MMSG_MAXWIDTH]; - struct iovec iov[UV__MMSG_MAXWIDTH]; - struct uv__mmsghdr msgs[UV__MMSG_MAXWIDTH]; +#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) + struct sockaddr_in6 peers[20]; + struct iovec iov[ARRAY_SIZE(peers)]; + struct mmsghdr msgs[ARRAY_SIZE(peers)]; ssize_t nread; uv_buf_t chunk_buf; size_t chunks; @@ -201,6 +169,7 @@ static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) { for (k = 0; k < chunks; ++k) { iov[k].iov_base = buf->base + k * UV__UDP_DGRAM_MAXSIZE; iov[k].iov_len = UV__UDP_DGRAM_MAXSIZE; + memset(&msgs[k].msg_hdr, 0, sizeof(msgs[k].msg_hdr)); msgs[k].msg_hdr.msg_iov = iov + k; msgs[k].msg_hdr.msg_iovlen = 1; msgs[k].msg_hdr.msg_name = peers + k; @@ -208,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 = uv__recvmmsg(handle->io_watcher.fd, msgs, chunks); + 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) @@ -239,8 +215,10 @@ 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__ || __APPLE__ */ + return UV_ENOSYS; +#endif /* __linux__ || ____FreeBSD__ || __APPLE__ */ } -#endif static void uv__udp_recvmsg(uv_udp_t* handle) { struct sockaddr_storage peer; @@ -267,14 +245,12 @@ static void uv__udp_recvmsg(uv_udp_t* handle) { } assert(buf.base != NULL); -#if HAVE_MMSG if (uv_udp_using_recvmmsg(handle)) { nread = uv__udp_recvmmsg(handle, &buf); if (nread > 0) count -= nread; continue; } -#endif memset(&h, 0, sizeof(h)); memset(&peer, 0, sizeof(peer)); @@ -310,175 +286,22 @@ static void uv__udp_recvmsg(uv_udp_t* handle) { && handle->recv_cb != NULL); } -#if HAVE_MMSG -static void uv__udp_sendmmsg(uv_udp_t* handle) { - uv_udp_send_t* req; - struct uv__mmsghdr h[UV__MMSG_MAXWIDTH]; - struct uv__mmsghdr *p; - QUEUE* q; - ssize_t npkts; - size_t pkts; - size_t i; - - if (QUEUE_EMPTY(&handle->write_queue)) - return; - -write_queue_drain: - for (pkts = 0, q = QUEUE_HEAD(&handle->write_queue); - pkts < UV__MMSG_MAXWIDTH && q != &handle->write_queue; - ++pkts, q = QUEUE_HEAD(q)) { - assert(q != NULL); - req = 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 = uv__sendmmsg(handle->io_watcher.fd, h, pkts); - while (npkts == -1 && errno == EINTR); - - if (npkts < 1) { - if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) - return; - for (i = 0, q = QUEUE_HEAD(&handle->write_queue); - i < pkts && q != &handle->write_queue; - ++i, q = QUEUE_HEAD(&handle->write_queue)) { - assert(q != NULL); - req = QUEUE_DATA(q, uv_udp_send_t, queue); - assert(req != NULL); - - req->status = UV__ERR(errno); - QUEUE_REMOVE(&req->queue); - 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 = QUEUE_HEAD(&handle->write_queue); - i < (size_t)npkts && q != &handle->write_queue; - ++i, q = QUEUE_HEAD(&handle->write_queue)) { - assert(q != NULL); - req = 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. - */ - QUEUE_REMOVE(&req->queue); - QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue); - } - - /* couldn't batch everything, continue sending (jump to avoid stack growth) */ - if (!QUEUE_EMPTY(&handle->write_queue)) - goto write_queue_drain; - uv__io_feed(handle->loop, &handle->io_watcher); - return; -} -#endif - -static void uv__udp_sendmsg(uv_udp_t* handle) { - uv_udp_send_t* req; - struct msghdr h; - QUEUE* q; - ssize_t size; - -#if HAVE_MMSG - uv_once(&once, uv__udp_mmsg_init); - if (uv__sendmmsg_avail) { - uv__udp_sendmmsg(handle); - return; - } -#endif - - while (!QUEUE_EMPTY(&handle->write_queue)) { - q = QUEUE_HEAD(&handle->write_queue); - assert(q != NULL); - - req = 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. - */ - QUEUE_REMOVE(&req->queue); - QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue); - uv__io_feed(handle->loop, &handle->io_watcher); - } -} /* On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but with some additional - * refinements for programs that use multicast. + * refinements for programs that use multicast. Therefore we preferentially + * set SO_REUSEPORT over SO_REUSEADDR here, but we set SO_REUSEPORT only + * when that socket option doesn't have the capability of load balancing. + * Otherwise, we fall back to SO_REUSEADDR. * - * Linux as of 3.9 has a SO_REUSEPORT socket option but with semantics that - * are different from the BSDs: it _shares_ the port rather than steal it - * from the current listener. While useful, it's not something we can emulate - * on other platforms so we don't enable it. + * Linux as of 3.9, DragonflyBSD 3.6, AIX 7.2.5 have the SO_REUSEPORT socket + * option but with semantics that are different from the BSDs: it _shares_ + * the port rather than steals it from the current listener. While useful, + * it's not something we can emulate on other platforms so we don't enable it. * * zOS does not support getsockname with SO_REUSEPORT option when using * AF_UNIX. */ -static int uv__set_reuse(int fd) { +static int uv__sock_reuseaddr(int fd) { int yes; yes = 1; @@ -494,7 +317,8 @@ 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__) +#elif defined(SO_REUSEPORT) && !defined(__linux__) && !defined(__GNU__) && \ + !defined(__sun__) && !defined(__DragonFly__) && !defined(_AIX73) if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes))) return UV__ERR(errno); #else @@ -537,7 +361,8 @@ int uv__udp_bind(uv_udp_t* handle, int fd; /* Check for bad flags. */ - if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR | UV_UDP_LINUX_RECVERR)) + if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR | + UV_UDP_REUSEPORT | UV_UDP_LINUX_RECVERR)) return UV_EINVAL; /* Cannot set IPv6-only mode on non-IPv6 socket. */ @@ -560,7 +385,13 @@ int uv__udp_bind(uv_udp_t* handle, } if (flags & UV_UDP_REUSEADDR) { - err = uv__set_reuse(fd); + err = uv__sock_reuseaddr(fd); + if (err) + return err; + } + + if (flags & UV_UDP_REUSEPORT) { + err = uv__sock_reuseport(fd); if (err) return err; } @@ -655,16 +486,16 @@ int uv__udp_connect(uv_udp_t* handle, } /* From https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html - * Any of uv supported UNIXs kernel should be standardized, but the kernel + * Any of uv supported UNIXs kernel should be standardized, but the kernel * implementation logic not same, let's use pseudocode to explain the udp * disconnect behaviors: - * + * * Predefined stubs for pseudocode: * 1. sodisconnect: The function to perform the real udp disconnect * 2. pru_connect: The function to perform the real udp connect * 3. so: The kernel object match with socket fd * 4. addr: The sockaddr parameter from user space - * + * * BSDs: * if(sodisconnect(so) == 0) { // udp disconnect succeed * if (addr->sa_len != so->addr->sa_len) return EINVAL; @@ -694,16 +525,25 @@ int uv__udp_disconnect(uv_udp_t* handle) { #endif memset(&addr, 0, sizeof(addr)); - + #if defined(__MVS__) addr.ss_family = AF_UNSPEC; #else addr.sa_family = AF_UNSPEC; #endif - + do { errno = 0; +#ifdef __PASE__ + /* On IBMi a connectionless transport socket can be disconnected by + * either setting the addr parameter to NULL or setting the + * addr_length parameter to zero, and issuing another connect(). + * https://www.ibm.com/docs/en/i/7.4?topic=ssw_ibm_i_74/apis/connec.htm + */ + r = connect(handle->io_watcher.fd, (struct sockaddr*) NULL, 0); +#else r = connect(handle->io_watcher.fd, (struct sockaddr*) &addr, sizeof(addr)); +#endif } while (r == -1 && errno == EINTR); if (r == -1) { @@ -744,11 +584,11 @@ int uv__udp_send(uv_udp_send_t* req, empty_queue = (handle->send_queue_count == 0); uv__req_init(handle->loop, req, UV_UDP_SEND); - assert(addrlen <= sizeof(req->addr)); + assert(addrlen <= sizeof(req->u.storage)); if (addr == NULL) - req->addr.ss_family = AF_UNSPEC; + req->u.storage.ss_family = AF_UNSPEC; else - memcpy(&req->addr, addr, addrlen); + memcpy(&req->u.storage, addr, addrlen); req->send_cb = send_cb; req->handle = handle; req->nbufs = nbufs; @@ -758,14 +598,14 @@ 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; } memcpy(req->bufs, bufs, nbufs * sizeof(bufs[0])); handle->send_queue_size += uv__count_bufs(req->bufs, req->nbufs); handle->send_queue_count++; - QUEUE_INSERT_TAIL(&handle->write_queue, &req->queue); + uv__queue_insert_tail(&handle->write_queue, &req->queue); uv__handle_start(handle); if (empty_queue && !(handle->flags & UV_HANDLE_UDP_PROCESSING)) { @@ -775,7 +615,7 @@ int uv__udp_send(uv_udp_send_t* req, * away. In such cases the `io_watcher` has to be queued for asynchronous * write. */ - if (!QUEUE_EMPTY(&handle->write_queue)) + if (!uv__queue_empty(&handle->write_queue)) uv__io_start(handle->loop, &handle->io_watcher, POLLOUT); } else { uv__io_start(handle->loop, &handle->io_watcher, POLLOUT); @@ -791,10 +631,9 @@ int uv__udp_try_send(uv_udp_t* handle, const struct sockaddr* addr, unsigned int addrlen) { int err; - struct msghdr h; - ssize_t size; - assert(nbufs > 0); + if (nbufs < 1) + return UV_EINVAL; /* already sending a message */ if (handle->send_queue_count != 0) @@ -808,24 +647,11 @@ int uv__udp_try_send(uv_udp_t* handle, assert(handle->flags & UV_HANDLE_UDP_CONNECTED); } - memset(&h, 0, sizeof h); - h.msg_name = (struct sockaddr*) addr; - h.msg_namelen = addrlen; - h.msg_iov = (struct iovec*) bufs; - h.msg_iovlen = nbufs; - - do { - size = sendmsg(handle->io_watcher.fd, &h, 0); - } while (size == -1 && errno == EINTR); + err = uv__udp_sendmsg1(handle->io_watcher.fd, bufs, nbufs, addr); + if (err > 0) + return uv__count_bufs(bufs, nbufs); - if (size == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) - return UV_EAGAIN; - else - return UV__ERR(errno); - } - - return size; + return err; } @@ -927,7 +753,8 @@ static int uv__udp_set_membership6(uv_udp_t* handle, !defined(__NetBSD__) && \ !defined(__ANDROID__) && \ !defined(__DragonFly__) && \ - !defined(__QNX__) + !defined(__QNX__) && \ + !defined(__GNU__) static int uv__udp_set_source_membership4(uv_udp_t* handle, const struct sockaddr_in* multicast_addr, const char* interface_addr, @@ -1042,19 +869,17 @@ int uv__udp_init_ex(uv_loop_t* loop, handle->send_queue_size = 0; handle->send_queue_count = 0; uv__io_init(&handle->io_watcher, uv__udp_io, fd); - QUEUE_INIT(&handle->write_queue); - QUEUE_INIT(&handle->write_completed_queue); + uv__queue_init(&handle->write_queue); + uv__queue_init(&handle->write_completed_queue); return 0; } int uv_udp_using_recvmmsg(const uv_udp_t* handle) { -#if HAVE_MMSG - if (handle->flags & UV_HANDLE_UDP_RECVMMSG) { - uv_once(&once, uv__udp_mmsg_init); - return uv__recvmmsg_avail; - } +#if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) + if (handle->flags & UV_HANDLE_UDP_RECVMMSG) + return 1; #endif return 0; } @@ -1074,7 +899,7 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { if (err) return err; - err = uv__set_reuse(sock); + err = uv__sock_reuseaddr(sock); if (err) return err; @@ -1119,7 +944,8 @@ int uv_udp_set_source_membership(uv_udp_t* handle, !defined(__NetBSD__) && \ !defined(__ANDROID__) && \ !defined(__DragonFly__) && \ - !defined(__QNX__) + !defined(__QNX__) && \ + !defined(__GNU__) int err; union uv__sockaddr mcast_addr; union uv__sockaddr src_addr; @@ -1402,3 +1228,191 @@ 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__) + 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__) */ + + 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); +} diff --git a/src/uv-common.c b/src/uv-common.c index f43dd3dee7e..60ff56b9dd7 100644 --- a/src/uv-common.c +++ b/src/uv-common.c @@ -128,6 +128,39 @@ int uv_replace_allocator(uv_malloc_func malloc_func, return 0; } + +void uv_os_free_passwd(uv_passwd_t* pwd) { + if (pwd == NULL) + return; + + /* On unix, the memory for name, shell, and homedir are allocated in a single + * uv__malloc() call. The base of the pointer is stored in pwd->username, so + * that is the field that needs to be freed. + */ + uv__free(pwd->username); +#ifdef _WIN32 + uv__free(pwd->homedir); +#endif + pwd->username = NULL; + pwd->shell = NULL; + pwd->homedir = NULL; +} + + +void uv_os_free_group(uv_group_t *grp) { + if (grp == NULL) + return; + + /* The memory for is allocated in a single uv__malloc() call. The base of the + * pointer is stored in grp->members, so that is the only field that needs to + * be freed. + */ + uv__free(grp->members); + grp->members = NULL; + grp->groupname = NULL; +} + + #define XX(uc, lc) case UV_##uc: return sizeof(uv_##lc##_t); size_t uv_handle_size(uv_handle_type type) { @@ -295,7 +328,9 @@ int uv_tcp_bind(uv_tcp_t* handle, if (handle->type != UV_TCP) return UV_EINVAL; - + if (uv__is_closing(handle)) { + return UV_EINVAL; + } if (addr->sa_family == AF_INET) addrlen = sizeof(struct sockaddr_in); else if (addr->sa_family == AF_INET6) @@ -479,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) { @@ -498,17 +552,17 @@ int uv_udp_recv_stop(uv_udp_t* handle) { void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg) { - QUEUE queue; - QUEUE* q; + struct uv__queue queue; + struct uv__queue* q; uv_handle_t* h; - QUEUE_MOVE(&loop->handle_queue, &queue); - while (!QUEUE_EMPTY(&queue)) { - q = QUEUE_HEAD(&queue); - h = QUEUE_DATA(q, uv_handle_t, handle_queue); + uv__queue_move(&loop->handle_queue, &queue); + while (!uv__queue_empty(&queue)) { + q = uv__queue_head(&queue); + h = uv__queue_data(q, uv_handle_t, handle_queue); - QUEUE_REMOVE(q); - QUEUE_INSERT_TAIL(&loop->handle_queue, q); + uv__queue_remove(q); + uv__queue_insert_tail(&loop->handle_queue, q); if (h->flags & UV_HANDLE_INTERNAL) continue; walk_cb(h, arg); @@ -518,14 +572,17 @@ void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg) { static void uv__print_handles(uv_loop_t* loop, int only_active, FILE* stream) { const char* type; - QUEUE* q; + struct uv__queue* q; uv_handle_t* h; if (loop == NULL) loop = uv_default_loop(); - QUEUE_FOREACH(q, &loop->handle_queue) { - h = QUEUE_DATA(q, uv_handle_t, handle_queue); + if (stream == NULL) + stream = stderr; + + uv__queue_foreach(q, &loop->handle_queue) { + h = uv__queue_data(q, uv_handle_t, handle_queue); if (only_active && !uv__is_active(h)) continue; @@ -606,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; @@ -648,14 +708,22 @@ static unsigned int* uv__get_nbufs(uv_fs_t* req) { void uv__fs_scandir_cleanup(uv_fs_t* req) { uv__dirent_t** dents; + unsigned int* nbufs; + unsigned int i; + unsigned int n; - unsigned int* nbufs = uv__get_nbufs(req); + if (req->result >= 0) { + dents = req->ptr; + nbufs = uv__get_nbufs(req); - dents = req->ptr; - if (*nbufs > 0 && *nbufs != (unsigned int) req->result) - (*nbufs)--; - for (; *nbufs < (unsigned int) req->result; (*nbufs)++) - uv__fs_scandir_free(dents[*nbufs]); + i = 0; + if (*nbufs > 0) + i = *nbufs - 1; + + n = (unsigned int) req->result; + for (; i < n; i++) + uv__fs_scandir_free(dents[i]); + } uv__fs_scandir_free(req->ptr); req->ptr = NULL; @@ -803,7 +871,7 @@ uv_loop_t* uv_loop_new(void) { int uv_loop_close(uv_loop_t* loop) { - QUEUE* q; + struct uv__queue* q; uv_handle_t* h; #ifndef NDEBUG void* saved_data; @@ -812,8 +880,8 @@ int uv_loop_close(uv_loop_t* loop) { if (uv__has_active_reqs(loop)) return UV_EBUSY; - QUEUE_FOREACH(q, &loop->handle_queue) { - h = QUEUE_DATA(q, uv_handle_t, handle_queue); + uv__queue_foreach(q, &loop->handle_queue) { + h = uv__queue_data(q, uv_handle_t, handle_queue); if (!(h->flags & UV_HANDLE_INTERNAL)) return UV_EBUSY; } @@ -877,12 +945,17 @@ void uv_os_free_environ(uv_env_item_t* envitems, int count) { void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { +#ifdef __linux__ + (void) &count; + uv__free(cpu_infos); +#else int i; for (i = 0; i < count; i++) uv__free(cpu_infos[i].model); uv__free(cpu_infos); +#endif /* __linux__ */ } @@ -896,7 +969,7 @@ __attribute__((destructor)) void uv_library_shutdown(void) { static int was_shutdown; - if (uv__load_relaxed(&was_shutdown)) + if (uv__exchange_int_relaxed(&was_shutdown, 1)) return; uv__process_title_cleanup(); @@ -907,7 +980,6 @@ void uv_library_shutdown(void) { #else uv__threadpool_cleanup(); #endif - uv__store_relaxed(&was_shutdown, 1); } @@ -953,6 +1025,15 @@ void uv__metrics_set_provider_entry_time(uv_loop_t* loop) { } +int uv_metrics_info(uv_loop_t* loop, uv_metrics_t* metrics) { + memcpy(metrics, + &uv__get_loop_metrics(loop)->metrics, + sizeof(*metrics)); + + return 0; +} + + uint64_t uv_metrics_idle_time(uv_loop_t* loop) { uv__loop_metrics_t* loop_metrics; uint64_t entry_time; diff --git a/src/uv-common.h b/src/uv-common.h index 8a190bf8fa8..372f0c4b3ac 100644 --- a/src/uv-common.h +++ b/src/uv-common.h @@ -30,18 +30,17 @@ #include #include #include - -#if defined(_MSC_VER) && _MSC_VER < 1600 -# include "uv/stdint-msvc2008.h" -#else -# include -#endif +#include #include "uv.h" #include "uv/tree.h" #include "queue.h" #include "strscpy.h" +#ifndef _MSC_VER +# include +#endif + #if EDOM > 0 # define UV__ERR(x) (-(x)) #else @@ -53,19 +52,25 @@ extern int snprintf(char*, size_t, const char*, ...); #endif #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#define ARRAY_END(a) ((a) + ARRAY_SIZE(a)) #define container_of(ptr, type, member) \ ((type *) ((char *) (ptr) - offsetof(type, member))) +/* C11 defines static_assert to be a macro which calls _Static_assert. */ +#if defined(static_assert) +#define STATIC_ASSERT(expr) static_assert(expr, #expr) +#else #define STATIC_ASSERT(expr) \ void uv__static_assert(int static_assert_failed[1 - 2 * !(expr)]) +#endif -#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 7) -#define uv__load_relaxed(p) __atomic_load_n(p, __ATOMIC_RELAXED) -#define uv__store_relaxed(p, v) __atomic_store_n(p, v, __ATOMIC_RELAXED) +#ifdef _MSC_VER +#define uv__exchange_int_relaxed(p, v) \ + InterlockedExchangeNoFence((LONG volatile*)(p), v) #else -#define uv__load_relaxed(p) (*p) -#define uv__store_relaxed(p, v) do *p = v; while (0) +#define uv__exchange_int_relaxed(p, v) \ + atomic_exchange_explicit((_Atomic int*)(p), v, memory_order_relaxed) #endif #define UV__UDP_DGRAM_MAXSIZE (64 * 1024) @@ -83,7 +88,6 @@ enum { /* Used by streams. */ UV_HANDLE_LISTENING = 0x00000040, UV_HANDLE_CONNECTION = 0x00000080, - UV_HANDLE_SHUTTING = 0x00000100, UV_HANDLE_SHUT = 0x00000200, UV_HANDLE_READ_PARTIAL = 0x00000400, UV_HANDLE_READ_EOF = 0x00000800, @@ -130,7 +134,10 @@ enum { UV_SIGNAL_ONE_SHOT = 0x02000000, /* Only used by uv_poll_t handles. */ - UV_HANDLE_POLL_SLOW = 0x01000000 + UV_HANDLE_POLL_SLOW = 0x01000000, + + /* Only used by uv_process_t handles. */ + UV_HANDLE_REAP = 0x10000000 }; int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap); @@ -184,6 +191,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); @@ -226,13 +239,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--; \ @@ -260,6 +273,14 @@ void uv__threadpool_cleanup(void); #define uv__is_closing(h) \ (((h)->flags & (UV_HANDLE_CLOSING | UV_HANDLE_CLOSED)) != 0) +#if defined(_WIN32) +# define uv__is_stream_shutting(h) \ + (h->stream.conn.shutdown_req != NULL) +#else +# define uv__is_stream_shutting(h) \ + (h->shutdown_req != NULL) +#endif + #define uv__handle_start(h) \ do { \ if (((h)->flags & UV_HANDLE_ACTIVE) != 0) break; \ @@ -308,7 +329,7 @@ void uv__threadpool_cleanup(void); (h)->loop = (loop_); \ (h)->type = (type_); \ (h)->flags = UV_HANDLE_REF; /* Ref the loop when active. */ \ - QUEUE_INSERT_TAIL(&(loop_)->handle_queue, &(h)->handle_queue); \ + uv__queue_insert_tail(&(loop_)->handle_queue, &(h)->handle_queue); \ uv__handle_platform_init(h); \ } \ while (0) @@ -334,7 +355,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) @@ -344,6 +365,21 @@ void uv__threadpool_cleanup(void); #define uv__get_loop_metrics(loop) \ (&uv__get_internal_fields(loop)->loop_metrics) +#define uv__metrics_inc_loop_count(loop) \ + do { \ + uv__get_loop_metrics(loop)->metrics.loop_count++; \ + } while (0) + +#define uv__metrics_inc_events(loop, e) \ + do { \ + uv__get_loop_metrics(loop)->metrics.events += (e); \ + } while (0) + +#define uv__metrics_inc_events_waiting(loop, e) \ + do { \ + uv__get_loop_metrics(loop)->metrics.events_waiting += (e); \ + } while (0) + /* Allocator prototypes */ void *uv__calloc(size_t count, size_t size); char *uv__strdup(const char* s); @@ -357,6 +393,7 @@ typedef struct uv__loop_metrics_s uv__loop_metrics_t; typedef struct uv__loop_internal_fields_s uv__loop_internal_fields_t; struct uv__loop_metrics_s { + uv_metrics_t metrics; uint64_t provider_entry_time; uint64_t provider_idle_time; uv_mutex_t lock; @@ -365,9 +402,50 @@ struct uv__loop_metrics_s { void uv__metrics_update_idle_time(uv_loop_t* loop); void uv__metrics_set_provider_entry_time(uv_loop_t* loop); +#ifdef __linux__ +struct uv__iou { + uint32_t* sqhead; + uint32_t* sqtail; + uint32_t sqmask; + uint32_t* sqflags; + uint32_t* cqhead; + uint32_t* cqtail; + uint32_t cqmask; + void* sq; /* pointer to munmap() on event loop teardown */ + void* cqe; /* pointer to array of struct uv__io_uring_cqe */ + void* sqe; /* pointer to array of struct uv__io_uring_sqe */ + size_t sqlen; + size_t cqlen; + size_t maxlen; + size_t sqelen; + int ringfd; + uint32_t in_flight; +}; +#endif /* __linux__ */ + struct uv__loop_internal_fields_s { unsigned int flags; uv__loop_metrics_t loop_metrics; + int current_timeout; +#ifdef __linux__ + struct uv__iou ctl; + struct uv__iou iou; + void* inv; /* used by uv__platform_invalidate_fd() */ +#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 + #endif /* UV_COMMON_H_ */ diff --git a/src/win/async.c b/src/win/async.c index d787f6604ea..b904676e3a7 100644 --- a/src/win/async.c +++ b/src/win/async.c @@ -28,7 +28,7 @@ #include "req-inl.h" -void uv_async_endgame(uv_loop_t* loop, uv_async_t* handle) { +void uv__async_endgame(uv_loop_t* loop, uv_async_t* handle) { if (handle->flags & UV_HANDLE_CLOSING && !handle->async_sent) { assert(!(handle->flags & UV_HANDLE_CLOSED)); @@ -54,9 +54,9 @@ int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) { } -void uv_async_close(uv_loop_t* loop, uv_async_t* handle) { +void uv__async_close(uv_loop_t* loop, uv_async_t* handle) { if (!((uv_async_t*)handle)->async_sent) { - uv_want_endgame(loop, (uv_handle_t*) handle); + uv__want_endgame(loop, (uv_handle_t*) handle); } uv__handle_closing(handle); @@ -83,7 +83,7 @@ int uv_async_send(uv_async_t* handle) { } -void uv_process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle, +void uv__process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle, uv_req_t* req) { assert(handle->type == UV_ASYNC); assert(req->type == UV_WAKEUP); @@ -91,7 +91,7 @@ void uv_process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle, handle->async_sent = 0; if (handle->flags & UV_HANDLE_CLOSING) { - uv_want_endgame(loop, (uv_handle_t*)handle); + uv__want_endgame(loop, (uv_handle_t*)handle); } else if (handle->async_cb != NULL) { handle->async_cb(handle); } diff --git a/src/win/core.c b/src/win/core.c index e53a0f8e286..bc63b06673a 100644 --- a/src/win/core.c +++ b/src/win/core.c @@ -84,10 +84,12 @@ static int uv__loops_capacity; #define UV__LOOPS_CHUNK_SIZE 8 static uv_mutex_t uv__loops_lock; + static void uv__loops_init(void) { uv_mutex_init(&uv__loops_lock); } + static int uv__loops_add(uv_loop_t* loop) { uv_loop_t** new_loops; int new_capacity, i; @@ -115,6 +117,7 @@ static int uv__loops_add(uv_loop_t* loop) { return ERROR_OUTOFMEMORY; } + static void uv__loops_remove(uv_loop_t* loop) { int loop_index; int smaller_capacity; @@ -173,7 +176,7 @@ void uv__wake_all_loops(void) { uv_mutex_unlock(&uv__loops_lock); } -static void uv_init(void) { +static void uv__init(void) { /* Tell Windows that we will handle critical errors. */ SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); @@ -199,19 +202,19 @@ static void uv_init(void) { /* Fetch winapi function pointers. This must be done first because other * initialization code might need these function pointers to be loaded. */ - uv_winapi_init(); + uv__winapi_init(); /* Initialize winsock */ - uv_winsock_init(); + uv__winsock_init(); /* Initialize FS */ - uv_fs_init(); + uv__fs_init(); /* Initialize signal stuff */ - uv_signals_init(); + uv__signals_init(); /* Initialize console */ - uv_console_init(); + uv__console_init(); /* Initialize utilities */ uv__util_init(); @@ -242,6 +245,9 @@ int uv_loop_init(uv_loop_t* loop) { err = uv_mutex_init(&lfields->loop_metrics.lock); if (err) goto fail_metrics_mutex_init; + memset(&lfields->loop_metrics.metrics, + 0, + sizeof(lfields->loop_metrics.metrics)); /* To prevent uninitialized memory access, loop->time must be initialized * to zero before calling uv_update_time for the first time. @@ -249,8 +255,8 @@ int uv_loop_init(uv_loop_t* loop) { loop->time = 0; uv_update_time(loop); - QUEUE_INIT(&loop->wq); - QUEUE_INIT(&loop->handle_queue); + uv__queue_init(&loop->wq); + uv__queue_init(&loop->handle_queue); loop->active_reqs.count = 0; loop->active_handles = 0; @@ -276,9 +282,6 @@ int uv_loop_init(uv_loop_t* loop) { memset(&loop->poll_peer_sockets, 0, sizeof loop->poll_peer_sockets); - loop->active_tcp_streams = 0; - loop->active_udp_streams = 0; - loop->timer_counter = 0; loop->stop_flag = 0; @@ -327,7 +330,7 @@ void uv_update_time(uv_loop_t* loop) { void uv__once_init(void) { - uv_once(&uv_init_guard_, uv_init); + uv_once(&uv_init_guard_, uv__init); } @@ -355,7 +358,7 @@ void uv__loop_close(uv_loop_t* loop) { } uv_mutex_lock(&loop->wq_mutex); - assert(QUEUE_EMPTY(&loop->wq) && "thread pool work queue not empty!"); + assert(uv__queue_empty(&loop->wq) && "thread pool work queue not empty!"); assert(!uv__has_active_reqs(loop)); uv_mutex_unlock(&loop->wq_mutex); uv_mutex_destroy(&loop->wq_mutex); @@ -395,106 +398,33 @@ int uv_loop_fork(uv_loop_t* loop) { } -int uv_backend_timeout(const uv_loop_t* loop) { - if (loop->stop_flag != 0) - return 0; - - if (!uv__has_active_handles(loop) && !uv__has_active_reqs(loop)) - return 0; - - if (loop->pending_reqs_tail) - return 0; - - if (loop->endgame_handles) - return 0; - - if (loop->idle_handles) - return 0; - - return uv__next_timeout(loop); +static int uv__loop_alive(const uv_loop_t* loop) { + return uv__has_active_handles(loop) || + uv__has_active_reqs(loop) || + loop->pending_reqs_tail != NULL || + loop->endgame_handles != NULL; } -static void uv__poll_wine(uv_loop_t* loop, DWORD timeout) { - DWORD bytes; - ULONG_PTR key; - OVERLAPPED* overlapped; - uv_req_t* req; - int repeat; - uint64_t timeout_time; - uint64_t user_timeout; - int reset_timeout; - - timeout_time = loop->time + timeout; - - if (uv__get_internal_fields(loop)->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); - - GetQueuedCompletionStatus(loop->iocp, - &bytes, - &key, - &overlapped, - timeout); - - if (reset_timeout != 0) { - 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); +int uv_loop_alive(const uv_loop_t* loop) { + return uv__loop_alive(loop); +} - if (overlapped) { - /* 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; - } +int uv_backend_timeout(const uv_loop_t* loop) { + if (loop->stop_flag == 0 && + /* uv__loop_alive(loop) && */ + (uv__has_active_handles(loop) || uv__has_active_reqs(loop)) && + loop->pending_reqs_tail == NULL && + loop->idle_handles == NULL && + loop->endgame_handles == NULL) + return uv__next_timeout(loop); + return 0; } static void uv__poll(uv_loop_t* loop, DWORD timeout) { + uv__loop_internal_fields_t* lfields; BOOL success; uv_req_t* req; OVERLAPPED_ENTRY overlappeds[128]; @@ -503,11 +433,13 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) { int repeat; uint64_t timeout_time; uint64_t user_timeout; + uint64_t actual_timeout; int reset_timeout; + lfields = uv__get_internal_fields(loop); timeout_time = loop->time + timeout; - if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + if (lfields->flags & UV_METRICS_IDLE_TIME) { reset_timeout = 1; user_timeout = timeout; timeout = 0; @@ -516,18 +448,26 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) { } for (repeat = 0; ; repeat++) { + actual_timeout = timeout; + /* 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); - success = pGetQueuedCompletionStatusEx(loop->iocp, - overlappeds, - ARRAY_SIZE(overlappeds), - &count, - timeout, - FALSE); + /* 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; + + success = GetQueuedCompletionStatusEx(loop->iocp, + overlappeds, + ARRAY_SIZE(overlappeds), + &count, + timeout, + FALSE); if (reset_timeout != 0) { timeout = user_timeout; @@ -535,9 +475,9 @@ 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 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. + * 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. */ uv__metrics_update_idle_time(loop); @@ -547,8 +487,12 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) { * meant only to wake us up. */ if (overlappeds[i].lpOverlapped) { - req = uv_overlapped_to_req(overlappeds[i].lpOverlapped); - uv_insert_pending_req(loop, req); + uv__metrics_inc_events(loop, 1); + if (actual_timeout == 0) + uv__metrics_inc_events_waiting(loop, 1); + + req = uv__overlapped_to_req(overlappeds[i].lpOverlapped); + uv__insert_pending_req(loop, req); } } @@ -581,43 +525,43 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) { } -static int uv__loop_alive(const uv_loop_t* loop) { - return uv__has_active_handles(loop) || - uv__has_active_reqs(loop) || - loop->endgame_handles != NULL; -} - - -int uv_loop_alive(const uv_loop_t* loop) { - return uv__loop_alive(loop); -} - - int uv_run(uv_loop_t *loop, uv_run_mode mode) { DWORD timeout; int r; - int ran_pending; + int can_sleep; r = uv__loop_alive(loop); if (!r) uv_update_time(loop); - while (r != 0 && loop->stop_flag == 0) { + /* Maintain backwards compatibility by processing timers before entering the + * while loop for UV_RUN_DEFAULT. Otherwise timers only need to be executed + * once, which should be done after polling in order to maintain proper + * execution order of the conceptual event loop. */ + if (mode == UV_RUN_DEFAULT && r != 0 && loop->stop_flag == 0) { uv_update_time(loop); uv__run_timers(loop); + } - ran_pending = uv_process_reqs(loop); - uv_idle_invoke(loop); - uv_prepare_invoke(loop); + while (r != 0 && loop->stop_flag == 0) { + can_sleep = loop->pending_reqs_tail == NULL && loop->idle_handles == NULL; + + uv__process_reqs(loop); + uv__idle_invoke(loop); + uv__prepare_invoke(loop); timeout = 0; - if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT) + if ((mode == UV_RUN_ONCE && can_sleep) || mode == UV_RUN_DEFAULT) timeout = uv_backend_timeout(loop); - if (pGetQueuedCompletionStatusEx) - uv__poll(loop, timeout); - else - uv__poll_wine(loop, timeout); + uv__metrics_inc_loop_count(loop); + + uv__poll(loop, timeout); + + /* Process immediate callbacks (e.g. write_cb) a small fixed number of + * times to avoid loop starvation.*/ + for (r = 0; r < 8 && loop->pending_reqs_tail != NULL; r++) + uv__process_reqs(loop); /* Run one final update on the provider_idle_time in case uv__poll* * returned because the timeout expired, but no events were received. This @@ -626,20 +570,11 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) { */ uv__metrics_update_idle_time(loop); - uv_check_invoke(loop); - uv_process_endgames(loop); - - if (mode == UV_RUN_ONCE) { - /* UV_RUN_ONCE implies forward progress: at least one callback must have - * been invoked when it returns. uv__io_poll() can return without doing - * I/O (meaning: no callbacks) when its timeout expires - which means we - * have pending timers that satisfy the forward progress constraint. - * - * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from - * the check. - */ - uv__run_timers(loop); - } + uv__check_invoke(loop); + uv__process_endgames(loop); + + uv_update_time(loop); + uv__run_timers(loop); r = uv__loop_alive(loop); if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT) diff --git a/src/win/dl.c b/src/win/dl.c index 676be4dc7b5..7880c9595be 100644 --- a/src/win/dl.c +++ b/src/win/dl.c @@ -27,18 +27,17 @@ static int uv__dlerror(uv_lib_t* lib, const char* filename, DWORD errorno); int uv_dlopen(const char* filename, uv_lib_t* lib) { WCHAR filename_w[32768]; + ssize_t r; lib->handle = NULL; lib->errmsg = NULL; - if (!MultiByteToWideChar(CP_UTF8, - 0, - filename, - -1, - filename_w, - ARRAY_SIZE(filename_w))) { - return uv__dlerror(lib, filename, GetLastError()); - } + r = uv_wtf8_length_as_utf16(filename); + if (r < 0) + return uv__dlerror(lib, filename, ERROR_NO_UNICODE_TRANSLATION); + if ((size_t) r > ARRAY_SIZE(filename_w)) + return uv__dlerror(lib, filename, ERROR_INSUFFICIENT_BUFFER); + uv_wtf8_to_utf16(filename, filename_w, r); lib->handle = LoadLibraryExW(filename_w, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); if (lib->handle == NULL) { diff --git a/src/win/error.c b/src/win/error.c index 3a269da87a9..7abf906bb5c 100644 --- a/src/win/error.c +++ b/src/win/error.c @@ -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); + } +} diff --git a/src/win/fs-event.c b/src/win/fs-event.c index 76da077551d..1bbb8c52be2 100644 --- a/src/win/fs-event.c +++ b/src/win/fs-event.c @@ -33,7 +33,7 @@ const unsigned int uv_directory_watcher_buffer_size = 4096; -static void uv_fs_event_queue_readdirchanges(uv_loop_t* loop, +static void uv__fs_event_queue_readdirchanges(uv_loop_t* loop, uv_fs_event_t* handle) { assert(handle->dir_handle != INVALID_HANDLE_VALUE); assert(!handle->req_pending); @@ -57,15 +57,15 @@ static void uv_fs_event_queue_readdirchanges(uv_loop_t* loop, NULL)) { /* Make this req pending reporting an error. */ SET_REQ_ERROR(&handle->req, GetLastError()); - uv_insert_pending_req(loop, (uv_req_t*)&handle->req); + uv__insert_pending_req(loop, (uv_req_t*)&handle->req); } handle->req_pending = 1; } -static void uv_relative_path(const WCHAR* filename, - const WCHAR* dir, - WCHAR** relpath) { +static void uv__relative_path(const WCHAR* filename, + const WCHAR* dir, + WCHAR** relpath) { size_t relpathlen; size_t filenamelen = wcslen(filename); size_t dirlen = wcslen(dir); @@ -80,7 +80,7 @@ static void uv_relative_path(const WCHAR* filename, (*relpath)[relpathlen] = L'\0'; } -static int uv_split_path(const WCHAR* filename, WCHAR** dir, +static int uv__split_path(const WCHAR* filename, WCHAR** dir, WCHAR** file) { size_t len, i; DWORD dir_len; @@ -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)); @@ -157,7 +157,8 @@ int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, const char* path, unsigned int flags) { - int name_size, is_path_dir, size; + int is_path_dir; + size_t size; DWORD attr, last_error; WCHAR* dir = NULL, *dir_to_watch, *pathw = NULL; DWORD short_path_buffer_len; @@ -176,23 +177,9 @@ int uv_fs_event_start(uv_fs_event_t* handle, uv__handle_start(handle); - /* Convert name to UTF16. */ - - name_size = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0) * - sizeof(WCHAR); - pathw = (WCHAR*)uv__malloc(name_size); - if (!pathw) { - uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); - } - - if (!MultiByteToWideChar(CP_UTF8, - 0, - path, - -1, - pathw, - name_size / sizeof(WCHAR))) { - return uv_translate_sys_error(GetLastError()); - } + last_error = uv__convert_utf8_to_utf16(path, &pathw); + if (last_error) + goto error_uv; /* Determine whether path is a file or a directory. */ attr = GetFileAttributesW(pathw); @@ -255,17 +242,19 @@ int uv_fs_event_start(uv_fs_event_t* handle, short_path_done: short_path = short_path_buffer; - if (uv_split_path(pathw, &dir, &handle->filew) != 0) { + if (uv__split_path(pathw, &dir, &handle->filew) != 0) { last_error = GetLastError(); goto error; } - if (uv_split_path(short_path, NULL, &handle->short_filew) != 0) { + if (uv__split_path(short_path, NULL, &handle->short_filew) != 0) { last_error = GetLastError(); goto error; } dir_to_watch = dir; + uv__free(short_path); + short_path = NULL; uv__free(pathw); pathw = NULL; } @@ -333,6 +322,9 @@ int uv_fs_event_start(uv_fs_event_t* handle, return 0; error: + last_error = uv_translate_sys_error(last_error); + +error_uv: if (handle->path) { uv__free(handle->path); handle->path = NULL; @@ -365,7 +357,7 @@ int uv_fs_event_start(uv_fs_event_t* handle, uv__free(short_path); - return uv_translate_sys_error(last_error); + return last_error; } @@ -423,7 +415,7 @@ static int file_info_cmp(WCHAR* str, WCHAR* file_name, size_t file_name_len) { } -void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, +void uv__process_fs_event_req(uv_loop_t* loop, uv_req_t* req, uv_fs_event_t* handle) { FILE_NOTIFY_INFORMATION* file_info; int err, sizew, size; @@ -442,7 +434,7 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, */ if (!uv__is_active(handle)) { if (handle->flags & UV_HANDLE_CLOSING) { - uv_want_endgame(loop, (uv_handle_t*) handle); + uv__want_endgame(loop, (uv_handle_t*) handle); } return; } @@ -515,9 +507,9 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, if (long_filenamew) { /* Get the file name out of the long path. */ - uv_relative_path(long_filenamew, - handle->dirw, - &filenamew); + uv__relative_path(long_filenamew, + handle->dirw, + &filenamew); uv__free(long_filenamew); long_filenamew = filenamew; sizew = -1; @@ -571,30 +563,50 @@ 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) { - uv_want_endgame(loop, (uv_handle_t*)handle); + uv__want_endgame(loop, (uv_handle_t*)handle); } else if (uv__is_active(handle)) { - uv_fs_event_queue_readdirchanges(loop, handle); + uv__fs_event_queue_readdirchanges(loop, handle); } } -void uv_fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle) { +void uv__fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle) { uv_fs_event_stop(handle); uv__handle_closing(handle); if (!handle->req_pending) { - uv_want_endgame(loop, (uv_handle_t*)handle); + uv__want_endgame(loop, (uv_handle_t*)handle); } } -void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) { +void uv__fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) { if ((handle->flags & UV_HANDLE_CLOSING) && !handle->req_pending) { assert(!(handle->flags & UV_HANDLE_CLOSED)); diff --git a/src/win/fs.c b/src/win/fs.c index 903764144f4..a4742aa2ec1 100644 --- a/src/win/fs.c +++ b/src/win/fs.c @@ -31,6 +31,11 @@ #include #include "uv.h" + +/* requires , included via "uv.h" above, but needs to + be included before our "winapi.h", included via "internal.h" below. */ +#include + #include "internal.h" #include "req-inl.h" #include "handle-inl.h" @@ -41,19 +46,43 @@ #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 { \ if (req == NULL) \ return UV_EINVAL; \ - uv_fs_req_init(loop, req, subtype, cb); \ + uv__fs_req_init(loop, req, subtype, cb); \ } \ while (0) #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, \ @@ -92,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; @@ -107,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) @@ -131,8 +161,18 @@ 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; -void uv_fs_init(void) { +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; GetSystemInfo(&system_info); @@ -142,172 +182,9 @@ void uv_fs_init(void) { } -INLINE static int fs__capture_path(uv_fs_t* req, const char* path, - const char* new_path, const int copy_path) { - char* buf; - char* pos; - ssize_t buf_sz = 0, path_len = 0, pathw_len = 0, new_pathw_len = 0; - - /* new_path can only be set if path is also set. */ - assert(new_path == NULL || path != NULL); - - if (path != NULL) { - pathw_len = MultiByteToWideChar(CP_UTF8, - 0, - path, - -1, - NULL, - 0); - if (pathw_len == 0) { - return GetLastError(); - } - - buf_sz += pathw_len * sizeof(WCHAR); - } - - if (path != NULL && copy_path) { - path_len = 1 + strlen(path); - buf_sz += path_len; - } - - if (new_path != NULL) { - new_pathw_len = MultiByteToWideChar(CP_UTF8, - 0, - new_path, - -1, - NULL, - 0); - if (new_pathw_len == 0) { - return GetLastError(); - } - - buf_sz += new_pathw_len * sizeof(WCHAR); - } - - - if (buf_sz == 0) { - req->file.pathw = NULL; - req->fs.info.new_pathw = NULL; - req->path = NULL; - return 0; - } - - buf = (char*) uv__malloc(buf_sz); - if (buf == NULL) { - return ERROR_OUTOFMEMORY; - } - - pos = buf; - - if (path != NULL) { - DWORD r = MultiByteToWideChar(CP_UTF8, - 0, - path, - -1, - (WCHAR*) pos, - pathw_len); - assert(r == (DWORD) pathw_len); - req->file.pathw = (WCHAR*) pos; - pos += r * sizeof(WCHAR); - } else { - req->file.pathw = NULL; - } - - if (new_path != NULL) { - DWORD r = MultiByteToWideChar(CP_UTF8, - 0, - new_path, - -1, - (WCHAR*) pos, - new_pathw_len); - assert(r == (DWORD) new_pathw_len); - req->fs.info.new_pathw = (WCHAR*) pos; - pos += r * sizeof(WCHAR); - } else { - req->fs.info.new_pathw = NULL; - } - - req->path = path; - if (path != NULL && copy_path) { - memcpy(pos, path, path_len); - assert(path_len == buf_sz - (pos - buf)); - req->path = pos; - } - - req->flags |= UV_FS_FREE_PATHS; - - return 0; -} - - - -INLINE static void uv_fs_req_init(uv_loop_t* loop, uv_fs_t* req, - uv_fs_type fs_type, const uv_fs_cb cb) { - uv__once_init(); - UV_REQ_INIT(req, UV_FS); - req->loop = loop; - req->flags = 0; - req->fs_type = fs_type; - req->sys_errno_ = 0; - req->result = 0; - req->ptr = NULL; - req->path = NULL; - req->cb = cb; - memset(&req->fs, 0, sizeof(req->fs)); -} - - -static int fs__wide_to_utf8(WCHAR* w_source_ptr, - DWORD w_source_len, - char** target_ptr, - uint64_t* target_len_ptr) { - int r; - int target_len; - char* target; - target_len = WideCharToMultiByte(CP_UTF8, - 0, - w_source_ptr, - w_source_len, - NULL, - 0, - NULL, - NULL); - - if (target_len == 0) { - return -1; - } - - if (target_len_ptr != NULL) { - *target_len_ptr = target_len; - } - - if (target_ptr == NULL) { - return 0; - } - - target = uv__malloc(target_len + 1); - if (target == NULL) { - SetLastError(ERROR_OUTOFMEMORY); - return -1; - } - - r = WideCharToMultiByte(CP_UTF8, - 0, - w_source_ptr, - w_source_len, - target, - target_len, - NULL, - NULL); - assert(r == target_len); - target[target_len] = '\0'; - *target_ptr = target; - return 0; -} - - -INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr, - uint64_t* target_len_ptr) { +INLINE static int fs__readlink_handle(HANDLE handle, + char** target_ptr, + size_t* target_len_ptr) { char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; REPARSE_DATA_BUFFER* reparse_data = (REPARSE_DATA_BUFFER*) buffer; WCHAR* w_target; @@ -437,7 +314,99 @@ INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr, return -1; } - return fs__wide_to_utf8(w_target, w_target_len, target_ptr, target_len_ptr); + assert(target_ptr == NULL || *target_ptr == NULL); + return uv_utf16_to_wtf8(w_target, w_target_len, target_ptr, target_len_ptr); +} + + +INLINE static int fs__capture_path(uv_fs_t* req, const char* path, + const char* new_path, const int copy_path) { + WCHAR* buf; + WCHAR* pos; + size_t buf_sz = 0; + size_t path_len = 0; + ssize_t pathw_len = 0; + ssize_t new_pathw_len = 0; + + /* new_path can only be set if path is also set. */ + assert(new_path == NULL || path != NULL); + + if (path != NULL) { + pathw_len = uv_wtf8_length_as_utf16(path); + if (pathw_len < 0) + return ERROR_INVALID_NAME; + buf_sz += pathw_len * sizeof(WCHAR); + } + + if (path != NULL && copy_path) { + path_len = 1 + strlen(path); + buf_sz += path_len; + } + + if (new_path != NULL) { + new_pathw_len = uv_wtf8_length_as_utf16(new_path); + if (new_pathw_len < 0) + return ERROR_INVALID_NAME; + buf_sz += new_pathw_len * sizeof(WCHAR); + } + + + if (buf_sz == 0) { + req->file.pathw = NULL; + req->fs.info.new_pathw = NULL; + req->path = NULL; + return 0; + } + + buf = uv__malloc(buf_sz); + if (buf == NULL) { + return ERROR_OUTOFMEMORY; + } + + pos = buf; + + if (path != NULL) { + uv_wtf8_to_utf16(path, pos, pathw_len); + req->file.pathw = pos; + pos += pathw_len; + } else { + req->file.pathw = NULL; + } + + if (new_path != NULL) { + uv_wtf8_to_utf16(new_path, pos, new_pathw_len); + req->fs.info.new_pathw = pos; + pos += new_pathw_len; + } else { + req->fs.info.new_pathw = NULL; + } + + req->path = path; + if (path != NULL && copy_path) { + memcpy(pos, path, path_len); + assert(path_len == buf_sz - (pos - buf) * sizeof(WCHAR)); + req->path = (char*) pos; + } + + req->flags |= UV_FS_FREE_PATHS; + + return 0; +} + + +INLINE static void uv__fs_req_init(uv_loop_t* loop, uv_fs_t* req, + uv_fs_type fs_type, const uv_fs_cb cb) { + uv__once_init(); + UV_REQ_INIT(req, UV_FS); + req->loop = loop; + req->flags = 0; + req->fs_type = fs_type; + req->sys_errno_ = 0; + req->result = 0; + req->ptr = NULL; + req->path = NULL; + req->cb = cb; + memset(&req->fs, 0, sizeof(req->fs)); } @@ -473,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)) { @@ -912,12 +881,11 @@ void fs__read(uv_fs_t* req) { SET_REQ_RESULT(req, bytes); } else { error = GetLastError(); - if (error == ERROR_ACCESS_DENIED) { error = ERROR_INVALID_FLAGS; } - if (error == ERROR_HANDLE_EOF) { + if (error == ERROR_HANDLE_EOF || error == ERROR_BROKEN_PIPE) { SET_REQ_RESULT(req, bytes); } else { SET_REQ_WIN32_ERROR(req, error); @@ -1123,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, @@ -1164,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)) { @@ -1179,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); @@ -1188,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)) { @@ -1249,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; @@ -1428,7 +1432,8 @@ void fs__scandir(uv_fs_t* req) { uv__dirent_t* dirent; size_t wchar_len; - size_t utf8_len; + size_t wtf8_len; + char* wtf8; /* Obtain a pointer to the current directory entry. */ position += next_entry_offset; @@ -1455,11 +1460,8 @@ void fs__scandir(uv_fs_t* req) { info->FileName[1] == L'.') continue; - /* Compute the space required to store the filename as UTF-8. */ - utf8_len = WideCharToMultiByte( - CP_UTF8, 0, &info->FileName[0], wchar_len, NULL, 0, NULL, NULL); - if (utf8_len == 0) - goto win32_error; + /* Compute the space required to store the filename as WTF-8. */ + wtf8_len = uv_utf16_length_as_wtf8(&info->FileName[0], wchar_len); /* Resize the dirent array if needed. */ if (dirents_used >= dirents_size) { @@ -1479,25 +1481,16 @@ void fs__scandir(uv_fs_t* req) { * includes room for the first character of the filename, but `utf8_len` * doesn't count the NULL terminator at this point. */ - dirent = uv__malloc(sizeof *dirent + utf8_len); + dirent = uv__malloc(sizeof *dirent + wtf8_len); if (dirent == NULL) goto out_of_memory_error; dirents[dirents_used++] = dirent; /* Convert file name to UTF-8. */ - if (WideCharToMultiByte(CP_UTF8, - 0, - &info->FileName[0], - wchar_len, - &dirent->d_name[0], - utf8_len, - NULL, - NULL) == 0) - goto win32_error; - - /* Add a null terminator to the filename. */ - dirent->d_name[utf8_len] = '\0'; + wtf8 = &dirent->d_name[0]; + if (uv_utf16_to_wtf8(&info->FileName[0], wchar_len, &wtf8, &wtf8_len) != 0) + goto out_of_memory_error; /* Fill out the type field. */ if (info->FileAttributes & FILE_ATTRIBUTE_DEVICE) @@ -1671,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; @@ -1705,12 +1698,70 @@ 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; + FILE_FS_DEVICE_INFORMATION device_info; FILE_ALL_INFORMATION file_info; 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, + &device_info, + sizeof device_info, + FileFsDeviceInformation); + + /* Buffer overflow (a warning status code) is expected here. */ + if (NT_ERROR(nt_status)) { + SetLastError(pRtlNtStatusToDosError(nt_status)); + return -1; + } + + /* If it's NUL device set fields as reasonable as possible and return. */ + if (device_info.DeviceType == FILE_DEVICE_NULL) { + fs__stat_assign_statbuf_null(statbuf); + return 0; + } nt_status = pNtQueryInformationFile(handle, &io_status, @@ -1732,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.QuadPart = volume_info.VolumeSerialNumber; + } + + stat_info.DeviceType = device_info.DeviceType; + stat_info.FileAttributes = file_info.BasicInformation.FileAttributes; + stat_info.NumberOfLinks = file_info.StandardInformation.NumberOfLinks; + stat_info.FileId.QuadPart = + file_info.InternalInformation.IndexNumber.QuadPart; + stat_info.ChangeTime.QuadPart = + file_info.BasicInformation.ChangeTime.QuadPart; + stat_info.CreationTime.QuadPart = + file_info.BasicInformation.CreationTime.QuadPart; + stat_info.LastAccessTime.QuadPart = + file_info.BasicInformation.LastAccessTime.QuadPart; + stat_info.LastWriteTime.QuadPart = + file_info.BasicInformation.LastWriteTime.QuadPart; + stat_info.AllocationSize.QuadPart = + file_info.StandardInformation.AllocationSize.QuadPart; + + if (do_lstat && + (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + /* + * If reading the link fails, the reparse point is not a symlink and needs + * to be treated as a regular file. The higher level lstat function will + * detect this failure and retry without do_lstat if appropriate. + */ + if (fs__readlink_handle(handle, NULL, &target_length) != 0) { + return -1; + } + stat_info.EndOfFile.QuadPart = target_length; + } else { + stat_info.EndOfFile.QuadPart = + file_info.StandardInformation.EndOfFile.QuadPart; } + fs__stat_assign_statbuf(statbuf, stat_info, do_lstat); + return 0; +} + +INLINE static void fs__stat_assign_statbuf_null(uv_stat_t* statbuf) { + memset(statbuf, 0, sizeof(uv_stat_t)); + statbuf->st_mode = _S_IFCHR; + statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) | + ((_S_IREAD | _S_IWRITE) >> 6); + statbuf->st_nlink = 1; + statbuf->st_blksize = 4096; + statbuf->st_rdev = FILE_DEVICE_NULL << 16; +} + +INLINE static void fs__stat_assign_statbuf(uv_stat_t* statbuf, + FILE_STAT_BASIC_INFORMATION stat_info, int do_lstat) { + statbuf->st_dev = stat_info.VolumeSerialNumber.QuadPart; + /* Todo: st_mode should probably always be 0666 for everyone. We might also * want to report 0777 if the file is a .exe or a directory. * @@ -1771,49 +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, &statbuf->st_size) != 0) - return -1; + (stat_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { statbuf->st_mode |= S_IFLNK; + 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 @@ -1845,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; } @@ -1860,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, @@ -1868,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; @@ -1880,9 +2157,14 @@ INLINE static DWORD fs__stat_impl_from_path(WCHAR* path, flags, NULL); - if (handle == INVALID_HANDLE_VALUE) + if (handle == INVALID_HANDLE_VALUE) { ret = GetLastError(); - else if (fs__stat_handle(handle, statbuf, do_lstat) != 0) + 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(); else ret = 0; @@ -1915,6 +2197,37 @@ INLINE static void fs__stat_impl(uv_fs_t* req, int do_lstat) { } +INLINE static int fs__fstat_handle(int fd, HANDLE handle, uv_stat_t* statbuf) { + DWORD file_type; + + /* Each file type is processed differently. */ + file_type = uv_guess_handle(fd); + switch (file_type) { + /* Disk files use the existing logic from fs__stat_handle. */ + case UV_FILE: + return fs__stat_handle(handle, statbuf, 0); + + /* Devices and pipes are processed identically. There is no more information + * for them from any API. Fields are set as reasonably as possible and the + * function returns. */ + case UV_TTY: + case UV_NAMED_PIPE: + memset(statbuf, 0, sizeof(uv_stat_t)); + statbuf->st_mode = file_type == UV_TTY ? _S_IFCHR : _S_IFIFO; + statbuf->st_nlink = 1; + statbuf->st_rdev = (file_type == UV_TTY ? FILE_DEVICE_CONSOLE : FILE_DEVICE_NAMED_PIPE) << 16; + statbuf->st_ino = (uintptr_t) handle; + return 0; + + /* If file type is unknown it is an error. */ + case UV_UNKNOWN_HANDLE: + default: + SetLastError(ERROR_INVALID_HANDLE); + return -1; + } +} + + static void fs__stat(uv_fs_t* req) { fs__stat_prepare_path(req->file.pathw); fs__stat_impl(req, 0); @@ -1940,7 +2253,7 @@ static void fs__fstat(uv_fs_t* req) { return; } - if (fs__stat_handle(handle, &req->statbuf, 0) != 0) { + if (fs__fstat_handle(fd, handle, &req->statbuf) != 0) { SET_REQ_WIN32_ERROR(req, GetLastError()); return; } @@ -2221,7 +2534,7 @@ static void fs__fchmod(uv_fs_t* req) { SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status)); goto fchmod_cleanup; } - /* Remeber to clear the flag later on */ + /* Remember to clear the flag later on */ clear_archive_flag = 1; } else { clear_archive_flag = 0; @@ -2300,13 +2613,13 @@ INLINE static DWORD fs__utime_impl_from_path(WCHAR* path, flags, NULL); - if (handle == INVALID_HANDLE_VALUE) { - ret = GetLastError(); - } else if (fs__utime_handle(handle, atime, mtime) != 0) { + if (handle == INVALID_HANDLE_VALUE) + return GetLastError(); + + if (fs__utime_handle(handle, atime, mtime) != 0) ret = GetLastError(); - } else { + else ret = 0; - } CloseHandle(handle); return ret; @@ -2442,16 +2755,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; @@ -2469,18 +2783,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); @@ -2603,8 +2917,12 @@ static void fs__readlink(uv_fs_t* req) { return; } + assert(req->ptr == NULL); if (fs__readlink_handle(handle, (char**) &req->ptr, NULL) != 0) { - SET_REQ_WIN32_ERROR(req, GetLastError()); + DWORD error = GetLastError(); + SET_REQ_WIN32_ERROR(req, error); + if (error == ERROR_NOT_A_REPARSE_POINT) + req->result = UV_EINVAL; CloseHandle(handle); return; } @@ -2659,7 +2977,8 @@ static ssize_t fs__realpath_handle(HANDLE handle, char** realpath_ptr) { return -1; } - r = fs__wide_to_utf8(w_realpath_ptr, w_realpath_len, realpath_ptr, NULL); + assert(*realpath_ptr == NULL); + r = uv_utf16_to_wtf8(w_realpath_ptr, w_realpath_len, realpath_ptr, NULL); uv__free(w_realpath_buf); return r; } @@ -2679,6 +2998,7 @@ static void fs__realpath(uv_fs_t* req) { return; } + assert(req->ptr == NULL); if (fs__realpath_handle(handle, (char**) &req->ptr) == -1) { CloseHandle(handle); SET_REQ_WIN32_ERROR(req, GetLastError()); @@ -2843,7 +3163,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); diff --git a/src/win/getaddrinfo.c b/src/win/getaddrinfo.c index dfab860a735..4b8ee75a062 100644 --- a/src/win/getaddrinfo.c +++ b/src/win/getaddrinfo.c @@ -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,16 +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; - int addrinfo_len = 0; - int 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; - - 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); @@ -125,41 +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 = WideCharToMultiByte(CP_UTF8, - 0, - addrinfow_ptr->ai_canonname, - -1, - NULL, - 0, - NULL, - NULL); - if (name_len == 0) { - req->retcode = uv_translate_sys_error(GetLastError()); + 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); + 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; @@ -169,48 +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 = WideCharToMultiByte(CP_UTF8, - 0, - addrinfow_ptr->ai_canonname, - -1, - NULL, - 0, - NULL, - NULL); - assert(name_len > 0); - assert(cur_ptr + name_len <= alloc_ptr + addrinfo_len); - name_len = WideCharToMultiByte(CP_UTF8, - 0, - addrinfow_ptr->ai_canonname, + 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, - cur_ptr, - name_len, - NULL, - NULL); - assert(name_len > 0); - addrinfo_ptr->ai_canonname = cur_ptr; - cur_ptr += ALIGNED_SIZE(name_len); + addrinfo_ptr->ai_canonname, + (size_t*)&name_len); + assert(r == 0); + 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 { @@ -225,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) @@ -261,12 +244,13 @@ int uv_getaddrinfo(uv_loop_t* loop, const char* service, const struct addrinfo* hints) { char hostname_ascii[256]; - int nodesize = 0; - int servicesize = 0; - int hintssize = 0; - char* alloc_ptr = NULL; - int err; - long rc; + size_t off = 0; + size_t nodesize = 0; + size_t servicesize = 0; + size_t serviceoff = 0; + size_t hintssize = 0; + size_t hintoff = 0; + ssize_t rc; if (req == NULL || (node == NULL && service == NULL)) { return UV_EINVAL; @@ -286,56 +270,38 @@ int uv_getaddrinfo(uv_loop_t* loop, hostname_ascii + sizeof(hostname_ascii)); if (rc < 0) return rc; - nodesize = ALIGNED_SIZE(MultiByteToWideChar(CP_UTF8, 0, hostname_ascii, - -1, NULL, 0) * sizeof(WCHAR)); - if (nodesize == 0) { - err = GetLastError(); - goto error; - } + nodesize = strlen(hostname_ascii) + 1; node = hostname_ascii; + off += nodesize * sizeof(WCHAR); } if (service != NULL) { - servicesize = ALIGNED_SIZE(MultiByteToWideChar(CP_UTF8, - 0, - service, - -1, - NULL, - 0) * - sizeof(WCHAR)); - if (servicesize == 0) { - err = GetLastError(); - goto error; - } + rc = uv_wtf8_length_as_utf16(service); + 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 = (char*)uv__malloc(nodesize + servicesize + hintssize); - if (!alloc_ptr) { - err = WSAENOBUFS; - goto error; - } - - /* save alloc_ptr now so we can free if error */ - req->alloc = (void*)alloc_ptr; + req->alloc = uv__malloc(off); + if (!req->alloc) + return UV_ENOMEM; /* Convert node string to UTF16 into allocated memory and save pointer in the - * request. */ + * request. The node here has been converted to ascii. */ if (node != NULL) { - req->node = (WCHAR*)alloc_ptr; - if (MultiByteToWideChar(CP_UTF8, - 0, - node, - -1, - (WCHAR*) alloc_ptr, - nodesize / sizeof(WCHAR)) == 0) { - err = GetLastError(); - goto error; - } - alloc_ptr += nodesize; + req->node = (WCHAR*) req->alloc; + uv_wtf8_to_utf16(node, req->node, nodesize); } else { req->node = NULL; } @@ -343,24 +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; - if (MultiByteToWideChar(CP_UTF8, - 0, - service, - -1, - (WCHAR*) alloc_ptr, - servicesize / sizeof(WCHAR)) == 0) { - err = GetLastError(); - goto error; - } - alloc_ptr += servicesize; + 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; @@ -373,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, @@ -387,19 +344,11 @@ int uv_getaddrinfo(uv_loop_t* loop, uv__getaddrinfo_done(&req->work_req, 0); return req->retcode; } - -error: - if (req != NULL) { - uv__free(req->alloc); - req->alloc = NULL; - } - return uv_translate_sys_error(err); } int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) { NET_LUID luid; wchar_t wname[NDIS_IF_MAX_STRING_SIZE + 1]; /* Add one for the NUL. */ - DWORD bufsize; int r; if (buffer == NULL || size == NULL || *size == 0) @@ -415,31 +364,7 @@ int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) { if (r != 0) return uv_translate_sys_error(r); - /* Check how much space we need */ - bufsize = WideCharToMultiByte(CP_UTF8, 0, wname, -1, NULL, 0, NULL, NULL); - - if (bufsize == 0) { - return uv_translate_sys_error(GetLastError()); - } else if (bufsize > *size) { - *size = bufsize; - return UV_ENOBUFS; - } - - /* Convert to UTF-8 */ - bufsize = WideCharToMultiByte(CP_UTF8, - 0, - wname, - -1, - buffer, - *size, - NULL, - NULL); - - if (bufsize == 0) - return uv_translate_sys_error(GetLastError()); - - *size = bufsize - 1; - return 0; + return uv__copy_utf16_to_utf8(wname, -1, buffer, size); } int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) { diff --git a/src/win/getnameinfo.c b/src/win/getnameinfo.c index b3773380c21..695549580d2 100644 --- a/src/win/getnameinfo.c +++ b/src/win/getnameinfo.c @@ -42,6 +42,7 @@ static void uv__getnameinfo_work(struct uv__work* w) { uv_getnameinfo_t* req; WCHAR host[NI_MAXHOST]; WCHAR service[NI_MAXSERV]; + size_t size; int ret; req = container_of(w, uv_getnameinfo_t, work_req); @@ -57,29 +58,17 @@ static void uv__getnameinfo_work(struct uv__work* w) { return; } - ret = WideCharToMultiByte(CP_UTF8, - 0, - host, - -1, - req->host, - sizeof(req->host), - NULL, - NULL); - if (ret == 0) { - req->retcode = uv_translate_sys_error(GetLastError()); + size = sizeof(req->host); + ret = uv__copy_utf16_to_utf8(host, -1, req->host, &size); + if (ret < 0) { + req->retcode = ret; return; } - ret = WideCharToMultiByte(CP_UTF8, - 0, - service, - -1, - req->service, - sizeof(req->service), - NULL, - NULL); - if (ret == 0) { - req->retcode = uv_translate_sys_error(GetLastError()); + size = sizeof(req->service); + ret = uv__copy_utf16_to_utf8(service, -1, req->service, &size); + if (ret < 0) { + req->retcode = ret; } } @@ -93,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) { @@ -135,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; diff --git a/src/win/handle-inl.h b/src/win/handle-inl.h index 82c657d579f..4722e85790a 100644 --- a/src/win/handle-inl.h +++ b/src/win/handle-inl.h @@ -55,7 +55,7 @@ \ if (handle->flags & UV_HANDLE_CLOSING && \ handle->reqs_pending == 0) { \ - uv_want_endgame(loop, (uv_handle_t*)handle); \ + uv__want_endgame(loop, (uv_handle_t*)handle); \ } \ } while (0) @@ -75,7 +75,7 @@ #define uv__handle_close(handle) \ do { \ - QUEUE_REMOVE(&(handle)->handle_queue); \ + uv__queue_remove(&(handle)->handle_queue); \ uv__active_handle_rm((uv_handle_t*) (handle)); \ \ (handle)->flags |= UV_HANDLE_CLOSED; \ @@ -85,7 +85,7 @@ } while (0) -INLINE static void uv_want_endgame(uv_loop_t* loop, uv_handle_t* handle) { +INLINE static void uv__want_endgame(uv_loop_t* loop, uv_handle_t* handle) { if (!(handle->flags & UV_HANDLE_ENDGAME_QUEUED)) { handle->flags |= UV_HANDLE_ENDGAME_QUEUED; @@ -95,7 +95,7 @@ INLINE static void uv_want_endgame(uv_loop_t* loop, uv_handle_t* handle) { } -INLINE static void uv_process_endgames(uv_loop_t* loop) { +INLINE static void uv__process_endgames(uv_loop_t* loop) { uv_handle_t* handle; while (loop->endgame_handles) { @@ -106,23 +106,23 @@ INLINE static void uv_process_endgames(uv_loop_t* loop) { switch (handle->type) { case UV_TCP: - uv_tcp_endgame(loop, (uv_tcp_t*) handle); + uv__tcp_endgame(loop, (uv_tcp_t*) handle); break; case UV_NAMED_PIPE: - uv_pipe_endgame(loop, (uv_pipe_t*) handle); + uv__pipe_endgame(loop, (uv_pipe_t*) handle); break; case UV_TTY: - uv_tty_endgame(loop, (uv_tty_t*) handle); + uv__tty_endgame(loop, (uv_tty_t*) handle); break; case UV_UDP: - uv_udp_endgame(loop, (uv_udp_t*) handle); + uv__udp_endgame(loop, (uv_udp_t*) handle); break; case UV_POLL: - uv_poll_endgame(loop, (uv_poll_t*) handle); + uv__poll_endgame(loop, (uv_poll_t*) handle); break; case UV_TIMER: @@ -133,23 +133,23 @@ INLINE static void uv_process_endgames(uv_loop_t* loop) { case UV_PREPARE: case UV_CHECK: case UV_IDLE: - uv_loop_watcher_endgame(loop, handle); + uv__loop_watcher_endgame(loop, handle); break; case UV_ASYNC: - uv_async_endgame(loop, (uv_async_t*) handle); + uv__async_endgame(loop, (uv_async_t*) handle); break; case UV_SIGNAL: - uv_signal_endgame(loop, (uv_signal_t*) handle); + uv__signal_endgame(loop, (uv_signal_t*) handle); break; case UV_PROCESS: - uv_process_endgame(loop, (uv_process_t*) handle); + uv__process_endgame(loop, (uv_process_t*) handle); break; case UV_FS_EVENT: - uv_fs_event_endgame(loop, (uv_fs_event_t*) handle); + uv__fs_event_endgame(loop, (uv_fs_event_t*) handle); break; case UV_FS_POLL: diff --git a/src/win/handle.c b/src/win/handle.c index 61e4df61b32..53a81fdbc21 100644 --- a/src/win/handle.c +++ b/src/win/handle.c @@ -77,63 +77,63 @@ void uv_close(uv_handle_t* handle, uv_close_cb cb) { /* Handle-specific close actions */ switch (handle->type) { case UV_TCP: - uv_tcp_close(loop, (uv_tcp_t*)handle); + uv__tcp_close(loop, (uv_tcp_t*)handle); return; case UV_NAMED_PIPE: - uv_pipe_close(loop, (uv_pipe_t*) handle); + uv__pipe_close(loop, (uv_pipe_t*) handle); return; case UV_TTY: - uv_tty_close((uv_tty_t*) handle); + uv__tty_close((uv_tty_t*) handle); return; case UV_UDP: - uv_udp_close(loop, (uv_udp_t*) handle); + uv__udp_close(loop, (uv_udp_t*) handle); return; case UV_POLL: - uv_poll_close(loop, (uv_poll_t*) handle); + uv__poll_close(loop, (uv_poll_t*) handle); return; case UV_TIMER: uv_timer_stop((uv_timer_t*)handle); uv__handle_closing(handle); - uv_want_endgame(loop, handle); + uv__want_endgame(loop, handle); return; case UV_PREPARE: uv_prepare_stop((uv_prepare_t*)handle); uv__handle_closing(handle); - uv_want_endgame(loop, handle); + uv__want_endgame(loop, handle); return; case UV_CHECK: uv_check_stop((uv_check_t*)handle); uv__handle_closing(handle); - uv_want_endgame(loop, handle); + uv__want_endgame(loop, handle); return; case UV_IDLE: uv_idle_stop((uv_idle_t*)handle); uv__handle_closing(handle); - uv_want_endgame(loop, handle); + uv__want_endgame(loop, handle); return; case UV_ASYNC: - uv_async_close(loop, (uv_async_t*) handle); + uv__async_close(loop, (uv_async_t*) handle); return; case UV_SIGNAL: - uv_signal_close(loop, (uv_signal_t*) handle); + uv__signal_close(loop, (uv_signal_t*) handle); return; case UV_PROCESS: - uv_process_close(loop, (uv_process_t*) handle); + uv__process_close(loop, (uv_process_t*) handle); return; case UV_FS_EVENT: - uv_fs_event_close(loop, (uv_fs_event_t*) handle); + uv__fs_event_close(loop, (uv_fs_event_t*) handle); return; case UV_FS_POLL: diff --git a/src/win/internal.h b/src/win/internal.h index b1b25b4c786..be408af6661 100644 --- a/src/win/internal.h +++ b/src/win/internal.h @@ -72,25 +72,28 @@ typedef struct { uint32_t delayed_error; } uv__ipc_socket_xfer_info_t; -int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb); -int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client); -int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb, +int uv__tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb); +int uv__tcp_accept(uv_tcp_t* server, uv_tcp_t* client); +int uv__tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb); -int uv_tcp_write(uv_loop_t* loop, uv_write_t* req, uv_tcp_t* handle, +int uv__tcp_write(uv_loop_t* loop, uv_write_t* req, uv_tcp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb); int uv__tcp_try_write(uv_tcp_t* handle, const uv_buf_t bufs[], unsigned int nbufs); -void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, uv_req_t* req); -void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle, +void uv__process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, uv_req_t* req); +void uv__process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle, uv_write_t* req); -void uv_process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle, +void uv__process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle, uv_req_t* req); -void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, +void uv__process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, uv_connect_t* req); +void uv__process_tcp_shutdown_req(uv_loop_t* loop, + uv_tcp_t* stream, + uv_shutdown_t* req); -void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp); -void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle); +void uv__tcp_close(uv_loop_t* loop, uv_tcp_t* tcp); +void uv__tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle); int uv__tcp_xfer_export(uv_tcp_t* handle, int pid, @@ -104,12 +107,12 @@ int uv__tcp_xfer_import(uv_tcp_t* tcp, /* * UDP */ -void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle, uv_req_t* req); -void uv_process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle, +void uv__process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle, uv_req_t* req); +void uv__process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle, uv_udp_send_t* req); -void uv_udp_close(uv_loop_t* loop, uv_udp_t* handle); -void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle); +void uv__udp_close(uv_loop_t* loop, uv_udp_t* handle); +void uv__udp_endgame(uv_loop_t* loop, uv_udp_t* handle); /* @@ -118,9 +121,9 @@ void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle); int uv__create_stdio_pipe_pair(uv_loop_t* loop, uv_pipe_t* parent_pipe, HANDLE* child_pipe_ptr, unsigned int flags); -int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); -int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client); -int uv_pipe_read_start(uv_pipe_t* handle, uv_alloc_cb alloc_cb, +int uv__pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); +int uv__pipe_accept(uv_pipe_t* server, uv_stream_t* client); +int uv__pipe_read_start(uv_pipe_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb); void uv__pipe_read_stop(uv_pipe_t* handle); int uv__pipe_write(uv_loop_t* loop, @@ -130,75 +133,67 @@ int uv__pipe_write(uv_loop_t* loop, size_t nbufs, uv_stream_t* send_handle, uv_write_cb cb); +void uv__pipe_shutdown(uv_loop_t* loop, uv_pipe_t* handle, uv_shutdown_t* req); -void uv_process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle, +void uv__process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle, uv_req_t* req); -void uv_process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle, +void uv__process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle, uv_write_t* req); -void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle, +void uv__process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle, uv_req_t* raw_req); -void uv_process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle, +void uv__process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle, uv_connect_t* req); -void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle, +void uv__process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle, uv_shutdown_t* req); -void uv_pipe_close(uv_loop_t* loop, uv_pipe_t* handle); -void uv_pipe_cleanup(uv_loop_t* loop, uv_pipe_t* handle); -void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle); +void uv__pipe_close(uv_loop_t* loop, uv_pipe_t* handle); +void uv__pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle); /* * TTY */ -void uv_console_init(void); +void uv__console_init(void); -int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb, +int uv__tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb); -int uv_tty_read_stop(uv_tty_t* handle); -int uv_tty_write(uv_loop_t* loop, uv_write_t* req, uv_tty_t* handle, +int uv__tty_read_stop(uv_tty_t* handle); +int uv__tty_write(uv_loop_t* loop, uv_write_t* req, uv_tty_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb); int uv__tty_try_write(uv_tty_t* handle, const uv_buf_t bufs[], unsigned int nbufs); -void uv_tty_close(uv_tty_t* handle); +void uv__tty_close(uv_tty_t* handle); -void uv_process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle, +void uv__process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle, uv_req_t* req); -void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle, +void uv__process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle, uv_write_t* req); -/* - * uv_process_tty_accept_req() is a stub to keep DELEGATE_STREAM_REQ working - * TODO: find a way to remove it - */ -void uv_process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle, - uv_req_t* raw_req); -/* - * uv_process_tty_connect_req() is a stub to keep DELEGATE_STREAM_REQ working - * TODO: find a way to remove it - */ -void uv_process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle, - uv_connect_t* req); - -void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle); +#define uv__process_tty_accept_req(loop, handle, req) abort() +#define uv__process_tty_connect_req(loop, handle, req) abort() +void uv__process_tty_shutdown_req(uv_loop_t* loop, + uv_tty_t* stream, + uv_shutdown_t* req); +void uv__tty_endgame(uv_loop_t* loop, uv_tty_t* handle); /* * Poll watchers */ -void uv_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, +void uv__process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req); -int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle); -void uv_poll_endgame(uv_loop_t* loop, uv_poll_t* handle); +int uv__poll_close(uv_loop_t* loop, uv_poll_t* handle); +void uv__poll_endgame(uv_loop_t* loop, uv_poll_t* handle); /* * Loop watchers */ -void uv_loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle); +void uv__loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle); -void uv_prepare_invoke(uv_loop_t* loop); -void uv_check_invoke(uv_loop_t* loop); -void uv_idle_invoke(uv_loop_t* loop); +void uv__prepare_invoke(uv_loop_t* loop); +void uv__check_invoke(uv_loop_t* loop); +void uv__idle_invoke(uv_loop_t* loop); void uv__once_init(void); @@ -206,53 +201,47 @@ void uv__once_init(void); /* * Async watcher */ -void uv_async_close(uv_loop_t* loop, uv_async_t* handle); -void uv_async_endgame(uv_loop_t* loop, uv_async_t* handle); +void uv__async_close(uv_loop_t* loop, uv_async_t* handle); +void uv__async_endgame(uv_loop_t* loop, uv_async_t* handle); -void uv_process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle, +void uv__process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle, uv_req_t* req); /* * Signal watcher */ -void uv_signals_init(void); +void uv__signals_init(void); int uv__signal_dispatch(int signum); -void uv_signal_close(uv_loop_t* loop, uv_signal_t* handle); -void uv_signal_endgame(uv_loop_t* loop, uv_signal_t* handle); +void uv__signal_close(uv_loop_t* loop, uv_signal_t* handle); +void uv__signal_endgame(uv_loop_t* loop, uv_signal_t* handle); -void uv_process_signal_req(uv_loop_t* loop, uv_signal_t* handle, +void uv__process_signal_req(uv_loop_t* loop, uv_signal_t* handle, uv_req_t* req); /* * Spawn */ -void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle); -void uv_process_close(uv_loop_t* loop, uv_process_t* handle); -void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle); - - -/* - * Error - */ -int uv_translate_sys_error(int sys_errno); +void uv__process_proc_exit(uv_loop_t* loop, uv_process_t* handle); +void uv__process_close(uv_loop_t* loop, uv_process_t* handle); +void uv__process_endgame(uv_loop_t* loop, uv_process_t* handle); /* * FS */ -void uv_fs_init(void); +void uv__fs_init(void); /* * FS Event */ -void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, +void uv__process_fs_event_req(uv_loop_t* loop, uv_req_t* req, uv_fs_event_t* handle); -void uv_fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle); -void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle); +void uv__fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle); +void uv__fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle); /* @@ -268,9 +257,9 @@ void uv__util_init(void); uint64_t uv__hrtime(unsigned int scale); __declspec(noreturn) void uv_fatal_error(const int errorno, const char* syscall); -int uv__getpwuid_r(uv_passwd_t* pwd); -int uv__convert_utf16_to_utf8(const WCHAR* utf16, int utf16len, char** utf8); -int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16); +int uv__convert_utf16_to_utf8(const WCHAR* utf16, size_t utf16len, char** utf8); +int uv__copy_utf16_to_utf8(const WCHAR* utf16, size_t utf16len, char* utf8, size_t *size); +int uv__convert_utf8_to_utf16(const char* utf8, WCHAR** utf16); typedef int (WINAPI *uv__peersockfunc)(SOCKET, struct sockaddr*, int*); @@ -299,28 +288,28 @@ HANDLE uv__stdio_handle(BYTE* buffer, int fd); /* * Winapi and ntapi utility functions */ -void uv_winapi_init(void); +void uv__winapi_init(void); /* * Winsock utility functions */ -void uv_winsock_init(void); +void uv__winsock_init(void); -int uv_ntstatus_to_winsock_error(NTSTATUS status); +int uv__ntstatus_to_winsock_error(NTSTATUS status); -BOOL uv_get_acceptex_function(SOCKET socket, LPFN_ACCEPTEX* target); -BOOL uv_get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target); +BOOL uv__get_acceptex_function(SOCKET socket, LPFN_ACCEPTEX* target); +BOOL uv__get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target); -int WSAAPI uv_wsarecv_workaround(SOCKET socket, WSABUF* buffers, +int WSAAPI uv__wsarecv_workaround(SOCKET socket, WSABUF* buffers, DWORD buffer_count, DWORD* bytes, DWORD* flags, WSAOVERLAPPED *overlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine); -int WSAAPI uv_wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers, +int WSAAPI uv__wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers, DWORD buffer_count, DWORD* bytes, DWORD* flags, struct sockaddr* addr, int* addr_len, WSAOVERLAPPED *overlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine); -int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in, +int WSAAPI uv__msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in, AFD_POLL_INFO* info_out, OVERLAPPED* overlapped); /* Whether there are any non-IFS LSPs stacked on TCP */ @@ -341,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_ */ diff --git a/src/win/loop-watcher.c b/src/win/loop-watcher.c index ad7fbea1697..fad9e8a8e4c 100644 --- a/src/win/loop-watcher.c +++ b/src/win/loop-watcher.c @@ -26,7 +26,7 @@ #include "handle-inl.h" -void uv_loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle) { +void uv__loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle) { if (handle->flags & UV_HANDLE_CLOSING) { assert(!(handle->flags & UV_HANDLE_CLOSED)); handle->flags |= UV_HANDLE_CLOSED; @@ -104,7 +104,7 @@ void uv_loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle) { } \ \ \ - void uv_##name##_invoke(uv_loop_t* loop) { \ + void uv__##name##_invoke(uv_loop_t* loop) { \ uv_##name##_t* handle; \ \ (loop)->next_##name##_handle = (loop)->name##_handles; \ diff --git a/src/win/pipe.c b/src/win/pipe.c index 984b766bb43..d05bfd28aec 100644 --- a/src/win/pipe.c +++ b/src/win/pipe.c @@ -49,13 +49,13 @@ static const int default_pending_pipe_instances = 4; /* Pipe prefix */ static char pipe_prefix[] = "\\\\?\\pipe"; -static const int pipe_prefix_len = sizeof(pipe_prefix) - 1; +static const size_t pipe_prefix_len = sizeof(pipe_prefix) - 1; /* IPC incoming xfer queue item. */ typedef struct { uv__ipc_socket_xfer_type_t xfer_type; uv__ipc_socket_xfer_info_t xfer_info; - QUEUE member; + struct uv__queue member; } uv__ipc_xfer_queue_item_t; /* IPC frame header flags. */ @@ -98,20 +98,28 @@ 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()); } int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) { - uv_stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE); + uv__stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE); handle->reqs_pending = 0; handle->handle = INVALID_HANDLE_VALUE; handle->name = NULL; handle->pipe.conn.ipc_remote_pid = 0; handle->pipe.conn.ipc_data_frame.payload_remaining = 0; - QUEUE_INIT(&handle->pipe.conn.ipc_xfer_queue); + uv__queue_init(&handle->pipe.conn.ipc_xfer_queue); handle->pipe.conn.ipc_xfer_queue_length = 0; handle->ipc = ipc; handle->pipe.conn.non_overlapped_writes_tail = NULL; @@ -120,15 +128,11 @@ int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) { } -static void uv_pipe_connection_init(uv_pipe_t* handle) { - uv_connection_init((uv_stream_t*) handle); +static void uv__pipe_connection_init(uv_pipe_t* handle) { + assert(!(handle->flags & UV_HANDLE_PIPESERVER)); + uv__connection_init((uv_stream_t*) handle); handle->read_req.data = handle; handle->pipe.conn.eof_timer = NULL; - assert(!(handle->flags & UV_HANDLE_PIPESERVER)); - if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) { - handle->pipe.conn.readfile_thread_handle = NULL; - InitializeCriticalSection(&handle->pipe.conn.readfile_thread_lock); - } } @@ -195,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; @@ -204,12 +208,12 @@ 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; for (;;) { - uv_unique_pipe_name(random, name, nameSize); + uv__unique_pipe_name(random, name, nameSize); pipeHandle = CreateNamedPipeA(name, access | FILE_FLAG_FIRST_PIPE_INSTANCE, @@ -245,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; @@ -353,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); @@ -393,6 +402,8 @@ int uv__create_stdio_pipe_pair(uv_loop_t* loop, unsigned int client_flags; int err; + uv__pipe_connection_init(parent_pipe); + server_pipe = INVALID_HANDLE_VALUE; client_pipe = INVALID_HANDLE_VALUE; @@ -415,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; @@ -427,7 +438,6 @@ int uv__create_stdio_pipe_pair(uv_loop_t* loop, goto error; } - uv_pipe_connection_init(parent_pipe); parent_pipe->handle = server_pipe; *child_pipe_ptr = client_pipe; @@ -450,11 +460,11 @@ int uv__create_stdio_pipe_pair(uv_loop_t* loop, } -static int uv_set_pipe_handle(uv_loop_t* loop, - uv_pipe_t* handle, - HANDLE pipeHandle, - int fd, - DWORD duplex_flags) { +static int uv__set_pipe_handle(uv_loop_t* loop, + uv_pipe_t* handle, + HANDLE pipeHandle, + int fd, + DWORD duplex_flags) { NTSTATUS nt_status; IO_STATUS_BLOCK io_status; FILE_MODE_INFORMATION mode_info; @@ -462,7 +472,9 @@ static int uv_set_pipe_handle(uv_loop_t* loop, DWORD current_mode = 0; DWORD err = 0; - if (handle->flags & UV_HANDLE_PIPESERVER) + assert(handle->flags & UV_HANDLE_CONNECTION); + assert(!(handle->flags & UV_HANDLE_PIPESERVER)); + if (handle->flags & UV_HANDLE_CLOSING) return UV_EINVAL; if (handle->handle != INVALID_HANDLE_VALUE) return UV_EBUSY; @@ -478,18 +490,17 @@ static int uv_set_pipe_handle(uv_loop_t* loop, */ if (!GetNamedPipeHandleState(pipeHandle, ¤t_mode, NULL, NULL, NULL, NULL, 0)) { - return -1; + return uv_translate_sys_error(GetLastError()); } else if (current_mode & PIPE_NOWAIT) { - SetLastError(ERROR_ACCESS_DENIED); - return -1; + return UV_EACCES; } } else { /* If this returns ERROR_INVALID_PARAMETER we probably opened * something that is not a pipe. */ if (err == ERROR_INVALID_PARAMETER) { - SetLastError(WSAENOTSOCK); + return UV_ENOTSOCK; } - return -1; + return uv_translate_sys_error(err); } } @@ -500,13 +511,15 @@ static int uv_set_pipe_handle(uv_loop_t* loop, sizeof(mode_info), FileModeInformation); if (nt_status != STATUS_SUCCESS) { - return -1; + return uv_translate_sys_error(err); } if (mode_info.Mode & FILE_SYNCHRONOUS_IO_ALERT || mode_info.Mode & FILE_SYNCHRONOUS_IO_NONALERT) { /* Non-overlapped pipe. */ handle->flags |= UV_HANDLE_NON_OVERLAPPED_PIPE; + handle->pipe.conn.readfile_thread_handle = NULL; + InitializeCriticalSection(&handle->pipe.conn.readfile_thread_lock); } else { /* Overlapped pipe. Try to associate with IOCP. */ if (CreateIoCompletionPort(pipeHandle, @@ -578,135 +591,104 @@ static DWORD WINAPI pipe_shutdown_thread_proc(void* parameter) { } -void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { - int err; +void uv__pipe_shutdown(uv_loop_t* loop, uv_pipe_t* handle, uv_shutdown_t *req) { DWORD result; - uv_shutdown_t* req; NTSTATUS nt_status; IO_STATUS_BLOCK io_status; FILE_PIPE_LOCAL_INFORMATION pipe_info; - uv__ipc_xfer_queue_item_t* xfer_queue_item; - if ((handle->flags & UV_HANDLE_CONNECTION) && - handle->stream.conn.shutdown_req != NULL && - handle->stream.conn.write_reqs_pending == 0) { - req = handle->stream.conn.shutdown_req; - - /* Clear the shutdown_req field so we don't go here again. */ - handle->stream.conn.shutdown_req = NULL; - - if (handle->flags & UV_HANDLE_CLOSING) { - UNREGISTER_HANDLE_REQ(loop, handle, req); - - /* Already closing. Cancel the shutdown. */ - if (req->cb) { - req->cb(req, UV_ECANCELED); - } - - DECREASE_PENDING_REQ_COUNT(handle); - return; - } - - /* Try to avoid flushing the pipe buffer in the thread pool. */ - nt_status = pNtQueryInformationFile(handle->handle, - &io_status, - &pipe_info, - sizeof pipe_info, - FilePipeLocalInformation); - - if (nt_status != STATUS_SUCCESS) { - /* Failure */ - UNREGISTER_HANDLE_REQ(loop, handle, req); - - handle->flags |= UV_HANDLE_WRITABLE; /* Questionable */ - if (req->cb) { - err = pRtlNtStatusToDosError(nt_status); - req->cb(req, uv_translate_sys_error(err)); - } - - DECREASE_PENDING_REQ_COUNT(handle); - return; - } - - if (pipe_info.OutboundQuota == pipe_info.WriteQuotaAvailable) { - /* Short-circuit, no need to call FlushFileBuffers. */ - uv_insert_pending_req(loop, (uv_req_t*) req); - return; - } - - /* Run FlushFileBuffers in the thread pool. */ - result = QueueUserWorkItem(pipe_shutdown_thread_proc, - req, - WT_EXECUTELONGFUNCTION); - if (result) { - return; + assert(handle->flags & UV_HANDLE_CONNECTION); + assert(req != NULL); + assert(handle->stream.conn.write_reqs_pending == 0); + SET_REQ_SUCCESS(req); - } else { - /* Failure. */ - UNREGISTER_HANDLE_REQ(loop, handle, req); + if (handle->flags & UV_HANDLE_CLOSING) { + uv__insert_pending_req(loop, (uv_req_t*) req); + return; + } - handle->flags |= UV_HANDLE_WRITABLE; /* Questionable */ - if (req->cb) { - err = GetLastError(); - req->cb(req, uv_translate_sys_error(err)); - } + /* Try to avoid flushing the pipe buffer in the thread pool. */ + nt_status = pNtQueryInformationFile(handle->handle, + &io_status, + &pipe_info, + sizeof pipe_info, + FilePipeLocalInformation); - DECREASE_PENDING_REQ_COUNT(handle); - return; - } + if (nt_status != STATUS_SUCCESS) { + SET_REQ_ERROR(req, pRtlNtStatusToDosError(nt_status)); + handle->flags |= UV_HANDLE_WRITABLE; /* Questionable. */ + uv__insert_pending_req(loop, (uv_req_t*) req); + return; } - if (handle->flags & UV_HANDLE_CLOSING && - handle->reqs_pending == 0) { - assert(!(handle->flags & UV_HANDLE_CLOSED)); - - if (handle->flags & UV_HANDLE_CONNECTION) { - /* Free pending sockets */ - while (!QUEUE_EMPTY(&handle->pipe.conn.ipc_xfer_queue)) { - QUEUE* q; - SOCKET socket; + if (pipe_info.OutboundQuota == pipe_info.WriteQuotaAvailable) { + /* Short-circuit, no need to call FlushFileBuffers: + * all writes have been read. */ + uv__insert_pending_req(loop, (uv_req_t*) req); + return; + } - q = QUEUE_HEAD(&handle->pipe.conn.ipc_xfer_queue); - QUEUE_REMOVE(q); - xfer_queue_item = QUEUE_DATA(q, uv__ipc_xfer_queue_item_t, member); + /* Run FlushFileBuffers in the thread pool. */ + result = QueueUserWorkItem(pipe_shutdown_thread_proc, + req, + WT_EXECUTELONGFUNCTION); + if (!result) { + SET_REQ_ERROR(req, GetLastError()); + handle->flags |= UV_HANDLE_WRITABLE; /* Questionable. */ + uv__insert_pending_req(loop, (uv_req_t*) req); + return; + } +} - /* Materialize socket and close it */ - socket = WSASocketW(FROM_PROTOCOL_INFO, - FROM_PROTOCOL_INFO, - FROM_PROTOCOL_INFO, - &xfer_queue_item->xfer_info.socket_info, - 0, - WSA_FLAG_OVERLAPPED); - uv__free(xfer_queue_item); - if (socket != INVALID_SOCKET) - closesocket(socket); - } - handle->pipe.conn.ipc_xfer_queue_length = 0; +void uv__pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { + uv__ipc_xfer_queue_item_t* xfer_queue_item; - 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->reqs_pending == 0); + assert(handle->flags & UV_HANDLE_CLOSING); + assert(!(handle->flags & UV_HANDLE_CLOSED)); - if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) - DeleteCriticalSection(&handle->pipe.conn.readfile_thread_lock); + if (handle->flags & UV_HANDLE_CONNECTION) { + /* Free pending sockets */ + while (!uv__queue_empty(&handle->pipe.conn.ipc_xfer_queue)) { + struct uv__queue* q; + SOCKET socket; + + q = uv__queue_head(&handle->pipe.conn.ipc_xfer_queue); + uv__queue_remove(q); + xfer_queue_item = uv__queue_data(q, uv__ipc_xfer_queue_item_t, member); + + /* Materialize socket and close it */ + socket = WSASocketW(FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + &xfer_queue_item->xfer_info.socket_info, + 0, + WSA_FLAG_OVERLAPPED); + uv__free(xfer_queue_item); + + if (socket != INVALID_SOCKET) + closesocket(socket); } + handle->pipe.conn.ipc_xfer_queue_length = 0; - if (handle->flags & UV_HANDLE_PIPESERVER) { - assert(handle->pipe.serv.accept_reqs); - uv__free(handle->pipe.serv.accept_reqs); - handle->pipe.serv.accept_reqs = 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; } - uv__handle_close(handle); + if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) + DeleteCriticalSection(&handle->pipe.conn.readfile_thread_lock); + } + + if (handle->flags & UV_HANDLE_PIPESERVER) { + assert(handle->pipe.serv.accept_reqs); + uv__free(handle->pipe.serv.accept_reqs); + handle->pipe.serv.accept_reqs = NULL; } + + uv__handle_close(handle); } @@ -720,26 +702,60 @@ void uv_pipe_pending_instances(uv_pipe_t* handle, int count) { /* Creates a pipe server. */ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { + return uv_pipe_bind2(handle, name, strlen(name), 0); +} + + +int uv_pipe_bind2(uv_pipe_t* handle, + const char* name, + size_t namelen, + unsigned int flags) { uv_loop_t* loop = handle->loop; - int i, err, nameSize; + int i, err; uv_pipe_accept_t* req; + char* name_copy; + + if (flags & ~UV_PIPE_NO_TRUNCATE) { + return UV_EINVAL; + } + + if (name == NULL) { + return UV_EINVAL; + } + + if (namelen == 0) { + return UV_EINVAL; + } + + if (includes_nul(name, namelen)) { + return UV_EINVAL; + } if (handle->flags & UV_HANDLE_BOUND) { return UV_EINVAL; } - if (!name) { + if (uv__is_closing(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) { - uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); + if (handle->pipe.serv.accept_reqs == NULL) { + goto error; } for (i = 0; i < handle->pipe.serv.pending_instances; i++) { @@ -750,20 +766,12 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { req->next_pending = NULL; } - /* Convert name to UTF16. */ - nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR); - handle->name = uv__malloc(nameSize); - if (!handle->name) { - uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); - } + /* 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 (!MultiByteToWideChar(CP_UTF8, - 0, - name, - -1, - handle->name, - nameSize / sizeof(WCHAR))) { - err = GetLastError(); + if (err) { goto error; } @@ -777,9 +785,11 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { 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; } @@ -791,12 +801,13 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { return 0; error: - if (handle->name) { - uv__free(handle->name); - handle->name = NULL; - } + 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; } @@ -815,19 +826,22 @@ static DWORD WINAPI pipe_connect_thread_proc(void* parameter) { assert(loop); /* We're here because CreateFile on a pipe returned ERROR_PIPE_BUSY. We wait - * for the pipe to become available with WaitNamedPipe. */ - while (WaitNamedPipeW(handle->name, 30000)) { + * up to 30 seconds for the pipe to become available with WaitNamedPipe. */ + while (WaitNamedPipeW(req->u.connect.name, 30000)) { /* The pipe is now available, try to connect. */ - pipeHandle = open_named_pipe(handle->name, &duplex_flags); + pipeHandle = open_named_pipe(req->u.connect.name, &duplex_flags); if (pipeHandle != INVALID_HANDLE_VALUE) break; SwitchToThread(); } - if (pipeHandle != INVALID_HANDLE_VALUE && - !uv_set_pipe_handle(loop, handle, pipeHandle, -1, duplex_flags)) { + uv__free(req->u.connect.name); + req->u.connect.name = NULL; + if (pipeHandle != INVALID_HANDLE_VALUE) { SET_REQ_SUCCESS(req); + req->u.connect.pipeHandle = pipeHandle; + req->u.connect.duplex_flags = duplex_flags; } else { SET_REQ_ERROR(req, GetLastError()); } @@ -839,73 +853,133 @@ static DWORD WINAPI pipe_connect_thread_proc(void* parameter) { } -void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, - const char* name, uv_connect_cb cb) { - uv_loop_t* loop = handle->loop; - int err, nameSize; +void uv_pipe_connect(uv_connect_t* req, + uv_pipe_t* handle, + const char* name, + uv_connect_cb 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); + } +} + + +int uv_pipe_connect2(uv_connect_t* req, + uv_pipe_t* handle, + const char* name, + size_t namelen, + unsigned int flags, + uv_connect_cb cb) { + 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; - /* Convert name to UTF16. */ - nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR); - handle->name = uv__malloc(nameSize); - if (!handle->name) { - uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); + if (flags & ~UV_PIPE_NO_TRUNCATE) { + return UV_EINVAL; } - if (!MultiByteToWideChar(CP_UTF8, - 0, - name, - -1, - handle->name, - nameSize / sizeof(WCHAR))) { - err = GetLastError(); + if (name == NULL) { + return UV_EINVAL; + } + + if (namelen == 0) { + return UV_EINVAL; + } + + if (includes_nul(name, namelen)) { + 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) { + err = ERROR_INVALID_PARAMETER; + goto error; + } + if (handle->flags & UV_HANDLE_CONNECTION) { + err = ERROR_PIPE_BUSY; + goto error; + } + uv__pipe_connection_init(handle); + + /* 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; } pipeHandle = open_named_pipe(handle->name, &duplex_flags); if (pipeHandle == INVALID_HANDLE_VALUE) { if (GetLastError() == ERROR_PIPE_BUSY) { + nameSize = (wcslen(handle->name) + 1) * sizeof(WCHAR); + req->u.connect.name = uv__malloc(nameSize); + if (!req->u.connect.name) { + uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); + } + + memcpy(req->u.connect.name, handle->name, nameSize); + /* Wait for the server to make a pipe instance available. */ if (!QueueUserWorkItem(&pipe_connect_thread_proc, req, WT_EXECUTELONGFUNCTION)) { + uv__free(req->u.connect.name); + req->u.connect.name = NULL; err = GetLastError(); goto error; } - REGISTER_HANDLE_REQ(loop, handle, req); + REGISTER_HANDLE_REQ(loop, handle); handle->reqs_pending++; - return; + return 0; } err = GetLastError(); goto error; } - assert(pipeHandle != INVALID_HANDLE_VALUE); - - if (uv_set_pipe_handle(loop, - (uv_pipe_t*) req->handle, - pipeHandle, - -1, - duplex_flags)) { - err = GetLastError(); - goto error; - } - + req->u.connect.pipeHandle = pipeHandle; + req->u.connect.duplex_flags = duplex_flags; SET_REQ_SUCCESS(req); - uv_insert_pending_req(loop, (uv_req_t*) req); + uv__insert_pending_req(loop, (uv_req_t*) req); handle->reqs_pending++; - REGISTER_HANDLE_REQ(loop, handle, req); - return; + REGISTER_HANDLE_REQ(loop, handle); + return 0; error: + uv__free(name_copy); + if (handle->name) { uv__free(handle->name); handle->name = NULL; @@ -916,10 +990,10 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, err); - uv_insert_pending_req(loop, (uv_req_t*) req); + uv__insert_pending_req(loop, (uv_req_t*) req); handle->reqs_pending++; - REGISTER_HANDLE_REQ(loop, handle, req); - return; + REGISTER_HANDLE_REQ(loop, handle); + return 0; } @@ -937,7 +1011,7 @@ void uv__pipe_interrupt_read(uv_pipe_t* handle) { /* Cancel asynchronous read. */ r = CancelIoEx(handle->handle, &handle->read_req.u.io.overlapped); assert(r || GetLastError() == ERROR_NOT_FOUND); - + (void) r; } else { /* Cancel synchronous read (which is happening in the thread pool). */ HANDLE thread; @@ -973,17 +1047,30 @@ void uv__pipe_interrupt_read(uv_pipe_t* handle) { void uv__pipe_read_stop(uv_pipe_t* handle) { handle->flags &= ~UV_HANDLE_READING; DECREASE_ACTIVE_COUNT(handle->loop, handle); - uv__pipe_interrupt_read(handle); } /* Cleans up uv_pipe_t (server or connection) and all resources associated with * it. */ -void uv_pipe_cleanup(uv_loop_t* loop, uv_pipe_t* handle) { +void uv__pipe_close(uv_loop_t* loop, uv_pipe_t* handle) { int i; HANDLE pipeHandle; + if (handle->flags & UV_HANDLE_READING) { + handle->flags &= ~UV_HANDLE_READING; + DECREASE_ACTIVE_COUNT(loop, handle); + } + + if (handle->flags & UV_HANDLE_LISTENING) { + handle->flags &= ~UV_HANDLE_LISTENING; + DECREASE_ACTIVE_COUNT(loop, handle); + } + + handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); + + uv__handle_closing(handle); + uv__pipe_interrupt_read(handle); if (handle->name) { @@ -1003,45 +1090,27 @@ void uv_pipe_cleanup(uv_loop_t* loop, uv_pipe_t* handle) { } if (handle->flags & UV_HANDLE_CONNECTION) { - handle->flags &= ~UV_HANDLE_WRITABLE; eof_timer_destroy(handle); } if ((handle->flags & UV_HANDLE_CONNECTION) - && handle->handle != INVALID_HANDLE_VALUE) + && handle->handle != INVALID_HANDLE_VALUE) { + /* This will eventually destroy the write queue for us too. */ close_pipe(handle); -} - - -void uv_pipe_close(uv_loop_t* loop, uv_pipe_t* handle) { - if (handle->flags & UV_HANDLE_READING) { - handle->flags &= ~UV_HANDLE_READING; - DECREASE_ACTIVE_COUNT(loop, handle); - } - - if (handle->flags & UV_HANDLE_LISTENING) { - handle->flags &= ~UV_HANDLE_LISTENING; - DECREASE_ACTIVE_COUNT(loop, handle); - } - - uv_pipe_cleanup(loop, handle); - - if (handle->reqs_pending == 0) { - uv_want_endgame(loop, (uv_handle_t*) handle); } - handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); - uv__handle_closing(handle); + if (handle->reqs_pending == 0) + uv__want_endgame(loop, (uv_handle_t*) handle); } -static void uv_pipe_queue_accept(uv_loop_t* loop, uv_pipe_t* handle, +static void uv__pipe_queue_accept(uv_loop_t* loop, uv_pipe_t* handle, uv_pipe_accept_t* req, BOOL firstInstance) { assert(handle->flags & UV_HANDLE_LISTENING); if (!firstInstance && !pipe_alloc_accept(loop, handle, req, FALSE)) { SET_REQ_ERROR(req, GetLastError()); - uv_insert_pending_req(loop, (uv_req_t*) req); + uv__insert_pending_req(loop, (uv_req_t*) req); handle->reqs_pending++; return; } @@ -1061,7 +1130,7 @@ static void uv_pipe_queue_accept(uv_loop_t* loop, uv_pipe_t* handle, /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, GetLastError()); } - uv_insert_pending_req(loop, (uv_req_t*) req); + uv__insert_pending_req(loop, (uv_req_t*) req); handle->reqs_pending++; return; } @@ -1071,34 +1140,36 @@ static void uv_pipe_queue_accept(uv_loop_t* loop, uv_pipe_t* handle, } -int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client) { +int uv__pipe_accept(uv_pipe_t* server, uv_stream_t* client) { uv_loop_t* loop = server->loop; uv_pipe_t* pipe_client; uv_pipe_accept_t* req; - QUEUE* q; + struct uv__queue* q; uv__ipc_xfer_queue_item_t* item; int err; if (server->ipc) { - if (QUEUE_EMPTY(&server->pipe.conn.ipc_xfer_queue)) { + if (uv__queue_empty(&server->pipe.conn.ipc_xfer_queue)) { /* No valid pending sockets. */ return WSAEWOULDBLOCK; } - q = QUEUE_HEAD(&server->pipe.conn.ipc_xfer_queue); - QUEUE_REMOVE(q); + q = uv__queue_head(&server->pipe.conn.ipc_xfer_queue); + uv__queue_remove(q); server->pipe.conn.ipc_xfer_queue_length--; - item = QUEUE_DATA(q, uv__ipc_xfer_queue_item_t, member); + item = uv__queue_data(q, uv__ipc_xfer_queue_item_t, member); err = uv__tcp_xfer_import( (uv_tcp_t*) client, item->xfer_type, &item->xfer_info); - if (err != 0) - return err; uv__free(item); + if (err != 0) + return err; + } else { pipe_client = (uv_pipe_t*) client; + uv__pipe_connection_init(pipe_client); /* Find a connection instance that has been connected, but not yet * accepted. */ @@ -1110,7 +1181,6 @@ int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client) { } /* Initialize the client handle and copy the pipeHandle to the client */ - uv_pipe_connection_init(pipe_client); pipe_client->handle = req->pipeHandle; pipe_client->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; @@ -1121,7 +1191,7 @@ int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client) { server->handle = INVALID_HANDLE_VALUE; if (!(server->flags & UV_HANDLE_CLOSING)) { - uv_pipe_queue_accept(loop, server, req, FALSE); + uv__pipe_queue_accept(loop, server, req, FALSE); } } @@ -1130,7 +1200,7 @@ int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client) { /* Starts listening for connections for the given pipe. */ -int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { +int uv__pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { uv_loop_t* loop = handle->loop; int i; @@ -1162,7 +1232,7 @@ int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { assert(handle->pipe.serv.accept_reqs[0].pipeHandle != INVALID_HANDLE_VALUE); for (i = 0; i < handle->pipe.serv.pending_instances; i++) { - uv_pipe_queue_accept(loop, handle, &handle->pipe.serv.accept_reqs[i], i == 0); + uv__pipe_queue_accept(loop, handle, &handle->pipe.serv.accept_reqs[i], i == 0); } return 0; @@ -1306,7 +1376,7 @@ static void CALLBACK post_completion_write_wait(void* context, BOOLEAN timed_out } -static void uv_pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) { +static void uv__pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) { uv_read_t* req; int result; @@ -1347,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; } } } @@ -1365,15 +1434,15 @@ static void uv_pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) { return; error: - uv_insert_pending_req(loop, (uv_req_t*)req); + uv__insert_pending_req(loop, (uv_req_t*)req); handle->flags |= UV_HANDLE_READ_PENDING; handle->reqs_pending++; } -int uv_pipe_read_start(uv_pipe_t* handle, - uv_alloc_cb alloc_cb, - uv_read_cb read_cb) { +int uv__pipe_read_start(uv_pipe_t* handle, + uv_alloc_cb alloc_cb, + uv_read_cb read_cb) { uv_loop_t* loop = handle->loop; handle->flags |= UV_HANDLE_READING; @@ -1381,24 +1450,24 @@ 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); + uv__pipe_queue_read(loop, handle); } return 0; } -static void uv_insert_non_overlapped_write_req(uv_pipe_t* handle, +static void uv__insert_non_overlapped_write_req(uv_pipe_t* handle, uv_write_t* req) { req->next_req = NULL; if (handle->pipe.conn.non_overlapped_writes_tail) { @@ -1434,7 +1503,7 @@ static uv_write_t* uv_remove_non_overlapped_write_req(uv_pipe_t* handle) { } -static void uv_queue_non_overlapped_write(uv_pipe_t* handle) { +static void uv__queue_non_overlapped_write(uv_pipe_t* handle) { uv_write_t* req = uv_remove_non_overlapped_write_req(handle); if (req) { if (!QueueUserWorkItem(&uv_pipe_writefile_thread_proc, @@ -1568,16 +1637,16 @@ 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); return 0; } else if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) { req->write_buffer = write_buf; - uv_insert_non_overlapped_write_req(handle, req); + uv__insert_non_overlapped_write_req(handle, req); if (handle->stream.conn.write_reqs_pending == 0) { - uv_queue_non_overlapped_write(handle); + uv__queue_non_overlapped_write(handle); } /* Request queued by the kernel. */ @@ -1616,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; @@ -1643,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++; @@ -1663,8 +1732,12 @@ static DWORD uv__pipe_get_ipc_remote_pid(uv_pipe_t* handle) { /* If the both ends of the IPC pipe are owned by the same process, * the remote end pid may not yet be set. If so, do it here. * TODO: this is weird; it'd probably better to use a handshake. */ - if (*pid == 0) - *pid = GetCurrentProcessId(); + if (*pid == 0) { + GetNamedPipeClientProcessId(handle->handle, pid); + if (*pid == GetCurrentProcessId()) { + GetNamedPipeServerProcessId(handle->handle, pid); + } + } return *pid; } @@ -1790,7 +1863,7 @@ int uv__pipe_write(uv_loop_t* loop, } -static void uv_pipe_read_eof(uv_loop_t* loop, uv_pipe_t* handle, +static void uv__pipe_read_eof(uv_loop_t* loop, uv_pipe_t* handle, uv_buf_t buf) { /* If there is an eof timer running, we don't need it any more, so discard * it. */ @@ -1802,7 +1875,7 @@ static void uv_pipe_read_eof(uv_loop_t* loop, uv_pipe_t* handle, } -static void uv_pipe_read_error(uv_loop_t* loop, uv_pipe_t* handle, int error, +static void uv__pipe_read_error(uv_loop_t* loop, uv_pipe_t* handle, int error, uv_buf_t buf) { /* If there is an eof timer running, we don't need it any more, so discard * it. */ @@ -1814,12 +1887,12 @@ 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) { +static void uv__pipe_read_error_or_eof(uv_loop_t* loop, uv_pipe_t* handle, + DWORD error, uv_buf_t buf) { if (error == ERROR_BROKEN_PIPE) { - uv_pipe_read_eof(loop, handle, buf); + uv__pipe_read_eof(loop, handle, buf); } else { - uv_pipe_read_error(loop, handle, error, buf); + uv__pipe_read_error(loop, handle, error, buf); } } @@ -1837,7 +1910,7 @@ static void uv__pipe_queue_ipc_xfer_info( item->xfer_type = xfer_type; item->xfer_info = *xfer_info; - QUEUE_INSERT_TAIL(&handle->pipe.conn.ipc_xfer_queue, &item->member); + uv__queue_insert_tail(&handle->pipe.conn.ipc_xfer_queue, &item->member); handle->pipe.conn.ipc_xfer_queue_length++; } @@ -1845,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; @@ -1866,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. */ @@ -1884,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. */ @@ -1921,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; @@ -1957,40 +2085,51 @@ 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. */ - - /* 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); + 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; - /* 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. */ error: - uv_pipe_read_error_or_eof(loop, handle, err, uv_null_buf_); + uv__pipe_read_error_or_eof(loop, handle, err, uv_null_buf_); return 0; /* Break out of read loop. */ } -void uv_process_pipe_read_req(uv_loop_t* loop, - uv_pipe_t* handle, - uv_req_t* req) { +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. */ @@ -1999,57 +2138,41 @@ 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 * the user; we'll start a new zero-read at the end of this function. */ if (err != ERROR_OPERATION_ABORTED) - uv_pipe_read_error_or_eof(loop, handle, err, uv_null_buf_); + uv__pipe_read_error_or_eof(loop, handle, err, uv_null_buf_); } 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) - 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) + if (more == 0) break; - - /* Recompute the number of bytes available. */ - avail -= bytes_read; } } /* Start another zero-read request if necessary. */ if ((handle->flags & UV_HANDLE_READING) && !(handle->flags & UV_HANDLE_READ_PENDING)) { - uv_pipe_queue_read(loop, handle); + uv__pipe_queue_read(loop, handle); } } -void uv_process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle, +void uv__process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle, uv_write_t* req) { int err; @@ -2058,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); @@ -2091,26 +2212,25 @@ void uv_process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle, if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE && handle->pipe.conn.non_overlapped_writes_tail) { assert(handle->stream.conn.write_reqs_pending > 0); - uv_queue_non_overlapped_write(handle); + uv__queue_non_overlapped_write(handle); } - if (handle->stream.conn.shutdown_req != NULL && - handle->stream.conn.write_reqs_pending == 0) { - uv_want_endgame(loop, (uv_handle_t*)handle); - } + if (handle->stream.conn.write_reqs_pending == 0 && + uv__is_stream_shutting(handle)) + uv__pipe_shutdown(loop, handle, handle->stream.conn.shutdown_req); DECREASE_PENDING_REQ_COUNT(handle); } -void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle, +void uv__process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle, uv_req_t* raw_req) { uv_pipe_accept_t* req = (uv_pipe_accept_t*) raw_req; assert(handle->type == UV_NAMED_PIPE); if (handle->flags & UV_HANDLE_CLOSING) { - /* The req->pipeHandle should be freed already in uv_pipe_cleanup(). */ + /* The req->pipeHandle should be freed already in uv__pipe_close(). */ assert(req->pipeHandle == INVALID_HANDLE_VALUE); DECREASE_PENDING_REQ_COUNT(handle); return; @@ -2130,7 +2250,7 @@ void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle, req->pipeHandle = INVALID_HANDLE_VALUE; } if (!(handle->flags & UV_HANDLE_CLOSING)) { - uv_pipe_queue_accept(loop, handle, req, FALSE); + uv__pipe_queue_accept(loop, handle, req, FALSE); } } @@ -2138,54 +2258,76 @@ void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle, } -void uv_process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle, +void uv__process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle, uv_connect_t* req) { + HANDLE pipeHandle; + DWORD duplex_flags; int err; assert(handle->type == UV_NAMED_PIPE); - UNREGISTER_HANDLE_REQ(loop, handle, req); + UNREGISTER_HANDLE_REQ(loop, handle); - if (req->cb) { - err = 0; - if (REQ_SUCCESS(req)) { - uv_pipe_connection_init(handle); - } else { - err = GET_REQ_ERROR(req); - } - req->cb(req, uv_translate_sys_error(err)); + err = 0; + if (REQ_SUCCESS(req)) { + pipeHandle = req->u.connect.pipeHandle; + duplex_flags = req->u.connect.duplex_flags; + if (handle->flags & UV_HANDLE_CLOSING) + err = UV_ECANCELED; + else + err = uv__set_pipe_handle(loop, handle, pipeHandle, -1, duplex_flags); + if (err) + CloseHandle(pipeHandle); + } else { + err = uv_translate_sys_error(GET_REQ_ERROR(req)); } + if (req->cb) + req->cb(req, err); + DECREASE_PENDING_REQ_COUNT(handle); } -void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle, + +void uv__process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle, uv_shutdown_t* req) { + int err; + assert(handle->type == UV_NAMED_PIPE); - UNREGISTER_HANDLE_REQ(loop, handle, req); + /* Clear the shutdown_req field so we don't go here again. */ + handle->stream.conn.shutdown_req = NULL; + UNREGISTER_HANDLE_REQ(loop, handle); - if (handle->flags & UV_HANDLE_READABLE) { - /* Initialize and optionally start the eof timer. Only do this if the pipe - * is readable and we haven't seen EOF come in ourselves. */ - eof_timer_init(handle); + if (handle->flags & UV_HANDLE_CLOSING) { + /* Already closing. Cancel the shutdown. */ + err = UV_ECANCELED; + } else if (!REQ_SUCCESS(req)) { + /* An error occurred in trying to shutdown gracefully. */ + err = uv_translate_sys_error(GET_REQ_ERROR(req)); + } else { + if (handle->flags & UV_HANDLE_READABLE) { + /* Initialize and optionally start the eof timer. Only do this if the pipe + * is readable and we haven't seen EOF come in ourselves. */ + eof_timer_init(handle); + + /* If reading start the timer right now. Otherwise uv__pipe_queue_read will + * start it. */ + if (handle->flags & UV_HANDLE_READ_PENDING) { + eof_timer_start(handle); + } - /* If reading start the timer right now. Otherwise uv_pipe_queue_read will - * start it. */ - if (handle->flags & UV_HANDLE_READ_PENDING) { - eof_timer_start(handle); + } else { + /* This pipe is not readable. We can just close it to let the other end + * know that we're done writing. */ + close_pipe(handle); } - - } else { - /* This pipe is not readable. We can just close it to let the other end - * know that we're done writing. */ - close_pipe(handle); + err = 0; } - if (req->cb) { - req->cb(req, 0); - } + if (req->cb) + req->cb(req, err); DECREASE_PENDING_REQ_COUNT(handle); } @@ -2200,7 +2342,8 @@ static void eof_timer_init(uv_pipe_t* pipe) { pipe->pipe.conn.eof_timer = (uv_timer_t*) uv__malloc(sizeof *pipe->pipe.conn.eof_timer); r = uv_timer_init(pipe->loop, pipe->pipe.conn.eof_timer); - assert(r == 0); /* timers can't fail */ + assert(r == 0); /* timers can't fail */ + (void) r; pipe->pipe.conn.eof_timer->data = pipe; uv_unref((uv_handle_t*) pipe->pipe.conn.eof_timer); } @@ -2231,9 +2374,9 @@ static void eof_timer_cb(uv_timer_t* timer) { assert(pipe->type == UV_NAMED_PIPE); /* This should always be true, since we start the timer only in - * uv_pipe_queue_read after successfully calling ReadFile, or in - * uv_process_pipe_shutdown_req if a read is pending, and we always - * immediately stop the timer in uv_process_pipe_read_req. */ + * uv__pipe_queue_read after successfully calling ReadFile, or in + * uv__process_pipe_shutdown_req if a read is pending, and we always + * immediately stop the timer in uv__process_pipe_read_req. */ assert(pipe->flags & UV_HANDLE_READ_PENDING); /* If there are many packets coming off the iocp then the timer callback may @@ -2254,7 +2397,7 @@ static void eof_timer_cb(uv_timer_t* timer) { /* Report the eof and update flags. This will get reported even if the user * stopped reading in the meantime. TODO: is that okay? */ - uv_pipe_read_eof(loop, pipe, uv_null_buf_); + uv__pipe_read_eof(loop, pipe, uv_null_buf_); } @@ -2280,10 +2423,16 @@ int uv_pipe_open(uv_pipe_t* pipe, uv_file file) { IO_STATUS_BLOCK io_status; FILE_ACCESS_INFORMATION access; DWORD duplex_flags = 0; + int err; if (os_handle == INVALID_HANDLE_VALUE) return UV_EBADF; + if (pipe->flags & UV_HANDLE_PIPESERVER) + return UV_EINVAL; + if (pipe->flags & UV_HANDLE_CONNECTION) + return UV_EBUSY; + uv__pipe_connection_init(pipe); uv__once_init(); /* In order to avoid closing a stdio file descriptor 0-2, duplicate the * underlying OS handle and forget about the original fd. @@ -2300,6 +2449,7 @@ int uv_pipe_open(uv_pipe_t* pipe, uv_file file) { FALSE, DUPLICATE_SAME_ACCESS)) return uv_translate_sys_error(GetLastError()); + assert(os_handle != INVALID_HANDLE_VALUE); file = -1; } @@ -2327,20 +2477,23 @@ int uv_pipe_open(uv_pipe_t* pipe, uv_file file) { if (access.AccessFlags & FILE_READ_DATA) duplex_flags |= UV_HANDLE_READABLE; - if (os_handle == INVALID_HANDLE_VALUE || - uv_set_pipe_handle(pipe->loop, - pipe, - os_handle, - file, - duplex_flags) == -1) { - return UV_EINVAL; + err = uv__set_pipe_handle(pipe->loop, + pipe, + os_handle, + file, + duplex_flags); + if (err) { + if (file == -1) + CloseHandle(os_handle); + return err; } - uv_pipe_connection_init(pipe); - if (pipe->ipc) { assert(!(pipe->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)); - pipe->pipe.conn.ipc_remote_pid = uv_os_getppid(); + GetNamedPipeClientProcessId(os_handle, &pipe->pipe.conn.ipc_remote_pid); + if (pipe->pipe.conn.ipc_remote_pid == GetCurrentProcessId()) { + GetNamedPipeServerProcessId(os_handle, &pipe->pipe.conn.ipc_remote_pid); + } assert(pipe->pipe.conn.ipc_remote_pid != (DWORD)(uv_pid_t) -1); } return 0; @@ -2353,7 +2506,6 @@ static int uv__pipe_getname(const uv_pipe_t* handle, char* buffer, size_t* size) FILE_NAME_INFORMATION tmp_name_info; FILE_NAME_INFORMATION* name_info; WCHAR* name_buf; - unsigned int addrlen; unsigned int name_size; unsigned int name_len; int err; @@ -2361,6 +2513,12 @@ static int uv__pipe_getname(const uv_pipe_t* handle, char* buffer, size_t* size) uv__once_init(); name_info = NULL; + if (handle->name != NULL) { + /* The user might try to query the name before we are connected, + * and this is just easier to return the cached value if we have it. */ + return uv__copy_utf16_to_utf8(handle->name, -1, buffer, size); + } + if (handle->handle == INVALID_HANDLE_VALUE) { *size = 0; return UV_EINVAL; @@ -2386,8 +2544,7 @@ static int uv__pipe_getname(const uv_pipe_t* handle, char* buffer, size_t* size) name_info = uv__malloc(name_size); if (!name_info) { *size = 0; - err = UV_ENOMEM; - goto cleanup; + return UV_ENOMEM; } nt_status = pNtQueryInformationFile(handle->handle, @@ -2420,51 +2577,19 @@ static int uv__pipe_getname(const uv_pipe_t* handle, char* buffer, size_t* size) name_len /= sizeof(WCHAR); - /* check how much space we need */ - addrlen = WideCharToMultiByte(CP_UTF8, - 0, - name_buf, - name_len, - NULL, - 0, - NULL, - NULL); - if (!addrlen) { + /* "\\\\.\\pipe" + name */ + if (*size < pipe_prefix_len) { *size = 0; - err = uv_translate_sys_error(GetLastError()); - goto error; - } else if (pipe_prefix_len + addrlen >= *size) { - /* "\\\\.\\pipe" + name */ - *size = pipe_prefix_len + addrlen + 1; - err = UV_ENOBUFS; - goto error; } - - memcpy(buffer, pipe_prefix, pipe_prefix_len); - addrlen = WideCharToMultiByte(CP_UTF8, - 0, - name_buf, - name_len, - buffer+pipe_prefix_len, - *size-pipe_prefix_len, - NULL, - NULL); - if (!addrlen) { - *size = 0; - err = uv_translate_sys_error(GetLastError()); - goto error; + else { + memcpy(buffer, pipe_prefix, pipe_prefix_len); + *size -= pipe_prefix_len; } - - addrlen += pipe_prefix_len; - *size = addrlen; - buffer[addrlen] = '\0'; - - err = 0; + err = uv__copy_utf16_to_utf8(name_buf, name_len, buffer+pipe_prefix_len, size); + *size += pipe_prefix_len; error: uv__free(name_info); - -cleanup: return err; } @@ -2477,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); @@ -2491,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; @@ -2498,6 +2629,11 @@ int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size) { if (handle->handle != INVALID_HANDLE_VALUE) return uv__pipe_getname(handle, buffer, size); + if (handle->flags & UV_HANDLE_CONNECTION) { + if (handle->name != NULL) + return uv__pipe_getname(handle, buffer, size); + } + return UV_EBADF; } diff --git a/src/win/poll.c b/src/win/poll.c index 9d377596066..7fec2b99650 100644 --- a/src/win/poll.c +++ b/src/win/poll.c @@ -34,7 +34,9 @@ static const GUID uv_msafd_provider_ids[UV_MSAFD_PROVIDER_COUNT] = { {0xf9eab0c0, 0x26d4, 0x11d0, {0xbb, 0xbf, 0x00, 0xaa, 0x00, 0x6c, 0x34, 0xe4}}, {0x9fc48064, 0x7298, 0x43e4, - {0xb7, 0xbd, 0x18, 0x1f, 0x20, 0x89, 0x79, 0x2a}} + {0xb7, 0xbd, 0x18, 0x1f, 0x20, 0x89, 0x79, 0x2a}}, + {0xa00943d9, 0x9c2e, 0x4633, + {0x9b, 0x59, 0x00, 0x57, 0xa3, 0x16, 0x09, 0x94}} }; typedef struct uv_single_fd_set_s { @@ -122,14 +124,14 @@ static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) { memset(&req->u.io.overlapped, 0, sizeof req->u.io.overlapped); - result = uv_msafd_poll((SOCKET) handle->peer_socket, - afd_poll_info, - afd_poll_info, - &req->u.io.overlapped); + result = uv__msafd_poll((SOCKET) handle->peer_socket, + afd_poll_info, + afd_poll_info, + &req->u.io.overlapped); if (result != 0 && WSAGetLastError() != WSA_IO_PENDING) { /* Queue this req, reporting an error. */ SET_REQ_ERROR(req, WSAGetLastError()); - uv_insert_pending_req(loop, req); + uv__insert_pending_req(loop, req); } } @@ -195,7 +197,7 @@ static void uv__fast_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, } else if ((handle->flags & UV_HANDLE_CLOSING) && handle->submitted_events_1 == 0 && handle->submitted_events_2 == 0) { - uv_want_endgame(loop, (uv_handle_t*) handle); + uv__want_endgame(loop, (uv_handle_t*) handle); } } @@ -357,7 +359,7 @@ static void uv__slow_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) { WT_EXECUTELONGFUNCTION)) { /* Make this req pending, reporting an error. */ SET_REQ_ERROR(req, GetLastError()); - uv_insert_pending_req(loop, req); + uv__insert_pending_req(loop, req); } } @@ -400,7 +402,7 @@ static void uv__slow_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, } else if ((handle->flags & UV_HANDLE_CLOSING) && handle->submitted_events_1 == 0 && handle->submitted_events_2 == 0) { - uv_want_endgame(loop, (uv_handle_t*) handle); + uv__want_endgame(loop, (uv_handle_t*) handle); } } @@ -423,9 +425,8 @@ int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle, return uv_translate_sys_error(WSAGetLastError()); /* Try to obtain a base handle for the socket. This increases this chances that - * we find an AFD handle and are able to use the fast poll mechanism. This will - * always fail on windows XP/2k3, since they don't support the. SIO_BASE_HANDLE - * ioctl. */ + * we find an AFD handle and are able to use the fast poll mechanism. + */ #ifndef NDEBUG base_socket = INVALID_SOCKET; #endif @@ -524,7 +525,7 @@ int uv_poll_stop(uv_poll_t* handle) { } -void uv_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req) { +void uv__process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req) { if (!(handle->flags & UV_HANDLE_POLL_SLOW)) { uv__fast_poll_process_poll_req(loop, handle, req); } else { @@ -533,7 +534,7 @@ void uv_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req) { } -int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle) { +int uv__poll_close(uv_loop_t* loop, uv_poll_t* handle) { AFD_POLL_INFO afd_poll_info; DWORD error; int result; @@ -543,7 +544,7 @@ int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle) { if (handle->submitted_events_1 == 0 && handle->submitted_events_2 == 0) { - uv_want_endgame(loop, (uv_handle_t*) handle); + uv__want_endgame(loop, (uv_handle_t*) handle); return 0; } @@ -559,10 +560,10 @@ int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle) { afd_poll_info.Handles[0].Status = 0; afd_poll_info.Handles[0].Events = AFD_POLL_ALL; - result = uv_msafd_poll(handle->socket, - &afd_poll_info, - uv__get_afd_poll_info_dummy(), - uv__get_overlapped_dummy()); + result = uv__msafd_poll(handle->socket, + &afd_poll_info, + uv__get_afd_poll_info_dummy(), + uv__get_overlapped_dummy()); if (result == SOCKET_ERROR) { error = WSAGetLastError(); @@ -574,7 +575,7 @@ int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle) { } -void uv_poll_endgame(uv_loop_t* loop, uv_poll_t* handle) { +void uv__poll_endgame(uv_loop_t* loop, uv_poll_t* handle) { assert(handle->flags & UV_HANDLE_CLOSING); assert(!(handle->flags & UV_HANDLE_CLOSED)); diff --git a/src/win/process-stdio.c b/src/win/process-stdio.c index 0db35723731..181db92ea30 100644 --- a/src/win/process-stdio.c +++ b/src/win/process-stdio.c @@ -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; } diff --git a/src/win/process.c b/src/win/process.c index 68d70c76b2a..27605ca36f4 100644 --- a/src/win/process.c +++ b/src/win/process.c @@ -26,12 +26,14 @@ #include #include #include -#include /* alloca */ #include "uv.h" #include "internal.h" #include "handle-inl.h" #include "req-inl.h" +#include +#include +#include /* GetModuleBaseNameW */ #define SIGKILL 9 @@ -102,49 +104,36 @@ static void uv__init_global_job_handle(void) { &info, sizeof info)) uv_fatal_error(GetLastError(), "SetInformationJobObject"); -} - -static int uv_utf8_to_utf16_alloc(const char* s, WCHAR** ws_ptr) { - int ws_len, r; - WCHAR* ws; - ws_len = MultiByteToWideChar(CP_UTF8, - 0, - s, - -1, - NULL, - 0); - if (ws_len <= 0) { - return GetLastError(); - } - - ws = (WCHAR*) uv__malloc(ws_len * sizeof(WCHAR)); - if (ws == NULL) { - return ERROR_OUTOFMEMORY; + if (!AssignProcessToJobObject(uv_global_job_handle_, GetCurrentProcess())) { + /* Make sure this handle is functional. The Windows kernel has a bug that + * if the first use of AssignProcessToJobObject is for a Windows Store + * program, subsequent attempts to use the handle with fail with + * INVALID_PARAMETER (87). This is possibly because all uses of the handle + * must be for the same Terminal Services session. We can ensure it is tied + * to our current session now by adding ourself to it. We could remove + * ourself afterwards, but there doesn't seem to be a reason to. + */ + DWORD err = GetLastError(); + if (err != ERROR_ACCESS_DENIED) + uv_fatal_error(err, "AssignProcessToJobObject"); } +} - r = MultiByteToWideChar(CP_UTF8, - 0, - s, - -1, - ws, - ws_len); - assert(r == ws_len); - *ws_ptr = ws; - return 0; +static int uv__utf8_to_utf16_alloc(const char* s, WCHAR** ws_ptr) { + return uv__convert_utf8_to_utf16(s, ws_ptr); } -static void uv_process_init(uv_loop_t* loop, uv_process_t* handle) { +static void uv__process_init(uv_loop_t* loop, uv_process_t* handle) { uv__handle_init(loop, (uv_handle_t*) handle, UV_PROCESS); handle->exit_cb = NULL; handle->pid = 0; handle->exit_signal = 0; handle->wait_handle = INVALID_HANDLE_VALUE; handle->process_handle = INVALID_HANDLE_VALUE; - handle->child_stdio_buffer = NULL; handle->exit_cb_pending = 0; UV_REQ_INIT(&handle->exit_req, UV_PROCESS_EXIT); @@ -314,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. @@ -341,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; @@ -382,19 +373,21 @@ 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 == L'\0') { + if (dir_end == NULL || *dir_end == L'\0') { break; } @@ -519,7 +512,7 @@ WCHAR* quote_cmd_arg(const WCHAR *source, WCHAR *target) { } } target[0] = L'\0'; - wcsrev(start); + _wcsrev(start); *(target++) = L'"'; return target; } @@ -537,21 +530,15 @@ int make_program_args(char** args, int verbatim_arguments, WCHAR** dst_ptr) { /* Count the required size. */ for (arg = args; *arg; arg++) { - DWORD arg_len; - - arg_len = MultiByteToWideChar(CP_UTF8, - 0, - *arg, - -1, - NULL, - 0); - if (arg_len == 0) { - return GetLastError(); - } + ssize_t arg_len; + + arg_len = uv_wtf8_length_as_utf16(*arg); + if (arg_len < 0) + return arg_len; dst_len += arg_len; - if (arg_len > temp_buffer_len) + if ((size_t) arg_len > temp_buffer_len) temp_buffer_len = arg_len; arg_count++; @@ -562,34 +549,28 @@ int make_program_args(char** args, int verbatim_arguments, WCHAR** dst_ptr) { dst_len = dst_len * 2 + arg_count * 2; /* Allocate buffer for the final command line. */ - dst = (WCHAR*) uv__malloc(dst_len * sizeof(WCHAR)); + dst = uv__malloc(dst_len * sizeof(WCHAR)); if (dst == NULL) { - err = ERROR_OUTOFMEMORY; + err = UV_ENOMEM; goto error; } /* Allocate temporary working buffer. */ - temp_buffer = (WCHAR*) uv__malloc(temp_buffer_len * sizeof(WCHAR)); + temp_buffer = uv__malloc(temp_buffer_len * sizeof(WCHAR)); if (temp_buffer == NULL) { - err = ERROR_OUTOFMEMORY; + err = UV_ENOMEM; goto error; } pos = dst; for (arg = args; *arg; arg++) { - DWORD arg_len; + ssize_t arg_len; /* Convert argument to wide char. */ - arg_len = MultiByteToWideChar(CP_UTF8, - 0, - *arg, - -1, - temp_buffer, - (int) (dst + dst_len - pos)); - if (arg_len == 0) { - err = GetLastError(); - goto error; - } + arg_len = uv_wtf8_length_as_utf16(*arg); + assert(arg_len > 0); + assert(temp_buffer_len >= (size_t) arg_len); + uv_wtf8_to_utf16(*arg, temp_buffer, arg_len); if (verbatim_arguments) { /* Copy verbatim. */ @@ -601,6 +582,7 @@ int make_program_args(char** args, int verbatim_arguments, WCHAR** dst_ptr) { } *pos++ = *(arg + 1) ? L' ' : L'\0'; + assert(pos <= dst + dst_len); } uv__free(temp_buffer); @@ -615,11 +597,9 @@ int make_program_args(char** args, int verbatim_arguments, WCHAR** dst_ptr) { } -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; @@ -634,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; } @@ -686,55 +647,46 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) { WCHAR* ptr; char** env; size_t env_len = 0; - int len; + size_t len; size_t i; - DWORD var_size; + size_t var_size; size_t env_block_count = 1; /* 1 for null-terminator */ WCHAR* dst_copy; WCHAR** ptr_copy; WCHAR** env_copy; - DWORD required_vars_value_len[ARRAY_SIZE(required_vars)]; + char* p; + size_t required_vars_value_len[ARRAY_SIZE(required_vars)]; /* first pass: determine size in UTF-16 */ for (env = env_block; *env; env++) { - int len; + ssize_t len; if (strchr(*env, '=')) { - len = MultiByteToWideChar(CP_UTF8, - 0, - *env, - -1, - NULL, - 0); - if (len <= 0) { - return GetLastError(); - } + len = uv_wtf8_length_as_utf16(*env); + if (len < 0) + return len; env_len += len; env_block_count++; } } /* second pass: copy to UTF-16 environment block */ - dst_copy = (WCHAR*)uv__malloc(env_len * sizeof(WCHAR)); - if (dst_copy == NULL && env_len > 0) { - return ERROR_OUTOFMEMORY; + 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; for (env = env_block; *env; env++) { + ssize_t len; if (strchr(*env, '=')) { - len = MultiByteToWideChar(CP_UTF8, - 0, - *env, - -1, - ptr, - (int) (env_len - (ptr - dst_copy))); - if (len <= 0) { - DWORD err = GetLastError(); - uv__free(dst_copy); - return err; - } + len = uv_wtf8_length_as_utf16(*env); + assert(len > 0); + assert((size_t) len <= env_len - (ptr - dst_copy)); + uv_wtf8_to_utf16(*env, ptr, len); *ptr_copy++ = ptr; ptr += len; } @@ -752,7 +704,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) { cmp = -1; } else { cmp = env_strncmp(required_vars[i].wide_eq, - required_vars[i].len, + required_vars[i].len, *ptr_copy); } if (cmp < 0) { @@ -774,8 +726,8 @@ 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); - return ERROR_OUTOFMEMORY; + uv__free(p); + return UV_ENOMEM; } for (ptr = dst, ptr_copy = env_copy, i = 0; @@ -819,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; } @@ -864,7 +816,7 @@ static void CALLBACK exit_wait_callback(void* data, BOOLEAN didTimeout) { /* Called on main thread after a child process has exited. */ -void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) { +void uv__process_proc_exit(uv_loop_t* loop, uv_process_t* handle) { int64_t exit_code; DWORD status; @@ -874,7 +826,7 @@ void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) { /* If we're closing, don't call the exit callback. Just schedule a close * callback now. */ if (handle->flags & UV_HANDLE_CLOSING) { - uv_want_endgame(loop, (uv_handle_t*) handle); + uv__want_endgame(loop, (uv_handle_t*) handle); return; } @@ -902,7 +854,7 @@ void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) { } -void uv_process_close(uv_loop_t* loop, uv_process_t* handle) { +void uv__process_close(uv_loop_t* loop, uv_process_t* handle) { uv__handle_closing(handle); if (handle->wait_handle != INVALID_HANDLE_VALUE) { @@ -918,12 +870,12 @@ void uv_process_close(uv_loop_t* loop, uv_process_t* handle) { } if (!handle->exit_cb_pending) { - uv_want_endgame(loop, (uv_handle_t*)handle); + uv__want_endgame(loop, (uv_handle_t*)handle); } } -void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle) { +void uv__process_endgame(uv_loop_t* loop, uv_process_t* handle) { assert(!handle->exit_cb_pending); assert(handle->flags & UV_HANDLE_CLOSING); assert(!(handle->flags & UV_HANDLE_CLOSED)); @@ -946,10 +898,12 @@ 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); + uv__process_init(loop, process); process->exit_cb = options->exit_cb; + child_stdio_buffer = NULL; if (options->flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) { return UV_ENOTSUP; @@ -964,37 +918,39 @@ 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 | UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS))); - err = uv_utf8_to_utf16_alloc(options->file, &application); + err = uv__utf8_to_utf16_alloc(options->file, &application); if (err) - goto done; + goto done_uv; err = make_program_args( options->args, options->flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS, &arguments); if (err) - goto done; + goto done_uv; if (options->env) { err = make_program_env(options->env, &env); if (err) - goto done; + goto done_uv; } if (options->cwd) { /* Explicit cwd */ - err = uv_utf8_to_utf16_alloc(options->cwd, &cwd); + err = uv__utf8_to_utf16_alloc(options->cwd, &cwd); if (err) - goto done; + goto done_uv; + cwd_len = wcslen(cwd); } else { /* Inherit cwd */ - DWORD cwd_len, r; + DWORD r; cwd_len = GetCurrentDirectoryW(0, NULL); if (!cwd_len) { @@ -1015,38 +971,45 @@ 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) { DWORD path_len, r; path_len = GetEnvironmentVariableW(L"PATH", NULL, 0); - if (path_len == 0) { - err = GetLastError(); - goto done; - } - - alloc_path = (WCHAR*) uv__malloc(path_len * sizeof(WCHAR)); - if (alloc_path == NULL) { - err = ERROR_OUTOFMEMORY; - goto done; - } - path = alloc_path; + if (path_len != 0) { + alloc_path = (WCHAR*) uv__malloc(path_len * sizeof(WCHAR)); + if (alloc_path == NULL) { + err = ERROR_OUTOFMEMORY; + goto done; + } + path = alloc_path; - r = GetEnvironmentVariableW(L"PATH", path, path_len); - if (r == 0 || r >= path_len) { - err = GetLastError(); - goto done; + r = GetEnvironmentVariableW(L"PATH", path, path_len); + if (r == 0 || r >= path_len) { + err = GetLastError(); + goto done; + } } } - err = uv__stdio_create(loop, options, &process->child_stdio_buffer); + err = uv__stdio_create(loop, options, &child_stdio_buffer); if (err) goto done; application_path = search_path(application, cwd, - path); + path, + options->flags); if (application_path == NULL) { /* Not found. */ err = ERROR_FILE_NOT_FOUND; @@ -1059,12 +1022,12 @@ int uv_spawn(uv_loop_t* loop, startup.lpTitle = NULL; startup.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; - startup.cbReserved2 = uv__stdio_size(process->child_stdio_buffer); - startup.lpReserved2 = (BYTE*) process->child_stdio_buffer; + startup.cbReserved2 = uv__stdio_size(child_stdio_buffer); + startup.lpReserved2 = (BYTE*) child_stdio_buffer; - startup.hStdInput = uv__stdio_handle(process->child_stdio_buffer, 0); - startup.hStdOutput = uv__stdio_handle(process->child_stdio_buffer, 1); - startup.hStdError = uv__stdio_handle(process->child_stdio_buffer, 2); + startup.hStdInput = uv__stdio_handle(child_stdio_buffer, 0); + startup.hStdOutput = uv__stdio_handle(child_stdio_buffer, 1); + startup.hStdError = uv__stdio_handle(child_stdio_buffer, 2); process_flags = CREATE_UNICODE_ENVIRONMENT; @@ -1098,6 +1061,7 @@ int uv_spawn(uv_loop_t* loop, * breakaway. */ process_flags |= DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP; + process_flags |= CREATE_SUSPENDED; } if (!CreateProcessW(application_path, @@ -1115,11 +1079,6 @@ int uv_spawn(uv_loop_t* loop, goto done; } - /* Spawn succeeded. Beyond this point, failure is reported asynchronously. */ - - process->process_handle = info.hProcess; - process->pid = info.dwProcessId; - /* If the process isn't spawned as detached, assign to the global job object * so windows will kill it when the parent process dies. */ if (!(options->flags & UV_PROCESS_DETACHED)) { @@ -1142,6 +1101,19 @@ int uv_spawn(uv_loop_t* loop, } } + if (process_flags & CREATE_SUSPENDED) { + if (ResumeThread(info.hThread) == ((DWORD)-1)) { + err = GetLastError(); + TerminateProcess(info.hProcess, 1); + goto done; + } + } + + /* Spawn succeeded. Beyond this point, failure is reported asynchronously. */ + + process->process_handle = info.hProcess; + process->pid = info.dwProcessId; + /* Set IPC pid to all IPC pipes. */ for (i = 0; i < options->stdio_count; i++) { const uv_stdio_container_t* fdopt = &options->stdio[i]; @@ -1169,8 +1141,13 @@ int uv_spawn(uv_loop_t* loop, * made or the handle is closed, whichever happens first. */ uv__handle_start(process); + goto done_uv; + /* Cleanup, whether we succeeded or failed. */ done: + err = uv_translate_sys_error(err); + + done_uv: uv__free(application); uv__free(application_path); uv__free(arguments); @@ -1178,13 +1155,13 @@ int uv_spawn(uv_loop_t* loop, uv__free(env); uv__free(alloc_path); - if (process->child_stdio_buffer != NULL) { + if (child_stdio_buffer != NULL) { /* Clean up child stdio handles. */ - uv__stdio_destroy(process->child_stdio_buffer); - process->child_stdio_buffer = NULL; + uv__stdio_destroy(child_stdio_buffer); + child_stdio_buffer = NULL; } - return uv_translate_sys_error(err); + return err; } @@ -1193,25 +1170,163 @@ static int uv__kill(HANDLE process_handle, int signum) { return UV_EINVAL; } + /* Create a dump file for the targeted process, if the registry key + * `HKLM:Software\Microsoft\Windows\Windows Error Reporting\LocalDumps` + * exists. The location of the dumps can be influenced by the `DumpFolder` + * sub-key, which has a default value of `%LOCALAPPDATA%\CrashDumps`, see [0] + * for more detail. Note that if the dump folder does not exist, we attempt + * to create it, to match behavior with WER itself. + * [0]: https://learn.microsoft.com/en-us/windows/win32/wer/collecting-user-mode-dumps */ + if (signum == SIGQUIT) { + HKEY registry_key; + DWORD pid, ret; + WCHAR basename[MAX_PATH]; + + /* Get target process name. */ + GetModuleBaseNameW(process_handle, NULL, &basename[0], sizeof(basename)); + + /* Get PID of target process. */ + pid = GetProcessId(process_handle); + + /* Get LocalDumps directory path. */ + ret = RegOpenKeyExW( + HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting\\LocalDumps", + 0, + KEY_QUERY_VALUE, + ®istry_key); + if (ret == ERROR_SUCCESS) { + HANDLE hDumpFile = NULL; + WCHAR dump_folder[MAX_PATH], dump_name[MAX_PATH]; + DWORD dump_folder_len = sizeof(dump_folder), key_type = 0; + ret = RegGetValueW(registry_key, + NULL, + L"DumpFolder", + RRF_RT_ANY, + &key_type, + (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_libuv, + 0, + NULL, + &localappdata); + _snwprintf_s(dump_folder, + sizeof(dump_folder), + _TRUNCATE, + L"%ls\\CrashDumps", + localappdata); + CoTaskMemFree(localappdata); + } + RegCloseKey(registry_key); + + /* Create dump folder if it doesn't already exist. */ + CreateDirectoryW(dump_folder, NULL); + + /* Construct dump filename from process name and PID. */ + _snwprintf_s(dump_name, + sizeof(dump_name), + _TRUNCATE, + L"%ls\\%ls.%d.dmp", + dump_folder, + basename, + pid); + + hDumpFile = CreateFileW(dump_name, + GENERIC_WRITE, + 0, + NULL, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hDumpFile != INVALID_HANDLE_VALUE) { + DWORD dump_options, sym_options; + FILE_DISPOSITION_INFO DeleteOnClose = { TRUE }; + + /* If something goes wrong while writing it out, delete the file. */ + SetFileInformationByHandle(hDumpFile, + FileDispositionInfo, + &DeleteOnClose, + sizeof(DeleteOnClose)); + + /* Tell wine to dump ELF modules as well. */ + sym_options = SymGetOptions(); + SymSetOptions(sym_options | 0x40000000); + +/* MiniDumpWithAvxXStateContext might be undef in server2012r2 or mingw < 12 */ +#ifndef MiniDumpWithAvxXStateContext +#define MiniDumpWithAvxXStateContext 0x00200000 +#endif + /* We default to a fairly complete dump. In the future, we may want to + * allow clients to customize what kind of dump to create. */ + dump_options = MiniDumpWithFullMemory | + MiniDumpIgnoreInaccessibleMemory | + MiniDumpWithAvxXStateContext; + + if (MiniDumpWriteDump(process_handle, + pid, + hDumpFile, + dump_options, + NULL, + NULL, + NULL)) { + /* Don't delete the file on close if we successfully wrote it out. */ + FILE_DISPOSITION_INFO DontDeleteOnClose = { FALSE }; + SetFileInformationByHandle(hDumpFile, + FileDispositionInfo, + &DontDeleteOnClose, + sizeof(DontDeleteOnClose)); + } + SymSetOptions(sym_options); + CloseHandle(hDumpFile); + } + } + } + switch (signum) { + case SIGQUIT: case SIGTERM: case SIGKILL: 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); @@ -1227,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: @@ -1262,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); } diff --git a/src/win/req-inl.h b/src/win/req-inl.h index f2513b7d3e7..cf16e8ba41f 100644 --- a/src/win/req-inl.h +++ b/src/win/req-inl.h @@ -50,19 +50,19 @@ (pRtlNtStatusToDosError(GET_REQ_STATUS((req)))) #define GET_REQ_SOCK_ERROR(req) \ - (uv_ntstatus_to_winsock_error(GET_REQ_STATUS((req)))) + (uv__ntstatus_to_winsock_error(GET_REQ_STATUS((req)))) -#define REGISTER_HANDLE_REQ(loop, handle, req) \ +#define REGISTER_HANDLE_REQ(loop, handle) \ do { \ 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) @@ -82,12 +82,12 @@ } -INLINE static uv_req_t* uv_overlapped_to_req(OVERLAPPED* overlapped) { - return CONTAINING_RECORD(overlapped, uv_req_t, u.io.overlapped); +INLINE static uv_req_t* uv__overlapped_to_req(OVERLAPPED* overlapped) { + return container_of(overlapped, uv_req_t, u.io.overlapped); } -INLINE static void uv_insert_pending_req(uv_loop_t* loop, uv_req_t* req) { +INLINE static void uv__insert_pending_req(uv_loop_t* loop, uv_req_t* req) { req->next_req = NULL; if (loop->pending_reqs_tail) { #ifdef _DEBUG @@ -115,19 +115,19 @@ INLINE static void uv_insert_pending_req(uv_loop_t* loop, uv_req_t* req) { do { \ switch (((uv_handle_t*) (req)->handle_at)->type) { \ case UV_TCP: \ - uv_process_tcp_##method##_req(loop, \ + uv__process_tcp_##method##_req(loop, \ (uv_tcp_t*) ((req)->handle_at), \ req); \ break; \ \ case UV_NAMED_PIPE: \ - uv_process_pipe_##method##_req(loop, \ + uv__process_pipe_##method##_req(loop, \ (uv_pipe_t*) ((req)->handle_at), \ req); \ break; \ \ case UV_TTY: \ - uv_process_tty_##method##_req(loop, \ + uv__process_tty_##method##_req(loop, \ (uv_tty_t*) ((req)->handle_at), \ req); \ break; \ @@ -138,13 +138,13 @@ INLINE static void uv_insert_pending_req(uv_loop_t* loop, uv_req_t* req) { } while (0) -INLINE static int uv_process_reqs(uv_loop_t* loop) { +INLINE static void uv__process_reqs(uv_loop_t* loop) { uv_req_t* req; uv_req_t* first; uv_req_t* next; if (loop->pending_reqs_tail == NULL) - return 0; + return; first = loop->pending_reqs_tail->next_req; next = first; @@ -172,50 +172,43 @@ INLINE static int uv_process_reqs(uv_loop_t* loop) { break; case UV_SHUTDOWN: - /* Tcp shutdown requests don't come here. */ - assert(((uv_shutdown_t*) req)->handle->type == UV_NAMED_PIPE); - uv_process_pipe_shutdown_req( - loop, - (uv_pipe_t*) ((uv_shutdown_t*) req)->handle, - (uv_shutdown_t*) req); + DELEGATE_STREAM_REQ(loop, (uv_shutdown_t*) req, shutdown, handle); break; case UV_UDP_RECV: - uv_process_udp_recv_req(loop, (uv_udp_t*) req->data, req); + uv__process_udp_recv_req(loop, (uv_udp_t*) req->data, req); break; case UV_UDP_SEND: - uv_process_udp_send_req(loop, - ((uv_udp_send_t*) req)->handle, - (uv_udp_send_t*) req); + uv__process_udp_send_req(loop, + ((uv_udp_send_t*) req)->handle, + (uv_udp_send_t*) req); break; case UV_WAKEUP: - uv_process_async_wakeup_req(loop, (uv_async_t*) req->data, req); + uv__process_async_wakeup_req(loop, (uv_async_t*) req->data, req); break; case UV_SIGNAL_REQ: - uv_process_signal_req(loop, (uv_signal_t*) req->data, req); + uv__process_signal_req(loop, (uv_signal_t*) req->data, req); break; case UV_POLL_REQ: - uv_process_poll_req(loop, (uv_poll_t*) req->data, req); + uv__process_poll_req(loop, (uv_poll_t*) req->data, req); break; case UV_PROCESS_EXIT: - uv_process_proc_exit(loop, (uv_process_t*) req->data); + uv__process_proc_exit(loop, (uv_process_t*) req->data); break; case UV_FS_EVENT_REQ: - uv_process_fs_event_req(loop, req, (uv_fs_event_t*) req->data); + uv__process_fs_event_req(loop, req, (uv_fs_event_t*) req->data); break; default: assert(0); } } - - return 1; } #endif /* UV_WIN_REQ_INL_H_ */ diff --git a/src/win/signal.c b/src/win/signal.c index 3d9f92cfb17..85730b27b2b 100644 --- a/src/win/signal.c +++ b/src/win/signal.c @@ -39,7 +39,7 @@ int uv__signal_start(uv_signal_t* handle, int signum, int oneshot); -void uv_signals_init(void) { +void uv__signals_init(void) { InitializeCriticalSection(&uv__signal_lock); if (!SetConsoleCtrlHandler(uv__signal_control_handler, TRUE)) abort(); @@ -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); @@ -231,7 +231,7 @@ int uv__signal_start(uv_signal_t* handle, } -void uv_process_signal_req(uv_loop_t* loop, uv_signal_t* handle, +void uv__process_signal_req(uv_loop_t* loop, uv_signal_t* handle, uv_req_t* req) { long dispatched_signum; @@ -254,22 +254,22 @@ void uv_process_signal_req(uv_loop_t* loop, uv_signal_t* handle, if (handle->flags & UV_HANDLE_CLOSING) { /* When it is closing, it must be stopped at this point. */ assert(handle->signum == 0); - uv_want_endgame(loop, (uv_handle_t*) handle); + uv__want_endgame(loop, (uv_handle_t*) handle); } } -void uv_signal_close(uv_loop_t* loop, uv_signal_t* handle) { +void uv__signal_close(uv_loop_t* loop, uv_signal_t* handle) { uv_signal_stop(handle); uv__handle_closing(handle); if (handle->pending_signum == 0) { - uv_want_endgame(loop, (uv_handle_t*) handle); + uv__want_endgame(loop, (uv_handle_t*) handle); } } -void uv_signal_endgame(uv_loop_t* loop, uv_signal_t* handle) { +void uv__signal_endgame(uv_loop_t* loop, uv_signal_t* handle) { assert(handle->flags & UV_HANDLE_CLOSING); assert(!(handle->flags & UV_HANDLE_CLOSED)); diff --git a/src/win/stream-inl.h b/src/win/stream-inl.h index 40f5ddd51ea..91b1e785daf 100644 --- a/src/win/stream-inl.h +++ b/src/win/stream-inl.h @@ -30,9 +30,9 @@ #include "req-inl.h" -INLINE static void uv_stream_init(uv_loop_t* loop, - uv_stream_t* handle, - uv_handle_type type) { +INLINE static void uv__stream_init(uv_loop_t* loop, + uv_stream_t* handle, + uv_handle_type type) { uv__handle_init(loop, (uv_handle_t*) handle, type); handle->write_queue_size = 0; handle->activecnt = 0; @@ -46,7 +46,7 @@ INLINE static void uv_stream_init(uv_loop_t* loop, } -INLINE static void uv_connection_init(uv_stream_t* handle) { +INLINE static void uv__connection_init(uv_stream_t* handle) { handle->flags |= UV_HANDLE_CONNECTION; } diff --git a/src/win/stream.c b/src/win/stream.c index abf477f6442..a53a10b0382 100644 --- a/src/win/stream.c +++ b/src/win/stream.c @@ -29,14 +29,16 @@ int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) { int err; - + if (uv__is_closing(stream)) { + return UV_EINVAL; + } err = ERROR_INVALID_PARAMETER; switch (stream->type) { case UV_TCP: - err = uv_tcp_listen((uv_tcp_t*)stream, backlog, cb); + err = uv__tcp_listen((uv_tcp_t*)stream, backlog, cb); break; case UV_NAMED_PIPE: - err = uv_pipe_listen((uv_pipe_t*)stream, backlog, cb); + err = uv__pipe_listen((uv_pipe_t*)stream, backlog, cb); break; default: assert(0); @@ -52,10 +54,10 @@ int uv_accept(uv_stream_t* server, uv_stream_t* client) { err = ERROR_INVALID_PARAMETER; switch (server->type) { case UV_TCP: - err = uv_tcp_accept((uv_tcp_t*)server, (uv_tcp_t*)client); + err = uv__tcp_accept((uv_tcp_t*)server, (uv_tcp_t*)client); break; case UV_NAMED_PIPE: - err = uv_pipe_accept((uv_pipe_t*)server, client); + err = uv__pipe_accept((uv_pipe_t*)server, client); break; default: assert(0); @@ -73,13 +75,13 @@ int uv__read_start(uv_stream_t* handle, err = ERROR_INVALID_PARAMETER; switch (handle->type) { case UV_TCP: - err = uv_tcp_read_start((uv_tcp_t*)handle, alloc_cb, read_cb); + err = uv__tcp_read_start((uv_tcp_t*)handle, alloc_cb, read_cb); break; case UV_NAMED_PIPE: - err = uv_pipe_read_start((uv_pipe_t*)handle, alloc_cb, read_cb); + err = uv__pipe_read_start((uv_pipe_t*)handle, alloc_cb, read_cb); break; case UV_TTY: - err = uv_tty_read_start((uv_tty_t*) handle, alloc_cb, read_cb); + err = uv__tty_read_start((uv_tty_t*) handle, alloc_cb, read_cb); break; default: assert(0); @@ -97,7 +99,7 @@ int uv_read_stop(uv_stream_t* handle) { err = 0; if (handle->type == UV_TTY) { - err = uv_tty_read_stop((uv_tty_t*) handle); + err = uv__tty_read_stop((uv_tty_t*) handle); } else if (handle->type == UV_NAMED_PIPE) { uv__pipe_read_stop((uv_pipe_t*) handle); } else { @@ -124,14 +126,14 @@ int uv_write(uv_write_t* req, err = ERROR_INVALID_PARAMETER; switch (handle->type) { case UV_TCP: - err = uv_tcp_write(loop, req, (uv_tcp_t*) handle, bufs, nbufs, cb); + err = uv__tcp_write(loop, req, (uv_tcp_t*) handle, bufs, nbufs, cb); break; 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); + err = uv__tty_write(loop, req, (uv_tty_t*) handle, bufs, nbufs, cb); break; default: assert(0); @@ -162,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); } @@ -202,7 +204,7 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) { uv_loop_t* loop = handle->loop; if (!(handle->flags & UV_HANDLE_WRITABLE) || - handle->flags & UV_HANDLE_SHUTTING || + uv__is_stream_shutting(handle) || uv__is_closing(handle)) { return UV_ENOTCONN; } @@ -212,12 +214,16 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) { req->cb = cb; handle->flags &= ~UV_HANDLE_WRITABLE; - handle->flags |= UV_HANDLE_SHUTTING; handle->stream.conn.shutdown_req = req; handle->reqs_pending++; - REGISTER_HANDLE_REQ(loop, handle, req); + REGISTER_HANDLE_REQ(loop, handle); - uv_want_endgame(loop, (uv_handle_t*)handle); + if (handle->stream.conn.write_reqs_pending == 0) { + if (handle->type == UV_NAMED_PIPE) + uv__pipe_shutdown(loop, (uv_pipe_t*) handle, req); + else + uv__insert_pending_req(loop, (uv_req_t*) req); + } return 0; } diff --git a/src/win/tcp.c b/src/win/tcp.c index 6ca11e070bf..c452c12e8f0 100644 --- a/src/win/tcp.c +++ b/src/win/tcp.c @@ -29,14 +29,6 @@ #include "req-inl.h" -/* - * Threshold of active tcp streams for which to preallocate tcp read buffers. - * (Due to node slab allocator performing poorly under this pattern, - * the optimization is temporarily disabled (threshold=0). This will be - * revisited once node allocator is improved.) - */ -const unsigned int uv_active_tcp_streams_threshold = 0; - /* * Number of simultaneous pending AcceptEx calls. */ @@ -66,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(); } @@ -78,11 +76,11 @@ static int uv__tcp_keepalive(uv_tcp_t* handle, SOCKET socket, int enable, unsign } -static int uv_tcp_set_socket(uv_loop_t* loop, - uv_tcp_t* handle, - SOCKET socket, - int family, - int imported) { +static int uv__tcp_set_socket(uv_loop_t* loop, + uv_tcp_t* handle, + SOCKET socket, + int family, + int imported) { DWORD yes = 1; int non_ifs_lsp; int err; @@ -162,7 +160,7 @@ int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* handle, unsigned int flags) { if (flags & ~0xFF) return UV_EINVAL; - uv_stream_init(loop, (uv_stream_t*) handle, UV_TCP); + uv__stream_init(loop, (uv_stream_t*) handle, UV_TCP); handle->tcp.serv.accept_reqs = NULL; handle->tcp.serv.pending_accepts = NULL; handle->socket = INVALID_SOCKET; @@ -173,7 +171,7 @@ int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* handle, unsigned int flags) { handle->delayed_error = 0; /* If anything fails beyond this point we need to remove the handle from - * the handle queue, since it was added by uv__handle_init in uv_stream_init. + * the handle queue, since it was added by uv__handle_init in uv__stream_init. */ if (domain != AF_UNSPEC) { @@ -183,14 +181,14 @@ int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* handle, unsigned int flags) { sock = socket(domain, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { err = WSAGetLastError(); - QUEUE_REMOVE(&handle->handle_queue); + uv__queue_remove(&handle->handle_queue); return uv_translate_sys_error(err); } - err = uv_tcp_set_socket(handle->loop, handle, sock, domain, 0); + err = uv__tcp_set_socket(handle->loop, handle, sock, domain, 0); if (err) { closesocket(sock); - QUEUE_REMOVE(&handle->handle_queue); + uv__queue_remove(&handle->handle_queue); return uv_translate_sys_error(err); } @@ -205,73 +203,74 @@ int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* handle) { } -void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { +void uv__process_tcp_shutdown_req(uv_loop_t* loop, uv_tcp_t* stream, uv_shutdown_t *req) { int err; - unsigned int i; - uv_tcp_accept_t* req; - if (handle->flags & UV_HANDLE_CONNECTION && - handle->stream.conn.shutdown_req != NULL && - handle->stream.conn.write_reqs_pending == 0) { + assert(req); + assert(stream->stream.conn.write_reqs_pending == 0); + assert(!(stream->flags & UV_HANDLE_SHUT)); + assert(stream->flags & UV_HANDLE_CONNECTION); - UNREGISTER_HANDLE_REQ(loop, handle, handle->stream.conn.shutdown_req); + stream->stream.conn.shutdown_req = NULL; + UNREGISTER_HANDLE_REQ(loop, stream); - err = 0; - if (handle->flags & UV_HANDLE_CLOSING) { - err = ERROR_OPERATION_ABORTED; - } else if (shutdown(handle->socket, SD_SEND) == SOCKET_ERROR) { - err = WSAGetLastError(); - } + err = 0; + if (stream->flags & UV_HANDLE_CLOSING) + /* The user destroyed the stream before we got to do the shutdown. */ + err = UV_ECANCELED; + else if (shutdown(stream->socket, SD_SEND) == SOCKET_ERROR) + err = uv_translate_sys_error(WSAGetLastError()); + else /* Success. */ + stream->flags |= UV_HANDLE_SHUT; + + if (req->cb) + req->cb(req, err); - if (handle->stream.conn.shutdown_req->cb) { - handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req, - uv_translate_sys_error(err)); - } + DECREASE_PENDING_REQ_COUNT(stream); +} - handle->stream.conn.shutdown_req = NULL; - DECREASE_PENDING_REQ_COUNT(handle); - return; - } - if (handle->flags & UV_HANDLE_CLOSING && - handle->reqs_pending == 0) { - assert(!(handle->flags & UV_HANDLE_CLOSED)); - assert(handle->socket == INVALID_SOCKET); +void uv__tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { + unsigned int i; + uv_tcp_accept_t* req; - if (!(handle->flags & UV_HANDLE_CONNECTION) && handle->tcp.serv.accept_reqs) { - if (handle->flags & UV_HANDLE_EMULATE_IOCP) { - for (i = 0; i < uv_simultaneous_server_accepts; i++) { - req = &handle->tcp.serv.accept_reqs[i]; - if (req->wait_handle != INVALID_HANDLE_VALUE) { - UnregisterWait(req->wait_handle); - req->wait_handle = INVALID_HANDLE_VALUE; - } - if (req->event_handle != NULL) { - CloseHandle(req->event_handle); - req->event_handle = NULL; - } + assert(handle->flags & UV_HANDLE_CLOSING); + assert(handle->reqs_pending == 0); + assert(!(handle->flags & UV_HANDLE_CLOSED)); + assert(handle->socket == INVALID_SOCKET); + + if (!(handle->flags & UV_HANDLE_CONNECTION) && handle->tcp.serv.accept_reqs) { + if (handle->flags & UV_HANDLE_EMULATE_IOCP) { + for (i = 0; i < uv_simultaneous_server_accepts; i++) { + req = &handle->tcp.serv.accept_reqs[i]; + if (req->wait_handle != INVALID_HANDLE_VALUE) { + UnregisterWait(req->wait_handle); + req->wait_handle = INVALID_HANDLE_VALUE; + } + if (req->event_handle != NULL) { + CloseHandle(req->event_handle); + req->event_handle = NULL; } } - - uv__free(handle->tcp.serv.accept_reqs); - handle->tcp.serv.accept_reqs = NULL; } - if (handle->flags & UV_HANDLE_CONNECTION && - handle->flags & UV_HANDLE_EMULATE_IOCP) { - if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) { - UnregisterWait(handle->read_req.wait_handle); - handle->read_req.wait_handle = INVALID_HANDLE_VALUE; - } - if (handle->read_req.event_handle != NULL) { - CloseHandle(handle->read_req.event_handle); - handle->read_req.event_handle = NULL; - } - } + uv__free(handle->tcp.serv.accept_reqs); + handle->tcp.serv.accept_reqs = NULL; + } - uv__handle_close(handle); - loop->active_tcp_streams--; + if (handle->flags & UV_HANDLE_CONNECTION && + handle->flags & UV_HANDLE_EMULATE_IOCP) { + if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) { + UnregisterWait(handle->read_req.wait_handle); + handle->read_req.wait_handle = INVALID_HANDLE_VALUE; + } + if (handle->read_req.event_handle != NULL) { + CloseHandle(handle->read_req.event_handle); + handle->read_req.event_handle = NULL; + } } + + uv__handle_close(handle); } @@ -286,13 +285,19 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { * See issue #1360. * */ -static int uv_tcp_try_bind(uv_tcp_t* handle, - const struct sockaddr* addr, - unsigned int addrlen, - unsigned int flags) { +static int uv__tcp_try_bind(uv_tcp_t* handle, + const struct sockaddr* addr, + unsigned int addrlen, + unsigned int flags) { 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; @@ -305,7 +310,7 @@ static int uv_tcp_try_bind(uv_tcp_t* handle, return WSAGetLastError(); } - err = uv_tcp_set_socket(handle->loop, handle, sock, addr->sa_family, 0); + err = uv__tcp_set_socket(handle->loop, handle, sock, addr->sa_family, 0); if (err) { closesocket(sock); return err; @@ -385,7 +390,7 @@ static void CALLBACK post_write_completion(void* context, BOOLEAN timed_out) { } -static void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) { +static void uv__tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) { uv_loop_t* loop = handle->loop; BOOL success; DWORD bytes; @@ -406,7 +411,7 @@ static void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) { accept_socket = socket(family, SOCK_STREAM, 0); if (accept_socket == INVALID_SOCKET) { SET_REQ_ERROR(req, WSAGetLastError()); - uv_insert_pending_req(loop, (uv_req_t*)req); + uv__insert_pending_req(loop, (uv_req_t*)req); handle->reqs_pending++; return; } @@ -414,7 +419,7 @@ static void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) { /* Make the socket non-inheritable */ if (!SetHandleInformation((HANDLE) accept_socket, HANDLE_FLAG_INHERIT, 0)) { SET_REQ_ERROR(req, GetLastError()); - uv_insert_pending_req(loop, (uv_req_t*)req); + uv__insert_pending_req(loop, (uv_req_t*)req); handle->reqs_pending++; closesocket(accept_socket); return; @@ -440,7 +445,7 @@ static void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) { /* Process the req without IOCP. */ req->accept_socket = accept_socket; handle->reqs_pending++; - uv_insert_pending_req(loop, (uv_req_t*)req); + uv__insert_pending_req(loop, (uv_req_t*)req); } else if (UV_SUCCEEDED_WITH_IOCP(success)) { /* The req will be processed with IOCP. */ req->accept_socket = accept_socket; @@ -451,12 +456,12 @@ static void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) { req->event_handle, post_completion, (void*) req, INFINITE, WT_EXECUTEINWAITTHREAD)) { SET_REQ_ERROR(req, GetLastError()); - uv_insert_pending_req(loop, (uv_req_t*)req); + uv__insert_pending_req(loop, (uv_req_t*)req); } } else { /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, WSAGetLastError()); - uv_insert_pending_req(loop, (uv_req_t*)req); + uv__insert_pending_req(loop, (uv_req_t*)req); handle->reqs_pending++; /* Destroy the preallocated client socket. */ closesocket(accept_socket); @@ -469,7 +474,7 @@ static void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) { } -static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { +static void uv__tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { uv_read_t* req; uv_buf_t buf; int result; @@ -481,26 +486,9 @@ static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { req = &handle->read_req; memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); - /* - * Preallocate a read buffer if the number of active streams is below - * the threshold. - */ - if (loop->active_tcp_streams < uv_active_tcp_streams_threshold) { - handle->flags &= ~UV_HANDLE_ZERO_READ; - handle->tcp.conn.read_buffer = uv_buf_init(NULL, 0); - handle->alloc_cb((uv_handle_t*) handle, 65536, &handle->tcp.conn.read_buffer); - if (handle->tcp.conn.read_buffer.base == NULL || - handle->tcp.conn.read_buffer.len == 0) { - handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &handle->tcp.conn.read_buffer); - return; - } - assert(handle->tcp.conn.read_buffer.base != NULL); - buf = handle->tcp.conn.read_buffer; - } else { - handle->flags |= UV_HANDLE_ZERO_READ; - buf.base = (char*) &uv_zero_; - buf.len = 0; - } + handle->flags |= UV_HANDLE_ZERO_READ; + buf.base = (char*) &uv_zero_; + buf.len = 0; /* Prepare the overlapped structure. */ memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped)); @@ -524,7 +512,7 @@ static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { /* Process the req without IOCP. */ req->u.io.overlapped.InternalHigh = bytes; - uv_insert_pending_req(loop, (uv_req_t*)req); + uv__insert_pending_req(loop, (uv_req_t*)req); } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { /* The req will be processed with IOCP. */ if (handle->flags & UV_HANDLE_EMULATE_IOCP && @@ -533,12 +521,12 @@ static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { req->event_handle, post_completion, (void*) req, INFINITE, WT_EXECUTEINWAITTHREAD)) { SET_REQ_ERROR(req, GetLastError()); - uv_insert_pending_req(loop, (uv_req_t*)req); + uv__insert_pending_req(loop, (uv_req_t*)req); } } else { /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, WSAGetLastError()); - uv_insert_pending_req(loop, (uv_req_t*)req); + uv__insert_pending_req(loop, (uv_req_t*)req); } } @@ -547,7 +535,7 @@ int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb) { struct linger l = { 1, 0 }; /* Disallow setting SO_LINGER to zero due to some platform inconsistencies */ - if (handle->flags & UV_HANDLE_SHUTTING) + if (uv__is_stream_shutting(handle)) return UV_EINVAL; if (0 != setsockopt(handle->socket, SOL_SOCKET, SO_LINGER, (const char*)&l, sizeof(l))) @@ -558,7 +546,7 @@ int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb) { } -int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { +int uv__tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { unsigned int i, simultaneous_accepts; uv_tcp_accept_t* req; int err; @@ -578,10 +566,10 @@ int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { } if (!(handle->flags & UV_HANDLE_BOUND)) { - err = uv_tcp_try_bind(handle, - (const struct sockaddr*) &uv_addr_ip4_any_, - sizeof(uv_addr_ip4_any_), - 0); + err = uv__tcp_try_bind(handle, + (const struct sockaddr*) &uv_addr_ip4_any_, + sizeof(uv_addr_ip4_any_), + 0); if (err) return err; if (handle->delayed_error) @@ -589,7 +577,7 @@ int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { } if (!handle->tcp.serv.func_acceptex) { - if (!uv_get_acceptex_function(handle->socket, &handle->tcp.serv.func_acceptex)) { + if (!uv__get_acceptex_function(handle->socket, &handle->tcp.serv.func_acceptex)) { return WSAEAFNOSUPPORT; } } @@ -630,7 +618,7 @@ int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { req->event_handle = NULL; } - uv_tcp_queue_accept(handle, req); + uv__tcp_queue_accept(handle, req); } /* Initialize other unused requests too, because uv_tcp_endgame doesn't @@ -650,8 +638,7 @@ int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { } -int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) { - uv_loop_t* loop = server->loop; +int uv__tcp_accept(uv_tcp_t* server, uv_tcp_t* client) { int err = 0; int family; @@ -672,7 +659,7 @@ int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) { family = AF_INET; } - err = uv_tcp_set_socket(client->loop, + err = uv__tcp_set_socket(client->loop, client, req->accept_socket, family, @@ -680,7 +667,7 @@ int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) { if (err) { closesocket(req->accept_socket); } else { - uv_connection_init((uv_stream_t*) client); + uv__connection_init((uv_stream_t*) client); /* AcceptEx() implicitly binds the accepted socket. */ client->flags |= UV_HANDLE_BOUND | UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; } @@ -693,7 +680,7 @@ int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) { if (!(server->flags & UV_HANDLE_CLOSING)) { /* Check if we're in a middle of changing the number of pending accepts. */ if (!(server->flags & UV_HANDLE_TCP_ACCEPT_STATE_CHANGING)) { - uv_tcp_queue_accept(server, req); + uv__tcp_queue_accept(server, req); } else { /* We better be switching to a single pending accept. */ assert(server->flags & UV_HANDLE_TCP_SINGLE_ACCEPT); @@ -706,20 +693,18 @@ int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) { * All previously queued accept requests are now processed. * We now switch to queueing just a single accept. */ - uv_tcp_queue_accept(server, &server->tcp.serv.accept_reqs[0]); + uv__tcp_queue_accept(server, &server->tcp.serv.accept_reqs[0]); server->flags &= ~UV_HANDLE_TCP_ACCEPT_STATE_CHANGING; server->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT; } } } - loop->active_tcp_streams++; - return err; } -int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb, +int uv__tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { uv_loop_t* loop = handle->loop; @@ -738,7 +723,7 @@ int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb, uv_fatal_error(GetLastError(), "CreateEvent"); } } - uv_tcp_queue_read(loop, handle); + uv__tcp_queue_read(loop, handle); } return 0; @@ -779,7 +764,7 @@ static int uv__is_fast_loopback_fail_supported(void) { return os_info.dwBuildNumber >= 16299; } -static int uv_tcp_try_connect(uv_connect_t* req, +static int uv__tcp_try_connect(uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr, unsigned int addrlen, @@ -807,7 +792,7 @@ static int uv_tcp_try_connect(uv_connect_t* req, } else { abort(); } - err = uv_tcp_try_bind(handle, bind_addr, addrlen, 0); + err = uv__tcp_try_bind(handle, bind_addr, addrlen, 0); if (err) return err; if (handle->delayed_error != 0) @@ -815,7 +800,7 @@ static int uv_tcp_try_connect(uv_connect_t* req, } if (!handle->tcp.conn.func_connectex) { - if (!uv_get_connectex_function(handle->socket, &handle->tcp.conn.func_connectex)) { + if (!uv__get_connectex_function(handle->socket, &handle->tcp.conn.func_connectex)) { return WSAEAFNOSUPPORT; } } @@ -849,8 +834,8 @@ static int uv_tcp_try_connect(uv_connect_t* req, if (handle->delayed_error != 0) { /* Process the req without IOCP. */ handle->reqs_pending++; - REGISTER_HANDLE_REQ(loop, handle, req); - uv_insert_pending_req(loop, (uv_req_t*)req); + REGISTER_HANDLE_REQ(loop, handle); + uv__insert_pending_req(loop, (uv_req_t*)req); return 0; } @@ -865,12 +850,12 @@ static int uv_tcp_try_connect(uv_connect_t* req, if (UV_SUCCEEDED_WITHOUT_IOCP(success)) { /* Process the req without IOCP. */ handle->reqs_pending++; - REGISTER_HANDLE_REQ(loop, handle, req); - uv_insert_pending_req(loop, (uv_req_t*)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(); } @@ -903,7 +888,7 @@ int uv_tcp_getpeername(const uv_tcp_t* handle, } -int uv_tcp_write(uv_loop_t* loop, +int uv__tcp_write(uv_loop_t* loop, uv_write_t* req, uv_tcp_t* handle, const uv_buf_t bufs[], @@ -940,30 +925,30 @@ 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); - uv_insert_pending_req(loop, (uv_req_t*) 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, req->event_handle, post_write_completion, (void*) req, INFINITE, WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE)) { SET_REQ_ERROR(req, GetLastError()); - uv_insert_pending_req(loop, (uv_req_t*)req); + uv__insert_pending_req(loop, (uv_req_t*)req); } } else { /* Send failed due to an error, report it later */ 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); + uv__insert_pending_req(loop, (uv_req_t*) req); } return 0; @@ -994,7 +979,7 @@ int uv__tcp_try_write(uv_tcp_t* handle, } -void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, +void uv__process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, uv_req_t* req) { DWORD bytes, flags, err; uv_buf_t buf; @@ -1115,7 +1100,7 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, /* Post another read if still reading and not closing. */ if ((handle->flags & UV_HANDLE_READING) && !(handle->flags & UV_HANDLE_READ_PENDING)) { - uv_tcp_queue_read(loop, handle); + uv__tcp_queue_read(loop, handle); } } @@ -1123,7 +1108,7 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, } -void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle, +void uv__process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle, uv_write_t* req) { int err; @@ -1132,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) { @@ -1160,16 +1145,17 @@ void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle, closesocket(handle->socket); handle->socket = INVALID_SOCKET; } - if (handle->stream.conn.shutdown_req != NULL) { - uv_want_endgame(loop, (uv_handle_t*)handle); - } + if (uv__is_stream_shutting(handle)) + uv__process_tcp_shutdown_req(loop, + handle, + handle->stream.conn.shutdown_req); } DECREASE_PENDING_REQ_COUNT(handle); } -void uv_process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle, +void uv__process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle, uv_req_t* raw_req) { uv_tcp_accept_t* req = (uv_tcp_accept_t*) raw_req; int err; @@ -1209,7 +1195,7 @@ void uv_process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle, closesocket(req->accept_socket); req->accept_socket = INVALID_SOCKET; if (handle->flags & UV_HANDLE_LISTENING) { - uv_tcp_queue_accept(handle, req); + uv__tcp_queue_accept(handle, req); } } @@ -1217,13 +1203,13 @@ void uv_process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle, } -void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, +void uv__process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, uv_connect_t* req) { int err; assert(handle->type == UV_TCP); - UNREGISTER_HANDLE_REQ(loop, handle, req); + UNREGISTER_HANDLE_REQ(loop, handle); err = 0; if (handle->delayed_error) { @@ -1242,9 +1228,8 @@ void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, SO_UPDATE_CONNECT_CONTEXT, NULL, 0) == 0) { - uv_connection_init((uv_stream_t*)handle); + uv__connection_init((uv_stream_t*)handle); handle->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; - loop->active_tcp_streams++; } else { err = WSAGetLastError(); } @@ -1312,7 +1297,7 @@ int uv__tcp_xfer_import(uv_tcp_t* tcp, return WSAGetLastError(); } - err = uv_tcp_set_socket( + err = uv__tcp_set_socket( tcp->loop, tcp, socket, xfer_info->socket_info.iAddressFamily, 1); if (err) { closesocket(socket); @@ -1323,11 +1308,10 @@ int uv__tcp_xfer_import(uv_tcp_t* tcp, tcp->flags |= UV_HANDLE_BOUND | UV_HANDLE_SHARED_TCP_SOCKET; if (xfer_type == UV__IPC_SOCKET_XFER_TCP_CONNECTION) { - uv_connection_init((uv_stream_t*)tcp); + uv__connection_init((uv_stream_t*)tcp); tcp->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; } - tcp->loop->active_tcp_streams++; return 0; } @@ -1404,14 +1388,14 @@ int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) { } -static void uv_tcp_try_cancel_reqs(uv_tcp_t* tcp) { +static void uv__tcp_try_cancel_reqs(uv_tcp_t* tcp) { SOCKET socket; int non_ifs_lsp; int reading; int writing; socket = tcp->socket; - reading = tcp->flags & UV_HANDLE_READING; + reading = tcp->flags & UV_HANDLE_READ_PENDING; writing = tcp->stream.conn.write_reqs_pending > 0; if (!reading && !writing) return; @@ -1428,7 +1412,7 @@ static void uv_tcp_try_cancel_reqs(uv_tcp_t* tcp) { uv_tcp_non_ifs_lsp_ipv4; /* If there are non-ifs LSPs then try to obtain a base handle for the socket. - * This will always fail on Windows XP/3k. */ + */ if (non_ifs_lsp) { DWORD bytes; if (WSAIoctl(socket, @@ -1456,12 +1440,12 @@ static void uv_tcp_try_cancel_reqs(uv_tcp_t* tcp) { } -void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) { +void uv__tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) { if (tcp->flags & UV_HANDLE_CONNECTION) { - uv_tcp_try_cancel_reqs(tcp); if (tcp->flags & UV_HANDLE_READING) { uv_read_stop((uv_stream_t*) tcp); } + uv__tcp_try_cancel_reqs(tcp); } else { if (tcp->tcp.serv.accept_reqs != NULL) { /* First close the incoming sockets to cancel the accept operations before @@ -1483,6 +1467,9 @@ void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) { DECREASE_ACTIVE_COUNT(loop, tcp); } + tcp->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); + uv__handle_closing(tcp); + /* If any overlapped req failed to cancel, calling `closesocket` now would * cause Win32 to send an RST packet. Try to avoid that for writes, if * possibly applicable, by waiting to process the completion notifications @@ -1494,12 +1481,8 @@ void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) { tcp->socket = INVALID_SOCKET; } - tcp->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); - uv__handle_closing(tcp); - - if (tcp->reqs_pending == 0) { - uv_want_endgame(tcp->loop, (uv_handle_t*)tcp); - } + if (tcp->reqs_pending == 0) + uv__want_endgame(loop, (uv_handle_t*) tcp); } @@ -1520,7 +1503,7 @@ int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) { return uv_translate_sys_error(GetLastError()); } - err = uv_tcp_set_socket(handle->loop, + err = uv__tcp_set_socket(handle->loop, handle, sock, protocol_info.iAddressFamily, @@ -1537,7 +1520,7 @@ int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) { saddr_len = sizeof(saddr); if (!uv_tcp_getpeername(handle, (struct sockaddr*) &saddr, &saddr_len)) { /* Socket is already connected. */ - uv_connection_init((uv_stream_t*) handle); + uv__connection_init((uv_stream_t*) handle); handle->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; } } @@ -1555,7 +1538,7 @@ int uv__tcp_bind(uv_tcp_t* handle, unsigned int flags) { int err; - err = uv_tcp_try_bind(handle, addr, addrlen, flags); + err = uv__tcp_try_bind(handle, addr, addrlen, flags); if (err) return uv_translate_sys_error(err); @@ -1573,18 +1556,13 @@ int uv__tcp_connect(uv_connect_t* req, uv_connect_cb cb) { int err; - err = uv_tcp_try_connect(req, handle, addr, addrlen, cb); + err = uv__tcp_try_connect(req, handle, addr, addrlen, cb); if (err) return uv_translate_sys_error(err); 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; @@ -1634,7 +1612,7 @@ int uv_socketpair(int type, int protocol, uv_os_sock_t fds[2], int flags0, int f goto wsaerror; if (!SetHandleInformation((HANDLE) client1, HANDLE_FLAG_INHERIT, 0)) goto error; - if (!uv_get_acceptex_function(server, &func_acceptex)) { + if (!uv__get_acceptex_function(server, &func_acceptex)) { err = WSAEAFNOSUPPORT; goto cleanup; } diff --git a/src/win/thread.c b/src/win/thread.c index ea5dc04e9e5..753cb6a34a5 100644 --- a/src/win/thread.c +++ b/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; - - created_event = CreateEvent(NULL, 1, 0, NULL); - if (created_event == 0) { - /* Could fail in a low-memory situation? */ - uv_fatal_error(GetLastError(), "CreateEvent"); - } +typedef void (*uv__once_cb)(void); - existing_event = InterlockedCompareExchangePointer(&guard->event, - created_event, - NULL); +typedef struct { + uv__once_cb callback; +} uv__once_data_t; - if (existing_event == NULL) { - /* We won the race */ - callback(); +static BOOL WINAPI uv__once_inner(INIT_ONCE *once, void* param, void** context) { + uv__once_data_t* data = param; - result = SetEvent(created_event); - assert(result); - guard->ran = 1; + data->callback(); - } 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), @@ -180,10 +170,86 @@ int uv_thread_create_ex(uv_thread_t* tid, return UV_EIO; } +int uv_thread_setaffinity(uv_thread_t* tid, + char* cpumask, + char* oldmask, + size_t mask_size) { + int i; + HANDLE hproc; + DWORD_PTR procmask; + DWORD_PTR sysmask; + DWORD_PTR threadmask; + DWORD_PTR oldthreadmask; + int cpumasksize; + + cpumasksize = uv_cpumask_size(); + assert(cpumasksize > 0); + if (mask_size < (size_t)cpumasksize) + return UV_EINVAL; + + hproc = GetCurrentProcess(); + if (!GetProcessAffinityMask(hproc, &procmask, &sysmask)) + return uv_translate_sys_error(GetLastError()); + + threadmask = 0; + for (i = 0; i < cpumasksize; i++) { + if (cpumask[i]) { + if (procmask & (1 << i)) + threadmask |= 1 << i; + else + return UV_EINVAL; + } + } + + oldthreadmask = SetThreadAffinityMask(*tid, threadmask); + if (oldthreadmask == 0) + return uv_translate_sys_error(GetLastError()); + + if (oldmask != NULL) { + for (i = 0; i < cpumasksize; i++) + oldmask[i] = (oldthreadmask >> i) & 1; + } + + return 0; +} + +int uv_thread_getaffinity(uv_thread_t* tid, + char* cpumask, + size_t mask_size) { + int i; + HANDLE hproc; + DWORD_PTR procmask; + DWORD_PTR sysmask; + DWORD_PTR threadmask; + int cpumasksize; + + cpumasksize = uv_cpumask_size(); + assert(cpumasksize > 0); + if (mask_size < (size_t)cpumasksize) + return UV_EINVAL; + + hproc = GetCurrentProcess(); + if (!GetProcessAffinityMask(hproc, &procmask, &sysmask)) + return uv_translate_sys_error(GetLastError()); + + threadmask = SetThreadAffinityMask(*tid, procmask); + if (threadmask == 0 || SetThreadAffinityMask(*tid, threadmask) == 0) + return uv_translate_sys_error(GetLastError()); + + for (i = 0; i < cpumasksize; i++) + cpumask[i] = (threadmask >> i) & 1; + + return 0; +} + +int uv_thread_getcpu(void) { + return GetCurrentProcessorNumber(); +} uv_thread_t uv_thread_self(void) { + uv_thread_t key; uv_once(&uv__current_thread_init_guard, uv__init_current_thread_key); - uv_thread_t key = uv_key_get(&uv__current_thread_key); + key = uv_key_get(&uv__current_thread_key); if (key == NULL) { /* If the thread wasn't started by uv_thread_create (such as the main * thread), we assign an id to it now. */ @@ -215,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; @@ -373,6 +525,7 @@ void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) { abort(); } + int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { if (SleepConditionVariableCS(&cond->cond_var, mutex, (DWORD)(timeout / 1e6))) return 0; @@ -382,69 +535,6 @@ int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { } -int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) { - int err; - - barrier->n = count; - barrier->count = 0; - - err = uv_mutex_init(&barrier->mutex); - if (err) - return err; - - err = uv_sem_init(&barrier->turnstile1, 0); - if (err) - goto error2; - - err = uv_sem_init(&barrier->turnstile2, 1); - if (err) - goto error; - - return 0; - -error: - uv_sem_destroy(&barrier->turnstile1); -error2: - uv_mutex_destroy(&barrier->mutex); - return err; - -} - - -void uv_barrier_destroy(uv_barrier_t* barrier) { - uv_sem_destroy(&barrier->turnstile2); - uv_sem_destroy(&barrier->turnstile1); - uv_mutex_destroy(&barrier->mutex); -} - - -int uv_barrier_wait(uv_barrier_t* barrier) { - int serial_thread; - - uv_mutex_lock(&barrier->mutex); - if (++barrier->count == barrier->n) { - uv_sem_wait(&barrier->turnstile2); - uv_sem_post(&barrier->turnstile1); - } - uv_mutex_unlock(&barrier->mutex); - - uv_sem_wait(&barrier->turnstile1); - uv_sem_post(&barrier->turnstile1); - - uv_mutex_lock(&barrier->mutex); - serial_thread = (--barrier->count == 0); - if (serial_thread) { - uv_sem_wait(&barrier->turnstile1); - uv_sem_post(&barrier->turnstile2); - } - uv_mutex_unlock(&barrier->mutex); - - uv_sem_wait(&barrier->turnstile2); - uv_sem_post(&barrier->turnstile2); - return serial_thread; -} - - int uv_key_create(uv_key_t* key) { key->tls_index = TlsAlloc(); if (key->tls_index == TLS_OUT_OF_INDEXES) diff --git a/src/win/tty.c b/src/win/tty.c index 1b9d4f85326..c0339ded2e4 100644 --- a/src/win/tty.c +++ b/src/win/tty.c @@ -23,12 +23,7 @@ #include #include #include - -#if defined(_MSC_VER) && _MSC_VER < 1600 -# include "uv/stdint-msvc2008.h" -#else -# include -#endif +#include #ifndef COMMON_LVB_REVERSE_VIDEO # define COMMON_LVB_REVERSE_VIDEO 0x4000 @@ -67,10 +62,10 @@ #define CURSOR_SIZE_SMALL 25 #define CURSOR_SIZE_LARGE 100 -static void uv_tty_capture_initial_style( +static void uv__tty_capture_initial_style( CONSOLE_SCREEN_BUFFER_INFO* screen_buffer_info, CONSOLE_CURSOR_INFO* cursor_info); -static void uv_tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info); +static void uv__tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info); static int uv__cancel_read_console(uv_tty_t* handle); @@ -163,7 +158,7 @@ static BOOL uv__need_check_vterm_state = TRUE; static uv_tty_vtermstate_t uv__vterm_state = UV_TTY_UNSUPPORTED; static void uv__determine_vterm_state(HANDLE handle); -void uv_console_init(void) { +void uv__console_init(void) { if (uv_sem_init(&uv_tty_output_lock, 1)) abort(); uv__tty_console_handle = CreateFileW(L"CONOUT$", @@ -175,14 +170,14 @@ void uv_console_init(void) { 0); if (uv__tty_console_handle != INVALID_HANDLE_VALUE) { CONSOLE_SCREEN_BUFFER_INFO sb_info; - QueueUserWorkItem(uv__tty_console_resize_message_loop_thread, - NULL, - WT_EXECUTELONGFUNCTION); uv_mutex_init(&uv__tty_console_resize_mutex); if (GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info)) { uv__tty_console_width = sb_info.dwSize.X; uv__tty_console_height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1; } + QueueUserWorkItem(uv__tty_console_resize_message_loop_thread, + NULL, + WT_EXECUTELONGFUNCTION); } } @@ -238,16 +233,16 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int unused) { uv__determine_vterm_state(handle); /* Remember the original console text attributes and cursor info. */ - uv_tty_capture_initial_style(&screen_buffer_info, &cursor_info); + uv__tty_capture_initial_style(&screen_buffer_info, &cursor_info); - uv_tty_update_virtual_window(&screen_buffer_info); + uv__tty_update_virtual_window(&screen_buffer_info); uv_sem_post(&uv_tty_output_lock); } - uv_stream_init(loop, (uv_stream_t*) tty, UV_TTY); - uv_connection_init((uv_stream_t*) tty); + uv__stream_init(loop, (uv_stream_t*) tty, UV_TTY); + uv__connection_init((uv_stream_t*) tty); tty->handle = handle; tty->u.fd = fd; @@ -289,7 +284,7 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int unused) { /* Set the default console text attributes based on how the console was * configured when libuv started. */ -static void uv_tty_capture_initial_style( +static void uv__tty_capture_initial_style( CONSOLE_SCREEN_BUFFER_INFO* screen_buffer_info, CONSOLE_CURSOR_INFO* cursor_info) { static int style_captured = 0; @@ -380,7 +375,7 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) { was_reading = 1; alloc_cb = tty->alloc_cb; read_cb = tty->read_cb; - err = uv_tty_read_stop(tty); + err = uv__tty_read_stop(tty); if (err) { return uv_translate_sys_error(err); } @@ -404,7 +399,7 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) { /* If we just stopped reading, restart. */ if (was_reading) { - err = uv_tty_read_start(tty, alloc_cb, read_cb); + err = uv__tty_read_start(tty, alloc_cb, read_cb); if (err) { return uv_translate_sys_error(err); } @@ -422,7 +417,7 @@ int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) { } uv_sem_wait(&uv_tty_output_lock); - uv_tty_update_virtual_window(&info); + uv__tty_update_virtual_window(&info); uv_sem_post(&uv_tty_output_lock); *width = uv_tty_virtual_width; @@ -452,7 +447,7 @@ static void CALLBACK uv_tty_post_raw_read(void* data, BOOLEAN didTimeout) { } -static void uv_tty_queue_read_raw(uv_loop_t* loop, uv_tty_t* handle) { +static void uv__tty_queue_read_raw(uv_loop_t* loop, uv_tty_t* handle) { uv_read_t* req; BOOL r; @@ -475,7 +470,7 @@ static void uv_tty_queue_read_raw(uv_loop_t* loop, uv_tty_t* handle) { if (!r) { handle->tty.rd.read_raw_wait = NULL; SET_REQ_ERROR(req, GetLastError()); - uv_insert_pending_req(loop, (uv_req_t*)req); + uv__insert_pending_req(loop, (uv_req_t*)req); } handle->flags |= UV_HANDLE_READ_PENDING; @@ -487,9 +482,11 @@ static DWORD CALLBACK uv_tty_line_read_thread(void* data) { uv_loop_t* loop; uv_tty_t* handle; uv_req_t* req; - DWORD bytes, read_bytes; + DWORD bytes; + size_t read_bytes; WCHAR utf16[MAX_INPUT_BUFFER_LENGTH / 3]; - DWORD chars, read_chars; + DWORD chars; + DWORD read_chars; LONG status; COORD pos; BOOL read_console_success; @@ -530,16 +527,13 @@ static DWORD CALLBACK uv_tty_line_read_thread(void* data) { NULL); if (read_console_success) { - read_bytes = WideCharToMultiByte(CP_UTF8, - 0, - utf16, - read_chars, - handle->tty.rd.read_line_buffer.base, - bytes, - NULL, - NULL); + read_bytes = bytes; + uv_utf16_to_wtf8(utf16, + read_chars, + &handle->tty.rd.read_line_buffer.base, + &read_bytes); SET_REQ_SUCCESS(req); - req->u.io.overlapped.InternalHigh = read_bytes; + req->u.io.overlapped.InternalHigh = (DWORD) read_bytes; } else { SET_REQ_ERROR(req, GetLastError()); } @@ -579,7 +573,7 @@ static DWORD CALLBACK uv_tty_line_read_thread(void* data) { } -static void uv_tty_queue_read_line(uv_loop_t* loop, uv_tty_t* handle) { +static void uv__tty_queue_read_line(uv_loop_t* loop, uv_tty_t* handle) { uv_read_t* req; BOOL r; @@ -611,7 +605,7 @@ static void uv_tty_queue_read_line(uv_loop_t* loop, uv_tty_t* handle) { WT_EXECUTELONGFUNCTION); if (!r) { SET_REQ_ERROR(req, GetLastError()); - uv_insert_pending_req(loop, (uv_req_t*)req); + uv__insert_pending_req(loop, (uv_req_t*)req); } handle->flags |= UV_HANDLE_READ_PENDING; @@ -619,11 +613,11 @@ static void uv_tty_queue_read_line(uv_loop_t* loop, uv_tty_t* handle) { } -static void uv_tty_queue_read(uv_loop_t* loop, uv_tty_t* handle) { +static void uv__tty_queue_read(uv_loop_t* loop, uv_tty_t* handle) { if (handle->flags & UV_HANDLE_TTY_RAW) { - uv_tty_queue_read_raw(loop, handle); + uv__tty_queue_read_raw(loop, handle); } else { - uv_tty_queue_read_line(loop, handle); + uv__tty_queue_read_line(loop, handle); } } @@ -701,7 +695,7 @@ 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); @@ -803,7 +797,9 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle, } if (KEV.uChar.UnicodeChar != 0) { - int prefix_len, char_len; + int prefix_len; + size_t char_len; + char* last_key_buf; /* Character key pressed */ if (KEV.uChar.UnicodeChar >= 0xD800 && @@ -824,38 +820,31 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle, prefix_len = 0; } - if (KEV.uChar.UnicodeChar >= 0xDC00 && - KEV.uChar.UnicodeChar < 0xE000) { + char_len = sizeof handle->tty.rd.last_key; + last_key_buf = &handle->tty.rd.last_key[prefix_len]; + if (handle->tty.rd.last_utf16_high_surrogate) { /* UTF-16 surrogate pair */ WCHAR utf16_buffer[2]; utf16_buffer[0] = handle->tty.rd.last_utf16_high_surrogate; utf16_buffer[1] = KEV.uChar.UnicodeChar; - char_len = WideCharToMultiByte(CP_UTF8, - 0, - utf16_buffer, - 2, - &handle->tty.rd.last_key[prefix_len], - sizeof handle->tty.rd.last_key, - NULL, - NULL); + if (uv_utf16_to_wtf8(utf16_buffer, + 2, + &last_key_buf, + &char_len)) + char_len = 0; + handle->tty.rd.last_utf16_high_surrogate = 0; } else { /* Single UTF-16 character */ - char_len = WideCharToMultiByte(CP_UTF8, - 0, - &KEV.uChar.UnicodeChar, - 1, - &handle->tty.rd.last_key[prefix_len], - sizeof handle->tty.rd.last_key, - NULL, - NULL); + if (uv_utf16_to_wtf8(&KEV.uChar.UnicodeChar, + 1, + &last_key_buf, + &char_len)) + char_len = 0; } - /* Whatever happened, the last character wasn't a high surrogate. */ - handle->tty.rd.last_utf16_high_surrogate = 0; - /* If the utf16 character(s) couldn't be converted something must be * wrong. */ - if (!char_len) { + if (char_len == 0) { handle->flags &= ~UV_HANDLE_READING; DECREASE_ACTIVE_COUNT(loop, handle); handle->read_cb((uv_stream_t*) handle, @@ -947,7 +936,7 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle, /* Wait for more input events. */ if ((handle->flags & UV_HANDLE_READING) && !(handle->flags & UV_HANDLE_READ_PENDING)) { - uv_tty_queue_read(loop, handle); + uv__tty_queue_read(loop, handle); } DECREASE_PENDING_REQ_COUNT(handle); @@ -992,14 +981,14 @@ void uv_process_tty_read_line_req(uv_loop_t* loop, uv_tty_t* handle, /* Wait for more input events. */ if ((handle->flags & UV_HANDLE_READING) && !(handle->flags & UV_HANDLE_READ_PENDING)) { - uv_tty_queue_read(loop, handle); + uv__tty_queue_read(loop, handle); } DECREASE_PENDING_REQ_COUNT(handle); } -void uv_process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle, +void uv__process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle, uv_req_t* req) { assert(handle->type == UV_TTY); assert(handle->flags & UV_HANDLE_TTY_READABLE); @@ -1015,7 +1004,7 @@ void uv_process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle, } -int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb, +int uv__tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { uv_loop_t* loop = handle->loop; @@ -1038,20 +1027,20 @@ int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb, * Short-circuit if this could be the case. */ if (handle->tty.rd.last_key_len > 0) { SET_REQ_SUCCESS(&handle->read_req); - uv_insert_pending_req(handle->loop, (uv_req_t*) &handle->read_req); + uv__insert_pending_req(handle->loop, (uv_req_t*) &handle->read_req); /* Make sure no attempt is made to insert it again until it's handled. */ handle->flags |= UV_HANDLE_READ_PENDING; handle->reqs_pending++; return 0; } - uv_tty_queue_read(loop, handle); + uv__tty_queue_read(loop, handle); return 0; } -int uv_tty_read_stop(uv_tty_t* handle) { +int uv__tty_read_stop(uv_tty_t* handle) { INPUT_RECORD record; DWORD written, err; @@ -1137,7 +1126,7 @@ static int uv__cancel_read_console(uv_tty_t* handle) { } -static void uv_tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info) { +static void uv__tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info) { uv_tty_virtual_width = info->dwSize.X; uv_tty_virtual_height = info->srWindow.Bottom - info->srWindow.Top + 1; @@ -1160,12 +1149,12 @@ static void uv_tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info) { } -static COORD uv_tty_make_real_coord(uv_tty_t* handle, +static COORD uv__tty_make_real_coord(uv_tty_t* handle, CONSOLE_SCREEN_BUFFER_INFO* info, int x, unsigned char x_relative, int y, unsigned char y_relative) { COORD result; - uv_tty_update_virtual_window(info); + uv__tty_update_virtual_window(info); /* Adjust y position */ if (y_relative) { @@ -1197,7 +1186,7 @@ static COORD uv_tty_make_real_coord(uv_tty_t* handle, } -static int uv_tty_emit_text(uv_tty_t* handle, WCHAR buffer[], DWORD length, +static int uv__tty_emit_text(uv_tty_t* handle, WCHAR buffer[], DWORD length, DWORD* error) { DWORD written; @@ -1218,7 +1207,7 @@ static int uv_tty_emit_text(uv_tty_t* handle, WCHAR buffer[], DWORD length, } -static int uv_tty_move_caret(uv_tty_t* handle, int x, unsigned char x_relative, +static int uv__tty_move_caret(uv_tty_t* handle, int x, unsigned char x_relative, int y, unsigned char y_relative, DWORD* error) { CONSOLE_SCREEN_BUFFER_INFO info; COORD pos; @@ -1232,7 +1221,7 @@ static int uv_tty_move_caret(uv_tty_t* handle, int x, unsigned char x_relative, *error = GetLastError(); } - pos = uv_tty_make_real_coord(handle, &info, x, x_relative, y, y_relative); + pos = uv__tty_make_real_coord(handle, &info, x, x_relative, y, y_relative); if (!SetConsoleCursorPosition(handle->handle, pos)) { if (GetLastError() == ERROR_INVALID_PARAMETER) { @@ -1248,7 +1237,7 @@ static int uv_tty_move_caret(uv_tty_t* handle, int x, unsigned char x_relative, } -static int uv_tty_reset(uv_tty_t* handle, DWORD* error) { +static int uv__tty_reset(uv_tty_t* handle, DWORD* error) { const COORD origin = {0, 0}; const WORD char_attrs = uv_tty_default_text_attributes; CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info; @@ -1300,7 +1289,7 @@ static int uv_tty_reset(uv_tty_t* handle, DWORD* error) { /* Move the virtual window up to the top. */ uv_tty_virtual_offset = 0; - uv_tty_update_virtual_window(&screen_buffer_info); + uv__tty_update_virtual_window(&screen_buffer_info); /* Reset the cursor size and the cursor state. */ if (!SetConsoleCursorInfo(handle->handle, &uv_tty_default_cursor_info)) { @@ -1312,7 +1301,7 @@ static int uv_tty_reset(uv_tty_t* handle, DWORD* error) { } -static int uv_tty_clear(uv_tty_t* handle, int dir, char entire_screen, +static int uv__tty_clear(uv_tty_t* handle, int dir, char entire_screen, DWORD* error) { CONSOLE_SCREEN_BUFFER_INFO info; COORD start, end; @@ -1341,7 +1330,7 @@ static int uv_tty_clear(uv_tty_t* handle, int dir, char entire_screen, x2r = 1; } else { /* Clear to end of row. We pretend the console is 65536 characters wide, - * uv_tty_make_real_coord will clip it to the actual console width. */ + * uv__tty_make_real_coord will clip it to the actual console width. */ x2 = 0xffff; x2r = 0; } @@ -1364,8 +1353,8 @@ static int uv_tty_clear(uv_tty_t* handle, int dir, char entire_screen, return -1; } - start = uv_tty_make_real_coord(handle, &info, x1, x1r, y1, y1r); - end = uv_tty_make_real_coord(handle, &info, x2, x2r, y2, y2r); + start = uv__tty_make_real_coord(handle, &info, x1, x1r, y1, y1r); + end = uv__tty_make_real_coord(handle, &info, x2, x2r, y2, y2r); count = (end.Y * info.dwSize.X + end.X) - (start.Y * info.dwSize.X + start.X) + 1; @@ -1400,7 +1389,7 @@ static int uv_tty_clear(uv_tty_t* handle, int dir, char entire_screen, info.wAttributes |= bg >> 4; \ } while (0) -static int uv_tty_set_style(uv_tty_t* handle, DWORD* error) { +static int uv__tty_set_style(uv_tty_t* handle, DWORD* error) { unsigned short argc = handle->tty.wr.ansi_csi_argc; unsigned short* argv = handle->tty.wr.ansi_csi_argv; int i; @@ -1556,7 +1545,7 @@ static int uv_tty_set_style(uv_tty_t* handle, DWORD* error) { } -static int uv_tty_save_state(uv_tty_t* handle, unsigned char save_attributes, +static int uv__tty_save_state(uv_tty_t* handle, unsigned char save_attributes, DWORD* error) { CONSOLE_SCREEN_BUFFER_INFO info; @@ -1569,10 +1558,11 @@ static int uv_tty_save_state(uv_tty_t* handle, unsigned char save_attributes, return -1; } - uv_tty_update_virtual_window(&info); + uv__tty_update_virtual_window(&info); handle->tty.wr.saved_position.X = info.dwCursorPosition.X; - handle->tty.wr.saved_position.Y = info.dwCursorPosition.Y - uv_tty_virtual_offset; + handle->tty.wr.saved_position.Y = info.dwCursorPosition.Y - + uv_tty_virtual_offset; handle->flags |= UV_HANDLE_TTY_SAVED_POSITION; if (save_attributes) { @@ -1585,7 +1575,7 @@ static int uv_tty_save_state(uv_tty_t* handle, unsigned char save_attributes, } -static int uv_tty_restore_state(uv_tty_t* handle, +static int uv__tty_restore_state(uv_tty_t* handle, unsigned char restore_attributes, DWORD* error) { CONSOLE_SCREEN_BUFFER_INFO info; WORD new_attributes; @@ -1595,7 +1585,7 @@ static int uv_tty_restore_state(uv_tty_t* handle, } if (handle->flags & UV_HANDLE_TTY_SAVED_POSITION) { - if (uv_tty_move_caret(handle, + if (uv__tty_move_caret(handle, handle->tty.wr.saved_position.X, 0, handle->tty.wr.saved_position.Y, @@ -1625,7 +1615,7 @@ static int uv_tty_restore_state(uv_tty_t* handle, return 0; } -static int uv_tty_set_cursor_visibility(uv_tty_t* handle, +static int uv__tty_set_cursor_visibility(uv_tty_t* handle, BOOL visible, DWORD* error) { CONSOLE_CURSOR_INFO cursor_info; @@ -1645,7 +1635,7 @@ static int uv_tty_set_cursor_visibility(uv_tty_t* handle, return 0; } -static int uv_tty_set_cursor_shape(uv_tty_t* handle, int style, DWORD* error) { +static int uv__tty_set_cursor_shape(uv_tty_t* handle, int style, DWORD* error) { CONSOLE_CURSOR_INFO cursor_info; if (!GetConsoleCursorInfo(handle->handle, &cursor_info)) { @@ -1670,7 +1660,7 @@ static int uv_tty_set_cursor_shape(uv_tty_t* handle, int style, DWORD* error) { } -static int uv_tty_write_bufs(uv_tty_t* handle, +static int uv__tty_write_bufs(uv_tty_t* handle, const uv_buf_t bufs[], unsigned int nbufs, DWORD* error) { @@ -1683,7 +1673,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, #define FLUSH_TEXT() \ do { \ if (utf16_buf_used > 0) { \ - uv_tty_emit_text(handle, utf16_buf, utf16_buf_used, error); \ + uv__tty_emit_text(handle, utf16_buf, utf16_buf_used, error); \ utf16_buf_used = 0; \ } \ } while (0) @@ -1802,21 +1792,21 @@ static int uv_tty_write_bufs(uv_tty_t* handle, case 'c': /* Full console reset. */ FLUSH_TEXT(); - uv_tty_reset(handle, error); + uv__tty_reset(handle, error); ansi_parser_state = ANSI_NORMAL; continue; case '7': /* Save the cursor position and text attributes. */ FLUSH_TEXT(); - uv_tty_save_state(handle, 1, error); + uv__tty_save_state(handle, 1, error); ansi_parser_state = ANSI_NORMAL; continue; case '8': /* Restore the cursor position and text attributes */ FLUSH_TEXT(); - uv_tty_restore_state(handle, 1, error); + uv__tty_restore_state(handle, 1, error); ansi_parser_state = ANSI_NORMAL; continue; @@ -1849,7 +1839,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, ? handle->tty.wr.ansi_csi_argv[0] : 1; if (style >= 0 && style <= 6) { FLUSH_TEXT(); - uv_tty_set_cursor_shape(handle, style, error); + uv__tty_set_cursor_shape(handle, style, error); } } @@ -1947,7 +1937,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, if (handle->tty.wr.ansi_csi_argc == 1 && handle->tty.wr.ansi_csi_argv[0] == 25) { FLUSH_TEXT(); - uv_tty_set_cursor_visibility(handle, 0, error); + uv__tty_set_cursor_visibility(handle, 0, error); } break; @@ -1956,7 +1946,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, if (handle->tty.wr.ansi_csi_argc == 1 && handle->tty.wr.ansi_csi_argv[0] == 25) { FLUSH_TEXT(); - uv_tty_set_cursor_visibility(handle, 1, error); + uv__tty_set_cursor_visibility(handle, 1, error); } break; } @@ -1970,7 +1960,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, FLUSH_TEXT(); y = -(handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1); - uv_tty_move_caret(handle, 0, 1, y, 1, error); + uv__tty_move_caret(handle, 0, 1, y, 1, error); break; case 'B': @@ -1978,7 +1968,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, FLUSH_TEXT(); y = handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1; - uv_tty_move_caret(handle, 0, 1, y, 1, error); + uv__tty_move_caret(handle, 0, 1, y, 1, error); break; case 'C': @@ -1986,7 +1976,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, FLUSH_TEXT(); x = handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1; - uv_tty_move_caret(handle, x, 1, 0, 1, error); + uv__tty_move_caret(handle, x, 1, 0, 1, error); break; case 'D': @@ -1994,7 +1984,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, FLUSH_TEXT(); x = -(handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1); - uv_tty_move_caret(handle, x, 1, 0, 1, error); + uv__tty_move_caret(handle, x, 1, 0, 1, error); break; case 'E': @@ -2002,7 +1992,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, FLUSH_TEXT(); y = handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1; - uv_tty_move_caret(handle, 0, 0, y, 1, error); + uv__tty_move_caret(handle, 0, 0, y, 1, error); break; case 'F': @@ -2010,7 +2000,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, FLUSH_TEXT(); y = -(handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1); - uv_tty_move_caret(handle, 0, 0, y, 1, error); + uv__tty_move_caret(handle, 0, 0, y, 1, error); break; case 'G': @@ -2019,7 +2009,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, x = (handle->tty.wr.ansi_csi_argc >= 1 && handle->tty.wr.ansi_csi_argv[0]) ? handle->tty.wr.ansi_csi_argv[0] - 1 : 0; - uv_tty_move_caret(handle, x, 0, 0, 1, error); + uv__tty_move_caret(handle, x, 0, 0, 1, error); break; case 'H': @@ -2032,7 +2022,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, x = (handle->tty.wr.ansi_csi_argc >= 2 && handle->tty.wr.ansi_csi_argv[1]) ? handle->tty.wr.ansi_csi_argv[1] - 1 : 0; - uv_tty_move_caret(handle, x, 0, y, 0, error); + uv__tty_move_caret(handle, x, 0, y, 0, error); break; case 'J': @@ -2041,7 +2031,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, d = handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 0; if (d >= 0 && d <= 2) { - uv_tty_clear(handle, d, 1, error); + uv__tty_clear(handle, d, 1, error); } break; @@ -2051,26 +2041,26 @@ static int uv_tty_write_bufs(uv_tty_t* handle, d = handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 0; if (d >= 0 && d <= 2) { - uv_tty_clear(handle, d, 0, error); + uv__tty_clear(handle, d, 0, error); } break; case 'm': /* Set style */ FLUSH_TEXT(); - uv_tty_set_style(handle, error); + uv__tty_set_style(handle, error); break; case 's': /* Save the cursor position. */ FLUSH_TEXT(); - uv_tty_save_state(handle, 0, error); + uv__tty_save_state(handle, 0, error); break; case 'u': /* Restore the cursor position */ FLUSH_TEXT(); - uv_tty_restore_state(handle, 0, error); + uv__tty_restore_state(handle, 0, error); break; } } @@ -2179,7 +2169,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, } -int uv_tty_write(uv_loop_t* loop, +int uv__tty_write(uv_loop_t* loop, uv_write_t* req, uv_tty_t* handle, const uv_buf_t bufs[], @@ -2193,17 +2183,17 @@ 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; - if (!uv_tty_write_bufs(handle, bufs, nbufs, &error)) { + if (!uv__tty_write_bufs(handle, bufs, nbufs, &error)) { SET_REQ_SUCCESS(req); } else { SET_REQ_ERROR(req, error); } - uv_insert_pending_req(loop, (uv_req_t*) req); + uv__insert_pending_req(loop, (uv_req_t*) req); return 0; } @@ -2217,107 +2207,88 @@ int uv__tty_try_write(uv_tty_t* handle, if (handle->stream.conn.write_reqs_pending > 0) return UV_EAGAIN; - if (uv_tty_write_bufs(handle, bufs, nbufs, &error)) + if (uv__tty_write_bufs(handle, bufs, nbufs, &error)) return uv_translate_sys_error(error); return uv__count_bufs(bufs, nbufs); } -void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle, +void uv__process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle, uv_write_t* req) { 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); req->cb(req, uv_translate_sys_error(err)); } + handle->stream.conn.write_reqs_pending--; - if (handle->stream.conn.shutdown_req != NULL && - handle->stream.conn.write_reqs_pending == 0) { - uv_want_endgame(loop, (uv_handle_t*)handle); - } + if (handle->stream.conn.write_reqs_pending == 0 && + uv__is_stream_shutting(handle)) + uv__process_tty_shutdown_req(loop, + handle, + handle->stream.conn.shutdown_req); DECREASE_PENDING_REQ_COUNT(handle); } -void uv_tty_close(uv_tty_t* handle) { +void uv__tty_close(uv_tty_t* handle) { assert(handle->u.fd == -1 || handle->u.fd > 2); if (handle->flags & UV_HANDLE_READING) - uv_tty_read_stop(handle); + uv__tty_read_stop(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; handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); uv__handle_closing(handle); - if (handle->reqs_pending == 0) { - uv_want_endgame(handle->loop, (uv_handle_t*) handle); - } + if (handle->reqs_pending == 0) + uv__want_endgame(handle->loop, (uv_handle_t*) handle); } -void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle) { - if (!(handle->flags & UV_HANDLE_TTY_READABLE) && - handle->stream.conn.shutdown_req != NULL && - handle->stream.conn.write_reqs_pending == 0) { - UNREGISTER_HANDLE_REQ(loop, handle, handle->stream.conn.shutdown_req); +void uv__process_tty_shutdown_req(uv_loop_t* loop, uv_tty_t* stream, uv_shutdown_t* req) { + assert(stream->stream.conn.write_reqs_pending == 0); + assert(req); - /* TTY shutdown is really just a no-op */ - if (handle->stream.conn.shutdown_req->cb) { - if (handle->flags & UV_HANDLE_CLOSING) { - handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req, UV_ECANCELED); - } else { - handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req, 0); - } - } + stream->stream.conn.shutdown_req = NULL; + UNREGISTER_HANDLE_REQ(loop, stream); - handle->stream.conn.shutdown_req = NULL; - - DECREASE_PENDING_REQ_COUNT(handle); - return; + /* TTY shutdown is really just a no-op */ + if (req->cb) { + if (stream->flags & UV_HANDLE_CLOSING) { + req->cb(req, UV_ECANCELED); + } else { + req->cb(req, 0); + } } - if (handle->flags & UV_HANDLE_CLOSING && - handle->reqs_pending == 0) { - /* The wait handle used for raw reading should be unregistered when the - * wait callback runs. */ - assert(!(handle->flags & UV_HANDLE_TTY_READABLE) || - handle->tty.rd.read_raw_wait == NULL); - - assert(!(handle->flags & UV_HANDLE_CLOSED)); - uv__handle_close(handle); - } + DECREASE_PENDING_REQ_COUNT(stream); } -/* - * uv_process_tty_accept_req() is a stub to keep DELEGATE_STREAM_REQ working - * TODO: find a way to remove it - */ -void uv_process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle, - uv_req_t* raw_req) { - abort(); -} +void uv__tty_endgame(uv_loop_t* loop, uv_tty_t* handle) { + assert(handle->flags & UV_HANDLE_CLOSING); + assert(handle->reqs_pending == 0); + /* The wait handle used for raw reading should be unregistered when the + * wait callback runs. */ + assert(!(handle->flags & UV_HANDLE_TTY_READABLE) || + handle->tty.rd.read_raw_wait == NULL); -/* - * uv_process_tty_connect_req() is a stub to keep DELEGATE_STREAM_REQ working - * TODO: find a way to remove it - */ -void uv_process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle, - uv_connect_t* req) { - abort(); + assert(!(handle->flags & UV_HANDLE_CLOSED)); + uv__handle_close(handle); } @@ -2409,8 +2380,8 @@ static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param) { /* Make sure to not overwhelm the system with resize events */ Sleep(33); WaitForSingleObject(uv__tty_console_resized, INFINITE); - uv__tty_console_signal_resize(); ResetEvent(uv__tty_console_resized); + uv__tty_console_signal_resize(); } return 0; } @@ -2426,7 +2397,6 @@ static void uv__tty_console_signal_resize(void) { height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1; uv_mutex_lock(&uv__tty_console_resize_mutex); - assert(uv__tty_console_width != -1 && uv__tty_console_height != -1); if (width != uv__tty_console_width || height != uv__tty_console_height) { uv__tty_console_width = width; uv__tty_console_height = height; diff --git a/src/win/udp.c b/src/win/udp.c index 3a86e0ea8c3..e0873c2a899 100644 --- a/src/win/udp.c +++ b/src/win/udp.c @@ -29,11 +29,6 @@ #include "req-inl.h" -/* - * Threshold of active udp streams for which to preallocate udp read buffers. - */ -const unsigned int uv_active_udp_streams_threshold = 0; - /* A zero-size buffer for use by uv_udp_read */ static char uv_zero_[] = ""; int uv_udp_getpeername(const uv_udp_t* handle, @@ -60,7 +55,7 @@ int uv_udp_getsockname(const uv_udp_t* handle, } -static int uv_udp_set_socket(uv_loop_t* loop, uv_udp_t* handle, SOCKET socket, +static int uv__udp_set_socket(uv_loop_t* loop, uv_udp_t* handle, SOCKET socket, int family) { DWORD yes = 1; WSAPROTOCOL_INFOW info; @@ -106,8 +101,8 @@ static int uv_udp_set_socket(uv_loop_t* loop, uv_udp_t* handle, SOCKET socket, FILE_SKIP_SET_EVENT_ON_HANDLE | FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) { handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP; - handle->func_wsarecv = uv_wsarecv_workaround; - handle->func_wsarecvfrom = uv_wsarecvfrom_workaround; + handle->func_wsarecv = uv__wsarecv_workaround; + handle->func_wsarecvfrom = uv__wsarecvfrom_workaround; } else if (GetLastError() != ERROR_INVALID_FUNCTION) { return GetLastError(); } @@ -151,14 +146,14 @@ int uv__udp_init_ex(uv_loop_t* loop, sock = socket(domain, SOCK_DGRAM, 0); if (sock == INVALID_SOCKET) { err = WSAGetLastError(); - QUEUE_REMOVE(&handle->handle_queue); + uv__queue_remove(&handle->handle_queue); return uv_translate_sys_error(err); } - err = uv_udp_set_socket(handle->loop, handle, sock, domain); + err = uv__udp_set_socket(handle->loop, handle, sock, domain); if (err) { closesocket(sock); - QUEUE_REMOVE(&handle->handle_queue); + uv__queue_remove(&handle->handle_queue); return uv_translate_sys_error(err); } } @@ -167,7 +162,7 @@ int uv__udp_init_ex(uv_loop_t* loop, } -void uv_udp_close(uv_loop_t* loop, uv_udp_t* handle) { +void uv__udp_close(uv_loop_t* loop, uv_udp_t* handle) { uv_udp_recv_stop(handle); closesocket(handle->socket); handle->socket = INVALID_SOCKET; @@ -175,12 +170,12 @@ void uv_udp_close(uv_loop_t* loop, uv_udp_t* handle) { uv__handle_closing(handle); if (handle->reqs_pending == 0) { - uv_want_endgame(loop, (uv_handle_t*) handle); + uv__want_endgame(loop, (uv_handle_t*) handle); } } -void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle) { +void uv__udp_endgame(uv_loop_t* loop, uv_udp_t* handle) { if (handle->flags & UV_HANDLE_CLOSING && handle->reqs_pending == 0) { assert(!(handle->flags & UV_HANDLE_CLOSED)); @@ -194,10 +189,10 @@ int uv_udp_using_recvmmsg(const uv_udp_t* handle) { } -static int uv_udp_maybe_bind(uv_udp_t* handle, - const struct sockaddr* addr, - unsigned int addrlen, - unsigned int flags) { +static int uv__udp_maybe_bind(uv_udp_t* handle, + const struct sockaddr* addr, + unsigned int addrlen, + unsigned int flags) { int r; int err; DWORD no = 0; @@ -205,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; @@ -216,7 +217,7 @@ static int uv_udp_maybe_bind(uv_udp_t* handle, return WSAGetLastError(); } - err = uv_udp_set_socket(handle->loop, handle, sock, addr->sa_family); + err = uv__udp_set_socket(handle->loop, handle, sock, addr->sa_family); if (err) { closesocket(sock); return err; @@ -264,7 +265,7 @@ static int uv_udp_maybe_bind(uv_udp_t* handle, } -static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) { +static void uv__udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) { uv_req_t* req; uv_buf_t buf; DWORD bytes, flags; @@ -276,84 +277,35 @@ static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) { req = &handle->recv_req; memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); - /* - * Preallocate a read buffer if the number of active streams is below - * the threshold. - */ - if (loop->active_udp_streams < uv_active_udp_streams_threshold) { - handle->flags &= ~UV_HANDLE_ZERO_READ; - - handle->recv_buffer = uv_buf_init(NULL, 0); - handle->alloc_cb((uv_handle_t*) handle, UV__UDP_DGRAM_MAXSIZE, &handle->recv_buffer); - if (handle->recv_buffer.base == NULL || handle->recv_buffer.len == 0) { - handle->recv_cb(handle, UV_ENOBUFS, &handle->recv_buffer, NULL, 0); - return; - } - assert(handle->recv_buffer.base != NULL); - - buf = handle->recv_buffer; - memset(&handle->recv_from, 0, sizeof handle->recv_from); - handle->recv_from_len = sizeof handle->recv_from; - flags = 0; - - result = handle->func_wsarecvfrom(handle->socket, - (WSABUF*) &buf, - 1, - &bytes, - &flags, - (struct sockaddr*) &handle->recv_from, - &handle->recv_from_len, - &req->u.io.overlapped, - NULL); - - if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { - /* Process the req without IOCP. */ - handle->flags |= UV_HANDLE_READ_PENDING; - req->u.io.overlapped.InternalHigh = bytes; - handle->reqs_pending++; - uv_insert_pending_req(loop, req); - } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { - /* The req will be processed with IOCP. */ - handle->flags |= UV_HANDLE_READ_PENDING; - handle->reqs_pending++; - } else { - /* Make this req pending reporting an error. */ - SET_REQ_ERROR(req, WSAGetLastError()); - uv_insert_pending_req(loop, req); - handle->reqs_pending++; - } + handle->flags |= UV_HANDLE_ZERO_READ; + + buf.base = (char*) uv_zero_; + buf.len = 0; + flags = MSG_PEEK; + + result = handle->func_wsarecv(handle->socket, + (WSABUF*) &buf, + 1, + &bytes, + &flags, + &req->u.io.overlapped, + NULL); + if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { + /* Process the req without IOCP. */ + handle->flags |= UV_HANDLE_READ_PENDING; + req->u.io.overlapped.InternalHigh = bytes; + handle->reqs_pending++; + uv__insert_pending_req(loop, req); + } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { + /* The req will be processed with IOCP. */ + handle->flags |= UV_HANDLE_READ_PENDING; + handle->reqs_pending++; } else { - handle->flags |= UV_HANDLE_ZERO_READ; - - buf.base = (char*) uv_zero_; - buf.len = 0; - flags = MSG_PEEK; - - result = handle->func_wsarecv(handle->socket, - (WSABUF*) &buf, - 1, - &bytes, - &flags, - &req->u.io.overlapped, - NULL); - - if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { - /* Process the req without IOCP. */ - handle->flags |= UV_HANDLE_READ_PENDING; - req->u.io.overlapped.InternalHigh = bytes; - handle->reqs_pending++; - uv_insert_pending_req(loop, req); - } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { - /* The req will be processed with IOCP. */ - handle->flags |= UV_HANDLE_READ_PENDING; - handle->reqs_pending++; - } else { - /* Make this req pending reporting an error. */ - SET_REQ_ERROR(req, WSAGetLastError()); - uv_insert_pending_req(loop, req); - handle->reqs_pending++; - } + /* Make this req pending reporting an error. */ + SET_REQ_ERROR(req, WSAGetLastError()); + uv__insert_pending_req(loop, req); + handle->reqs_pending++; } } @@ -367,16 +319,15 @@ int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, return UV_EALREADY; } - err = uv_udp_maybe_bind(handle, - (const struct sockaddr*) &uv_addr_ip4_any_, - sizeof(uv_addr_ip4_any_), - 0); + err = uv__udp_maybe_bind(handle, + (const struct sockaddr*) &uv_addr_ip4_any_, + sizeof(uv_addr_ip4_any_), + 0); if (err) return uv_translate_sys_error(err); handle->flags |= UV_HANDLE_READING; INCREASE_ACTIVE_COUNT(loop, handle); - loop->active_udp_streams++; handle->recv_cb = recv_cb; handle->alloc_cb = alloc_cb; @@ -384,7 +335,7 @@ int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, /* If reading was stopped and then started again, there could still be a recv * request pending. */ if (!(handle->flags & UV_HANDLE_READ_PENDING)) - uv_udp_queue_recv(loop, handle); + uv__udp_queue_recv(loop, handle); return 0; } @@ -393,7 +344,6 @@ int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, int uv__udp_recv_stop(uv_udp_t* handle) { if (handle->flags & UV_HANDLE_READING) { handle->flags &= ~UV_HANDLE_READING; - handle->loop->active_udp_streams--; DECREASE_ACTIVE_COUNT(loop, handle); } @@ -432,15 +382,15 @@ 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); - uv_insert_pending_req(loop, (uv_req_t*)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->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(); @@ -450,7 +400,7 @@ static int uv__send(uv_udp_send_t* req, } -void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle, +void uv__process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle, uv_req_t* req) { uv_buf_t buf; int partial; @@ -497,71 +447,82 @@ void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle, DWORD bytes, err, flags; struct sockaddr_storage from; int from_len; + int count; + + /* Prevent loop starvation when the data comes in as fast as + * (or faster than) we can read it. */ + count = 32; + + do { + /* Do at most `count` nonblocking receive. */ + buf = uv_buf_init(NULL, 0); + handle->alloc_cb((uv_handle_t*) handle, UV__UDP_DGRAM_MAXSIZE, &buf); + if (buf.base == NULL || buf.len == 0) { + handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0); + goto done; + } - /* Do a nonblocking receive. - * TODO: try to read multiple datagrams at once. FIONREAD maybe? */ - buf = uv_buf_init(NULL, 0); - handle->alloc_cb((uv_handle_t*) handle, UV__UDP_DGRAM_MAXSIZE, &buf); - if (buf.base == NULL || buf.len == 0) { - handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0); - goto done; - } - assert(buf.base != NULL); - - memset(&from, 0, sizeof from); - from_len = sizeof from; + memset(&from, 0, sizeof from); + from_len = sizeof from; - flags = 0; + flags = 0; - if (WSARecvFrom(handle->socket, - (WSABUF*)&buf, - 1, - &bytes, - &flags, - (struct sockaddr*) &from, - &from_len, - NULL, - NULL) != SOCKET_ERROR) { + if (WSARecvFrom(handle->socket, + (WSABUF*)&buf, + 1, + &bytes, + &flags, + (struct sockaddr*) &from, + &from_len, + NULL, + NULL) != SOCKET_ERROR) { - /* Message received */ - handle->recv_cb(handle, bytes, &buf, (const struct sockaddr*) &from, 0); - } else { - err = WSAGetLastError(); - if (err == WSAEMSGSIZE) { - /* Message truncated */ - handle->recv_cb(handle, - bytes, - &buf, - (const struct sockaddr*) &from, - UV_UDP_PARTIAL); - } else if (err == WSAEWOULDBLOCK) { - /* Kernel buffer empty */ - handle->recv_cb(handle, 0, &buf, NULL, 0); - } else if (err == WSAECONNRESET || err == WSAENETRESET) { - /* WSAECONNRESET/WSANETRESET is ignored because this just indicates - * that a previous sendto operation failed. - */ - handle->recv_cb(handle, 0, &buf, NULL, 0); + /* Message received */ + err = ERROR_SUCCESS; + handle->recv_cb(handle, bytes, &buf, (const struct sockaddr*) &from, 0); } else { - /* Any other error that we want to report back to the user. */ - uv_udp_recv_stop(handle); - handle->recv_cb(handle, uv_translate_sys_error(err), &buf, NULL, 0); + err = WSAGetLastError(); + if (err == WSAEMSGSIZE) { + /* Message truncated */ + handle->recv_cb(handle, + bytes, + &buf, + (const struct sockaddr*) &from, + UV_UDP_PARTIAL); + } else if (err == WSAEWOULDBLOCK) { + /* Kernel buffer empty */ + handle->recv_cb(handle, 0, &buf, NULL, 0); + } else if (err == WSAECONNRESET || err == WSAENETRESET) { + /* WSAECONNRESET/WSANETRESET is ignored because this just indicates + * that a previous sendto operation failed. + */ + handle->recv_cb(handle, 0, &buf, NULL, 0); + } else { + /* Any other error that we want to report back to the user. */ + uv_udp_recv_stop(handle); + handle->recv_cb(handle, uv_translate_sys_error(err), &buf, NULL, 0); + } } } + while (err == ERROR_SUCCESS && + count-- > 0 && + /* The recv_cb callback may decide to pause or close the handle. */ + (handle->flags & UV_HANDLE_READING) && + !(handle->flags & UV_HANDLE_READ_PENDING)); } done: /* Post another read if still reading and not closing. */ if ((handle->flags & UV_HANDLE_READING) && !(handle->flags & UV_HANDLE_READ_PENDING)) { - uv_udp_queue_recv(loop, handle); + uv__udp_queue_recv(loop, handle); } DECREASE_PENDING_REQ_COUNT(handle); } -void uv_process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle, +void uv__process_udp_send_req(uv_loop_t* loop, uv_udp_t* handle, uv_udp_send_t* req) { int err; @@ -572,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; @@ -598,10 +559,10 @@ static int uv__udp_set_membership4(uv_udp_t* handle, return UV_EINVAL; /* If the socket is unbound, bind to inaddr_any. */ - err = uv_udp_maybe_bind(handle, - (const struct sockaddr*) &uv_addr_ip4_any_, - sizeof(uv_addr_ip4_any_), - UV_UDP_REUSEADDR); + err = uv__udp_maybe_bind(handle, + (const struct sockaddr*) &uv_addr_ip4_any_, + sizeof(uv_addr_ip4_any_), + UV_UDP_REUSEADDR); if (err) return uv_translate_sys_error(err); @@ -652,10 +613,10 @@ int uv__udp_set_membership6(uv_udp_t* handle, if ((handle->flags & UV_HANDLE_BOUND) && !(handle->flags & UV_HANDLE_IPV6)) return UV_EINVAL; - err = uv_udp_maybe_bind(handle, - (const struct sockaddr*) &uv_addr_ip6_any_, - sizeof(uv_addr_ip6_any_), - UV_UDP_REUSEADDR); + err = uv__udp_maybe_bind(handle, + (const struct sockaddr*) &uv_addr_ip6_any_, + sizeof(uv_addr_ip6_any_), + UV_UDP_REUSEADDR); if (err) return uv_translate_sys_error(err); @@ -708,10 +669,10 @@ static int uv__udp_set_source_membership4(uv_udp_t* handle, return UV_EINVAL; /* If the socket is unbound, bind to inaddr_any. */ - err = uv_udp_maybe_bind(handle, - (const struct sockaddr*) &uv_addr_ip4_any_, - sizeof(uv_addr_ip4_any_), - UV_UDP_REUSEADDR); + err = uv__udp_maybe_bind(handle, + (const struct sockaddr*) &uv_addr_ip4_any_, + sizeof(uv_addr_ip4_any_), + UV_UDP_REUSEADDR); if (err) return uv_translate_sys_error(err); @@ -763,10 +724,10 @@ int uv__udp_set_source_membership6(uv_udp_t* handle, if ((handle->flags & UV_HANDLE_BOUND) && !(handle->flags & UV_HANDLE_IPV6)) return UV_EINVAL; - err = uv_udp_maybe_bind(handle, - (const struct sockaddr*) &uv_addr_ip6_any_, - sizeof(uv_addr_ip6_any_), - UV_UDP_REUSEADDR); + err = uv__udp_maybe_bind(handle, + (const struct sockaddr*) &uv_addr_ip6_any_, + sizeof(uv_addr_ip6_any_), + UV_UDP_REUSEADDR); if (err) return uv_translate_sys_error(err); @@ -962,10 +923,10 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { return uv_translate_sys_error(GetLastError()); } - err = uv_udp_set_socket(handle->loop, - handle, - sock, - protocol_info.iAddressFamily); + err = uv__udp_set_socket(handle->loop, + handle, + sock, + protocol_info.iAddressFamily); if (err) return uv_translate_sys_error(err); @@ -1044,7 +1005,7 @@ int uv__udp_bind(uv_udp_t* handle, unsigned int flags) { int err; - err = uv_udp_maybe_bind(handle, addr, addrlen, flags); + err = uv__udp_maybe_bind(handle, addr, addrlen, flags); if (err) return uv_translate_sys_error(err); @@ -1066,7 +1027,7 @@ int uv__udp_connect(uv_udp_t* handle, else return UV_EINVAL; - err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0); + err = uv__udp_maybe_bind(handle, bind_addr, addrlen, 0); if (err) return uv_translate_sys_error(err); } @@ -1087,7 +1048,7 @@ int uv__udp_disconnect(uv_udp_t* handle) { memset(&addr, 0, sizeof(addr)); - err = connect(handle->socket, &addr, sizeof(addr)); + err = connect(handle->socket, (struct sockaddr*) &addr, sizeof(addr)); if (err) return uv_translate_sys_error(WSAGetLastError()); @@ -1117,7 +1078,7 @@ int uv__udp_send(uv_udp_send_t* req, else return UV_EINVAL; - err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0); + err = uv__udp_maybe_bind(handle, bind_addr, addrlen, 0); if (err) return uv_translate_sys_error(err); } @@ -1140,12 +1101,14 @@ 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); if (err) return err; + addr = (const struct sockaddr*) &converted; } /* Already sending a message.*/ @@ -1159,7 +1122,7 @@ int uv__udp_try_send(uv_udp_t* handle, bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_; else return UV_EINVAL; - err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0); + err = uv__udp_maybe_bind(handle, bind_addr, addrlen, 0); if (err) return uv_translate_sys_error(err); } @@ -1169,7 +1132,7 @@ int uv__udp_try_send(uv_udp_t* handle, nbufs, &bytes, 0, - (const struct sockaddr*) &converted, + addr, addrlen, NULL, NULL); @@ -1179,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; +} diff --git a/src/win/util.c b/src/win/util.c index 33e874ac442..57061bf8d70 100644 --- a/src/win/util.c +++ b/src/win/util.c @@ -31,6 +31,7 @@ #include "internal.h" /* clang-format off */ +#include #include #include #include @@ -94,7 +95,7 @@ void uv__util_init(void) { int uv_exepath(char* buffer, size_t* size_ptr) { - int utf8_len, utf16_buffer_len, utf16_len; + size_t utf8_len, utf16_buffer_len, utf16_len; WCHAR* utf16_buffer; int err; @@ -121,29 +122,18 @@ int uv_exepath(char* buffer, size_t* size_ptr) { goto error; } - /* utf16_len contains the length, *not* including the terminating null. */ - utf16_buffer[utf16_len] = L'\0'; - /* Convert to UTF-8 */ - utf8_len = WideCharToMultiByte(CP_UTF8, - 0, - utf16_buffer, - -1, - buffer, - (int) *size_ptr, - NULL, - NULL); - if (utf8_len == 0) { - err = GetLastError(); - goto error; + utf8_len = *size_ptr - 1; /* Reserve space for NUL */ + err = uv_utf16_to_wtf8(utf16_buffer, utf16_len, &buffer, &utf8_len); + if (err == UV_ENOBUFS) { + utf8_len = *size_ptr - 1; + err = 0; } + *size_ptr = utf8_len; uv__free(utf16_buffer); - /* utf8_len *does* include the terminating null at this point, but the - * returned size shouldn't. */ - *size_ptr = utf8_len - 1; - return 0; + return err; error: uv__free(utf16_buffer); @@ -151,143 +141,104 @@ int uv_exepath(char* buffer, size_t* size_ptr) { } -int uv_cwd(char* buffer, size_t* size) { - DWORD utf16_len; - WCHAR *utf16_buffer; - int r; +static int uv__cwd(WCHAR** buf, DWORD *len) { + WCHAR* p; + DWORD n; + DWORD t; - if (buffer == NULL || size == NULL) { - return UV_EINVAL; - } + t = GetCurrentDirectoryW(0, NULL); + for (;;) { + if (t == 0) + return uv_translate_sys_error(GetLastError()); - utf16_len = GetCurrentDirectoryW(0, NULL); - if (utf16_len == 0) { - return uv_translate_sys_error(GetLastError()); - } - utf16_buffer = uv__malloc(utf16_len * sizeof(WCHAR)); - if (utf16_buffer == NULL) { - return UV_ENOMEM; - } + /* |t| is the size of the buffer _including_ nul. */ + p = uv__malloc(t * sizeof(*p)); + if (p == NULL) + return UV_ENOMEM; - utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer); - if (utf16_len == 0) { - uv__free(utf16_buffer); - return uv_translate_sys_error(GetLastError()); - } + /* |n| is the size of the buffer _excluding_ nul but _only on success_. + * If |t| was too small because another thread changed the working + * directory, |n| is the size the buffer should be _including_ nul. + * It therefore follows we must resize when n >= t and fail when n == 0. + */ + n = GetCurrentDirectoryW(t, p); + if (n > 0) + if (n < t) + break; - /* utf16_len contains the length, *not* including the terminating null. */ - utf16_buffer[utf16_len] = L'\0'; + uv__free(p); + t = n; + } /* The returned directory should not have a trailing slash, unless it points - * at a drive root, like c:\. Remove it if needed. */ - if (utf16_buffer[utf16_len - 1] == L'\\' && - !(utf16_len == 3 && utf16_buffer[1] == L':')) { - utf16_len--; - utf16_buffer[utf16_len] = L'\0'; - } - - /* Check how much space we need */ - r = WideCharToMultiByte(CP_UTF8, - 0, - utf16_buffer, - -1, - NULL, - 0, - NULL, - NULL); - if (r == 0) { - uv__free(utf16_buffer); - return uv_translate_sys_error(GetLastError()); - } else if (r > (int) *size) { - uv__free(utf16_buffer); - *size = r; - return UV_ENOBUFS; + * at a drive root, like c:\. Remove it if needed. + */ + t = n - 1; + if (p[t] == L'\\' && !(n == 3 && p[1] == L':')) { + p[t] = L'\0'; + n = t; } - /* Convert to UTF-8 */ - r = WideCharToMultiByte(CP_UTF8, - 0, - utf16_buffer, - -1, - buffer, - *size > INT_MAX ? INT_MAX : (int) *size, - NULL, - NULL); - uv__free(utf16_buffer); + *buf = p; + *len = n; - if (r == 0) { - return uv_translate_sys_error(GetLastError()); - } - - *size = r - 1; return 0; } -int uv_chdir(const char* dir) { +int uv_cwd(char* buffer, size_t* size) { + DWORD utf16_len; WCHAR *utf16_buffer; - size_t utf16_len, new_utf16_len; - WCHAR drive_letter, env_var[4]; + int r; - if (dir == NULL) { + if (buffer == NULL || size == NULL || *size == 0) { return UV_EINVAL; } - utf16_len = MultiByteToWideChar(CP_UTF8, - 0, - dir, - -1, - NULL, - 0); - if (utf16_len == 0) { - return uv_translate_sys_error(GetLastError()); - } - utf16_buffer = uv__malloc(utf16_len * sizeof(WCHAR)); - if (utf16_buffer == NULL) { - return UV_ENOMEM; - } + r = uv__cwd(&utf16_buffer, &utf16_len); + if (r < 0) + return r; - if (MultiByteToWideChar(CP_UTF8, - 0, - dir, - -1, - utf16_buffer, - utf16_len) == 0) { - uv__free(utf16_buffer); - return uv_translate_sys_error(GetLastError()); - } + r = uv__copy_utf16_to_utf8(utf16_buffer, utf16_len, buffer, size); + + uv__free(utf16_buffer); + + return r; +} + + +int uv_chdir(const char* dir) { + WCHAR *utf16_buffer; + DWORD utf16_len; + WCHAR drive_letter, env_var[4]; + int r; + + /* Convert to UTF-16 */ + r = uv__convert_utf8_to_utf16(dir, &utf16_buffer); + if (r) + return r; if (!SetCurrentDirectoryW(utf16_buffer)) { uv__free(utf16_buffer); return uv_translate_sys_error(GetLastError()); } + /* uv__cwd() will return a new buffer. */ + uv__free(utf16_buffer); + utf16_buffer = NULL; + /* Windows stores the drive-local path in an "hidden" environment variable, * which has the form "=C:=C:\Windows". SetCurrentDirectory does not update * this, so we'll have to do it. */ - new_utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer); - if (new_utf16_len > utf16_len ) { - uv__free(utf16_buffer); - utf16_buffer = uv__malloc(new_utf16_len * sizeof(WCHAR)); - if (utf16_buffer == NULL) { - /* When updating the environment variable fails, return UV_OK anyway. - * We did successfully change current working directory, only updating - * hidden env variable failed. */ - return 0; - } - new_utf16_len = GetCurrentDirectoryW(new_utf16_len, utf16_buffer); - } - if (utf16_len == 0) { - uv__free(utf16_buffer); + r = uv__cwd(&utf16_buffer, &utf16_len); + if (r == UV_ENOMEM) { + /* When updating the environment variable fails, return UV_OK anyway. + * We did successfully change current working directory, only updating + * hidden env variable failed. */ return 0; } - - /* The returned directory should not have a trailing slash, unless it points - * at a drive root, like c:\. Remove it if needed. */ - if (utf16_buffer[utf16_len - 1] == L'\\' && - !(utf16_len == 3 && utf16_buffer[1] == L':')) { - utf16_len--; - utf16_buffer[utf16_len] = L'\0'; + if (r < 0) { + return r; } if (utf16_len < 2 || utf16_buffer[1] != L':') { @@ -330,7 +281,7 @@ uint64_t uv_get_free_memory(void) { memory_status.dwLength = sizeof(memory_status); if (!GlobalMemoryStatusEx(&memory_status)) { - return -1; + return 0; } return (uint64_t)memory_status.ullAvailPhys; @@ -342,7 +293,7 @@ uint64_t uv_get_total_memory(void) { memory_status.dwLength = sizeof(memory_status); if (!GlobalMemoryStatusEx(&memory_status)) { - return -1; + return 0; } return (uint64_t)memory_status.ullTotalPhys; @@ -354,31 +305,30 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + uv_pid_t uv_os_getpid(void) { return GetCurrentProcessId(); } uv_pid_t uv_os_getppid(void) { - int parent_pid = -1; - HANDLE handle; - PROCESSENTRY32 pe; - DWORD current_pid = GetCurrentProcessId(); - - 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)); + NTSTATUS nt_status; + PROCESS_BASIC_INFORMATION basic_info; + + 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; } @@ -398,29 +348,14 @@ int uv_set_process_title(const char* title) { uv__once_init(); - /* Find out how big the buffer for the wide-char title must be */ - length = MultiByteToWideChar(CP_UTF8, 0, title, -1, NULL, 0); - if (!length) { - err = GetLastError(); - goto done; - } - - /* Convert to wide-char string */ - title_w = (WCHAR*)uv__malloc(sizeof(WCHAR) * length); - if (!title_w) { - uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); - } - - length = MultiByteToWideChar(CP_UTF8, 0, title, -1, title_w, length); - if (!length) { - err = GetLastError(); - goto done; - } + err = uv__convert_utf8_to_utf16(title, &title_w); + if (err) + return err; /* If the title must be truncated insert a \0 terminator there */ - if (length > MAX_TITLE_LENGTH) { + length = wcslen(title_w); + if (length >= MAX_TITLE_LENGTH) title_w[MAX_TITLE_LENGTH - 1] = L'\0'; - } if (!SetConsoleTitleW(title_w)) { err = GetLastError(); @@ -442,20 +377,19 @@ int uv_set_process_title(const char* title) { static int uv__get_process_title(void) { WCHAR title_w[MAX_TITLE_LENGTH]; + DWORD wlen; - if (!GetConsoleTitleW(title_w, sizeof(title_w) / sizeof(WCHAR))) { - return -1; - } - - if (uv__convert_utf16_to_utf8(title_w, -1, &process_title) != 0) - return -1; + wlen = GetConsoleTitleW(title_w, sizeof(title_w) / sizeof(WCHAR)); + if (wlen == 0) + return uv_translate_sys_error(GetLastError()); - return 0; + return uv__convert_utf16_to_utf8(title_w, wlen, &process_title); } int uv_get_process_title(char* buffer, size_t size) { size_t len; + int r; if (buffer == NULL || size == 0) return UV_EINVAL; @@ -467,9 +401,12 @@ int uv_get_process_title(char* buffer, size_t size) { * If the process_title was never read before nor explicitly set, * we must query it with getConsoleTitleW */ - if (!process_title && uv__get_process_title() == -1) { - LeaveCriticalSection(&process_title_lock); - return uv_translate_sys_error(GetLastError()); + if (process_title == NULL) { + r = uv__get_process_title(); + if (r) { + LeaveCriticalSection(&process_title_lock); + return r; + } } assert(process_title); @@ -487,11 +424,43 @@ int uv_get_process_title(char* buffer, size_t size) { } +/* https://github.com/libuv/libuv/issues/1674 */ +int uv_clock_gettime(uv_clock_id clock_id, uv_timespec64_t* ts) { + FILETIME ft; + int64_t t; + + if (ts == NULL) + return UV_EFAULT; + + switch (clock_id) { + case UV_CLOCK_MONOTONIC: + uv__once_init(); + t = uv__hrtime(UV__NANOSEC); + ts->tv_sec = t / 1000000000; + ts->tv_nsec = t % 1000000000; + return 0; + case UV_CLOCK_REALTIME: + GetSystemTimePreciseAsFileTime(&ft); + /* In 100-nanosecond increments from 1601-01-01 UTC because why not? */ + t = (int64_t) ft.dwHighDateTime << 32 | ft.dwLowDateTime; + /* Convert to UNIX epoch, 1970-01-01. Still in 100 ns increments. */ + t -= 116444736000000000ll; + /* Now convert to seconds and nanoseconds. */ + ts->tv_sec = t / 10000000; + ts->tv_nsec = t % 10000000 * 100; + return 0; + } + + return UV_EINVAL; +} + + uint64_t uv_hrtime(void) { uv__once_init(); return uv__hrtime(UV__NANOSEC); } + uint64_t uv__hrtime(unsigned int scale) { LARGE_INTEGER counter; double scaled_freq; @@ -531,103 +500,29 @@ int uv_resident_set_memory(size_t* rss) { int uv_uptime(double* uptime) { - BYTE stack_buffer[4096]; - BYTE* malloced_buffer = NULL; - BYTE* buffer = (BYTE*) stack_buffer; - size_t buffer_size = sizeof(stack_buffer); - DWORD data_size; - - PERF_DATA_BLOCK* data_block; - PERF_OBJECT_TYPE* object_type; - PERF_COUNTER_DEFINITION* counter_definition; - - DWORD i; - - for (;;) { - LONG result; - - data_size = (DWORD) buffer_size; - result = RegQueryValueExW(HKEY_PERFORMANCE_DATA, - L"2", - NULL, - NULL, - buffer, - &data_size); - if (result == ERROR_SUCCESS) { - break; - } else if (result != ERROR_MORE_DATA) { - *uptime = 0; - return uv_translate_sys_error(result); - } - - buffer_size *= 2; - /* Don't let the buffer grow infinitely. */ - if (buffer_size > 1 << 20) { - goto internalError; - } - - uv__free(malloced_buffer); - - buffer = malloced_buffer = (BYTE*) uv__malloc(buffer_size); - if (malloced_buffer == NULL) { - *uptime = 0; - return UV_ENOMEM; - } - } - - if (data_size < sizeof(*data_block)) - goto internalError; - - data_block = (PERF_DATA_BLOCK*) buffer; - - if (wmemcmp(data_block->Signature, L"PERF", 4) != 0) - goto internalError; - - if (data_size < data_block->HeaderLength + sizeof(*object_type)) - goto internalError; - - object_type = (PERF_OBJECT_TYPE*) (buffer + data_block->HeaderLength); - - if (object_type->NumInstances != PERF_NO_INSTANCES) - goto internalError; + *uptime = GetTickCount64() / 1000.0; + return 0; +} - counter_definition = (PERF_COUNTER_DEFINITION*) (buffer + - data_block->HeaderLength + object_type->HeaderLength); - for (i = 0; i < object_type->NumCounters; i++) { - if ((BYTE*) counter_definition + sizeof(*counter_definition) > - buffer + data_size) { - break; - } - if (counter_definition->CounterNameTitleIndex == 674 && - counter_definition->CounterSize == sizeof(uint64_t)) { - if (counter_definition->CounterOffset + sizeof(uint64_t) > data_size || - !(counter_definition->CounterType & PERF_OBJECT_TIMER)) { - goto internalError; - } else { - BYTE* address = (BYTE*) object_type + object_type->DefinitionLength + - counter_definition->CounterOffset; - uint64_t value = *((uint64_t*) address); - *uptime = floor((double) (object_type->PerfTime.QuadPart - value) / - (double) object_type->PerfFreq.QuadPart); - uv__free(malloced_buffer); - return 0; - } - } +unsigned int uv_available_parallelism(void) { + DWORD_PTR procmask; + DWORD_PTR sysmask; + int count; + int i; - counter_definition = (PERF_COUNTER_DEFINITION*) - ((BYTE*) counter_definition + counter_definition->ByteLength); - } + /* TODO(bnoordhuis) Use GetLogicalProcessorInformationEx() to support systems + * with > 64 CPUs? See https://github.com/libuv/libuv/pull/3458 + */ + count = 0; + if (GetProcessAffinityMask(GetCurrentProcess(), &procmask, &sysmask)) + for (i = 0; i < 8 * sizeof(procmask); i++) + count += 1 & (procmask >> i); - /* If we get here, the uptime value was not found. */ - uv__free(malloced_buffer); - *uptime = 0; - return UV_ENOSYS; + if (count > 0) + return count; - internalError: - uv__free(malloced_buffer); - *uptime = 0; - return UV_EIO; + return 1; } @@ -756,71 +651,6 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) { } -static int is_windows_version_or_greater(DWORD os_major, - DWORD os_minor, - WORD service_pack_major, - WORD service_pack_minor) { - OSVERSIONINFOEX osvi; - DWORDLONG condition_mask = 0; - int op = VER_GREATER_EQUAL; - - /* Initialize the OSVERSIONINFOEX structure. */ - ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - osvi.dwMajorVersion = os_major; - osvi.dwMinorVersion = os_minor; - osvi.wServicePackMajor = service_pack_major; - osvi.wServicePackMinor = service_pack_minor; - - /* Initialize the condition mask. */ - VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, op); - VER_SET_CONDITION(condition_mask, VER_MINORVERSION, op); - VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMAJOR, op); - VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMINOR, op); - - /* Perform the test. */ - return (int) VerifyVersionInfo( - &osvi, - VER_MAJORVERSION | VER_MINORVERSION | - VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR, - condition_mask); -} - - -static int address_prefix_match(int family, - struct sockaddr* address, - struct sockaddr* prefix_address, - int prefix_len) { - uint8_t* address_data; - uint8_t* prefix_address_data; - int i; - - assert(address->sa_family == family); - assert(prefix_address->sa_family == family); - - if (family == AF_INET6) { - address_data = (uint8_t*) &(((struct sockaddr_in6 *) address)->sin6_addr); - prefix_address_data = - (uint8_t*) &(((struct sockaddr_in6 *) prefix_address)->sin6_addr); - } else { - address_data = (uint8_t*) &(((struct sockaddr_in *) address)->sin_addr); - prefix_address_data = - (uint8_t*) &(((struct sockaddr_in *) prefix_address)->sin_addr); - } - - for (i = 0; i < prefix_len >> 3; i++) { - if (address_data[i] != prefix_address_data[i]) - return 0; - } - - if (prefix_len % 8) - return prefix_address_data[i] == - (address_data[i] & (0xff << (8 - prefix_len % 8))); - - return 1; -} - - int uv_interface_addresses(uv_interface_address_t** addresses_ptr, int* count_ptr) { IP_ADAPTER_ADDRESSES* win_address_buf; @@ -833,26 +663,13 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr, uv_interface_address_t* uv_address; int count; - - int is_vista_or_greater; ULONG flags; *addresses_ptr = NULL; *count_ptr = 0; - is_vista_or_greater = is_windows_version_or_greater(6, 0, 0, 0); - if (is_vista_or_greater) { - flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | - GAA_FLAG_SKIP_DNS_SERVER; - } else { - /* We need at least XP SP1. */ - if (!is_windows_version_or_greater(5, 1, 1, 0)) - return UV_ENOTSUP; - - flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | - GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_INCLUDE_PREFIX; - } - + flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | + GAA_FLAG_SKIP_DNS_SERVER; /* Fetch the size of the adapters reported by windows, and then get the list * itself. */ @@ -939,19 +756,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr, continue; /* Compute the size of the interface name. */ - name_size = WideCharToMultiByte(CP_UTF8, - 0, - adapter->FriendlyName, - -1, - NULL, - 0, - NULL, - FALSE); - if (name_size <= 0) { - uv__free(win_address_buf); - return uv_translate_sys_error(GetLastError()); - } - uv_address_buf_size += name_size; + name_size = uv_utf16_length_as_wtf8(adapter->FriendlyName, -1); + uv_address_buf_size += name_size + 1; /* Count the number of addresses associated with this interface, and * compute the size. */ @@ -981,30 +787,25 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr, adapter != NULL; adapter = adapter->Next) { IP_ADAPTER_UNICAST_ADDRESS* unicast_address; - int name_size; - size_t max_name_size; + size_t name_size; + int r; if (adapter->OperStatus != IfOperStatusUp || adapter->FirstUnicastAddress == NULL) continue; /* Convert the interface name to UTF8. */ - max_name_size = (char*) uv_address_buf + uv_address_buf_size - name_buf; - if (max_name_size > (size_t) INT_MAX) - max_name_size = INT_MAX; - name_size = WideCharToMultiByte(CP_UTF8, - 0, - adapter->FriendlyName, - -1, - name_buf, - (int) max_name_size, - NULL, - FALSE); - if (name_size <= 0) { + name_size = (char*) uv_address_buf + uv_address_buf_size - name_buf; + r = uv__copy_utf16_to_utf8(adapter->FriendlyName, + -1, + name_buf, + &name_size); + if (r) { uv__free(win_address_buf); uv__free(uv_address_buf); - return uv_translate_sys_error(GetLastError()); + return r; } + name_size += 1; /* Add NUL byte. */ /* Add an uv_interface_address_t element for every unicast address. */ for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS*) @@ -1016,37 +817,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr, sa = unicast_address->Address.lpSockaddr; - /* XP has no OnLinkPrefixLength field. */ - if (is_vista_or_greater) { - prefix_len = - ((IP_ADAPTER_UNICAST_ADDRESS_LH*) unicast_address)->OnLinkPrefixLength; - } else { - /* Prior to Windows Vista the FirstPrefix pointed to the list with - * single prefix for each IP address assigned to the adapter. - * Order of FirstPrefix does not match order of FirstUnicastAddress, - * so we need to find corresponding prefix. - */ - IP_ADAPTER_PREFIX* prefix; - prefix_len = 0; - - for (prefix = adapter->FirstPrefix; prefix; prefix = prefix->Next) { - /* We want the longest matching prefix. */ - if (prefix->Address.lpSockaddr->sa_family != sa->sa_family || - prefix->PrefixLength <= prefix_len) - continue; - - if (address_prefix_match(sa->sa_family, sa, - prefix->Address.lpSockaddr, prefix->PrefixLength)) { - prefix_len = prefix->PrefixLength; - } - } - - /* If there is no matching prefix information, return a single-host - * subnet mask (e.g. 255.255.255.255 for IPv4). - */ - if (!prefix_len) - prefix_len = (sa->sa_family == AF_INET6) ? 128 : 32; - } + prefix_len = + ((IP_ADAPTER_UNICAST_ADDRESS_LH*) unicast_address)->OnLinkPrefixLength; memset(uv_address, 0, sizeof *uv_address); @@ -1102,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; } @@ -1168,11 +984,16 @@ 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__getpwuid_r() */ - r = uv__getpwuid_r(&pwd); + /* USERPROFILE is not set, so call uv_os_get_passwd() */ + r = uv_os_get_passwd(&pwd); if (r != 0) { return r; @@ -1195,8 +1016,8 @@ int uv_os_homedir(char* buffer, size_t* size) { int uv_os_tmpdir(char* buffer, size_t* size) { + int r; wchar_t *path; - DWORD bufsize; size_t len; if (buffer == NULL || size == NULL || *size == 0) @@ -1207,13 +1028,19 @@ 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)); if (path == NULL) { return UV_ENOMEM; } - len = GetTempPathW(len, path); + len = GetTempPathW(len, path); if (len == 0) { uv__free(path); @@ -1228,45 +1055,9 @@ int uv_os_tmpdir(char* buffer, size_t* size) { path[len] = L'\0'; } - /* Check how much space we need */ - bufsize = WideCharToMultiByte(CP_UTF8, 0, path, -1, NULL, 0, NULL, NULL); - - if (bufsize == 0) { - uv__free(path); - return uv_translate_sys_error(GetLastError()); - } else if (bufsize > *size) { - uv__free(path); - *size = bufsize; - return UV_ENOBUFS; - } - - /* Convert to UTF-8 */ - bufsize = WideCharToMultiByte(CP_UTF8, - 0, - path, - -1, - buffer, - *size, - NULL, - NULL); + r = uv__copy_utf16_to_utf8(path, len, buffer, size); uv__free(path); - - if (bufsize == 0) - return uv_translate_sys_error(GetLastError()); - - *size = bufsize - 1; - return 0; -} - - -void uv_os_free_passwd(uv_passwd_t* pwd) { - if (pwd == NULL) - return; - - uv__free(pwd->username); - uv__free(pwd->homedir); - pwd->username = NULL; - pwd->homedir = NULL; + return r; } @@ -1277,96 +1068,72 @@ void uv_os_free_passwd(uv_passwd_t* pwd) { * If utf16 is null terminated, utf16len can be set to -1, otherwise it must * be specified. */ -int uv__convert_utf16_to_utf8(const WCHAR* utf16, int utf16len, char** utf8) { - DWORD bufsize; +int uv__convert_utf16_to_utf8(const WCHAR* utf16, size_t utf16len, char** utf8) { + size_t utf8_len = 0; if (utf16 == NULL) return UV_EINVAL; - /* Check how much space we need */ - bufsize = WideCharToMultiByte(CP_UTF8, - 0, - utf16, - utf16len, - NULL, - 0, - NULL, - NULL); - - if (bufsize == 0) - return uv_translate_sys_error(GetLastError()); - - /* Allocate the destination buffer adding an extra byte for the terminating - * NULL. If utf16len is not -1 WideCharToMultiByte will not add it, so - * we do it ourselves always, just in case. */ - *utf8 = uv__malloc(bufsize + 1); - - if (*utf8 == NULL) - return UV_ENOMEM; - - /* Convert to UTF-8 */ - bufsize = WideCharToMultiByte(CP_UTF8, - 0, - utf16, - utf16len, - *utf8, - bufsize, - NULL, - NULL); - - if (bufsize == 0) { - uv__free(*utf8); - *utf8 = NULL; - return uv_translate_sys_error(GetLastError()); - } - - (*utf8)[bufsize] = '\0'; - return 0; + *utf8 = NULL; + return uv_utf16_to_wtf8(utf16, utf16len, utf8, &utf8_len); } /* * Converts a UTF-8 string into a UTF-16 one. The resulting string is * null-terminated. - * - * If utf8 is null terminated, utf8len can be set to -1, otherwise it must - * be specified. */ -int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16) { +int uv__convert_utf8_to_utf16(const char* utf8, WCHAR** utf16) { int bufsize; if (utf8 == NULL) return UV_EINVAL; - /* Check how much space we need */ - bufsize = MultiByteToWideChar(CP_UTF8, 0, utf8, utf8len, NULL, 0); - - if (bufsize == 0) - return uv_translate_sys_error(GetLastError()); + /* Check how much space we need (including NUL). */ + bufsize = uv_wtf8_length_as_utf16(utf8); + if (bufsize < 0) + return UV__EINVAL; - /* Allocate the destination buffer adding an extra byte for the terminating - * NULL. If utf8len is not -1 MultiByteToWideChar will not add it, so - * we do it ourselves always, just in case. */ - *utf16 = uv__malloc(sizeof(WCHAR) * (bufsize + 1)); + /* Allocate the destination buffer. */ + *utf16 = uv__malloc(sizeof(WCHAR) * bufsize); if (*utf16 == NULL) return UV_ENOMEM; /* Convert to UTF-16 */ - bufsize = MultiByteToWideChar(CP_UTF8, 0, utf8, utf8len, *utf16, bufsize); - - if (bufsize == 0) { - uv__free(*utf16); - *utf16 = NULL; - return uv_translate_sys_error(GetLastError()); - } + uv_wtf8_to_utf16(utf8, *utf16, bufsize); - (*utf16)[bufsize] = L'\0'; return 0; } -int uv__getpwuid_r(uv_passwd_t* pwd) { +/* + * Converts a UTF-16 string into a UTF-8 one in an existing buffer. The + * resulting string is null-terminated. + * + * If utf16 is null terminated, utf16len can be set to -1, otherwise it must + * be specified. + */ +int uv__copy_utf16_to_utf8(const WCHAR* utf16buffer, size_t utf16len, char* utf8, size_t *size) { + int r; + + if (utf8 == NULL || size == NULL) + return UV_EINVAL; + + if (*size == 0) { + *size = uv_utf16_length_as_wtf8(utf16buffer, utf16len); + r = UV_ENOBUFS; + } else { + *size -= 1; /* Reserve space for NUL. */ + r = uv_utf16_to_wtf8(utf16buffer, utf16len, &utf8, size); + } + if (r == UV_ENOBUFS) + *size += 1; /* Add space for NUL. */ + return r; +} + + +static int uv__getpwuid_r(uv_passwd_t* pwd) { HANDLE token; wchar_t username[UNLEN + 1]; wchar_t *path; @@ -1444,6 +1211,16 @@ int uv_os_get_passwd(uv_passwd_t* pwd) { } +int uv_os_get_passwd2(uv_passwd_t* pwd, uv_uid_t uid) { + return UV_ENOTSUP; +} + + +int uv_os_get_group(uv_group_t* grp, uv_uid_t gid) { + return UV_ENOTSUP; +} + + int uv_os_environ(uv_env_item_t** envitems, int* count) { wchar_t* env; wchar_t* penv; @@ -1520,14 +1297,13 @@ int uv_os_getenv(const char* name, char* buffer, size_t* size) { wchar_t* var; DWORD varlen; wchar_t* name_w; - DWORD bufsize; size_t len; int r; if (name == NULL || buffer == NULL || size == NULL || *size == 0) return UV_EINVAL; - r = uv__convert_utf8_to_utf16(name, -1, &name_w); + r = uv__convert_utf8_to_utf16(name, &name_w); if (r != 0) return r; @@ -1539,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; @@ -1560,43 +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; - } - } - - /* Check how much space we need */ - bufsize = WideCharToMultiByte(CP_UTF8, 0, var, -1, NULL, 0, NULL, NULL); - - if (bufsize == 0) { - r = uv_translate_sys_error(GetLastError()); - goto fail; - } else if (bufsize > *size) { - *size = bufsize; - r = UV_ENOBUFS; - goto fail; - } - - /* Convert to UTF-8 */ - bufsize = WideCharToMultiByte(CP_UTF8, - 0, - var, - -1, - buffer, - *size, - NULL, - NULL); - - if (bufsize == 0) { - r = uv_translate_sys_error(GetLastError()); - goto fail; - } - - *size = bufsize - 1; - r = 0; + if (r == 0) + r = uv__copy_utf16_to_utf8(var, len, buffer, size); fail: @@ -1618,12 +1362,12 @@ int uv_os_setenv(const char* name, const char* value) { if (name == NULL || value == NULL) return UV_EINVAL; - r = uv__convert_utf8_to_utf16(name, -1, &name_w); + r = uv__convert_utf8_to_utf16(name, &name_w); if (r != 0) return r; - r = uv__convert_utf8_to_utf16(value, -1, &value_w); + r = uv__convert_utf8_to_utf16(value, &value_w); if (r != 0) { uv__free(name_w); @@ -1648,7 +1392,7 @@ int uv_os_unsetenv(const char* name) { if (name == NULL) return UV_EINVAL; - r = uv__convert_utf8_to_utf16(name, -1, &name_w); + r = uv__convert_utf8_to_utf16(name, &name_w); if (r != 0) return r; @@ -1665,9 +1409,6 @@ int uv_os_unsetenv(const char* name) { int uv_os_gethostname(char* buffer, size_t* size) { WCHAR buf[UV_MAXHOSTNAMESIZE]; - size_t len; - char* utf8_str; - int convert_result; if (buffer == NULL || size == NULL || *size == 0) return UV_EINVAL; @@ -1680,22 +1421,7 @@ int uv_os_gethostname(char* buffer, size_t* size) { if (pGetHostNameW(buf, UV_MAXHOSTNAMESIZE) != 0) return uv_translate_sys_error(WSAGetLastError()); - convert_result = uv__convert_utf16_to_utf8(buf, -1, &utf8_str); - - if (convert_result != 0) - return convert_result; - - len = strlen(utf8_str); - if (len >= *size) { - *size = len + 1; - uv__free(utf8_str); - return UV_ENOBUFS; - } - - memcpy(buffer, utf8_str, len + 1); - uv__free(utf8_str); - *size = len; - return 0; + return uv__copy_utf16_to_utf8(buf, -1, buffer, size); } @@ -1792,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 @@ -1801,7 +1569,7 @@ int uv_os_uname(uv_utsname_t* buffer) { HKEY registry_key; WCHAR product_name_w[256]; DWORD product_name_w_size; - int version_size; + size_t version_size; int processor_level; int r; @@ -1812,27 +1580,14 @@ 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; r = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, - KEY_QUERY_VALUE, + KEY_QUERY_VALUE | KEY_WOW64_64KEY, ®istry_key); if (r == ERROR_SUCCESS) { @@ -1847,37 +1602,45 @@ int uv_os_uname(uv_utsname_t* buffer) { RegCloseKey(registry_key); if (r == ERROR_SUCCESS) { - version_size = WideCharToMultiByte(CP_UTF8, - 0, - product_name_w, - -1, - buffer->version, - sizeof(buffer->version), - NULL, - NULL); - if (version_size == 0) { - r = uv_translate_sys_error(GetLastError()); - goto error; + /* Windows 11 shares dwMajorVersion with Windows 10 + * this workaround tries to disambiguate that by checking + * if the dwBuildNumber is from Windows 11 releases (>= 22000). + * + * This workaround replaces the ProductName key value + * from "Windows 10 *" to "Windows 11 *" */ + if (os_info.dwMajorVersion == 10 && + os_info.dwBuildNumber >= 22000 && + product_name_w_size >= ARRAY_SIZE(L"Windows 10")) { + /* If ProductName starts with "Windows 10" */ + if (wcsncmp(product_name_w, L"Windows 10", ARRAY_SIZE(L"Windows 10") - 1) == 0) { + /* Bump 10 to 11 */ + product_name_w[9] = '1'; + } } + + version_size = sizeof(buffer->version); + r = uv__copy_utf16_to_utf8(product_name_w, + -1, + buffer->version, + &version_size); + if (r) + goto error; } } /* Append service pack information to the version if present. */ if (os_info.szCSDVersion[0] != L'\0') { if (version_size > 0) - buffer->version[version_size - 1] = ' '; - - if (WideCharToMultiByte(CP_UTF8, - 0, - os_info.szCSDVersion, - -1, - buffer->version + version_size, - sizeof(buffer->version) - version_size, - NULL, - NULL) == 0) { - r = uv_translate_sys_error(GetLastError()); + buffer->version[version_size++] = ' '; + + version_size = sizeof(buffer->version) - version_size; + r = uv__copy_utf16_to_utf8(os_info.szCSDVersion, + -1, + buffer->version + + sizeof(buffer->version) - version_size, + &version_size); + if (r) goto error; - } } /* Populate the sysname field. */ diff --git a/src/win/winapi.c b/src/win/winapi.c index bf306cd83bd..315a0d49aff 100644 --- a/src/win/winapi.c +++ b/src/win/winapi.c @@ -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; -void uv_winapi_init(void) { +/* 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,31 +117,28 @@ 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 = LoadLibraryA("powrprof.dll"); + powrprof_module = LoadLibraryExA("powrprof.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32); if (powrprof_module != NULL) { pPowerRegisterSuspendResumeNotification = (sPowerRegisterSuspendResumeNotification) GetProcAddress(powrprof_module, "PowerRegisterSuspendResumeNotification"); } - user32_module = LoadLibraryA("user32.dll"); + user32_module = GetModuleHandleA("user32.dll"); if (user32_module != NULL) { pSetWinEventHook = (sSetWinEventHook) GetProcAddress(user32_module, "SetWinEventHook"); } - ws2_32_module = LoadLibraryA("ws2_32.dll"); + ws2_32_module = GetModuleHandleA("ws2_32.dll"); if (ws2_32_module != NULL) { pGetHostNameW = (uv_sGetHostNameW) GetProcAddress( 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"); + } } diff --git a/src/win/winapi.h b/src/win/winapi.h index d380bda42a3..47c34622ff7 100644 --- a/src/win/winapi.h +++ b/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; + FILE_ID_128 FileId128; + LARGE_INTEGER VolumeSerialNumber; +} 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) diff --git a/src/win/winsock.c b/src/win/winsock.c index 4cf6e6b042c..a68b095366a 100644 --- a/src/win/winsock.c +++ b/src/win/winsock.c @@ -38,7 +38,7 @@ struct sockaddr_in6 uv_addr_ip6_any_; /* * Retrieves the pointer to a winsock extension function. */ -static BOOL uv_get_extension_function(SOCKET socket, GUID guid, +static BOOL uv__get_extension_function(SOCKET socket, GUID guid, void **target) { int result; DWORD bytes; @@ -62,20 +62,20 @@ static BOOL uv_get_extension_function(SOCKET socket, GUID guid, } -BOOL uv_get_acceptex_function(SOCKET socket, LPFN_ACCEPTEX* target) { +BOOL uv__get_acceptex_function(SOCKET socket, LPFN_ACCEPTEX* target) { const GUID wsaid_acceptex = WSAID_ACCEPTEX; - return uv_get_extension_function(socket, wsaid_acceptex, (void**)target); + return uv__get_extension_function(socket, wsaid_acceptex, (void**)target); } -BOOL uv_get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target) { +BOOL uv__get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target) { const GUID wsaid_connectex = WSAID_CONNECTEX; - return uv_get_extension_function(socket, wsaid_connectex, (void**)target); + return uv__get_extension_function(socket, wsaid_connectex, (void**)target); } -void uv_winsock_init(void) { +void uv__winsock_init(void) { WSADATA wsa_data; int errorno; SOCKET dummy; @@ -134,7 +134,7 @@ void uv_winsock_init(void) { } -int uv_ntstatus_to_winsock_error(NTSTATUS status) { +int uv__ntstatus_to_winsock_error(NTSTATUS status) { switch (status) { case STATUS_SUCCESS: return ERROR_SUCCESS; @@ -267,7 +267,7 @@ int uv_ntstatus_to_winsock_error(NTSTATUS status) { * the user to use the default msafd driver, doesn't work when other LSPs are * stacked on top of it. */ -int WSAAPI uv_wsarecv_workaround(SOCKET socket, WSABUF* buffers, +int WSAAPI uv__wsarecv_workaround(SOCKET socket, WSABUF* buffers, DWORD buffer_count, DWORD* bytes, DWORD* flags, WSAOVERLAPPED *overlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine) { NTSTATUS status; @@ -346,7 +346,7 @@ int WSAAPI uv_wsarecv_workaround(SOCKET socket, WSABUF* buffers, break; default: - error = uv_ntstatus_to_winsock_error(status); + error = uv__ntstatus_to_winsock_error(status); break; } @@ -360,8 +360,8 @@ int WSAAPI uv_wsarecv_workaround(SOCKET socket, WSABUF* buffers, } -/* See description of uv_wsarecv_workaround. */ -int WSAAPI uv_wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers, +/* See description of uv__wsarecv_workaround. */ +int WSAAPI uv__wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers, DWORD buffer_count, DWORD* bytes, DWORD* flags, struct sockaddr* addr, int* addr_len, WSAOVERLAPPED *overlapped, LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine) { @@ -444,7 +444,7 @@ int WSAAPI uv_wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers, break; default: - error = uv_ntstatus_to_winsock_error(status); + error = uv__ntstatus_to_winsock_error(status); break; } @@ -458,7 +458,7 @@ int WSAAPI uv_wsarecvfrom_workaround(SOCKET socket, WSABUF* buffers, } -int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in, +int WSAAPI uv__msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in, AFD_POLL_INFO* info_out, OVERLAPPED* overlapped) { IO_STATUS_BLOCK iosb; IO_STATUS_BLOCK* iosb_ptr; @@ -531,7 +531,7 @@ int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in, break; default: - error = uv_ntstatus_to_winsock_error(status); + error = uv__ntstatus_to_winsock_error(status); break; } diff --git a/src/win/winsock.h b/src/win/winsock.h index 2af958870a7..bb3808a35c2 100644 --- a/src/win/winsock.h +++ b/src/win/winsock.h @@ -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); diff --git a/test/benchmark-async-pummel.c b/test/benchmark-async-pummel.c index 49660a6f575..68864c842e5 100644 --- a/test/benchmark-async-pummel.c +++ b/test/benchmark-async-pummel.c @@ -62,6 +62,7 @@ static void pummel(void* arg) { static int test_async_pummel(int nthreads) { + char fmtbuf[2][32]; uv_thread_t* tids; uv_async_t handle; uint64_t time; @@ -70,31 +71,31 @@ static int test_async_pummel(int nthreads) { tids = calloc(nthreads, sizeof(tids[0])); ASSERT_NOT_NULL(tids); - ASSERT(0 == uv_async_init(uv_default_loop(), &handle, async_cb)); + ASSERT_OK(uv_async_init(uv_default_loop(), &handle, async_cb)); ACCESS_ONCE(const char*, handle.data) = running; for (i = 0; i < nthreads; i++) - ASSERT(0 == uv_thread_create(tids + i, pummel, &handle)); + ASSERT_OK(uv_thread_create(tids + i, pummel, &handle)); time = uv_hrtime(); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); time = uv_hrtime() - time; done = 1; for (i = 0; i < nthreads; i++) - ASSERT(0 == uv_thread_join(tids + i)); + ASSERT_OK(uv_thread_join(tids + i)); printf("async_pummel_%d: %s callbacks in %.2f seconds (%s/sec)\n", nthreads, - fmt(callbacks), + fmt(&fmtbuf[0], callbacks), time / 1e9, - fmt(callbacks / (time / 1e9))); + fmt(&fmtbuf[1], callbacks / (time / 1e9))); free(tids); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/benchmark-async.c b/test/benchmark-async.c index 5167ecbd758..5544c46b3b2 100644 --- a/test/benchmark-async.c +++ b/test/benchmark-async.c @@ -43,7 +43,7 @@ struct ctx { static void worker_async_cb(uv_async_t* handle) { struct ctx* ctx = container_of(handle, struct ctx, worker_async); - ASSERT(0 == uv_async_send(&ctx->main_async)); + ASSERT_OK(uv_async_send(&ctx->main_async)); ctx->worker_sent++; ctx->worker_seen++; @@ -55,7 +55,7 @@ static void worker_async_cb(uv_async_t* handle) { static void main_async_cb(uv_async_t* handle) { struct ctx* ctx = container_of(handle, struct ctx, main_async); - ASSERT(0 == uv_async_send(&ctx->worker_async)); + ASSERT_OK(uv_async_send(&ctx->worker_async)); ctx->main_sent++; ctx->main_seen++; @@ -66,13 +66,14 @@ static void main_async_cb(uv_async_t* handle) { static void worker(void* arg) { struct ctx* ctx = arg; - ASSERT(0 == uv_async_send(&ctx->main_async)); - ASSERT(0 == uv_run(&ctx->loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_async_send(&ctx->main_async)); + ASSERT_OK(uv_run(&ctx->loop, UV_RUN_DEFAULT)); uv_loop_close(&ctx->loop); } static int test_async(int nthreads) { + char fmtbuf[32]; struct ctx* threads; struct ctx* ctx; uint64_t time; @@ -84,39 +85,39 @@ static int test_async(int nthreads) { for (i = 0; i < nthreads; i++) { ctx = threads + i; ctx->nthreads = nthreads; - ASSERT(0 == uv_loop_init(&ctx->loop)); - ASSERT(0 == uv_async_init(&ctx->loop, &ctx->worker_async, worker_async_cb)); - ASSERT(0 == uv_async_init(uv_default_loop(), - &ctx->main_async, - main_async_cb)); - ASSERT(0 == uv_thread_create(&ctx->thread, worker, ctx)); + ASSERT_OK(uv_loop_init(&ctx->loop)); + ASSERT_OK(uv_async_init(&ctx->loop, &ctx->worker_async, worker_async_cb)); + ASSERT_OK(uv_async_init(uv_default_loop(), + &ctx->main_async, + main_async_cb)); + ASSERT_OK(uv_thread_create(&ctx->thread, worker, ctx)); } time = uv_hrtime(); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); for (i = 0; i < nthreads; i++) - ASSERT(0 == uv_thread_join(&threads[i].thread)); + ASSERT_OK(uv_thread_join(&threads[i].thread)); time = uv_hrtime() - time; for (i = 0; i < nthreads; i++) { ctx = threads + i; - ASSERT(ctx->worker_sent == NUM_PINGS); - ASSERT(ctx->worker_seen == NUM_PINGS); - ASSERT(ctx->main_sent == (unsigned int) NUM_PINGS); - ASSERT(ctx->main_seen == (unsigned int) NUM_PINGS); + ASSERT_EQ(ctx->worker_sent, NUM_PINGS); + ASSERT_EQ(ctx->worker_seen, NUM_PINGS); + ASSERT_EQ(ctx->main_sent, (unsigned int) NUM_PINGS); + ASSERT_EQ(ctx->main_seen, (unsigned int) NUM_PINGS); } printf("async%d: %.2f sec (%s/sec)\n", nthreads, time / 1e9, - fmt(NUM_PINGS / (time / 1e9))); + fmt(&fmtbuf, NUM_PINGS / (time / 1e9))); free(threads); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/benchmark-fs-stat.c b/test/benchmark-fs-stat.c index 32d2589586c..c4106224109 100644 --- a/test/benchmark-fs-stat.c +++ b/test/benchmark-fs-stat.c @@ -60,6 +60,7 @@ static void warmup(const char* path) { static void sync_bench(const char* path) { + char fmtbuf[2][32]; uint64_t before; uint64_t after; uv_fs_t req; @@ -74,9 +75,9 @@ static void sync_bench(const char* path) { after = uv_hrtime(); printf("%s stats (sync): %.2fs (%s/s)\n", - fmt(1.0 * NUM_SYNC_REQS), + fmt(&fmtbuf[0], 1.0 * NUM_SYNC_REQS), (after - before) / 1e9, - fmt((1.0 * NUM_SYNC_REQS) / ((after - before) / 1e9))); + fmt(&fmtbuf[1], (1.0 * NUM_SYNC_REQS) / ((after - before) / 1e9))); fflush(stdout); } @@ -93,6 +94,7 @@ static void stat_cb(uv_fs_t* fs_req) { static void async_bench(const char* path) { struct async_req reqs[MAX_CONCURRENT_REQS]; struct async_req* req; + char fmtbuf[2][32]; uint64_t before; uint64_t after; int count; @@ -112,10 +114,10 @@ static void async_bench(const char* path) { after = uv_hrtime(); printf("%s stats (%d concurrent): %.2fs (%s/s)\n", - fmt(1.0 * NUM_ASYNC_REQS), + fmt(&fmtbuf[0], 1.0 * NUM_ASYNC_REQS), i, (after - before) / 1e9, - fmt((1.0 * NUM_ASYNC_REQS) / ((after - before) / 1e9))); + fmt(&fmtbuf[1], (1.0 * NUM_ASYNC_REQS) / ((after - before) / 1e9))); fflush(stdout); } } @@ -131,6 +133,6 @@ BENCHMARK_IMPL(fs_stat) { warmup(path); sync_bench(path); async_bench(path); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/benchmark-getaddrinfo.c b/test/benchmark-getaddrinfo.c index 1dbc23ddba0..cb4d2bc935a 100644 --- a/test/benchmark-getaddrinfo.c +++ b/test/benchmark-getaddrinfo.c @@ -43,7 +43,7 @@ static void getaddrinfo_initiate(uv_getaddrinfo_t* handle); static void getaddrinfo_cb(uv_getaddrinfo_t* handle, int status, struct addrinfo* res) { - ASSERT(status == 0); + ASSERT_OK(status); calls_completed++; if (calls_initiated < TOTAL_CALLS) { getaddrinfo_initiate(handle); @@ -59,7 +59,7 @@ static void getaddrinfo_initiate(uv_getaddrinfo_t* handle) { calls_initiated++; r = uv_getaddrinfo(loop, handle, &getaddrinfo_cb, name, NULL, NULL); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -80,13 +80,13 @@ BENCHMARK_IMPL(getaddrinfo) { uv_update_time(loop); end_time = uv_now(loop); - ASSERT(calls_initiated == TOTAL_CALLS); - ASSERT(calls_completed == TOTAL_CALLS); + ASSERT_EQ(calls_initiated, TOTAL_CALLS); + ASSERT_EQ(calls_completed, TOTAL_CALLS); fprintf(stderr, "getaddrinfo: %.0f req/s\n", (double) calls_completed / (double) (end_time - start_time) * 1000.0); fflush(stderr); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/benchmark-list.h b/test/benchmark-list.h index 71e4eab9a57..901e5ffa79f 100644 --- a/test/benchmark-list.h +++ b/test/benchmark-list.h @@ -22,8 +22,11 @@ BENCHMARK_DECLARE (sizes) BENCHMARK_DECLARE (loop_count) BENCHMARK_DECLARE (loop_count_timed) +BENCHMARK_DECLARE (loop_alive) BENCHMARK_DECLARE (ping_pongs) -BENCHMARK_DECLARE (ping_udp) +BENCHMARK_DECLARE (ping_udp1) +BENCHMARK_DECLARE (ping_udp10) +BENCHMARK_DECLARE (ping_udp100) BENCHMARK_DECLARE (tcp_write_batch) BENCHMARK_DECLARE (tcp4_pound_100) BENCHMARK_DECLARE (tcp4_pound_1000) @@ -72,6 +75,7 @@ BENCHMARK_DECLARE (async_pummel_1) BENCHMARK_DECLARE (async_pummel_2) BENCHMARK_DECLARE (async_pummel_4) BENCHMARK_DECLARE (async_pummel_8) +BENCHMARK_DECLARE (queue_work) BENCHMARK_DECLARE (spawn) BENCHMARK_DECLARE (thread_create) BENCHMARK_DECLARE (million_async) @@ -86,10 +90,15 @@ TASK_LIST_START BENCHMARK_ENTRY (sizes) BENCHMARK_ENTRY (loop_count) BENCHMARK_ENTRY (loop_count_timed) + BENCHMARK_ENTRY (loop_alive) BENCHMARK_ENTRY (ping_pongs) BENCHMARK_HELPER (ping_pongs, tcp4_echo_server) + BENCHMARK_ENTRY (ping_udp1) + BENCHMARK_ENTRY (ping_udp10) + BENCHMARK_ENTRY (ping_udp100) + BENCHMARK_ENTRY (tcp_write_batch) BENCHMARK_HELPER (tcp_write_batch, tcp4_blackhole_server) @@ -155,6 +164,7 @@ TASK_LIST_START BENCHMARK_ENTRY (async_pummel_2) BENCHMARK_ENTRY (async_pummel_4) BENCHMARK_ENTRY (async_pummel_8) + BENCHMARK_ENTRY (queue_work) BENCHMARK_ENTRY (spawn) BENCHMARK_ENTRY (thread_create) diff --git a/test/benchmark-loop-count.c b/test/benchmark-loop-count.c index 970a94c2fec..0db4aa567a9 100644 --- a/test/benchmark-loop-count.c +++ b/test/benchmark-loop-count.c @@ -26,6 +26,7 @@ #include #define NUM_TICKS (2 * 1000 * 1000) +#define NUM_TICKS2 (2 * 1000 * 1000 * 100) static unsigned long ticks; static uv_idle_t idle_handle; @@ -37,6 +38,19 @@ static void idle_cb(uv_idle_t* handle) { uv_idle_stop(handle); } +static void idle_alive_cb(uv_idle_t* handle) { + int ticks = 0; + + while (++ticks < NUM_TICKS2) { + int r = uv_loop_alive(handle->loop); + if (r == 0) + abort(); + } + + *(int*)handle->data = ticks; + uv_idle_stop(handle); +} + static void idle2_cb(uv_idle_t* handle) { ticks++; @@ -60,7 +74,7 @@ BENCHMARK_IMPL(loop_count) { uv_run(loop, UV_RUN_DEFAULT); ns = uv_hrtime() - ns; - ASSERT(ticks == NUM_TICKS); + ASSERT_UINT64_EQ(ticks, NUM_TICKS); fprintf(stderr, "loop_count: %d ticks in %.2fs (%.0f/s)\n", NUM_TICKS, @@ -68,7 +82,7 @@ BENCHMARK_IMPL(loop_count) { NUM_TICKS / (ns / 1e9)); fflush(stderr); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -87,6 +101,34 @@ BENCHMARK_IMPL(loop_count_timed) { fprintf(stderr, "loop_count: %lu ticks (%.0f ticks/s)\n", ticks, ticks / 5.0); fflush(stderr); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); + return 0; +} + +/* Measure the performance of running uv_loop_alive(). Adding this so we can get + * some sort of metric for the impact of switching active_reqs.count to use + * atomics. No other code sits in a hot path. */ +BENCHMARK_IMPL(loop_alive) { + uv_loop_t* loop = uv_default_loop(); + int ticks = 0; + uint64_t ns; + + uv_idle_init(loop, &idle_handle); + idle_handle.data = &ticks; + uv_idle_start(&idle_handle, idle_alive_cb); + + ns = uv_hrtime(); + uv_run(loop, UV_RUN_DEFAULT); + ns = uv_hrtime() - ns; + + ASSERT_EQ(ticks, NUM_TICKS2); + + fprintf(stderr, "loop_alive: %d ticks in %.2fs (%.0f/s)\n", + NUM_TICKS2, + ns / 1e9, + NUM_TICKS2 / (ns / 1e9)); + fflush(stderr); + + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/benchmark-million-async.c b/test/benchmark-million-async.c index 937a12f81e6..13b52d488d0 100644 --- a/test/benchmark-million-async.c +++ b/test/benchmark-million-async.c @@ -60,7 +60,7 @@ static void timer_cb(uv_timer_t* handle) { unsigned i; done = 1; - ASSERT(0 == uv_thread_join(&thread_id)); + ASSERT_OK(uv_thread_join(&thread_id)); for (i = 0; i < ARRAY_SIZE(container->async_handles); i++) { uv_async_t* handle = container->async_handles + i; @@ -76,6 +76,7 @@ static void timer_cb(uv_timer_t* handle) { BENCHMARK_IMPL(million_async) { + char fmtbuf[3][32]; uv_timer_t timer_handle; uv_async_t* handle; uv_loop_t* loop; @@ -92,21 +93,21 @@ BENCHMARK_IMPL(million_async) { for (i = 0; i < ARRAY_SIZE(container->async_handles); i++) { handle = container->async_handles + i; - ASSERT(0 == uv_async_init(loop, handle, async_cb)); + ASSERT_OK(uv_async_init(loop, handle, async_cb)); handle->data = NULL; } - ASSERT(0 == uv_timer_init(loop, &timer_handle)); - ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, timeout, 0)); - ASSERT(0 == uv_thread_create(&thread_id, thread_cb, NULL)); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_timer_init(loop, &timer_handle)); + ASSERT_OK(uv_timer_start(&timer_handle, timer_cb, timeout, 0)); + ASSERT_OK(uv_thread_create(&thread_id, thread_cb, NULL)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); printf("%s async events in %.1f seconds (%s/s, %s unique handles seen)\n", - fmt(container->async_events), + fmt(&fmtbuf[0], container->async_events), timeout / 1000., - fmt(container->async_events / (timeout / 1000.)), - fmt(container->handles_seen)); + fmt(&fmtbuf[1], container->async_events / (timeout / 1000.)), + fmt(&fmtbuf[2], container->handles_seen)); free(container); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/benchmark-million-timers.c b/test/benchmark-million-timers.c index ef25c2052d6..fd999c46976 100644 --- a/test/benchmark-million-timers.c +++ b/test/benchmark-million-timers.c @@ -57,22 +57,22 @@ BENCHMARK_IMPL(million_timers) { before_all = uv_hrtime(); for (i = 0; i < NUM_TIMERS; i++) { if (i % 1000 == 0) timeout++; - ASSERT(0 == uv_timer_init(loop, timers + i)); - ASSERT(0 == uv_timer_start(timers + i, timer_cb, timeout, 0)); + ASSERT_OK(uv_timer_init(loop, timers + i)); + ASSERT_OK(uv_timer_start(timers + i, timer_cb, timeout, 0)); } before_run = uv_hrtime(); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); after_run = uv_hrtime(); for (i = 0; i < NUM_TIMERS; i++) uv_close((uv_handle_t*) (timers + i), close_cb); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); after_all = uv_hrtime(); - ASSERT(timer_cb_called == NUM_TIMERS); - ASSERT(close_cb_called == NUM_TIMERS); + ASSERT_EQ(timer_cb_called, NUM_TIMERS); + ASSERT_EQ(close_cb_called, NUM_TIMERS); free(timers); fprintf(stderr, "%.2f seconds total\n", (after_all - before_all) / 1e9); @@ -81,6 +81,6 @@ BENCHMARK_IMPL(million_timers) { fprintf(stderr, "%.2f seconds cleanup\n", (after_all - after_run) / 1e9); fflush(stderr); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/benchmark-multi-accept.c b/test/benchmark-multi-accept.c index 86b7da5acd1..1d19e330b2b 100644 --- a/test/benchmark-multi-accept.c +++ b/test/benchmark-multi-accept.c @@ -117,19 +117,19 @@ static void ipc_connection_cb(uv_stream_t* ipc_pipe, int status) { ASSERT_NOT_NULL(pc); if (ipc_pipe->type == UV_TCP) - ASSERT(0 == uv_tcp_init(loop, (uv_tcp_t*) &pc->peer_handle)); + ASSERT_OK(uv_tcp_init(loop, (uv_tcp_t*) &pc->peer_handle)); else if (ipc_pipe->type == UV_NAMED_PIPE) - ASSERT(0 == uv_pipe_init(loop, (uv_pipe_t*) &pc->peer_handle, 1)); + ASSERT_OK(uv_pipe_init(loop, (uv_pipe_t*) &pc->peer_handle, 1)); else ASSERT(0); - ASSERT(0 == uv_accept(ipc_pipe, (uv_stream_t*) &pc->peer_handle)); - ASSERT(0 == uv_write2(&pc->write_req, - (uv_stream_t*) &pc->peer_handle, - &buf, - 1, - (uv_stream_t*) &sc->server_handle, - ipc_write_cb)); + ASSERT_OK(uv_accept(ipc_pipe, (uv_stream_t*) &pc->peer_handle)); + ASSERT_OK(uv_write2(&pc->write_req, + (uv_stream_t*) &pc->peer_handle, + &buf, + 1, + (uv_stream_t*) &sc->server_handle, + ipc_write_cb)); if (--sc->num_connects == 0) uv_close((uv_handle_t*) ipc_pipe, NULL); @@ -153,10 +153,10 @@ static void ipc_close_cb(uv_handle_t* handle) { static void ipc_connect_cb(uv_connect_t* req, int status) { struct ipc_client_ctx* ctx; ctx = container_of(req, struct ipc_client_ctx, connect_req); - ASSERT(0 == status); - ASSERT(0 == uv_read_start((uv_stream_t*) &ctx->ipc_pipe, - ipc_alloc_cb, - ipc_read_cb)); + ASSERT_OK(status); + ASSERT_OK(uv_read_start((uv_stream_t*) &ctx->ipc_pipe, + ipc_alloc_cb, + ipc_read_cb)); } @@ -182,16 +182,16 @@ static void ipc_read_cb(uv_stream_t* handle, ctx = container_of(ipc_pipe, struct ipc_client_ctx, ipc_pipe); loop = ipc_pipe->loop; - ASSERT(1 == uv_pipe_pending_count(ipc_pipe)); + ASSERT_EQ(1, uv_pipe_pending_count(ipc_pipe)); type = uv_pipe_pending_type(ipc_pipe); if (type == UV_TCP) - ASSERT(0 == uv_tcp_init(loop, (uv_tcp_t*) ctx->server_handle)); + ASSERT_OK(uv_tcp_init(loop, (uv_tcp_t*) ctx->server_handle)); else if (type == UV_NAMED_PIPE) - ASSERT(0 == uv_pipe_init(loop, (uv_pipe_t*) ctx->server_handle, 0)); + ASSERT_OK(uv_pipe_init(loop, (uv_pipe_t*) ctx->server_handle, 0)); else ASSERT(0); - ASSERT(0 == uv_accept(handle, ctx->server_handle)); + ASSERT_OK(uv_accept(handle, ctx->server_handle)); uv_close((uv_handle_t*) &ctx->ipc_pipe, NULL); } @@ -211,10 +211,10 @@ static void send_listen_handles(uv_handle_type type, ctx.num_connects = num_servers; if (type == UV_TCP) { - ASSERT(0 == uv_tcp_init(loop, (uv_tcp_t*) &ctx.server_handle)); - ASSERT(0 == uv_tcp_bind((uv_tcp_t*) &ctx.server_handle, - (const struct sockaddr*) &listen_addr, - 0)); + ASSERT_OK(uv_tcp_init(loop, (uv_tcp_t*) &ctx.server_handle)); + ASSERT_OK(uv_tcp_bind((uv_tcp_t*) &ctx.server_handle, + (const struct sockaddr*) &listen_addr, + 0)); } else ASSERT(0); @@ -223,16 +223,16 @@ static void send_listen_handles(uv_handle_type type, * If we accept a connection then the connected pipe must be initialized * with ipc=1. */ - ASSERT(0 == uv_pipe_init(loop, &ctx.ipc_pipe, 0)); - ASSERT(0 == uv_pipe_bind(&ctx.ipc_pipe, IPC_PIPE_NAME)); - ASSERT(0 == uv_listen((uv_stream_t*) &ctx.ipc_pipe, 128, ipc_connection_cb)); + ASSERT_OK(uv_pipe_init(loop, &ctx.ipc_pipe, 0)); + ASSERT_OK(uv_pipe_bind(&ctx.ipc_pipe, IPC_PIPE_NAME)); + ASSERT_OK(uv_listen((uv_stream_t*) &ctx.ipc_pipe, 128, ipc_connection_cb)); for (i = 0; i < num_servers; i++) uv_sem_post(&servers[i].semaphore); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); uv_close((uv_handle_t*) &ctx.server_handle, NULL); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); for (i = 0; i < num_servers; i++) uv_sem_wait(&servers[i].semaphore); @@ -245,12 +245,12 @@ static void get_listen_handle(uv_loop_t* loop, uv_stream_t* server_handle) { ctx.server_handle = server_handle; ctx.server_handle->data = "server handle"; - ASSERT(0 == uv_pipe_init(loop, &ctx.ipc_pipe, 1)); + ASSERT_OK(uv_pipe_init(loop, &ctx.ipc_pipe, 1)); uv_pipe_connect(&ctx.connect_req, &ctx.ipc_pipe, IPC_PIPE_NAME, ipc_connect_cb); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); } @@ -259,9 +259,9 @@ static void server_cb(void *arg) { uv_loop_t loop; ctx = arg; - ASSERT(0 == uv_loop_init(&loop)); + ASSERT_OK(uv_loop_init(&loop)); - ASSERT(0 == uv_async_init(&loop, &ctx->async_handle, sv_async_cb)); + ASSERT_OK(uv_async_init(&loop, &ctx->async_handle, sv_async_cb)); uv_unref((uv_handle_t*) &ctx->async_handle); /* Wait until the main thread is ready. */ @@ -270,10 +270,10 @@ static void server_cb(void *arg) { uv_sem_post(&ctx->semaphore); /* Now start the actual benchmark. */ - ASSERT(0 == uv_listen((uv_stream_t*) &ctx->server_handle, - 128, - sv_connection_cb)); - ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_listen((uv_stream_t*) &ctx->server_handle, + 128, + sv_connection_cb)); + ASSERT_OK(uv_run(&loop, UV_RUN_DEFAULT)); uv_loop_close(&loop); } @@ -292,20 +292,20 @@ static void sv_connection_cb(uv_stream_t* server_handle, int status) { struct server_ctx* ctx; ctx = container_of(server_handle, struct server_ctx, server_handle); - ASSERT(status == 0); + ASSERT_OK(status); storage = malloc(sizeof(*storage)); ASSERT_NOT_NULL(storage); if (server_handle->type == UV_TCP) - ASSERT(0 == uv_tcp_init(server_handle->loop, (uv_tcp_t*) storage)); + ASSERT_OK(uv_tcp_init(server_handle->loop, (uv_tcp_t*) storage)); else if (server_handle->type == UV_NAMED_PIPE) - ASSERT(0 == uv_pipe_init(server_handle->loop, (uv_pipe_t*) storage, 0)); + ASSERT_OK(uv_pipe_init(server_handle->loop, (uv_pipe_t*) storage, 0)); else ASSERT(0); - ASSERT(0 == uv_accept(server_handle, (uv_stream_t*) storage)); - ASSERT(0 == uv_read_start((uv_stream_t*) storage, sv_alloc_cb, sv_read_cb)); + ASSERT_OK(uv_accept(server_handle, (uv_stream_t*) storage)); + ASSERT_OK(uv_read_start((uv_stream_t*) storage, sv_alloc_cb, sv_read_cb)); ctx->num_connects++; } @@ -322,7 +322,7 @@ static void sv_alloc_cb(uv_handle_t* handle, static void sv_read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { - ASSERT(nread == UV_EOF); + ASSERT_EQ(nread, UV_EOF); uv_close((uv_handle_t*) handle, (uv_close_cb) free); } @@ -330,7 +330,7 @@ static void sv_read_cb(uv_stream_t* handle, static void cl_connect_cb(uv_connect_t* req, int status) { struct client_ctx* ctx = container_of(req, struct client_ctx, connect_req); uv_idle_start(&ctx->idle_handle, cl_idle_cb); - ASSERT(0 == status); + ASSERT_OK(status); } @@ -351,11 +351,11 @@ static void cl_close_cb(uv_handle_t* handle) { return; } - ASSERT(0 == uv_tcp_init(handle->loop, (uv_tcp_t*) &ctx->client_handle)); - ASSERT(0 == uv_tcp_connect(&ctx->connect_req, - (uv_tcp_t*) &ctx->client_handle, - (const struct sockaddr*) &listen_addr, - cl_connect_cb)); + ASSERT_OK(uv_tcp_init(handle->loop, (uv_tcp_t*) &ctx->client_handle)); + ASSERT_OK(uv_tcp_connect(&ctx->connect_req, + (uv_tcp_t*) &ctx->client_handle, + (const struct sockaddr*) &listen_addr, + cl_connect_cb)); } @@ -367,7 +367,7 @@ static int test_tcp(unsigned int num_servers, unsigned int num_clients) { unsigned int i; double time; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &listen_addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &listen_addr)); loop = uv_default_loop(); servers = calloc(num_servers, sizeof(servers[0])); @@ -381,8 +381,8 @@ static int test_tcp(unsigned int num_servers, unsigned int num_clients) { */ for (i = 0; i < num_servers; i++) { struct server_ctx* ctx = servers + i; - ASSERT(0 == uv_sem_init(&ctx->semaphore, 0)); - ASSERT(0 == uv_thread_create(&ctx->thread_id, server_cb, ctx)); + ASSERT_OK(uv_sem_init(&ctx->semaphore, 0)); + ASSERT_OK(uv_thread_create(&ctx->thread_id, server_cb, ctx)); } send_listen_handles(UV_TCP, num_servers, servers); @@ -392,17 +392,17 @@ static int test_tcp(unsigned int num_servers, unsigned int num_clients) { ctx->num_connects = NUM_CONNECTS / num_clients; handle = (uv_tcp_t*) &ctx->client_handle; handle->data = "client handle"; - ASSERT(0 == uv_tcp_init(loop, handle)); - ASSERT(0 == uv_tcp_connect(&ctx->connect_req, - handle, - (const struct sockaddr*) &listen_addr, - cl_connect_cb)); - ASSERT(0 == uv_idle_init(loop, &ctx->idle_handle)); + ASSERT_OK(uv_tcp_init(loop, handle)); + ASSERT_OK(uv_tcp_connect(&ctx->connect_req, + handle, + (const struct sockaddr*) &listen_addr, + cl_connect_cb)); + ASSERT_OK(uv_idle_init(loop, &ctx->idle_handle)); } { uint64_t t = uv_hrtime(); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); t = uv_hrtime() - t; time = t / 1e9; } @@ -410,7 +410,7 @@ static int test_tcp(unsigned int num_servers, unsigned int num_clients) { for (i = 0; i < num_servers; i++) { struct server_ctx* ctx = servers + i; uv_async_send(&ctx->async_handle); - ASSERT(0 == uv_thread_join(&ctx->thread_id)); + ASSERT_OK(uv_thread_join(&ctx->thread_id)); uv_sem_destroy(&ctx->semaphore); } @@ -431,7 +431,7 @@ static int test_tcp(unsigned int num_servers, unsigned int num_clients) { free(clients); free(servers); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/benchmark-ping-pongs.c b/test/benchmark-ping-pongs.c index 646a7df9447..fd5f40b9103 100644 --- a/test/benchmark-ping-pongs.c +++ b/test/benchmark-ping-pongs.c @@ -90,7 +90,7 @@ static void pinger_close_cb(uv_handle_t* handle) { static void pinger_write_cb(uv_write_t* req, int status) { - ASSERT(status == 0); + ASSERT_OK(status); free(req); } @@ -110,14 +110,14 @@ static void pinger_write_ping(pinger_t* pinger) { static void pinger_shutdown_cb(uv_shutdown_t* req, int status) { - ASSERT(status == 0); + ASSERT_OK(status); pinger_shutdown_cb_called++; /* * The close callback has not been triggered yet. We must wait for EOF * until we close the connection. */ - ASSERT(completed_pingers == 0); + ASSERT_OK(completed_pingers); } @@ -130,13 +130,13 @@ static void pinger_read_cb(uv_stream_t* tcp, pinger = (pinger_t*)tcp->data; if (nread < 0) { - ASSERT(nread == UV_EOF); + ASSERT_EQ(nread, UV_EOF); if (buf->base) { buf_free(buf); } - ASSERT(pinger_shutdown_cb_called == 1); + ASSERT_EQ(1, pinger_shutdown_cb_called); uv_close((uv_handle_t*)tcp, pinger_close_cb); return; @@ -144,7 +144,7 @@ static void pinger_read_cb(uv_stream_t* tcp, /* Now we count the pings */ for (i = 0; i < nread; i++) { - ASSERT(buf->base[i] == PING[pinger->state]); + ASSERT_EQ(buf->base[i], PING[pinger->state]); pinger->state = (pinger->state + 1) % (sizeof(PING) - 1); if (pinger->state == 0) { pinger->pongs++; @@ -166,7 +166,7 @@ static void pinger_read_cb(uv_stream_t* tcp, static void pinger_connect_cb(uv_connect_t* req, int status) { pinger_t *pinger = (pinger_t*)req->handle->data; - ASSERT(status == 0); + ASSERT_OK(status); pinger_write_ping(pinger); @@ -182,8 +182,8 @@ static void pinger_new(void) { pinger_t *pinger; int r; - ASSERT(0 == uv_ip4_addr("0.0.0.0", 0, &client_addr)); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", 0, &client_addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); pinger = malloc(sizeof(*pinger)); pinger->state = 0; pinger->pongs = 0; @@ -194,9 +194,9 @@ static void pinger_new(void) { pinger->tcp.data = pinger; - ASSERT(0 == uv_tcp_bind(&pinger->tcp, - (const struct sockaddr*) &client_addr, - 0)); + ASSERT_OK(uv_tcp_bind(&pinger->tcp, + (const struct sockaddr*) &client_addr, + 0)); r = uv_tcp_connect(&pinger->connect_req, &pinger->tcp, @@ -214,8 +214,8 @@ BENCHMARK_IMPL(ping_pongs) { pinger_new(); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(completed_pingers == 1); + ASSERT_EQ(1, completed_pingers); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/benchmark-ping-udp.c b/test/benchmark-ping-udp.c index a29502a786f..2d2fe9c6339 100644 --- a/test/benchmark-ping-udp.c +++ b/test/benchmark-ping-udp.c @@ -94,9 +94,12 @@ static void pinger_read_cb(uv_udp_t* udp, pinger_t* pinger; pinger = (pinger_t*)udp->data; + /* No data here means something went wrong */ + ASSERT_GT(nread, 0); + /* Now we count the pings */ for (i = 0; i < nread; i++) { - ASSERT(buf->base[i] == PING[pinger->state]); + ASSERT_EQ(buf->base[i], PING[pinger->state]); pinger->state = (pinger->state + 1) % (sizeof(PING) - 1); if (pinger->state == 0) { pinger->pongs++; @@ -108,20 +111,23 @@ static void pinger_read_cb(uv_udp_t* udp, } } - buf_free(buf); + if (buf && !(flags & UV_UDP_MMSG_CHUNK)) + buf_free(buf); } static void udp_pinger_new(void) { pinger_t* pinger = malloc(sizeof(*pinger)); int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &pinger->server_addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &pinger->server_addr)); pinger->state = 0; pinger->pongs = 0; /* Try to do NUM_PINGS ping-pongs (connection-less). */ r = uv_udp_init(loop, &pinger->udp); - ASSERT(r == 0); + ASSERT_OK(r); + r = uv_udp_bind(&pinger->udp, (const struct sockaddr*) &pinger->server_addr, 0); + ASSERT_OK(r); pinger->udp.data = pinger; @@ -142,12 +148,12 @@ static int ping_udp(unsigned pingers) { udp_pinger_new(); } uv_run(loop, UV_RUN_DEFAULT); - ASSERT(completed_pingers >= 1); + ASSERT_GE(completed_pingers, 1); fprintf(stderr, "ping_pongs: %d pingers, ~ %lu roundtrips/s\n", completed_pingers, completed_pings / (TIME/1000)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/benchmark-pound.c b/test/benchmark-pound.c index 830bc554b34..83ce522779a 100644 --- a/test/benchmark-pound.c +++ b/test/benchmark-pound.c @@ -115,7 +115,7 @@ static void connect_cb(uv_connect_t* req, int status) { } ASSERT_NOT_NULL(req); - ASSERT(status == 0); + ASSERT_OK(status); conn = (conn_rec*)req->data; ASSERT_NOT_NULL(conn); @@ -125,13 +125,13 @@ static void connect_cb(uv_connect_t* req, int status) { #endif r = uv_read_start(&conn->stream, alloc_cb, read_cb); - ASSERT(r == 0); + ASSERT_OK(r); buf.base = buffer; buf.len = sizeof(buffer) - 1; r = uv_write(&conn->write_req, &conn->stream, &buf, 1, after_write); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -200,9 +200,9 @@ static void tcp_make_connect(conn_rec* p) { tp = (tcp_conn_rec*) p; r = uv_tcp_init(loop, (uv_tcp_t*)&p->stream); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_connect(&tp->conn_req, (uv_tcp_t*) &p->stream, @@ -227,7 +227,7 @@ static void pipe_make_connect(conn_rec* p) { int r; r = uv_pipe_init(loop, (uv_pipe_t*)&p->stream, 0); - ASSERT(r == 0); + ASSERT_OK(r); uv_pipe_connect(&((pipe_conn_rec*) p)->conn_req, (uv_pipe_t*) &p->stream, @@ -306,7 +306,7 @@ static int pound_it(int concurrency, conns_failed); fflush(stderr); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/benchmark-pump.c b/test/benchmark-pump.c index 7d3977dfc32..8797668ee93 100644 --- a/test/benchmark-pump.c +++ b/test/benchmark-pump.c @@ -159,9 +159,9 @@ static void start_stats_collection(void) { /* Show-stats timer */ stats_left = STATS_COUNT; r = uv_timer_init(loop, &timer_handle); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&timer_handle, show_stats, STATS_INTERVAL, STATS_INTERVAL); - ASSERT(r == 0); + ASSERT_OK(r); uv_update_time(loop); start_time = uv_now(loop); @@ -170,7 +170,7 @@ static void start_stats_collection(void) { static void read_cb(uv_stream_t* stream, ssize_t bytes, const uv_buf_t* buf) { if (nrecv_total == 0) { - ASSERT(start_time == 0); + ASSERT_OK(start_time); uv_update_time(loop); start_time = uv_now(loop); } @@ -188,7 +188,7 @@ static void read_cb(uv_stream_t* stream, ssize_t bytes, const uv_buf_t* buf) { static void write_cb(uv_write_t* req, int status) { - ASSERT(status == 0); + ASSERT_OK(status); req_free((uv_req_t*) req); @@ -209,7 +209,7 @@ static void do_write(uv_stream_t* stream) { req = (uv_write_t*) req_alloc(); r = uv_write(req, stream, &buf, 1, write_cb); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -220,7 +220,7 @@ static void connect_cb(uv_connect_t* req, int status) { fprintf(stderr, "%s", uv_strerror(status)); fflush(stderr); } - ASSERT(status == 0); + ASSERT_OK(status); write_sockets++; req_free((uv_req_t*) req); @@ -253,19 +253,19 @@ static void maybe_connect_some(void) { tcp = &tcp_write_handles[max_connect_socket++]; r = uv_tcp_init(loop, tcp); - ASSERT(r == 0); + ASSERT_OK(r); req = (uv_connect_t*) req_alloc(); r = uv_tcp_connect(req, tcp, (const struct sockaddr*) &connect_addr, connect_cb); - ASSERT(r == 0); + ASSERT_OK(r); } else { pipe = &pipe_write_handles[max_connect_socket++]; r = uv_pipe_init(loop, pipe, 0); - ASSERT(r == 0); + ASSERT_OK(r); req = (uv_connect_t*) req_alloc(); uv_pipe_connect(req, pipe, TEST_PIPENAME, connect_cb); @@ -278,24 +278,24 @@ static void connection_cb(uv_stream_t* s, int status) { uv_stream_t* stream; int r; - ASSERT(server == s); - ASSERT(status == 0); + ASSERT_PTR_EQ(server, s); + ASSERT_OK(status); if (type == TCP) { stream = (uv_stream_t*)malloc(sizeof(uv_tcp_t)); r = uv_tcp_init(loop, (uv_tcp_t*)stream); - ASSERT(r == 0); + ASSERT_OK(r); } else { stream = (uv_stream_t*)malloc(sizeof(uv_pipe_t)); r = uv_pipe_init(loop, (uv_pipe_t*)stream, 0); - ASSERT(r == 0); + ASSERT_OK(r); } r = uv_accept(s, stream); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_start(stream, buf_alloc, read_cb); - ASSERT(r == 0); + ASSERT_OK(r); read_sockets++; max_read_sockets++; @@ -379,16 +379,16 @@ HELPER_IMPL(tcp_pump_server) { type = TCP; loop = uv_default_loop(); - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &listen_addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &listen_addr)); /* Server */ server = (uv_stream_t*)&tcpServer; r = uv_tcp_init(loop, &tcpServer); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&tcpServer, (const struct sockaddr*) &listen_addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&tcpServer, MAX_WRITE_HANDLES, connection_cb); - ASSERT(r == 0); + ASSERT_OK(r); notify_parent_process(); uv_run(loop, UV_RUN_DEFAULT); @@ -406,40 +406,40 @@ HELPER_IMPL(pipe_pump_server) { /* Server */ server = (uv_stream_t*)&pipeServer; r = uv_pipe_init(loop, &pipeServer, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_bind(&pipeServer, TEST_PIPENAME); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&pipeServer, MAX_WRITE_HANDLES, connection_cb); - ASSERT(r == 0); + ASSERT_OK(r); notify_parent_process(); uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } static void tcp_pump(int n) { - ASSERT(n <= MAX_WRITE_HANDLES); + ASSERT_LE(n, MAX_WRITE_HANDLES); TARGET_CONNECTIONS = n; type = TCP; loop = uv_default_loop(); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &connect_addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &connect_addr)); /* Start making connections */ maybe_connect_some(); uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); } static void pipe_pump(int n) { - ASSERT(n <= MAX_WRITE_HANDLES); + ASSERT_LE(n, MAX_WRITE_HANDLES); TARGET_CONNECTIONS = n; type = PIPE; @@ -450,7 +450,7 @@ static void pipe_pump(int n) { uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); } diff --git a/test/test-callback-order.c b/test/benchmark-queue-work.c similarity index 52% rename from test/test-callback-order.c rename to test/benchmark-queue-work.c index 8bc2c4f75de..5ae0883101f 100644 --- a/test/test-callback-order.c +++ b/test/benchmark-queue-work.c @@ -1,4 +1,4 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. +/* Copyright libuv contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -19,59 +19,53 @@ * IN THE SOFTWARE. */ -#include "uv.h" #include "task.h" +#include "uv.h" -static int idle_cb_called; -static int timer_cb_called; - -static uv_idle_t idle_handle; -static uv_timer_t timer_handle; - +static int done = 0; +static unsigned events = 0; +static unsigned result; -/* idle_cb should run before timer_cb */ -static void idle_cb(uv_idle_t* handle) { - ASSERT(idle_cb_called == 0); - ASSERT(timer_cb_called == 0); - uv_idle_stop(handle); - idle_cb_called++; +static unsigned fastrand(void) { + static unsigned g = 0; + g = g * 214013 + 2531011; + return g; } - -static void timer_cb(uv_timer_t* handle) { - ASSERT(idle_cb_called == 1); - ASSERT(timer_cb_called == 0); - uv_timer_stop(handle); - timer_cb_called++; +static void work_cb(uv_work_t* req) { + req->data = &result; + *(unsigned*)req->data = fastrand(); } - -static void next_tick(uv_idle_t* handle) { - uv_loop_t* loop = handle->loop; - uv_idle_stop(handle); - uv_idle_init(loop, &idle_handle); - uv_idle_start(&idle_handle, idle_cb); - uv_timer_init(loop, &timer_handle); - uv_timer_start(&timer_handle, timer_cb, 0, 0); +static void after_work_cb(uv_work_t* req, int status) { + events++; + if (!done) + ASSERT_OK(uv_queue_work(req->loop, req, work_cb, after_work_cb)); } +static void timer_cb(uv_timer_t* handle) { done = 1; } -TEST_IMPL(callback_order) { +BENCHMARK_IMPL(queue_work) { + char fmtbuf[2][32]; + uv_timer_t timer_handle; + uv_work_t work; uv_loop_t* loop; - uv_idle_t idle; + int timeout; loop = uv_default_loop(); - uv_idle_init(loop, &idle); - uv_idle_start(&idle, next_tick); + timeout = 5000; - ASSERT(idle_cb_called == 0); - ASSERT(timer_cb_called == 0); + ASSERT_OK(uv_timer_init(loop, &timer_handle)); + ASSERT_OK(uv_timer_start(&timer_handle, timer_cb, timeout, 0)); - uv_run(loop, UV_RUN_DEFAULT); + ASSERT_OK(uv_queue_work(loop, &work, work_cb, after_work_cb)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(idle_cb_called == 1); - ASSERT(timer_cb_called == 1); + printf("%s async jobs in %.1f seconds (%s/s)\n", + fmt(&fmtbuf[0], events), + timeout / 1000., + fmt(&fmtbuf[1], events / (timeout / 1000.))); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/benchmark-spawn.c b/test/benchmark-spawn.c index ed9ad608f37..ef27b385e7a 100644 --- a/test/benchmark-spawn.c +++ b/test/benchmark-spawn.c @@ -58,7 +58,7 @@ static void maybe_spawn(void) { static void process_close_cb(uv_handle_t* handle) { - ASSERT(process_open == 1); + ASSERT_EQ(1, process_open); process_open = 0; maybe_spawn(); } @@ -67,8 +67,8 @@ static void process_close_cb(uv_handle_t* handle) { static void exit_cb(uv_process_t* process, int64_t exit_status, int term_signal) { - ASSERT(exit_status == 42); - ASSERT(term_signal == 0); + ASSERT_EQ(42, exit_status); + ASSERT_OK(term_signal); uv_close((uv_handle_t*)process, process_close_cb); } @@ -82,7 +82,7 @@ static void on_alloc(uv_handle_t* handle, static void pipe_close_cb(uv_handle_t* pipe) { - ASSERT(pipe_open == 1); + ASSERT_EQ(1, pipe_open); pipe_open = 0; maybe_spawn(); } @@ -90,7 +90,7 @@ static void pipe_close_cb(uv_handle_t* pipe) { static void on_read(uv_stream_t* pipe, ssize_t nread, const uv_buf_t* buf) { if (nread > 0) { - ASSERT(pipe_open == 1); + ASSERT_EQ(1, pipe_open); output_used += nread; } else if (nread < 0) { if (nread == UV_EOF) { @@ -104,8 +104,8 @@ static void spawn(void) { uv_stdio_container_t stdio[2]; int r; - ASSERT(process_open == 0); - ASSERT(pipe_open == 0); + ASSERT_OK(process_open); + ASSERT_OK(pipe_open); args[0] = exepath; args[1] = "spawn_helper"; @@ -123,14 +123,14 @@ static void spawn(void) { options.stdio[1].data.stream = (uv_stream_t*)&out; r = uv_spawn(loop, &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); process_open = 1; pipe_open = 1; output_used = 0; r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -141,7 +141,7 @@ BENCHMARK_IMPL(spawn) { loop = uv_default_loop(); r = uv_exepath(exepath, &exepath_size); - ASSERT(r == 0); + ASSERT_OK(r); exepath[exepath_size] = '\0'; uv_update_time(loop); @@ -150,7 +150,7 @@ BENCHMARK_IMPL(spawn) { spawn(); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); uv_update_time(loop); end_time = uv_now(loop); @@ -159,6 +159,6 @@ BENCHMARK_IMPL(spawn) { (double) N / (double) (end_time - start_time) * 1000.0); fflush(stderr); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/benchmark-tcp-write-batch.c b/test/benchmark-tcp-write-batch.c index 16aa72f6bf7..9dfcf14eb09 100644 --- a/test/benchmark-tcp-write-batch.c +++ b/test/benchmark-tcp-write-batch.c @@ -55,16 +55,16 @@ static void connect_cb(uv_connect_t* req, int status) { int i; int r; - ASSERT(req->handle == (uv_stream_t*)&tcp_client); + ASSERT_PTR_EQ(req->handle, (uv_stream_t*)&tcp_client); for (i = 0; i < NUM_WRITE_REQS; i++) { w = &write_reqs[i]; r = uv_write(&w->req, req->handle, &w->buf, 1, write_cb); - ASSERT(r == 0); + ASSERT_OK(r); } r = uv_shutdown(&shutdown_req, req->handle, shutdown_cb); - ASSERT(r == 0); + ASSERT_OK(r); connect_cb_called++; } @@ -72,14 +72,14 @@ static void connect_cb(uv_connect_t* req, int status) { static void write_cb(uv_write_t* req, int status) { ASSERT_NOT_NULL(req); - ASSERT(status == 0); + ASSERT_OK(status); write_cb_called++; } static void shutdown_cb(uv_shutdown_t* req, int status) { - ASSERT(req->handle == (uv_stream_t*)&tcp_client); - ASSERT(req->handle->write_queue_size == 0); + ASSERT_PTR_EQ(req->handle, (uv_stream_t*)&tcp_client); + ASSERT_OK(req->handle->write_queue_size); uv_close((uv_handle_t*)req->handle, close_cb); free(write_reqs); @@ -89,7 +89,7 @@ static void shutdown_cb(uv_shutdown_t* req, int status) { static void close_cb(uv_handle_t* handle) { - ASSERT(handle == (uv_handle_t*)&tcp_client); + ASSERT_PTR_EQ(handle, (uv_handle_t*)&tcp_client); close_cb_called++; } @@ -112,33 +112,33 @@ BENCHMARK_IMPL(tcp_write_batch) { } loop = uv_default_loop(); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_init(loop, &tcp_client); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_connect(&connect_req, &tcp_client, (const struct sockaddr*) &addr, connect_cb); - ASSERT(r == 0); + ASSERT_OK(r); start = uv_hrtime(); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); stop = uv_hrtime(); - ASSERT(connect_cb_called == 1); - ASSERT(write_cb_called == NUM_WRITE_REQS); - ASSERT(shutdown_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(write_cb_called, NUM_WRITE_REQS); + ASSERT_EQ(1, shutdown_cb_called); + ASSERT_EQ(1, close_cb_called); printf("%ld write requests in %.2fs.\n", (long)NUM_WRITE_REQS, (stop - start) / 1e9); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/benchmark-thread.c b/test/benchmark-thread.c index b37a7fd6d01..b339e7caad9 100644 --- a/test/benchmark-thread.c +++ b/test/benchmark-thread.c @@ -31,7 +31,7 @@ static volatile int num_threads; static void thread_entry(void* arg) { - ASSERT(arg == (void *) 42); + ASSERT_PTR_EQ(arg, (void *) 42); num_threads++; /* FIXME write barrier? */ } @@ -47,15 +47,15 @@ BENCHMARK_IMPL(thread_create) { for (i = 0; i < NUM_THREADS; i++) { r = uv_thread_create(&tid, thread_entry, (void *) 42); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_thread_join(&tid); - ASSERT(r == 0); + ASSERT_OK(r); } duration = (uv_hrtime() - start_time) / 1e9; - ASSERT(num_threads == NUM_THREADS); + ASSERT_EQ(num_threads, NUM_THREADS); printf("%d threads created in %.2f seconds (%.0f/s)\n", NUM_THREADS, duration, NUM_THREADS / duration); diff --git a/test/benchmark-udp-pummel.c b/test/benchmark-udp-pummel.c index 1a220570260..7b7e1afad7d 100644 --- a/test/benchmark-udp-pummel.c +++ b/test/benchmark-udp-pummel.c @@ -63,7 +63,7 @@ static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { static char slab[65536]; - ASSERT(suggested_size <= sizeof(slab)); + ASSERT_LE(suggested_size, sizeof(slab)); buf->base = slab; buf->len = sizeof(slab); } @@ -75,7 +75,7 @@ static void send_cb(uv_udp_send_t* req, int status) { ASSERT_NOT_NULL(req); if (status != 0) { - ASSERT(status == UV_ECANCELED); + ASSERT_EQ(status, UV_ECANCELED); return; } @@ -83,7 +83,7 @@ static void send_cb(uv_udp_send_t* req, int status) { return; s = container_of(req, struct sender_state, send_req); - ASSERT(req->handle == &s->udp_handle); + ASSERT_PTR_EQ(req->handle, &s->udp_handle); if (timed) goto send; @@ -96,12 +96,12 @@ static void send_cb(uv_udp_send_t* req, int status) { packet_counter--; send: - ASSERT(0 == uv_udp_send(&s->send_req, - &s->udp_handle, - bufs, - ARRAY_SIZE(bufs), - (const struct sockaddr*) &s->addr, - send_cb)); + ASSERT_OK(uv_udp_send(&s->send_req, + &s->udp_handle, + bufs, + ARRAY_SIZE(bufs), + (const struct sockaddr*) &s->addr, + send_cb)); send_cb_called++; } @@ -115,11 +115,11 @@ static void recv_cb(uv_udp_t* handle, return; if (nread < 0) { - ASSERT(nread == UV_ECANCELED); + ASSERT_EQ(nread, UV_ECANCELED); return; } - ASSERT(addr->sa_family == AF_INET); + ASSERT_EQ(addr->sa_family, AF_INET); ASSERT(!memcmp(buf->base, EXPECTED, nread)); recv_cb_called++; @@ -153,8 +153,8 @@ static int pummel(unsigned int n_senders, uv_loop_t* loop; unsigned int i; - ASSERT(n_senders <= ARRAY_SIZE(senders)); - ASSERT(n_receivers <= ARRAY_SIZE(receivers)); + ASSERT_LE(n_senders, ARRAY_SIZE(senders)); + ASSERT_LE(n_receivers, ARRAY_SIZE(receivers)); loop = uv_default_loop(); @@ -162,8 +162,8 @@ static int pummel(unsigned int n_senders, n_receivers_ = n_receivers; if (timeout) { - ASSERT(0 == uv_timer_init(loop, &timer_handle)); - ASSERT(0 == uv_timer_start(&timer_handle, timeout_cb, timeout, 0)); + ASSERT_OK(uv_timer_init(loop, &timer_handle)); + ASSERT_OK(uv_timer_start(&timer_handle, timeout_cb, timeout, 0)); /* Timer should not keep loop alive. */ uv_unref((uv_handle_t*)&timer_handle); timed = 1; @@ -172,10 +172,10 @@ static int pummel(unsigned int n_senders, for (i = 0; i < n_receivers; i++) { struct receiver_state* s = receivers + i; struct sockaddr_in addr; - ASSERT(0 == uv_ip4_addr("0.0.0.0", BASE_PORT + i, &addr)); - ASSERT(0 == uv_udp_init(loop, &s->udp_handle)); - ASSERT(0 == uv_udp_bind(&s->udp_handle, (const struct sockaddr*) &addr, 0)); - ASSERT(0 == uv_udp_recv_start(&s->udp_handle, alloc_cb, recv_cb)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", BASE_PORT + i, &addr)); + ASSERT_OK(uv_udp_init(loop, &s->udp_handle)); + ASSERT_OK(uv_udp_bind(&s->udp_handle, (const struct sockaddr*) &addr, 0)); + ASSERT_OK(uv_udp_recv_start(&s->udp_handle, alloc_cb, recv_cb)); uv_unref((uv_handle_t*)&s->udp_handle); } @@ -187,20 +187,20 @@ static int pummel(unsigned int n_senders, for (i = 0; i < n_senders; i++) { struct sender_state* s = senders + i; - ASSERT(0 == uv_ip4_addr("127.0.0.1", - BASE_PORT + (i % n_receivers), - &s->addr)); - ASSERT(0 == uv_udp_init(loop, &s->udp_handle)); - ASSERT(0 == uv_udp_send(&s->send_req, - &s->udp_handle, - bufs, - ARRAY_SIZE(bufs), - (const struct sockaddr*) &s->addr, - send_cb)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", + BASE_PORT + (i % n_receivers), + &s->addr)); + ASSERT_OK(uv_udp_init(loop, &s->udp_handle)); + ASSERT_OK(uv_udp_send(&s->send_req, + &s->udp_handle, + bufs, + ARRAY_SIZE(bufs), + (const struct sockaddr*) &s->addr, + send_cb)); } duration = uv_hrtime(); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); duration = uv_hrtime() - duration; /* convert from nanoseconds to milliseconds */ duration = duration / (uint64_t) 1e6; @@ -215,7 +215,7 @@ static int pummel(unsigned int n_senders, send_cb_called, duration / 1000.0); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/blackhole-server.c b/test/blackhole-server.c index 0a8758e1a17..79d6146f479 100644 --- a/test/blackhole-server.c +++ b/test/blackhole-server.c @@ -43,20 +43,20 @@ static void connection_cb(uv_stream_t* stream, int status) { conn_rec* conn; int r; - ASSERT(status == 0); - ASSERT(stream == (uv_stream_t*)&tcp_server); + ASSERT_OK(status); + ASSERT_PTR_EQ(stream, (uv_stream_t*)&tcp_server); conn = malloc(sizeof *conn); ASSERT_NOT_NULL(conn); r = uv_tcp_init(stream->loop, &conn->handle); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_accept(stream, (uv_stream_t*)&conn->handle); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_start((uv_stream_t*)&conn->handle, alloc_cb, read_cb); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -76,12 +76,12 @@ static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { if (nread >= 0) return; - ASSERT(nread == UV_EOF); + ASSERT_EQ(nread, UV_EOF); conn = container_of(stream, conn_rec, handle); r = uv_shutdown(&conn->shutdown_req, stream, shutdown_cb); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -103,16 +103,16 @@ HELPER_IMPL(tcp4_blackhole_server) { int r; loop = uv_default_loop(); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_init(loop, &tcp_server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&tcp_server, 128, connection_cb); - ASSERT(r == 0); + ASSERT_OK(r); notify_parent_process(); r = uv_run(loop, UV_RUN_DEFAULT); diff --git a/test/echo-server.c b/test/echo-server.c index 058c9925475..572f87df51e 100644 --- a/test/echo-server.c +++ b/test/echo-server.c @@ -65,14 +65,14 @@ static void after_write(uv_write_t* req, int status) { static void after_shutdown(uv_shutdown_t* req, int status) { - ASSERT_EQ(status, 0); + ASSERT_OK(status); uv_close((uv_handle_t*) req->handle, on_close); free(req); } static void on_shutdown(uv_shutdown_t* req, int status) { - ASSERT_EQ(status, 0); + ASSERT_OK(status); free(req); } @@ -92,7 +92,7 @@ static void after_read(uv_stream_t* handle, free(buf->base); sreq = malloc(sizeof* sreq); if (uv_is_writable(handle)) { - ASSERT_EQ(0, uv_shutdown(sreq, handle, after_shutdown)); + ASSERT_OK(uv_shutdown(sreq, handle, after_shutdown)); } return; } @@ -118,7 +118,7 @@ static void after_read(uv_stream_t* handle, if (i + 2 < nread && buf->base[i + 2] == 'H') reset = 1; if (reset && handle->type == UV_TCP) - ASSERT_EQ(0, uv_tcp_close_reset((uv_tcp_t*) handle, on_close)); + ASSERT_OK(uv_tcp_close_reset((uv_tcp_t*) handle, on_close)); else if (shutdown) break; else @@ -141,7 +141,7 @@ static void after_read(uv_stream_t* handle, } if (shutdown) - ASSERT_EQ(0, uv_shutdown(malloc(sizeof* sreq), handle, on_shutdown)); + ASSERT_OK(uv_shutdown(malloc(sizeof* sreq), handle, on_shutdown)); } @@ -173,21 +173,21 @@ static void on_connection(uv_stream_t* server, int status) { if (status != 0) { fprintf(stderr, "Connect error %s\n", uv_err_name(status)); } - ASSERT(status == 0); + ASSERT_OK(status); switch (serverType) { case TCP: stream = malloc(sizeof(uv_tcp_t)); ASSERT_NOT_NULL(stream); r = uv_tcp_init(loop, (uv_tcp_t*)stream); - ASSERT(r == 0); + ASSERT_OK(r); break; case PIPE: stream = malloc(sizeof(uv_pipe_t)); ASSERT_NOT_NULL(stream); r = uv_pipe_init(loop, (uv_pipe_t*)stream, 0); - ASSERT(r == 0); + ASSERT_OK(r); break; default: @@ -199,15 +199,15 @@ static void on_connection(uv_stream_t* server, int status) { stream->data = server; r = uv_accept(server, stream); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_start(stream, echo_alloc, after_read); - ASSERT(r == 0); + ASSERT_OK(r); } static void on_server_close(uv_handle_t* handle) { - ASSERT(handle == server); + ASSERT_PTR_EQ(handle, server); } static uv_udp_send_t* send_alloc(void) { @@ -221,7 +221,7 @@ static uv_udp_send_t* send_alloc(void) { static void on_send(uv_udp_send_t* req, int status) { ASSERT_NOT_NULL(req); - ASSERT(status == 0); + ASSERT_OK(status); req->data = send_freelist; send_freelist = req; } @@ -239,20 +239,20 @@ static void on_recv(uv_udp_t* handle, return; } - ASSERT(nread > 0); - ASSERT(addr->sa_family == AF_INET); + ASSERT_GT(nread, 0); + ASSERT_EQ(addr->sa_family, AF_INET); req = send_alloc(); ASSERT_NOT_NULL(req); sndbuf = uv_buf_init(rcvbuf->base, nread); - ASSERT(0 <= uv_udp_send(req, handle, &sndbuf, 1, addr, on_send)); + ASSERT_LE(0, uv_udp_send(req, handle, &sndbuf, 1, addr, on_send)); } static int tcp4_echo_start(int port) { struct sockaddr_in addr; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", port, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", port, &addr)); server = (uv_handle_t*)&tcpServer; serverType = TCP; @@ -286,7 +286,7 @@ static int tcp6_echo_start(int port) { struct sockaddr_in6 addr6; int r; - ASSERT(0 == uv_ip6_addr("::1", port, &addr6)); + ASSERT_OK(uv_ip6_addr("::1", port, &addr6)); server = (uv_handle_t*)&tcpServer; serverType = TCP; @@ -321,7 +321,7 @@ static int udp4_echo_start(int port) { struct sockaddr_in addr; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", port, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", port, &addr)); server = (uv_handle_t*)&udpServer; serverType = UDP; diff --git a/test/fixtures/one_file/one_file b/test/fixtures/one_file/one_file new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/run-tests.c b/test/run-tests.c index 5e53aaa80b1..17fb0e0cf73 100644 --- a/test/run-tests.c +++ b/test/run-tests.c @@ -25,6 +25,7 @@ #ifdef _WIN32 # include +# define read _read #else # include #endif @@ -49,7 +50,6 @@ __attribute__((constructor)) void init() { int ipc_helper(int listen_after_write); int ipc_helper_heavy_traffic_deadlock_bug(void); int ipc_helper_tcp_connection(void); -int ipc_helper_closed_handle(void); int ipc_send_recv_helper(void); int ipc_helper_bind_twice(void); int ipc_helper_send_zero(void); @@ -86,10 +86,6 @@ int main(int argc, char **argv) { fflush(stderr); return EXIT_FAILURE; } - -#ifndef __SUNPRO_C - return EXIT_SUCCESS; -#endif } @@ -119,10 +115,6 @@ static int maybe_run_test(int argc, char **argv) { return ipc_helper_tcp_connection(); } - if (strcmp(argv[1], "ipc_helper_closed_handle") == 0) { - return ipc_helper_closed_handle(); - } - if (strcmp(argv[1], "ipc_helper_bind_twice") == 0) { return ipc_helper_bind_twice(); } @@ -154,7 +146,7 @@ static int maybe_run_test(int argc, char **argv) { if (strcmp(argv[1], "spawn_helper3") == 0) { char buffer[256]; notify_parent_process(); - ASSERT(buffer == fgets(buffer, sizeof(buffer) - 1, stdin)); + ASSERT_PTR_EQ(buffer, fgets(buffer, sizeof(buffer) - 1, stdin)); buffer[sizeof(buffer) - 1] = '\0'; fputs(buffer, stdout); return 1; @@ -192,10 +184,10 @@ static int maybe_run_test(int argc, char **argv) { notify_parent_process(); r = fprintf(stdout, "hello world\n"); - ASSERT(r > 0); + ASSERT_GT(r, 0); r = fprintf(stderr, "hello errworld\n"); - ASSERT(r > 0); + ASSERT_GT(r, 0); return 1; } @@ -211,7 +203,7 @@ static int maybe_run_test(int argc, char **argv) { ASSERT_NOT_NULL(test); r = fprintf(stdout, "%s", test); - ASSERT(r > 0); + ASSERT_GT(r, 0); return 1; } @@ -225,23 +217,24 @@ static int maybe_run_test(int argc, char **argv) { sCompareObjectHandles pCompareObjectHandles; /* function introduced in Windows 10 */ #endif notify_parent_process(); - ASSERT(sizeof(closed_fd) == read(0, &closed_fd, sizeof(closed_fd))); - ASSERT(sizeof(open_fd) == read(0, &open_fd, sizeof(open_fd))); + ASSERT_EQ(sizeof(closed_fd), read(0, &closed_fd, sizeof(closed_fd))); + ASSERT_EQ(sizeof(open_fd), read(0, &open_fd, sizeof(open_fd))); #ifdef _WIN32 - ASSERT((intptr_t) closed_fd > 0); - ASSERT((intptr_t) open_fd > 0); - ASSERT(0 != GetHandleInformation(open_fd, &flags)); + ASSERT_GT((intptr_t) closed_fd, 0); + ASSERT_GT((intptr_t) open_fd, 0); + ASSERT_NE(0, GetHandleInformation(open_fd, &flags)); kernelbase_module = GetModuleHandleA("kernelbase.dll"); pCompareObjectHandles = (sCompareObjectHandles) GetProcAddress(kernelbase_module, "CompareObjectHandles"); - ASSERT(pCompareObjectHandles == NULL || !pCompareObjectHandles(open_fd, closed_fd)); + ASSERT_NE(pCompareObjectHandles == NULL || \ + !pCompareObjectHandles(open_fd, closed_fd), 0); #else - ASSERT(open_fd > 2); - ASSERT(closed_fd > 2); + ASSERT_GT(open_fd, 2); + ASSERT_GT(closed_fd, 2); # if defined(__PASE__) /* On IBMi PASE, write() returns 1 */ - ASSERT(1 == write(closed_fd, "x", 1)); + ASSERT_EQ(1, write(closed_fd, "x", 1)); # else - ASSERT(-1 == write(closed_fd, "x", 1)); + ASSERT_EQ(-1, write(closed_fd, "x", 1)); # endif /* !__PASE__ */ #endif return 1; @@ -258,8 +251,8 @@ static int maybe_run_test(int argc, char **argv) { uv_uid_t uid = atoi(argv[2]); uv_gid_t gid = atoi(argv[3]); - ASSERT(uid == getuid()); - ASSERT(gid == getgid()); + ASSERT_EQ(uid, getuid()); + ASSERT_EQ(gid, getgid()); notify_parent_process(); return 1; diff --git a/test/runner-unix.c b/test/runner-unix.c index a13648bc883..81560add820 100644 --- a/test/runner-unix.c +++ b/test/runner-unix.c @@ -40,6 +40,10 @@ #include #include +#ifdef __APPLE__ +#include +#endif + extern char** environ; static void closefd(int fd) { @@ -131,7 +135,11 @@ int process_start(char* name, char* part, process_info_t* p, int is_helper) { p->terminated = 0; p->status = 0; +#if defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH) + pid = -1; +#else pid = fork(); +#endif if (pid < 0) { perror("fork"); @@ -144,7 +152,9 @@ int process_start(char* name, char* part, process_info_t* p, int is_helper) { closefd(pipefd[0]); dup2(stdout_fd, STDOUT_FILENO); dup2(stdout_fd, STDERR_FILENO); +#if !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH)) execve(args[0], args, environ); +#endif perror("execve()"); _exit(127); } @@ -333,8 +343,8 @@ int process_wait(process_info_t* vec, int n, int timeout) { abort(); terminate: - close(args.pipe[0]); - close(args.pipe[1]); + closefd(args.pipe[0]); + closefd(args.pipe[1]); return retval; } @@ -344,6 +354,7 @@ long int process_output_size(process_info_t *p) { /* Size of the p->stdout_file */ struct stat buf; + memset(&buf, 0, sizeof(buf)); int r = fstat(fileno(p->stdout_file), &buf); if (r < 0) { return -1; diff --git a/test/runner-win.c b/test/runner-win.c index 8c2a00b8a62..6c6e35f7731 100644 --- a/test/runner-win.c +++ b/test/runner-win.c @@ -185,7 +185,7 @@ int process_wait(process_info_t *vec, int n, int timeout) { if (n == 0) return 0; - ASSERT(n <= MAXIMUM_WAIT_OBJECTS); + ASSERT_LE(n, MAXIMUM_WAIT_OBJECTS); for (i = 0; i < n; i++) handles[i] = vec[i].process; @@ -245,7 +245,7 @@ int process_read_last_line(process_info_t *p, DWORD start; OVERLAPPED overlapped; - ASSERT(buffer_len > 0); + ASSERT_GT(buffer_len, 0); size = GetFileSize(p->stdio_out, NULL); if (size == INVALID_FILE_SIZE) @@ -310,7 +310,7 @@ static int clear_line(void) { COORD coord; DWORD written; - handle = (HANDLE)_get_osfhandle(fileno(stderr)); + handle = (HANDLE)_get_osfhandle(_fileno(stderr)); if (handle == INVALID_HANDLE_VALUE) return -1; diff --git a/test/runner.c b/test/runner.c index 789108275cd..54abb39dd22 100644 --- a/test/runner.c +++ b/test/runner.c @@ -27,6 +27,11 @@ #include "task.h" #include "uv.h" +/* Refs: https://github.com/libuv/libuv/issues/4369 */ +#if defined(__ANDROID__) +#include +#endif + char executable_path[sizeof(executable_path)]; @@ -37,28 +42,14 @@ static int compare_task(const void* va, const void* vb) { } -const char* fmt(double d) { - static char buf[1024]; - static char* p; +char* fmt(char (*buf)[32], double d) { uint64_t v; + char* p; - if (p == NULL) - p = buf; - - p += 31; - - if (p >= buf + sizeof(buf)) - return ""; - + p = &(*buf)[32]; v = (uint64_t) d; -#if 0 /* works but we don't care about fractional precision */ - if (d - v >= 0.01) { - *--p = '0' + (uint64_t) (d * 100) % 10; - *--p = '0' + (uint64_t) (d * 10) % 10; - *--p = '.'; - } -#endif + *--p = '\0'; if (v == 0) *--p = '0'; @@ -77,9 +68,7 @@ const char* fmt(double d) { int run_tests(int benchmark_output) { int actual; int total; - int passed; int failed; - int skipped; int current; int test_result; int skip; @@ -102,9 +91,7 @@ int run_tests(int benchmark_output) { fflush(stdout); /* Run all tests. */ - passed = 0; failed = 0; - skipped = 0; current = 1; for (task = TASKS; task->main; task++) { if (task->is_helper) { @@ -113,8 +100,8 @@ int run_tests(int benchmark_output) { test_result = run_test(task->task_name, benchmark_output, current); switch (test_result) { - case TEST_OK: passed++; break; - case TEST_SKIP: skipped++; break; + case TEST_OK: break; + case TEST_SKIP: break; default: failed++; } current++; @@ -160,6 +147,13 @@ void log_tap_result(int test_count, fflush(stdout); } +void enable_fdsan(void) { +/* Refs: https://github.com/libuv/libuv/issues/4369 */ +#if defined(__ANDROID__) + android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ALWAYS); +#endif +} + int run_test(const char* test, int benchmark_output, @@ -178,6 +172,8 @@ int run_test(const char* test, main_proc = NULL; process_count = 0; + enable_fdsan(); + #ifndef _WIN32 /* Clean up stale socket from previous run. */ remove(TEST_PIPENAME); diff --git a/test/task.h b/test/task.h index 925f1b1c0ae..e25a9c9a138 100644 --- a/test/task.h +++ b/test/task.h @@ -29,12 +29,7 @@ #include #include #include - -#if defined(_MSC_VER) && _MSC_VER < 1600 -# include "uv/stdint-msvc2008.h" -#else -# include -#endif +#include #if !defined(_WIN32) # include @@ -55,9 +50,13 @@ #define TEST_PORT_3 9125 #ifdef _WIN32 -# define TEST_PIPENAME "\\\\?\\pipe\\uv-test" -# define TEST_PIPENAME_2 "\\\\?\\pipe\\uv-test2" -# define TEST_PIPENAME_3 "\\\\?\\pipe\\uv-test3" +# define TEST_PIPENAME "\\\\.\\pipe\\uv-test" +# define TEST_PIPENAME_2 "\\\\.\\pipe\\uv-test2" +# define TEST_PIPENAME_3 "\\\\.\\pipe\\uv-test3" +#elif __ANDROID__ +# define TEST_PIPENAME "/data/local/tmp/uv-test-sock" +# define TEST_PIPENAME_2 "/data/local/tmp/uv-test-sock2" +# define TEST_PIPENAME_3 "/data/local/tmp/uv-test-sock3" #else # define TEST_PIPENAME "/tmp/uv-test-sock" # define TEST_PIPENAME_2 "/tmp/uv-test-sock2" @@ -203,6 +202,7 @@ typedef enum { #define ASSERT_LE(a, b) ASSERT_BASE(a, <=, b, int64_t, PRId64) #define ASSERT_LT(a, b) ASSERT_BASE(a, <, b, int64_t, PRId64) #define ASSERT_NE(a, b) ASSERT_BASE(a, !=, b, int64_t, PRId64) +#define ASSERT_OK(a) ASSERT_BASE(a, ==, 0, int64_t, PRId64) #define ASSERT_UINT64_EQ(a, b) ASSERT_BASE(a, ==, b, uint64_t, PRIu64) #define ASSERT_UINT64_GE(a, b) ASSERT_BASE(a, >=, b, uint64_t, PRIu64) @@ -248,13 +248,16 @@ typedef enum { #define ASSERT_PTR_NE(a, b) \ ASSERT_BASE(a, !=, b, void*, "p") -/* This macro cleans up the main loop. This is used to avoid valgrind - * warnings about memory being "leaked" by the main event loop. +#define ASSERT_PTR_LT(a, b) \ + ASSERT_BASE(a, <, b, void*, "p") + +/* This macro cleans up the event loop. This is used to avoid valgrind + * warnings about memory being "leaked" by the event loop. */ -#define MAKE_VALGRIND_HAPPY() \ +#define MAKE_VALGRIND_HAPPY(loop) \ do { \ - close_loop(uv_default_loop()); \ - ASSERT(0 == uv_loop_close(uv_default_loop())); \ + close_loop(loop); \ + ASSERT_EQ(0, uv_loop_close(loop)); \ uv_library_shutdown(); \ } while (0) @@ -271,8 +274,8 @@ typedef enum { int run_helper_##name(void); \ int run_helper_##name(void) -/* Format big numbers nicely. WARNING: leaks memory. */ -const char* fmt(double d); +/* Format big numbers nicely. */ +char* fmt(char (*buf)[32], double d); /* Reserved test exit codes. */ enum test_status { @@ -375,4 +378,11 @@ UNUSED static int can_ipv6(void) { "Cygwin runtime hangs on listen+connect in same process." #endif +#if !defined(__linux__) && \ + !(defined(__FreeBSD__) && __FreeBSD_version >= 1301000) && \ + !defined(_WIN32) +# define NO_CPU_AFFINITY \ + "affinity not supported on this platform." +#endif + #endif /* TASK_H_ */ diff --git a/test/test-active.c b/test/test-active.c index 384389561a7..fadbd10d228 100644 --- a/test/test-active.c +++ b/test/test-active.c @@ -45,40 +45,40 @@ TEST_IMPL(active) { uv_timer_t timer; r = uv_timer_init(uv_default_loop(), &timer); - ASSERT(r == 0); + ASSERT_OK(r); /* uv_is_active() and uv_is_closing() should always return either 0 or 1. */ - ASSERT(0 == uv_is_active((uv_handle_t*) &timer)); - ASSERT(0 == uv_is_closing((uv_handle_t*) &timer)); + ASSERT_OK(uv_is_active((uv_handle_t*) &timer)); + ASSERT_OK(uv_is_closing((uv_handle_t*) &timer)); r = uv_timer_start(&timer, timer_cb, 1000, 0); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(1 == uv_is_active((uv_handle_t*) &timer)); - ASSERT(0 == uv_is_closing((uv_handle_t*) &timer)); + ASSERT_EQ(1, uv_is_active((uv_handle_t*) &timer)); + ASSERT_OK(uv_is_closing((uv_handle_t*) &timer)); r = uv_timer_stop(&timer); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(0 == uv_is_active((uv_handle_t*) &timer)); - ASSERT(0 == uv_is_closing((uv_handle_t*) &timer)); + ASSERT_OK(uv_is_active((uv_handle_t*) &timer)); + ASSERT_OK(uv_is_closing((uv_handle_t*) &timer)); r = uv_timer_start(&timer, timer_cb, 1000, 0); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(1 == uv_is_active((uv_handle_t*) &timer)); - ASSERT(0 == uv_is_closing((uv_handle_t*) &timer)); + ASSERT_EQ(1, uv_is_active((uv_handle_t*) &timer)); + ASSERT_OK(uv_is_closing((uv_handle_t*) &timer)); uv_close((uv_handle_t*) &timer, close_cb); - ASSERT(0 == uv_is_active((uv_handle_t*) &timer)); - ASSERT(1 == uv_is_closing((uv_handle_t*) &timer)); + ASSERT_OK(uv_is_active((uv_handle_t*) &timer)); + ASSERT_EQ(1, uv_is_closing((uv_handle_t*) &timer)); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-async-null-cb.c b/test/test-async-null-cb.c index 52652d91ebf..ac9fc899c7a 100644 --- a/test/test-async-null-cb.c +++ b/test/test-async-null-cb.c @@ -36,7 +36,7 @@ static void thread_cb(void* dummy) { static void check_cb(uv_check_t* handle) { - ASSERT(check_cb_called == 0); + ASSERT_OK(check_cb_called); uv_close((uv_handle_t*) &async_handle, NULL); uv_close((uv_handle_t*) &check_handle, NULL); check_cb_called++; @@ -52,13 +52,13 @@ TEST_IMPL(async_null_cb) { */ memset(&async_handle, 0xff, sizeof(async_handle)); - ASSERT(0 == uv_async_init(uv_default_loop(), &async_handle, NULL)); - ASSERT(0 == uv_check_init(uv_default_loop(), &check_handle)); - ASSERT(0 == uv_check_start(&check_handle, check_cb)); - ASSERT(0 == uv_thread_create(&thread, thread_cb, NULL)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(0 == uv_thread_join(&thread)); - ASSERT(1 == check_cb_called); - MAKE_VALGRIND_HAPPY(); + ASSERT_OK(uv_async_init(uv_default_loop(), &async_handle, NULL)); + ASSERT_OK(uv_check_init(uv_default_loop(), &check_handle)); + ASSERT_OK(uv_check_start(&check_handle, check_cb)); + ASSERT_OK(uv_thread_create(&thread, thread_cb, NULL)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_thread_join(&thread)); + ASSERT_EQ(1, check_cb_called); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-async.c b/test/test-async.c index 619be620e3e..935436ec093 100644 --- a/test/test-async.c +++ b/test/test-async.c @@ -49,7 +49,7 @@ static void thread_cb(void *arg) { } r = uv_async_send(&async); - ASSERT(r == 0); + ASSERT_OK(r); /* Work around a bug in Valgrind. * @@ -78,7 +78,7 @@ static void close_cb(uv_handle_t* handle) { static void async_cb(uv_async_t* handle) { int n; - ASSERT(handle == &async); + ASSERT_PTR_EQ(handle, &async); uv_mutex_lock(&mutex); n = ++async_cb_called; @@ -94,13 +94,13 @@ static void async_cb(uv_async_t* handle) { static void prepare_cb(uv_prepare_t* handle) { int r; - ASSERT(handle == &prepare); + ASSERT_PTR_EQ(handle, &prepare); if (prepare_cb_called++) return; r = uv_thread_create(&thread, thread_cb, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_mutex_unlock(&mutex); } @@ -109,26 +109,26 @@ TEST_IMPL(async) { int r; r = uv_mutex_init(&mutex); - ASSERT(r == 0); + ASSERT_OK(r); uv_mutex_lock(&mutex); r = uv_prepare_init(uv_default_loop(), &prepare); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_prepare_start(&prepare, prepare_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_async_init(uv_default_loop(), &async, async_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(prepare_cb_called > 0); - ASSERT(async_cb_called == 3); - ASSERT(close_cb_called == 2); + ASSERT_GT(prepare_cb_called, 0); + ASSERT_EQ(3, async_cb_called); + ASSERT_EQ(2, close_cb_called); - ASSERT(0 == uv_thread_join(&thread)); + ASSERT_OK(uv_thread_join(&thread)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-barrier.c b/test/test-barrier.c index 89858db5711..5e904c40bc4 100644 --- a/test/test-barrier.c +++ b/test/test-barrier.c @@ -27,20 +27,22 @@ typedef struct { uv_barrier_t barrier; - int delay; - volatile int posted; - int main_barrier_wait_rval; - int worker_barrier_wait_rval; + unsigned delay; + unsigned niter; + unsigned main_barrier_wait_rval; + unsigned worker_barrier_wait_rval; } worker_config; static void worker(void* arg) { worker_config* c = arg; + unsigned i; if (c->delay) uv_sleep(c->delay); - c->worker_barrier_wait_rval = uv_barrier_wait(&c->barrier); + for (i = 0; i < c->niter; i++) + c->worker_barrier_wait_rval += uv_barrier_wait(&c->barrier); } @@ -49,17 +51,18 @@ TEST_IMPL(barrier_1) { worker_config wc; memset(&wc, 0, sizeof(wc)); + wc.niter = 1; - ASSERT(0 == uv_barrier_init(&wc.barrier, 2)); - ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + ASSERT_OK(uv_barrier_init(&wc.barrier, 2)); + ASSERT_OK(uv_thread_create(&thread, worker, &wc)); uv_sleep(100); wc.main_barrier_wait_rval = uv_barrier_wait(&wc.barrier); - ASSERT(0 == uv_thread_join(&thread)); + ASSERT_OK(uv_thread_join(&thread)); uv_barrier_destroy(&wc.barrier); - ASSERT(1 == (wc.main_barrier_wait_rval ^ wc.worker_barrier_wait_rval)); + ASSERT_EQ(1, (wc.main_barrier_wait_rval ^ wc.worker_barrier_wait_rval)); return 0; } @@ -71,16 +74,17 @@ TEST_IMPL(barrier_2) { memset(&wc, 0, sizeof(wc)); wc.delay = 100; + wc.niter = 1; - ASSERT(0 == uv_barrier_init(&wc.barrier, 2)); - ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + ASSERT_OK(uv_barrier_init(&wc.barrier, 2)); + ASSERT_OK(uv_thread_create(&thread, worker, &wc)); wc.main_barrier_wait_rval = uv_barrier_wait(&wc.barrier); - ASSERT(0 == uv_thread_join(&thread)); + ASSERT_OK(uv_thread_join(&thread)); uv_barrier_destroy(&wc.barrier); - ASSERT(1 == (wc.main_barrier_wait_rval ^ wc.worker_barrier_wait_rval)); + ASSERT_EQ(1, (wc.main_barrier_wait_rval ^ wc.worker_barrier_wait_rval)); return 0; } @@ -89,26 +93,32 @@ TEST_IMPL(barrier_2) { TEST_IMPL(barrier_3) { uv_thread_t thread; worker_config wc; + unsigned i; memset(&wc, 0, sizeof(wc)); + wc.niter = 5; - ASSERT(0 == uv_barrier_init(&wc.barrier, 2)); - ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + ASSERT_OK(uv_barrier_init(&wc.barrier, 2)); + ASSERT_OK(uv_thread_create(&thread, worker, &wc)); - wc.main_barrier_wait_rval = uv_barrier_wait(&wc.barrier); + for (i = 0; i < wc.niter; i++) + wc.main_barrier_wait_rval += uv_barrier_wait(&wc.barrier); - ASSERT(0 == uv_thread_join(&thread)); + ASSERT_OK(uv_thread_join(&thread)); uv_barrier_destroy(&wc.barrier); - ASSERT(1 == (wc.main_barrier_wait_rval ^ wc.worker_barrier_wait_rval)); + ASSERT_EQ(wc.niter, wc.main_barrier_wait_rval + wc.worker_barrier_wait_rval); return 0; } static void serial_worker(void* data) { uv_barrier_t* barrier; + unsigned i; barrier = data; + for (i = 0; i < 5; i++) + uv_barrier_wait(barrier); if (uv_barrier_wait(barrier) > 0) uv_barrier_destroy(barrier); @@ -123,16 +133,18 @@ TEST_IMPL(barrier_serial_thread) { uv_barrier_t barrier; unsigned i; - ASSERT(0 == uv_barrier_init(&barrier, ARRAY_SIZE(threads) + 1)); + ASSERT_OK(uv_barrier_init(&barrier, ARRAY_SIZE(threads) + 1)); for (i = 0; i < ARRAY_SIZE(threads); ++i) - ASSERT(0 == uv_thread_create(&threads[i], serial_worker, &barrier)); + ASSERT_OK(uv_thread_create(&threads[i], serial_worker, &barrier)); + for (i = 0; i < 5; i++) + uv_barrier_wait(&barrier); if (uv_barrier_wait(&barrier) > 0) uv_barrier_destroy(&barrier); for (i = 0; i < ARRAY_SIZE(threads); ++i) - ASSERT(0 == uv_thread_join(&threads[i])); + ASSERT_OK(uv_thread_join(&threads[i])); return 0; } @@ -141,8 +153,8 @@ TEST_IMPL(barrier_serial_thread) { TEST_IMPL(barrier_serial_thread_single) { uv_barrier_t barrier; - ASSERT(0 == uv_barrier_init(&barrier, 1)); - ASSERT(0 < uv_barrier_wait(&barrier)); + ASSERT_OK(uv_barrier_init(&barrier, 1)); + ASSERT_LT(0, uv_barrier_wait(&barrier)); uv_barrier_destroy(&barrier); return 0; } diff --git a/test/test-callback-stack.c b/test/test-callback-stack.c index a5195c7b7f3..dfd102c0247 100644 --- a/test/test-callback-stack.c +++ b/test/test-callback-stack.c @@ -60,7 +60,7 @@ static void close_cb(uv_handle_t* handle) { static void shutdown_cb(uv_shutdown_t* req, int status) { - ASSERT(status == 0); + ASSERT_OK(status); ASSERT(nested == 0 && "shutdown_cb must be called from a fresh stack"); shutdown_cb_called++; @@ -77,7 +77,7 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { return; } else if (nread < 0) { - ASSERT(nread == UV_EOF); + ASSERT_EQ(nread, UV_EOF); nested++; uv_close((uv_handle_t*)tcp, close_cb); @@ -105,7 +105,7 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { static void timer_cb(uv_timer_t* handle) { - ASSERT(handle == &timer); + ASSERT_PTR_EQ(handle, &timer); ASSERT(nested == 0 && "timer_cb must be called from a fresh stack"); puts("Timeout complete. Now read data..."); @@ -125,7 +125,7 @@ static void timer_cb(uv_timer_t* handle) { static void write_cb(uv_write_t* req, int status) { int r; - ASSERT(status == 0); + ASSERT_OK(status); ASSERT(nested == 0 && "write_cb must be called from a fresh stack"); puts("Data written. 500ms timeout..."); @@ -136,9 +136,9 @@ static void write_cb(uv_write_t* req, int status) { * for the backend to use dirty stack for calling read_cb. */ nested++; r = uv_timer_init(uv_default_loop(), &timer); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&timer, timer_cb, 500, 0); - ASSERT(r == 0); + ASSERT_OK(r); nested--; write_cb_called++; @@ -150,7 +150,7 @@ static void connect_cb(uv_connect_t* req, int status) { puts("Connected. Write some data to echo server..."); - ASSERT(status == 0); + ASSERT_OK(status); ASSERT(nested == 0 && "connect_cb must be called from a fresh stack"); nested++; @@ -171,7 +171,7 @@ static void connect_cb(uv_connect_t* req, int status) { TEST_IMPL(callback_stack) { struct sockaddr_in addr; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); if (uv_tcp_init(uv_default_loop(), &client)) { FATAL("uv_tcp_init failed"); @@ -191,14 +191,19 @@ TEST_IMPL(callback_stack) { uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(nested == 0); - ASSERT(connect_cb_called == 1 && "connect_cb must be called exactly once"); - ASSERT(write_cb_called == 1 && "write_cb must be called exactly once"); - ASSERT(timer_cb_called == 1 && "timer_cb must be called exactly once"); - ASSERT(bytes_received == sizeof MESSAGE); - ASSERT(shutdown_cb_called == 1 && "shutdown_cb must be called exactly once"); - ASSERT(close_cb_called == 2 && "close_cb must be called exactly twice"); - - MAKE_VALGRIND_HAPPY(); + ASSERT_OK(nested); + ASSERT_NE(connect_cb_called == 1 && \ + "connect_cb must be called exactly once", 0); + ASSERT_NE(write_cb_called == 1 && "write_cb must be called exactly once", + 0); + ASSERT_NE(timer_cb_called == 1 && "timer_cb must be called exactly once", + 0); + ASSERT_EQ(bytes_received, sizeof MESSAGE); + ASSERT_NE(shutdown_cb_called == 1 && \ + "shutdown_cb must be called exactly once", 0); + ASSERT_NE(close_cb_called == 2 && "close_cb must be called exactly twice", + 0); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-close-fd.c b/test/test-close-fd.c index 0d3927f652e..c072fdbb79a 100644 --- a/test/test-close-fd.c +++ b/test/test-close-fd.c @@ -36,11 +36,11 @@ static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { static void read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { switch (++read_cb_called) { case 1: - ASSERT(nread == 1); + ASSERT_EQ(1, nread); uv_read_stop(handle); break; case 2: - ASSERT(nread == UV_EOF); + ASSERT_EQ(nread, UV_EOF); uv_close((uv_handle_t *) handle, NULL); break; default: @@ -55,30 +55,30 @@ TEST_IMPL(close_fd) { uv_file fd[2]; bufs[0] = uv_buf_init("", 1); - ASSERT(0 == uv_pipe(fd, 0, 0)); - ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0)); - ASSERT(0 == uv_pipe_open(&pipe_handle, fd[0])); + ASSERT_OK(uv_pipe(fd, 0, 0)); + ASSERT_OK(uv_pipe_init(uv_default_loop(), &pipe_handle, 0)); + ASSERT_OK(uv_pipe_open(&pipe_handle, fd[0])); /* uv_pipe_open() takes ownership of the file descriptor. */ fd[0] = -1; - ASSERT(1 == uv_fs_write(NULL, &req, fd[1], bufs, 1, -1, NULL)); - ASSERT(1 == req.result); + ASSERT_EQ(1, uv_fs_write(NULL, &req, fd[1], bufs, 1, -1, NULL)); + ASSERT_EQ(1, req.result); uv_fs_req_cleanup(&req); #ifdef _WIN32 - ASSERT(0 == _close(fd[1])); + ASSERT_OK(_close(fd[1])); #else - ASSERT(0 == close(fd[1])); + ASSERT_OK(close(fd[1])); #endif fd[1] = -1; - ASSERT(0 == uv_read_start((uv_stream_t *) &pipe_handle, alloc_cb, read_cb)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(1 == read_cb_called); - ASSERT(0 == uv_is_active((const uv_handle_t *) &pipe_handle)); - ASSERT(0 == uv_read_start((uv_stream_t *) &pipe_handle, alloc_cb, read_cb)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(2 == read_cb_called); - ASSERT(0 != uv_is_closing((const uv_handle_t *) &pipe_handle)); + ASSERT_OK(uv_read_start((uv_stream_t *) &pipe_handle, alloc_cb, read_cb)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_EQ(1, read_cb_called); + ASSERT_OK(uv_is_active((const uv_handle_t *) &pipe_handle)); + ASSERT_OK(uv_read_start((uv_stream_t *) &pipe_handle, alloc_cb, read_cb)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_EQ(2, read_cb_called); + ASSERT_NE(0, uv_is_closing((const uv_handle_t *) &pipe_handle)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-close-order.c b/test/test-close-order.c index c2fd6c3d0de..e1aae93b7a8 100644 --- a/test/test-close-order.c +++ b/test/test-close-order.c @@ -39,9 +39,9 @@ static void close_cb(uv_handle_t* handle) { /* check_cb should run before any close_cb */ static void check_cb(uv_check_t* handle) { - ASSERT(check_cb_called == 0); - ASSERT(timer_cb_called == 1); - ASSERT(close_cb_called == 0); + ASSERT_OK(check_cb_called); + ASSERT_EQ(1, timer_cb_called); + ASSERT_OK(close_cb_called); uv_close((uv_handle_t*) handle, close_cb); uv_close((uv_handle_t*) &timer_handle2, close_cb); check_cb_called++; @@ -65,16 +65,16 @@ TEST_IMPL(close_order) { uv_timer_init(loop, &timer_handle2); uv_timer_start(&timer_handle2, timer_cb, 100000, 0); - ASSERT(check_cb_called == 0); - ASSERT(close_cb_called == 0); - ASSERT(timer_cb_called == 0); + ASSERT_OK(check_cb_called); + ASSERT_OK(close_cb_called); + ASSERT_OK(timer_cb_called); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(check_cb_called == 1); - ASSERT(close_cb_called == 3); - ASSERT(timer_cb_called == 1); + ASSERT_EQ(1, check_cb_called); + ASSERT_EQ(3, close_cb_called); + ASSERT_EQ(1, timer_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-condvar.c b/test/test-condvar.c index 32abccc2e76..2711f8cc1b3 100644 --- a/test/test-condvar.c +++ b/test/test-condvar.c @@ -55,10 +55,10 @@ void worker_config_init(worker_config* wc, wc->use_broadcast = use_broadcast; /* Init. */ - ASSERT(0 == uv_sem_init(&wc->sem_waiting, 0)); - ASSERT(0 == uv_sem_init(&wc->sem_signaled, 0)); - ASSERT(0 == uv_cond_init(&wc->cond)); - ASSERT(0 == uv_mutex_init(&wc->mutex)); + ASSERT_OK(uv_sem_init(&wc->sem_waiting, 0)); + ASSERT_OK(uv_sem_init(&wc->sem_signaled, 0)); + ASSERT_OK(uv_cond_init(&wc->cond)); + ASSERT_OK(uv_mutex_init(&wc->mutex)); } void worker_config_destroy(worker_config* wc) { @@ -87,7 +87,7 @@ static void condvar_signal(worker_config* c, int* flag) { uv_mutex_lock(&c->mutex); /* Help waiter differentiate between spurious and legitimate wakeup. */ - ASSERT(*flag == 0); + ASSERT_OK(*flag); *flag = 1; if (c->use_broadcast) @@ -113,7 +113,7 @@ static int condvar_wait(worker_config* c, const int* flag) { do { uv_cond_wait(&c->cond, &c->mutex); } while (*flag == 0); - ASSERT(*flag == 1); + ASSERT_EQ(1, *flag); uv_mutex_unlock(&c->mutex); @@ -130,13 +130,13 @@ TEST_IMPL(condvar_1) { /* Helper signal-then-wait. */ worker_config_init(&wc, 0, condvar_signal, condvar_wait); - ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + ASSERT_OK(uv_thread_create(&thread, worker, &wc)); /* We wait-then-signal. */ - ASSERT(0 == wc.wait_cond(&wc, &wc.posted_1)); + ASSERT_OK(wc.wait_cond(&wc, &wc.posted_1)); wc.signal_cond(&wc, &wc.posted_2); - ASSERT(0 == uv_thread_join(&thread)); + ASSERT_OK(uv_thread_join(&thread)); worker_config_destroy(&wc); return 0; @@ -149,13 +149,13 @@ TEST_IMPL(condvar_2) { /* Helper to signal-then-wait. */ worker_config_init(&wc, 1, condvar_signal, condvar_wait); - ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + ASSERT_OK(uv_thread_create(&thread, worker, &wc)); /* We wait-then-signal. */ - ASSERT(0 == wc.wait_cond(&wc, &wc.posted_1)); + ASSERT_OK(wc.wait_cond(&wc, &wc.posted_1)); wc.signal_cond(&wc, &wc.posted_2); - ASSERT(0 == uv_thread_join(&thread)); + ASSERT_OK(uv_thread_join(&thread)); worker_config_destroy(&wc); return 0; @@ -176,9 +176,9 @@ static int condvar_timedwait(worker_config* c, const int* flag) { /* Wait until I get a non-spurious signal. */ do { r = uv_cond_timedwait(&c->cond, &c->mutex, (uint64_t)(1 * 1e9)); /* 1 s */ - ASSERT(r == 0); /* Should not time out. */ + ASSERT_OK(r); /* Should not time out. */ } while (*flag == 0); - ASSERT(*flag == 1); + ASSERT_EQ(1, *flag); uv_mutex_unlock(&c->mutex); @@ -194,13 +194,13 @@ TEST_IMPL(condvar_3) { /* Helper to signal-then-wait. */ worker_config_init(&wc, 0, condvar_signal, condvar_timedwait); - ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + ASSERT_OK(uv_thread_create(&thread, worker, &wc)); /* We wait-then-signal. */ wc.wait_cond(&wc, &wc.posted_1); wc.signal_cond(&wc, &wc.posted_2); - ASSERT(0 == uv_thread_join(&thread)); + ASSERT_OK(uv_thread_join(&thread)); worker_config_destroy(&wc); return 0; @@ -213,13 +213,13 @@ TEST_IMPL(condvar_4) { /* Helper to signal-then-wait. */ worker_config_init(&wc, 1, condvar_signal, condvar_timedwait); - ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + ASSERT_OK(uv_thread_create(&thread, worker, &wc)); /* We wait-then-signal. */ wc.wait_cond(&wc, &wc.posted_1); wc.signal_cond(&wc, &wc.posted_2); - ASSERT(0 == uv_thread_join(&thread)); + ASSERT_OK(uv_thread_join(&thread)); worker_config_destroy(&wc); return 0; @@ -228,11 +228,6 @@ TEST_IMPL(condvar_4) { /* uv_cond_timedwait: One thread waits, no signal. Timeout should be delivered. */ TEST_IMPL(condvar_5) { worker_config wc; - int r; - /* ns */ - uint64_t before; - uint64_t after; - uint64_t elapsed; uint64_t timeout; timeout = 100 * 1000 * 1000; /* 100 ms in ns */ @@ -242,25 +237,11 @@ TEST_IMPL(condvar_5) { uv_mutex_lock(&wc.mutex); - /* We wait. - * No signaler, so this will only return if timeout is delivered. */ - before = uv_hrtime(); - r = uv_cond_timedwait(&wc.cond, &wc.mutex, timeout); - after = uv_hrtime(); + /* We wait. No signaler, so this will only return if timeout is delivered. */ + ASSERT_EQ(UV_ETIMEDOUT, uv_cond_timedwait(&wc.cond, &wc.mutex, timeout)); uv_mutex_unlock(&wc.mutex); - /* It timed out. */ - ASSERT(r == UV_ETIMEDOUT); - - /* It must have taken at least timeout, modulo system timer ticks. - * But it should not take too much longer. - * cf. MSDN docs: - * https://msdn.microsoft.com/en-us/library/ms687069(VS.85).aspx */ - elapsed = after - before; - ASSERT(0.75 * timeout <= elapsed); /* 1.0 too large for Windows. */ - ASSERT(elapsed <= 5.0 * timeout); /* MacOS has reported failures up to 1.75. */ - worker_config_destroy(&wc); return 0; diff --git a/test/test-connect-unspecified.c b/test/test-connect-unspecified.c index 5f32b67a6a4..73e59a9972b 100644 --- a/test/test-connect-unspecified.c +++ b/test/test-connect-unspecified.c @@ -23,11 +23,11 @@ #include "task.h" static void connect_4(uv_connect_t* req, int status) { - ASSERT(status != UV_EADDRNOTAVAIL); + ASSERT_NE(status, UV_EADDRNOTAVAIL); } static void connect_6(uv_connect_t* req, int status) { - ASSERT(status != UV_EADDRNOTAVAIL); + ASSERT_NE(status, UV_EADDRNOTAVAIL); } TEST_IMPL(connect_unspecified) { @@ -41,23 +41,24 @@ TEST_IMPL(connect_unspecified) { loop = uv_default_loop(); - ASSERT(uv_tcp_init(loop, &socket4) == 0); - ASSERT(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr4) == 0); - ASSERT(uv_tcp_connect(&connect4, - &socket4, - (const struct sockaddr*) &addr4, - connect_4) == 0); + ASSERT_OK(uv_tcp_init(loop, &socket4)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr4)); + ASSERT_OK(uv_tcp_connect(&connect4, + &socket4, + (const struct sockaddr*) &addr4, + connect_4)); if (can_ipv6()) { - ASSERT(uv_tcp_init(loop, &socket6) == 0); - ASSERT(uv_ip6_addr("::", TEST_PORT, &addr6) == 0); - ASSERT(uv_tcp_connect(&connect6, - &socket6, - (const struct sockaddr*) &addr6, - connect_6) == 0); + ASSERT_OK(uv_tcp_init(loop, &socket6)); + ASSERT_OK(uv_ip6_addr("::", TEST_PORT, &addr6)); + ASSERT_OK(uv_tcp_connect(&connect6, + &socket6, + (const struct sockaddr*) &addr6, + connect_6)); } - ASSERT(uv_run(loop, UV_RUN_DEFAULT) == 0); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-connection-fail.c b/test/test-connection-fail.c index 59048102529..9efd3ac907a 100644 --- a/test/test-connection-fail.c +++ b/test/test-connection-fail.c @@ -54,8 +54,8 @@ static void timer_cb(uv_timer_t* handle) { * but libuv hasn't automatically closed the socket. The user must * uv_close the handle manually. */ - ASSERT(close_cb_calls == 0); - ASSERT(connect_cb_calls == 1); + ASSERT_OK(close_cb_calls); + ASSERT_EQ(1, connect_cb_calls); /* Close the tcp handle. */ uv_close((uv_handle_t*)&tcp, on_close); @@ -66,22 +66,22 @@ static void timer_cb(uv_timer_t* handle) { static void on_connect_with_close(uv_connect_t *req, int status) { - ASSERT((uv_stream_t*) &tcp == req->handle); - ASSERT(status == UV_ECONNREFUSED); + ASSERT_PTR_EQ((uv_stream_t*) &tcp, req->handle); + ASSERT_EQ(status, UV_ECONNREFUSED); connect_cb_calls++; - ASSERT(close_cb_calls == 0); + ASSERT_OK(close_cb_calls); uv_close((uv_handle_t*)req->handle, on_close); } static void on_connect_without_close(uv_connect_t *req, int status) { - ASSERT(status == UV_ECONNREFUSED); + ASSERT_EQ(status, UV_ECONNREFUSED); connect_cb_calls++; uv_timer_start(&timer, timer_cb, 100, 0); - ASSERT(close_cb_calls == 0); + ASSERT_OK(close_cb_calls); } @@ -89,10 +89,10 @@ static void connection_fail(uv_connect_cb connect_cb) { struct sockaddr_in client_addr, server_addr; int r; - ASSERT(0 == uv_ip4_addr("0.0.0.0", 0, &client_addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", 0, &client_addr)); /* There should be no servers listening on this port. */ - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); /* Try to connect to the server and do NUM_PINGS ping-pongs. */ r = uv_tcp_init(uv_default_loop(), &tcp); @@ -100,7 +100,7 @@ static void connection_fail(uv_connect_cb connect_cb) { /* We are never doing multiple reads/connects at a time anyway. so these * handles can be pre-initialized. */ - ASSERT(0 == uv_tcp_bind(&tcp, (const struct sockaddr*) &client_addr, 0)); + ASSERT_OK(uv_tcp_bind(&tcp, (const struct sockaddr*) &client_addr, 0)); r = uv_tcp_connect(&req, &tcp, @@ -110,8 +110,8 @@ static void connection_fail(uv_connect_cb connect_cb) { uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(connect_cb_calls == 1); - ASSERT(close_cb_calls == 1); + ASSERT_EQ(1, connect_cb_calls); + ASSERT_EQ(1, close_cb_calls); } @@ -127,10 +127,10 @@ TEST_IMPL(connection_fail) { connection_fail(on_connect_with_close); - ASSERT(timer_close_cb_calls == 0); - ASSERT(timer_cb_calls == 0); + ASSERT_OK(timer_close_cb_calls); + ASSERT_OK(timer_cb_calls); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -149,13 +149,13 @@ TEST_IMPL(connection_fail_doesnt_auto_close) { int r; r = uv_timer_init(uv_default_loop(), &timer); - ASSERT(r == 0); + ASSERT_OK(r); connection_fail(on_connect_without_close); - ASSERT(timer_close_cb_calls == 1); - ASSERT(timer_cb_calls == 1); + ASSERT_EQ(1, timer_close_cb_calls); + ASSERT_EQ(1, timer_cb_calls); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-cwd-and-chdir.c b/test/test-cwd-and-chdir.c index faeed021030..e4a0eef0767 100644 --- a/test/test-cwd-and-chdir.c +++ b/test/test-cwd-and-chdir.c @@ -34,24 +34,24 @@ TEST_IMPL(cwd_and_chdir) { size1 = 1; err = uv_cwd(buffer_orig, &size1); - ASSERT(err == UV_ENOBUFS); - ASSERT(size1 > 1); + ASSERT_EQ(err, UV_ENOBUFS); + ASSERT_GT(size1, 1); size1 = sizeof buffer_orig; err = uv_cwd(buffer_orig, &size1); - ASSERT(err == 0); - ASSERT(size1 > 0); - ASSERT(buffer_orig[size1] != '/'); + ASSERT_OK(err); + ASSERT_GT(size1, 0); + ASSERT_NE(buffer_orig[size1], '/'); err = uv_chdir(buffer_orig); - ASSERT(err == 0); + ASSERT_OK(err); size2 = sizeof buffer_new; err = uv_cwd(buffer_new, &size2); - ASSERT(err == 0); + ASSERT_OK(err); - ASSERT(size1 == size2); - ASSERT(strcmp(buffer_orig, buffer_new) == 0); + ASSERT_EQ(size1, size2); + ASSERT_OK(strcmp(buffer_orig, buffer_new)); return 0; } diff --git a/test/test-default-loop-close.c b/test/test-default-loop-close.c index 51e1e7dc23b..d08a33ea551 100644 --- a/test/test-default-loop-close.c +++ b/test/test-default-loop-close.c @@ -39,21 +39,20 @@ TEST_IMPL(default_loop_close) { loop = uv_default_loop(); ASSERT_NOT_NULL(loop); - ASSERT(0 == uv_timer_init(loop, &timer_handle)); - ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 1, 0)); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(1 == timer_cb_called); - ASSERT(0 == uv_loop_close(loop)); + ASSERT_OK(uv_timer_init(loop, &timer_handle)); + ASSERT_OK(uv_timer_start(&timer_handle, timer_cb, 1, 0)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(1, timer_cb_called); + ASSERT_OK(uv_loop_close(loop)); loop = uv_default_loop(); ASSERT_NOT_NULL(loop); - ASSERT(0 == uv_timer_init(loop, &timer_handle)); - ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 1, 0)); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(2 == timer_cb_called); - ASSERT(0 == uv_loop_close(loop)); + ASSERT_OK(uv_timer_init(loop, &timer_handle)); + ASSERT_OK(uv_timer_start(&timer_handle, timer_cb, 1, 0)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(2, timer_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-delayed-accept.c b/test/test-delayed-accept.c index 88b31e26903..f7cf80ab7b3 100644 --- a/test/test-delayed-accept.c +++ b/test/test-delayed-accept.c @@ -54,11 +54,11 @@ static void do_accept(uv_timer_t* timer_handle) { ASSERT_NOT_NULL(accepted_handle); r = uv_tcp_init(uv_default_loop(), accepted_handle); - ASSERT(r == 0); + ASSERT_OK(r); server = (uv_tcp_t*)timer_handle->data; r = uv_accept((uv_stream_t*)server, (uv_stream_t*)accepted_handle); - ASSERT(r == 0); + ASSERT_OK(r); do_accept_called++; @@ -79,19 +79,19 @@ static void connection_cb(uv_stream_t* tcp, int status) { int r; uv_timer_t* timer_handle; - ASSERT(status == 0); + ASSERT_OK(status); timer_handle = (uv_timer_t*)malloc(sizeof *timer_handle); ASSERT_NOT_NULL(timer_handle); /* Accept the client after 1 second */ r = uv_timer_init(uv_default_loop(), timer_handle); - ASSERT(r == 0); + ASSERT_OK(r); timer_handle->data = tcp; r = uv_timer_start(timer_handle, do_accept, 1000, 0); - ASSERT(r == 0); + ASSERT_OK(r); connection_cb_called++; } @@ -102,16 +102,16 @@ static void start_server(void) { uv_tcp_t* server = (uv_tcp_t*)malloc(sizeof *server); int r; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); ASSERT_NOT_NULL(server); r = uv_tcp_init(uv_default_loop(), server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)server, 128, connection_cb); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -123,10 +123,10 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { } if (nread >= 0) { - ASSERT(nread == 0); + ASSERT_OK(nread); } else { ASSERT_NOT_NULL(tcp); - ASSERT(nread == UV_EOF); + ASSERT_EQ(nread, UV_EOF); uv_close((uv_handle_t*)tcp, close_cb); } } @@ -136,12 +136,12 @@ static void connect_cb(uv_connect_t* req, int status) { int r; ASSERT_NOT_NULL(req); - ASSERT(status == 0); + ASSERT_OK(status); /* Not that the server will send anything, but otherwise we'll never know * when the server closes the connection. */ r = uv_read_start((uv_stream_t*)(req->handle), alloc_cb, read_cb); - ASSERT(r == 0); + ASSERT_OK(r); connect_cb_called++; @@ -155,18 +155,18 @@ static void client_connect(void) { uv_connect_t* connect_req = malloc(sizeof *connect_req); int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); ASSERT_NOT_NULL(client); ASSERT_NOT_NULL(connect_req); r = uv_tcp_init(uv_default_loop(), client); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_connect(connect_req, client, (const struct sockaddr*) &addr, connect_cb); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -179,11 +179,11 @@ TEST_IMPL(delayed_accept) { uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(connection_cb_called == 2); - ASSERT(do_accept_called == 2); - ASSERT(connect_cb_called == 2); - ASSERT(close_cb_called == 7); + ASSERT_EQ(2, connection_cb_called); + ASSERT_EQ(2, do_accept_called); + ASSERT_EQ(2, connect_cb_called); + ASSERT_EQ(7, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-dlerror.c b/test/test-dlerror.c index a436ec016bf..dec0da3afc8 100644 --- a/test/test-dlerror.c +++ b/test/test-dlerror.c @@ -38,12 +38,14 @@ TEST_IMPL(dlerror) { ASSERT_NOT_NULL(strstr(msg, dlerror_no_error)); r = uv_dlopen(path, &lib); - ASSERT(r == -1); + ASSERT_EQ(r, -1); msg = uv_dlerror(&lib); ASSERT_NOT_NULL(msg); #if !defined(__OpenBSD__) && !defined(__QNX__) - ASSERT_NOT_NULL(strstr(msg, path)); + /* musl's libc.a does not support dlopen(), only libc.so does. */ + if (NULL == strstr(msg, "Dynamic loading not supported")) + ASSERT_NOT_NULL(strstr(msg, path)); #endif ASSERT_NULL(strstr(msg, dlerror_no_error)); @@ -51,7 +53,9 @@ TEST_IMPL(dlerror) { msg = uv_dlerror(&lib); ASSERT_NOT_NULL(msg); #if !defined(__OpenBSD__) && !defined(__QNX__) - ASSERT_NOT_NULL(strstr(msg, path)); + /* musl's libc.a does not support dlopen(), only libc.so does. */ + if (NULL == strstr(msg, "Dynamic loading not supported")) + ASSERT_NOT_NULL(strstr(msg, path)); #endif ASSERT_NULL(strstr(msg, dlerror_no_error)); diff --git a/test/test-eintr-handling.c b/test/test-eintr-handling.c index 1aaf623b789..1f75e77e251 100644 --- a/test/test-eintr-handling.c +++ b/test/test-eintr-handling.c @@ -48,13 +48,13 @@ struct thread_ctx { static void thread_main(void* arg) { int nwritten; - ASSERT(0 == kill(getpid(), SIGUSR1)); + ASSERT_OK(kill(getpid(), SIGUSR1)); do nwritten = write(pipe_fds[1], test_buf, sizeof(test_buf)); while (nwritten == -1 && errno == EINTR); - ASSERT(nwritten == sizeof(test_buf)); + ASSERT_EQ(nwritten, sizeof(test_buf)); } static void sig_func(uv_signal_t* handle, int signum) { @@ -70,24 +70,26 @@ TEST_IMPL(eintr_handling) { iov = uv_buf_init(buf, sizeof(buf)); loop = uv_default_loop(); - ASSERT(0 == uv_signal_init(loop, &signal)); - ASSERT(0 == uv_signal_start(&signal, sig_func, SIGUSR1)); + ASSERT_OK(uv_signal_init(loop, &signal)); + ASSERT_OK(uv_signal_start(&signal, sig_func, SIGUSR1)); - ASSERT(0 == pipe(pipe_fds)); - ASSERT(0 == uv_thread_create(&thread, thread_main, &ctx)); + ASSERT_OK(pipe(pipe_fds)); + ASSERT_OK(uv_thread_create(&thread, thread_main, &ctx)); nread = uv_fs_read(loop, &read_req, pipe_fds[0], &iov, 1, -1, NULL); - ASSERT(nread == sizeof(test_buf)); - ASSERT(0 == strcmp(buf, test_buf)); + ASSERT_EQ(nread, sizeof(test_buf)); + ASSERT_OK(strcmp(buf, test_buf)); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(0 == close(pipe_fds[0])); - ASSERT(0 == close(pipe_fds[1])); + ASSERT_OK(close(pipe_fds[0])); + ASSERT_OK(close(pipe_fds[1])); uv_close((uv_handle_t*) &signal, NULL); - MAKE_VALGRIND_HAPPY(); + ASSERT_OK(uv_thread_join(&thread)); + + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-embed.c b/test/test-embed.c index c6ddceb149d..6e9917239aa 100644 --- a/test/test-embed.c +++ b/test/test-embed.c @@ -1,4 +1,4 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. +/* Copyright libuv project contributors. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -25,115 +25,55 @@ #include #include -#ifndef HAVE_KQUEUE -# if defined(__APPLE__) || \ - defined(__DragonFly__) || \ - defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ - defined(__OpenBSD__) || \ - defined(__NetBSD__) -# define HAVE_KQUEUE 1 -# endif +#if !defined(_WIN32) && !defined(_AIX) +#include #endif -#ifndef HAVE_EPOLL -# if defined(__linux__) -# define HAVE_EPOLL 1 -# endif -#endif - -#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL) - -#if defined(HAVE_KQUEUE) -# include -# include -# include -#endif - -#if defined(HAVE_EPOLL) -# include -#endif - -static uv_thread_t embed_thread; -static uv_sem_t embed_sem; -static uv_timer_t embed_timer; -static uv_async_t embed_async; -static volatile int embed_closed; - -static int embed_timer_called; - - -static void embed_thread_runner(void* arg) { - int r; - int fd; - int timeout; - - while (!embed_closed) { - fd = uv_backend_fd(uv_default_loop()); - timeout = uv_backend_timeout(uv_default_loop()); - - do { -#if defined(HAVE_KQUEUE) - struct timespec ts; - ts.tv_sec = timeout / 1000; - ts.tv_nsec = (timeout % 1000) * 1000000; - r = kevent(fd, NULL, 0, NULL, 0, &ts); -#elif defined(HAVE_EPOLL) - { - struct epoll_event ev; - r = epoll_wait(fd, &ev, 1, timeout); - } -#endif - } while (r == -1 && errno == EINTR); - uv_async_send(&embed_async); - uv_sem_wait(&embed_sem); - } -} - +static uv_async_t async; +static uv_barrier_t barrier; -static void embed_cb(uv_async_t* async) { - uv_run(uv_default_loop(), UV_RUN_ONCE); - uv_sem_post(&embed_sem); +static void thread_main(void* arg) { + ASSERT_LE(0, uv_barrier_wait(&barrier)); + uv_sleep(250); + ASSERT_OK(uv_async_send(&async)); } -static void embed_timer_cb(uv_timer_t* timer) { - embed_timer_called++; - embed_closed = 1; - - uv_close((uv_handle_t*) &embed_async, NULL); +static void async_cb(uv_async_t* handle) { + uv_close((uv_handle_t*) handle, NULL); } -#endif TEST_IMPL(embed) { -#if defined(HAVE_KQUEUE) || defined(HAVE_EPOLL) - uv_loop_t external; - - ASSERT(0 == uv_loop_init(&external)); - - embed_timer_called = 0; - embed_closed = 0; - - uv_async_init(&external, &embed_async, embed_cb); - - /* Start timer in default loop */ - uv_timer_init(uv_default_loop(), &embed_timer); - uv_timer_start(&embed_timer, embed_timer_cb, 250, 0); - - /* Start worker that will interrupt external loop */ - uv_sem_init(&embed_sem, 0); - uv_thread_create(&embed_thread, embed_thread_runner, NULL); - - /* But run external loop */ - uv_run(&external, UV_RUN_DEFAULT); - - uv_thread_join(&embed_thread); - uv_loop_close(&external); - - ASSERT(embed_timer_called == 1); + uv_thread_t thread; + uv_loop_t* loop; + + loop = uv_default_loop(); + ASSERT_OK(uv_async_init(loop, &async, async_cb)); + ASSERT_OK(uv_barrier_init(&barrier, 2)); + ASSERT_OK(uv_thread_create(&thread, thread_main, NULL)); + ASSERT_LE(0, uv_barrier_wait(&barrier)); + + while (uv_loop_alive(loop)) { +#if defined(_WIN32) || defined(_AIX) + ASSERT_LE(0, uv_run(loop, UV_RUN_ONCE)); +#else + int rc; + do { + struct pollfd p; + p.fd = uv_backend_fd(loop); + p.events = POLLIN; + p.revents = 0; + rc = poll(&p, 1, uv_backend_timeout(loop)); + } while (rc == -1 && errno == EINTR); + ASSERT_LE(0, uv_run(loop, UV_RUN_NOWAIT)); #endif + } + + ASSERT_OK(uv_thread_join(&thread)); + uv_barrier_destroy(&barrier); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-emfile.c b/test/test-emfile.c index bc1fce5f559..3ad8378ca1e 100644 --- a/test/test-emfile.c +++ b/test/test-emfile.c @@ -54,37 +54,45 @@ TEST_IMPL(emfile) { /* Lower the file descriptor limit and use up all fds save one. */ limits.rlim_cur = limits.rlim_max = maxfd + 1; if (setrlimit(RLIMIT_NOFILE, &limits)) { - ASSERT(errno == EPERM); /* Valgrind blocks the setrlimit() call. */ + ASSERT_EQ(errno, EPERM); /* Valgrind blocks the setrlimit() call. */ RETURN_SKIP("setrlimit(RLIMIT_NOFILE) failed, running under valgrind?"); } loop = uv_default_loop(); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); - ASSERT(0 == uv_tcp_init(loop, &server_handle)); - ASSERT(0 == uv_tcp_init(loop, &client_handle)); - ASSERT(0 == uv_tcp_bind(&server_handle, (const struct sockaddr*) &addr, 0)); - ASSERT(0 == uv_listen((uv_stream_t*) &server_handle, 8, connection_cb)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_tcp_init(loop, &server_handle)); + ASSERT_OK(uv_tcp_init(loop, &client_handle)); + ASSERT_OK(uv_tcp_bind(&server_handle, (const struct sockaddr*) &addr, 0)); + ASSERT_OK(uv_listen((uv_stream_t*) &server_handle, 8, connection_cb)); /* Remember the first one so we can clean up afterwards. */ do first_fd = dup(0); while (first_fd == -1 && errno == EINTR); - ASSERT(first_fd > 0); + ASSERT_GT(first_fd, 0); while (dup(0) != -1 || errno == EINTR); - ASSERT(errno == EMFILE); + ASSERT_EQ(errno, EMFILE); close(maxfd); +#if defined(__ANDROID__) + /* Android connect syscall requires an extra file descriptor + * + * It fails in uv__tcp_connect + * */ + close(maxfd - 1); +#endif + /* Now connect and use up the last available file descriptor. The EMFILE * handling logic in src/unix/stream.c should ensure that connect_cb() runs * whereas connection_cb() should *not* run. */ - ASSERT(0 == uv_tcp_connect(&connect_req, - &client_handle, - (const struct sockaddr*) &addr, - connect_cb)); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(1 == connect_cb_called); + ASSERT_OK(uv_tcp_connect(&connect_req, + &client_handle, + (const struct sockaddr*) &addr, + connect_cb)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(1, connect_cb_called); /* Close the dups again. Ignore errors in the unlikely event that the * file descriptors were not contiguous. @@ -94,7 +102,7 @@ TEST_IMPL(emfile) { first_fd += 1; } - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -108,7 +116,7 @@ static void connect_cb(uv_connect_t* req, int status) { /* |status| should equal 0 because the connection should have been accepted, * it's just that the server immediately closes it again. */ - ASSERT(0 == status); + ASSERT_OK(status); connect_cb_called += 1; uv_close((uv_handle_t*) &server_handle, NULL); uv_close((uv_handle_t*) &client_handle, NULL); diff --git a/test/test-env-vars.c b/test/test-env-vars.c index ecaba337ca1..fd25ea26e14 100644 --- a/test/test-env-vars.c +++ b/test/test-env-vars.c @@ -33,85 +33,90 @@ TEST_IMPL(env_vars) { int i, r, envcount, found, found_win_special; uv_env_item_t* envitems; +#if defined(_WIN32) && defined(__ASAN__) + /* See investigation in https://github.com/libuv/libuv/issues/4338 */ + RETURN_SKIP("Test does not currently work on Windows under ASAN"); +#endif + /* Reject invalid inputs when setting an environment variable */ r = uv_os_setenv(NULL, "foo"); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_os_setenv(name, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_os_setenv(NULL, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); /* Reject invalid inputs when retrieving an environment variable */ size = BUF_SIZE; r = uv_os_getenv(NULL, buf, &size); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_os_getenv(name, NULL, &size); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_os_getenv(name, buf, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); size = 0; r = uv_os_getenv(name, buf, &size); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); /* Reject invalid inputs when deleting an environment variable */ r = uv_os_unsetenv(NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); /* Successfully set an environment variable */ r = uv_os_setenv(name, "123456789"); - ASSERT(r == 0); + ASSERT_OK(r); /* Successfully read an environment variable */ size = BUF_SIZE; buf[0] = '\0'; r = uv_os_getenv(name, buf, &size); - ASSERT(r == 0); - ASSERT(strcmp(buf, "123456789") == 0); - ASSERT(size == BUF_SIZE - 1); + ASSERT_OK(r); + ASSERT_OK(strcmp(buf, "123456789")); + ASSERT_EQ(size, BUF_SIZE - 1); /* Return UV_ENOBUFS if the buffer cannot hold the environment variable */ size = BUF_SIZE - 1; buf[0] = '\0'; r = uv_os_getenv(name, buf, &size); - ASSERT(r == UV_ENOBUFS); - ASSERT(size == BUF_SIZE); + ASSERT_EQ(r, UV_ENOBUFS); + ASSERT_EQ(size, BUF_SIZE); /* Successfully delete an environment variable */ r = uv_os_unsetenv(name); - ASSERT(r == 0); + ASSERT_OK(r); /* Return UV_ENOENT retrieving an environment variable that does not exist */ r = uv_os_getenv(name, buf, &size); - ASSERT(r == UV_ENOENT); + ASSERT_EQ(r, UV_ENOENT); /* Successfully delete an environment variable that does not exist */ r = uv_os_unsetenv(name); - ASSERT(r == 0); + ASSERT_OK(r); /* Setting an environment variable to the empty string does not delete it. */ r = uv_os_setenv(name, ""); - ASSERT(r == 0); + ASSERT_OK(r); size = BUF_SIZE; r = uv_os_getenv(name, buf, &size); - ASSERT(r == 0); - ASSERT(size == 0); - ASSERT(strlen(buf) == 0); + ASSERT_OK(r); + ASSERT_OK(size); + ASSERT_OK(strlen(buf)); /* Check getting all env variables. */ r = uv_os_setenv(name, "123456789"); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_os_setenv(name2, ""); - ASSERT(r == 0); + ASSERT_OK(r); #ifdef _WIN32 /* Create a special environment variable on Windows in case there are no naturally occurring ones. */ r = uv_os_setenv("=Z:", "\\"); - ASSERT(r == 0); + ASSERT_OK(r); #endif r = uv_os_environ(&envitems, &envcount); - ASSERT(r == 0); - ASSERT(envcount > 0); + ASSERT_OK(r); + ASSERT_GT(envcount, 0); found = 0; found_win_special = 0; @@ -120,27 +125,30 @@ TEST_IMPL(env_vars) { /* printf("Env: %s = %s\n", envitems[i].name, envitems[i].value); */ if (strcmp(envitems[i].name, name) == 0) { found++; - ASSERT(strcmp(envitems[i].value, "123456789") == 0); + ASSERT_OK(strcmp(envitems[i].value, "123456789")); } else if (strcmp(envitems[i].name, name2) == 0) { found++; - ASSERT(strlen(envitems[i].value) == 0); + ASSERT_OK(strlen(envitems[i].value)); } else if (envitems[i].name[0] == '=') { found_win_special++; } } - ASSERT(found == 2); + ASSERT_EQ(2, found); #ifdef _WIN32 - ASSERT(found_win_special > 0); + ASSERT_GT(found_win_special, 0); +#else + /* There's no rule saying a key can't start with '='. */ + (void) &found_win_special; #endif uv_os_free_environ(envitems, envcount); r = uv_os_unsetenv(name); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_os_unsetenv(name2); - ASSERT(r == 0); + ASSERT_OK(r); for (i = 1; i <= 4; i++) { size_t n; @@ -155,14 +163,14 @@ TEST_IMPL(env_vars) { memset(p, 'x', n); p[n] = '\0'; - ASSERT_EQ(0, uv_os_setenv(name, p)); - ASSERT_EQ(0, uv_os_getenv(name, p, &size)); + ASSERT_OK(uv_os_setenv(name, p)); + ASSERT_OK(uv_os_getenv(name, p, &size)); ASSERT_EQ(n, size); for (n = 0; n < size; n++) ASSERT_EQ('x', p[n]); - ASSERT_EQ(0, uv_os_unsetenv(name)); + ASSERT_OK(uv_os_unsetenv(name)); free(p); } diff --git a/test/test-error.c b/test/test-error.c index f0fb864607f..b6e18b0f052 100644 --- a/test/test-error.c +++ b/test/test-error.c @@ -51,8 +51,8 @@ TEST_IMPL(error_message) { } ASSERT_NULL(strstr(uv_strerror(UV_EINVAL), "Success")); - ASSERT(strcmp(uv_strerror(1337), "Unknown error") == 0); - ASSERT(strcmp(uv_strerror(-1337), "Unknown error") == 0); + ASSERT_OK(strcmp(uv_strerror(1337), "Unknown error")); + ASSERT_OK(strcmp(uv_strerror(-1337), "Unknown error")); ASSERT_NULL(strstr(uv_strerror_r(UV_EINVAL, buf, sizeof(buf)), "Success")); ASSERT_NOT_NULL(strstr(uv_strerror_r(1337, buf, sizeof(buf)), "1337")); @@ -64,19 +64,19 @@ TEST_IMPL(error_message) { TEST_IMPL(sys_error) { #if defined(_WIN32) - ASSERT(uv_translate_sys_error(ERROR_NOACCESS) == UV_EACCES); - ASSERT(uv_translate_sys_error(ERROR_ELEVATION_REQUIRED) == UV_EACCES); - ASSERT(uv_translate_sys_error(WSAEADDRINUSE) == UV_EADDRINUSE); - ASSERT(uv_translate_sys_error(ERROR_BAD_PIPE) == UV_EPIPE); + ASSERT_EQ(uv_translate_sys_error(ERROR_NOACCESS), UV_EFAULT); + ASSERT_EQ(uv_translate_sys_error(ERROR_ELEVATION_REQUIRED), UV_EACCES); + ASSERT_EQ(uv_translate_sys_error(WSAEADDRINUSE), UV_EADDRINUSE); + ASSERT_EQ(uv_translate_sys_error(ERROR_BAD_PIPE), UV_EPIPE); #else - ASSERT(uv_translate_sys_error(EPERM) == UV_EPERM); - ASSERT(uv_translate_sys_error(EPIPE) == UV_EPIPE); - ASSERT(uv_translate_sys_error(EINVAL) == UV_EINVAL); + ASSERT_EQ(uv_translate_sys_error(EPERM), UV_EPERM); + ASSERT_EQ(uv_translate_sys_error(EPIPE), UV_EPIPE); + ASSERT_EQ(uv_translate_sys_error(EINVAL), UV_EINVAL); #endif - ASSERT(uv_translate_sys_error(UV_EINVAL) == UV_EINVAL); - ASSERT(uv_translate_sys_error(UV_ERANGE) == UV_ERANGE); - ASSERT(uv_translate_sys_error(UV_EACCES) == UV_EACCES); - ASSERT(uv_translate_sys_error(0) == 0); + ASSERT_EQ(uv_translate_sys_error(UV_EINVAL), UV_EINVAL); + ASSERT_EQ(uv_translate_sys_error(UV_ERANGE), UV_ERANGE); + ASSERT_EQ(uv_translate_sys_error(UV_EACCES), UV_EACCES); + ASSERT_OK(uv_translate_sys_error(0)); return 0; } diff --git a/test/test-fork.c b/test/test-fork.c index 9e4684f0e15..fe42f03daf5 100644 --- a/test/test-fork.c +++ b/test/test-fork.c @@ -27,6 +27,10 @@ #include #include +#ifdef __APPLE__ +#include +#endif + #include "uv.h" #include "task.h" @@ -47,29 +51,30 @@ static char socket_cb_read_buf[1024]; static void socket_cb(uv_poll_t* poll, int status, int events) { ssize_t cnt; socket_cb_called++; - ASSERT(0 == status); + ASSERT_OK(status); printf("Socket cb got events %d\n", events); - ASSERT(UV_READABLE == (events & UV_READABLE)); + ASSERT_EQ(UV_READABLE, (events & UV_READABLE)); if (socket_cb_read_fd) { cnt = read(socket_cb_read_fd, socket_cb_read_buf, socket_cb_read_size); - ASSERT(cnt == socket_cb_read_size); + ASSERT_EQ(cnt, socket_cb_read_size); } uv_close((uv_handle_t*) poll, NULL); } static void run_timer_loop_once(void) { - uv_loop_t* loop; + uv_loop_t loop; uv_timer_t timer_handle; - loop = uv_default_loop(); + ASSERT_OK(uv_loop_init(&loop)); timer_cb_called = 0; /* Reset for the child. */ - ASSERT(0 == uv_timer_init(loop, &timer_handle)); - ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 1, 0)); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(1 == timer_cb_called); + ASSERT_OK(uv_timer_init(&loop, &timer_handle)); + ASSERT_OK(uv_timer_start(&timer_handle, timer_cb, 1, 0)); + ASSERT_OK(uv_run(&loop, UV_RUN_DEFAULT)); + ASSERT_EQ(1, timer_cb_called); + ASSERT_OK(uv_loop_close(&loop)); } @@ -82,10 +87,10 @@ static void assert_wait_child(pid_t child_pid) { if (waited_pid == -1) { perror("Failed to wait"); } - ASSERT(child_pid == waited_pid); + ASSERT_EQ(child_pid, waited_pid); ASSERT(WIFEXITED(child_stat)); /* Clean exit, not a signal. */ ASSERT(!WIFSIGNALED(child_stat)); - ASSERT(0 == WEXITSTATUS(child_stat)); + ASSERT_OK(WEXITSTATUS(child_stat)); } @@ -99,19 +104,23 @@ TEST_IMPL(fork_timer) { pid_t child_pid; run_timer_loop_once(); +#if defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH) + child_pid = -1; +#else child_pid = fork(); - ASSERT(child_pid != -1); +#endif + ASSERT_NE(child_pid, -1); if (child_pid != 0) { /* parent */ assert_wait_child(child_pid); } else { /* child */ - ASSERT(0 == uv_loop_fork(uv_default_loop())); + ASSERT_OK(uv_loop_fork(uv_default_loop())); run_timer_loop_once(); } - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -126,29 +135,33 @@ TEST_IMPL(fork_socketpair) { /* Prime the loop. */ run_timer_loop_once(); - ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds)); + ASSERT_OK(socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds)); /* Create the server watcher in the parent, use it in the child. */ - ASSERT(0 == uv_poll_init(uv_default_loop(), &poll_handle, socket_fds[0])); + ASSERT_OK(uv_poll_init(uv_default_loop(), &poll_handle, socket_fds[0])); +#if defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH) + child_pid = -1; +#else child_pid = fork(); - ASSERT(child_pid != -1); +#endif + ASSERT_NE(child_pid, -1); if (child_pid != 0) { /* parent */ - ASSERT(3 == send(socket_fds[1], "hi\n", 3, 0)); + ASSERT_EQ(3, send(socket_fds[1], "hi\n", 3, 0)); assert_wait_child(child_pid); } else { /* child */ - ASSERT(0 == uv_loop_fork(uv_default_loop())); - ASSERT(0 == socket_cb_called); - ASSERT(0 == uv_poll_start(&poll_handle, UV_READABLE, socket_cb)); + ASSERT_OK(uv_loop_fork(uv_default_loop())); + ASSERT_OK(socket_cb_called); + ASSERT_OK(uv_poll_start(&poll_handle, UV_READABLE, socket_cb)); printf("Going to run the loop in the child\n"); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(1 == socket_cb_called); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_EQ(1, socket_cb_called); } - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -163,56 +176,60 @@ TEST_IMPL(fork_socketpair_started) { char sync_buf[1]; uv_poll_t poll_handle; - ASSERT(0 == pipe(sync_pipe)); + ASSERT_OK(pipe(sync_pipe)); /* Prime the loop. */ run_timer_loop_once(); - ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds)); + ASSERT_OK(socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds)); /* Create and start the server watcher in the parent, use it in the child. */ - ASSERT(0 == uv_poll_init(uv_default_loop(), &poll_handle, socket_fds[0])); - ASSERT(0 == uv_poll_start(&poll_handle, UV_READABLE, socket_cb)); + ASSERT_OK(uv_poll_init(uv_default_loop(), &poll_handle, socket_fds[0])); + ASSERT_OK(uv_poll_start(&poll_handle, UV_READABLE, socket_cb)); /* Run the loop AFTER the poll watcher is registered to make sure it gets passed to the kernel. Use NOWAIT and expect a non-zero return to prove the poll watcher is active. */ - ASSERT(1 == uv_run(uv_default_loop(), UV_RUN_NOWAIT)); + ASSERT_EQ(1, uv_run(uv_default_loop(), UV_RUN_NOWAIT)); +#if defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH) + child_pid = -1; +#else child_pid = fork(); - ASSERT(child_pid != -1); +#endif + ASSERT_NE(child_pid, -1); if (child_pid != 0) { /* parent */ - ASSERT(0 == uv_poll_stop(&poll_handle)); + ASSERT_OK(uv_poll_stop(&poll_handle)); uv_close((uv_handle_t*)&poll_handle, NULL); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(0 == socket_cb_called); - ASSERT(1 == write(sync_pipe[1], "1", 1)); /* alert child */ - ASSERT(3 == send(socket_fds[1], "hi\n", 3, 0)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(socket_cb_called); + ASSERT_EQ(1, write(sync_pipe[1], "1", 1)); /* alert child */ + ASSERT_EQ(3, send(socket_fds[1], "hi\n", 3, 0)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(0 == socket_cb_called); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(socket_cb_called); assert_wait_child(child_pid); } else { /* child */ printf("Child is %d\n", getpid()); - ASSERT(1 == read(sync_pipe[0], sync_buf, 1)); /* wait for parent */ - ASSERT(0 == uv_loop_fork(uv_default_loop())); - ASSERT(0 == socket_cb_called); + ASSERT_EQ(1, read(sync_pipe[0], sync_buf, 1)); /* wait for parent */ + ASSERT_OK(uv_loop_fork(uv_default_loop())); + ASSERT_OK(socket_cb_called); printf("Going to run the loop in the child\n"); socket_cb_read_fd = socket_fds[0]; socket_cb_read_size = 3; - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(1 == socket_cb_called); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_EQ(1, socket_cb_called); printf("Buf %s\n", socket_cb_read_buf); - ASSERT(0 == strcmp("hi\n", socket_cb_read_buf)); + ASSERT_OK(strcmp("hi\n", socket_cb_read_buf)); } - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -236,40 +253,46 @@ TEST_IMPL(fork_signal_to_child) { fork_signal_cb_called = 0; /* reset */ - ASSERT(0 == pipe(sync_pipe)); + ASSERT_OK(pipe(sync_pipe)); /* Prime the loop. */ run_timer_loop_once(); - ASSERT(0 == uv_signal_init(uv_default_loop(), &signal_handle)); - ASSERT(0 == uv_signal_start(&signal_handle, fork_signal_to_child_cb, SIGUSR1)); + ASSERT_OK(uv_signal_init(uv_default_loop(), &signal_handle)); + ASSERT_OK(uv_signal_start(&signal_handle, + fork_signal_to_child_cb, + SIGUSR1)); +#if defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH) + child_pid = -1; +#else child_pid = fork(); - ASSERT(child_pid != -1); +#endif + ASSERT_NE(child_pid, -1); if (child_pid != 0) { /* parent */ - ASSERT(1 == read(sync_pipe[0], sync_buf, 1)); /* wait for child */ - ASSERT(0 == kill(child_pid, SIGUSR1)); + ASSERT_EQ(1, read(sync_pipe[0], sync_buf, 1)); /* wait for child */ + ASSERT_OK(kill(child_pid, SIGUSR1)); /* Run the loop, make sure we don't get the signal. */ printf("Running loop in parent\n"); uv_unref((uv_handle_t*)&signal_handle); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_NOWAIT)); - ASSERT(0 == fork_signal_cb_called); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_NOWAIT)); + ASSERT_OK(fork_signal_cb_called); printf("Waiting for child in parent\n"); assert_wait_child(child_pid); } else { /* child */ - ASSERT(0 == uv_loop_fork(uv_default_loop())); - ASSERT(1 == write(sync_pipe[1], "1", 1)); /* alert parent */ + ASSERT_OK(uv_loop_fork(uv_default_loop())); + ASSERT_EQ(1, write(sync_pipe[1], "1", 1)); /* alert parent */ /* Get the signal. */ - ASSERT(0 != uv_loop_alive(uv_default_loop())); + ASSERT_NE(0, uv_loop_alive(uv_default_loop())); printf("Running loop in child\n"); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); - ASSERT(SIGUSR1 == fork_signal_cb_called); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_ONCE)); + ASSERT_EQ(SIGUSR1, fork_signal_cb_called); } - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -287,43 +310,49 @@ TEST_IMPL(fork_signal_to_child_closed) { fork_signal_cb_called = 0; /* reset */ - ASSERT(0 == pipe(sync_pipe)); - ASSERT(0 == pipe(sync_pipe2)); + ASSERT_OK(pipe(sync_pipe)); + ASSERT_OK(pipe(sync_pipe2)); /* Prime the loop. */ run_timer_loop_once(); - ASSERT(0 == uv_signal_init(uv_default_loop(), &signal_handle)); - ASSERT(0 == uv_signal_start(&signal_handle, fork_signal_to_child_cb, SIGUSR1)); + ASSERT_OK(uv_signal_init(uv_default_loop(), &signal_handle)); + ASSERT_OK(uv_signal_start(&signal_handle, + fork_signal_to_child_cb, + SIGUSR1)); +#if defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH) + child_pid = -1; +#else child_pid = fork(); - ASSERT(child_pid != -1); +#endif + ASSERT_NE(child_pid, -1); if (child_pid != 0) { /* parent */ printf("Wating on child in parent\n"); - ASSERT(1 == read(sync_pipe[0], sync_buf, 1)); /* wait for child */ + ASSERT_EQ(1, read(sync_pipe[0], sync_buf, 1)); /* wait for child */ printf("Parent killing child\n"); - ASSERT(0 == kill(child_pid, SIGUSR1)); + ASSERT_OK(kill(child_pid, SIGUSR1)); /* Run the loop, make sure we don't get the signal. */ printf("Running loop in parent\n"); uv_unref((uv_handle_t*)&signal_handle); /* so the loop can exit; we *shouldn't* get any signals */ run_timer_loop_once(); /* but while we share a pipe, we do, so have something active. */ - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_ONCE)); printf("Signal in parent %d\n", fork_signal_cb_called); - ASSERT(0 == fork_signal_cb_called); - ASSERT(1 == write(sync_pipe2[1], "1", 1)); /* alert child */ + ASSERT_OK(fork_signal_cb_called); + ASSERT_EQ(1, write(sync_pipe2[1], "1", 1)); /* alert child */ printf("Waiting for child in parent\n"); assert_wait_child(child_pid); } else { /* Child. Our signal handler should still be installed. */ - ASSERT(0 == uv_loop_fork(uv_default_loop())); + ASSERT_OK(uv_loop_fork(uv_default_loop())); printf("Checking loop in child\n"); - ASSERT(0 != uv_loop_alive(uv_default_loop())); + ASSERT_NE(0, uv_loop_alive(uv_default_loop())); printf("Alerting parent in child\n"); - ASSERT(1 == write(sync_pipe[1], "1", 1)); /* alert parent */ + ASSERT_EQ(1, write(sync_pipe[1], "1", 1)); /* alert parent */ /* Don't run the loop. Wait for the parent to call us */ printf("Waiting on parent in child\n"); /* Wait for parent. read may fail if the parent tripped an ASSERT @@ -331,7 +360,7 @@ TEST_IMPL(fork_signal_to_child_closed) { */ r = read(sync_pipe2[0], sync_buf, 1); ASSERT(-1 <= r && r <= 1); - ASSERT(0 == fork_signal_cb_called); + ASSERT_OK(fork_signal_cb_called); printf("Exiting child \n"); /* Note that we're deliberately not running the loop * in the child, and also not closing the loop's handles, @@ -342,7 +371,48 @@ TEST_IMPL(fork_signal_to_child_closed) { exit(0); } - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} + +static void fork_signal_cb(uv_signal_t* h, int s) { + fork_signal_cb_called = s; +} +static void empty_close_cb(uv_handle_t* h){} + +TEST_IMPL(fork_close_signal_in_child) { + uv_loop_t loop; + uv_signal_t signal_handle; + pid_t child_pid; + + ASSERT_OK(uv_loop_init(&loop)); + ASSERT_OK(uv_signal_init(&loop, &signal_handle)); + ASSERT_OK(uv_signal_start(&signal_handle, &fork_signal_cb, SIGHUP)); + + ASSERT_OK(kill(getpid(), SIGHUP)); + child_pid = fork(); + ASSERT_NE(child_pid, -1); + ASSERT_OK(fork_signal_cb_called); + + if (!child_pid) { + uv_loop_fork(&loop); + uv_close((uv_handle_t*)&signal_handle, &empty_close_cb); + uv_run(&loop, UV_RUN_DEFAULT); + /* Child doesn't receive the signal */ + ASSERT_OK(fork_signal_cb_called); + } else { + /* Parent. Runing once to receive the signal */ + uv_run(&loop, UV_RUN_ONCE); + ASSERT_EQ(SIGHUP, fork_signal_cb_called); + + /* loop should stop after closing the only handle */ + uv_close((uv_handle_t*)&signal_handle, &empty_close_cb); + ASSERT_OK(uv_run(&loop, UV_RUN_DEFAULT)); + + assert_wait_child(child_pid); + } + + MAKE_VALGRIND_HAPPY(&loop); return 0; } @@ -353,11 +423,11 @@ static void create_file(const char* name) { uv_fs_t req; r = uv_fs_open(NULL, &req, name, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); + ASSERT_GE(r, 0); file = r; uv_fs_req_cleanup(&req); r = uv_fs_close(NULL, &req, file, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&req); } @@ -369,17 +439,17 @@ static void touch_file(const char* name) { uv_buf_t buf; r = uv_fs_open(NULL, &req, name, O_RDWR, 0, NULL); - ASSERT(r >= 0); + ASSERT_GE(r, 0); file = r; uv_fs_req_cleanup(&req); buf = uv_buf_init("foo", 4); r = uv_fs_write(NULL, &req, file, &buf, 1, -1, NULL); - ASSERT(r >= 0); + ASSERT_GE(r, 0); uv_fs_req_cleanup(&req); r = uv_fs_close(NULL, &req, file, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&req); } @@ -399,11 +469,11 @@ static void fs_event_cb_file_current_dir(uv_fs_event_t* handle, const char* filename, int events, int status) { - ASSERT(fs_event_cb_called == 0); + ASSERT_OK(fs_event_cb_called); ++fs_event_cb_called; - ASSERT(status == 0); + ASSERT_OK(status); #if defined(__APPLE__) || defined(__linux__) - ASSERT(strcmp(filename, "watch_file") == 0); + ASSERT_OK(strcmp(filename, "watch_file")); #else ASSERT(filename == NULL || strcmp(filename, "watch_file") == 0); #endif @@ -421,28 +491,28 @@ static void assert_watch_file_current_dir(uv_loop_t* const loop, int file_or_dir create_file("watch_file"); r = uv_fs_event_init(loop, &fs_event); - ASSERT(r == 0); + ASSERT_OK(r); /* watching a dir is the only way to get fsevents involved on apple platforms */ r = uv_fs_event_start(&fs_event, fs_event_cb_file_current_dir, file_or_dir == 1 ? "." : "watch_file", 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_init(loop, &timer); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&timer, timer_cb_touch, 100, 0); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(timer_cb_touch_called == 0); - ASSERT(fs_event_cb_called == 0); + ASSERT_OK(timer_cb_touch_called); + ASSERT_OK(fs_event_cb_called); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(timer_cb_touch_called == 1); - ASSERT(fs_event_cb_called == 1); + ASSERT_EQ(1, timer_cb_touch_called); + ASSERT_EQ(1, fs_event_cb_called); /* Cleanup */ remove("watch_file"); @@ -462,8 +532,12 @@ static int _do_fork_fs_events_child(int file_or_dir) { /* Watch in the parent, prime the loop and/or threads. */ assert_watch_file_current_dir(uv_default_loop(), file_or_dir); +#if defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH) + child_pid = -1; +#else child_pid = fork(); - ASSERT(child_pid != -1); +#endif + ASSERT_NE(child_pid, -1); if (child_pid != 0) { /* parent */ @@ -479,10 +553,10 @@ static int _do_fork_fs_events_child(int file_or_dir) { uv_loop_init(&loop); printf("Child first watch\n"); assert_watch_file_current_dir(&loop, file_or_dir); - ASSERT(0 == uv_loop_close(&loop)); + ASSERT_OK(uv_loop_close(&loop)); printf("Child second watch default loop\n"); /* Ee can watch in the default loop. */ - ASSERT(0 == uv_loop_fork(uv_default_loop())); + ASSERT_OK(uv_loop_fork(uv_default_loop())); /* On some platforms (OS X), if we don't update the time now, * the timer cb fires before the event loop enters uv__io_poll, * instead of after, meaning we don't see the change! This may be @@ -495,12 +569,12 @@ static int _do_fork_fs_events_child(int file_or_dir) { especially important on Apple platforms where if we're not careful trying to touch the CFRunLoop, even just to shut it down, that we allocated in the FS_TEST_DIR case would crash. */ - ASSERT(0 == uv_loop_close(uv_default_loop())); + ASSERT_OK(uv_loop_close(uv_default_loop())); printf("Exiting child \n"); } - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -558,36 +632,40 @@ TEST_IMPL(fork_fs_events_file_parent_child) { create_file("watch_file"); r = uv_fs_event_init(loop, &fs_event); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_event_start(&fs_event, fs_event_cb_file_current_dir, "watch_file", 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_init(loop, &timer); - ASSERT(r == 0); + ASSERT_OK(r); +#if defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH) + child_pid = -1; +#else child_pid = fork(); - ASSERT(child_pid != -1); +#endif + ASSERT_NE(child_pid, -1); if (child_pid != 0) { /* parent */ assert_wait_child(child_pid); } else { /* child */ printf("Running child\n"); - ASSERT(0 == uv_loop_fork(loop)); + ASSERT_OK(uv_loop_fork(loop)); r = uv_timer_start(&timer, timer_cb_touch, 100, 0); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(timer_cb_touch_called == 0); - ASSERT(fs_event_cb_called == 0); + ASSERT_OK(timer_cb_touch_called); + ASSERT_OK(fs_event_cb_called); printf("Running loop in child \n"); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(timer_cb_touch_called == 1); - ASSERT(fs_event_cb_called == 1); + ASSERT_EQ(1, timer_cb_touch_called); + ASSERT_EQ(1, fs_event_cb_called); /* Cleanup */ remove("watch_file"); @@ -597,7 +675,7 @@ TEST_IMPL(fork_fs_events_file_parent_child) { } - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; #endif } @@ -613,7 +691,7 @@ static void work_cb(uv_work_t* req) { static void after_work_cb(uv_work_t* req, int status) { - ASSERT(status == 0); + ASSERT_OK(status); after_work_cb_count++; } @@ -622,16 +700,16 @@ static void assert_run_work(uv_loop_t* const loop) { uv_work_t work_req; int r; - ASSERT(work_cb_count == 0); - ASSERT(after_work_cb_count == 0); + ASSERT_OK(work_cb_count); + ASSERT_OK(after_work_cb_count); printf("Queue in %d\n", getpid()); r = uv_queue_work(loop, &work_req, work_cb, after_work_cb); - ASSERT(r == 0); + ASSERT_OK(r); printf("Running in %d\n", getpid()); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(work_cb_count == 1); - ASSERT(after_work_cb_count == 1); + ASSERT_EQ(1, work_cb_count); + ASSERT_EQ(1, after_work_cb_count); /* cleanup */ work_cb_count = 0; @@ -646,11 +724,19 @@ TEST_IMPL(fork_threadpool_queue_work_simple) { pid_t child_pid; uv_loop_t loop; +#ifdef __TSAN__ + RETURN_SKIP("ThreadSanitizer doesn't support multi-threaded fork"); +#endif + /* Prime the pool and default loop. */ assert_run_work(uv_default_loop()); +#if defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH) + child_pid = -1; +#else child_pid = fork(); - ASSERT(child_pid != -1); +#endif + ASSERT_NE(child_pid, -1); if (child_pid != 0) { /* Parent. We can still run work. */ @@ -665,13 +751,13 @@ TEST_IMPL(fork_threadpool_queue_work_simple) { uv_loop_close(&loop); printf("Child second watch default loop\n"); /* We can work in the default loop. */ - ASSERT(0 == uv_loop_fork(uv_default_loop())); + ASSERT_OK(uv_loop_fork(uv_default_loop())); assert_run_work(uv_default_loop()); printf("Exiting child \n"); } - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } #endif /* !__MVS__ */ diff --git a/test/test-fs-copyfile.c b/test/test-fs-copyfile.c index c785a4b51fb..f7a0c2363e8 100644 --- a/test/test-fs-copyfile.c +++ b/test/test-fs-copyfile.c @@ -46,21 +46,27 @@ static void handle_result(uv_fs_t* req) { uv_fs_t stat_req; uint64_t size; uint64_t mode; + uint64_t uid; + uint64_t gid; int r; - ASSERT(req->fs_type == UV_FS_COPYFILE); - ASSERT(req->result == 0); + ASSERT_EQ(req->fs_type, UV_FS_COPYFILE); + ASSERT_OK(req->result); /* Verify that the file size and mode are the same. */ r = uv_fs_stat(NULL, &stat_req, req->path, NULL); - ASSERT(r == 0); + ASSERT_OK(r); size = stat_req.statbuf.st_size; mode = stat_req.statbuf.st_mode; + uid = stat_req.statbuf.st_uid; + gid = stat_req.statbuf.st_gid; uv_fs_req_cleanup(&stat_req); r = uv_fs_stat(NULL, &stat_req, dst, NULL); - ASSERT(r == 0); - ASSERT(stat_req.statbuf.st_size == size); - ASSERT(stat_req.statbuf.st_mode == mode); + ASSERT_OK(r); + ASSERT_EQ(stat_req.statbuf.st_size, size); + ASSERT_EQ(stat_req.statbuf.st_mode, mode); + ASSERT_EQ(stat_req.statbuf.st_uid, uid); + ASSERT_EQ(stat_req.statbuf.st_gid, gid); uv_fs_req_cleanup(&stat_req); uv_fs_req_cleanup(req); result_check_count++; @@ -74,10 +80,11 @@ static void touch_file(const char* name, unsigned int size) { int r; unsigned int i; - r = uv_fs_open(NULL, &req, name, O_WRONLY | O_CREAT | O_TRUNC, + r = uv_fs_open(NULL, &req, name, + UV_FS_O_WRONLY | UV_FS_O_CREAT | UV_FS_O_TRUNC, S_IWUSR | S_IRUSR, NULL); uv_fs_req_cleanup(&req); - ASSERT(r >= 0); + ASSERT_GE(r, 0); file = r; buf = uv_buf_init("a", 1); @@ -86,12 +93,12 @@ static void touch_file(const char* name, unsigned int size) { for (i = 0; i < size; i++) { r = uv_fs_write(NULL, &req, file, &buf, 1, i, NULL); uv_fs_req_cleanup(&req); - ASSERT(r >= 0); + ASSERT_GE(r, 0); } r = uv_fs_close(NULL, &req, file, NULL); uv_fs_req_cleanup(&req); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -105,95 +112,102 @@ TEST_IMPL(fs_copyfile) { /* Fails with EINVAL if bad flags are passed. */ r = uv_fs_copyfile(NULL, &req, src, dst, -1, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_fs_req_cleanup(&req); /* Fails with ENOENT if source does not exist. */ unlink(src); unlink(dst); r = uv_fs_copyfile(NULL, &req, src, dst, 0, NULL); - ASSERT(req.result == UV_ENOENT); - ASSERT(r == UV_ENOENT); + ASSERT_EQ(req.result, UV_ENOENT); + ASSERT_EQ(r, UV_ENOENT); uv_fs_req_cleanup(&req); /* The destination should not exist. */ r = uv_fs_stat(NULL, &req, dst, NULL); - ASSERT(r != 0); + ASSERT(r); uv_fs_req_cleanup(&req); /* Succeeds if src and dst files are identical. */ touch_file(src, 12); r = uv_fs_copyfile(NULL, &req, src, src, 0, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&req); /* Verify that the src file did not get truncated. */ r = uv_fs_stat(NULL, &req, src, NULL); - ASSERT_EQ(r, 0); - ASSERT_EQ(req.statbuf.st_size, 12); + ASSERT_OK(r); + ASSERT_EQ(12, req.statbuf.st_size); uv_fs_req_cleanup(&req); unlink(src); /* Copies file synchronously. Creates new file. */ unlink(dst); r = uv_fs_copyfile(NULL, &req, fixture, dst, 0, NULL); - ASSERT(r == 0); + ASSERT_OK(r); handle_result(&req); /* Copies a file of size zero. */ unlink(dst); touch_file(src, 0); r = uv_fs_copyfile(NULL, &req, src, dst, 0, NULL); - ASSERT(r == 0); + ASSERT_OK(r); handle_result(&req); /* Copies file synchronously. Overwrites existing file. */ r = uv_fs_copyfile(NULL, &req, fixture, dst, 0, NULL); - ASSERT(r == 0); + ASSERT_OK(r); handle_result(&req); /* Fails to overwrites existing file. */ + ASSERT_OK(uv_fs_chmod(NULL, &req, dst, 0644, NULL)); + uv_fs_req_cleanup(&req); r = uv_fs_copyfile(NULL, &req, fixture, dst, UV_FS_COPYFILE_EXCL, NULL); - ASSERT(r == UV_EEXIST); + ASSERT_EQ(r, UV_EEXIST); uv_fs_req_cleanup(&req); /* Truncates when an existing destination is larger than the source file. */ + ASSERT_OK(uv_fs_chmod(NULL, &req, dst, 0644, NULL)); + uv_fs_req_cleanup(&req); touch_file(src, 1); r = uv_fs_copyfile(NULL, &req, src, dst, 0, NULL); - ASSERT(r == 0); + ASSERT_OK(r); handle_result(&req); /* Copies a larger file. */ unlink(dst); touch_file(src, 4096 * 2); r = uv_fs_copyfile(NULL, &req, src, dst, 0, NULL); - ASSERT(r == 0); + ASSERT_OK(r); handle_result(&req); unlink(src); /* Copies file asynchronously */ unlink(dst); r = uv_fs_copyfile(loop, &req, fixture, dst, 0, handle_result); - ASSERT(r == 0); - ASSERT(result_check_count == 5); + ASSERT_OK(r); + ASSERT_EQ(5, result_check_count); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(result_check_count == 6); + ASSERT_EQ(6, result_check_count); + /* Ensure file is user-writable (not copied from src). */ + ASSERT_OK(uv_fs_chmod(NULL, &req, dst, 0644, NULL)); + uv_fs_req_cleanup(&req); /* If the flags are invalid, the loop should not be kept open */ unlink(dst); r = uv_fs_copyfile(loop, &req, fixture, dst, -1, fail_cb); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_run(loop, UV_RUN_DEFAULT); /* Copies file using UV_FS_COPYFILE_FICLONE. */ unlink(dst); r = uv_fs_copyfile(NULL, &req, fixture, dst, UV_FS_COPYFILE_FICLONE, NULL); - ASSERT(r == 0); + ASSERT_OK(r); handle_result(&req); /* Copies file using UV_FS_COPYFILE_FICLONE_FORCE. */ unlink(dst); r = uv_fs_copyfile(NULL, &req, fixture, dst, UV_FS_COPYFILE_FICLONE_FORCE, NULL); - ASSERT(r <= 0); + ASSERT_LE(r, 0); if (r == 0) handle_result(&req); @@ -206,12 +220,13 @@ TEST_IMPL(fs_copyfile) { r = uv_fs_copyfile(NULL, &req, fixture, dst, 0, NULL); /* On IBMi PASE, qsecofr users can overwrite read-only files */ # ifndef __PASE__ - ASSERT(req.result == UV_EACCES); - ASSERT(r == UV_EACCES); + ASSERT_EQ(req.result, UV_EACCES); + ASSERT_EQ(r, UV_EACCES); # endif uv_fs_req_cleanup(&req); #endif unlink(dst); /* Cleanup */ + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-fs-event.c b/test/test-fs-event.c index cbe63190780..b53057dc25b 100644 --- a/test/test-fs-event.c +++ b/test/test-fs-event.c @@ -29,23 +29,6 @@ # include #endif -#ifndef HAVE_KQUEUE -# if defined(__APPLE__) || \ - defined(__DragonFly__) || \ - defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ - defined(__OpenBSD__) || \ - defined(__NetBSD__) -# define HAVE_KQUEUE 1 -# endif -#endif - -#if defined(__arm__)/* Increase the timeout so the test passes on arm CI bots */ -# define CREATE_TIMEOUT 100 -#else -# define CREATE_TIMEOUT 1 -#endif - static uv_fs_event_t fs_event; static const char file_prefix[] = "fsevent-"; static const int fs_event_file_count = 16; @@ -87,13 +70,31 @@ static void create_file(const char* name) { uv_file file; uv_fs_t req; - r = uv_fs_open(NULL, &req, name, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); + r = uv_fs_open(NULL, &req, name, UV_FS_O_WRONLY | UV_FS_O_CREAT, + S_IWUSR | S_IRUSR, + NULL); + ASSERT_GE(r, 0); file = r; uv_fs_req_cleanup(&req); r = uv_fs_close(NULL, &req, file, NULL); - ASSERT(r == 0); + ASSERT_OK(r); + uv_fs_req_cleanup(&req); +} + +static int delete_dir(const char* name) { + int r; + uv_fs_t req; + r = uv_fs_rmdir(NULL, &req, name, NULL); uv_fs_req_cleanup(&req); + return r; +} + +static int delete_file(const char* name) { + int r; + uv_fs_t req; + r = uv_fs_unlink(NULL, &req, name, NULL); + uv_fs_req_cleanup(&req); + return r; } static void touch_file(const char* name) { @@ -102,18 +103,18 @@ static void touch_file(const char* name) { uv_fs_t req; uv_buf_t buf; - r = uv_fs_open(NULL, &req, name, O_RDWR, 0, NULL); - ASSERT(r >= 0); + r = uv_fs_open(NULL, &req, name, UV_FS_O_RDWR, 0, NULL); + ASSERT_GE(r, 0); file = r; uv_fs_req_cleanup(&req); buf = uv_buf_init("foo", 4); r = uv_fs_write(NULL, &req, file, &buf, 1, -1, NULL); - ASSERT(r >= 0); + ASSERT_GE(r, 0); uv_fs_req_cleanup(&req); r = uv_fs_close(NULL, &req, file, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&req); } @@ -132,15 +133,35 @@ static void fail_cb(uv_fs_event_t* handle, static void fs_event_cb_dir(uv_fs_event_t* handle, const char* filename, int events, int status) { ++fs_event_cb_called; - ASSERT(handle == &fs_event); - ASSERT(status == 0); - ASSERT(events == UV_CHANGE); + ASSERT_PTR_EQ(handle, &fs_event); + ASSERT_OK(status); + ASSERT_EQ(events, UV_CHANGE); #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__) - ASSERT(strcmp(filename, "file1") == 0); + ASSERT_OK(strcmp(filename, "file1")); #else ASSERT(filename == NULL || strcmp(filename, "file1") == 0); #endif - ASSERT(0 == uv_fs_event_stop(handle)); + ASSERT_OK(uv_fs_event_stop(handle)); + uv_close((uv_handle_t*)handle, close_cb); +} + +static void fs_event_cb_del_dir(uv_fs_event_t* handle, + const char* filename, + int events, + int status) { + ++fs_event_cb_called; + ASSERT_PTR_EQ(handle, &fs_event); + ASSERT_OK(status); + ASSERT(events == UV_CHANGE || events == UV_RENAME); + /* There is a bug in the FreeBSD kernel where the filename is sometimes NULL. + * Refs: https://github.com/libuv/libuv/issues/4606 + */ + #if defined(__FreeBSD__) + ASSERT(filename == NULL || strcmp(filename, "watch_del_dir") == 0); + #else + ASSERT_OK(strcmp(filename, "watch_del_dir")); + #endif + ASSERT_OK(uv_fs_event_stop(handle)); uv_close((uv_handle_t*)handle, close_cb); } @@ -155,7 +176,7 @@ static const char* fs_event_get_filename(int i) { static void fs_event_create_files(uv_timer_t* handle) { /* Make sure we're not attempting to create files we do not intend */ - ASSERT(fs_event_created < fs_event_file_count); + ASSERT_LT(fs_event_created, fs_event_file_count); /* Create the file */ create_file(fs_event_get_filename(fs_event_created)); @@ -163,13 +184,19 @@ static void fs_event_create_files(uv_timer_t* handle) { if (++fs_event_created < fs_event_file_count) { /* Create another file on a different event loop tick. We do it this way * to avoid fs events coalescing into one fs event. */ - ASSERT(0 == uv_timer_start(&timer, - fs_event_create_files, - CREATE_TIMEOUT, - 0)); + ASSERT_OK(uv_timer_start(&timer, fs_event_create_files, 100, 0)); } } +static void fs_event_del_dir(uv_timer_t* handle) { + int r; + + r = delete_dir("watch_del_dir"); + ASSERT_OK(r); + + uv_close((uv_handle_t*)handle, close_cb); +} + static void fs_event_unlink_files(uv_timer_t* handle) { int r; int i; @@ -178,21 +205,21 @@ static void fs_event_unlink_files(uv_timer_t* handle) { if (handle == NULL) { /* Unlink all files */ for (i = 0; i < 16; i++) { - r = remove(fs_event_get_filename(i)); + r = delete_file(fs_event_get_filename(i)); if (handle != NULL) - ASSERT(r == 0); + ASSERT_OK(r); } } else { /* Make sure we're not attempting to remove files we do not intend */ - ASSERT(fs_event_removed < fs_event_file_count); + ASSERT_LT(fs_event_removed, fs_event_file_count); /* Remove the file */ - ASSERT(0 == remove(fs_event_get_filename(fs_event_removed))); + ASSERT_OK(delete_file(fs_event_get_filename(fs_event_removed))); if (++fs_event_removed < fs_event_file_count) { /* Remove another file on a different event loop tick. We do it this way * to avoid fs events coalescing into one fs event. */ - ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files, 1, 0)); + ASSERT_OK(uv_timer_start(&timer, fs_event_unlink_files, 1, 0)); } } } @@ -202,19 +229,20 @@ static void fs_event_cb_dir_multi_file(uv_fs_event_t* handle, int events, int status) { fs_event_cb_called++; - ASSERT(handle == &fs_event); - ASSERT(status == 0); + ASSERT_PTR_EQ(handle, &fs_event); + ASSERT_OK(status); ASSERT(events == UV_CHANGE || events == UV_RENAME); - #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__) - ASSERT(strncmp(filename, file_prefix, sizeof(file_prefix) - 1) == 0); - #else - ASSERT(filename == NULL || - strncmp(filename, file_prefix, sizeof(file_prefix) - 1) == 0); - #endif +#if defined(__APPLE__) || defined(_WIN32) || defined(__linux__) + ASSERT_NOT_NULL(filename); + ASSERT_MEM_EQ(filename, file_prefix, sizeof(file_prefix) - 1); +#else + if (filename != NULL) + ASSERT_MEM_EQ(filename, file_prefix, sizeof(file_prefix) - 1); +#endif if (fs_event_created + fs_event_removed == fs_event_file_count) { /* Once we've processed all create events, delete all files */ - ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files, 1, 0)); + ASSERT_OK(uv_timer_start(&timer, fs_event_unlink_files, 1, 0)); } else if (fs_event_cb_called == 2 * fs_event_file_count) { /* Once we've processed all create and delete events, stop watching */ uv_close((uv_handle_t*) &timer, close_cb); @@ -234,7 +262,7 @@ static const char* fs_event_get_filename_in_subdir(int i) { static void fs_event_create_files_in_subdir(uv_timer_t* handle) { /* Make sure we're not attempting to create files we do not intend */ - ASSERT(fs_event_created < fs_event_file_count); + ASSERT_LT(fs_event_created, fs_event_file_count); /* Create the file */ create_file(fs_event_get_filename_in_subdir(fs_event_created)); @@ -242,7 +270,7 @@ static void fs_event_create_files_in_subdir(uv_timer_t* handle) { if (++fs_event_created < fs_event_file_count) { /* Create another file on a different event loop tick. We do it this way * to avoid fs events coalescing into one fs event. */ - ASSERT(0 == uv_timer_start(&timer, fs_event_create_files_in_subdir, 1, 0)); + ASSERT_OK(uv_timer_start(&timer, fs_event_create_files_in_subdir, 100, 0)); } } @@ -254,21 +282,24 @@ static void fs_event_unlink_files_in_subdir(uv_timer_t* handle) { if (handle == NULL) { /* Unlink all files */ for (i = 0; i < 16; i++) { - r = remove(fs_event_get_filename_in_subdir(i)); + r = delete_file(fs_event_get_filename_in_subdir(i)); if (handle != NULL) - ASSERT(r == 0); + ASSERT_OK(r); } } else { /* Make sure we're not attempting to remove files we do not intend */ - ASSERT(fs_event_removed < fs_event_file_count); + ASSERT_LT(fs_event_removed, fs_event_file_count); /* Remove the file */ - ASSERT(0 == remove(fs_event_get_filename_in_subdir(fs_event_removed))); + ASSERT_OK(delete_file(fs_event_get_filename_in_subdir(fs_event_removed))); if (++fs_event_removed < fs_event_file_count) { /* Remove another file on a different event loop tick. We do it this way * to avoid fs events coalescing into one fs event. */ - ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files_in_subdir, 1, 0)); + ASSERT_OK(uv_timer_start(&timer, + fs_event_unlink_files_in_subdir, + 1, + 0)); } } } @@ -292,27 +323,30 @@ static void fs_event_cb_dir_multi_file_in_subdir(uv_fs_event_t* handle, return; fs_multievent_cb_called++; - ASSERT(handle == &fs_event); - ASSERT(status == 0); + ASSERT_PTR_EQ(handle, &fs_event); + ASSERT_OK(status); ASSERT(events == UV_CHANGE || events == UV_RENAME); #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__) - ASSERT(strncmp(filename, - file_prefix_in_subdir, - sizeof(file_prefix_in_subdir) - 1) == 0); + ASSERT_OK(strncmp(filename, + file_prefix_in_subdir, + sizeof(file_prefix_in_subdir) - 1)); #else - ASSERT(filename == NULL || - strncmp(filename, - file_prefix_in_subdir, - sizeof(file_prefix_in_subdir) - 1) == 0); + ASSERT_NE(filename == NULL || + strncmp(filename, + file_prefix_in_subdir, + sizeof(file_prefix_in_subdir) - 1) == 0, 0); #endif if (fs_event_created == fs_event_file_count && fs_multievent_cb_called == fs_event_created) { /* Once we've processed all create events, delete all files */ - ASSERT(0 == uv_timer_start(&timer, fs_event_unlink_files_in_subdir, 1, 0)); + ASSERT_OK(uv_timer_start(&timer, + fs_event_unlink_files_in_subdir, + 1, + 0)); } else if (fs_multievent_cb_called == 2 * fs_event_file_count) { /* Once we've processed all create and delete events, stop watching */ - ASSERT(fs_event_removed == fs_event_file_count); + ASSERT_EQ(fs_event_removed, fs_event_file_count); uv_close((uv_handle_t*) &timer, close_cb); uv_close((uv_handle_t*) handle, close_cb); } @@ -322,49 +356,32 @@ static void fs_event_cb_dir_multi_file_in_subdir(uv_fs_event_t* handle, static void fs_event_cb_file(uv_fs_event_t* handle, const char* filename, int events, int status) { ++fs_event_cb_called; - ASSERT(handle == &fs_event); - ASSERT(status == 0); - ASSERT(events == UV_CHANGE); + ASSERT_PTR_EQ(handle, &fs_event); + ASSERT_OK(status); + ASSERT_EQ(events, UV_CHANGE); #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__) - ASSERT(strcmp(filename, "file2") == 0); + ASSERT_OK(strcmp(filename, "file2")); #else ASSERT(filename == NULL || strcmp(filename, "file2") == 0); #endif - ASSERT(0 == uv_fs_event_stop(handle)); - uv_close((uv_handle_t*)handle, close_cb); -} - -static void timer_cb_close_handle(uv_timer_t* timer) { - uv_handle_t* handle; - - ASSERT_NOT_NULL(timer); - handle = timer->data; - - uv_close((uv_handle_t*)timer, NULL); + ASSERT_OK(uv_fs_event_stop(handle)); uv_close((uv_handle_t*)handle, close_cb); } static void fs_event_cb_file_current_dir(uv_fs_event_t* handle, const char* filename, int events, int status) { - ASSERT(fs_event_cb_called == 0); ++fs_event_cb_called; - ASSERT(handle == &fs_event); - ASSERT(status == 0); - ASSERT(events == UV_CHANGE); + ASSERT_PTR_EQ(handle, &fs_event); + ASSERT_OK(status); + ASSERT_EQ(events, UV_CHANGE); #if defined(__APPLE__) || defined(_WIN32) || defined(__linux__) - ASSERT(strcmp(filename, "watch_file") == 0); + ASSERT_OK(strcmp(filename, "watch_file")); #else ASSERT(filename == NULL || strcmp(filename, "watch_file") == 0); #endif - /* Regression test for SunOS: touch should generate just one event. */ - { - static uv_timer_t timer; - uv_timer_init(handle->loop, &timer); - timer.data = handle; - uv_timer_start(&timer, timer_cb_close_handle, 250, 0); - } + uv_close((uv_handle_t*)handle, close_cb); } static void timer_cb_file(uv_timer_t* handle) { @@ -392,7 +409,7 @@ static void timer_cb_exact(uv_timer_t* handle) { } else { uv_close((uv_handle_t*)handle, NULL); r = uv_fs_event_stop(&fs_event); - ASSERT(r == 0); + ASSERT_OK(r); uv_close((uv_handle_t*) &fs_event, NULL); } @@ -410,9 +427,9 @@ static void fs_event_cb_close(uv_fs_event_t* handle, const char* filename, int events, int status) { - ASSERT(status == 0); + ASSERT_OK(status); - ASSERT(fs_event_cb_called < 3); + ASSERT_LT(fs_event_cb_called, 3); ++fs_event_cb_called; if (fs_event_cb_called == 3) { @@ -426,6 +443,8 @@ TEST_IMPL(fs_event_watch_dir) { RETURN_SKIP(NO_FS_EVENTS); #elif defined(__MVS__) RETURN_SKIP("Directory watching not supported on this platform."); +#elif defined(__APPLE__) && defined(__TSAN__) + RETURN_SKIP("Times out under TSAN."); #endif uv_loop_t* loop = uv_default_loop(); @@ -433,38 +452,78 @@ TEST_IMPL(fs_event_watch_dir) { /* Setup */ fs_event_unlink_files(NULL); - remove("watch_dir/file2"); - remove("watch_dir/file1"); - remove("watch_dir/"); + delete_file("watch_dir/file2"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/"); create_dir("watch_dir"); r = uv_fs_event_init(loop, &fs_event); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_event_start(&fs_event, fs_event_cb_dir_multi_file, "watch_dir", 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_init(loop, &timer); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&timer, fs_event_create_files, 100, 0); - ASSERT(r == 0); + ASSERT_OK(r); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT_EQ(fs_event_cb_called, fs_event_created + fs_event_removed); + ASSERT_EQ(2, close_cb_called); + + /* Cleanup */ + fs_event_unlink_files(NULL); + delete_file("watch_dir/file2"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/"); + + MAKE_VALGRIND_HAPPY(loop); + return 0; +} + +TEST_IMPL(fs_event_watch_delete_dir) { +#if defined(NO_FS_EVENTS) + RETURN_SKIP(NO_FS_EVENTS); +#elif defined(__MVS__) + RETURN_SKIP("Directory watching not supported on this platform."); +#elif defined(__APPLE__) && defined(__TSAN__) + RETURN_SKIP("Times out under TSAN."); +#endif + + uv_loop_t* loop = uv_default_loop(); + int r; + + /* Setup */ + fs_event_unlink_files(NULL); + delete_dir("watch_del_dir/"); + create_dir("watch_del_dir"); + + r = uv_fs_event_init(loop, &fs_event); + ASSERT_OK(r); + r = uv_fs_event_start(&fs_event, fs_event_cb_del_dir, "watch_del_dir", 0); + ASSERT_OK(r); + r = uv_timer_init(loop, &timer); + ASSERT_OK(r); + r = uv_timer_start(&timer, fs_event_del_dir, 100, 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(fs_event_cb_called == fs_event_created + fs_event_removed); - ASSERT(close_cb_called == 2); + ASSERT_EQ(1, fs_event_cb_called); + ASSERT_EQ(2, close_cb_called); /* Cleanup */ fs_event_unlink_files(NULL); - remove("watch_dir/file2"); - remove("watch_dir/file1"); - remove("watch_dir/"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } TEST_IMPL(fs_event_watch_dir_recursive) { -#if defined(__APPLE__) || defined(_WIN32) +#if defined(__APPLE__) && defined(__TSAN__) + RETURN_SKIP("Times out under TSAN."); +#elif defined(__APPLE__) || defined(_WIN32) uv_loop_t* loop; int r; uv_fs_event_t fs_event_root; @@ -472,35 +531,35 @@ TEST_IMPL(fs_event_watch_dir_recursive) { /* Setup */ loop = uv_default_loop(); fs_event_unlink_files(NULL); - remove("watch_dir/file2"); - remove("watch_dir/file1"); - remove("watch_dir/subdir"); - remove("watch_dir/"); + delete_file("watch_dir/file2"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/subdir"); + delete_dir("watch_dir/"); create_dir("watch_dir"); create_dir("watch_dir/subdir"); r = uv_fs_event_init(loop, &fs_event); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_event_start(&fs_event, fs_event_cb_dir_multi_file_in_subdir, "watch_dir", UV_FS_EVENT_RECURSIVE); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_init(loop, &timer); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&timer, fs_event_create_files_in_subdir, 100, 0); - ASSERT(r == 0); + ASSERT_OK(r); #ifndef _WIN32 /* Also try to watch the root directory. * This will be noisier, so we're just checking for any couple events to happen. */ r = uv_fs_event_init(loop, &fs_event_root); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_event_start(&fs_event_root, fs_event_cb_close, "/", UV_FS_EVENT_RECURSIVE); - ASSERT(r == 0); + ASSERT_OK(r); #else fs_event_cb_called += 3; close_cb_called += 1; @@ -509,18 +568,18 @@ TEST_IMPL(fs_event_watch_dir_recursive) { uv_run(loop, UV_RUN_DEFAULT); - ASSERT(fs_multievent_cb_called == fs_event_created + fs_event_removed); - ASSERT(fs_event_cb_called == 3); - ASSERT(close_cb_called == 3); + ASSERT_EQ(fs_multievent_cb_called, fs_event_created + fs_event_removed); + ASSERT_EQ(3, fs_event_cb_called); + ASSERT_EQ(3, close_cb_called); /* Cleanup */ fs_event_unlink_files_in_subdir(NULL); - remove("watch_dir/file2"); - remove("watch_dir/file1"); - remove("watch_dir/subdir"); - remove("watch_dir/"); + delete_file("watch_dir/file2"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/subdir"); + delete_dir("watch_dir/"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; #else RETURN_SKIP("Recursive directory watching not supported on this platform."); @@ -536,8 +595,8 @@ TEST_IMPL(fs_event_watch_dir_short_path) { /* Setup */ loop = uv_default_loop(); - remove("watch_dir/file1"); - remove("watch_dir/"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/"); create_dir("watch_dir"); create_file("watch_dir/file1"); @@ -548,26 +607,26 @@ TEST_IMPL(fs_event_watch_dir_short_path) { has_shortnames = uv_fs_stat(NULL, &req, "watch_~1", NULL) != UV_ENOENT; if (has_shortnames) { r = uv_fs_event_init(loop, &fs_event); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_event_start(&fs_event, fs_event_cb_dir, "watch_~1", 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_init(loop, &timer); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&timer, timer_cb_file, 100, 0); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(fs_event_cb_called == 1); - ASSERT(timer_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, fs_event_cb_called); + ASSERT_EQ(1, timer_cb_called); + ASSERT_EQ(1, close_cb_called); } /* Cleanup */ - remove("watch_dir/file1"); - remove("watch_dir/"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); if (!has_shortnames) RETURN_SKIP("Was not able to address files with 8.3 short name."); @@ -586,34 +645,34 @@ TEST_IMPL(fs_event_watch_file) { int r; /* Setup */ - remove("watch_dir/file2"); - remove("watch_dir/file1"); - remove("watch_dir/"); + delete_file("watch_dir/file2"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/"); create_dir("watch_dir"); create_file("watch_dir/file1"); create_file("watch_dir/file2"); r = uv_fs_event_init(loop, &fs_event); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_event_start(&fs_event, fs_event_cb_file, "watch_dir/file2", 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_init(loop, &timer); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&timer, timer_cb_file, 100, 100); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(fs_event_cb_called == 1); - ASSERT(timer_cb_called == 2); - ASSERT(close_cb_called == 2); + ASSERT_EQ(1, fs_event_cb_called); + ASSERT_EQ(2, timer_cb_called); + ASSERT_EQ(2, close_cb_called); /* Cleanup */ - remove("watch_dir/file2"); - remove("watch_dir/file1"); - remove("watch_dir/"); + delete_file("watch_dir/file2"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -633,14 +692,14 @@ TEST_IMPL(fs_event_watch_file_exact_path) { loop = uv_default_loop(); /* Setup */ - remove("watch_dir/file.js"); - remove("watch_dir/file.jsx"); - remove("watch_dir/"); + delete_file("watch_dir/file.js"); + delete_file("watch_dir/file.jsx"); + delete_dir("watch_dir/"); create_dir("watch_dir"); create_file("watch_dir/file.js"); create_file("watch_dir/file.jsx"); #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_12) - /* Empirically, FSEvents seems to (reliably) report the preceeding + /* Empirically, FSEvents seems to (reliably) report the preceding * create_file events prior to macOS 10.11.6 in the subsequent fs_watch * creation, but that behavior hasn't been observed to occur on newer * versions. Give a long delay here to let the system settle before running @@ -650,23 +709,23 @@ TEST_IMPL(fs_event_watch_file_exact_path) { #endif r = uv_fs_event_init(loop, &fs_event); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_event_start(&fs_event, fs_event_fail, "watch_dir/file.jsx", 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_init(loop, &timer); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&timer, timer_cb_exact, 100, 100); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); - ASSERT(timer_cb_exact_called == 2); + ASSERT_OK(r); + ASSERT_EQ(2, timer_cb_exact_called); /* Cleanup */ - remove("watch_dir/file.js"); - remove("watch_dir/file.jsx"); - remove("watch_dir/"); + delete_file("watch_dir/file.js"); + delete_file("watch_dir/file.jsx"); + delete_dir("watch_dir/"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -682,15 +741,15 @@ TEST_IMPL(fs_event_watch_file_twice) { loop = uv_default_loop(); timer.data = watchers; - ASSERT(0 == uv_fs_event_init(loop, watchers + 0)); - ASSERT(0 == uv_fs_event_start(watchers + 0, fail_cb, path, 0)); - ASSERT(0 == uv_fs_event_init(loop, watchers + 1)); - ASSERT(0 == uv_fs_event_start(watchers + 1, fail_cb, path, 0)); - ASSERT(0 == uv_timer_init(loop, &timer)); - ASSERT(0 == uv_timer_start(&timer, timer_cb_watch_twice, 10, 0)); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_fs_event_init(loop, watchers + 0)); + ASSERT_OK(uv_fs_event_start(watchers + 0, fail_cb, path, 0)); + ASSERT_OK(uv_fs_event_init(loop, watchers + 1)); + ASSERT_OK(uv_fs_event_start(watchers + 1, fail_cb, path, 0)); + ASSERT_OK(uv_timer_init(loop, &timer)); + ASSERT_OK(uv_timer_start(&timer, timer_cb_watch_twice, 10, 0)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -705,10 +764,10 @@ TEST_IMPL(fs_event_watch_file_current_dir) { loop = uv_default_loop(); /* Setup */ - remove("watch_file"); + delete_file("watch_file"); create_file("watch_file"); #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_12) - /* Empirically, kevent seems to (sometimes) report the preceeding + /* Empirically, kevent seems to (sometimes) report the preceding * create_file events prior to macOS 10.11.6 in the subsequent fs_event_start * So let the system settle before running the test. */ uv_sleep(1100); @@ -716,35 +775,36 @@ TEST_IMPL(fs_event_watch_file_current_dir) { #endif r = uv_fs_event_init(loop, &fs_event); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_event_start(&fs_event, fs_event_cb_file_current_dir, "watch_file", 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_init(loop, &timer); - ASSERT(r == 0); + ASSERT_OK(r); timer.data = "watch_file"; r = uv_timer_start(&timer, timer_cb_touch, 1100, 0); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(timer_cb_touch_called == 0); - ASSERT(fs_event_cb_called == 0); - ASSERT(close_cb_called == 0); + ASSERT_OK(timer_cb_touch_called); + ASSERT_OK(fs_event_cb_called); + ASSERT_OK(close_cb_called); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(timer_cb_touch_called == 1); - ASSERT(fs_event_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, timer_cb_touch_called); + /* FSEvents on macOS sometimes sends one change event, sometimes two. */ + ASSERT_NE(0, fs_event_cb_called); + ASSERT_EQ(1, close_cb_called); /* Cleanup */ - remove("watch_file"); + delete_file("watch_file"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -762,15 +822,15 @@ TEST_IMPL(fs_event_watch_file_root_dir) { loop = uv_default_loop(); r = uv_fs_event_init(loop, &fs_event); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_event_start(&fs_event, fail_cb, path, 0); if (r == UV_ENOENT) RETURN_SKIP("bootsect.bak doesn't exist in system root.\n"); - ASSERT(r == 0); + ASSERT_OK(r); uv_close((uv_handle_t*) &fs_event, NULL); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } #endif @@ -784,32 +844,32 @@ TEST_IMPL(fs_event_no_callback_after_close) { int r; /* Setup */ - remove("watch_dir/file1"); - remove("watch_dir/"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/"); create_dir("watch_dir"); create_file("watch_dir/file1"); r = uv_fs_event_init(loop, &fs_event); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_event_start(&fs_event, fs_event_cb_file, "watch_dir/file1", 0); - ASSERT(r == 0); + ASSERT_OK(r); uv_close((uv_handle_t*)&fs_event, close_cb); touch_file("watch_dir/file1"); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(fs_event_cb_called == 0); - ASSERT(close_cb_called == 1); + ASSERT_OK(fs_event_cb_called); + ASSERT_EQ(1, close_cb_called); /* Cleanup */ - remove("watch_dir/file1"); - remove("watch_dir/"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -822,31 +882,31 @@ TEST_IMPL(fs_event_no_callback_on_close) { int r; /* Setup */ - remove("watch_dir/file1"); - remove("watch_dir/"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/"); create_dir("watch_dir"); create_file("watch_dir/file1"); r = uv_fs_event_init(loop, &fs_event); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_event_start(&fs_event, fs_event_cb_file, "watch_dir/file1", 0); - ASSERT(r == 0); + ASSERT_OK(r); uv_close((uv_handle_t*)&fs_event, close_cb); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(fs_event_cb_called == 0); - ASSERT(close_cb_called == 1); + ASSERT_OK(fs_event_cb_called); + ASSERT_EQ(1, close_cb_called); /* Cleanup */ - remove("watch_dir/file1"); - remove("watch_dir/"); + delete_file("watch_dir/file1"); + delete_dir("watch_dir/"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -855,9 +915,9 @@ static void timer_cb(uv_timer_t* handle) { int r; r = uv_fs_event_init(handle->loop, &fs_event); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_event_start(&fs_event, fs_event_fail, ".", 0); - ASSERT(r == 0); + ASSERT_OK(r); uv_close((uv_handle_t*)&fs_event, close_cb); uv_close((uv_handle_t*)handle, close_cb); @@ -875,16 +935,16 @@ TEST_IMPL(fs_event_immediate_close) { loop = uv_default_loop(); r = uv_timer_init(loop, &timer); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&timer, timer_cb, 1, 0); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(close_cb_called == 2); + ASSERT_EQ(2, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -902,9 +962,9 @@ TEST_IMPL(fs_event_close_with_pending_event) { create_file("watch_dir/file"); r = uv_fs_event_init(loop, &fs_event); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_event_start(&fs_event, fs_event_fail, "watch_dir", 0); - ASSERT(r == 0); + ASSERT_OK(r); /* Generate an fs event. */ touch_file("watch_dir/file"); @@ -913,13 +973,51 @@ TEST_IMPL(fs_event_close_with_pending_event) { uv_run(loop, UV_RUN_DEFAULT); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, close_cb_called); /* Clean up */ - remove("watch_dir/file"); - remove("watch_dir/"); + delete_file("watch_dir/file"); + delete_dir("watch_dir/"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); + return 0; +} + +TEST_IMPL(fs_event_close_with_pending_delete_event) { +#if defined(NO_FS_EVENTS) + RETURN_SKIP(NO_FS_EVENTS); +#endif + uv_loop_t* loop; + int r; + + loop = uv_default_loop(); + + create_dir("watch_dir"); + create_file("watch_dir/file"); + + r = uv_fs_event_init(loop, &fs_event); + ASSERT_OK(r); + r = uv_fs_event_start(&fs_event, fs_event_fail, "watch_dir/file", 0); + ASSERT_OK(r); + + /* Generate an fs event. */ + delete_file("watch_dir/file"); + + /* Allow time for the remove event to propagate to the pending list. */ + /* XXX - perhaps just for __sun? */ + uv_sleep(1100); + uv_update_time(loop); + + uv_close((uv_handle_t*)&fs_event, close_cb); + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT_EQ(1, close_cb_called); + + /* Clean up */ + delete_dir("watch_dir/"); + + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -928,6 +1026,8 @@ TEST_IMPL(fs_event_close_in_callback) { RETURN_SKIP(NO_FS_EVENTS); #elif defined(__MVS__) RETURN_SKIP("Directory watching not supported on this platform."); +#elif defined(__APPLE__) && defined(__TSAN__) + RETURN_SKIP("Times out under TSAN."); #endif uv_loop_t* loop; int r; @@ -938,14 +1038,14 @@ TEST_IMPL(fs_event_close_in_callback) { create_dir("watch_dir"); r = uv_fs_event_init(loop, &fs_event); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_event_start(&fs_event, fs_event_cb_close, "watch_dir", 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_init(loop, &timer); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&timer, fs_event_create_files, 100, 0); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); @@ -953,14 +1053,14 @@ TEST_IMPL(fs_event_close_in_callback) { uv_run(loop, UV_RUN_ONCE); - ASSERT(close_cb_called == 2); - ASSERT(fs_event_cb_called == 3); + ASSERT_EQ(2, close_cb_called); + ASSERT_EQ(3, fs_event_cb_called); /* Clean up */ fs_event_unlink_files(NULL); - remove("watch_dir/"); + delete_dir("watch_dir/"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -978,24 +1078,24 @@ TEST_IMPL(fs_event_start_and_close) { create_dir("watch_dir"); r = uv_fs_event_init(loop, &fs_event1); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_event_start(&fs_event1, fs_event_cb_dir, "watch_dir", 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_event_init(loop, &fs_event2); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_event_start(&fs_event2, fs_event_cb_dir, "watch_dir", 0); - ASSERT(r == 0); + ASSERT_OK(r); uv_close((uv_handle_t*) &fs_event2, close_cb); uv_close((uv_handle_t*) &fs_event1, close_cb); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(close_cb_called == 2); + ASSERT_EQ(2, close_cb_called); - remove("watch_dir/"); - MAKE_VALGRIND_HAPPY(); + delete_dir("watch_dir/"); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -1022,136 +1122,36 @@ TEST_IMPL(fs_event_getpath) { for (i = 0; i < ARRAY_SIZE(watch_dir); i++) { r = uv_fs_event_init(loop, &fs_event); - ASSERT(r == 0); + ASSERT_OK(r); len = sizeof buf; r = uv_fs_event_getpath(&fs_event, buf, &len); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_event_start(&fs_event, fail_cb, watch_dir[i], 0); - ASSERT(r == 0); - len = 0; + ASSERT_OK(r); + len = 1; r = uv_fs_event_getpath(&fs_event, buf, &len); - ASSERT(r == UV_ENOBUFS); - ASSERT(len < sizeof buf); /* sanity check */ - ASSERT(len == strlen(watch_dir[i]) + 1); + ASSERT_EQ(r, UV_ENOBUFS); + ASSERT_LT(len, sizeof buf); /* sanity check */ + ASSERT_EQ(len, strlen(watch_dir[i]) + 1); r = uv_fs_event_getpath(&fs_event, buf, &len); - ASSERT(r == 0); - ASSERT(len == strlen(watch_dir[i])); + ASSERT_OK(r); + ASSERT_EQ(len, strlen(watch_dir[i])); ASSERT(strcmp(buf, watch_dir[i]) == 0); r = uv_fs_event_stop(&fs_event); - ASSERT(r == 0); + ASSERT_OK(r); uv_close((uv_handle_t*) &fs_event, close_cb); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, close_cb_called); close_cb_called = 0; } - remove("watch_dir/"); - MAKE_VALGRIND_HAPPY(); + delete_dir("watch_dir/"); + MAKE_VALGRIND_HAPPY(loop); return 0; } -#if defined(__APPLE__) - -static int fs_event_error_reported; - -static void fs_event_error_report_cb(uv_fs_event_t* handle, - const char* filename, - int events, - int status) { - if (status != 0) - fs_event_error_reported = status; -} - -static void timer_cb_nop(uv_timer_t* handle) { - ++timer_cb_called; - uv_close((uv_handle_t*) handle, close_cb); -} - -static void fs_event_error_report_close_cb(uv_handle_t* handle) { - ASSERT_NOT_NULL(handle); - close_cb_called++; - - /* handle is allocated on-stack, no need to free it */ -} - - -TEST_IMPL(fs_event_error_reporting) { - unsigned int i; - uv_loop_t loops[1024]; - uv_fs_event_t events[ARRAY_SIZE(loops)]; - uv_loop_t* loop; - uv_fs_event_t* event; - - TEST_FILE_LIMIT(ARRAY_SIZE(loops) * 3); - - remove("watch_dir/"); - create_dir("watch_dir"); - - /* Create a lot of loops, and start FSEventStream in each of them. - * Eventually, this should create enough streams to make FSEventStreamStart() - * fail. - */ - for (i = 0; i < ARRAY_SIZE(loops); i++) { - loop = &loops[i]; - ASSERT(0 == uv_loop_init(loop)); - event = &events[i]; - - timer_cb_called = 0; - close_cb_called = 0; - ASSERT(0 == uv_fs_event_init(loop, event)); - ASSERT(0 == uv_fs_event_start(event, - fs_event_error_report_cb, - "watch_dir", - 0)); - uv_unref((uv_handle_t*) event); - - /* Let loop run for some time */ - ASSERT(0 == uv_timer_init(loop, &timer)); - ASSERT(0 == uv_timer_start(&timer, timer_cb_nop, 2, 0)); - uv_run(loop, UV_RUN_DEFAULT); - ASSERT(1 == timer_cb_called); - ASSERT(1 == close_cb_called); - if (fs_event_error_reported != 0) - break; - } - - /* At least one loop should fail */ - ASSERT(fs_event_error_reported == UV_EMFILE); - - /* Stop and close all events, and destroy loops */ - do { - loop = &loops[i]; - event = &events[i]; - - ASSERT(0 == uv_fs_event_stop(event)); - uv_ref((uv_handle_t*) event); - uv_close((uv_handle_t*) event, fs_event_error_report_close_cb); - - close_cb_called = 0; - uv_run(loop, UV_RUN_DEFAULT); - ASSERT(close_cb_called == 1); - - uv_loop_close(loop); - } while (i-- != 0); - - remove("watch_dir/"); - MAKE_VALGRIND_HAPPY(); - return 0; -} - -#else /* !defined(__APPLE__) */ - -TEST_IMPL(fs_event_error_reporting) { - /* No-op, needed only for FSEvents backend */ - - MAKE_VALGRIND_HAPPY(); - return 0; -} - -#endif /* defined(__APPLE__) */ - TEST_IMPL(fs_event_watch_invalid_path) { #if defined(NO_FS_EVENTS) RETURN_SKIP(NO_FS_EVENTS); @@ -1162,14 +1162,14 @@ TEST_IMPL(fs_event_watch_invalid_path) { loop = uv_default_loop(); r = uv_fs_event_init(loop, &fs_event); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_event_start(&fs_event, fs_event_cb_file, "<:;", 0); - ASSERT(r != 0); - ASSERT(uv_is_active((uv_handle_t*) &fs_event) == 0); + ASSERT(r); + ASSERT_OK(uv_is_active((uv_handle_t*) &fs_event)); r = uv_fs_event_start(&fs_event, fs_event_cb_file, "", 0); - ASSERT(r != 0); - ASSERT(uv_is_active((uv_handle_t*) &fs_event) == 0); - MAKE_VALGRIND_HAPPY(); + ASSERT(r); + ASSERT_OK(uv_is_active((uv_handle_t*) &fs_event)); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -1190,31 +1190,31 @@ TEST_IMPL(fs_event_stop_in_cb) { RETURN_SKIP(NO_FS_EVENTS); #endif - remove(path); + delete_file(path); create_file(path); - ASSERT_EQ(0, uv_fs_event_init(uv_default_loop(), &fs)); - ASSERT_EQ(0, uv_fs_event_start(&fs, fs_event_cb_stop, path, 0)); + ASSERT_OK(uv_fs_event_init(uv_default_loop(), &fs)); + ASSERT_OK(uv_fs_event_start(&fs, fs_event_cb_stop, path, 0)); /* Note: timer_cb_touch() closes the handle. */ timer.data = path; - ASSERT_EQ(0, uv_timer_init(uv_default_loop(), &timer)); - ASSERT_EQ(0, uv_timer_start(&timer, timer_cb_touch, 100, 0)); + ASSERT_OK(uv_timer_init(uv_default_loop(), &timer)); + ASSERT_OK(uv_timer_start(&timer, timer_cb_touch, 100, 0)); - ASSERT_EQ(0, fs_event_cb_stop_calls); - ASSERT_EQ(0, timer_cb_touch_called); + ASSERT_OK(fs_event_cb_stop_calls); + ASSERT_OK(timer_cb_touch_called); - ASSERT_EQ(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT_EQ(1, fs_event_cb_stop_calls); ASSERT_EQ(1, timer_cb_touch_called); uv_close((uv_handle_t*) &fs, NULL); - ASSERT_EQ(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT_EQ(1, fs_event_cb_stop_calls); - remove(path); + delete_file(path); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-fs-fd-hash.c b/test/test-fs-fd-hash.c index 8b4bc0351b3..4ed3d548e62 100644 --- a/test/test-fs-fd-hash.c +++ b/test/test-fs-fd-hash.c @@ -43,7 +43,7 @@ void assert_nonexistent(int fd) { void assert_existent(int fd) { struct uv__fd_info_s info = { 0 }; ASSERT(uv__fd_hash_get(fd, &info)); - ASSERT(info.flags == fd + FD_DIFF); + ASSERT_EQ(info.flags, fd + FD_DIFF); } void assert_insertion(int fd) { @@ -58,7 +58,7 @@ void assert_removal(int fd) { struct uv__fd_info_s info = { 0 }; assert_existent(fd); uv__fd_hash_remove(fd, &info); - ASSERT(info.flags == fd + FD_DIFF); + ASSERT_EQ(info.flags, fd + FD_DIFF); assert_nonexistent(fd); } @@ -106,7 +106,7 @@ TEST_IMPL(fs_fd_hash) { { struct uv__fd_info_s info = { 0 }; ASSERT(uv__fd_hash_get(0, &info)); - ASSERT(info.flags == FD_DIFF + FD_DIFF); + ASSERT_EQ(info.flags, FD_DIFF + FD_DIFF); } { /* Leave as it was, will be again tested below */ diff --git a/test/test-fs-open-flags.c b/test/test-fs-open-flags.c index 372afe13975..e64ac20d72b 100644 --- a/test/test-fs-open-flags.c +++ b/test/test-fs-open-flags.c @@ -68,8 +68,8 @@ static void setup(void) { uv_fs_req_cleanup(&rmdir_req); r = uv_fs_mkdir(NULL, &mkdir_req, empty_dir, 0755, NULL); - ASSERT(r == 0); - ASSERT(mkdir_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(mkdir_req.result); uv_fs_req_cleanup(&mkdir_req); } @@ -89,13 +89,13 @@ static void refresh(void) { r = uv_fs_open(NULL, &open_req, empty_file, UV_FS_O_TRUNC | UV_FS_O_CREAT | UV_FS_O_WRONLY, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(open_req.result, 0); uv_fs_req_cleanup(&open_req); r = uv_fs_close(NULL, &close_req, open_req.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); /* dummy_file */ @@ -103,19 +103,19 @@ static void refresh(void) { r = uv_fs_open(NULL, &open_req, dummy_file, UV_FS_O_TRUNC | UV_FS_O_CREAT | UV_FS_O_WRONLY, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(open_req.result, 0); uv_fs_req_cleanup(&open_req); iov = uv_buf_init("a", 1); r = uv_fs_write(NULL, &write_req, open_req.result, &iov, 1, -1, NULL); - ASSERT(r == 1); - ASSERT(write_req.result == 1); + ASSERT_EQ(1, r); + ASSERT_EQ(1, write_req.result); uv_fs_req_cleanup(&write_req); r = uv_fs_close(NULL, &close_req, open_req.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); } @@ -131,14 +131,14 @@ static void openFail(char *file, int error) { refresh(); r = uv_fs_open(NULL, &open_req, file, flags, S_IWUSR | S_IRUSR, NULL); - ASSERT(r == error); - ASSERT(open_req.result == error); + ASSERT_EQ(r, error); + ASSERT_EQ(open_req.result, error); uv_fs_req_cleanup(&open_req); /* Ensure the first call does not create the file */ r = uv_fs_open(NULL, &open_req, file, flags, S_IWUSR | S_IRUSR, NULL); - ASSERT(r == error); - ASSERT(open_req.result == error); + ASSERT_EQ(r, error); + ASSERT_EQ(open_req.result, error); uv_fs_req_cleanup(&open_req); cleanup(); @@ -150,8 +150,8 @@ static void refreshOpen(char *file) { refresh(); r = uv_fs_open(NULL, &open_req, file, flags, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(open_req.result, 0); uv_fs_req_cleanup(&open_req); } @@ -162,37 +162,37 @@ static void writeExpect(char *file, char *expected, int size) { iov = uv_buf_init("b", 1); r = uv_fs_write(NULL, &write_req, open_req.result, &iov, 1, -1, NULL); - ASSERT(r == 1); - ASSERT(write_req.result == 1); + ASSERT_EQ(1, r); + ASSERT_EQ(1, write_req.result); uv_fs_req_cleanup(&write_req); iov = uv_buf_init("c", 1); r = uv_fs_write(NULL, &write_req, open_req.result, &iov, 1, -1, NULL); - ASSERT(r == 1); - ASSERT(write_req.result == 1); + ASSERT_EQ(1, r); + ASSERT_EQ(1, write_req.result); uv_fs_req_cleanup(&write_req); r = uv_fs_close(NULL, &close_req, open_req.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); /* Check contents */ r = uv_fs_open(NULL, &open_req, file, UV_FS_O_RDONLY, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(open_req.result, 0); uv_fs_req_cleanup(&open_req); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req.result, &iov, 1, -1, NULL); - ASSERT(r == size); - ASSERT(read_req.result == size); - ASSERT(strncmp(buf, expected, size) == 0); + ASSERT_EQ(r, size); + ASSERT_EQ(read_req.result, size); + ASSERT_OK(strncmp(buf, expected, size)); uv_fs_req_cleanup(&read_req); r = uv_fs_close(NULL, &close_req, open_req.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); cleanup(); @@ -205,19 +205,19 @@ static void writeFail(char *file, int error) { iov = uv_buf_init("z", 1); r = uv_fs_write(NULL, &write_req, open_req.result, &iov, 1, -1, NULL); - ASSERT(r == error); - ASSERT(write_req.result == error); + ASSERT_EQ(r, error); + ASSERT_EQ(write_req.result, error); uv_fs_req_cleanup(&write_req); iov = uv_buf_init("z", 1); r = uv_fs_write(NULL, &write_req, open_req.result, &iov, 1, -1, NULL); - ASSERT(r == error); - ASSERT(write_req.result == error); + ASSERT_EQ(r, error); + ASSERT_EQ(write_req.result, error); uv_fs_req_cleanup(&write_req); r = uv_fs_close(NULL, &close_req, open_req.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); cleanup(); @@ -230,14 +230,14 @@ static void readExpect(char *file, char *expected, int size) { iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req.result, &iov, 1, -1, NULL); - ASSERT(r == size); - ASSERT(read_req.result == size); - ASSERT(strncmp(buf, expected, size) == 0); + ASSERT_EQ(r, size); + ASSERT_EQ(read_req.result, size); + ASSERT_OK(strncmp(buf, expected, size)); uv_fs_req_cleanup(&read_req); r = uv_fs_close(NULL, &close_req, open_req.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); cleanup(); @@ -250,19 +250,19 @@ static void readFail(char *file, int error) { iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req.result, &iov, 1, -1, NULL); - ASSERT(r == error); - ASSERT(read_req.result == error); + ASSERT_EQ(r, error); + ASSERT_EQ(read_req.result, error); uv_fs_req_cleanup(&read_req); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req.result, &iov, 1, -1, NULL); - ASSERT(r == error); - ASSERT(read_req.result == error); + ASSERT_EQ(r, error); + ASSERT_EQ(read_req.result, error); uv_fs_req_cleanup(&read_req); r = uv_fs_close(NULL, &close_req, open_req.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); cleanup(); @@ -424,7 +424,7 @@ TEST_IMPL(fs_open_flags) { /* Cleanup. */ rmdir(empty_dir); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-fs-poll.c b/test/test-fs-poll.c index 76fe6fc3957..5f95baf3d3f 100644 --- a/test/test-fs-poll.c +++ b/test/test-fs-poll.c @@ -103,44 +103,44 @@ static void poll_cb(uv_fs_poll_t* handle, memset(&zero_statbuf, 0, sizeof(zero_statbuf)); - ASSERT(handle == &poll_handle); - ASSERT(1 == uv_is_active((uv_handle_t*) handle)); + ASSERT_PTR_EQ(handle, &poll_handle); + ASSERT_EQ(1, uv_is_active((uv_handle_t*) handle)); ASSERT_NOT_NULL(prev); ASSERT_NOT_NULL(curr); switch (poll_cb_called++) { case 0: - ASSERT(status == UV_ENOENT); - ASSERT(0 == memcmp(prev, &zero_statbuf, sizeof(zero_statbuf))); - ASSERT(0 == memcmp(curr, &zero_statbuf, sizeof(zero_statbuf))); + ASSERT_EQ(status, UV_ENOENT); + ASSERT_OK(memcmp(prev, &zero_statbuf, sizeof(zero_statbuf))); + ASSERT_OK(memcmp(curr, &zero_statbuf, sizeof(zero_statbuf))); touch_file(FIXTURE); break; case 1: - ASSERT(status == 0); - ASSERT(0 == memcmp(prev, &zero_statbuf, sizeof(zero_statbuf))); - ASSERT(0 != memcmp(curr, &zero_statbuf, sizeof(zero_statbuf))); - ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 20, 0)); + ASSERT_OK(status); + ASSERT_OK(memcmp(prev, &zero_statbuf, sizeof(zero_statbuf))); + ASSERT_NE(0, memcmp(curr, &zero_statbuf, sizeof(zero_statbuf))); + ASSERT_OK(uv_timer_start(&timer_handle, timer_cb, 20, 0)); break; case 2: - ASSERT(status == 0); - ASSERT(0 != memcmp(prev, &zero_statbuf, sizeof(zero_statbuf))); - ASSERT(0 != memcmp(curr, &zero_statbuf, sizeof(zero_statbuf))); - ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 200, 0)); + ASSERT_OK(status); + ASSERT_NE(0, memcmp(prev, &zero_statbuf, sizeof(zero_statbuf))); + ASSERT_NE(0, memcmp(curr, &zero_statbuf, sizeof(zero_statbuf))); + ASSERT_OK(uv_timer_start(&timer_handle, timer_cb, 200, 0)); break; case 3: - ASSERT(status == 0); - ASSERT(0 != memcmp(prev, &zero_statbuf, sizeof(zero_statbuf))); - ASSERT(0 != memcmp(curr, &zero_statbuf, sizeof(zero_statbuf))); + ASSERT_OK(status); + ASSERT_NE(0, memcmp(prev, &zero_statbuf, sizeof(zero_statbuf))); + ASSERT_NE(0, memcmp(curr, &zero_statbuf, sizeof(zero_statbuf))); remove(FIXTURE); break; case 4: - ASSERT(status == UV_ENOENT); - ASSERT(0 != memcmp(prev, &zero_statbuf, sizeof(zero_statbuf))); - ASSERT(0 == memcmp(curr, &zero_statbuf, sizeof(zero_statbuf))); + ASSERT_EQ(status, UV_ENOENT); + ASSERT_NE(0, memcmp(prev, &zero_statbuf, sizeof(zero_statbuf))); + ASSERT_OK(memcmp(curr, &zero_statbuf, sizeof(zero_statbuf))); uv_close((uv_handle_t*)handle, close_cb); break; @@ -155,16 +155,16 @@ TEST_IMPL(fs_poll) { remove(FIXTURE); - ASSERT(0 == uv_timer_init(loop, &timer_handle)); - ASSERT(0 == uv_fs_poll_init(loop, &poll_handle)); - ASSERT(0 == uv_fs_poll_start(&poll_handle, poll_cb, FIXTURE, 100)); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_timer_init(loop, &timer_handle)); + ASSERT_OK(uv_fs_poll_init(loop, &poll_handle)); + ASSERT_OK(uv_fs_poll_start(&poll_handle, poll_cb, FIXTURE, 100)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(poll_cb_called == 5); - ASSERT(timer_cb_called == 2); - ASSERT(close_cb_called == 1); + ASSERT_EQ(5, poll_cb_called); + ASSERT_EQ(2, timer_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -176,23 +176,23 @@ TEST_IMPL(fs_poll_getpath) { remove(FIXTURE); - ASSERT(0 == uv_fs_poll_init(loop, &poll_handle)); + ASSERT_OK(uv_fs_poll_init(loop, &poll_handle)); len = sizeof buf; - ASSERT(UV_EINVAL == uv_fs_poll_getpath(&poll_handle, buf, &len)); - ASSERT(0 == uv_fs_poll_start(&poll_handle, poll_cb_fail, FIXTURE, 100)); + ASSERT_EQ(UV_EINVAL, uv_fs_poll_getpath(&poll_handle, buf, &len)); + ASSERT_OK(uv_fs_poll_start(&poll_handle, poll_cb_fail, FIXTURE, 100)); len = sizeof buf; - ASSERT(0 == uv_fs_poll_getpath(&poll_handle, buf, &len)); - ASSERT(buf[len - 1] != 0); - ASSERT(buf[len] == '\0'); - ASSERT(0 == memcmp(buf, FIXTURE, len)); + ASSERT_OK(uv_fs_poll_getpath(&poll_handle, buf, &len)); + ASSERT_NE(0, buf[len - 1]); + ASSERT_EQ(buf[len], '\0'); + ASSERT_OK(memcmp(buf, FIXTURE, len)); uv_close((uv_handle_t*) &poll_handle, close_cb); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -203,18 +203,16 @@ TEST_IMPL(fs_poll_close_request) { remove(FIXTURE); - ASSERT(0 == uv_loop_init(&loop)); + ASSERT_OK(uv_loop_init(&loop)); - ASSERT(0 == uv_fs_poll_init(&loop, &poll_handle)); - ASSERT(0 == uv_fs_poll_start(&poll_handle, poll_cb_fail, FIXTURE, 100)); + ASSERT_OK(uv_fs_poll_init(&loop, &poll_handle)); + ASSERT_OK(uv_fs_poll_start(&poll_handle, poll_cb_fail, FIXTURE, 100)); uv_close((uv_handle_t*) &poll_handle, close_cb); while (close_cb_called == 0) uv_run(&loop, UV_RUN_ONCE); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, close_cb_called); - ASSERT(0 == uv_loop_close(&loop)); - - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(&loop); return 0; } @@ -225,22 +223,20 @@ TEST_IMPL(fs_poll_close_request_multi_start_stop) { remove(FIXTURE); - ASSERT(0 == uv_loop_init(&loop)); + ASSERT_OK(uv_loop_init(&loop)); - ASSERT(0 == uv_fs_poll_init(&loop, &poll_handle)); + ASSERT_OK(uv_fs_poll_init(&loop, &poll_handle)); for (i = 0; i < 10; ++i) { - ASSERT(0 == uv_fs_poll_start(&poll_handle, poll_cb_fail, FIXTURE, 100)); - ASSERT(0 == uv_fs_poll_stop(&poll_handle)); + ASSERT_OK(uv_fs_poll_start(&poll_handle, poll_cb_fail, FIXTURE, 100)); + ASSERT_OK(uv_fs_poll_stop(&poll_handle)); } uv_close((uv_handle_t*) &poll_handle, close_cb); while (close_cb_called == 0) uv_run(&loop, UV_RUN_ONCE); - ASSERT(close_cb_called == 1); - - ASSERT(0 == uv_loop_close(&loop)); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(&loop); return 0; } @@ -251,22 +247,20 @@ TEST_IMPL(fs_poll_close_request_multi_stop_start) { remove(FIXTURE); - ASSERT(0 == uv_loop_init(&loop)); + ASSERT_OK(uv_loop_init(&loop)); - ASSERT(0 == uv_fs_poll_init(&loop, &poll_handle)); + ASSERT_OK(uv_fs_poll_init(&loop, &poll_handle)); for (i = 0; i < 10; ++i) { - ASSERT(0 == uv_fs_poll_stop(&poll_handle)); - ASSERT(0 == uv_fs_poll_start(&poll_handle, poll_cb_fail, FIXTURE, 100)); + ASSERT_OK(uv_fs_poll_stop(&poll_handle)); + ASSERT_OK(uv_fs_poll_start(&poll_handle, poll_cb_fail, FIXTURE, 100)); } uv_close((uv_handle_t*) &poll_handle, close_cb); while (close_cb_called == 0) uv_run(&loop, UV_RUN_ONCE); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, close_cb_called); - ASSERT(0 == uv_loop_close(&loop)); - - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(&loop); return 0; } @@ -277,24 +271,22 @@ TEST_IMPL(fs_poll_close_request_stop_when_active) { remove(FIXTURE); - ASSERT(0 == uv_loop_init(&loop)); + ASSERT_OK(uv_loop_init(&loop)); /* Set up all handles. */ - ASSERT(0 == uv_fs_poll_init(&loop, &poll_handle)); - ASSERT(0 == uv_fs_poll_start(&poll_handle, poll_cb_noop, FIXTURE, 100)); + ASSERT_OK(uv_fs_poll_init(&loop, &poll_handle)); + ASSERT_OK(uv_fs_poll_start(&poll_handle, poll_cb_noop, FIXTURE, 100)); uv_run(&loop, UV_RUN_ONCE); /* Close the timer handle, and do not crash. */ - ASSERT(0 == uv_fs_poll_stop(&poll_handle)); + ASSERT_OK(uv_fs_poll_stop(&poll_handle)); uv_run(&loop, UV_RUN_ONCE); /* Clean up after the test. */ uv_close((uv_handle_t*) &poll_handle, close_cb); uv_run(&loop, UV_RUN_ONCE); - ASSERT(close_cb_called == 1); - - ASSERT(0 == uv_loop_close(&loop)); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(&loop); return 0; } diff --git a/test/test-fs-readdir.c b/test/test-fs-readdir.c index 6bb69178415..bacea653587 100644 --- a/test/test-fs-readdir.c +++ b/test/test-fs-readdir.c @@ -29,6 +29,7 @@ static uv_fs_t readdir_req; static uv_fs_t closedir_req; static uv_dirent_t dirents[1]; +static uv_dirent_t symlink_dirents[2]; static int empty_opendir_cb_count; static int empty_closedir_cb_count; @@ -47,9 +48,9 @@ static void cleanup_test_files(void) { } static void empty_closedir_cb(uv_fs_t* req) { - ASSERT(req == &closedir_req); - ASSERT(req->fs_type == UV_FS_CLOSEDIR); - ASSERT(req->result == 0); + ASSERT_PTR_EQ(req, &closedir_req); + ASSERT_EQ(req->fs_type, UV_FS_CLOSEDIR); + ASSERT_OK(req->result); ++empty_closedir_cb_count; uv_fs_req_cleanup(req); } @@ -58,25 +59,25 @@ static void empty_readdir_cb(uv_fs_t* req) { uv_dir_t* dir; int r; - ASSERT(req == &readdir_req); - ASSERT(req->fs_type == UV_FS_READDIR); - ASSERT(req->result == 0); + ASSERT_PTR_EQ(req, &readdir_req); + ASSERT_EQ(req->fs_type, UV_FS_READDIR); + ASSERT_OK(req->result); dir = req->ptr; uv_fs_req_cleanup(req); r = uv_fs_closedir(uv_default_loop(), &closedir_req, dir, empty_closedir_cb); - ASSERT(r == 0); + ASSERT_OK(r); } static void empty_opendir_cb(uv_fs_t* req) { uv_dir_t* dir; int r; - ASSERT(req == &opendir_req); - ASSERT(req->fs_type == UV_FS_OPENDIR); - ASSERT(req->result == 0); + ASSERT_PTR_EQ(req, &opendir_req); + ASSERT_EQ(req->fs_type, UV_FS_OPENDIR); + ASSERT_OK(req->result); ASSERT_NOT_NULL(req->ptr); dir = req->ptr; dir->dirents = dirents; @@ -85,7 +86,7 @@ static void empty_opendir_cb(uv_fs_t* req) { &readdir_req, dir, empty_readdir_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(req); ++empty_opendir_cb_count; } @@ -115,9 +116,9 @@ TEST_IMPL(fs_readdir_empty_dir) { &opendir_req, path, NULL); - ASSERT(r == 0); - ASSERT(opendir_req.fs_type == UV_FS_OPENDIR); - ASSERT(opendir_req.result == 0); + ASSERT_OK(r); + ASSERT_EQ(opendir_req.fs_type, UV_FS_OPENDIR); + ASSERT_OK(opendir_req.result); ASSERT_NOT_NULL(opendir_req.ptr); dir = opendir_req.ptr; uv_fs_req_cleanup(&opendir_req); @@ -130,13 +131,13 @@ TEST_IMPL(fs_readdir_empty_dir) { &readdir_req, dir, NULL); - ASSERT(nb_entries_read == 0); + ASSERT_OK(nb_entries_read); uv_fs_req_cleanup(&readdir_req); /* Fill the req to ensure that required fields are cleaned up. */ memset(&closedir_req, 0xdb, sizeof(closedir_req)); uv_fs_closedir(uv_default_loop(), &closedir_req, dir, NULL); - ASSERT(closedir_req.result == 0); + ASSERT_OK(closedir_req.result); uv_fs_req_cleanup(&closedir_req); /* Testing the asynchronous flavor. */ @@ -147,16 +148,16 @@ TEST_IMPL(fs_readdir_empty_dir) { memset(&closedir_req, 0xdb, sizeof(closedir_req)); r = uv_fs_opendir(uv_default_loop(), &opendir_req, path, empty_opendir_cb); - ASSERT(r == 0); - ASSERT(empty_opendir_cb_count == 0); - ASSERT(empty_closedir_cb_count == 0); + ASSERT_OK(r); + ASSERT_OK(empty_opendir_cb_count); + ASSERT_OK(empty_closedir_cb_count); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); - ASSERT(empty_opendir_cb_count == 1); - ASSERT(empty_closedir_cb_count == 1); + ASSERT_OK(r); + ASSERT_EQ(1, empty_opendir_cb_count); + ASSERT_EQ(1, empty_closedir_cb_count); uv_fs_rmdir(uv_default_loop(), &rmdir_req, path, NULL); uv_fs_req_cleanup(&rmdir_req); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -168,9 +169,9 @@ TEST_IMPL(fs_readdir_empty_dir) { static int non_existing_opendir_cb_count; static void non_existing_opendir_cb(uv_fs_t* req) { - ASSERT(req == &opendir_req); - ASSERT(req->fs_type == UV_FS_OPENDIR); - ASSERT(req->result == UV_ENOENT); + ASSERT_PTR_EQ(req, &opendir_req); + ASSERT_EQ(req->fs_type, UV_FS_OPENDIR); + ASSERT_EQ(req->result, UV_ENOENT); ASSERT_NULL(req->ptr); uv_fs_req_cleanup(req); @@ -188,9 +189,9 @@ TEST_IMPL(fs_readdir_non_existing_dir) { /* Testing the synchronous flavor. */ r = uv_fs_opendir(uv_default_loop(), &opendir_req, path, NULL); - ASSERT(r == UV_ENOENT); - ASSERT(opendir_req.fs_type == UV_FS_OPENDIR); - ASSERT(opendir_req.result == UV_ENOENT); + ASSERT_EQ(r, UV_ENOENT); + ASSERT_EQ(opendir_req.fs_type, UV_FS_OPENDIR); + ASSERT_EQ(opendir_req.result, UV_ENOENT); ASSERT_NULL(opendir_req.ptr); uv_fs_req_cleanup(&opendir_req); @@ -202,13 +203,13 @@ TEST_IMPL(fs_readdir_non_existing_dir) { &opendir_req, path, non_existing_opendir_cb); - ASSERT(r == 0); - ASSERT(non_existing_opendir_cb_count == 0); + ASSERT_OK(r); + ASSERT_OK(non_existing_opendir_cb_count); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); - ASSERT(non_existing_opendir_cb_count == 1); + ASSERT_OK(r); + ASSERT_EQ(1, non_existing_opendir_cb_count); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -220,9 +221,9 @@ TEST_IMPL(fs_readdir_non_existing_dir) { static int file_opendir_cb_count; static void file_opendir_cb(uv_fs_t* req) { - ASSERT(req == &opendir_req); - ASSERT(req->fs_type == UV_FS_OPENDIR); - ASSERT(req->result == UV_ENOTDIR); + ASSERT_PTR_EQ(req, &opendir_req); + ASSERT_EQ(req->fs_type, UV_FS_OPENDIR); + ASSERT_EQ(req->result, UV_ENOTDIR); ASSERT_NULL(req->ptr); uv_fs_req_cleanup(req); @@ -241,9 +242,9 @@ TEST_IMPL(fs_readdir_file) { /* Testing the synchronous flavor. */ r = uv_fs_opendir(uv_default_loop(), &opendir_req, path, NULL); - ASSERT(r == UV_ENOTDIR); - ASSERT(opendir_req.fs_type == UV_FS_OPENDIR); - ASSERT(opendir_req.result == UV_ENOTDIR); + ASSERT_EQ(r, UV_ENOTDIR); + ASSERT_EQ(opendir_req.fs_type, UV_FS_OPENDIR); + ASSERT_EQ(opendir_req.result, UV_ENOTDIR); ASSERT_NULL(opendir_req.ptr); uv_fs_req_cleanup(&opendir_req); @@ -253,12 +254,12 @@ TEST_IMPL(fs_readdir_file) { /* Testing the async flavor. */ r = uv_fs_opendir(uv_default_loop(), &opendir_req, path, file_opendir_cb); - ASSERT(r == 0); - ASSERT(file_opendir_cb_count == 0); + ASSERT_OK(r); + ASSERT_OK(file_opendir_cb_count); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); - ASSERT(file_opendir_cb_count == 1); - MAKE_VALGRIND_HAPPY(); + ASSERT_OK(r); + ASSERT_EQ(1, file_opendir_cb_count); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -273,8 +274,8 @@ static int non_empty_readdir_cb_count; static int non_empty_closedir_cb_count; static void non_empty_closedir_cb(uv_fs_t* req) { - ASSERT(req == &closedir_req); - ASSERT(req->result == 0); + ASSERT_PTR_EQ(req, &closedir_req); + ASSERT_OK(req->result); uv_fs_req_cleanup(req); ++non_empty_closedir_cb_count; } @@ -282,30 +283,30 @@ static void non_empty_closedir_cb(uv_fs_t* req) { static void non_empty_readdir_cb(uv_fs_t* req) { uv_dir_t* dir; - ASSERT(req == &readdir_req); - ASSERT(req->fs_type == UV_FS_READDIR); + ASSERT_PTR_EQ(req, &readdir_req); + ASSERT_EQ(req->fs_type, UV_FS_READDIR); dir = req->ptr; if (req->result == 0) { uv_fs_req_cleanup(req); - ASSERT(non_empty_readdir_cb_count == 3); + ASSERT_EQ(3, non_empty_readdir_cb_count); uv_fs_closedir(uv_default_loop(), &closedir_req, dir, non_empty_closedir_cb); } else { - ASSERT(req->result == 1); - ASSERT(dir->dirents == dirents); + ASSERT_EQ(1, req->result); + ASSERT_PTR_EQ(dir->dirents, dirents); ASSERT(strcmp(dirents[0].name, "file1") == 0 || strcmp(dirents[0].name, "file2") == 0 || strcmp(dirents[0].name, "test_subdir") == 0); #ifdef HAVE_DIRENT_TYPES if (!strcmp(dirents[0].name, "test_subdir")) - ASSERT(dirents[0].type == UV_DIRENT_DIR); + ASSERT_EQ(dirents[0].type, UV_DIRENT_DIR); else - ASSERT(dirents[0].type == UV_DIRENT_FILE); + ASSERT_EQ(dirents[0].type, UV_DIRENT_FILE); #else - ASSERT(dirents[0].type == UV_DIRENT_UNKNOWN); + ASSERT_EQ(dirents[0].type, UV_DIRENT_UNKNOWN); #endif /* HAVE_DIRENT_TYPES */ ++non_empty_readdir_cb_count; @@ -323,9 +324,9 @@ static void non_empty_opendir_cb(uv_fs_t* req) { uv_dir_t* dir; int r; - ASSERT(req == &opendir_req); - ASSERT(req->fs_type == UV_FS_OPENDIR); - ASSERT(req->result == 0); + ASSERT_PTR_EQ(req, &opendir_req); + ASSERT_EQ(req->fs_type, UV_FS_OPENDIR); + ASSERT_OK(req->result); ASSERT_NOT_NULL(req->ptr); dir = req->ptr; @@ -336,7 +337,7 @@ static void non_empty_opendir_cb(uv_fs_t* req) { &readdir_req, dir, non_empty_readdir_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(req); ++non_empty_opendir_cb_count; } @@ -353,35 +354,35 @@ TEST_IMPL(fs_readdir_non_empty_dir) { cleanup_test_files(); r = uv_fs_mkdir(uv_default_loop(), &mkdir_req, "test_dir", 0755, NULL); - ASSERT(r == 0); + ASSERT_OK(r); /* Create two files synchronously. */ r = uv_fs_open(uv_default_loop(), &create_req, "test_dir/file1", - O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, + UV_FS_O_WRONLY | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); + ASSERT_GE(r, 0); uv_fs_req_cleanup(&create_req); r = uv_fs_close(uv_default_loop(), &close_req, create_req.result, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&close_req); r = uv_fs_open(uv_default_loop(), &create_req, "test_dir/file2", - O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, + UV_FS_O_WRONLY | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); + ASSERT_GE(r, 0); uv_fs_req_cleanup(&create_req); r = uv_fs_close(uv_default_loop(), &close_req, create_req.result, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&close_req); r = uv_fs_mkdir(uv_default_loop(), @@ -389,7 +390,7 @@ TEST_IMPL(fs_readdir_non_empty_dir) { "test_dir/test_subdir", 0755, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&mkdir_req); /* Fill the req to ensure that required fields are cleaned up. */ @@ -397,9 +398,9 @@ TEST_IMPL(fs_readdir_non_empty_dir) { /* Testing the synchronous flavor. */ r = uv_fs_opendir(uv_default_loop(), &opendir_req, "test_dir", NULL); - ASSERT(r == 0); - ASSERT(opendir_req.fs_type == UV_FS_OPENDIR); - ASSERT(opendir_req.result == 0); + ASSERT_OK(r); + ASSERT_EQ(opendir_req.fs_type, UV_FS_OPENDIR); + ASSERT_OK(opendir_req.result); ASSERT_NOT_NULL(opendir_req.ptr); entries_count = 0; @@ -417,23 +418,23 @@ TEST_IMPL(fs_readdir_non_empty_dir) { strcmp(dirents[0].name, "test_subdir") == 0); #ifdef HAVE_DIRENT_TYPES if (!strcmp(dirents[0].name, "test_subdir")) - ASSERT(dirents[0].type == UV_DIRENT_DIR); + ASSERT_EQ(dirents[0].type, UV_DIRENT_DIR); else - ASSERT(dirents[0].type == UV_DIRENT_FILE); + ASSERT_EQ(dirents[0].type, UV_DIRENT_FILE); #else - ASSERT(dirents[0].type == UV_DIRENT_UNKNOWN); + ASSERT_EQ(dirents[0].type, UV_DIRENT_UNKNOWN); #endif /* HAVE_DIRENT_TYPES */ uv_fs_req_cleanup(&readdir_req); ++entries_count; } - ASSERT(entries_count == 3); + ASSERT_EQ(3, entries_count); uv_fs_req_cleanup(&readdir_req); /* Fill the req to ensure that required fields are cleaned up. */ memset(&closedir_req, 0xdb, sizeof(closedir_req)); uv_fs_closedir(uv_default_loop(), &closedir_req, dir, NULL); - ASSERT(closedir_req.result == 0); + ASSERT_OK(closedir_req.result); uv_fs_req_cleanup(&closedir_req); /* Testing the asynchronous flavor. */ @@ -445,18 +446,103 @@ TEST_IMPL(fs_readdir_non_empty_dir) { &opendir_req, "test_dir", non_empty_opendir_cb); - ASSERT(r == 0); - ASSERT(non_empty_opendir_cb_count == 0); - ASSERT(non_empty_closedir_cb_count == 0); + ASSERT_OK(r); + ASSERT_OK(non_empty_opendir_cb_count); + ASSERT_OK(non_empty_closedir_cb_count); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); - ASSERT(non_empty_opendir_cb_count == 1); - ASSERT(non_empty_closedir_cb_count == 1); + ASSERT_OK(r); + ASSERT_EQ(1, non_empty_opendir_cb_count); + ASSERT_EQ(1, non_empty_closedir_cb_count); uv_fs_rmdir(uv_default_loop(), &rmdir_req, "test_subdir", NULL); uv_fs_req_cleanup(&rmdir_req); cleanup_test_files(); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } + +static void readdir_symlink_readdir_cb(uv_fs_t* req) { + uv_dir_t* dir; + + ASSERT_PTR_EQ(req, &readdir_req); + ASSERT_EQ(req->fs_type, UV_FS_READDIR); + dir = req->ptr; + + if (req->result == 0) { + uv_fs_req_cleanup(req); + ASSERT_EQ(3, non_empty_readdir_cb_count); + uv_fs_closedir(uv_default_loop(), + &closedir_req, + dir, + non_empty_closedir_cb); + } else { + if (strcmp(symlink_dirents[0].name, "test_symlink") == 0) { + ASSERT_EQ(symlink_dirents[0].type, UV_DIRENT_LINK); + } else { + ASSERT_EQ(symlink_dirents[1].type, UV_DIRENT_LINK); + } + uv_fs_req_cleanup(req); + } +} + +static void readdir_symlink_opendir_cb(uv_fs_t* req) { + uv_dir_t* dir; + int r; + + ASSERT_PTR_EQ(req, &opendir_req); + ASSERT_EQ(req->fs_type, UV_FS_OPENDIR); + ASSERT_OK(req->result); + ASSERT_NOT_NULL(req->ptr); + + dir = req->ptr; + dir->dirents = symlink_dirents; + dir->nentries = ARRAY_SIZE(symlink_dirents); + + r = uv_fs_readdir(uv_default_loop(), + &readdir_req, + dir, + readdir_symlink_readdir_cb); + ASSERT_OK(r); + uv_fs_req_cleanup(req); +} + +static void cleanup_symlink_test_files(void) { + uv_fs_t req; + + uv_fs_rmdir(NULL, &req, "test_symlink_dir/test_subdir", NULL); + uv_fs_req_cleanup(&req); + uv_fs_unlink(NULL, &req, "test_symlink_dir/test_symlink", NULL); + uv_fs_req_cleanup(&req); + uv_fs_rmdir(NULL, &req, "test_symlink_dir", NULL); + uv_fs_req_cleanup(&req); +} + +TEST_IMPL(fs_readdir_symlink) { + + uv_fs_t mkdir_req; + uv_fs_t symlink_req; + int r; + + cleanup_symlink_test_files(); + + r = uv_fs_mkdir(uv_default_loop(), &mkdir_req, "test_symlink_dir", 0755, NULL); + ASSERT_OK(r); + + r = uv_fs_mkdir(uv_default_loop(), &mkdir_req, "test_symlink_dir/test_subdir", 0755, NULL); + ASSERT_OK(r); + + r = uv_fs_symlink(uv_default_loop(), &symlink_req, "test_symlink_dir/test_subdir", "test_symlink_dir/test_symlink", UV_FS_SYMLINK_DIR, NULL); + ASSERT_OK(r); + + r = uv_fs_opendir(uv_default_loop(), &opendir_req, "test_symlink_dir", readdir_symlink_opendir_cb); + ASSERT_OK(r); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT_OK(r); + + cleanup_symlink_test_files(); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} diff --git a/test/test-fs.c b/test/test-fs.c index aecf10f9e28..423d72dd2f7 100644 --- a/test/test-fs.c +++ b/test/test-fs.c @@ -37,6 +37,9 @@ # ifndef ERROR_SYMLINK_NOT_SUPPORTED # define ERROR_SYMLINK_NOT_SUPPORTED 1464 # endif +# ifndef S_IFIFO +# define S_IFIFO _S_IFIFO +# endif # define unlink _unlink # define rmdir _rmdir # define open _open @@ -48,6 +51,9 @@ # ifndef lseek # define lseek _lseek # endif +# define S_IFDIR _S_IFDIR +# define S_IFCHR _S_IFCHR +# define S_IFREG _S_IFREG #endif #define TOO_LONG_NAME_LENGTH 65536 @@ -98,6 +104,7 @@ static uv_loop_t* loop; static uv_fs_t open_req1; static uv_fs_t open_req2; +static uv_fs_t open_req_noclose; static uv_fs_t read_req; static uv_fs_t write_req; static uv_fs_t unlink_req; @@ -167,8 +174,8 @@ static void check_permission(const char* filename, unsigned int mode) { uv_stat_t* s; r = uv_fs_stat(NULL, &req, filename, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); s = &req.statbuf; #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MSYS__) @@ -192,24 +199,24 @@ static void dummy_cb(uv_fs_t* req) { static void link_cb(uv_fs_t* req) { - ASSERT(req->fs_type == UV_FS_LINK); - ASSERT(req->result == 0); + ASSERT_EQ(req->fs_type, UV_FS_LINK); + ASSERT_OK(req->result); link_cb_count++; uv_fs_req_cleanup(req); } static void symlink_cb(uv_fs_t* req) { - ASSERT(req->fs_type == UV_FS_SYMLINK); - ASSERT(req->result == 0); + ASSERT_EQ(req->fs_type, UV_FS_SYMLINK); + ASSERT_OK(req->result); symlink_cb_count++; uv_fs_req_cleanup(req); } static void readlink_cb(uv_fs_t* req) { - ASSERT(req->fs_type == UV_FS_READLINK); - ASSERT(req->result == 0); - ASSERT(strcmp(req->ptr, "test_file_symlink2") == 0); + ASSERT_EQ(req->fs_type, UV_FS_READLINK); + ASSERT_OK(req->result); + ASSERT_OK(strcmp(req->ptr, "test_file_symlink2")); readlink_cb_count++; uv_fs_req_cleanup(req); } @@ -218,26 +225,16 @@ static void readlink_cb(uv_fs_t* req) { static void realpath_cb(uv_fs_t* req) { char test_file_abs_buf[PATHMAX]; size_t test_file_abs_size = sizeof(test_file_abs_buf); - ASSERT(req->fs_type == UV_FS_REALPATH); -#ifdef _WIN32 - /* - * Windows XP and Server 2003 don't support GetFinalPathNameByHandleW() - */ - if (req->result == UV_ENOSYS) { - realpath_cb_count++; - uv_fs_req_cleanup(req); - return; - } -#endif - ASSERT(req->result == 0); + ASSERT_EQ(req->fs_type, UV_FS_REALPATH); + ASSERT_OK(req->result); uv_cwd(test_file_abs_buf, &test_file_abs_size); #ifdef _WIN32 strcat(test_file_abs_buf, "\\test_file"); - ASSERT(stricmp(req->ptr, test_file_abs_buf) == 0); + ASSERT_OK(_stricmp(req->ptr, test_file_abs_buf)); #else strcat(test_file_abs_buf, "/test_file"); - ASSERT(strcmp(req->ptr, test_file_abs_buf) == 0); + ASSERT_OK(strcmp(req->ptr, test_file_abs_buf)); #endif realpath_cb_count++; uv_fs_req_cleanup(req); @@ -245,15 +242,15 @@ static void realpath_cb(uv_fs_t* req) { static void access_cb(uv_fs_t* req) { - ASSERT(req->fs_type == UV_FS_ACCESS); + ASSERT_EQ(req->fs_type, UV_FS_ACCESS); access_cb_count++; uv_fs_req_cleanup(req); } static void fchmod_cb(uv_fs_t* req) { - ASSERT(req->fs_type == UV_FS_FCHMOD); - ASSERT(req->result == 0); + ASSERT_EQ(req->fs_type, UV_FS_FCHMOD); + ASSERT_OK(req->result); fchmod_cb_count++; uv_fs_req_cleanup(req); check_permission("test_file", *(int*)req->data); @@ -261,8 +258,8 @@ static void fchmod_cb(uv_fs_t* req) { static void chmod_cb(uv_fs_t* req) { - ASSERT(req->fs_type == UV_FS_CHMOD); - ASSERT(req->result == 0); + ASSERT_EQ(req->fs_type, UV_FS_CHMOD); + ASSERT_OK(req->result); chmod_cb_count++; uv_fs_req_cleanup(req); check_permission("test_file", *(int*)req->data); @@ -270,50 +267,50 @@ static void chmod_cb(uv_fs_t* req) { static void fchown_cb(uv_fs_t* req) { - ASSERT(req->fs_type == UV_FS_FCHOWN); - ASSERT(req->result == 0); + ASSERT_EQ(req->fs_type, UV_FS_FCHOWN); + ASSERT_OK(req->result); fchown_cb_count++; uv_fs_req_cleanup(req); } static void chown_cb(uv_fs_t* req) { - ASSERT(req->fs_type == UV_FS_CHOWN); - ASSERT(req->result == 0); + ASSERT_EQ(req->fs_type, UV_FS_CHOWN); + ASSERT_OK(req->result); chown_cb_count++; uv_fs_req_cleanup(req); } static void lchown_cb(uv_fs_t* req) { - ASSERT(req->fs_type == UV_FS_LCHOWN); - ASSERT(req->result == 0); + ASSERT_EQ(req->fs_type, UV_FS_LCHOWN); + ASSERT_OK(req->result); lchown_cb_count++; uv_fs_req_cleanup(req); } static void chown_root_cb(uv_fs_t* req) { - ASSERT(req->fs_type == UV_FS_CHOWN); + ASSERT_EQ(req->fs_type, UV_FS_CHOWN); #if defined(_WIN32) || defined(__MSYS__) /* On windows, chown is a no-op and always succeeds. */ - ASSERT(req->result == 0); + ASSERT_OK(req->result); #else /* On unix, chown'ing the root directory is not allowed - * unless you're root, of course. */ if (geteuid() == 0) - ASSERT(req->result == 0); + ASSERT_OK(req->result); else # if defined(__CYGWIN__) /* On Cygwin, uid 0 is invalid (no root). */ - ASSERT(req->result == UV_EINVAL); + ASSERT_EQ(req->result, UV_EINVAL); # elif defined(__PASE__) /* On IBMi PASE, there is no root user. uid 0 is user qsecofr. - * User may grant qsecofr's privileges, including changing + * User may grant qsecofr's privileges, including changing * the file's ownership to uid 0. */ ASSERT(req->result == 0 || req->result == UV_EPERM); # else - ASSERT(req->result == UV_EPERM); + ASSERT_EQ(req->result, UV_EPERM); # endif #endif chown_cb_count++; @@ -321,18 +318,18 @@ static void chown_root_cb(uv_fs_t* req) { } static void unlink_cb(uv_fs_t* req) { - ASSERT(req == &unlink_req); - ASSERT(req->fs_type == UV_FS_UNLINK); - ASSERT(req->result == 0); + ASSERT_PTR_EQ(req, &unlink_req); + ASSERT_EQ(req->fs_type, UV_FS_UNLINK); + ASSERT_OK(req->result); unlink_cb_count++; uv_fs_req_cleanup(req); } static void fstat_cb(uv_fs_t* req) { uv_stat_t* s = req->ptr; - ASSERT(req->fs_type == UV_FS_FSTAT); - ASSERT(req->result == 0); - ASSERT(s->st_size == sizeof(test_buf)); + ASSERT_EQ(req->fs_type, UV_FS_FSTAT); + ASSERT_OK(req->result); + ASSERT_EQ(s->st_size, sizeof(test_buf)); uv_fs_req_cleanup(req); fstat_cb_count++; } @@ -341,29 +338,29 @@ static void fstat_cb(uv_fs_t* req) { static void statfs_cb(uv_fs_t* req) { uv_statfs_t* stats; - ASSERT(req->fs_type == UV_FS_STATFS); - ASSERT(req->result == 0); + ASSERT_EQ(req->fs_type, UV_FS_STATFS); + ASSERT_OK(req->result); ASSERT_NOT_NULL(req->ptr); stats = req->ptr; #if defined(_WIN32) || defined(__sun) || defined(_AIX) || defined(__MVS__) || \ defined(__OpenBSD__) || defined(__NetBSD__) - ASSERT(stats->f_type == 0); + ASSERT_OK(stats->f_type); #else - ASSERT(stats->f_type > 0); + ASSERT_UINT64_GT(stats->f_type, 0); #endif - ASSERT(stats->f_bsize > 0); - ASSERT(stats->f_blocks > 0); - ASSERT(stats->f_bfree <= stats->f_blocks); - ASSERT(stats->f_bavail <= stats->f_bfree); + ASSERT_GT(stats->f_bsize, 0); + ASSERT_GT(stats->f_blocks, 0); + ASSERT_LE(stats->f_bfree, stats->f_blocks); + ASSERT_LE(stats->f_bavail, stats->f_bfree); #ifdef _WIN32 - ASSERT(stats->f_files == 0); - ASSERT(stats->f_ffree == 0); + ASSERT_OK(stats->f_files); + ASSERT_OK(stats->f_ffree); #else /* There is no assertion for stats->f_files that makes sense, so ignore it. */ - ASSERT(stats->f_ffree <= stats->f_files); + ASSERT_LE(stats->f_ffree, stats->f_files); #endif uv_fs_req_cleanup(req); ASSERT_NULL(req->ptr); @@ -373,27 +370,27 @@ static void statfs_cb(uv_fs_t* req) { static void close_cb(uv_fs_t* req) { int r; - ASSERT(req == &close_req); - ASSERT(req->fs_type == UV_FS_CLOSE); - ASSERT(req->result == 0); + ASSERT_PTR_EQ(req, &close_req); + ASSERT_EQ(req->fs_type, UV_FS_CLOSE); + ASSERT_OK(req->result); close_cb_count++; uv_fs_req_cleanup(req); if (close_cb_count == 3) { r = uv_fs_unlink(loop, &unlink_req, "test_file2", unlink_cb); - ASSERT(r == 0); + ASSERT_OK(r); } } static void ftruncate_cb(uv_fs_t* req) { int r; - ASSERT(req == &ftruncate_req); - ASSERT(req->fs_type == UV_FS_FTRUNCATE); - ASSERT(req->result == 0); + ASSERT_PTR_EQ(req, &ftruncate_req); + ASSERT_EQ(req->fs_type, UV_FS_FTRUNCATE); + ASSERT_OK(req->result); ftruncate_cb_count++; uv_fs_req_cleanup(req); r = uv_fs_close(loop, &close_req, open_req1.result, close_cb); - ASSERT(r == 0); + ASSERT_OK(r); } static void fail_cb(uv_fs_t* req) { @@ -402,45 +399,45 @@ static void fail_cb(uv_fs_t* req) { static void read_cb(uv_fs_t* req) { int r; - ASSERT(req == &read_req); - ASSERT(req->fs_type == UV_FS_READ); - ASSERT(req->result >= 0); /* FIXME(bnoordhuis) Check if requested size? */ + ASSERT_PTR_EQ(req, &read_req); + ASSERT_EQ(req->fs_type, UV_FS_READ); + ASSERT_GE(req->result, 0); /* FIXME(bnoordhuis) Check if requested size? */ read_cb_count++; uv_fs_req_cleanup(req); if (read_cb_count == 1) { - ASSERT(strcmp(buf, test_buf) == 0); + ASSERT_OK(strcmp(buf, test_buf)); r = uv_fs_ftruncate(loop, &ftruncate_req, open_req1.result, 7, ftruncate_cb); } else { - ASSERT(strcmp(buf, "test-bu") == 0); + ASSERT_OK(strcmp(buf, "test-bu")); r = uv_fs_close(loop, &close_req, open_req1.result, close_cb); } - ASSERT(r == 0); + ASSERT_OK(r); } static void open_cb(uv_fs_t* req) { int r; - ASSERT(req == &open_req1); - ASSERT(req->fs_type == UV_FS_OPEN); + ASSERT_PTR_EQ(req, &open_req1); + ASSERT_EQ(req->fs_type, UV_FS_OPEN); if (req->result < 0) { fprintf(stderr, "async open error: %d\n", (int) req->result); ASSERT(0); } open_cb_count++; ASSERT(req->path); - ASSERT(memcmp(req->path, "test_file2\0", 11) == 0); + ASSERT_OK(memcmp(req->path, "test_file2\0", 11)); uv_fs_req_cleanup(req); memset(buf, 0, sizeof(buf)); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(loop, &read_req, open_req1.result, &iov, 1, -1, read_cb); - ASSERT(r == 0); + ASSERT_OK(r); } static void open_cb_simple(uv_fs_t* req) { - ASSERT(req->fs_type == UV_FS_OPEN); + ASSERT_EQ(req->fs_type, UV_FS_OPEN); if (req->result < 0) { fprintf(stderr, "async open error: %d\n", (int) req->result); ASSERT(0); @@ -453,69 +450,69 @@ static void open_cb_simple(uv_fs_t* req) { static void fsync_cb(uv_fs_t* req) { int r; - ASSERT(req == &fsync_req); - ASSERT(req->fs_type == UV_FS_FSYNC); - ASSERT(req->result == 0); + ASSERT_PTR_EQ(req, &fsync_req); + ASSERT_EQ(req->fs_type, UV_FS_FSYNC); + ASSERT_OK(req->result); fsync_cb_count++; uv_fs_req_cleanup(req); r = uv_fs_close(loop, &close_req, open_req1.result, close_cb); - ASSERT(r == 0); + ASSERT_OK(r); } static void fdatasync_cb(uv_fs_t* req) { int r; - ASSERT(req == &fdatasync_req); - ASSERT(req->fs_type == UV_FS_FDATASYNC); - ASSERT(req->result == 0); + ASSERT_PTR_EQ(req, &fdatasync_req); + ASSERT_EQ(req->fs_type, UV_FS_FDATASYNC); + ASSERT_OK(req->result); fdatasync_cb_count++; uv_fs_req_cleanup(req); r = uv_fs_fsync(loop, &fsync_req, open_req1.result, fsync_cb); - ASSERT(r == 0); + ASSERT_OK(r); } static void write_cb(uv_fs_t* req) { int r; - ASSERT(req == &write_req); - ASSERT(req->fs_type == UV_FS_WRITE); - ASSERT(req->result >= 0); /* FIXME(bnoordhuis) Check if requested size? */ + ASSERT_PTR_EQ(req, &write_req); + ASSERT_EQ(req->fs_type, UV_FS_WRITE); + ASSERT_GE(req->result, 0); /* FIXME(bnoordhuis) Check if requested size? */ write_cb_count++; uv_fs_req_cleanup(req); r = uv_fs_fdatasync(loop, &fdatasync_req, open_req1.result, fdatasync_cb); - ASSERT(r == 0); + ASSERT_OK(r); } static void create_cb(uv_fs_t* req) { int r; - ASSERT(req == &open_req1); - ASSERT(req->fs_type == UV_FS_OPEN); - ASSERT(req->result >= 0); + ASSERT_PTR_EQ(req, &open_req1); + ASSERT_EQ(req->fs_type, UV_FS_OPEN); + ASSERT_GE(req->result, 0); create_cb_count++; uv_fs_req_cleanup(req); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(loop, &write_req, req->result, &iov, 1, -1, write_cb); - ASSERT(r == 0); + ASSERT_OK(r); } static void rename_cb(uv_fs_t* req) { - ASSERT(req == &rename_req); - ASSERT(req->fs_type == UV_FS_RENAME); - ASSERT(req->result == 0); + ASSERT_PTR_EQ(req, &rename_req); + ASSERT_EQ(req->fs_type, UV_FS_RENAME); + ASSERT_OK(req->result); rename_cb_count++; uv_fs_req_cleanup(req); } static void mkdir_cb(uv_fs_t* req) { - ASSERT(req == &mkdir_req); - ASSERT(req->fs_type == UV_FS_MKDIR); - ASSERT(req->result == 0); + ASSERT_PTR_EQ(req, &mkdir_req); + ASSERT_EQ(req->fs_type, UV_FS_MKDIR); + ASSERT_OK(req->result); mkdir_cb_count++; ASSERT(req->path); - ASSERT(memcmp(req->path, "test_dir\0", 9) == 0); + ASSERT_OK(memcmp(req->path, "test_dir\0", 9)); uv_fs_req_cleanup(req); } @@ -523,24 +520,24 @@ static void mkdir_cb(uv_fs_t* req) { static void check_mkdtemp_result(uv_fs_t* req) { int r; - ASSERT(req->fs_type == UV_FS_MKDTEMP); - ASSERT(req->result == 0); + ASSERT_EQ(req->fs_type, UV_FS_MKDTEMP); + ASSERT_OK(req->result); ASSERT(req->path); - ASSERT(strlen(req->path) == 15); - ASSERT(memcmp(req->path, "test_dir_", 9) == 0); - ASSERT(memcmp(req->path + 9, "XXXXXX", 6) != 0); + ASSERT_EQ(15, strlen(req->path)); + ASSERT_OK(memcmp(req->path, "test_dir_", 9)); + ASSERT_NE(0, memcmp(req->path + 9, "XXXXXX", 6)); check_permission(req->path, 0700); /* Check if req->path is actually a directory */ r = uv_fs_stat(NULL, &stat_req, req->path, NULL); - ASSERT(r == 0); + ASSERT_OK(r); ASSERT(((uv_stat_t*)stat_req.ptr)->st_mode & S_IFDIR); uv_fs_req_cleanup(&stat_req); } static void mkdtemp_cb(uv_fs_t* req) { - ASSERT(req == &mkdtemp_req1); + ASSERT_PTR_EQ(req, &mkdtemp_req1); check_mkdtemp_result(req); mkdtemp_cb_count++; } @@ -549,36 +546,36 @@ static void mkdtemp_cb(uv_fs_t* req) { static void check_mkstemp_result(uv_fs_t* req) { int r; - ASSERT(req->fs_type == UV_FS_MKSTEMP); - ASSERT(req->result >= 0); + ASSERT_EQ(req->fs_type, UV_FS_MKSTEMP); + ASSERT_GE(req->result, 0); ASSERT(req->path); - ASSERT(strlen(req->path) == 16); - ASSERT(memcmp(req->path, "test_file_", 10) == 0); - ASSERT(memcmp(req->path + 10, "XXXXXX", 6) != 0); + ASSERT_EQ(16, strlen(req->path)); + ASSERT_OK(memcmp(req->path, "test_file_", 10)); + ASSERT_NE(0, memcmp(req->path + 10, "XXXXXX", 6)); check_permission(req->path, 0600); /* Check if req->path is actually a file */ r = uv_fs_stat(NULL, &stat_req, req->path, NULL); - ASSERT(r == 0); + ASSERT_OK(r); ASSERT(stat_req.statbuf.st_mode & S_IFREG); uv_fs_req_cleanup(&stat_req); } static void mkstemp_cb(uv_fs_t* req) { - ASSERT(req == &mkstemp_req1); + ASSERT_PTR_EQ(req, &mkstemp_req1); check_mkstemp_result(req); mkstemp_cb_count++; } static void rmdir_cb(uv_fs_t* req) { - ASSERT(req == &rmdir_req); - ASSERT(req->fs_type == UV_FS_RMDIR); - ASSERT(req->result == 0); + ASSERT_PTR_EQ(req, &rmdir_req); + ASSERT_EQ(req->fs_type, UV_FS_RMDIR); + ASSERT_OK(req->result); rmdir_cb_count++; ASSERT(req->path); - ASSERT(memcmp(req->path, "test_dir\0", 9) == 0); + ASSERT_OK(memcmp(req->path, "test_dir\0", 9)); uv_fs_req_cleanup(req); } @@ -595,21 +592,21 @@ static void assert_is_file_type(uv_dirent_t dent) { * https://github.com/libuv/libuv/issues/501 */ #if defined(__APPLE__) || defined(_WIN32) - ASSERT(dent.type == UV_DIRENT_FILE); + ASSERT_EQ(dent.type, UV_DIRENT_FILE); #else ASSERT(dent.type == UV_DIRENT_FILE || dent.type == UV_DIRENT_UNKNOWN); #endif #else - ASSERT(dent.type == UV_DIRENT_UNKNOWN); + ASSERT_EQ(dent.type, UV_DIRENT_UNKNOWN); #endif } static void scandir_cb(uv_fs_t* req) { uv_dirent_t dent; - ASSERT(req == &scandir_req); - ASSERT(req->fs_type == UV_FS_SCANDIR); - ASSERT(req->result == 2); + ASSERT_PTR_EQ(req, &scandir_req); + ASSERT_EQ(req->fs_type, UV_FS_SCANDIR); + ASSERT_EQ(2, req->result); ASSERT(req->ptr); while (UV_EOF != uv_fs_scandir_next(req, &dent)) { @@ -618,7 +615,7 @@ static void scandir_cb(uv_fs_t* req) { } scandir_cb_count++; ASSERT(req->path); - ASSERT(memcmp(req->path, "test_dir\0", 9) == 0); + ASSERT_OK(memcmp(req->path, "test_dir\0", 9)); uv_fs_req_cleanup(req); ASSERT(!req->ptr); } @@ -627,11 +624,11 @@ static void scandir_cb(uv_fs_t* req) { static void empty_scandir_cb(uv_fs_t* req) { uv_dirent_t dent; - ASSERT(req == &scandir_req); - ASSERT(req->fs_type == UV_FS_SCANDIR); - ASSERT(req->result == 0); + ASSERT_PTR_EQ(req, &scandir_req); + ASSERT_EQ(req->fs_type, UV_FS_SCANDIR); + ASSERT_OK(req->result); ASSERT_NULL(req->ptr); - ASSERT(UV_EOF == uv_fs_scandir_next(req, &dent)); + ASSERT_EQ(UV_EOF, uv_fs_scandir_next(req, &dent)); uv_fs_req_cleanup(req); scandir_cb_count++; } @@ -639,20 +636,20 @@ static void empty_scandir_cb(uv_fs_t* req) { static void non_existent_scandir_cb(uv_fs_t* req) { uv_dirent_t dent; - ASSERT(req == &scandir_req); - ASSERT(req->fs_type == UV_FS_SCANDIR); - ASSERT(req->result == UV_ENOENT); + ASSERT_PTR_EQ(req, &scandir_req); + ASSERT_EQ(req->fs_type, UV_FS_SCANDIR); + ASSERT_EQ(req->result, UV_ENOENT); ASSERT_NULL(req->ptr); - ASSERT(UV_ENOENT == uv_fs_scandir_next(req, &dent)); + ASSERT_EQ(UV_ENOENT, uv_fs_scandir_next(req, &dent)); uv_fs_req_cleanup(req); scandir_cb_count++; } static void file_scandir_cb(uv_fs_t* req) { - ASSERT(req == &scandir_req); - ASSERT(req->fs_type == UV_FS_SCANDIR); - ASSERT(req->result == UV_ENOTDIR); + ASSERT_PTR_EQ(req, &scandir_req); + ASSERT_EQ(req->fs_type, UV_FS_SCANDIR); + ASSERT_EQ(req->result, UV_ENOTDIR); ASSERT_NULL(req->ptr); uv_fs_req_cleanup(req); scandir_cb_count++; @@ -660,9 +657,18 @@ static void file_scandir_cb(uv_fs_t* req) { static void stat_cb(uv_fs_t* req) { - ASSERT(req == &stat_req); + ASSERT_PTR_EQ(req, &stat_req); + ASSERT(req->fs_type == UV_FS_STAT || req->fs_type == UV_FS_LSTAT); + ASSERT_OK(req->result); + ASSERT(req->ptr); + stat_cb_count++; + uv_fs_req_cleanup(req); + ASSERT(!req->ptr); +} + +static void stat_batch_cb(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_STAT || req->fs_type == UV_FS_LSTAT); - ASSERT(req->result == 0); + ASSERT_OK(req->result); ASSERT(req->ptr); stat_cb_count++; uv_fs_req_cleanup(req); @@ -671,40 +677,40 @@ static void stat_cb(uv_fs_t* req) { static void sendfile_cb(uv_fs_t* req) { - ASSERT(req == &sendfile_req); - ASSERT(req->fs_type == UV_FS_SENDFILE); - ASSERT(req->result == 65545); + ASSERT_PTR_EQ(req, &sendfile_req); + ASSERT_EQ(req->fs_type, UV_FS_SENDFILE); + ASSERT_EQ(65545, req->result); sendfile_cb_count++; uv_fs_req_cleanup(req); } static void sendfile_nodata_cb(uv_fs_t* req) { - ASSERT(req == &sendfile_req); - ASSERT(req->fs_type == UV_FS_SENDFILE); - ASSERT(req->result == 0); + ASSERT_PTR_EQ(req, &sendfile_req); + ASSERT_EQ(req->fs_type, UV_FS_SENDFILE); + ASSERT_OK(req->result); sendfile_cb_count++; uv_fs_req_cleanup(req); } static void open_noent_cb(uv_fs_t* req) { - ASSERT(req->fs_type == UV_FS_OPEN); - ASSERT(req->result == UV_ENOENT); + ASSERT_EQ(req->fs_type, UV_FS_OPEN); + ASSERT_EQ(req->result, UV_ENOENT); open_cb_count++; uv_fs_req_cleanup(req); } static void open_nametoolong_cb(uv_fs_t* req) { - ASSERT(req->fs_type == UV_FS_OPEN); - ASSERT(req->result == UV_ENAMETOOLONG); + ASSERT_EQ(req->fs_type, UV_FS_OPEN); + ASSERT_EQ(req->result, UV_ENAMETOOLONG); open_cb_count++; uv_fs_req_cleanup(req); } static void open_loop_cb(uv_fs_t* req) { - ASSERT(req->fs_type == UV_FS_OPEN); - ASSERT(req->result == UV_ELOOP); + ASSERT_EQ(req->fs_type, UV_FS_OPEN); + ASSERT_EQ(req->result, UV_ELOOP); open_cb_count++; uv_fs_req_cleanup(req); } @@ -716,21 +722,22 @@ TEST_IMPL(fs_file_noent) { loop = uv_default_loop(); - r = uv_fs_open(NULL, &req, "does_not_exist", O_RDONLY, 0, NULL); - ASSERT(r == UV_ENOENT); - ASSERT(req.result == UV_ENOENT); + r = uv_fs_open(NULL, &req, "does_not_exist", UV_FS_O_RDONLY, 0, NULL); + ASSERT_EQ(r, UV_ENOENT); + ASSERT_EQ(req.result, UV_ENOENT); uv_fs_req_cleanup(&req); - r = uv_fs_open(loop, &req, "does_not_exist", O_RDONLY, 0, open_noent_cb); - ASSERT(r == 0); + r = uv_fs_open(loop, &req, "does_not_exist", UV_FS_O_RDONLY, 0, + open_noent_cb); + ASSERT_OK(r); - ASSERT(open_cb_count == 0); + ASSERT_OK(open_cb_count); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(open_cb_count == 1); + ASSERT_EQ(1, open_cb_count); /* TODO add EACCES test */ - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -744,19 +751,19 @@ TEST_IMPL(fs_file_nametoolong) { memset(name, 'a', TOO_LONG_NAME_LENGTH); name[TOO_LONG_NAME_LENGTH] = 0; - r = uv_fs_open(NULL, &req, name, O_RDONLY, 0, NULL); - ASSERT(r == UV_ENAMETOOLONG); - ASSERT(req.result == UV_ENAMETOOLONG); + r = uv_fs_open(NULL, &req, name, UV_FS_O_RDONLY, 0, NULL); + ASSERT_EQ(r, UV_ENAMETOOLONG); + ASSERT_EQ(req.result, UV_ENAMETOOLONG); uv_fs_req_cleanup(&req); - r = uv_fs_open(loop, &req, name, O_RDONLY, 0, open_nametoolong_cb); - ASSERT(r == 0); + r = uv_fs_open(loop, &req, name, UV_FS_O_RDONLY, 0, open_nametoolong_cb); + ASSERT_OK(r); - ASSERT(open_cb_count == 0); + ASSERT_OK(open_cb_count); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(open_cb_count == 1); + ASSERT_EQ(1, open_cb_count); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -770,11 +777,10 @@ TEST_IMPL(fs_file_loop) { r = uv_fs_symlink(NULL, &req, "test_symlink", "test_symlink", 0, NULL); #ifdef _WIN32 /* - * Windows XP and Server 2003 don't support symlinks; we'll get UV_ENOTSUP. - * Starting with vista they are supported, but only when elevated, otherwise + * Symlinks are only suported but only when elevated, otherwise * we'll see UV_EPERM. */ - if (r == UV_ENOTSUP || r == UV_EPERM) + if (r == UV_EPERM) return 0; #elif defined(__MSYS__) /* MSYS2's approximation of symlinks with copies does not work for broken @@ -782,24 +788,24 @@ TEST_IMPL(fs_file_loop) { if (r == UV_ENOENT) return 0; #endif - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&req); - r = uv_fs_open(NULL, &req, "test_symlink", O_RDONLY, 0, NULL); - ASSERT(r == UV_ELOOP); - ASSERT(req.result == UV_ELOOP); + r = uv_fs_open(NULL, &req, "test_symlink", UV_FS_O_RDONLY, 0, NULL); + ASSERT_EQ(r, UV_ELOOP); + ASSERT_EQ(req.result, UV_ELOOP); uv_fs_req_cleanup(&req); - r = uv_fs_open(loop, &req, "test_symlink", O_RDONLY, 0, open_loop_cb); - ASSERT(r == 0); + r = uv_fs_open(loop, &req, "test_symlink", UV_FS_O_RDONLY, 0, open_loop_cb); + ASSERT_OK(r); - ASSERT(open_cb_count == 0); + ASSERT_OK(open_cb_count); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(open_cb_count == 1); + ASSERT_EQ(1, open_cb_count); unlink("test_symlink"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -816,9 +822,9 @@ static void check_utime(const char* path, else r = uv_fs_stat(loop, &req, path, NULL); - ASSERT_EQ(r, 0); + ASSERT_OK(r); - ASSERT_EQ(req.result, 0); + ASSERT_OK(req.result); s = &req.statbuf; if (s->st_atim.tv_nsec == 0 && s->st_mtim.tv_nsec == 0) { @@ -851,7 +857,12 @@ static void check_utime(const char* path, #endif st_atim = s->st_atim.tv_sec + s->st_atim.tv_nsec / 1e9; st_mtim = s->st_mtim.tv_sec + s->st_mtim.tv_nsec / 1e9; - ASSERT_DOUBLE_EQ(st_atim, atime); + /* + * Linux does not allow reading reliably the atime of a symlink + * since readlink() can update it + */ + if (!test_lutime) + ASSERT_DOUBLE_EQ(st_atim, atime); ASSERT_DOUBLE_EQ(st_mtim, mtime); } @@ -862,9 +873,9 @@ static void check_utime(const char* path, static void utime_cb(uv_fs_t* req) { utime_check_t* c; - ASSERT(req == &utime_req); - ASSERT(req->result == 0); - ASSERT(req->fs_type == UV_FS_UTIME); + ASSERT_PTR_EQ(req, &utime_req); + ASSERT_OK(req->result); + ASSERT_EQ(req->fs_type, UV_FS_UTIME); c = req->data; check_utime(c->path, c->atime, c->mtime, /* test_lutime */ 0); @@ -877,9 +888,9 @@ static void utime_cb(uv_fs_t* req) { static void futime_cb(uv_fs_t* req) { utime_check_t* c; - ASSERT(req == &futime_req); - ASSERT(req->result == 0); - ASSERT(req->fs_type == UV_FS_FUTIME); + ASSERT_PTR_EQ(req, &futime_req); + ASSERT_OK(req->result); + ASSERT_EQ(req->fs_type, UV_FS_FUTIME); c = req->data; check_utime(c->path, c->atime, c->mtime, /* test_lutime */ 0); @@ -892,8 +903,8 @@ static void futime_cb(uv_fs_t* req) { static void lutime_cb(uv_fs_t* req) { utime_check_t* c; - ASSERT(req->result == 0); - ASSERT(req->fs_type == UV_FS_LUTIME); + ASSERT_OK(req->result); + ASSERT_EQ(req->fs_type, UV_FS_LUTIME); c = req->data; check_utime(c->path, c->atime, c->mtime, /* test_lutime */ 1); @@ -912,56 +923,56 @@ TEST_IMPL(fs_file_async) { loop = uv_default_loop(); - r = uv_fs_open(loop, &open_req1, "test_file", O_WRONLY | O_CREAT, + r = uv_fs_open(loop, &open_req1, "test_file", UV_FS_O_WRONLY | UV_FS_O_CREAT, S_IRUSR | S_IWUSR, create_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(create_cb_count == 1); - ASSERT(write_cb_count == 1); - ASSERT(fsync_cb_count == 1); - ASSERT(fdatasync_cb_count == 1); - ASSERT(close_cb_count == 1); + ASSERT_EQ(1, create_cb_count); + ASSERT_EQ(1, write_cb_count); + ASSERT_EQ(1, fsync_cb_count); + ASSERT_EQ(1, fdatasync_cb_count); + ASSERT_EQ(1, close_cb_count); r = uv_fs_rename(loop, &rename_req, "test_file", "test_file2", rename_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(create_cb_count == 1); - ASSERT(write_cb_count == 1); - ASSERT(close_cb_count == 1); - ASSERT(rename_cb_count == 1); + ASSERT_EQ(1, create_cb_count); + ASSERT_EQ(1, write_cb_count); + ASSERT_EQ(1, close_cb_count); + ASSERT_EQ(1, rename_cb_count); - r = uv_fs_open(loop, &open_req1, "test_file2", O_RDWR, 0, open_cb); - ASSERT(r == 0); + r = uv_fs_open(loop, &open_req1, "test_file2", UV_FS_O_RDWR, 0, open_cb); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(open_cb_count == 1); - ASSERT(read_cb_count == 1); - ASSERT(close_cb_count == 2); - ASSERT(rename_cb_count == 1); - ASSERT(create_cb_count == 1); - ASSERT(write_cb_count == 1); - ASSERT(ftruncate_cb_count == 1); + ASSERT_EQ(1, open_cb_count); + ASSERT_EQ(1, read_cb_count); + ASSERT_EQ(2, close_cb_count); + ASSERT_EQ(1, rename_cb_count); + ASSERT_EQ(1, create_cb_count); + ASSERT_EQ(1, write_cb_count); + ASSERT_EQ(1, ftruncate_cb_count); - r = uv_fs_open(loop, &open_req1, "test_file2", O_RDONLY, 0, open_cb); - ASSERT(r == 0); + r = uv_fs_open(loop, &open_req1, "test_file2", UV_FS_O_RDONLY, 0, open_cb); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(open_cb_count == 2); - ASSERT(read_cb_count == 2); - ASSERT(close_cb_count == 3); - ASSERT(rename_cb_count == 1); - ASSERT(unlink_cb_count == 1); - ASSERT(create_cb_count == 1); - ASSERT(write_cb_count == 1); - ASSERT(ftruncate_cb_count == 1); + ASSERT_EQ(2, open_cb_count); + ASSERT_EQ(2, read_cb_count); + ASSERT_EQ(3, close_cb_count); + ASSERT_EQ(1, rename_cb_count); + ASSERT_EQ(1, unlink_cb_count); + ASSERT_EQ(1, create_cb_count); + ASSERT_EQ(1, write_cb_count); + ASSERT_EQ(1, ftruncate_cb_count); /* Cleanup. */ unlink("test_file"); unlink("test_file2"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -976,71 +987,73 @@ static void fs_file_sync(int add_flags) { loop = uv_default_loop(); r = uv_fs_open(loop, &open_req1, "test_file", - O_WRONLY | O_CREAT | add_flags, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + UV_FS_O_WRONLY | UV_FS_O_CREAT | add_flags, S_IWUSR | S_IRUSR, + NULL); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); - ASSERT(r >= 0); - ASSERT(write_req.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(write_req.result, 0); uv_fs_req_cleanup(&write_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); - r = uv_fs_open(NULL, &open_req1, "test_file", O_RDWR | add_flags, 0, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + r = uv_fs_open(NULL, &open_req1, "test_file", UV_FS_O_RDWR | add_flags, 0, + NULL); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); - ASSERT(r >= 0); - ASSERT(read_req.result >= 0); - ASSERT(strcmp(buf, test_buf) == 0); + ASSERT_GE(r, 0); + ASSERT_GE(read_req.result, 0); + ASSERT_OK(strcmp(buf, test_buf)); uv_fs_req_cleanup(&read_req); r = uv_fs_ftruncate(NULL, &ftruncate_req, open_req1.result, 7, NULL); - ASSERT(r == 0); - ASSERT(ftruncate_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(ftruncate_req.result); uv_fs_req_cleanup(&ftruncate_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); r = uv_fs_rename(NULL, &rename_req, "test_file", "test_file2", NULL); - ASSERT(r == 0); - ASSERT(rename_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(rename_req.result); uv_fs_req_cleanup(&rename_req); - r = uv_fs_open(NULL, &open_req1, "test_file2", O_RDONLY | add_flags, 0, + r = uv_fs_open(NULL, &open_req1, "test_file2", UV_FS_O_RDONLY | add_flags, 0, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); memset(buf, 0, sizeof(buf)); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); - ASSERT(r >= 0); - ASSERT(read_req.result >= 0); - ASSERT(strcmp(buf, "test-bu") == 0); + ASSERT_GE(r, 0); + ASSERT_GE(read_req.result, 0); + ASSERT_OK(strcmp(buf, "test-bu")); uv_fs_req_cleanup(&read_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); r = uv_fs_unlink(NULL, &unlink_req, "test_file2", NULL); - ASSERT(r == 0); - ASSERT(unlink_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(unlink_req.result); uv_fs_req_cleanup(&unlink_req); /* Cleanup */ @@ -1051,10 +1064,54 @@ TEST_IMPL(fs_file_sync) { fs_file_sync(0); fs_file_sync(UV_FS_O_FILEMAP); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } +TEST_IMPL(fs_posix_delete) { + int r; + + /* Setup. */ + unlink("test_dir/file"); + rmdir("test_dir"); + + r = uv_fs_mkdir(NULL, &mkdir_req, "test_dir", 0755, NULL); + ASSERT_OK(r); + + r = uv_fs_open(NULL, &open_req_noclose, "test_dir/file", UV_FS_O_WRONLY | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); + ASSERT_GE(r, 0); + uv_fs_req_cleanup(&open_req_noclose); + + /* should not be possible to delete the non-empty dir */ + r = uv_fs_rmdir(NULL, &rmdir_req, "test_dir", NULL); + ASSERT((r == UV_ENOTEMPTY) || (r == UV_EEXIST)); + ASSERT_EQ(r, rmdir_req.result); + uv_fs_req_cleanup(&rmdir_req); + + r = uv_fs_rmdir(NULL, &rmdir_req, "test_dir/file", NULL); + ASSERT((r == UV_ENOTDIR) || (r == UV_ENOENT)); + ASSERT_EQ(r, rmdir_req.result); + uv_fs_req_cleanup(&rmdir_req); + + r = uv_fs_unlink(NULL, &unlink_req, "test_dir/file", NULL); + ASSERT_OK(r); + ASSERT_OK(unlink_req.result); + uv_fs_req_cleanup(&unlink_req); + + /* delete the dir while the file is still open, which should succeed on posix */ + r = uv_fs_rmdir(NULL, &rmdir_req, "test_dir", NULL); + ASSERT_OK(r); + ASSERT_OK(rmdir_req.result); + uv_fs_req_cleanup(&rmdir_req); + + /* Cleanup */ + r = uv_fs_close(NULL, &close_req, open_req_noclose.result, NULL); + ASSERT_OK(r); + uv_fs_req_cleanup(&close_req); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} static void fs_file_write_null_buffer(int add_flags) { int r; @@ -1065,20 +1122,21 @@ static void fs_file_write_null_buffer(int add_flags) { loop = uv_default_loop(); r = uv_fs_open(NULL, &open_req1, "test_file", - O_WRONLY | O_CREAT | add_flags, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + UV_FS_O_WRONLY | UV_FS_O_CREAT | add_flags, S_IWUSR | S_IRUSR, + NULL); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); iov = uv_buf_init(NULL, 0); r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); - ASSERT(r == 0); - ASSERT(write_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(write_req.result); uv_fs_req_cleanup(&write_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); unlink("test_file"); @@ -1087,7 +1145,7 @@ TEST_IMPL(fs_file_write_null_buffer) { fs_file_write_null_buffer(0); fs_file_write_null_buffer(UV_FS_O_FILEMAP); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -1104,38 +1162,40 @@ TEST_IMPL(fs_async_dir) { loop = uv_default_loop(); r = uv_fs_mkdir(loop, &mkdir_req, "test_dir", 0755, mkdir_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(mkdir_cb_count == 1); + ASSERT_EQ(1, mkdir_cb_count); /* Create 2 files synchronously. */ - r = uv_fs_open(NULL, &open_req1, "test_dir/file1", O_WRONLY | O_CREAT, + r = uv_fs_open(NULL, &open_req1, "test_dir/file1", + UV_FS_O_WRONLY | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); + ASSERT_GE(r, 0); uv_fs_req_cleanup(&open_req1); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&close_req); - r = uv_fs_open(NULL, &open_req1, "test_dir/file2", O_WRONLY | O_CREAT, + r = uv_fs_open(NULL, &open_req1, "test_dir/file2", + UV_FS_O_WRONLY | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); + ASSERT_GE(r, 0); uv_fs_req_cleanup(&open_req1); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&close_req); r = uv_fs_scandir(loop, &scandir_req, "test_dir", 0, scandir_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(scandir_cb_count == 1); + ASSERT_EQ(1, scandir_cb_count); /* sync uv_fs_scandir */ r = uv_fs_scandir(NULL, &scandir_req, "test_dir", 0, NULL); - ASSERT(r == 2); - ASSERT(scandir_req.result == 2); + ASSERT_EQ(2, r); + ASSERT_EQ(2, scandir_req.result); ASSERT(scandir_req.ptr); while (UV_EOF != uv_fs_scandir_next(&scandir_req, &dent)) { ASSERT(strcmp(dent.name, "file1") == 0 || strcmp(dent.name, "file2") == 0); @@ -1145,49 +1205,49 @@ TEST_IMPL(fs_async_dir) { ASSERT(!scandir_req.ptr); r = uv_fs_stat(loop, &stat_req, "test_dir", stat_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); r = uv_fs_stat(loop, &stat_req, "test_dir/", stat_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); r = uv_fs_lstat(loop, &stat_req, "test_dir", stat_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); r = uv_fs_lstat(loop, &stat_req, "test_dir/", stat_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(stat_cb_count == 4); + ASSERT_EQ(4, stat_cb_count); r = uv_fs_unlink(loop, &unlink_req, "test_dir/file1", unlink_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(unlink_cb_count == 1); + ASSERT_EQ(1, unlink_cb_count); r = uv_fs_unlink(loop, &unlink_req, "test_dir/file2", unlink_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(unlink_cb_count == 2); + ASSERT_EQ(2, unlink_cb_count); r = uv_fs_rmdir(loop, &rmdir_req, "test_dir", rmdir_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(rmdir_cb_count == 1); + ASSERT_EQ(1, rmdir_cb_count); /* Cleanup */ unlink("test_dir/file1"); unlink("test_dir/file2"); rmdir("test_dir"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } -static int test_sendfile(void (*setup)(int), uv_fs_cb cb, off_t expected_size) { +static int test_sendfile(void (*setup)(int), uv_fs_cb cb, size_t expected_size) { int f, r; struct stat s1, s2; uv_fs_t req; @@ -1199,57 +1259,59 @@ static int test_sendfile(void (*setup)(int), uv_fs_cb cb, off_t expected_size) { unlink("test_file"); unlink("test_file2"); - f = open("test_file", O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR); - ASSERT(f != -1); + f = open("test_file", UV_FS_O_WRONLY | UV_FS_O_CREAT, S_IWUSR | S_IRUSR); + ASSERT_NE(f, -1); if (setup != NULL) setup(f); r = close(f); - ASSERT(r == 0); + ASSERT_OK(r); /* Test starts here. */ - r = uv_fs_open(NULL, &open_req1, "test_file", O_RDWR, 0, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + r = uv_fs_open(NULL, &open_req1, "test_file", UV_FS_O_RDWR, 0, NULL); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); - r = uv_fs_open(NULL, &open_req2, "test_file2", O_WRONLY | O_CREAT, + r = uv_fs_open(NULL, &open_req2, "test_file2", UV_FS_O_WRONLY | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req2.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(open_req2.result, 0); uv_fs_req_cleanup(&open_req2); r = uv_fs_sendfile(loop, &sendfile_req, open_req2.result, open_req1.result, 1, 131072, cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(sendfile_cb_count == 1); + ASSERT_EQ(1, sendfile_cb_count); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&close_req); r = uv_fs_close(NULL, &close_req, open_req2.result, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&close_req); - ASSERT(0 == stat("test_file", &s1)); - ASSERT(0 == stat("test_file2", &s2)); - ASSERT(s2.st_size == expected_size); + memset(&s1, 0, sizeof(s1)); + memset(&s2, 0, sizeof(s2)); + ASSERT_OK(stat("test_file", &s1)); + ASSERT_OK(stat("test_file2", &s2)); + ASSERT_EQ(s2.st_size, expected_size); if (expected_size > 0) { ASSERT_UINT64_EQ(s1.st_size, s2.st_size + 1); - r = uv_fs_open(NULL, &open_req1, "test_file2", O_RDWR, 0, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + r = uv_fs_open(NULL, &open_req1, "test_file2", UV_FS_O_RDWR, 0, NULL); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); memset(buf1, 0, sizeof(buf1)); iov = uv_buf_init(buf1, sizeof(buf1)); r = uv_fs_read(NULL, &req, open_req1.result, &iov, 1, -1, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); ASSERT_EQ(buf1[0], 'e'); /* 'e' from begin */ uv_fs_req_cleanup(&req); } else { @@ -1260,15 +1322,15 @@ static int test_sendfile(void (*setup)(int), uv_fs_cb cb, off_t expected_size) { unlink("test_file"); unlink("test_file2"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } static void sendfile_setup(int f) { - ASSERT(6 == write(f, "begin\n", 6)); - ASSERT(65542 == lseek(f, 65536, SEEK_CUR)); - ASSERT(4 == write(f, "end\n", 4)); + ASSERT_EQ(6, write(f, "begin\n", 6)); + ASSERT_EQ(65542, lseek(f, 65536, SEEK_CUR)); + ASSERT_EQ(4, write(f, "end\n", 4)); } @@ -1289,18 +1351,18 @@ TEST_IMPL(fs_mkdtemp) { loop = uv_default_loop(); r = uv_fs_mkdtemp(loop, &mkdtemp_req1, path_template, mkdtemp_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(mkdtemp_cb_count == 1); + ASSERT_EQ(1, mkdtemp_cb_count); /* sync mkdtemp */ r = uv_fs_mkdtemp(NULL, &mkdtemp_req2, path_template, NULL); - ASSERT(r == 0); + ASSERT_OK(r); check_mkdtemp_result(&mkdtemp_req2); /* mkdtemp return different values on subsequent calls */ - ASSERT(strcmp(mkdtemp_req1.path, mkdtemp_req2.path) != 0); + ASSERT_NE(0, strcmp(mkdtemp_req1.path, mkdtemp_req2.path)); /* Cleanup */ rmdir(mkdtemp_req1.path); @@ -1308,7 +1370,7 @@ TEST_IMPL(fs_mkdtemp) { uv_fs_req_cleanup(&mkdtemp_req1); uv_fs_req_cleanup(&mkdtemp_req2); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -1322,30 +1384,32 @@ TEST_IMPL(fs_mkstemp) { loop = uv_default_loop(); r = uv_fs_mkstemp(loop, &mkstemp_req1, path_template, mkstemp_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(mkstemp_cb_count == 1); + ASSERT_EQ(1, mkstemp_cb_count); /* sync mkstemp */ r = uv_fs_mkstemp(NULL, &mkstemp_req2, path_template, NULL); - ASSERT(r >= 0); + ASSERT_GE(r, 0); check_mkstemp_result(&mkstemp_req2); /* mkstemp return different values on subsequent calls */ - ASSERT(strcmp(mkstemp_req1.path, mkstemp_req2.path) != 0); + ASSERT_NE(0, strcmp(mkstemp_req1.path, mkstemp_req2.path)); /* invalid template returns EINVAL */ ASSERT_EQ(UV_EINVAL, uv_fs_mkstemp(NULL, &mkstemp_req3, "test_file", NULL)); /* Make sure that path is empty string */ - ASSERT_EQ(0, strlen(mkstemp_req3.path)); + ASSERT_OK(strlen(mkstemp_req3.path)); + + uv_fs_req_cleanup(&mkstemp_req3); /* We can write to the opened file */ iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &req, mkstemp_req1.result, &iov, 1, -1, NULL); - ASSERT(r == sizeof(test_buf)); - ASSERT(req.result == sizeof(test_buf)); + ASSERT_EQ(r, sizeof(test_buf)); + ASSERT_EQ(req.result, sizeof(test_buf)); uv_fs_req_cleanup(&req); /* Cleanup */ @@ -1354,16 +1418,16 @@ TEST_IMPL(fs_mkstemp) { uv_fs_close(NULL, &req, mkstemp_req2.result, NULL); uv_fs_req_cleanup(&req); - fd = uv_fs_open(NULL, &req, mkstemp_req1.path , O_RDONLY, 0, NULL); - ASSERT(fd >= 0); + fd = uv_fs_open(NULL, &req, mkstemp_req1.path, UV_FS_O_RDONLY, 0, NULL); + ASSERT_GE(fd, 0); uv_fs_req_cleanup(&req); memset(buf, 0, sizeof(buf)); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &req, fd, &iov, 1, -1, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); - ASSERT(strcmp(buf, test_buf) == 0); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); + ASSERT_OK(strcmp(buf, test_buf)); uv_fs_req_cleanup(&req); uv_fs_close(NULL, &req, fd, NULL); @@ -1374,7 +1438,7 @@ TEST_IMPL(fs_mkstemp) { uv_fs_req_cleanup(&mkstemp_req1); uv_fs_req_cleanup(&mkstemp_req2); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -1388,26 +1452,34 @@ TEST_IMPL(fs_fstat) { struct stat t; #endif +#if defined(__s390__) && defined(__QEMU__) + /* qemu-user-s390x has this weird bug where statx() reports nanoseconds + * but plain fstat() does not. + */ + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + /* Setup. */ unlink("test_file"); loop = uv_default_loop(); - r = uv_fs_open(NULL, &req, "test_file", O_RDWR | O_CREAT, + r = uv_fs_open(NULL, &req, "test_file", UV_FS_O_RDWR | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); file = req.result; uv_fs_req_cleanup(&req); #ifndef _WIN32 - ASSERT(0 == fstat(file, &t)); - ASSERT(0 == uv_fs_fstat(NULL, &req, file, NULL)); - ASSERT(req.result == 0); + memset(&t, 0, sizeof(t)); + ASSERT_OK(fstat(file, &t)); + ASSERT_OK(uv_fs_fstat(NULL, &req, file, NULL)); + ASSERT_OK(req.result); s = req.ptr; # if defined(__APPLE__) - ASSERT(s->st_birthtim.tv_sec == t.st_birthtimespec.tv_sec); - ASSERT(s->st_birthtim.tv_nsec == t.st_birthtimespec.tv_nsec); + ASSERT_EQ(s->st_birthtim.tv_sec, t.st_birthtimespec.tv_sec); + ASSERT_EQ(s->st_birthtim.tv_nsec, t.st_birthtimespec.tv_nsec); # elif defined(__linux__) /* If statx() is supported, the birth time should be equal to the change time * because we just created the file. On older kernels, it's set to zero. @@ -1421,53 +1493,53 @@ TEST_IMPL(fs_fstat) { iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &req, file, &iov, 1, -1, NULL); - ASSERT(r == sizeof(test_buf)); - ASSERT(req.result == sizeof(test_buf)); + ASSERT_EQ(r, sizeof(test_buf)); + ASSERT_EQ(req.result, sizeof(test_buf)); uv_fs_req_cleanup(&req); memset(&req.statbuf, 0xaa, sizeof(req.statbuf)); r = uv_fs_fstat(NULL, &req, file, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); s = req.ptr; - ASSERT(s->st_size == sizeof(test_buf)); + ASSERT_EQ(s->st_size, sizeof(test_buf)); #ifndef _WIN32 r = fstat(file, &t); - ASSERT(r == 0); - - ASSERT(s->st_dev == (uint64_t) t.st_dev); - ASSERT(s->st_mode == (uint64_t) t.st_mode); - ASSERT(s->st_nlink == (uint64_t) t.st_nlink); - ASSERT(s->st_uid == (uint64_t) t.st_uid); - ASSERT(s->st_gid == (uint64_t) t.st_gid); - ASSERT(s->st_rdev == (uint64_t) t.st_rdev); - ASSERT(s->st_ino == (uint64_t) t.st_ino); - ASSERT(s->st_size == (uint64_t) t.st_size); - ASSERT(s->st_blksize == (uint64_t) t.st_blksize); - ASSERT(s->st_blocks == (uint64_t) t.st_blocks); + ASSERT_OK(r); + + ASSERT_EQ(s->st_dev, (uint64_t) t.st_dev); + ASSERT_EQ(s->st_mode, (uint64_t) t.st_mode); + ASSERT_EQ(s->st_nlink, (uint64_t) t.st_nlink); + ASSERT_EQ(s->st_uid, (uint64_t) t.st_uid); + ASSERT_EQ(s->st_gid, (uint64_t) t.st_gid); + ASSERT_EQ(s->st_rdev, (uint64_t) t.st_rdev); + ASSERT_EQ(s->st_ino, (uint64_t) t.st_ino); + ASSERT_EQ(s->st_size, (uint64_t) t.st_size); + ASSERT_EQ(s->st_blksize, (uint64_t) t.st_blksize); + ASSERT_EQ(s->st_blocks, (uint64_t) t.st_blocks); #if defined(__APPLE__) - ASSERT(s->st_atim.tv_sec == t.st_atimespec.tv_sec); - ASSERT(s->st_atim.tv_nsec == t.st_atimespec.tv_nsec); - ASSERT(s->st_mtim.tv_sec == t.st_mtimespec.tv_sec); - ASSERT(s->st_mtim.tv_nsec == t.st_mtimespec.tv_nsec); - ASSERT(s->st_ctim.tv_sec == t.st_ctimespec.tv_sec); - ASSERT(s->st_ctim.tv_nsec == t.st_ctimespec.tv_nsec); + ASSERT_EQ(s->st_atim.tv_sec, t.st_atimespec.tv_sec); + ASSERT_EQ(s->st_atim.tv_nsec, t.st_atimespec.tv_nsec); + ASSERT_EQ(s->st_mtim.tv_sec, t.st_mtimespec.tv_sec); + ASSERT_EQ(s->st_mtim.tv_nsec, t.st_mtimespec.tv_nsec); + ASSERT_EQ(s->st_ctim.tv_sec, t.st_ctimespec.tv_sec); + ASSERT_EQ(s->st_ctim.tv_nsec, t.st_ctimespec.tv_nsec); #elif defined(_AIX) || \ defined(__MVS__) - ASSERT(s->st_atim.tv_sec == t.st_atime); - ASSERT(s->st_atim.tv_nsec == 0); - ASSERT(s->st_mtim.tv_sec == t.st_mtime); - ASSERT(s->st_mtim.tv_nsec == 0); - ASSERT(s->st_ctim.tv_sec == t.st_ctime); - ASSERT(s->st_ctim.tv_nsec == 0); + ASSERT_EQ(s->st_atim.tv_sec, t.st_atime); + ASSERT_OK(s->st_atim.tv_nsec); + ASSERT_EQ(s->st_mtim.tv_sec, t.st_mtime); + ASSERT_OK(s->st_mtim.tv_nsec); + ASSERT_EQ(s->st_ctim.tv_sec, t.st_ctime); + ASSERT_OK(s->st_ctim.tv_nsec); #elif defined(__ANDROID__) - ASSERT(s->st_atim.tv_sec == t.st_atime); - ASSERT(s->st_atim.tv_nsec == t.st_atimensec); - ASSERT(s->st_mtim.tv_sec == t.st_mtime); - ASSERT(s->st_mtim.tv_nsec == t.st_mtimensec); - ASSERT(s->st_ctim.tv_sec == t.st_ctime); - ASSERT(s->st_ctim.tv_nsec == t.st_ctimensec); + ASSERT_EQ(s->st_atim.tv_sec, t.st_atime); + ASSERT_EQ(s->st_atim.tv_nsec, t.st_atimensec); + ASSERT_EQ(s->st_mtim.tv_sec, t.st_mtime); + ASSERT_EQ(s->st_mtim.tv_nsec, t.st_mtimensec); + ASSERT_EQ(s->st_ctim.tv_sec, t.st_ctime); + ASSERT_EQ(s->st_ctim.tv_nsec, t.st_ctimensec); #elif defined(__sun) || \ defined(__DragonFly__) || \ defined(__FreeBSD__) || \ @@ -1478,47 +1550,47 @@ TEST_IMPL(fs_fstat) { defined(_SVID_SOURCE) || \ defined(_XOPEN_SOURCE) || \ defined(_DEFAULT_SOURCE) - ASSERT(s->st_atim.tv_sec == t.st_atim.tv_sec); - ASSERT(s->st_atim.tv_nsec == t.st_atim.tv_nsec); - ASSERT(s->st_mtim.tv_sec == t.st_mtim.tv_sec); - ASSERT(s->st_mtim.tv_nsec == t.st_mtim.tv_nsec); - ASSERT(s->st_ctim.tv_sec == t.st_ctim.tv_sec); - ASSERT(s->st_ctim.tv_nsec == t.st_ctim.tv_nsec); + ASSERT_EQ(s->st_atim.tv_sec, t.st_atim.tv_sec); + ASSERT_EQ(s->st_atim.tv_nsec, t.st_atim.tv_nsec); + ASSERT_EQ(s->st_mtim.tv_sec, t.st_mtim.tv_sec); + ASSERT_EQ(s->st_mtim.tv_nsec, t.st_mtim.tv_nsec); + ASSERT_EQ(s->st_ctim.tv_sec, t.st_ctim.tv_sec); + ASSERT_EQ(s->st_ctim.tv_nsec, t.st_ctim.tv_nsec); # if defined(__FreeBSD__) || \ defined(__NetBSD__) - ASSERT(s->st_birthtim.tv_sec == t.st_birthtim.tv_sec); - ASSERT(s->st_birthtim.tv_nsec == t.st_birthtim.tv_nsec); + ASSERT_EQ(s->st_birthtim.tv_sec, t.st_birthtim.tv_sec); + ASSERT_EQ(s->st_birthtim.tv_nsec, t.st_birthtim.tv_nsec); # endif #else - ASSERT(s->st_atim.tv_sec == t.st_atime); - ASSERT(s->st_atim.tv_nsec == 0); - ASSERT(s->st_mtim.tv_sec == t.st_mtime); - ASSERT(s->st_mtim.tv_nsec == 0); - ASSERT(s->st_ctim.tv_sec == t.st_ctime); - ASSERT(s->st_ctim.tv_nsec == 0); + ASSERT_EQ(s->st_atim.tv_sec, t.st_atime); + ASSERT_OK(s->st_atim.tv_nsec); + ASSERT_EQ(s->st_mtim.tv_sec, t.st_mtime); + ASSERT_OK(s->st_mtim.tv_nsec); + ASSERT_EQ(s->st_ctim.tv_sec, t.st_ctime); + ASSERT_OK(s->st_ctim.tv_nsec); #endif #endif #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) - ASSERT(s->st_flags == t.st_flags); - ASSERT(s->st_gen == t.st_gen); + ASSERT_EQ(s->st_flags, t.st_flags); + ASSERT_EQ(s->st_gen, t.st_gen); #else - ASSERT(s->st_flags == 0); - ASSERT(s->st_gen == 0); + ASSERT_OK(s->st_flags); + ASSERT_OK(s->st_gen); #endif uv_fs_req_cleanup(&req); /* Now do the uv_fs_fstat call asynchronously */ r = uv_fs_fstat(loop, &req, file, fstat_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(fstat_cb_count == 1); + ASSERT_EQ(1, fstat_cb_count); r = uv_fs_close(NULL, &req, file, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); /* @@ -1530,7 +1602,46 @@ TEST_IMPL(fs_fstat) { /* Cleanup. */ unlink("test_file"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); + return 0; +} + + +TEST_IMPL(fs_fstat_stdio) { + int fd; + int res; + uv_fs_t req; +#ifdef _WIN32 + uv_stat_t* st; + DWORD ft; +#endif + + for (fd = 0; fd <= 2; ++fd) { + res = uv_fs_fstat(NULL, &req, fd, NULL); + ASSERT_OK(res); + ASSERT_OK(req.result); + +#ifdef _WIN32 + st = req.ptr; + ft = uv_guess_handle(fd); + switch (ft) { + case UV_TTY: + case UV_NAMED_PIPE: + ASSERT_EQ(st->st_mode, (ft == UV_TTY ? S_IFCHR : S_IFIFO)); + ASSERT_EQ(1, st->st_nlink); + ASSERT_EQ(st->st_rdev, + (ft == UV_TTY ? FILE_DEVICE_CONSOLE : FILE_DEVICE_NAMED_PIPE) + << 16); + break; + default: + break; + } +#endif + + uv_fs_req_cleanup(&req); + } + + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -1548,52 +1659,52 @@ TEST_IMPL(fs_access) { /* File should not exist */ r = uv_fs_access(NULL, &req, "test_file", F_OK, NULL); - ASSERT(r < 0); - ASSERT(req.result < 0); + ASSERT_LT(r, 0); + ASSERT_LT(req.result, 0); uv_fs_req_cleanup(&req); /* File should not exist */ r = uv_fs_access(loop, &req, "test_file", F_OK, access_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(access_cb_count == 1); + ASSERT_EQ(1, access_cb_count); access_cb_count = 0; /* reset for the next test */ /* Create file */ - r = uv_fs_open(NULL, &req, "test_file", O_RDWR | O_CREAT, + r = uv_fs_open(NULL, &req, "test_file", UV_FS_O_RDWR | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); file = req.result; uv_fs_req_cleanup(&req); /* File should exist */ r = uv_fs_access(NULL, &req, "test_file", F_OK, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); /* File should exist */ r = uv_fs_access(loop, &req, "test_file", F_OK, access_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(access_cb_count == 1); + ASSERT_EQ(1, access_cb_count); access_cb_count = 0; /* reset for the next test */ /* Close file */ r = uv_fs_close(NULL, &req, file, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); /* Directory access */ r = uv_fs_mkdir(NULL, &req, "test_dir", 0777, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&req); r = uv_fs_access(NULL, &req, "test_dir", W_OK, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); /* @@ -1606,7 +1717,7 @@ TEST_IMPL(fs_access) { unlink("test_file"); rmdir("test_dir"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -1621,24 +1732,24 @@ TEST_IMPL(fs_chmod) { loop = uv_default_loop(); - r = uv_fs_open(NULL, &req, "test_file", O_RDWR | O_CREAT, + r = uv_fs_open(NULL, &req, "test_file", UV_FS_O_RDWR | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); file = req.result; uv_fs_req_cleanup(&req); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &req, file, &iov, 1, -1, NULL); - ASSERT(r == sizeof(test_buf)); - ASSERT(req.result == sizeof(test_buf)); + ASSERT_EQ(r, sizeof(test_buf)); + ASSERT_EQ(req.result, sizeof(test_buf)); uv_fs_req_cleanup(&req); #ifndef _WIN32 /* Make the file write-only */ r = uv_fs_chmod(NULL, &req, "test_file", 0200, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); check_permission("test_file", 0200); @@ -1646,16 +1757,16 @@ TEST_IMPL(fs_chmod) { /* Make the file read-only */ r = uv_fs_chmod(NULL, &req, "test_file", 0400, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); check_permission("test_file", 0400); /* Make the file read+write with sync uv_fs_fchmod */ r = uv_fs_fchmod(NULL, &req, file, 0600, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); check_permission("test_file", 0600); @@ -1667,9 +1778,9 @@ TEST_IMPL(fs_chmod) { req.data = &mode; } r = uv_fs_chmod(loop, &req, "test_file", 0200, chmod_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(chmod_cb_count == 1); + ASSERT_EQ(1, chmod_cb_count); chmod_cb_count = 0; /* reset for the next test */ #endif @@ -1679,9 +1790,9 @@ TEST_IMPL(fs_chmod) { req.data = &mode; } r = uv_fs_chmod(loop, &req, "test_file", 0400, chmod_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(chmod_cb_count == 1); + ASSERT_EQ(1, chmod_cb_count); /* async fchmod */ { @@ -1689,9 +1800,9 @@ TEST_IMPL(fs_chmod) { req.data = &mode; } r = uv_fs_fchmod(loop, &req, file, 0600, fchmod_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(fchmod_cb_count == 1); + ASSERT_EQ(1, fchmod_cb_count); uv_fs_close(loop, &req, file, NULL); @@ -1704,7 +1815,7 @@ TEST_IMPL(fs_chmod) { /* Cleanup. */ unlink("test_file"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -1720,36 +1831,34 @@ TEST_IMPL(fs_unlink_readonly) { loop = uv_default_loop(); r = uv_fs_open(NULL, - &req, - "test_file", - O_RDWR | O_CREAT, + &req, "test_file", UV_FS_O_RDWR | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); file = req.result; uv_fs_req_cleanup(&req); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &req, file, &iov, 1, -1, NULL); - ASSERT(r == sizeof(test_buf)); - ASSERT(req.result == sizeof(test_buf)); + ASSERT_EQ(r, sizeof(test_buf)); + ASSERT_EQ(req.result, sizeof(test_buf)); uv_fs_req_cleanup(&req); uv_fs_close(loop, &req, file, NULL); /* Make the file read-only */ r = uv_fs_chmod(NULL, &req, "test_file", 0400, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); check_permission("test_file", 0400); /* Try to unlink the file */ r = uv_fs_unlink(NULL, &req, "test_file", NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); /* @@ -1763,7 +1872,7 @@ TEST_IMPL(fs_unlink_readonly) { uv_fs_req_cleanup(&req); unlink("test_file"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -1779,35 +1888,33 @@ TEST_IMPL(fs_unlink_archive_readonly) { loop = uv_default_loop(); r = uv_fs_open(NULL, - &req, - "test_file", - O_RDWR | O_CREAT, + &req, "test_file", UV_FS_O_RDWR | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); file = req.result; uv_fs_req_cleanup(&req); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &req, file, &iov, 1, -1, NULL); - ASSERT(r == sizeof(test_buf)); - ASSERT(req.result == sizeof(test_buf)); + ASSERT_EQ(r, sizeof(test_buf)); + ASSERT_EQ(req.result, sizeof(test_buf)); uv_fs_req_cleanup(&req); uv_fs_close(loop, &req, file, NULL); /* Make the file read-only and clear archive flag */ r = SetFileAttributes("test_file", FILE_ATTRIBUTE_READONLY); - ASSERT(r != 0); + ASSERT(r); uv_fs_req_cleanup(&req); check_permission("test_file", 0400); /* Try to unlink the file */ r = uv_fs_unlink(NULL, &req, "test_file", NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); /* @@ -1821,7 +1928,7 @@ TEST_IMPL(fs_unlink_archive_readonly) { uv_fs_req_cleanup(&req); unlink("test_file"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } #endif @@ -1837,71 +1944,71 @@ TEST_IMPL(fs_chown) { loop = uv_default_loop(); - r = uv_fs_open(NULL, &req, "test_file", O_RDWR | O_CREAT, + r = uv_fs_open(NULL, &req, "test_file", UV_FS_O_RDWR | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); file = req.result; uv_fs_req_cleanup(&req); /* sync chown */ r = uv_fs_chown(NULL, &req, "test_file", -1, -1, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); /* sync fchown */ r = uv_fs_fchown(NULL, &req, file, -1, -1, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); /* async chown */ r = uv_fs_chown(loop, &req, "test_file", -1, -1, chown_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(chown_cb_count == 1); + ASSERT_EQ(1, chown_cb_count); #ifndef __MVS__ /* chown to root (fail) */ chown_cb_count = 0; r = uv_fs_chown(loop, &req, "test_file", 0, 0, chown_root_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(chown_cb_count == 1); + ASSERT_EQ(1, chown_cb_count); #endif /* async fchown */ r = uv_fs_fchown(loop, &req, file, -1, -1, fchown_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(fchown_cb_count == 1); + ASSERT_EQ(1, fchown_cb_count); #ifndef __HAIKU__ /* Haiku doesn't support hardlink */ /* sync link */ r = uv_fs_link(NULL, &req, "test_file", "test_file_link", NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); /* sync lchown */ r = uv_fs_lchown(NULL, &req, "test_file_link", -1, -1, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); /* async lchown */ r = uv_fs_lchown(loop, &req, "test_file_link", -1, -1, lchown_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(lchown_cb_count == 1); + ASSERT_EQ(1, lchown_cb_count); #endif /* Close file */ r = uv_fs_close(NULL, &req, file, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); /* @@ -1914,7 +2021,7 @@ TEST_IMPL(fs_chown) { unlink("test_file"); unlink("test_file_link"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -1932,60 +2039,60 @@ TEST_IMPL(fs_link) { loop = uv_default_loop(); - r = uv_fs_open(NULL, &req, "test_file", O_RDWR | O_CREAT, + r = uv_fs_open(NULL, &req, "test_file", UV_FS_O_RDWR | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); file = req.result; uv_fs_req_cleanup(&req); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &req, file, &iov, 1, -1, NULL); - ASSERT(r == sizeof(test_buf)); - ASSERT(req.result == sizeof(test_buf)); + ASSERT_EQ(r, sizeof(test_buf)); + ASSERT_EQ(req.result, sizeof(test_buf)); uv_fs_req_cleanup(&req); uv_fs_close(loop, &req, file, NULL); /* sync link */ r = uv_fs_link(NULL, &req, "test_file", "test_file_link", NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); - r = uv_fs_open(NULL, &req, "test_file_link", O_RDWR, 0, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); + r = uv_fs_open(NULL, &req, "test_file_link", UV_FS_O_RDWR, 0, NULL); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); link = req.result; uv_fs_req_cleanup(&req); memset(buf, 0, sizeof(buf)); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &req, link, &iov, 1, 0, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); - ASSERT(strcmp(buf, test_buf) == 0); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); + ASSERT_OK(strcmp(buf, test_buf)); close(link); /* async link */ r = uv_fs_link(loop, &req, "test_file", "test_file_link2", link_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(link_cb_count == 1); + ASSERT_EQ(1, link_cb_count); - r = uv_fs_open(NULL, &req, "test_file_link2", O_RDWR, 0, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); + r = uv_fs_open(NULL, &req, "test_file_link2", UV_FS_O_RDWR, 0, NULL); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); link = req.result; uv_fs_req_cleanup(&req); memset(buf, 0, sizeof(buf)); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &req, link, &iov, 1, 0, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); - ASSERT(strcmp(buf, test_buf) == 0); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); + ASSERT_OK(strcmp(buf, test_buf)); uv_fs_close(loop, &req, link, NULL); @@ -2000,28 +2107,61 @@ TEST_IMPL(fs_link) { unlink("test_file_link"); unlink("test_file_link2"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } TEST_IMPL(fs_readlink) { - uv_fs_t req; + /* Must return UV_ENOENT on an inexistent file */ + { + uv_fs_t req; - loop = uv_default_loop(); - ASSERT(0 == uv_fs_readlink(loop, &req, "no_such_file", dummy_cb)); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(dummy_cb_count == 1); - ASSERT_NULL(req.ptr); - ASSERT(req.result == UV_ENOENT); - uv_fs_req_cleanup(&req); + loop = uv_default_loop(); + ASSERT_OK(uv_fs_readlink(loop, &req, "no_such_file", dummy_cb)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(1, dummy_cb_count); + ASSERT_NULL(req.ptr); + ASSERT_EQ(req.result, UV_ENOENT); + uv_fs_req_cleanup(&req); - ASSERT(UV_ENOENT == uv_fs_readlink(NULL, &req, "no_such_file", NULL)); - ASSERT_NULL(req.ptr); - ASSERT(req.result == UV_ENOENT); - uv_fs_req_cleanup(&req); + ASSERT_EQ(UV_ENOENT, uv_fs_readlink(NULL, &req, "no_such_file", NULL)); + ASSERT_NULL(req.ptr); + ASSERT_EQ(req.result, UV_ENOENT); + uv_fs_req_cleanup(&req); + } - MAKE_VALGRIND_HAPPY(); + /* Must return UV_EINVAL on a non-symlink file */ + { + int r; + uv_fs_t req; + uv_file file; + + /* Setup */ + + /* Create a non-symlink file */ + r = uv_fs_open(NULL, &req, "test_file", UV_FS_O_RDWR | UV_FS_O_CREAT, + S_IWUSR | S_IRUSR, NULL); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); + file = req.result; + uv_fs_req_cleanup(&req); + + r = uv_fs_close(NULL, &req, file, NULL); + ASSERT_OK(r); + ASSERT_OK(req.result); + uv_fs_req_cleanup(&req); + + /* Test */ + r = uv_fs_readlink(NULL, &req, "test_file", NULL); + ASSERT_EQ(r, UV_EINVAL); + uv_fs_req_cleanup(&req); + + /* Cleanup */ + unlink("test_file"); + } + + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -2030,28 +2170,19 @@ TEST_IMPL(fs_realpath) { uv_fs_t req; loop = uv_default_loop(); - ASSERT(0 == uv_fs_realpath(loop, &req, "no_such_file", dummy_cb)); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(dummy_cb_count == 1); + ASSERT_OK(uv_fs_realpath(loop, &req, "no_such_file", dummy_cb)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(1, dummy_cb_count); ASSERT_NULL(req.ptr); -#ifdef _WIN32 - /* - * Windows XP and Server 2003 don't support GetFinalPathNameByHandleW() - */ - if (req.result == UV_ENOSYS) { - uv_fs_req_cleanup(&req); - RETURN_SKIP("realpath is not supported on Windows XP"); - } -#endif - ASSERT(req.result == UV_ENOENT); + ASSERT_EQ(req.result, UV_ENOENT); uv_fs_req_cleanup(&req); - ASSERT(UV_ENOENT == uv_fs_realpath(NULL, &req, "no_such_file", NULL)); + ASSERT_EQ(UV_ENOENT, uv_fs_realpath(NULL, &req, "no_such_file", NULL)); ASSERT_NULL(req.ptr); - ASSERT(req.result == UV_ENOENT); + ASSERT_EQ(req.result, UV_ENOENT); uv_fs_req_cleanup(&req); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -2081,17 +2212,17 @@ TEST_IMPL(fs_symlink) { loop = uv_default_loop(); - r = uv_fs_open(NULL, &req, "test_file", O_RDWR | O_CREAT, + r = uv_fs_open(NULL, &req, "test_file", UV_FS_O_RDWR | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); file = req.result; uv_fs_req_cleanup(&req); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &req, file, &iov, 1, -1, NULL); - ASSERT(r == sizeof(test_buf)); - ASSERT(req.result == sizeof(test_buf)); + ASSERT_EQ(r, sizeof(test_buf)); + ASSERT_EQ(req.result, sizeof(test_buf)); uv_fs_req_cleanup(&req); uv_fs_close(loop, &req, file, NULL); @@ -2115,22 +2246,22 @@ TEST_IMPL(fs_symlink) { } } #endif - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); - r = uv_fs_open(NULL, &req, "test_file_symlink", O_RDWR, 0, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); + r = uv_fs_open(NULL, &req, "test_file_symlink", UV_FS_O_RDWR, 0, NULL); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); link = req.result; uv_fs_req_cleanup(&req); memset(buf, 0, sizeof(buf)); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &req, link, &iov, 1, 0, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); - ASSERT(strcmp(buf, test_buf) == 0); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); + ASSERT_OK(strcmp(buf, test_buf)); uv_fs_close(loop, &req, link, NULL); @@ -2140,7 +2271,7 @@ TEST_IMPL(fs_symlink) { "test_file_symlink_symlink", 0, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&req); #if defined(__MSYS__) @@ -2148,25 +2279,16 @@ TEST_IMPL(fs_symlink) { #endif r = uv_fs_readlink(NULL, &req, "test_file_symlink_symlink", NULL); - ASSERT(r == 0); - ASSERT(strcmp(req.ptr, "test_file_symlink") == 0); + ASSERT_OK(r); + ASSERT_OK(strcmp(req.ptr, "test_file_symlink")); uv_fs_req_cleanup(&req); r = uv_fs_realpath(NULL, &req, "test_file_symlink_symlink", NULL); + ASSERT_OK(r); #ifdef _WIN32 - /* - * Windows XP and Server 2003 don't support GetFinalPathNameByHandleW() - */ - if (r == UV_ENOSYS) { - uv_fs_req_cleanup(&req); - RETURN_SKIP("realpath is not supported on Windows XP"); - } -#endif - ASSERT(r == 0); -#ifdef _WIN32 - ASSERT(stricmp(req.ptr, test_file_abs_buf) == 0); + ASSERT_OK(_stricmp(req.ptr, test_file_abs_buf)); #else - ASSERT(strcmp(req.ptr, test_file_abs_buf) == 0); + ASSERT_OK(strcmp(req.ptr, test_file_abs_buf)); #endif uv_fs_req_cleanup(&req); @@ -2177,22 +2299,22 @@ TEST_IMPL(fs_symlink) { "test_file_symlink2", 0, symlink_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(symlink_cb_count == 1); + ASSERT_EQ(1, symlink_cb_count); - r = uv_fs_open(NULL, &req, "test_file_symlink2", O_RDWR, 0, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); + r = uv_fs_open(NULL, &req, "test_file_symlink2", UV_FS_O_RDWR, 0, NULL); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); link = req.result; uv_fs_req_cleanup(&req); memset(buf, 0, sizeof(buf)); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &req, link, &iov, 1, 0, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); - ASSERT(strcmp(buf, test_buf) == 0); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); + ASSERT_OK(strcmp(buf, test_buf)); uv_fs_close(loop, &req, link, NULL); @@ -2202,27 +2324,18 @@ TEST_IMPL(fs_symlink) { "test_file_symlink2_symlink", 0, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&req); r = uv_fs_readlink(loop, &req, "test_file_symlink2_symlink", readlink_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(readlink_cb_count == 1); + ASSERT_EQ(1, readlink_cb_count); r = uv_fs_realpath(loop, &req, "test_file", realpath_cb); -#ifdef _WIN32 - /* - * Windows XP and Server 2003 don't support GetFinalPathNameByHandleW() - */ - if (r == UV_ENOSYS) { - uv_fs_req_cleanup(&req); - RETURN_SKIP("realpath is not supported on Windows XP"); - } -#endif - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(realpath_cb_count == 1); + ASSERT_EQ(1, realpath_cb_count); /* * Run the loop just to check we don't have make any extraneous uv_ref() @@ -2237,7 +2350,7 @@ TEST_IMPL(fs_symlink) { unlink("test_file_symlink2"); unlink("test_file_symlink2_symlink"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -2266,8 +2379,8 @@ int test_symlink_dir_impl(int type) { strcpy(test_dir_abs_buf, "\\\\?\\"); uv_cwd(test_dir_abs_buf + 4, &test_dir_abs_size); test_dir_abs_size += 4; - strcat(test_dir_abs_buf, "\\test_dir\\"); - test_dir_abs_size += strlen("\\test_dir\\"); + strcat(test_dir_abs_buf, "\\test_dir"); + test_dir_abs_size += strlen("\\test_dir"); test_dir = test_dir_abs_buf; #else uv_cwd(test_dir_abs_buf, &test_dir_abs_size); @@ -2283,80 +2396,73 @@ int test_symlink_dir_impl(int type) { "creation of directory symlinks"); } fprintf(stderr, "r == %i\n", r); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); r = uv_fs_stat(NULL, &req, "test_dir_symlink", NULL); - ASSERT(r == 0); + ASSERT_OK(r); ASSERT(((uv_stat_t*)req.ptr)->st_mode & S_IFDIR); uv_fs_req_cleanup(&req); r = uv_fs_lstat(NULL, &req, "test_dir_symlink", NULL); - ASSERT(r == 0); + ASSERT_OK(r); #if defined(__MSYS__) RETURN_SKIP("symlink reading is not supported on MSYS2"); #endif ASSERT(((uv_stat_t*)req.ptr)->st_mode & S_IFLNK); #ifdef _WIN32 - ASSERT(((uv_stat_t*)req.ptr)->st_size == strlen(test_dir + 4)); + ASSERT_EQ(((uv_stat_t*)req.ptr)->st_size, strlen(test_dir + 4)); #else # ifdef __PASE__ /* On IBMi PASE, st_size returns the length of the symlink itself. */ - ASSERT(((uv_stat_t*)req.ptr)->st_size == strlen("test_dir_symlink")); + ASSERT_EQ(((uv_stat_t*)req.ptr)->st_size, strlen("test_dir_symlink")); # else - ASSERT(((uv_stat_t*)req.ptr)->st_size == strlen(test_dir)); + ASSERT_EQ(((uv_stat_t*)req.ptr)->st_size, strlen(test_dir)); # endif #endif uv_fs_req_cleanup(&req); r = uv_fs_readlink(NULL, &req, "test_dir_symlink", NULL); - ASSERT(r == 0); + ASSERT_OK(r); #ifdef _WIN32 - ASSERT(strcmp(req.ptr, test_dir + 4) == 0); + ASSERT_OK(strcmp(req.ptr, test_dir + 4)); #else - ASSERT(strcmp(req.ptr, test_dir) == 0); + ASSERT_OK(strcmp(req.ptr, test_dir)); #endif uv_fs_req_cleanup(&req); r = uv_fs_realpath(NULL, &req, "test_dir_symlink", NULL); + ASSERT_OK(r); #ifdef _WIN32 - /* - * Windows XP and Server 2003 don't support GetFinalPathNameByHandleW() - */ - if (r == UV_ENOSYS) { - uv_fs_req_cleanup(&req); - RETURN_SKIP("realpath is not supported on Windows XP"); - } -#endif - ASSERT(r == 0); -#ifdef _WIN32 - ASSERT(strlen(req.ptr) == test_dir_abs_size - 5); - ASSERT(strnicmp(req.ptr, test_dir + 4, test_dir_abs_size - 5) == 0); + ASSERT_EQ(strlen(req.ptr), test_dir_abs_size - 4); + ASSERT_OK(_strnicmp(req.ptr, test_dir + 4, test_dir_abs_size - 4)); #else - ASSERT(strcmp(req.ptr, test_dir_abs_buf) == 0); + ASSERT_OK(strcmp(req.ptr, test_dir_abs_buf)); #endif uv_fs_req_cleanup(&req); - r = uv_fs_open(NULL, &open_req1, "test_dir/file1", O_WRONLY | O_CREAT, + r = uv_fs_open(NULL, &open_req1, "test_dir/file1", + UV_FS_O_WRONLY | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); + ASSERT_GE(r, 0); uv_fs_req_cleanup(&open_req1); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&close_req); - r = uv_fs_open(NULL, &open_req1, "test_dir/file2", O_WRONLY | O_CREAT, + r = uv_fs_open(NULL, &open_req1, "test_dir/file2", + UV_FS_O_WRONLY | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); + ASSERT_GE(r, 0); uv_fs_req_cleanup(&open_req1); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&close_req); r = uv_fs_scandir(NULL, &scandir_req, "test_dir_symlink", 0, NULL); - ASSERT(r == 2); - ASSERT(scandir_req.result == 2); + ASSERT_EQ(2, r); + ASSERT_EQ(2, scandir_req.result); ASSERT(scandir_req.ptr); while (UV_EOF != uv_fs_scandir_next(&scandir_req, &dent)) { ASSERT(strcmp(dent.name, "file1") == 0 || strcmp(dent.name, "file2") == 0); @@ -2367,16 +2473,16 @@ int test_symlink_dir_impl(int type) { /* unlink will remove the directory symlink */ r = uv_fs_unlink(NULL, &req, "test_dir_symlink", NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&req); r = uv_fs_scandir(NULL, &scandir_req, "test_dir_symlink", 0, NULL); - ASSERT(r == UV_ENOENT); + ASSERT_EQ(r, UV_ENOENT); uv_fs_req_cleanup(&scandir_req); r = uv_fs_scandir(NULL, &scandir_req, "test_dir", 0, NULL); - ASSERT(r == 2); - ASSERT(scandir_req.result == 2); + ASSERT_EQ(2, r); + ASSERT_EQ(2, scandir_req.result); ASSERT(scandir_req.ptr); while (UV_EOF != uv_fs_scandir_next(&scandir_req, &dent)) { ASSERT(strcmp(dent.name, "file1") == 0 || strcmp(dent.name, "file2") == 0); @@ -2391,7 +2497,7 @@ int test_symlink_dir_impl(int type) { rmdir("test_dir"); rmdir("test_dir_symlink"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -2429,7 +2535,7 @@ TEST_IMPL(fs_non_symlink_reparse_point) { FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); - ASSERT(file_handle != INVALID_HANDLE_VALUE); + ASSERT_PTR_NE(file_handle, INVALID_HANDLE_VALUE); memset(&reparse_buffer, 0, REPARSE_GUID_DATA_BUFFER_HEADER_SIZE); reparse_buffer.ReparseTag = REPARSE_TAG; @@ -2444,7 +2550,7 @@ TEST_IMPL(fs_non_symlink_reparse_point) { 0, &bytes_returned, NULL); - ASSERT(r != 0); + ASSERT(r); CloseHandle(file_handle); @@ -2458,11 +2564,11 @@ TEST_IMPL(fs_non_symlink_reparse_point) { shared via SMB as "Macintosh HD". r = uv_fs_stat(NULL, &req, "\\\\\\Macintosh HD\\.DS_Store", NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&req); r = uv_fs_lstat(NULL, &req, "\\\\\\Macintosh HD\\.DS_Store", NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&req); */ @@ -2473,25 +2579,25 @@ TEST_IMPL(fs_non_symlink_reparse_point) { the scope of this test. r = uv_fs_stat(NULL, &req, "test_dir/test_file", NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&req); r = uv_fs_lstat(NULL, &req, "test_dir/test_file", NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&req); */ r = uv_fs_scandir(NULL, &scandir_req, "test_dir", 0, NULL); - ASSERT(r == 1); - ASSERT(scandir_req.result == 1); + ASSERT_EQ(1, r); + ASSERT_EQ(1, scandir_req.result); ASSERT(scandir_req.ptr); while (UV_EOF != uv_fs_scandir_next(&scandir_req, &dent)) { - ASSERT(strcmp(dent.name, "test_file") == 0); + ASSERT_OK(strcmp(dent.name, "test_file")); /* uv_fs_scandir incorrectly identifies non-symlink reparse points as links because it doesn't open the file and verify the reparse point tag. The PowerShell Get-ChildItem command shares this behavior, so it's reasonable to leave it as is. */ - ASSERT(dent.type == UV_DIRENT_LINK); + ASSERT_EQ(dent.type, UV_DIRENT_LINK); } uv_fs_req_cleanup(&scandir_req); ASSERT(!scandir_req.ptr); @@ -2500,7 +2606,7 @@ TEST_IMPL(fs_non_symlink_reparse_point) { unlink("test_dir/test_file"); rmdir("test_dir"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -2520,10 +2626,10 @@ TEST_IMPL(fs_lstat_windows_store_apps) { len = sizeof(localappdata); r = uv_os_getenv("LOCALAPPDATA", localappdata, &len); if (r == UV_ENOENT) { - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return TEST_SKIP; } - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = snprintf(windowsapps_path, sizeof(localappdata), "%s\\Microsoft\\WindowsApps", @@ -2531,11 +2637,11 @@ TEST_IMPL(fs_lstat_windows_store_apps) { ASSERT_GT(r, 0); if (uv_fs_opendir(loop, &req, windowsapps_path, NULL) != 0) { /* If we cannot read the directory, skip the test. */ - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return TEST_SKIP; } if (uv_fs_scandir(loop, &req, windowsapps_path, 0, NULL) <= 0) { - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return TEST_SKIP; } while (uv_fs_scandir_next(&req, &dirent) != UV_EOF) { @@ -2549,9 +2655,9 @@ TEST_IMPL(fs_lstat_windows_store_apps) { dirent.name) < 0) { continue; } - ASSERT_EQ(uv_fs_lstat(loop, &stat_req, file_path, NULL), 0); + ASSERT_OK(uv_fs_lstat(loop, &stat_req, file_path, NULL)); } - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } #endif @@ -2568,17 +2674,19 @@ TEST_IMPL(fs_utime) { /* Setup. */ loop = uv_default_loop(); unlink(path); - r = uv_fs_open(NULL, &req, path, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); + r = uv_fs_open(NULL, &req, path, UV_FS_O_RDWR | UV_FS_O_CREAT, + S_IWUSR | S_IRUSR, + NULL); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); uv_fs_req_cleanup(&req); uv_fs_close(loop, &req, r, NULL); atime = mtime = 400497753.25; /* 1982-09-10 11:22:33.25 */ r = uv_fs_utime(NULL, &req, path, atime, mtime, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); check_utime(path, atime, mtime, /* test_lutime */ 0); @@ -2591,14 +2699,14 @@ TEST_IMPL(fs_utime) { /* async utime */ utime_req.data = &checkme; r = uv_fs_utime(loop, &utime_req, path, atime, mtime, utime_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(utime_cb_count == 1); + ASSERT_EQ(1, utime_cb_count); /* Cleanup. */ unlink(path); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -2612,11 +2720,13 @@ TEST_IMPL(fs_utime_round) { loop = uv_default_loop(); unlink(path); - r = uv_fs_open(NULL, &req, path, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR, NULL); + r = uv_fs_open(NULL, &req, path, UV_FS_O_RDWR | UV_FS_O_CREAT, + S_IWUSR | S_IRUSR, + NULL); ASSERT_GE(r, 0); ASSERT_GE(req.result, 0); uv_fs_req_cleanup(&req); - ASSERT_EQ(0, uv_fs_close(loop, &req, r, NULL)); + ASSERT_OK(uv_fs_close(loop, &req, r, NULL)); atime = mtime = -14245440.25; /* 1969-07-20T02:56:00.25Z */ @@ -2631,13 +2741,13 @@ TEST_IMPL(fs_utime_round) { RETURN_SKIP("utime on some OS (z/OS, IBM i PASE, AIX) or filesystems may reject pre-epoch timestamps"); } #endif - ASSERT_EQ(0, r); - ASSERT_EQ(0, req.result); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); check_utime(path, atime, mtime, /* test_lutime */ 0); unlink(path); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -2647,28 +2757,28 @@ TEST_IMPL(fs_stat_root) { int r; r = uv_fs_stat(NULL, &stat_req, "\\", NULL); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_stat(NULL, &stat_req, "..\\..\\..\\..\\..\\..\\..", NULL); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_stat(NULL, &stat_req, "..", NULL); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_stat(NULL, &stat_req, "..\\", NULL); - ASSERT(r == 0); + ASSERT_OK(r); /* stats the current directory on c: */ r = uv_fs_stat(NULL, &stat_req, "c:", NULL); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_stat(NULL, &stat_req, "c:\\", NULL); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_stat(NULL, &stat_req, "\\\\?\\C:\\", NULL); - ASSERT(r == 0); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } #endif @@ -2689,27 +2799,29 @@ TEST_IMPL(fs_futime) { /* Setup. */ loop = uv_default_loop(); unlink(path); - r = uv_fs_open(NULL, &req, path, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); + r = uv_fs_open(NULL, &req, path, UV_FS_O_RDWR | UV_FS_O_CREAT, + S_IWUSR | S_IRUSR, + NULL); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); uv_fs_req_cleanup(&req); uv_fs_close(loop, &req, r, NULL); atime = mtime = 400497753.25; /* 1982-09-10 11:22:33.25 */ - r = uv_fs_open(NULL, &req, path, O_RDWR, 0, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); + r = uv_fs_open(NULL, &req, path, UV_FS_O_RDWR, 0, NULL); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); file = req.result; /* FIXME probably not how it's supposed to be used */ uv_fs_req_cleanup(&req); r = uv_fs_futime(NULL, &req, file, atime, mtime, NULL); #if defined(__CYGWIN__) || defined(__MSYS__) - ASSERT(r == UV_ENOSYS); + ASSERT_EQ(r, UV_ENOSYS); RETURN_SKIP("futime not supported on Cygwin"); #else - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); #endif uv_fs_req_cleanup(&req); @@ -2724,14 +2836,14 @@ TEST_IMPL(fs_futime) { /* async futime */ futime_req.data = &checkme; r = uv_fs_futime(loop, &futime_req, file, atime, mtime, futime_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(futime_cb_count == 1); + ASSERT_EQ(1, futime_cb_count); /* Cleanup. */ unlink(path); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -2749,9 +2861,11 @@ TEST_IMPL(fs_lutime) { /* Setup */ loop = uv_default_loop(); unlink(path); - r = uv_fs_open(NULL, &req, path, O_RDWR | O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); + r = uv_fs_open(NULL, &req, path, UV_FS_O_RDWR | UV_FS_O_CREAT, + S_IWUSR | S_IRUSR, + NULL); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); uv_fs_req_cleanup(&req); uv_fs_close(loop, &req, r, NULL); @@ -2767,8 +2881,8 @@ TEST_IMPL(fs_lutime) { "Symlink creation requires elevated console (with admin rights)"); } #endif - ASSERT(s == 0); - ASSERT(req.result == 0); + ASSERT_OK(s); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); /* Test the synchronous version. */ @@ -2782,12 +2896,12 @@ TEST_IMPL(fs_lutime) { r = uv_fs_lutime(NULL, &req, symlink_path, atime, mtime, NULL); #if (defined(_AIX) && !defined(_AIX71)) || \ defined(__MVS__) - ASSERT(r == UV_ENOSYS); + ASSERT_EQ(r, UV_ENOSYS); RETURN_SKIP("lutime is not implemented for z/OS and AIX versions below 7.1"); #endif - ASSERT(r == 0); + ASSERT_OK(r); lutime_cb(&req); - ASSERT(lutime_cb_count == 1); + ASSERT_EQ(1, lutime_cb_count); /* Test the asynchronous version. */ atime = mtime = 1291404900; /* 2010-12-03 20:35:00 */ @@ -2797,15 +2911,15 @@ TEST_IMPL(fs_lutime) { checkme.path = symlink_path; r = uv_fs_lutime(loop, &req, symlink_path, atime, mtime, lutime_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(lutime_cb_count == 2); + ASSERT_EQ(2, lutime_cb_count); /* Cleanup. */ unlink(path); unlink(symlink_path); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -2817,11 +2931,11 @@ TEST_IMPL(fs_stat_missing_path) { loop = uv_default_loop(); r = uv_fs_stat(NULL, &req, "non_existent_file", NULL); - ASSERT(r == UV_ENOENT); - ASSERT(req.result == UV_ENOENT); + ASSERT_EQ(r, UV_ENOENT); + ASSERT_EQ(req.result, UV_ENOENT); uv_fs_req_cleanup(&req); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -2842,23 +2956,23 @@ TEST_IMPL(fs_scandir_empty_dir) { memset(&req, 0xdb, sizeof(req)); r = uv_fs_scandir(NULL, &req, path, 0, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); ASSERT_NULL(req.ptr); - ASSERT(UV_EOF == uv_fs_scandir_next(&req, &dent)); + ASSERT_EQ(UV_EOF, uv_fs_scandir_next(&req, &dent)); uv_fs_req_cleanup(&req); r = uv_fs_scandir(loop, &scandir_req, path, 0, empty_scandir_cb); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(scandir_cb_count == 0); + ASSERT_OK(scandir_cb_count); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(scandir_cb_count == 1); + ASSERT_EQ(1, scandir_cb_count); uv_fs_rmdir(NULL, &req, path, NULL); uv_fs_req_cleanup(&req); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -2879,20 +2993,20 @@ TEST_IMPL(fs_scandir_non_existent_dir) { memset(&req, 0xdb, sizeof(req)); r = uv_fs_scandir(NULL, &req, path, 0, NULL); - ASSERT(r == UV_ENOENT); - ASSERT(req.result == UV_ENOENT); + ASSERT_EQ(r, UV_ENOENT); + ASSERT_EQ(req.result, UV_ENOENT); ASSERT_NULL(req.ptr); - ASSERT(UV_ENOENT == uv_fs_scandir_next(&req, &dent)); + ASSERT_EQ(UV_ENOENT, uv_fs_scandir_next(&req, &dent)); uv_fs_req_cleanup(&req); r = uv_fs_scandir(loop, &scandir_req, path, 0, non_existent_scandir_cb); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(scandir_cb_count == 0); + ASSERT_OK(scandir_cb_count); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(scandir_cb_count == 1); + ASSERT_EQ(1, scandir_cb_count); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -2904,17 +3018,35 @@ TEST_IMPL(fs_scandir_file) { loop = uv_default_loop(); r = uv_fs_scandir(NULL, &scandir_req, path, 0, NULL); - ASSERT(r == UV_ENOTDIR); + ASSERT_EQ(r, UV_ENOTDIR); uv_fs_req_cleanup(&scandir_req); r = uv_fs_scandir(loop, &scandir_req, path, 0, file_scandir_cb); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(scandir_cb_count == 0); + ASSERT_OK(scandir_cb_count); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(scandir_cb_count == 1); + ASSERT_EQ(1, scandir_cb_count); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); + return 0; +} + + +/* Run in Valgrind. Should not leak when the iterator isn't exhausted. */ +TEST_IMPL(fs_scandir_early_exit) { + uv_dirent_t d; + uv_fs_t req; + + ASSERT_LT(0, uv_fs_scandir(NULL, &req, "test/fixtures/one_file", 0, NULL)); + ASSERT_NE(UV_EOF, uv_fs_scandir_next(&req, &d)); + uv_fs_req_cleanup(&req); + + ASSERT_LT(0, uv_fs_scandir(NULL, &req, "test/fixtures", 0, NULL)); + ASSERT_NE(UV_EOF, uv_fs_scandir_next(&req, &d)); + uv_fs_req_cleanup(&req); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -2927,24 +3059,24 @@ TEST_IMPL(fs_open_dir) { path = "."; loop = uv_default_loop(); - r = uv_fs_open(NULL, &req, path, O_RDONLY, 0, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); + r = uv_fs_open(NULL, &req, path, UV_FS_O_RDONLY, 0, NULL); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); ASSERT_NULL(req.ptr); file = r; uv_fs_req_cleanup(&req); r = uv_fs_close(NULL, &req, file, NULL); - ASSERT(r == 0); + ASSERT_OK(r); - r = uv_fs_open(loop, &req, path, O_RDONLY, 0, open_cb_simple); - ASSERT(r == 0); + r = uv_fs_open(loop, &req, path, UV_FS_O_RDONLY, 0, open_cb_simple); + ASSERT_OK(r); - ASSERT(open_cb_count == 0); + ASSERT_OK(open_cb_count); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(open_cb_count == 1); + ASSERT_EQ(1, open_cb_count); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -2958,58 +3090,59 @@ static void fs_file_open_append(int add_flags) { loop = uv_default_loop(); r = uv_fs_open(NULL, &open_req1, "test_file", - O_WRONLY | O_CREAT | add_flags, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + UV_FS_O_WRONLY | UV_FS_O_CREAT | add_flags, S_IWUSR | S_IRUSR, + NULL); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); - ASSERT(r >= 0); - ASSERT(write_req.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(write_req.result, 0); uv_fs_req_cleanup(&write_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); r = uv_fs_open(NULL, &open_req1, "test_file", - O_RDWR | O_APPEND | add_flags, 0, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + UV_FS_O_RDWR | UV_FS_O_APPEND | add_flags, 0, NULL); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); - ASSERT(r >= 0); - ASSERT(write_req.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(write_req.result, 0); uv_fs_req_cleanup(&write_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); - r = uv_fs_open(NULL, &open_req1, "test_file", O_RDONLY | add_flags, + r = uv_fs_open(NULL, &open_req1, "test_file", UV_FS_O_RDONLY | add_flags, S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); printf("read = %d\n", r); - ASSERT(r == 26); - ASSERT(read_req.result == 26); - ASSERT(memcmp(buf, - "test-buffer\n\0test-buffer\n\0", - sizeof("test-buffer\n\0test-buffer\n\0") - 1) == 0); + ASSERT_EQ(26, r); + ASSERT_EQ(26, read_req.result); + ASSERT_OK(memcmp(buf, + "test-buffer\n\0test-buffer\n\0", + sizeof("test-buffer\n\0test-buffer\n\0") - 1)); uv_fs_req_cleanup(&read_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); /* Cleanup */ @@ -3019,7 +3152,7 @@ TEST_IMPL(fs_file_open_append) { fs_file_open_append(0); fs_file_open_append(UV_FS_O_FILEMAP); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -3033,62 +3166,62 @@ TEST_IMPL(fs_rename_to_existing_file) { loop = uv_default_loop(); - r = uv_fs_open(NULL, &open_req1, "test_file", O_WRONLY | O_CREAT, + r = uv_fs_open(NULL, &open_req1, "test_file", UV_FS_O_WRONLY | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); - ASSERT(r >= 0); - ASSERT(write_req.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(write_req.result, 0); uv_fs_req_cleanup(&write_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); - r = uv_fs_open(NULL, &open_req1, "test_file2", O_WRONLY | O_CREAT, + r = uv_fs_open(NULL, &open_req1, "test_file2", UV_FS_O_WRONLY | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); r = uv_fs_rename(NULL, &rename_req, "test_file", "test_file2", NULL); - ASSERT(r == 0); - ASSERT(rename_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(rename_req.result); uv_fs_req_cleanup(&rename_req); - r = uv_fs_open(NULL, &open_req1, "test_file2", O_RDONLY, 0, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + r = uv_fs_open(NULL, &open_req1, "test_file2", UV_FS_O_RDONLY, 0, NULL); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); memset(buf, 0, sizeof(buf)); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); - ASSERT(r >= 0); - ASSERT(read_req.result >= 0); - ASSERT(strcmp(buf, test_buf) == 0); + ASSERT_GE(r, 0); + ASSERT_GE(read_req.result, 0); + ASSERT_OK(strcmp(buf, test_buf)); uv_fs_req_cleanup(&read_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); /* Cleanup */ unlink("test_file"); unlink("test_file2"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -3097,56 +3230,56 @@ static void fs_read_bufs(int add_flags) { char scratch[768]; uv_buf_t bufs[4]; - ASSERT(0 <= uv_fs_open(NULL, &open_req1, - "test/fixtures/lorem_ipsum.txt", - O_RDONLY | add_flags, 0, NULL)); - ASSERT(open_req1.result >= 0); + ASSERT_LE(0, uv_fs_open(NULL, &open_req1, + "test/fixtures/lorem_ipsum.txt", + UV_FS_O_RDONLY | add_flags, 0, NULL)); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); - ASSERT(UV_EINVAL == uv_fs_read(NULL, &read_req, open_req1.result, - NULL, 0, 0, NULL)); - ASSERT(UV_EINVAL == uv_fs_read(NULL, &read_req, open_req1.result, - NULL, 1, 0, NULL)); - ASSERT(UV_EINVAL == uv_fs_read(NULL, &read_req, open_req1.result, - bufs, 0, 0, NULL)); + ASSERT_EQ(UV_EINVAL, uv_fs_read(NULL, &read_req, open_req1.result, + NULL, 0, 0, NULL)); + ASSERT_EQ(UV_EINVAL, uv_fs_read(NULL, &read_req, open_req1.result, + NULL, 1, 0, NULL)); + ASSERT_EQ(UV_EINVAL, uv_fs_read(NULL, &read_req, open_req1.result, + bufs, 0, 0, NULL)); bufs[0] = uv_buf_init(scratch + 0, 256); bufs[1] = uv_buf_init(scratch + 256, 256); bufs[2] = uv_buf_init(scratch + 512, 128); bufs[3] = uv_buf_init(scratch + 640, 128); - ASSERT(446 == uv_fs_read(NULL, - &read_req, - open_req1.result, - bufs + 0, - 2, /* 2x 256 bytes. */ - 0, /* Positional read. */ - NULL)); - ASSERT(read_req.result == 446); + ASSERT_EQ(446, uv_fs_read(NULL, + &read_req, + open_req1.result, + bufs + 0, + 2, /* 2x 256 bytes. */ + 0, /* Positional read. */ + NULL)); + ASSERT_EQ(446, read_req.result); uv_fs_req_cleanup(&read_req); - ASSERT(190 == uv_fs_read(NULL, - &read_req, - open_req1.result, - bufs + 2, - 2, /* 2x 128 bytes. */ - 256, /* Positional read. */ - NULL)); - ASSERT(read_req.result == /* 446 - 256 */ 190); + ASSERT_EQ(190, uv_fs_read(NULL, + &read_req, + open_req1.result, + bufs + 2, + 2, /* 2x 128 bytes. */ + 256, /* Positional read. */ + NULL)); + ASSERT_EQ(read_req.result, /* 446 - 256 */ 190); uv_fs_req_cleanup(&read_req); - ASSERT(0 == memcmp(bufs[1].base + 0, bufs[2].base, 128)); - ASSERT(0 == memcmp(bufs[1].base + 128, bufs[3].base, 190 - 128)); + ASSERT_OK(memcmp(bufs[1].base + 0, bufs[2].base, 128)); + ASSERT_OK(memcmp(bufs[1].base + 128, bufs[3].base, 190 - 128)); - ASSERT(0 == uv_fs_close(NULL, &close_req, open_req1.result, NULL)); - ASSERT(close_req.result == 0); + ASSERT_OK(uv_fs_close(NULL, &close_req, open_req1.result, NULL)); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); } TEST_IMPL(fs_read_bufs) { fs_read_bufs(0); fs_read_bufs(UV_FS_O_FILEMAP); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -3163,46 +3296,47 @@ static void fs_read_file_eof(int add_flags) { loop = uv_default_loop(); r = uv_fs_open(NULL, &open_req1, "test_file", - O_WRONLY | O_CREAT | add_flags, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + UV_FS_O_WRONLY | UV_FS_O_CREAT | add_flags, S_IWUSR | S_IRUSR, + NULL); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); - ASSERT(r >= 0); - ASSERT(write_req.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(write_req.result, 0); uv_fs_req_cleanup(&write_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); - r = uv_fs_open(NULL, &open_req1, "test_file", O_RDONLY | add_flags, 0, + r = uv_fs_open(NULL, &open_req1, "test_file", UV_FS_O_RDONLY | add_flags, 0, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); memset(buf, 0, sizeof(buf)); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); - ASSERT(r >= 0); - ASSERT(read_req.result >= 0); - ASSERT(strcmp(buf, test_buf) == 0); + ASSERT_GE(r, 0); + ASSERT_GE(read_req.result, 0); + ASSERT_OK(strcmp(buf, test_buf)); uv_fs_req_cleanup(&read_req); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, read_req.result, NULL); - ASSERT(r == 0); - ASSERT(read_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(read_req.result); uv_fs_req_cleanup(&read_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); /* Cleanup */ @@ -3212,7 +3346,7 @@ TEST_IMPL(fs_read_file_eof) { fs_read_file_eof(0); fs_read_file_eof(UV_FS_O_FILEMAP); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -3227,27 +3361,28 @@ static void fs_write_multiple_bufs(int add_flags) { loop = uv_default_loop(); r = uv_fs_open(NULL, &open_req1, "test_file", - O_WRONLY | O_CREAT | add_flags, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + UV_FS_O_WRONLY | UV_FS_O_CREAT | add_flags, S_IWUSR | S_IRUSR, + NULL); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); iovs[0] = uv_buf_init(test_buf, sizeof(test_buf)); iovs[1] = uv_buf_init(test_buf2, sizeof(test_buf2)); r = uv_fs_write(NULL, &write_req, open_req1.result, iovs, 2, 0, NULL); - ASSERT(r >= 0); - ASSERT(write_req.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(write_req.result, 0); uv_fs_req_cleanup(&write_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); - r = uv_fs_open(NULL, &open_req1, "test_file", O_RDONLY | add_flags, 0, + r = uv_fs_open(NULL, &open_req1, "test_file", UV_FS_O_RDONLY | add_flags, 0, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); memset(buf, 0, sizeof(buf)); @@ -3255,48 +3390,48 @@ static void fs_write_multiple_bufs(int add_flags) { /* Read the strings back to separate buffers. */ iovs[0] = uv_buf_init(buf, sizeof(test_buf)); iovs[1] = uv_buf_init(buf2, sizeof(test_buf2)); - ASSERT(lseek(open_req1.result, 0, SEEK_CUR) == 0); + ASSERT_OK(lseek(open_req1.result, 0, SEEK_CUR)); r = uv_fs_read(NULL, &read_req, open_req1.result, iovs, 2, -1, NULL); - ASSERT(r >= 0); - ASSERT(read_req.result == sizeof(test_buf) + sizeof(test_buf2)); - ASSERT(strcmp(buf, test_buf) == 0); - ASSERT(strcmp(buf2, test_buf2) == 0); + ASSERT_GE(r, 0); + ASSERT_EQ(read_req.result, sizeof(test_buf) + sizeof(test_buf2)); + ASSERT_OK(strcmp(buf, test_buf)); + ASSERT_OK(strcmp(buf2, test_buf2)); uv_fs_req_cleanup(&read_req); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); - ASSERT(r == 0); - ASSERT(read_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(read_req.result); uv_fs_req_cleanup(&read_req); /* Read the strings back to separate buffers. */ iovs[0] = uv_buf_init(buf, sizeof(test_buf)); iovs[1] = uv_buf_init(buf2, sizeof(test_buf2)); r = uv_fs_read(NULL, &read_req, open_req1.result, iovs, 2, 0, NULL); - ASSERT(r >= 0); + ASSERT_GE(r, 0); if (read_req.result == sizeof(test_buf)) { /* Infer that preadv is not available. */ uv_fs_req_cleanup(&read_req); r = uv_fs_read(NULL, &read_req, open_req1.result, &iovs[1], 1, read_req.result, NULL); - ASSERT(r >= 0); - ASSERT(read_req.result == sizeof(test_buf2)); + ASSERT_GE(r, 0); + ASSERT_EQ(read_req.result, sizeof(test_buf2)); } else { - ASSERT(read_req.result == sizeof(test_buf) + sizeof(test_buf2)); + ASSERT_EQ(read_req.result, sizeof(test_buf) + sizeof(test_buf2)); } - ASSERT(strcmp(buf, test_buf) == 0); - ASSERT(strcmp(buf2, test_buf2) == 0); + ASSERT_OK(strcmp(buf, test_buf)); + ASSERT_OK(strcmp(buf2, test_buf2)); uv_fs_req_cleanup(&read_req); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, sizeof(test_buf) + sizeof(test_buf2), NULL); - ASSERT(r == 0); - ASSERT(read_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(read_req.result); uv_fs_req_cleanup(&read_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); /* Cleanup */ @@ -3306,7 +3441,7 @@ TEST_IMPL(fs_write_multiple_bufs) { fs_write_multiple_bufs(0); fs_write_multiple_bufs(UV_FS_O_FILEMAP); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -3333,11 +3468,11 @@ static void fs_write_alotof_bufs(int add_flags) { r = uv_fs_open(NULL, &open_req1, "test_file", - O_RDWR | O_CREAT | add_flags, + UV_FS_O_RDWR | UV_FS_O_CREAT | add_flags, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); for (index = 0; index < iovcount; ++index) @@ -3350,8 +3485,8 @@ static void fs_write_alotof_bufs(int add_flags) { iovcount, -1, NULL); - ASSERT(r >= 0); - ASSERT((size_t)write_req.result == sizeof(test_buf) * iovcount); + ASSERT_GE(r, 0); + ASSERT_EQ((size_t)write_req.result, sizeof(test_buf) * iovcount); uv_fs_req_cleanup(&write_req); /* Read the strings back to separate buffers. */ @@ -3363,31 +3498,32 @@ static void fs_write_alotof_bufs(int add_flags) { sizeof(test_buf)); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); - r = uv_fs_open(NULL, &open_req1, "test_file", O_RDONLY | add_flags, 0, + r = uv_fs_open(NULL, &open_req1, "test_file", UV_FS_O_RDONLY | add_flags, 0, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); r = uv_fs_read(NULL, &read_req, open_req1.result, iovs, iovcount, -1, NULL); if (iovcount > iovmax) iovcount = iovmax; - ASSERT(r >= 0); - ASSERT((size_t)read_req.result == sizeof(test_buf) * iovcount); + ASSERT_GE(r, 0); + ASSERT_EQ((size_t)read_req.result, sizeof(test_buf) * iovcount); for (index = 0; index < iovcount; ++index) - ASSERT(strncmp(buffer + index * sizeof(test_buf), - test_buf, - sizeof(test_buf)) == 0); + ASSERT_OK(strncmp(buffer + index * sizeof(test_buf), + test_buf, + sizeof(test_buf))); uv_fs_req_cleanup(&read_req); free(buffer); - ASSERT(lseek(open_req1.result, write_req.result, SEEK_SET) == write_req.result); + ASSERT_EQ(lseek(open_req1.result, write_req.result, SEEK_SET), + write_req.result); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, @@ -3396,13 +3532,13 @@ static void fs_write_alotof_bufs(int add_flags) { 1, -1, NULL); - ASSERT(r == 0); - ASSERT(read_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(read_req.result); uv_fs_req_cleanup(&read_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); /* Cleanup */ @@ -3413,7 +3549,7 @@ TEST_IMPL(fs_write_alotof_bufs) { fs_write_alotof_bufs(0); fs_write_alotof_bufs(UV_FS_O_FILEMAP); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -3445,17 +3581,17 @@ static void fs_write_alotof_bufs_with_offset(int add_flags) { r = uv_fs_open(NULL, &open_req1, "test_file", - O_RDWR | O_CREAT | add_flags, + UV_FS_O_RDWR | UV_FS_O_CREAT | add_flags, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); iov = uv_buf_init(filler, filler_len); r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); - ASSERT(r == filler_len); - ASSERT(write_req.result == filler_len); + ASSERT_EQ(r, filler_len); + ASSERT_EQ(write_req.result, filler_len); uv_fs_req_cleanup(&write_req); offset = (int64_t)r; @@ -3469,8 +3605,8 @@ static void fs_write_alotof_bufs_with_offset(int add_flags) { iovcount, offset, NULL); - ASSERT(r >= 0); - ASSERT((size_t)write_req.result == sizeof(test_buf) * iovcount); + ASSERT_GE(r, 0); + ASSERT_EQ((size_t)write_req.result, sizeof(test_buf) * iovcount); uv_fs_req_cleanup(&write_req); /* Read the strings back to separate buffers. */ @@ -3483,25 +3619,25 @@ static void fs_write_alotof_bufs_with_offset(int add_flags) { r = uv_fs_read(NULL, &read_req, open_req1.result, iovs, iovcount, offset, NULL); - ASSERT(r >= 0); + ASSERT_GE(r, 0); if (r == sizeof(test_buf)) iovcount = 1; /* Infer that preadv is not available. */ else if (iovcount > iovmax) iovcount = iovmax; - ASSERT((size_t)read_req.result == sizeof(test_buf) * iovcount); + ASSERT_EQ((size_t)read_req.result, sizeof(test_buf) * iovcount); for (index = 0; index < iovcount; ++index) - ASSERT(strncmp(buffer + index * sizeof(test_buf), - test_buf, - sizeof(test_buf)) == 0); + ASSERT_OK(strncmp(buffer + index * sizeof(test_buf), + test_buf, + sizeof(test_buf))); uv_fs_req_cleanup(&read_req); free(buffer); r = uv_fs_stat(NULL, &stat_req, "test_file", NULL); - ASSERT(r == 0); - ASSERT((int64_t)((uv_stat_t*)stat_req.ptr)->st_size == - offset + (int64_t)write_req.result); + ASSERT_OK(r); + ASSERT_EQ((int64_t)((uv_stat_t*)stat_req.ptr)->st_size, + offset + (int64_t)write_req.result); uv_fs_req_cleanup(&stat_req); iov = uv_buf_init(buf, sizeof(buf)); @@ -3512,13 +3648,13 @@ static void fs_write_alotof_bufs_with_offset(int add_flags) { 1, offset + write_req.result, NULL); - ASSERT(r == 0); - ASSERT(read_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(read_req.result); uv_fs_req_cleanup(&read_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); /* Cleanup */ @@ -3529,7 +3665,7 @@ TEST_IMPL(fs_write_alotof_bufs_with_offset) { fs_write_alotof_bufs_with_offset(0); fs_write_alotof_bufs_with_offset(UV_FS_O_FILEMAP); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -3541,9 +3677,9 @@ TEST_IMPL(fs_read_dir) { /* Setup */ rmdir("test_dir"); r = uv_fs_mkdir(loop, &mkdir_req, "test_dir", 0755, mkdir_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(mkdir_cb_count == 1); + ASSERT_EQ(1, mkdir_cb_count); /* Setup Done Here */ /* Get a file descriptor for the directory */ @@ -3553,7 +3689,7 @@ TEST_IMPL(fs_read_dir) { UV_FS_O_RDONLY | UV_FS_O_DIRECTORY, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); + ASSERT_GE(r, 0); uv_fs_req_cleanup(&open_req1); /* Try to read data from the directory */ @@ -3573,18 +3709,18 @@ TEST_IMPL(fs_read_dir) { */ ASSERT((r >= 0) || (r == UV_EISDIR)); #else - ASSERT(r == UV_EISDIR); + ASSERT_EQ(r, UV_EISDIR); #endif uv_fs_req_cleanup(&read_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&close_req); /* Cleanup */ rmdir("test_dir"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -3625,7 +3761,7 @@ static void thread_main(void* arg) { if (ctx->doread) { result = write(ctx->fd, data, nbytes); /* Should not see EINTR (or other errors) */ - ASSERT(result == nbytes); + ASSERT_EQ(result, nbytes); } else { result = read(ctx->fd, data, nbytes); /* Should not see EINTR (or other errors), @@ -3677,9 +3813,9 @@ static void test_fs_partial(int doread) { ctx.doread = doread; ctx.interval = 1000; ctx.size = sizeof(test_buf) * iovcount; - ctx.data = malloc(ctx.size); + ctx.data = calloc(ctx.size, 1); ASSERT_NOT_NULL(ctx.data); - buffer = malloc(ctx.size); + buffer = calloc(ctx.size, 1); ASSERT_NOT_NULL(buffer); for (index = 0; index < iovcount; ++index) @@ -3687,13 +3823,13 @@ static void test_fs_partial(int doread) { loop = uv_default_loop(); - ASSERT(0 == uv_signal_init(loop, &signal)); - ASSERT(0 == uv_signal_start(&signal, sig_func, SIGUSR1)); + ASSERT_OK(uv_signal_init(loop, &signal)); + ASSERT_OK(uv_signal_start(&signal, sig_func, SIGUSR1)); - ASSERT(0 == pipe(pipe_fds)); + ASSERT_OK(pipe(pipe_fds)); ctx.fd = pipe_fds[doread]; - ASSERT(0 == uv_thread_create(&thread, thread_main, &ctx)); + ASSERT_OK(uv_thread_create(&thread, thread_main, &ctx)); if (doread) { uv_buf_t* read_iovs; @@ -3710,39 +3846,40 @@ static void test_fs_partial(int doread) { iovcount -= read_iovcount; nread += result; } else { - ASSERT(result == UV_EINTR); + ASSERT_EQ(result, UV_EINTR); } uv_fs_req_cleanup(&read_req); } } else { int result; result = uv_fs_write(loop, &write_req, pipe_fds[1], iovs, iovcount, -1, NULL); - ASSERT(write_req.result == result); - ASSERT(result == ctx.size); + ASSERT_EQ(write_req.result, result); + ASSERT_EQ(result, ctx.size); uv_fs_req_cleanup(&write_req); } - ASSERT(0 == memcmp(buffer, ctx.data, ctx.size)); + ASSERT_OK(uv_thread_join(&thread)); - ASSERT(0 == uv_thread_join(&thread)); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_MEM_EQ(buffer, ctx.data, ctx.size); - ASSERT(0 == close(pipe_fds[1])); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + + ASSERT_OK(close(pipe_fds[1])); uv_close((uv_handle_t*) &signal, NULL); { /* Make sure we read everything that we wrote. */ int result; result = uv_fs_read(loop, &read_req, pipe_fds[0], iovs, 1, -1, NULL); - ASSERT(result == 0); + ASSERT_OK(result); uv_fs_req_cleanup(&read_req); } - ASSERT(0 == close(pipe_fds[0])); + ASSERT_OK(close(pipe_fds[0])); free(iovs); free(buffer); free(ctx.data); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); } TEST_IMPL(fs_partial_read) { @@ -3761,13 +3898,13 @@ TEST_IMPL(fs_read_write_null_arguments) { int r; r = uv_fs_read(NULL, &read_req, 0, NULL, 0, -1, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_fs_req_cleanup(&read_req); r = uv_fs_write(NULL, &write_req, 0, NULL, 0, -1, NULL); /* Validate some memory management on failed input validation before sending fs work to the thread pool. */ - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); ASSERT_NULL(write_req.path); ASSERT_NULL(write_req.ptr); #ifdef _WIN32 @@ -3782,39 +3919,40 @@ TEST_IMPL(fs_read_write_null_arguments) { iov = uv_buf_init(NULL, 0); r = uv_fs_read(NULL, &read_req, 0, &iov, 0, -1, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_fs_req_cleanup(&read_req); iov = uv_buf_init(NULL, 0); r = uv_fs_write(NULL, &write_req, 0, &iov, 0, -1, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_fs_req_cleanup(&write_req); /* If the arguments are invalid, the loop should not be kept open */ loop = uv_default_loop(); r = uv_fs_read(loop, &read_req, 0, NULL, 0, -1, fail_cb); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_run(loop, UV_RUN_DEFAULT); uv_fs_req_cleanup(&read_req); r = uv_fs_write(loop, &write_req, 0, NULL, 0, -1, fail_cb); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_run(loop, UV_RUN_DEFAULT); uv_fs_req_cleanup(&write_req); iov = uv_buf_init(NULL, 0); r = uv_fs_read(loop, &read_req, 0, &iov, 0, -1, fail_cb); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_run(loop, UV_RUN_DEFAULT); uv_fs_req_cleanup(&read_req); iov = uv_buf_init(NULL, 0); r = uv_fs_write(loop, &write_req, 0, &iov, 0, -1, fail_cb); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_run(loop, UV_RUN_DEFAULT); uv_fs_req_cleanup(&write_req); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -3829,31 +3967,29 @@ TEST_IMPL(get_osfhandle_valid_handle) { loop = uv_default_loop(); r = uv_fs_open(NULL, - &open_req1, - "test_file", - O_RDWR | O_CREAT, + &open_req1, "test_file", UV_FS_O_RDWR | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); fd = uv_get_osfhandle(open_req1.result); #ifdef _WIN32 - ASSERT(fd != INVALID_HANDLE_VALUE); + ASSERT_PTR_NE(fd, INVALID_HANDLE_VALUE); #else - ASSERT(fd >= 0); + ASSERT_GE(fd, 0); #endif r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); /* Cleanup. */ unlink("test_file"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -3870,36 +4006,36 @@ TEST_IMPL(open_osfhandle_valid_handle) { r = uv_fs_open(NULL, &open_req1, "test_file", - O_RDWR | O_CREAT, + UV_FS_O_RDWR | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); handle = uv_get_osfhandle(open_req1.result); #ifdef _WIN32 - ASSERT(handle != INVALID_HANDLE_VALUE); + ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE); #else - ASSERT(handle >= 0); + ASSERT_GE(handle, 0); #endif fd = uv_open_osfhandle(handle); #ifdef _WIN32 - ASSERT(fd > 0); + ASSERT_GT(fd, 0); #else - ASSERT(fd == open_req1.result); + ASSERT_EQ(fd, open_req1.result); #endif r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); /* Cleanup. */ unlink("test_file"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -3911,35 +4047,33 @@ TEST_IMPL(fs_file_pos_after_op_with_offset) { loop = uv_default_loop(); r = uv_fs_open(loop, - &open_req1, - "test_file", - O_RDWR | O_CREAT, + &open_req1, "test_file", UV_FS_O_RDWR | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r > 0); + ASSERT_GT(r, 0); uv_fs_req_cleanup(&open_req1); iov = uv_buf_init(test_buf, sizeof(test_buf)); r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, 0, NULL); - ASSERT(r == sizeof(test_buf)); - ASSERT(lseek(open_req1.result, 0, SEEK_CUR) == 0); + ASSERT_EQ(r, sizeof(test_buf)); + ASSERT_OK(lseek(open_req1.result, 0, SEEK_CUR)); uv_fs_req_cleanup(&write_req); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, 0, NULL); - ASSERT(r == sizeof(test_buf)); - ASSERT(strcmp(buf, test_buf) == 0); - ASSERT(lseek(open_req1.result, 0, SEEK_CUR) == 0); + ASSERT_EQ(r, sizeof(test_buf)); + ASSERT_OK(strcmp(buf, test_buf)); + ASSERT_OK(lseek(open_req1.result, 0, SEEK_CUR)); uv_fs_req_cleanup(&read_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&close_req); /* Cleanup */ unlink("test_file"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -3949,30 +4083,30 @@ static void fs_file_pos_common(void) { iov = uv_buf_init("abc", 3); r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); - ASSERT(r == 3); + ASSERT_EQ(3, r); uv_fs_req_cleanup(&write_req); /* Read with offset should not change the position */ iov = uv_buf_init(buf, 1); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, 1, NULL); - ASSERT(r == 1); - ASSERT(buf[0] == 'b'); + ASSERT_EQ(1, r); + ASSERT_EQ(buf[0], 'b'); uv_fs_req_cleanup(&read_req); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&read_req); /* Write without offset should change the position */ iov = uv_buf_init("d", 1); r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, -1, NULL); - ASSERT(r == 1); + ASSERT_EQ(1, r); uv_fs_req_cleanup(&write_req); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&read_req); } @@ -3981,23 +4115,23 @@ static void fs_file_pos_close_check(const char *contents, int size) { /* Close */ r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&close_req); /* Confirm file contents */ - r = uv_fs_open(NULL, &open_req1, "test_file", O_RDONLY, 0, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + r = uv_fs_open(NULL, &open_req1, "test_file", UV_FS_O_RDONLY, 0, NULL); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); - ASSERT(r == size); - ASSERT(strncmp(buf, contents, size) == 0); + ASSERT_EQ(r, size); + ASSERT_OK(strncmp(buf, contents, size)); uv_fs_req_cleanup(&read_req); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&close_req); /* Cleanup */ @@ -4013,10 +4147,10 @@ static void fs_file_pos_write(int add_flags) { r = uv_fs_open(NULL, &open_req1, "test_file", - O_TRUNC | O_CREAT | O_RDWR | add_flags, + UV_FS_O_TRUNC | UV_FS_O_CREAT | UV_FS_O_RDWR | add_flags, S_IWUSR | S_IRUSR, NULL); - ASSERT(r > 0); + ASSERT_GT(r, 0); uv_fs_req_cleanup(&open_req1); fs_file_pos_common(); @@ -4024,12 +4158,12 @@ static void fs_file_pos_write(int add_flags) { /* Write with offset should not change the position */ iov = uv_buf_init("e", 1); r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, 1, NULL); - ASSERT(r == 1); + ASSERT_EQ(1, r); uv_fs_req_cleanup(&write_req); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&read_req); fs_file_pos_close_check("aecd", 4); @@ -4038,7 +4172,7 @@ TEST_IMPL(fs_file_pos_write) { fs_file_pos_write(0); fs_file_pos_write(UV_FS_O_FILEMAP); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -4051,10 +4185,10 @@ static void fs_file_pos_append(int add_flags) { r = uv_fs_open(NULL, &open_req1, "test_file", - O_APPEND | O_CREAT | O_RDWR | add_flags, + UV_FS_O_APPEND | UV_FS_O_CREAT | UV_FS_O_RDWR | add_flags, S_IWUSR | S_IRUSR, NULL); - ASSERT(r > 0); + ASSERT_GT(r, 0); uv_fs_req_cleanup(&open_req1); fs_file_pos_common(); @@ -4063,13 +4197,13 @@ static void fs_file_pos_append(int add_flags) { * but does not change the position */ iov = uv_buf_init("e", 1); r = uv_fs_write(NULL, &write_req, open_req1.result, &iov, 1, 1, NULL); - ASSERT(r == 1); + ASSERT_EQ(1, r); uv_fs_req_cleanup(&write_req); iov = uv_buf_init(buf, sizeof(buf)); r = uv_fs_read(NULL, &read_req, open_req1.result, &iov, 1, -1, NULL); - ASSERT(r == 1); - ASSERT(buf[0] == 'e'); + ASSERT_EQ(1, r); + ASSERT_EQ(buf[0], 'e'); uv_fs_req_cleanup(&read_req); fs_file_pos_close_check("abcde", 5); @@ -4078,7 +4212,7 @@ TEST_IMPL(fs_file_pos_append) { fs_file_pos_append(0); fs_file_pos_append(UV_FS_O_FILEMAP); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } #endif @@ -4088,97 +4222,97 @@ TEST_IMPL(fs_null_req) { int r; r = uv_fs_open(NULL, NULL, NULL, 0, 0, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_close(NULL, NULL, 0, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_read(NULL, NULL, 0, NULL, 0, -1, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_write(NULL, NULL, 0, NULL, 0, -1, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_unlink(NULL, NULL, NULL, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_mkdir(NULL, NULL, NULL, 0, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_mkdtemp(NULL, NULL, NULL, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_mkstemp(NULL, NULL, NULL, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_rmdir(NULL, NULL, NULL, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_scandir(NULL, NULL, NULL, 0, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_link(NULL, NULL, NULL, NULL, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_symlink(NULL, NULL, NULL, NULL, 0, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_readlink(NULL, NULL, NULL, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_realpath(NULL, NULL, NULL, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_chown(NULL, NULL, NULL, 0, 0, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_fchown(NULL, NULL, 0, 0, 0, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_stat(NULL, NULL, NULL, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_lstat(NULL, NULL, NULL, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_fstat(NULL, NULL, 0, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_rename(NULL, NULL, NULL, NULL, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_fsync(NULL, NULL, 0, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_fdatasync(NULL, NULL, 0, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_ftruncate(NULL, NULL, 0, 0, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_copyfile(NULL, NULL, NULL, NULL, 0, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_sendfile(NULL, NULL, 0, 0, 0, 0, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_access(NULL, NULL, NULL, 0, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_chmod(NULL, NULL, NULL, 0, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_fchmod(NULL, NULL, 0, 0, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_utime(NULL, NULL, NULL, 0.0, 0.0, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_futime(NULL, NULL, 0, 0.0, 0.0, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_fs_statfs(NULL, NULL, NULL, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); /* This should be a no-op. */ uv_fs_req_cleanup(NULL); @@ -4193,52 +4327,50 @@ TEST_IMPL(fs_exclusive_sharing_mode) { /* Setup. */ unlink("test_file"); - ASSERT(UV_FS_O_EXLOCK > 0); + ASSERT_GT(UV_FS_O_EXLOCK, 0); r = uv_fs_open(NULL, &open_req1, "test_file", - O_RDWR | O_CREAT | UV_FS_O_EXLOCK, + UV_FS_O_RDWR | UV_FS_O_CREAT | UV_FS_O_EXLOCK, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); r = uv_fs_open(NULL, &open_req2, - "test_file", - O_RDONLY | UV_FS_O_EXLOCK, + "test_file", UV_FS_O_RDONLY | UV_FS_O_EXLOCK, S_IWUSR | S_IRUSR, NULL); - ASSERT(r < 0); - ASSERT(open_req2.result < 0); + ASSERT_LT(r, 0); + ASSERT_LT(open_req2.result, 0); uv_fs_req_cleanup(&open_req2); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); r = uv_fs_open(NULL, &open_req2, - "test_file", - O_RDONLY | UV_FS_O_EXLOCK, + "test_file", UV_FS_O_RDONLY | UV_FS_O_EXLOCK, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req2.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(open_req2.result, 0); uv_fs_req_cleanup(&open_req2); r = uv_fs_close(NULL, &close_req, open_req2.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); /* Cleanup */ unlink("test_file"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } #endif @@ -4250,10 +4382,10 @@ TEST_IMPL(fs_file_flag_no_buffering) { /* Setup. */ unlink("test_file"); - ASSERT(UV_FS_O_APPEND > 0); - ASSERT(UV_FS_O_CREAT > 0); - ASSERT(UV_FS_O_DIRECT > 0); - ASSERT(UV_FS_O_RDWR > 0); + ASSERT_GT(UV_FS_O_APPEND, 0); + ASSERT_GT(UV_FS_O_CREAT, 0); + ASSERT_GT(UV_FS_O_DIRECT, 0); + ASSERT_GT(UV_FS_O_RDWR, 0); /* FILE_APPEND_DATA must be excluded from FILE_GENERIC_WRITE: */ r = uv_fs_open(NULL, @@ -4262,13 +4394,13 @@ TEST_IMPL(fs_file_flag_no_buffering) { UV_FS_O_RDWR | UV_FS_O_CREAT | UV_FS_O_DIRECT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); /* FILE_APPEND_DATA and FILE_FLAG_NO_BUFFERING are mutually exclusive: */ @@ -4278,14 +4410,14 @@ TEST_IMPL(fs_file_flag_no_buffering) { UV_FS_O_APPEND | UV_FS_O_DIRECT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r == UV_EINVAL); - ASSERT(open_req2.result == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); + ASSERT_EQ(open_req2.result, UV_EINVAL); uv_fs_req_cleanup(&open_req2); /* Cleanup */ unlink("test_file"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } #endif @@ -4320,7 +4452,7 @@ TEST_IMPL(fs_open_readonly_acl) { /* Setup - clear the ACL and remove the file */ loop = uv_default_loop(); r = uv_os_get_passwd(&pwd); - ASSERT(r == 0); + ASSERT_OK(r); call_icacls("icacls test_file_icacls /remove \"%s\" /inheritance:e", pwd.username); uv_fs_chmod(loop, &req, "test_file_icacls", S_IWUSR, NULL); @@ -4330,15 +4462,15 @@ TEST_IMPL(fs_open_readonly_acl) { r = uv_fs_open(loop, &open_req1, "test_file_icacls", - O_RDONLY | O_CREAT, + UV_FS_O_RDONLY | UV_FS_O_CREAT, S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(open_req1.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); uv_fs_req_cleanup(&open_req1); r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); - ASSERT(r == 0); - ASSERT(close_req.result == 0); + ASSERT_OK(r); + ASSERT_OK(close_req.result); uv_fs_req_cleanup(&close_req); /* Set up ACL */ @@ -4353,7 +4485,8 @@ TEST_IMPL(fs_open_readonly_acl) { } /* Try opening the file */ - r = uv_fs_open(NULL, &open_req1, "test_file_icacls", O_RDONLY, 0, NULL); + r = uv_fs_open(NULL, &open_req1, "test_file_icacls", UV_FS_O_RDONLY, 0, + NULL); if (r < 0) { goto acl_cleanup; } @@ -4370,8 +4503,62 @@ TEST_IMPL(fs_open_readonly_acl) { pwd.username); unlink("test_file_icacls"); uv_os_free_passwd(&pwd); - ASSERT(r == 0); - MAKE_VALGRIND_HAPPY(); + ASSERT_OK(r); + MAKE_VALGRIND_HAPPY(loop); + return 0; +} + +TEST_IMPL(fs_stat_no_permission) { + uv_passwd_t pwd; + uv_fs_t req; + int r; + char* filename = "test_file_no_permission.txt"; + + /* Setup - clear the ACL and remove the file */ + loop = uv_default_loop(); + r = uv_os_get_passwd(&pwd); + ASSERT_OK(r); + call_icacls("icacls %s /remove *S-1-1-0:(F)", filename); + unlink(filename); + + /* Create the file */ + r = uv_fs_open(loop, + &open_req1, + filename, + UV_FS_O_RDONLY | UV_FS_O_CREAT, + S_IRUSR, + NULL); + ASSERT_GE(r, 0); + ASSERT_GE(open_req1.result, 0); + uv_fs_req_cleanup(&open_req1); + r = uv_fs_close(NULL, &close_req, open_req1.result, NULL); + ASSERT_OK(r); + ASSERT_OK(close_req.result); + uv_fs_req_cleanup(&close_req); + + /* Set up ACL */ + r = call_icacls("icacls %s /deny *S-1-1-0:(F)", filename); + if (r != 0) { + goto acl_cleanup; + } + + /* Read file stats */ + r = uv_fs_stat(NULL, &req, filename, NULL); + if (r != 0) { + goto acl_cleanup; + } + + uv_fs_req_cleanup(&req); + + acl_cleanup: + /* Cleanup */ + call_icacls("icacls %s /reset", filename); + uv_fs_unlink(NULL, &unlink_req, filename, NULL); + uv_fs_req_cleanup(&unlink_req); + unlink(filename); + uv_os_free_passwd(&pwd); + ASSERT_OK(r); + MAKE_VALGRIND_HAPPY(loop); return 0; } #endif @@ -4386,40 +4573,38 @@ TEST_IMPL(fs_fchmod_archive_readonly) { /* Setup*/ unlink("test_file"); r = uv_fs_open(NULL, - &req, - "test_file", - O_WRONLY | O_CREAT, + &req, "test_file", UV_FS_O_WRONLY | UV_FS_O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); file = req.result; uv_fs_req_cleanup(&req); r = uv_fs_close(NULL, &req, file, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&req); /* Make the file read-only and clear archive flag */ r = SetFileAttributes("test_file", FILE_ATTRIBUTE_READONLY); - ASSERT(r != 0); + ASSERT(r); check_permission("test_file", 0400); /* Try fchmod */ - r = uv_fs_open(NULL, &req, "test_file", O_RDONLY, 0, NULL); - ASSERT(r >= 0); - ASSERT(req.result >= 0); + r = uv_fs_open(NULL, &req, "test_file", UV_FS_O_RDONLY, 0, NULL); + ASSERT_GE(r, 0); + ASSERT_GE(req.result, 0); file = req.result; uv_fs_req_cleanup(&req); r = uv_fs_fchmod(NULL, &req, file, S_IWUSR, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); + ASSERT_OK(r); + ASSERT_OK(req.result); uv_fs_req_cleanup(&req); r = uv_fs_close(NULL, &req, file, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&req); check_permission("test_file", S_IWUSR); /* Restore Archive flag for rest of the tests */ r = SetFileAttributes("test_file", FILE_ATTRIBUTE_ARCHIVE); - ASSERT(r != 0); + ASSERT(r); return 0; } @@ -4431,7 +4616,7 @@ TEST_IMPL(fs_invalid_mkdir_name) { loop = uv_default_loop(); r = uv_fs_mkdir(loop, &req, "invalid>", 0, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); ASSERT_EQ(UV_EINVAL, uv_fs_mkdir(loop, &req, "test:lol", 0, NULL)); return 0; @@ -4446,16 +4631,17 @@ TEST_IMPL(fs_statfs) { /* Test the synchronous version. */ r = uv_fs_statfs(NULL, &req, ".", NULL); - ASSERT(r == 0); + ASSERT_OK(r); statfs_cb(&req); - ASSERT(statfs_cb_count == 1); + ASSERT_EQ(1, statfs_cb_count); /* Test the asynchronous version. */ r = uv_fs_statfs(loop, &req, ".", statfs_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(statfs_cb_count == 2); + ASSERT_EQ(2, statfs_cb_count); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -4465,14 +4651,91 @@ TEST_IMPL(fs_get_system_error) { int system_error; r = uv_fs_statfs(NULL, &req, "non_existing_file", NULL); - ASSERT(r != 0); + ASSERT(r); system_error = uv_fs_get_system_error(&req); #ifdef _WIN32 - ASSERT(system_error == ERROR_FILE_NOT_FOUND); + ASSERT_EQ(system_error, ERROR_FILE_NOT_FOUND); #else - ASSERT(system_error == ENOENT); + ASSERT_EQ(system_error, ENOENT); #endif return 0; } + + +TEST_IMPL(fs_stat_batch_multiple) { + uv_fs_t req[300]; + int r; + int i; + + rmdir("test_dir"); + + r = uv_fs_mkdir(NULL, &mkdir_req, "test_dir", 0755, NULL); + ASSERT_OK(r); + + loop = uv_default_loop(); + + for (i = 0; i < (int) ARRAY_SIZE(req); ++i) { + r = uv_fs_stat(loop, &req[i], "test_dir", stat_batch_cb); + ASSERT_OK(r); + } + + uv_run(loop, UV_RUN_DEFAULT); + ASSERT_EQ(stat_cb_count, ARRAY_SIZE(req)); + + MAKE_VALGRIND_HAPPY(loop); + return 0; +} + + +#ifdef _WIN32 +TEST_IMPL(fs_wtf) { + int r; + HANDLE file_handle; + uv_dirent_t dent; + static char test_file_buf[PATHMAX]; + + /* set-up */ + _wunlink(L"test_dir/hi\xD801\x0037"); + rmdir("test_dir"); + + loop = uv_default_loop(); + + r = uv_fs_mkdir(NULL, &mkdir_req, "test_dir", 0777, NULL); + ASSERT_OK(r); + uv_fs_req_cleanup(&mkdir_req); + + file_handle = CreateFileW(L"test_dir/hi\xD801\x0037", + GENERIC_WRITE | FILE_WRITE_ATTRIBUTES, + 0, + NULL, + CREATE_ALWAYS, + FILE_FLAG_OPEN_REPARSE_POINT | + FILE_FLAG_BACKUP_SEMANTICS, + NULL); + ASSERT_PTR_NE(file_handle, INVALID_HANDLE_VALUE); + + CloseHandle(file_handle); + + r = uv_fs_scandir(NULL, &scandir_req, "test_dir", 0, NULL); + ASSERT_EQ(1, r); + ASSERT_EQ(1, scandir_req.result); + ASSERT_NOT_NULL(scandir_req.ptr); + while (UV_EOF != uv_fs_scandir_next(&scandir_req, &dent)) { + snprintf(test_file_buf, sizeof(test_file_buf), "test_dir\\%s", dent.name); + printf("stat %s\n", test_file_buf); + r = uv_fs_stat(NULL, &stat_req, test_file_buf, NULL); + ASSERT_OK(r); + } + uv_fs_req_cleanup(&scandir_req); + ASSERT_NULL(scandir_req.ptr); + + /* clean-up */ + _wunlink(L"test_dir/hi\xD801\x0037"); + rmdir("test_dir"); + + MAKE_VALGRIND_HAPPY(loop); + return 0; +} +#endif diff --git a/test/test-get-currentexe.c b/test/test-get-currentexe.c index dc239cc89d1..c813d3a5c4f 100644 --- a/test/test-get-currentexe.c +++ b/test/test-get-currentexe.c @@ -35,6 +35,9 @@ TEST_IMPL(get_currentexe) { #if defined(__QEMU__) RETURN_SKIP("Test does not currently work in QEMU"); #endif +#if defined(__OpenBSD__) + RETURN_SKIP("Test does not currently work in OpenBSD"); +#endif char buffer[PATHMAX]; char path[PATHMAX]; @@ -57,43 +60,43 @@ TEST_IMPL(get_currentexe) { * executable_path. */ ASSERT(match && !strcmp(match, path)); - ASSERT(size == strlen(buffer)); + ASSERT_EQ(size, strlen(buffer)); /* Negative tests */ size = sizeof(buffer) / sizeof(buffer[0]); r = uv_exepath(NULL, &size); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_exepath(buffer, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); size = 0; r = uv_exepath(buffer, &size); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); memset(buffer, -1, sizeof(buffer)); size = 1; r = uv_exepath(buffer, &size); - ASSERT(r == 0); - ASSERT(size == 0); - ASSERT(buffer[0] == '\0'); + ASSERT_OK(r); + ASSERT_OK(size); + ASSERT_EQ(buffer[0], '\0'); memset(buffer, -1, sizeof(buffer)); size = 2; r = uv_exepath(buffer, &size); - ASSERT(r == 0); - ASSERT(size == 1); - ASSERT(buffer[0] != '\0'); - ASSERT(buffer[1] == '\0'); + ASSERT_OK(r); + ASSERT_EQ(1, size); + ASSERT_NE(buffer[0], '\0'); + ASSERT_EQ(buffer[1], '\0'); /* Verify uv_exepath is not affected by uv_set_process_title(). */ r = uv_set_process_title("foobar"); - ASSERT_EQ(r, 0); + ASSERT_OK(r); size = sizeof(buffer); r = uv_exepath(buffer, &size); - ASSERT_EQ(r, 0); + ASSERT_OK(r); match = strstr(buffer, path); /* Verify that the path returned from uv_exepath is a subdirectory of diff --git a/test/test-get-loadavg.c b/test/test-get-loadavg.c index 4762e47576d..ef1719c047f 100644 --- a/test/test-get-loadavg.c +++ b/test/test-get-loadavg.c @@ -27,9 +27,9 @@ TEST_IMPL(get_loadavg) { double avg[3] = {-1, -1, -1}; uv_loadavg(avg); - ASSERT(avg[0] >= 0); - ASSERT(avg[1] >= 0); - ASSERT(avg[2] >= 0); + ASSERT_GE(avg[0], 0); + ASSERT_GE(avg[1], 0); + ASSERT_GE(avg[2], 0); return 0; } diff --git a/test/test-get-memory.c b/test/test-get-memory.c index 4555ba08895..2a23f79470f 100644 --- a/test/test-get-memory.c +++ b/test/test-get-memory.c @@ -26,19 +26,28 @@ TEST_IMPL(get_memory) { uint64_t free_mem = uv_get_free_memory(); uint64_t total_mem = uv_get_total_memory(); uint64_t constrained_mem = uv_get_constrained_memory(); + uint64_t available_mem = uv_get_available_memory(); - printf("free_mem=%llu, total_mem=%llu, constrained_mem=%llu\n", + printf("free_mem=%llu, total_mem=%llu, constrained_mem=%llu, " + "available_mem=%llu\n", (unsigned long long) free_mem, (unsigned long long) total_mem, - (unsigned long long) constrained_mem); + (unsigned long long) constrained_mem, + (unsigned long long) available_mem); - ASSERT(free_mem > 0); - ASSERT(total_mem > 0); + ASSERT_GT(free_mem, 0); + ASSERT_GT(total_mem, 0); /* On IBMi PASE, the amount of memory in use is always zero. */ #ifdef __PASE__ - ASSERT(total_mem == free_mem); + ASSERT_EQ(total_mem, free_mem); #else - ASSERT(total_mem > free_mem); + ASSERT_GT(total_mem, free_mem); #endif + ASSERT_LE(available_mem, total_mem); + /* we'd really want to test if available <= free, but that is fragile: + * with no limit set, get_available calls and returns get_free; so if + * any memory was freed between our calls to get_free and get_available + * we would fail such a test test (as observed on CI). + */ return 0; } diff --git a/test/test-get-passwd.c b/test/test-get-passwd.c index 865c07d6515..b1e76200975 100644 --- a/test/test-get-passwd.c +++ b/test/test-get-passwd.c @@ -22,6 +22,10 @@ #include "uv.h" #include "task.h" #include +#ifndef _WIN32 +#include +#include +#endif TEST_IMPL(get_passwd) { /* TODO(gengjiawen): Fix test on QEMU. */ @@ -35,40 +39,44 @@ TEST_IMPL(get_passwd) { /* Test the normal case */ r = uv_os_get_passwd(&pwd); - ASSERT(r == 0); + ASSERT_OK(r); len = strlen(pwd.username); - ASSERT(len > 0); + ASSERT_GT(len, 0); #ifdef _WIN32 ASSERT_NULL(pwd.shell); #else len = strlen(pwd.shell); # ifndef __PASE__ - ASSERT(len > 0); + ASSERT_GT(len, 0); # endif #endif len = strlen(pwd.homedir); - ASSERT(len > 0); + ASSERT_GT(len, 0); #ifdef _WIN32 if (len == 3 && pwd.homedir[1] == ':') - ASSERT(pwd.homedir[2] == '\\'); + ASSERT_EQ(pwd.homedir[2], '\\'); else - ASSERT(pwd.homedir[len - 1] != '\\'); + ASSERT_NE(pwd.homedir[len - 1], '\\'); #else if (len == 1) - ASSERT(pwd.homedir[0] == '/'); + ASSERT_EQ(pwd.homedir[0], '/'); else - ASSERT(pwd.homedir[len - 1] != '/'); + ASSERT_NE(pwd.homedir[len - 1], '/'); #endif #ifdef _WIN32 - ASSERT(pwd.uid == -1); - ASSERT(pwd.gid == -1); + ASSERT_EQ(pwd.uid, (unsigned)-1); + ASSERT_EQ(pwd.gid, (unsigned)-1); #else - ASSERT(pwd.uid >= 0); - ASSERT(pwd.gid >= 0); + ASSERT_NE(pwd.uid, (unsigned)-1); + ASSERT_NE(pwd.gid, (unsigned)-1); + ASSERT_EQ(pwd.uid, geteuid()); + if (pwd.uid != 0 && pwd.gid != getgid()) + /* This will be likely true, as only root could have changed it. */ + ASSERT_EQ(pwd.gid, getegid()); #endif /* Test uv_os_free_passwd() */ @@ -87,7 +95,117 @@ TEST_IMPL(get_passwd) { /* Test invalid input */ r = uv_os_get_passwd(NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); + + return 0; +} + + +TEST_IMPL(get_passwd2) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + + uv_passwd_t pwd; + uv_passwd_t pwd2; + size_t len; + int r; + + /* Test the normal case */ + r = uv_os_get_passwd(&pwd); + ASSERT_OK(r); + + r = uv_os_get_passwd2(&pwd2, pwd.uid); + +#ifdef _WIN32 + ASSERT_EQ(r, UV_ENOTSUP); + (void) &len; + +#else + ASSERT_OK(r); + ASSERT_EQ(pwd.uid, pwd2.uid); + ASSERT_STR_EQ(pwd.username, pwd2.username); + ASSERT_STR_EQ(pwd.shell, pwd2.shell); + ASSERT_STR_EQ(pwd.homedir, pwd2.homedir); + uv_os_free_passwd(&pwd2); + + r = uv_os_get_passwd2(&pwd2, 0); + ASSERT_OK(r); + + len = strlen(pwd2.username); + ASSERT_GT(len, 0); +#if defined(__PASE__) + // uid 0 is qsecofr on IBM i + ASSERT_STR_EQ(pwd2.username, "qsecofr"); +#else + ASSERT_STR_EQ(pwd2.username, "root"); +#endif + len = strlen(pwd2.homedir); +# ifndef __PASE__ + ASSERT_GT(len, 0); +#endif + len = strlen(pwd2.shell); +# ifndef __PASE__ + ASSERT_GT(len, 0); +# endif + + uv_os_free_passwd(&pwd2); +#endif + + uv_os_free_passwd(&pwd); + + /* Test invalid input */ + r = uv_os_get_passwd2(NULL, pwd.uid); +#ifdef _WIN32 + ASSERT_EQ(r, UV_ENOTSUP); +#else + ASSERT_EQ(r, UV_EINVAL); +#endif + + return 0; +} + + +TEST_IMPL(get_group) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + + uv_passwd_t pwd; + uv_group_t grp; + size_t len; + int r; + + r = uv_os_get_passwd(&pwd); + ASSERT_OK(r); + + r = uv_os_get_group(&grp, pwd.gid); + +#ifdef _WIN32 + ASSERT_EQ(r, UV_ENOTSUP); + (void) &len; + +#else + ASSERT_OK(r); + ASSERT_EQ(pwd.gid, grp.gid); + + len = strlen(grp.groupname); + ASSERT_GT(len, 0); + + uv_os_free_group(&grp); +#endif + + uv_os_free_passwd(&pwd); + + /* Test invalid input */ + r = uv_os_get_group(NULL, pwd.gid); +#ifdef _WIN32 + ASSERT_EQ(r, UV_ENOTSUP); +#else + ASSERT_EQ(r, UV_EINVAL); +#endif return 0; } diff --git a/test/test-getaddrinfo.c b/test/test-getaddrinfo.c index d0b6a505016..76137f06ca0 100644 --- a/test/test-getaddrinfo.c +++ b/test/test-getaddrinfo.c @@ -40,8 +40,8 @@ static void getaddrinfo_fail_cb(uv_getaddrinfo_t* req, int status, struct addrinfo* res) { - ASSERT(fail_cb_called == 0); - ASSERT(status < 0); + ASSERT_OK(fail_cb_called); + ASSERT_LT(status, 0); ASSERT_NULL(res); uv_freeaddrinfo(res); /* Should not crash. */ fail_cb_called++; @@ -51,7 +51,7 @@ static void getaddrinfo_fail_cb(uv_getaddrinfo_t* req, static void getaddrinfo_basic_cb(uv_getaddrinfo_t* handle, int status, struct addrinfo* res) { - ASSERT(handle == getaddrinfo_handle); + ASSERT_PTR_EQ(handle, getaddrinfo_handle); getaddrinfo_cbs++; free(handle); uv_freeaddrinfo(res); @@ -66,7 +66,7 @@ static void getaddrinfo_cuncurrent_cb(uv_getaddrinfo_t* handle, for (i = 0; i < CONCURRENT_COUNT; i++) { if (&getaddrinfo_handles[i] == handle) { - ASSERT(i == *data); + ASSERT_EQ(i, *data); callback_counts[i]++; break; @@ -89,24 +89,24 @@ TEST_IMPL(getaddrinfo_fail) { uv_getaddrinfo_t req; - ASSERT(UV_EINVAL == uv_getaddrinfo(uv_default_loop(), - &req, - (uv_getaddrinfo_cb) abort, - NULL, - NULL, - NULL)); + ASSERT_EQ(UV_EINVAL, uv_getaddrinfo(uv_default_loop(), + &req, + (uv_getaddrinfo_cb) abort, + NULL, + NULL, + NULL)); /* Use a FQDN by ending in a period */ - ASSERT(0 == uv_getaddrinfo(uv_default_loop(), - &req, - getaddrinfo_fail_cb, - "example.invalid.", - NULL, - NULL)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(fail_cb_called == 1); - - MAKE_VALGRIND_HAPPY(); + ASSERT_OK(uv_getaddrinfo(uv_default_loop(), + &req, + getaddrinfo_fail_cb, + "example.invalid.", + NULL, + NULL)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_EQ(1, fail_cb_called); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -119,15 +119,15 @@ TEST_IMPL(getaddrinfo_fail_sync) { uv_getaddrinfo_t req; /* Use a FQDN by ending in a period */ - ASSERT(0 > uv_getaddrinfo(uv_default_loop(), - &req, - NULL, - "example.invalid.", - NULL, - NULL)); + ASSERT_GT(0, uv_getaddrinfo(uv_default_loop(), + &req, + NULL, + "example.invalid.", + NULL, + NULL)); uv_freeaddrinfo(req.addrinfo); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -147,13 +147,13 @@ TEST_IMPL(getaddrinfo_basic) { name, NULL, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(getaddrinfo_cbs == 1); + ASSERT_EQ(1, getaddrinfo_cbs); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -165,15 +165,15 @@ TEST_IMPL(getaddrinfo_basic_sync) { #endif uv_getaddrinfo_t req; - ASSERT(0 == uv_getaddrinfo(uv_default_loop(), - &req, - NULL, - name, - NULL, - NULL)); + ASSERT_OK(uv_getaddrinfo(uv_default_loop(), + &req, + NULL, + name, + NULL, + NULL)); uv_freeaddrinfo(req.addrinfo); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -201,15 +201,15 @@ TEST_IMPL(getaddrinfo_concurrent) { name, NULL, NULL); - ASSERT(r == 0); + ASSERT_OK(r); } uv_run(uv_default_loop(), UV_RUN_DEFAULT); for (i = 0; i < CONCURRENT_COUNT; i++) { - ASSERT(callback_counts[i] == 1); + ASSERT_EQ(1, callback_counts[i]); } - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-gethostname.c b/test/test-gethostname.c index 1a9816d43c6..cc15a8253eb 100644 --- a/test/test-gethostname.c +++ b/test/test-gethostname.c @@ -32,27 +32,27 @@ TEST_IMPL(gethostname) { /* Reject invalid inputs */ size = 1; r = uv_os_gethostname(NULL, &size); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_os_gethostname(buf, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); size = 0; r = uv_os_gethostname(buf, &size); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); /* Return UV_ENOBUFS if the buffer cannot hold the hostname */ enobufs_size = 1; buf[0] = '\0'; r = uv_os_gethostname(buf, &enobufs_size); - ASSERT(r == UV_ENOBUFS); - ASSERT(buf[0] == '\0'); - ASSERT(enobufs_size > 1); + ASSERT_EQ(r, UV_ENOBUFS); + ASSERT_EQ(buf[0], '\0'); + ASSERT_GT(enobufs_size, 1); /* Successfully get the hostname */ size = UV_MAXHOSTNAMESIZE; r = uv_os_gethostname(buf, &size); - ASSERT(r == 0); + ASSERT_OK(r); ASSERT(size > 0 && size == strlen(buf)); - ASSERT(size + 1 == enobufs_size); + ASSERT_EQ(size + 1, enobufs_size); return 0; } diff --git a/test/test-getnameinfo.c b/test/test-getnameinfo.c index 2bfedd3a39b..415e48a4f96 100644 --- a/test/test-getnameinfo.c +++ b/test/test-getnameinfo.c @@ -39,7 +39,7 @@ static void getnameinfo_req(uv_getnameinfo_t* handle, const char* hostname, const char* service) { ASSERT_NOT_NULL(handle); - ASSERT(status == 0); + ASSERT_OK(status); ASSERT_NOT_NULL(hostname); ASSERT_NOT_NULL(service); } @@ -54,18 +54,18 @@ TEST_IMPL(getnameinfo_basic_ip4) { int r; r = uv_ip4_addr(address_ip4, port, &addr4); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_getnameinfo(uv_default_loop(), &req, &getnameinfo_req, (const struct sockaddr*)&addr4, 0); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -76,17 +76,17 @@ TEST_IMPL(getnameinfo_basic_ip4_sync) { RETURN_SKIP("Test does not currently work in QEMU"); #endif - ASSERT(0 == uv_ip4_addr(address_ip4, port, &addr4)); + ASSERT_OK(uv_ip4_addr(address_ip4, port, &addr4)); - ASSERT(0 == uv_getnameinfo(uv_default_loop(), - &req, - NULL, - (const struct sockaddr*)&addr4, - 0)); - ASSERT(req.host[0] != '\0'); - ASSERT(req.service[0] != '\0'); + ASSERT_OK(uv_getnameinfo(uv_default_loop(), + &req, + NULL, + (const struct sockaddr*)&addr4, + 0)); + ASSERT_NE(req.host[0], '\0'); + ASSERT_NE(req.service[0], '\0'); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -100,17 +100,17 @@ TEST_IMPL(getnameinfo_basic_ip6) { int r; r = uv_ip6_addr(address_ip6, port, &addr6); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_getnameinfo(uv_default_loop(), &req, &getnameinfo_req, (const struct sockaddr*)&addr6, 0); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-getsockname.c b/test/test-getsockname.c index 7c77fcb0a9b..7bc0ba2e9f2 100644 --- a/test/test-getsockname.c +++ b/test/test-getsockname.c @@ -30,8 +30,9 @@ static const int server_port = TEST_PORT; /* Will be updated right after making the uv_connect_call */ static int connect_port = -1; -static int getsocknamecount = 0; +static int getsocknamecount_tcp = 0; static int getpeernamecount = 0; +static int getsocknamecount_udp = 0; static uv_loop_t* loop; static uv_tcp_t tcp; @@ -72,7 +73,7 @@ static void after_read(uv_stream_t* handle, req = (uv_shutdown_t*) malloc(sizeof *req); r = uv_shutdown(req, handle, after_shutdown); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -83,22 +84,22 @@ static void check_sockname(struct sockaddr* addr, const char* compare_ip, char check_ip[17]; int r; - ASSERT(0 == uv_ip4_addr(compare_ip, compare_port, &compare_addr)); + ASSERT_OK(uv_ip4_addr(compare_ip, compare_port, &compare_addr)); /* Both addresses should be ipv4 */ - ASSERT(check_addr.sin_family == AF_INET); - ASSERT(compare_addr.sin_family == AF_INET); + ASSERT_EQ(check_addr.sin_family, AF_INET); + ASSERT_EQ(compare_addr.sin_family, AF_INET); /* Check if the ip matches */ - ASSERT(memcmp(&check_addr.sin_addr, - &compare_addr.sin_addr, - sizeof compare_addr.sin_addr) == 0); + ASSERT_OK(memcmp(&check_addr.sin_addr, + &compare_addr.sin_addr, + sizeof compare_addr.sin_addr)); /* Check if the port matches. If port == 0 anything goes. */ ASSERT(compare_port == 0 || check_addr.sin_port == compare_addr.sin_port); r = uv_ip4_name(&check_addr, (char*) check_ip, sizeof check_ip); - ASSERT(r == 0); + ASSERT_OK(r); printf("%s: %s:%d\n", context, check_ip, ntohs(check_addr.sin_port)); } @@ -113,34 +114,34 @@ static void on_connection(uv_stream_t* server, int status) { if (status != 0) { fprintf(stderr, "Connect error %s\n", uv_err_name(status)); } - ASSERT(status == 0); + ASSERT_OK(status); handle = malloc(sizeof(*handle)); ASSERT_NOT_NULL(handle); r = uv_tcp_init(loop, handle); - ASSERT(r == 0); + ASSERT_OK(r); /* associate server with stream */ handle->data = server; r = uv_accept(server, (uv_stream_t*)handle); - ASSERT(r == 0); + ASSERT_OK(r); namelen = sizeof sockname; r = uv_tcp_getsockname(handle, &sockname, &namelen); - ASSERT(r == 0); + ASSERT_OK(r); check_sockname(&sockname, "127.0.0.1", server_port, "accepted socket"); - getsocknamecount++; + getsocknamecount_tcp++; namelen = sizeof peername; r = uv_tcp_getpeername(handle, &peername, &namelen); - ASSERT(r == 0); + ASSERT_OK(r); check_sockname(&peername, "127.0.0.1", connect_port, "accepted socket peer"); getpeernamecount++; r = uv_read_start((uv_stream_t*)handle, alloc, after_read); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -148,17 +149,17 @@ static void on_connect(uv_connect_t* req, int status) { struct sockaddr sockname, peername; int r, namelen; - ASSERT(status == 0); + ASSERT_OK(status); namelen = sizeof sockname; r = uv_tcp_getsockname((uv_tcp_t*) req->handle, &sockname, &namelen); - ASSERT(r == 0); + ASSERT_OK(r); check_sockname(&sockname, "127.0.0.1", 0, "connected socket"); - getsocknamecount++; + getsocknamecount_tcp++; namelen = sizeof peername; r = uv_tcp_getpeername((uv_tcp_t*) req->handle, &peername, &namelen); - ASSERT(r == 0); + ASSERT_OK(r); check_sockname(&peername, "127.0.0.1", server_port, "connected socket peer"); getpeernamecount++; @@ -172,7 +173,7 @@ static int tcp_listener(void) { int namelen; int r; - ASSERT(0 == uv_ip4_addr("0.0.0.0", server_port, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", server_port, &addr)); r = uv_tcp_init(loop, &tcpServer); if (r) { @@ -195,13 +196,13 @@ static int tcp_listener(void) { memset(&sockname, -1, sizeof sockname); namelen = sizeof sockname; r = uv_tcp_getsockname(&tcpServer, &sockname, &namelen); - ASSERT(r == 0); + ASSERT_OK(r); check_sockname(&sockname, "0.0.0.0", server_port, "server socket"); - getsocknamecount++; + getsocknamecount_tcp++; namelen = sizeof sockname; r = uv_tcp_getpeername(&tcpServer, &peername, &namelen); - ASSERT(r == UV_ENOTCONN); + ASSERT_EQ(r, UV_ENOTCONN); getpeernamecount++; return 0; @@ -213,7 +214,7 @@ static void tcp_connector(void) { struct sockaddr sockname; int r, namelen; - ASSERT(0 == uv_ip4_addr("127.0.0.1", server_port, &server_addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", server_port, &server_addr)); r = uv_tcp_init(loop, &tcp); tcp.data = &connect_req; @@ -229,9 +230,9 @@ static void tcp_connector(void) { namelen = sizeof sockname; r = uv_tcp_getsockname(&tcp, &sockname, &namelen); ASSERT(!r); - ASSERT(sockname.sa_family == AF_INET); + ASSERT_EQ(sockname.sa_family, AF_INET); connect_port = ntohs(((struct sockaddr_in*) &sockname)->sin_port); - ASSERT(connect_port > 0); + ASSERT_GT(connect_port, 0); } @@ -244,7 +245,7 @@ static void udp_recv(uv_udp_t* handle, int namelen; int r; - ASSERT(nread >= 0); + ASSERT_GE(nread, 0); free(buf->base); if (nread == 0) { @@ -254,9 +255,9 @@ static void udp_recv(uv_udp_t* handle, memset(&sockname, -1, sizeof sockname); namelen = sizeof(sockname); r = uv_udp_getsockname(&udp, &sockname, &namelen); - ASSERT(r == 0); + ASSERT_OK(r); check_sockname(&sockname, "0.0.0.0", 0, "udp receiving socket"); - getsocknamecount++; + getsocknamecount_udp++; uv_close((uv_handle_t*) &udp, NULL); uv_close((uv_handle_t*) handle, NULL); @@ -274,7 +275,7 @@ static int udp_listener(void) { int namelen; int r; - ASSERT(0 == uv_ip4_addr("0.0.0.0", server_port, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", server_port, &addr)); r = uv_udp_init(loop, &udpServer); if (r) { @@ -291,12 +292,12 @@ static int udp_listener(void) { memset(&sockname, -1, sizeof sockname); namelen = sizeof sockname; r = uv_udp_getsockname(&udpServer, &sockname, &namelen); - ASSERT(r == 0); + ASSERT_OK(r); check_sockname(&sockname, "0.0.0.0", server_port, "udp listener socket"); - getsocknamecount++; + getsocknamecount_udp++; r = uv_udp_recv_start(&udpServer, alloc, udp_recv); - ASSERT(r == 0); + ASSERT_OK(r); return 0; } @@ -311,7 +312,7 @@ static void udp_sender(void) { ASSERT(!r); buf = uv_buf_init("PING", 4); - ASSERT(0 == uv_ip4_addr("127.0.0.1", server_port, &server_addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", server_port, &server_addr)); r = uv_udp_send(&send_req, &udp, @@ -333,10 +334,10 @@ TEST_IMPL(getsockname_tcp) { uv_run(loop, UV_RUN_DEFAULT); - ASSERT(getsocknamecount == 3); - ASSERT(getpeernamecount == 3); + ASSERT_EQ(3, getsocknamecount_tcp); + ASSERT_EQ(3, getpeernamecount); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -351,11 +352,11 @@ TEST_IMPL(getsockname_udp) { uv_run(loop, UV_RUN_DEFAULT); - ASSERT(getsocknamecount == 2); + ASSERT_EQ(2, getsocknamecount_udp); - ASSERT(udp.send_queue_size == 0); - ASSERT(udpServer.send_queue_size == 0); + ASSERT_OK(udp.send_queue_size); + ASSERT_OK(udpServer.send_queue_size); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-getters-setters.c b/test/test-getters-setters.c index 2a37122df3f..3b9e89e1fe0 100644 --- a/test/test-getters-setters.c +++ b/test/test-getters-setters.c @@ -24,15 +24,19 @@ #include #include +#ifdef _WIN32 +# define S_IFDIR _S_IFDIR +#endif + int cookie1; int cookie2; int cookie3; TEST_IMPL(handle_type_name) { - ASSERT(strcmp(uv_handle_type_name(UV_NAMED_PIPE), "pipe") == 0); - ASSERT(strcmp(uv_handle_type_name(UV_UDP), "udp") == 0); - ASSERT(strcmp(uv_handle_type_name(UV_FILE), "file") == 0); + ASSERT_OK(strcmp(uv_handle_type_name(UV_NAMED_PIPE), "pipe")); + ASSERT_OK(strcmp(uv_handle_type_name(UV_UDP), "udp")); + ASSERT_OK(strcmp(uv_handle_type_name(UV_FILE), "file")); ASSERT_NULL(uv_handle_type_name(UV_HANDLE_TYPE_MAX)); ASSERT_NULL(uv_handle_type_name(UV_HANDLE_TYPE_MAX + 1)); ASSERT_NULL(uv_handle_type_name(UV_UNKNOWN_HANDLE)); @@ -41,9 +45,9 @@ TEST_IMPL(handle_type_name) { TEST_IMPL(req_type_name) { - ASSERT(strcmp(uv_req_type_name(UV_REQ), "req") == 0); - ASSERT(strcmp(uv_req_type_name(UV_UDP_SEND), "udp_send") == 0); - ASSERT(strcmp(uv_req_type_name(UV_WORK), "work") == 0); + ASSERT_OK(strcmp(uv_req_type_name(UV_REQ), "req")); + ASSERT_OK(strcmp(uv_req_type_name(UV_UDP_SEND), "udp_send")); + ASSERT_OK(strcmp(uv_req_type_name(UV_WORK), "work")); ASSERT_NULL(uv_req_type_name(UV_REQ_TYPE_MAX)); ASSERT_NULL(uv_req_type_name(UV_REQ_TYPE_MAX + 1)); ASSERT_NULL(uv_req_type_name(UV_UNKNOWN_REQ)); @@ -60,47 +64,48 @@ TEST_IMPL(getters_setters) { loop = malloc(uv_loop_size()); ASSERT_NOT_NULL(loop); r = uv_loop_init(loop); - ASSERT(r == 0); + ASSERT_OK(r); uv_loop_set_data(loop, &cookie1); - ASSERT(loop->data == &cookie1); - ASSERT(uv_loop_get_data(loop) == &cookie1); + ASSERT_PTR_EQ(loop->data, &cookie1); + ASSERT_PTR_EQ(uv_loop_get_data(loop), &cookie1); pipe = malloc(uv_handle_size(UV_NAMED_PIPE)); r = uv_pipe_init(loop, pipe, 0); - ASSERT(uv_handle_get_type((uv_handle_t*)pipe) == UV_NAMED_PIPE); + ASSERT_OK(r); + ASSERT_EQ(uv_handle_get_type((uv_handle_t*)pipe), UV_NAMED_PIPE); - ASSERT(uv_handle_get_loop((uv_handle_t*)pipe) == loop); + ASSERT_PTR_EQ(uv_handle_get_loop((uv_handle_t*)pipe), loop); pipe->data = &cookie2; - ASSERT(uv_handle_get_data((uv_handle_t*)pipe) == &cookie2); + ASSERT_PTR_EQ(uv_handle_get_data((uv_handle_t*)pipe), &cookie2); uv_handle_set_data((uv_handle_t*)pipe, &cookie1); - ASSERT(uv_handle_get_data((uv_handle_t*)pipe) == &cookie1); - ASSERT(pipe->data == &cookie1); + ASSERT_PTR_EQ(uv_handle_get_data((uv_handle_t*)pipe), &cookie1); + ASSERT_PTR_EQ(pipe->data, &cookie1); - ASSERT(uv_stream_get_write_queue_size((uv_stream_t*)pipe) == 0); + ASSERT_OK(uv_stream_get_write_queue_size((uv_stream_t*)pipe)); pipe->write_queue_size++; - ASSERT(uv_stream_get_write_queue_size((uv_stream_t*)pipe) == 1); + ASSERT_EQ(1, uv_stream_get_write_queue_size((uv_stream_t*)pipe)); pipe->write_queue_size--; uv_close((uv_handle_t*)pipe, NULL); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); fs = malloc(uv_req_size(UV_FS)); uv_fs_stat(loop, fs, ".", NULL); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(uv_fs_get_type(fs) == UV_FS_STAT); - ASSERT(uv_fs_get_result(fs) == 0); - ASSERT(uv_fs_get_ptr(fs) == uv_fs_get_statbuf(fs)); + ASSERT_EQ(uv_fs_get_type(fs), UV_FS_STAT); + ASSERT_OK(uv_fs_get_result(fs)); + ASSERT_PTR_EQ(uv_fs_get_ptr(fs), uv_fs_get_statbuf(fs)); ASSERT(uv_fs_get_statbuf(fs)->st_mode & S_IFDIR); - ASSERT(strcmp(uv_fs_get_path(fs), ".") == 0); + ASSERT_OK(strcmp(uv_fs_get_path(fs), ".")); uv_fs_req_cleanup(fs); r = uv_loop_close(loop); - ASSERT(r == 0); + ASSERT_OK(r); free(pipe); free(fs); diff --git a/test/test-gettimeofday.c b/test/test-gettimeofday.c index 4ebc11f93ed..7d9012815c4 100644 --- a/test/test-gettimeofday.c +++ b/test/test-gettimeofday.c @@ -28,12 +28,12 @@ TEST_IMPL(gettimeofday) { tv.tv_sec = 0; r = uv_gettimeofday(&tv); - ASSERT(r == 0); - ASSERT(tv.tv_sec != 0); + ASSERT_OK(r); + ASSERT_NE(0, tv.tv_sec); /* Test invalid input. */ r = uv_gettimeofday(NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); return 0; } diff --git a/test/test-handle-fileno.c b/test/test-handle-fileno.c index 8a093e2ea46..be53152ce84 100644 --- a/test/test-handle-fileno.c +++ b/test/test-handle-fileno.c @@ -56,49 +56,49 @@ TEST_IMPL(handle_fileno) { uv_loop_t* loop; loop = uv_default_loop(); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_idle_init(loop, &idle); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fileno((uv_handle_t*) &idle, &fd); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_close((uv_handle_t*) &idle, NULL); r = uv_tcp_init(loop, &tcp); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fileno((uv_handle_t*) &tcp, &fd); - ASSERT(r == UV_EBADF); + ASSERT_EQ(r, UV_EBADF); r = uv_tcp_bind(&tcp, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fileno((uv_handle_t*) &tcp, &fd); - ASSERT(r == 0); + ASSERT_OK(r); uv_close((uv_handle_t*) &tcp, NULL); r = uv_fileno((uv_handle_t*) &tcp, &fd); - ASSERT(r == UV_EBADF); + ASSERT_EQ(r, UV_EBADF); r = uv_udp_init(loop, &udp); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fileno((uv_handle_t*) &udp, &fd); - ASSERT(r == UV_EBADF); + ASSERT_EQ(r, UV_EBADF); r = uv_udp_bind(&udp, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fileno((uv_handle_t*) &udp, &fd); - ASSERT(r == 0); + ASSERT_OK(r); uv_close((uv_handle_t*) &udp, NULL); r = uv_fileno((uv_handle_t*) &udp, &fd); - ASSERT(r == UV_EBADF); + ASSERT_EQ(r, UV_EBADF); r = uv_pipe_init(loop, &pipe, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fileno((uv_handle_t*) &pipe, &fd); - ASSERT(r == UV_EBADF); + ASSERT_EQ(r, UV_EBADF); r = uv_pipe_bind(&pipe, TEST_PIPENAME); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fileno((uv_handle_t*) &pipe, &fd); - ASSERT(r == 0); + ASSERT_OK(r); uv_close((uv_handle_t*) &pipe, NULL); r = uv_fileno((uv_handle_t*) &pipe, &fd); - ASSERT(r == UV_EBADF); + ASSERT_EQ(r, UV_EBADF); tty_fd = get_tty_fd(); if (tty_fd < 0) { @@ -106,20 +106,20 @@ TEST_IMPL(handle_fileno) { fflush(stderr); } else { r = uv_tty_init(loop, &tty, tty_fd, 0); - ASSERT(r == 0); + ASSERT_OK(r); ASSERT(uv_is_readable((uv_stream_t*) &tty)); ASSERT(!uv_is_writable((uv_stream_t*) &tty)); r = uv_fileno((uv_handle_t*) &tty, &fd); - ASSERT(r == 0); + ASSERT_OK(r); uv_close((uv_handle_t*) &tty, NULL); r = uv_fileno((uv_handle_t*) &tty, &fd); - ASSERT(r == UV_EBADF); + ASSERT_EQ(r, UV_EBADF); ASSERT(!uv_is_readable((uv_stream_t*) &tty)); ASSERT(!uv_is_writable((uv_stream_t*) &tty)); } uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-homedir.c b/test/test-homedir.c index 508351f72c0..e335540d106 100644 --- a/test/test-homedir.c +++ b/test/test-homedir.c @@ -34,39 +34,48 @@ TEST_IMPL(homedir) { /* Test the normal case */ len = sizeof homedir; homedir[0] = '\0'; - ASSERT(strlen(homedir) == 0); + ASSERT_OK(strlen(homedir)); r = uv_os_homedir(homedir, &len); - ASSERT(r == 0); - ASSERT(strlen(homedir) == len); - ASSERT(len > 0); - ASSERT(homedir[len] == '\0'); + ASSERT_OK(r); + ASSERT_EQ(strlen(homedir), len); + ASSERT_GT(len, 0); + ASSERT_EQ(homedir[len], '\0'); #ifdef _WIN32 if (len == 3 && homedir[1] == ':') - ASSERT(homedir[2] == '\\'); + ASSERT_EQ(homedir[2], '\\'); else - ASSERT(homedir[len - 1] != '\\'); + ASSERT_NE(homedir[len - 1], '\\'); #else if (len == 1) - ASSERT(homedir[0] == '/'); + ASSERT_EQ(homedir[0], '/'); else - ASSERT(homedir[len - 1] != '/'); + ASSERT_NE(homedir[len - 1], '/'); #endif /* Test the case where the buffer is too small */ len = SMALLPATH; r = uv_os_homedir(homedir, &len); - ASSERT(r == UV_ENOBUFS); - ASSERT(len > SMALLPATH); + ASSERT_EQ(r, UV_ENOBUFS); + ASSERT_GT(len, SMALLPATH); /* Test invalid inputs */ r = uv_os_homedir(NULL, &len); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_os_homedir(homedir, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); len = 0; r = uv_os_homedir(homedir, &len); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); + +#ifdef _WIN32 + /* Test empty environment variable */ + r = uv_os_setenv("USERPROFILE", ""); + ASSERT_EQ(r, 0); + len = sizeof homedir; + r = uv_os_homedir(homedir, &len); + ASSERT_EQ(r, UV_ENOENT); +#endif return 0; } diff --git a/test/test-hrtime.c b/test/test-hrtime.c index 9d461d9623d..c0b88c6757d 100644 --- a/test/test-hrtime.c +++ b/test/test-hrtime.c @@ -45,8 +45,21 @@ TEST_IMPL(hrtime) { * that the difference between the two hrtime values has a reasonable * lower bound. */ - ASSERT(diff > (uint64_t) 25 * NANOSEC / MILLISEC); + ASSERT_UINT64_GT(diff, (uint64_t) 25 * NANOSEC / MILLISEC); --i; } return 0; } + + +TEST_IMPL(clock_gettime) { + uv_timespec64_t t; + + ASSERT_EQ(UV_EINVAL, uv_clock_gettime(1337, &t)); + ASSERT_EQ(UV_EFAULT, uv_clock_gettime(1337, NULL)); + ASSERT_OK(uv_clock_gettime(UV_CLOCK_MONOTONIC, &t)); + ASSERT_OK(uv_clock_gettime(UV_CLOCK_REALTIME, &t)); + ASSERT_GT(1682500000000ll, t.tv_sec); /* 2023-04-26T09:06:40.000Z */ + + return 0; +} diff --git a/test/test-idle.c b/test/test-idle.c index f49d1964827..069e3483580 100644 --- a/test/test-idle.c +++ b/test/test-idle.c @@ -39,7 +39,7 @@ static void close_cb(uv_handle_t* handle) { static void timer_cb(uv_timer_t* handle) { - ASSERT(handle == &timer_handle); + ASSERT_PTR_EQ(handle, &timer_handle); uv_close((uv_handle_t*) &idle_handle, close_cb); uv_close((uv_handle_t*) &check_handle, close_cb); @@ -52,7 +52,7 @@ static void timer_cb(uv_timer_t* handle) { static void idle_cb(uv_idle_t* handle) { - ASSERT(handle == &idle_handle); + ASSERT_PTR_EQ(handle, &idle_handle); idle_cb_called++; fprintf(stderr, "idle_cb %d\n", idle_cb_called); @@ -61,7 +61,7 @@ static void idle_cb(uv_idle_t* handle) { static void check_cb(uv_check_t* handle) { - ASSERT(handle == &check_handle); + ASSERT_PTR_EQ(handle, &check_handle); check_cb_called++; fprintf(stderr, "check_cb %d\n", check_cb_called); @@ -73,27 +73,53 @@ TEST_IMPL(idle_starvation) { int r; r = uv_idle_init(uv_default_loop(), &idle_handle); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_idle_start(&idle_handle, idle_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_check_init(uv_default_loop(), &check_handle); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_check_start(&check_handle, check_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_init(uv_default_loop(), &timer_handle); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&timer_handle, timer_cb, 50, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(idle_cb_called > 0); - ASSERT(timer_cb_called == 1); - ASSERT(close_cb_called == 3); + ASSERT_GT(idle_cb_called, 0); + ASSERT_EQ(1, timer_cb_called); + ASSERT_EQ(3, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} + + +static void idle_stop(uv_idle_t* handle) { + uv_idle_stop(handle); +} + + +TEST_IMPL(idle_check) { + ASSERT_OK(uv_idle_init(uv_default_loop(), &idle_handle)); + ASSERT_OK(uv_idle_start(&idle_handle, idle_stop)); + + ASSERT_OK(uv_check_init(uv_default_loop(), &check_handle)); + ASSERT_OK(uv_check_start(&check_handle, check_cb)); + + ASSERT_EQ(1, uv_run(uv_default_loop(), UV_RUN_ONCE)); + ASSERT_EQ(1, check_cb_called); + + ASSERT_OK(close_cb_called); + uv_close((uv_handle_t*) &idle_handle, close_cb); + uv_close((uv_handle_t*) &check_handle, close_cb); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_ONCE)); + ASSERT_EQ(2, close_cb_called); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-idna.c b/test/test-idna.c index f4fad9653df..46df9f3c581 100644 --- a/test/test-idna.c +++ b/test/test-idna.c @@ -20,6 +20,7 @@ */ #include "task.h" +#define uv__malloc malloc #include "../src/idna.c" #include @@ -31,66 +32,66 @@ TEST_IMPL(utf8_decode1) { /* ASCII. */ p = b; snprintf(b, sizeof(b), "%c\x7F", 0x00); - ASSERT(0 == uv__utf8_decode1(&p, b + sizeof(b))); - ASSERT(p == b + 1); - ASSERT(127 == uv__utf8_decode1(&p, b + sizeof(b))); - ASSERT(p == b + 2); + ASSERT_OK(uv__utf8_decode1(&p, b + sizeof(b))); + ASSERT_PTR_EQ(p, b + 1); + ASSERT_EQ(127, uv__utf8_decode1(&p, b + sizeof(b))); + ASSERT_PTR_EQ(p, b + 2); /* Two-byte sequences. */ p = b; - snprintf(b, sizeof(b), "\xC2\x80\xDF\xBF"); - ASSERT(128 == uv__utf8_decode1(&p, b + sizeof(b))); - ASSERT(p == b + 2); - ASSERT(0x7FF == uv__utf8_decode1(&p, b + sizeof(b))); - ASSERT(p == b + 4); + snprintf(b, sizeof(b), "%s", "\xC2\x80\xDF\xBF"); + ASSERT_EQ(128, uv__utf8_decode1(&p, b + sizeof(b))); + ASSERT_PTR_EQ(p, b + 2); + ASSERT_EQ(0x7FF, uv__utf8_decode1(&p, b + sizeof(b))); + ASSERT_PTR_EQ(p, b + 4); /* Three-byte sequences. */ p = b; - snprintf(b, sizeof(b), "\xE0\xA0\x80\xEF\xBF\xBF"); - ASSERT(0x800 == uv__utf8_decode1(&p, b + sizeof(b))); - ASSERT(p == b + 3); - ASSERT(0xFFFF == uv__utf8_decode1(&p, b + sizeof(b))); - ASSERT(p == b + 6); + snprintf(b, sizeof(b), "%s", "\xE0\xA0\x80\xEF\xBF\xBF"); + ASSERT_EQ(0x800, uv__utf8_decode1(&p, b + sizeof(b))); + ASSERT_PTR_EQ(p, b + 3); + ASSERT_EQ(0xFFFF, uv__utf8_decode1(&p, b + sizeof(b))); + ASSERT_PTR_EQ(p, b + 6); /* Four-byte sequences. */ p = b; - snprintf(b, sizeof(b), "\xF0\x90\x80\x80\xF4\x8F\xBF\xBF"); - ASSERT(0x10000 == uv__utf8_decode1(&p, b + sizeof(b))); - ASSERT(p == b + 4); - ASSERT(0x10FFFF == uv__utf8_decode1(&p, b + sizeof(b))); - ASSERT(p == b + 8); + snprintf(b, sizeof(b), "%s", "\xF0\x90\x80\x80\xF4\x8F\xBF\xBF"); + ASSERT_EQ(0x10000, uv__utf8_decode1(&p, b + sizeof(b))); + ASSERT_PTR_EQ(p, b + 4); + ASSERT_EQ(0x10FFFF, uv__utf8_decode1(&p, b + sizeof(b))); + ASSERT_PTR_EQ(p, b + 8); /* Four-byte sequences > U+10FFFF; disallowed. */ p = b; - snprintf(b, sizeof(b), "\xF4\x90\xC0\xC0\xF7\xBF\xBF\xBF"); - ASSERT((unsigned) -1 == uv__utf8_decode1(&p, b + sizeof(b))); - ASSERT(p == b + 4); - ASSERT((unsigned) -1 == uv__utf8_decode1(&p, b + sizeof(b))); - ASSERT(p == b + 8); + snprintf(b, sizeof(b), "%s", "\xF4\x90\xC0\xC0\xF7\xBF\xBF\xBF"); + ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b))); + ASSERT_PTR_EQ(p, b + 4); + ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b))); + ASSERT_PTR_EQ(p, b + 8); /* Overlong; disallowed. */ p = b; - snprintf(b, sizeof(b), "\xC0\x80\xC1\x80"); - ASSERT((unsigned) -1 == uv__utf8_decode1(&p, b + sizeof(b))); - ASSERT(p == b + 2); - ASSERT((unsigned) -1 == uv__utf8_decode1(&p, b + sizeof(b))); - ASSERT(p == b + 4); + snprintf(b, sizeof(b), "%s", "\xC0\x80\xC1\x80"); + ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b))); + ASSERT_PTR_EQ(p, b + 2); + ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b))); + ASSERT_PTR_EQ(p, b + 4); /* Surrogate pairs; disallowed. */ p = b; - snprintf(b, sizeof(b), "\xED\xA0\x80\xED\xA3\xBF"); - ASSERT((unsigned) -1 == uv__utf8_decode1(&p, b + sizeof(b))); - ASSERT(p == b + 3); - ASSERT((unsigned) -1 == uv__utf8_decode1(&p, b + sizeof(b))); - ASSERT(p == b + 6); + snprintf(b, sizeof(b), "%s", "\xED\xA0\x80\xED\xA3\xBF"); + ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b))); + ASSERT_PTR_EQ(p, b + 3); + ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b))); + ASSERT_PTR_EQ(p, b + 6); /* Simply illegal. */ p = b; - snprintf(b, sizeof(b), "\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF"); + snprintf(b, sizeof(b), "%s", "\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF"); for (i = 1; i <= 8; i++) { - ASSERT((unsigned) -1 == uv__utf8_decode1(&p, b + sizeof(b))); - ASSERT(p == b + i); + ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + sizeof(b))); + ASSERT_PTR_EQ(p, b + i); } return 0; @@ -99,18 +100,23 @@ TEST_IMPL(utf8_decode1) { TEST_IMPL(utf8_decode1_overrun) { const char* p; char b[1]; + char c[1]; /* Single byte. */ p = b; b[0] = 0x7F; ASSERT_EQ(0x7F, uv__utf8_decode1(&p, b + 1)); - ASSERT_EQ(p, b + 1); + ASSERT_PTR_EQ(p, b + 1); /* Multi-byte. */ p = b; b[0] = 0xC0; ASSERT_EQ((unsigned) -1, uv__utf8_decode1(&p, b + 1)); - ASSERT_EQ(p, b + 1); + ASSERT_PTR_EQ(p, b + 1); + + b[0] = 0x7F; + ASSERT_EQ(UV_EINVAL, uv__idna_toascii(b, b + 0, c, c + 1)); + ASSERT_EQ(UV_EINVAL, uv__idna_toascii(b, b + 1, c, c + 1)); return 0; } @@ -122,7 +128,7 @@ TEST_IMPL(utf8_decode1_overrun) { do { \ char d[256] = {0}; \ static const char s[] = "" input ""; \ - ASSERT(err == uv__idna_toascii(s, s + sizeof(s) - 1, d, d + sizeof(d))); \ + ASSERT_EQ(err, uv__idna_toascii(s, s + sizeof(s) - 1, d, d + sizeof(d))); \ } while (0) #define T(input, expected) \ @@ -132,21 +138,21 @@ TEST_IMPL(utf8_decode1_overrun) { char d2[256] = {0}; \ static const char s[] = "" input ""; \ n = uv__idna_toascii(s, s + sizeof(s) - 1, d1, d1 + sizeof(d1)); \ - ASSERT(n == sizeof(expected)); \ - ASSERT(0 == memcmp(d1, expected, n)); \ + ASSERT_EQ(n, sizeof(expected)); \ + ASSERT_OK(memcmp(d1, expected, n)); \ /* Sanity check: encoding twice should not change the output. */ \ n = uv__idna_toascii(d1, d1 + strlen(d1), d2, d2 + sizeof(d2)); \ - ASSERT(n == sizeof(expected)); \ - ASSERT(0 == memcmp(d2, expected, n)); \ - ASSERT(0 == memcmp(d1, d2, sizeof(d2))); \ + ASSERT_EQ(n, sizeof(expected)); \ + ASSERT_OK(memcmp(d2, expected, n)); \ + ASSERT_OK(memcmp(d1, d2, sizeof(d2))); \ } while (0) TEST_IMPL(idna_toascii) { /* Illegal inputs. */ F("\xC0\x80\xC1\x80", UV_EINVAL); /* Overlong UTF-8 sequence. */ F("\xC0\x80\xC1\x80.com", UV_EINVAL); /* Overlong UTF-8 sequence. */ + F("", UV_EINVAL); /* No conversion. */ - T("", ""); T(".", "."); T(".com", ".com"); T("example", "example"); @@ -212,3 +218,15 @@ TEST_IMPL(idna_toascii) { #undef T #endif /* __MVS__ */ + +TEST_IMPL(wtf8) { + static const char input[] = "ᜄȺy𐞲:𞢢𘴇𐀀'¥3̞[ /* close() */ + +static uv_pipe_t p1; +static uv_pipe_t p2; +static uv_idle_t idle_handle; +static int iters; +static int duped_fd; +static int newpipefds[2]; + +static void alloc_buffer(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[32]; + *buf = uv_buf_init(slab, sizeof(slab)); +} + +static void read_data2(uv_stream_t* stream, + ssize_t nread, + const uv_buf_t* buf) { + if (nread < 0) { + ASSERT_EQ(nread, UV_EOF); + ASSERT_OK(close(duped_fd)); + duped_fd = -1; + uv_close((uv_handle_t*) &p2, NULL); + uv_close((uv_handle_t*) &idle_handle, NULL); + } else { + /* If nread == 0 is because of POLLHUP received still from pipefds[0] file + * description which is still referenced in duped_fd. It should not happen + * if close(p1) was called after EPOLL_CTL_DEL. + */ + ASSERT_GT(nread, 0); + } +} + +static void idle_cb(uv_idle_t* handle) { + if (++iters == 1) { + ASSERT_OK(uv_pipe_open(&p2, newpipefds[0])); + ASSERT_OK(uv_read_start((uv_stream_t*) &p2, alloc_buffer, read_data2)); + } else { + ASSERT_OK(uv_idle_stop(handle)); + ASSERT_OK(close(newpipefds[1])); + newpipefds[1] = -1; + } +} + +static void read_data(uv_stream_t* stream, + ssize_t nread, + const uv_buf_t* buf) { + ASSERT_EQ(nread, UV_EOF); + uv_close((uv_handle_t*) stream, NULL); + ASSERT_OK(uv_idle_start(&idle_handle, idle_cb)); +} + +TEST_IMPL(iouring_pollhup) { + uv_loop_t* loop; + int pipefds[2]; + + loop = uv_default_loop(); + ASSERT_OK(uv_pipe_init(loop, &p1, 0)); + ASSERT_OK(uv_pipe_init(loop, &p2, 0)); + ASSERT_OK(uv_idle_init(loop, &idle_handle)); + ASSERT_OK(pipe(pipefds)); + ASSERT_OK(pipe(newpipefds)); + + ASSERT_OK(uv_pipe_open(&p1, pipefds[0])); + duped_fd = dup(pipefds[0]); + ASSERT_NE(duped_fd, -1); + + ASSERT_OK(uv_read_start((uv_stream_t*) &p1, alloc_buffer, read_data)); + ASSERT_OK(close(pipefds[1])); /* Close write end, generate POLLHUP. */ + pipefds[1] = -1; + + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} + +#endif /* !_WIN32 */ diff --git a/test/test-ip-name.c b/test/test-ip-name.c index 1cb1b605834..cdc0c563427 100644 --- a/test/test-ip-name.c +++ b/test/test-ip-name.c @@ -40,26 +40,26 @@ TEST_IMPL(ip_name) { struct sockaddr_in6* addr6 = &test_addr.addr6; /* test ip4_name */ - ASSERT_EQ(0, uv_ip4_addr("192.168.0.1", TEST_PORT, addr4)); - ASSERT_EQ(0, uv_ip4_name(addr4, dst, INET_ADDRSTRLEN)); - ASSERT_EQ(0, strcmp("192.168.0.1", dst)); + ASSERT_OK(uv_ip4_addr("192.168.0.1", TEST_PORT, addr4)); + ASSERT_OK(uv_ip4_name(addr4, dst, INET_ADDRSTRLEN)); + ASSERT_OK(strcmp("192.168.0.1", dst)); - ASSERT_EQ(0, uv_ip_name(addr, dst, INET_ADDRSTRLEN)); - ASSERT_EQ(0, strcmp("192.168.0.1", dst)); + ASSERT_OK(uv_ip_name(addr, dst, INET_ADDRSTRLEN)); + ASSERT_OK(strcmp("192.168.0.1", dst)); /* test ip6_name */ - ASSERT_EQ(0, uv_ip6_addr("fe80::2acf:daff:fedd:342a", TEST_PORT, addr6)); - ASSERT_EQ(0, uv_ip6_name(addr6, dst, INET6_ADDRSTRLEN)); - ASSERT_EQ(0, strcmp("fe80::2acf:daff:fedd:342a", dst)); - - ASSERT_EQ(0, uv_ip_name(addr, dst, INET6_ADDRSTRLEN)); - ASSERT_EQ(0, strcmp("fe80::2acf:daff:fedd:342a", dst)); + ASSERT_OK(uv_ip6_addr("fe80::2acf:daff:fedd:342a", TEST_PORT, addr6)); + ASSERT_OK(uv_ip6_name(addr6, dst, INET6_ADDRSTRLEN)); + ASSERT_OK(strcmp("fe80::2acf:daff:fedd:342a", dst)); + + ASSERT_OK(uv_ip_name(addr, dst, INET6_ADDRSTRLEN)); + ASSERT_OK(strcmp("fe80::2acf:daff:fedd:342a", dst)); /* test other sa_family */ addr->sa_family = AF_UNIX; /* size is not a concern here */ ASSERT_EQ(UV_EAFNOSUPPORT, uv_ip_name(addr, dst, INET6_ADDRSTRLEN)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-ip4-addr.c b/test/test-ip4-addr.c index dfefb0f914a..4a16c832d33 100644 --- a/test/test-ip4-addr.c +++ b/test/test-ip4-addr.c @@ -30,26 +30,26 @@ TEST_IMPL(ip4_addr) { struct sockaddr_in addr; char dst[16]; - ASSERT(0 == uv_inet_ntop(AF_INET, "\xFF\xFF\xFF\xFF", dst, sizeof(dst))); - ASSERT(0 == strcmp(dst, "255.255.255.255")); - ASSERT(UV_ENOSPC == uv_inet_ntop(AF_INET, "\xFF\xFF\xFF\xFF", - dst, sizeof(dst) - 1)); - - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); - ASSERT(0 == uv_ip4_addr("255.255.255.255", TEST_PORT, &addr)); - ASSERT(UV_EINVAL == uv_ip4_addr("255.255.255*000", TEST_PORT, &addr)); - ASSERT(UV_EINVAL == uv_ip4_addr("255.255.255.256", TEST_PORT, &addr)); - ASSERT(UV_EINVAL == uv_ip4_addr("2555.0.0.0", TEST_PORT, &addr)); - ASSERT(UV_EINVAL == uv_ip4_addr("255", TEST_PORT, &addr)); + ASSERT_OK(uv_inet_ntop(AF_INET, "\xFF\xFF\xFF\xFF", dst, sizeof(dst))); + ASSERT_OK(strcmp(dst, "255.255.255.255")); + ASSERT_EQ(UV_ENOSPC, uv_inet_ntop(AF_INET, "\xFF\xFF\xFF\xFF", + dst, sizeof(dst) - 1)); + + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("255.255.255.255", TEST_PORT, &addr)); + ASSERT_EQ(UV_EINVAL, uv_ip4_addr("255.255.255*000", TEST_PORT, &addr)); + ASSERT_EQ(UV_EINVAL, uv_ip4_addr("255.255.255.256", TEST_PORT, &addr)); + ASSERT_EQ(UV_EINVAL, uv_ip4_addr("2555.0.0.0", TEST_PORT, &addr)); + ASSERT_EQ(UV_EINVAL, uv_ip4_addr("255", TEST_PORT, &addr)); #ifdef SIN6_LEN - ASSERT(addr.sin_len == sizeof(addr)); + ASSERT_EQ(addr.sin_len, sizeof(addr)); #endif /* for broken address family */ - ASSERT(UV_EAFNOSUPPORT == uv_inet_pton(42, "127.0.0.1", - &addr.sin_addr.s_addr)); + ASSERT_EQ(UV_EAFNOSUPPORT, uv_inet_pton(42, "127.0.0.1", + &addr.sin_addr.s_addr)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-ip6-addr.c b/test/test-ip6-addr.c index 8036c4b1712..632b7928994 100644 --- a/test/test-ip6-addr.c +++ b/test/test-ip6-addr.c @@ -51,7 +51,7 @@ TEST_IMPL(ip6_addr_link_local) { int ix; int r; - ASSERT(0 == uv_interface_addresses(&addresses, &count)); + ASSERT_OK(uv_interface_addresses(&addresses, &count)); for (ix = 0; ix < count; ix++) { address = addresses + ix; @@ -59,10 +59,10 @@ TEST_IMPL(ip6_addr_link_local) { if (address->address.address6.sin6_family != AF_INET6) continue; - ASSERT(0 == uv_inet_ntop(AF_INET6, - &address->address.address6.sin6_addr, - string_address, - sizeof(string_address))); + ASSERT_OK(uv_inet_ntop(AF_INET6, + &address->address.address6.sin6_addr, + string_address, + sizeof(string_address))); /* Skip addresses that are not link-local. */ if (strncmp(string_address, "fe80::", 6) != 0) @@ -72,21 +72,23 @@ TEST_IMPL(ip6_addr_link_local) { device_name = address->name; scoped_addr_len = sizeof(scoped_addr); - ASSERT(0 == uv_if_indextoname(iface_index, scoped_addr, &scoped_addr_len)); + ASSERT_OK(uv_if_indextoname(iface_index, + scoped_addr, + &scoped_addr_len)); #ifndef _WIN32 /* This assert fails on Windows, as Windows semantics are different. */ - ASSERT(0 == strcmp(device_name, scoped_addr)); + ASSERT_OK(strcmp(device_name, scoped_addr)); #endif interface_id_len = sizeof(interface_id); r = uv_if_indextoiid(iface_index, interface_id, &interface_id_len); - ASSERT(0 == r); + ASSERT_OK(r); #ifdef _WIN32 /* On Windows, the interface identifier is the numeric string of the index. */ - ASSERT(strtoul(interface_id, NULL, 10) == iface_index); + ASSERT_EQ(strtoul(interface_id, NULL, 10), iface_index); #else /* On Unix/Linux, the interface identifier is the interface device name. */ - ASSERT(0 == strcmp(device_name, interface_id)); + ASSERT_OK(strcmp(device_name, interface_id)); #endif snprintf(scoped_addr, @@ -102,18 +104,20 @@ TEST_IMPL(ip6_addr_link_local) { device_name); fflush(stderr); - ASSERT(0 == uv_ip6_addr(scoped_addr, TEST_PORT, &addr)); - fprintf(stderr, "Got scope_id 0x%02x\n", addr.sin6_scope_id); + ASSERT_OK(uv_ip6_addr(scoped_addr, TEST_PORT, &addr)); + fprintf(stderr, "Got scope_id 0x%2x\n", (unsigned)addr.sin6_scope_id); fflush(stderr); - ASSERT(iface_index == addr.sin6_scope_id); + ASSERT_EQ(iface_index, addr.sin6_scope_id); } uv_free_interface_addresses(addresses, count); scoped_addr_len = sizeof(scoped_addr); - ASSERT(0 != uv_if_indextoname((unsigned int)-1, scoped_addr, &scoped_addr_len)); + ASSERT_NE(0, uv_if_indextoname((unsigned int)-1, + scoped_addr, + &scoped_addr_len)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -137,16 +141,16 @@ TEST_IMPL(ip6_addr_link_local) { X("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255.255") \ #define TEST_GOOD(ADDR) \ - ASSERT(0 == uv_inet_pton(AF_INET6, ADDR, &addr)); \ - ASSERT(0 == uv_inet_pton(AF_INET6, ADDR "%en1", &addr)); \ - ASSERT(0 == uv_inet_pton(AF_INET6, ADDR "%%%%", &addr)); \ - ASSERT(0 == uv_inet_pton(AF_INET6, ADDR "%en1:1.2.3.4", &addr)); \ + ASSERT_OK(uv_inet_pton(AF_INET6, ADDR, &addr)); \ + ASSERT_OK(uv_inet_pton(AF_INET6, ADDR "%en1", &addr)); \ + ASSERT_OK(uv_inet_pton(AF_INET6, ADDR "%%%%", &addr)); \ + ASSERT_OK(uv_inet_pton(AF_INET6, ADDR "%en1:1.2.3.4", &addr)); \ #define TEST_BAD(ADDR) \ - ASSERT(0 != uv_inet_pton(AF_INET6, ADDR, &addr)); \ - ASSERT(0 != uv_inet_pton(AF_INET6, ADDR "%en1", &addr)); \ - ASSERT(0 != uv_inet_pton(AF_INET6, ADDR "%%%%", &addr)); \ - ASSERT(0 != uv_inet_pton(AF_INET6, ADDR "%en1:1.2.3.4", &addr)); \ + ASSERT_NE(0, uv_inet_pton(AF_INET6, ADDR, &addr)); \ + ASSERT_NE(0, uv_inet_pton(AF_INET6, ADDR "%en1", &addr)); \ + ASSERT_NE(0, uv_inet_pton(AF_INET6, ADDR "%%%%", &addr)); \ + ASSERT_NE(0, uv_inet_pton(AF_INET6, ADDR "%en1:1.2.3.4", &addr)); \ TEST_IMPL(ip6_pton) { struct in6_addr addr; @@ -154,7 +158,7 @@ TEST_IMPL(ip6_pton) { GOOD_ADDR_LIST(TEST_GOOD) BAD_ADDR_LIST(TEST_BAD) - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -163,9 +167,9 @@ TEST_IMPL(ip6_pton) { TEST_IMPL(ip6_sin6_len) { struct sockaddr_in6 s; - ASSERT_EQ(0, uv_ip6_addr("::", 0, &s)); + ASSERT_OK(uv_ip6_addr("::", 0, &s)); #ifdef SIN6_LEN - ASSERT(s.sin6_len == sizeof(s)); + ASSERT_EQ(s.sin6_len, sizeof(s)); #endif return 0; } diff --git a/test/test-ipc-heavy-traffic-deadlock-bug.c b/test/test-ipc-heavy-traffic-deadlock-bug.c index 89b977d2c34..0305500abfb 100644 --- a/test/test-ipc-heavy-traffic-deadlock-bug.c +++ b/test/test-ipc-heavy-traffic-deadlock-bug.c @@ -49,7 +49,7 @@ static size_t bytes_read; static void write_cb(uv_write_t* req, int status) { struct write_info* write_info = container_of(req, struct write_info, write_req); - ASSERT(status == 0); + ASSERT_OK(status); bytes_written += BUFFERS_PER_WRITE * BUFFER_SIZE; free(write_info); } @@ -75,7 +75,7 @@ static void do_write(uv_stream_t* handle) { r = uv_write( &write_info->write_req, handle, bufs, BUFFERS_PER_WRITE, write_cb); - ASSERT(r == 0); + ASSERT_OK(r); } static void alloc_cb(uv_handle_t* handle, @@ -94,18 +94,18 @@ static void read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { ssize_t i; int r; - ASSERT(nread >= 0); + ASSERT_GE(nread, 0); bytes_read += nread; for (i = 0; i < nread; i++) - ASSERT(buf->base[i] == BUFFER_CONTENT); + ASSERT_EQ(buf->base[i], BUFFER_CONTENT); free(buf->base); if (bytes_read >= XFER_SIZE) { r = uv_read_stop(handle); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_shutdown(&shutdown_req, handle, shutdown_cb); - ASSERT(r == 0); + ASSERT_OK(r); } } @@ -121,13 +121,13 @@ static void do_writes_and_reads(uv_stream_t* handle) { } r = uv_read_start(handle, alloc_cb, read_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(handle->loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(bytes_written == XFER_SIZE); - ASSERT(bytes_read == XFER_SIZE); + ASSERT_EQ(bytes_written, XFER_SIZE); + ASSERT_EQ(bytes_read, XFER_SIZE); } TEST_IMPL(ipc_heavy_traffic_deadlock_bug) { @@ -137,7 +137,7 @@ TEST_IMPL(ipc_heavy_traffic_deadlock_bug) { spawn_helper(&pipe, &process, "ipc_helper_heavy_traffic_deadlock_bug"); do_writes_and_reads((uv_stream_t*) &pipe); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(pipe.loop); return 0; } @@ -146,14 +146,14 @@ int ipc_helper_heavy_traffic_deadlock_bug(void) { int r; r = uv_pipe_init(uv_default_loop(), &pipe, 1); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_open(&pipe, 0); - ASSERT(r == 0); + ASSERT_OK(r); notify_parent_process(); do_writes_and_reads((uv_stream_t*) &pipe); uv_sleep(100); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-ipc-send-recv.c b/test/test-ipc-send-recv.c index 12d4e33221f..0cdd881be05 100644 --- a/test/test-ipc-send-recv.c +++ b/test/test-ipc-send-recv.c @@ -76,10 +76,12 @@ static int write2_cb_called; static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { - /* we're not actually reading anything so a small buffer is okay */ - static char slab[8]; - buf->base = slab; - buf->len = sizeof(slab); + /* We're not actually reading anything so a small buffer is okay + * but it needs to be heap-allocated to appease TSan. + */ + buf->len = 8; + buf->base = malloc(buf->len); + ASSERT_NOT_NULL(buf->base); } @@ -91,8 +93,10 @@ static void recv_cb(uv_stream_t* handle, int r; union handles* recv; + free(buf->base); + pipe = (uv_pipe_t*) handle; - ASSERT(pipe == &ctx.channel); + ASSERT_PTR_EQ(pipe, &ctx.channel); do { if (++recv_cb_count == 1) { @@ -108,13 +112,13 @@ static void recv_cb(uv_stream_t* handle, * acceptable value. */ if (nread == UV_EOF) { /* UV_EOF is only acceptable for the final recv_cb call */ - ASSERT(recv_cb_count == 2); + ASSERT_EQ(2, recv_cb_count); } else { - ASSERT(nread >= 0); - ASSERT(uv_pipe_pending_count(pipe) > 0); + ASSERT_GE(nread, 0); + ASSERT_GT(uv_pipe_pending_count(pipe), 0); pending = uv_pipe_pending_type(pipe); - ASSERT(pending == ctx.expected_type); + ASSERT_EQ(pending, ctx.expected_type); if (pending == UV_NAMED_PIPE) r = uv_pipe_init(ctx.channel.loop, &recv->pipe, 0); @@ -122,10 +126,10 @@ static void recv_cb(uv_stream_t* handle, r = uv_tcp_init(ctx.channel.loop, &recv->tcp); else abort(); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_accept(handle, &recv->stream); - ASSERT(r == 0); + ASSERT_OK(r); } } while (uv_pipe_pending_count(pipe) > 0); @@ -139,8 +143,8 @@ static void connect_cb(uv_connect_t* req, int status) { int r; uv_buf_t buf; - ASSERT(req == &ctx.connect_req); - ASSERT(status == 0); + ASSERT_PTR_EQ(req, &ctx.connect_req); + ASSERT_OK(status); buf = uv_buf_init(".", 1); r = uv_write2(&ctx.write_req, @@ -148,7 +152,7 @@ static void connect_cb(uv_connect_t* req, int status) { &buf, 1, &ctx.send.stream, NULL); - ASSERT(r == 0); + ASSERT_OK(r); /* Perform two writes to the same pipe to make sure that on Windows we are * not running into issue 505: @@ -159,10 +163,10 @@ static void connect_cb(uv_connect_t* req, int status) { &buf, 1, &ctx.send2.stream, NULL); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_start((uv_stream_t*)&ctx.channel, alloc_cb, recv_cb); - ASSERT(r == 0); + ASSERT_OK(r); } static int run_test(int inprocess) { @@ -172,12 +176,12 @@ static int run_test(int inprocess) { if (inprocess) { r = uv_thread_create(&tid, ipc_send_recv_helper_threadproc, (void *) 42); - ASSERT(r == 0); + ASSERT_OK(r); uv_sleep(1000); r = uv_pipe_init(uv_default_loop(), &ctx.channel, 1); - ASSERT(r == 0); + ASSERT_OK(r); uv_pipe_connect(&ctx.connect_req, &ctx.channel, TEST_PIPENAME_3, connect_cb); } else { @@ -187,13 +191,13 @@ static int run_test(int inprocess) { } r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(recv_cb_count == 2); + ASSERT_EQ(2, recv_cb_count); if (inprocess) { r = uv_thread_join(&tid); - ASSERT(r == 0); + ASSERT_OK(r); } return 0; @@ -205,21 +209,21 @@ static int run_ipc_send_recv_pipe(int inprocess) { ctx.expected_type = UV_NAMED_PIPE; r = uv_pipe_init(uv_default_loop(), &ctx.send.pipe, 1); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_bind(&ctx.send.pipe, TEST_PIPENAME); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_init(uv_default_loop(), &ctx.send2.pipe, 1); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_bind(&ctx.send2.pipe, TEST_PIPENAME_2); - ASSERT(r == 0); + ASSERT_OK(r); r = run_test(inprocess); - ASSERT(r == 0); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -241,26 +245,26 @@ static int run_ipc_send_recv_tcp(int inprocess) { struct sockaddr_in addr; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); ctx.expected_type = UV_TCP; r = uv_tcp_init(uv_default_loop(), &ctx.send.tcp); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_init(uv_default_loop(), &ctx.send2.tcp); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&ctx.send.tcp, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&ctx.send2.tcp, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = run_test(inprocess); - ASSERT(r == 0); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -282,7 +286,7 @@ TEST_IMPL(ipc_send_recv_tcp_inprocess) { /* Everything here runs in a child process or second thread. */ static void write2_cb(uv_write_t* req, int status) { - ASSERT(status == 0); + ASSERT_OK(status); /* After two successful writes in the child process, allow the child * process to be closed. */ @@ -304,12 +308,18 @@ static void read_cb(uv_stream_t* handle, union handles* recv; uv_write_t* write_req; + free(rdbuf->base); + if (nread == UV_EOF || nread == UV_ECONNABORTED) { return; } + ASSERT_GE(nread, 0); + pipe = (uv_pipe_t*) handle; - do { + ASSERT_PTR_EQ(pipe, &ctx2.channel); + + while (uv_pipe_pending_count(pipe) > 0) { if (++read_cb_count == 2) { recv = &ctx2.recv; write_req = &ctx2.write_req; @@ -318,10 +328,6 @@ static void read_cb(uv_stream_t* handle, write_req = &ctx2.write_req2; } - ASSERT(pipe == &ctx2.channel); - ASSERT(nread >= 0); - ASSERT(uv_pipe_pending_count(pipe) > 0); - pending = uv_pipe_pending_type(pipe); ASSERT(pending == UV_NAMED_PIPE || pending == UV_TCP); @@ -331,10 +337,10 @@ static void read_cb(uv_stream_t* handle, r = uv_tcp_init(ctx2.channel.loop, &recv->tcp); else abort(); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_accept(handle, &recv->stream); - ASSERT(r == 0); + ASSERT_OK(r); wrbuf = uv_buf_init(".", 1); r = uv_write2(write_req, @@ -343,27 +349,27 @@ static void read_cb(uv_stream_t* handle, 1, &recv->stream, write2_cb); - ASSERT(r == 0); - } while (uv_pipe_pending_count(pipe) > 0); + ASSERT_OK(r); + } } static void send_recv_start(void) { int r; - ASSERT(1 == uv_is_readable((uv_stream_t*)&ctx2.channel)); - ASSERT(1 == uv_is_writable((uv_stream_t*)&ctx2.channel)); - ASSERT(0 == uv_is_closing((uv_handle_t*)&ctx2.channel)); + ASSERT_EQ(1, uv_is_readable((uv_stream_t*)&ctx2.channel)); + ASSERT_EQ(1, uv_is_writable((uv_stream_t*)&ctx2.channel)); + ASSERT_OK(uv_is_closing((uv_handle_t*)&ctx2.channel)); r = uv_read_start((uv_stream_t*)&ctx2.channel, alloc_cb, read_cb); - ASSERT(r == 0); + ASSERT_OK(r); } static void listen_cb(uv_stream_t* handle, int status) { int r; - ASSERT(handle == (uv_stream_t*)&ctx2.listen); - ASSERT(status == 0); + ASSERT_PTR_EQ(handle, (uv_stream_t*)&ctx2.listen); + ASSERT_OK(status); r = uv_accept((uv_stream_t*)&ctx2.listen, (uv_stream_t*)&ctx2.channel); - ASSERT(r == 0); + ASSERT_OK(r); send_recv_start(); } @@ -376,27 +382,27 @@ int run_ipc_send_recv_helper(uv_loop_t* loop, int inprocess) { memset(&ctx2, 0, sizeof(ctx2)); r = uv_pipe_init(loop, &ctx2.listen, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_init(loop, &ctx2.channel, 1); - ASSERT(r == 0); + ASSERT_OK(r); if (inprocess) { r = uv_pipe_bind(&ctx2.listen, TEST_PIPENAME_3); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&ctx2.listen, SOMAXCONN, listen_cb); - ASSERT(r == 0); + ASSERT_OK(r); } else { r = uv_pipe_open(&ctx2.channel, 0); - ASSERT(r == 0); + ASSERT_OK(r); send_recv_start(); } notify_parent_process(); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); return 0; } @@ -408,9 +414,9 @@ int ipc_send_recv_helper(void) { int r; r = run_ipc_send_recv_helper(uv_default_loop(), 0); - ASSERT(r == 0); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -419,11 +425,11 @@ void ipc_send_recv_helper_threadproc(void* arg) { uv_loop_t loop; r = uv_loop_init(&loop); - ASSERT(r == 0); + ASSERT_OK(r); r = run_ipc_send_recv_helper(&loop, 1); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_loop_close(&loop); - ASSERT(r == 0); + ASSERT_OK(r); } diff --git a/test/test-ipc.c b/test/test-ipc.c index 68a0e1bb814..49975d9b4d7 100644 --- a/test/test-ipc.c +++ b/test/test-ipc.c @@ -45,8 +45,6 @@ static int close_cb_called; static int connection_accepted; static int tcp_conn_read_cb_called; static int tcp_conn_write_cb_called; -static int closed_handle_data_read; -static int closed_handle_write; static int send_zero_write; typedef struct { @@ -57,15 +55,6 @@ typedef struct { #define CONN_COUNT 100 #define BACKLOG 128 -#define LARGE_SIZE 100000 - -static uv_buf_t large_buf; -static char buffer[LARGE_SIZE]; -static uv_write_t write_reqs[300]; -static int write_reqs_completed; - -static unsigned int write_until_data_queued(void); -static void send_handle_and_close(void); static void close_server_conn_cb(uv_handle_t* handle) { @@ -79,16 +68,16 @@ static void on_connection(uv_stream_t* server, int status) { if (!local_conn_accepted) { /* Accept the connection and close it. Also and close the server. */ - ASSERT_EQ(status, 0); + ASSERT_OK(status); ASSERT_PTR_EQ(&tcp_server, server); conn = malloc(sizeof(*conn)); ASSERT_NOT_NULL(conn); r = uv_tcp_init(server->loop, conn); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_accept(server, (uv_stream_t*)conn); - ASSERT_EQ(r, 0); + ASSERT_OK(r); uv_close((uv_handle_t*)conn, close_server_conn_cb); uv_close((uv_handle_t*)server, NULL); @@ -102,8 +91,8 @@ static void exit_cb(uv_process_t* process, int term_signal) { printf("exit_cb\n"); exit_cb_called++; - ASSERT_EQ(exit_status, 0); - ASSERT_EQ(term_signal, 0); + ASSERT_OK(exit_status); + ASSERT_OK(term_signal); uv_close((uv_handle_t*)process, NULL); } @@ -137,14 +126,14 @@ static void make_many_connections(void) { ASSERT_NOT_NULL(conn); r = uv_tcp_init(uv_default_loop(), &conn->conn); - ASSERT_EQ(r, 0); - ASSERT_EQ(0, uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(r); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_connect(&conn->conn_req, (uv_tcp_t*) &conn->conn, (const struct sockaddr*) &addr, connect_cb); - ASSERT_EQ(r, 0); + ASSERT_OK(r); conn->conn.data = conn; } @@ -190,13 +179,13 @@ static void on_read(uv_stream_t* handle, /* Accept the pending TCP server, and start listening on it. */ ASSERT_EQ(pending, UV_TCP); r = uv_tcp_init(uv_default_loop(), &tcp_server); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_accept((uv_stream_t*)pipe, (uv_stream_t*)&tcp_server); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, on_connection); - ASSERT_EQ(r, 0); + ASSERT_OK(r); tcp_server_listening = 1; @@ -205,13 +194,13 @@ static void on_read(uv_stream_t* handle, outbuf = uv_buf_init("foobar\n", 7); r = uv_write(&write_req, (uv_stream_t*)pipe, &outbuf, 1, NULL); - ASSERT_EQ(r, 0); + ASSERT_OK(r); /* Create a bunch of connections to get both servers to accept. */ make_many_connections(); } else if (memcmp("accepted_connection\n", buf->base, nread) == 0) { /* Remote server has accepted a connection. Close the channel. */ - ASSERT_EQ(0, uv_pipe_pending_count(pipe)); + ASSERT_OK(uv_pipe_pending_count(pipe)); ASSERT_EQ(pending, UV_UNKNOWN_HANDLE); remote_conn_accepted = 1; uv_close((uv_handle_t*)&channel, NULL); @@ -259,28 +248,28 @@ static void on_read_listen_after_bound_twice(uv_stream_t* handle, /* Accept the first TCP server, and start listening on it. */ ASSERT_EQ(pending, UV_TCP); r = uv_tcp_init(uv_default_loop(), &tcp_server); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_accept((uv_stream_t*)pipe, (uv_stream_t*)&tcp_server); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, on_connection); - ASSERT_EQ(r, 0); + ASSERT_OK(r); } else if (read_cb_called == 2) { /* Accept the second TCP server, and start listening on it. */ ASSERT_EQ(pending, UV_TCP); r = uv_tcp_init(uv_default_loop(), &tcp_server2); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_accept((uv_stream_t*)pipe, (uv_stream_t*)&tcp_server2); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&tcp_server2, BACKLOG, on_connection); ASSERT_EQ(r, UV_EADDRINUSE); uv_close((uv_handle_t*)&tcp_server, NULL); uv_close((uv_handle_t*)&tcp_server2, NULL); - ASSERT_EQ(0, uv_pipe_pending_count(pipe)); + ASSERT_OK(uv_pipe_pending_count(pipe)); uv_close((uv_handle_t*)&channel, NULL); } @@ -299,12 +288,12 @@ void spawn_helper(uv_pipe_t* channel, uv_stdio_container_t stdio[3]; r = uv_pipe_init(uv_default_loop(), channel, 1); - ASSERT_EQ(r, 0); - ASSERT_NE(channel->ipc, 0); + ASSERT_OK(r); + ASSERT_NE(0, channel->ipc); exepath_size = sizeof(exepath); r = uv_exepath(exepath, &exepath_size); - ASSERT_EQ(r, 0); + ASSERT_OK(r); exepath[exepath_size] = '\0'; args[0] = exepath; @@ -326,12 +315,12 @@ void spawn_helper(uv_pipe_t* channel, stdio[2].data.fd = 2; r = uv_spawn(uv_default_loop(), process, &options); - ASSERT_EQ(r, 0); + ASSERT_OK(r); } static void on_tcp_write(uv_write_t* req, int status) { - ASSERT_EQ(status, 0); + ASSERT_OK(status); ASSERT_PTR_EQ(req->handle, &tcp_connection); tcp_write_cb_called++; } @@ -396,10 +385,10 @@ static void on_read_connection(uv_stream_t* handle, /* Accept the pending TCP connection */ ASSERT_EQ(pending, UV_TCP); r = uv_tcp_init(uv_default_loop(), &tcp_connection); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_accept(handle, (uv_stream_t*)&tcp_connection); - ASSERT_EQ(r, 0); + ASSERT_OK(r); /* Make sure that the expected data is correctly multiplexed. */ ASSERT_MEM_EQ("hello\n", buf->base, nread); @@ -408,33 +397,13 @@ static void on_read_connection(uv_stream_t* handle, outbuf = uv_buf_init("world\n", 6); r = uv_write(&write_req, (uv_stream_t*)&tcp_connection, &outbuf, 1, on_tcp_write); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_read_start((uv_stream_t*)&tcp_connection, on_read_alloc, on_tcp_read); - ASSERT_EQ(r, 0); - - free(buf->base); -} - - -#ifndef _WIN32 -static void on_read_closed_handle(uv_stream_t* handle, - ssize_t nread, - const uv_buf_t* buf) { - if (nread == 0 || nread == UV_EOF) { - free(buf->base); - return; - } - - if (nread < 0) { - printf("error recving on channel: %s\n", uv_strerror(nread)); - abort(); - } + ASSERT_OK(r); - closed_handle_data_read += nread; free(buf->base); } -#endif static void on_read_send_zero(uv_stream_t* handle, @@ -453,9 +422,9 @@ static int run_ipc_test(const char* helper, uv_read_cb read_cb) { uv_read_start((uv_stream_t*)&channel, on_alloc, read_cb); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT_EQ(r, 0); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -465,10 +434,10 @@ TEST_IMPL(ipc_listen_before_write) { RETURN_SKIP(NO_SEND_HANDLE_ON_PIPE); #endif int r = run_ipc_test("ipc_helper_listen_before_write", on_read); - ASSERT_EQ(local_conn_accepted, 1); - ASSERT_EQ(remote_conn_accepted, 1); - ASSERT_EQ(read_cb_called, 1); - ASSERT_EQ(exit_cb_called, 1); + ASSERT_EQ(1, local_conn_accepted); + ASSERT_EQ(1, remote_conn_accepted); + ASSERT_EQ(1, read_cb_called); + ASSERT_EQ(1, exit_cb_called); return r; } @@ -478,10 +447,10 @@ TEST_IMPL(ipc_listen_after_write) { RETURN_SKIP(NO_SEND_HANDLE_ON_PIPE); #endif int r = run_ipc_test("ipc_helper_listen_after_write", on_read); - ASSERT_EQ(local_conn_accepted, 1); - ASSERT_EQ(remote_conn_accepted, 1); - ASSERT_EQ(read_cb_called, 1); - ASSERT_EQ(exit_cb_called, 1); + ASSERT_EQ(1, local_conn_accepted); + ASSERT_EQ(1, remote_conn_accepted); + ASSERT_EQ(1, read_cb_called); + ASSERT_EQ(1, exit_cb_called); return r; } @@ -491,22 +460,13 @@ TEST_IMPL(ipc_tcp_connection) { RETURN_SKIP(NO_SEND_HANDLE_ON_PIPE); #endif int r = run_ipc_test("ipc_helper_tcp_connection", on_read_connection); - ASSERT_EQ(read_cb_called, 1); - ASSERT_EQ(tcp_write_cb_called, 1); - ASSERT_EQ(tcp_read_cb_called, 1); - ASSERT_EQ(exit_cb_called, 1); + ASSERT_EQ(1, read_cb_called); + ASSERT_EQ(1, tcp_write_cb_called); + ASSERT_EQ(1, tcp_read_cb_called); + ASSERT_EQ(1, exit_cb_called); return r; } -#ifndef _WIN32 -TEST_IMPL(ipc_closed_handle) { - int r; - r = run_ipc_test("ipc_helper_closed_handle", on_read_closed_handle); - ASSERT_EQ(r, 0); - return 0; -} -#endif - #ifdef _WIN32 TEST_IMPL(listen_with_simultaneous_accepts) { @@ -514,22 +474,22 @@ TEST_IMPL(listen_with_simultaneous_accepts) { int r; struct sockaddr_in addr; - ASSERT_EQ(0, uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_tcp_simultaneous_accepts(&server, 1); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&server, SOMAXCONN, NULL); - ASSERT_EQ(r, 0); - ASSERT_EQ(server.reqs_pending, 32); + ASSERT_OK(r); + ASSERT_EQ(32, server.reqs_pending); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -539,22 +499,22 @@ TEST_IMPL(listen_no_simultaneous_accepts) { int r; struct sockaddr_in addr; - ASSERT_EQ(0, uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_tcp_simultaneous_accepts(&server, 0); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&server, SOMAXCONN, NULL); - ASSERT_EQ(r, 0); - ASSERT_EQ(server.reqs_pending, 1); + ASSERT_OK(r); + ASSERT_EQ(1, server.reqs_pending); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -563,8 +523,8 @@ TEST_IMPL(ipc_listen_after_bind_twice) { RETURN_SKIP(NO_SEND_HANDLE_ON_PIPE); #endif int r = run_ipc_test("ipc_helper_bind_twice", on_read_listen_after_bound_twice); - ASSERT_EQ(read_cb_called, 2); - ASSERT_EQ(exit_cb_called, 1); + ASSERT_EQ(2, read_cb_called); + ASSERT_EQ(1, exit_cb_called); return r; } #endif @@ -572,7 +532,7 @@ TEST_IMPL(ipc_listen_after_bind_twice) { TEST_IMPL(ipc_send_zero) { int r; r = run_ipc_test("ipc_helper_send_zero", on_read_send_zero); - ASSERT_EQ(r, 0); + ASSERT_OK(r); return 0; } @@ -602,25 +562,8 @@ static void tcp_connection_write_cb(uv_write_t* req, int status) { } -static void closed_handle_large_write_cb(uv_write_t* req, int status) { - ASSERT_EQ(status, 0); - ASSERT(closed_handle_data_read = LARGE_SIZE); - if (++write_reqs_completed == ARRAY_SIZE(write_reqs)) { - write_reqs_completed = 0; - if (write_until_data_queued() > 0) - send_handle_and_close(); - } -} - - -static void closed_handle_write_cb(uv_write_t* req, int status) { - ASSERT_EQ(status, UV_EBADF); - closed_handle_write = 1; -} - - static void send_zero_write_cb(uv_write_t* req, int status) { - ASSERT_EQ(status, 0); + ASSERT_OK(status); send_zero_write++; } @@ -648,7 +591,7 @@ static void on_tcp_child_process_read(uv_stream_t* tcp, /* Write to the socket */ outbuf = uv_buf_init("hello again\n", 12); r = uv_write(&conn.tcp_write_req, tcp, &outbuf, 1, tcp_connection_write_cb); - ASSERT_EQ(r, 0); + ASSERT_OK(r); tcp_conn_read_cb_called++; } @@ -657,9 +600,9 @@ static void on_tcp_child_process_read(uv_stream_t* tcp, static void connect_child_process_cb(uv_connect_t* req, int status) { int r; - ASSERT_EQ(status, 0); + ASSERT_OK(status); r = uv_read_start(req->handle, on_read_alloc, on_tcp_child_process_read); - ASSERT_EQ(r, 0); + ASSERT_OK(r); } @@ -672,21 +615,21 @@ static void ipc_on_connection(uv_stream_t* server, int status) { * Accept the connection and close it. Also let the other * side know. */ - ASSERT_EQ(status, 0); + ASSERT_OK(status); ASSERT_PTR_EQ(&tcp_server, server); r = uv_tcp_init(server->loop, &conn.conn); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_accept(server, (uv_stream_t*)&conn.conn); - ASSERT_EQ(r, 0); + ASSERT_OK(r); uv_close((uv_handle_t*)&conn.conn, close_cb); buf = uv_buf_init("accepted_connection\n", 20); r = uv_write2(&conn_notify_req, (uv_stream_t*)&channel, &buf, 1, NULL, conn_notify_write_cb); - ASSERT_EQ(r, 0); + ASSERT_OK(r); connection_accepted = 1; } @@ -703,28 +646,28 @@ static void ipc_on_connection_tcp_conn(uv_stream_t* server, int status) { uv_buf_t buf; uv_tcp_t* conn; - ASSERT_EQ(status, 0); + ASSERT_OK(status); ASSERT_PTR_EQ(&tcp_server, server); conn = malloc(sizeof(*conn)); ASSERT_NOT_NULL(conn); r = uv_tcp_init(server->loop, conn); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_accept(server, (uv_stream_t*)conn); - ASSERT_EQ(r, 0); + ASSERT_OK(r); /* Send the accepted connection to the other process */ buf = uv_buf_init("hello\n", 6); r = uv_write2(&conn_notify_req, (uv_stream_t*)&channel, &buf, 1, (uv_stream_t*)conn, NULL); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_read_start((uv_stream_t*) conn, on_read_alloc, on_tcp_child_process_read); - ASSERT_EQ(r, 0); + ASSERT_OK(r); uv_close((uv_handle_t*)conn, close_and_free_cb); } @@ -739,46 +682,46 @@ int ipc_helper(int listen_after_write) { int r; uv_buf_t buf; - ASSERT_EQ(0, uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_pipe_init(uv_default_loop(), &channel, 1); - ASSERT_EQ(r, 0); + ASSERT_OK(r); uv_pipe_open(&channel, 0); ASSERT_EQ(1, uv_is_readable((uv_stream_t*) &channel)); ASSERT_EQ(1, uv_is_writable((uv_stream_t*) &channel)); - ASSERT_EQ(0, uv_is_closing((uv_handle_t*) &channel)); + ASSERT_OK(uv_is_closing((uv_handle_t*) &channel)); r = uv_tcp_init(uv_default_loop(), &tcp_server); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); - ASSERT_EQ(r, 0); + ASSERT_OK(r); if (!listen_after_write) { r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, ipc_on_connection); - ASSERT_EQ(r, 0); + ASSERT_OK(r); } buf = uv_buf_init("hello\n", 6); r = uv_write2(&write_req, (uv_stream_t*)&channel, &buf, 1, (uv_stream_t*)&tcp_server, NULL); - ASSERT_EQ(r, 0); + ASSERT_OK(r); if (listen_after_write) { r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, ipc_on_connection); - ASSERT_EQ(r, 0); + ASSERT_OK(r); } notify_parent_process(); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT_EQ(r, 0); + ASSERT_OK(r); - ASSERT_EQ(connection_accepted, 1); - ASSERT_EQ(close_cb_called, 3); + ASSERT_EQ(1, connection_accepted); + ASSERT_EQ(3, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -793,118 +736,48 @@ int ipc_helper_tcp_connection(void) { struct sockaddr_in addr; r = uv_pipe_init(uv_default_loop(), &channel, 1); - ASSERT_EQ(r, 0); + ASSERT_OK(r); uv_pipe_open(&channel, 0); ASSERT_EQ(1, uv_is_readable((uv_stream_t*) &channel)); ASSERT_EQ(1, uv_is_writable((uv_stream_t*) &channel)); - ASSERT_EQ(0, uv_is_closing((uv_handle_t*) &channel)); + ASSERT_OK(uv_is_closing((uv_handle_t*) &channel)); r = uv_tcp_init(uv_default_loop(), &tcp_server); - ASSERT_EQ(r, 0); + ASSERT_OK(r); - ASSERT_EQ(0, uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, ipc_on_connection_tcp_conn); - ASSERT_EQ(r, 0); + ASSERT_OK(r); /* Make a connection to the server */ r = uv_tcp_init(uv_default_loop(), &conn.conn); - ASSERT_EQ(r, 0); + ASSERT_OK(r); - ASSERT_EQ(0, uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_connect(&conn.conn_req, (uv_tcp_t*) &conn.conn, (const struct sockaddr*) &addr, connect_child_process_cb); - ASSERT_EQ(r, 0); - - r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT_EQ(r, 0); - - ASSERT_EQ(tcp_conn_read_cb_called, 1); - ASSERT_EQ(tcp_conn_write_cb_called, 1); - ASSERT_EQ(close_cb_called, 4); - - MAKE_VALGRIND_HAPPY(); - return 0; -} - -static unsigned int write_until_data_queued() { - unsigned int i; - int r; - - i = 0; - do { - r = uv_write(&write_reqs[i], - (uv_stream_t*)&channel, - &large_buf, - 1, - closed_handle_large_write_cb); - ASSERT_EQ(r, 0); - i++; - } while (channel.write_queue_size == 0 && - i < ARRAY_SIZE(write_reqs)); - - return channel.write_queue_size; -} - -static void send_handle_and_close() { - int r; - struct sockaddr_in addr; - - r = uv_tcp_init(uv_default_loop(), &tcp_server); - ASSERT_EQ(r, 0); - - ASSERT_EQ(0, uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); - - r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); - ASSERT_EQ(r, 0); - - r = uv_write2(&write_req, - (uv_stream_t*)&channel, - &large_buf, - 1, - (uv_stream_t*)&tcp_server, - closed_handle_write_cb); - ASSERT_EQ(r, 0); - - uv_close((uv_handle_t*)&tcp_server, NULL); -} - -int ipc_helper_closed_handle(void) { - int r; - - memset(buffer, '.', LARGE_SIZE); - large_buf = uv_buf_init(buffer, LARGE_SIZE); - - r = uv_pipe_init(uv_default_loop(), &channel, 1); - ASSERT_EQ(r, 0); - - uv_pipe_open(&channel, 0); - - ASSERT_EQ(1, uv_is_readable((uv_stream_t*) &channel)); - ASSERT_EQ(1, uv_is_writable((uv_stream_t*) &channel)); - ASSERT_EQ(0, uv_is_closing((uv_handle_t*) &channel)); - - if (write_until_data_queued() > 0) - send_handle_and_close(); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT_EQ(r, 0); + ASSERT_OK(r); - ASSERT_EQ(closed_handle_write, 1); + ASSERT_EQ(1, tcp_conn_read_cb_called); + ASSERT_EQ(1, tcp_conn_write_cb_called); + ASSERT_EQ(4, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } - int ipc_helper_bind_twice(void) { /* * This is launched from test-ipc.c. stdin is a duplex channel @@ -914,40 +787,40 @@ int ipc_helper_bind_twice(void) { int r; uv_buf_t buf; - ASSERT_EQ(0, uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_pipe_init(uv_default_loop(), &channel, 1); - ASSERT_EQ(r, 0); + ASSERT_OK(r); uv_pipe_open(&channel, 0); ASSERT_EQ(1, uv_is_readable((uv_stream_t*) &channel)); ASSERT_EQ(1, uv_is_writable((uv_stream_t*) &channel)); - ASSERT_EQ(0, uv_is_closing((uv_handle_t*) &channel)); + ASSERT_OK(uv_is_closing((uv_handle_t*) &channel)); buf = uv_buf_init("hello\n", 6); r = uv_tcp_init(uv_default_loop(), &tcp_server); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_tcp_init(uv_default_loop(), &tcp_server2); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_tcp_bind(&tcp_server2, (const struct sockaddr*) &addr, 0); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_write2(&write_req, (uv_stream_t*)&channel, &buf, 1, (uv_stream_t*)&tcp_server, NULL); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_write2(&write_req2, (uv_stream_t*)&channel, &buf, 1, (uv_stream_t*)&tcp_server2, NULL); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT_EQ(r, 0); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -958,13 +831,13 @@ int ipc_helper_send_zero(void) { zero_buf = uv_buf_init(0, 0); r = uv_pipe_init(uv_default_loop(), &channel, 0); - ASSERT_EQ(r, 0); + ASSERT_OK(r); uv_pipe_open(&channel, 0); ASSERT_EQ(1, uv_is_readable((uv_stream_t*) &channel)); ASSERT_EQ(1, uv_is_writable((uv_stream_t*) &channel)); - ASSERT_EQ(0, uv_is_closing((uv_handle_t*) &channel)); + ASSERT_OK(uv_is_closing((uv_handle_t*) &channel)); r = uv_write(&write_req, (uv_stream_t*)&channel, @@ -972,13 +845,13 @@ int ipc_helper_send_zero(void) { 1, send_zero_write_cb); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT_EQ(r, 0); + ASSERT_OK(r); - ASSERT_EQ(send_zero_write, 1); + ASSERT_EQ(1, send_zero_write); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-list.h b/test/test-list.h index 1f566861a0e..c6651299c12 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -22,7 +22,6 @@ #include "uv.h" TEST_DECLARE (platform_output) -TEST_DECLARE (callback_order) TEST_DECLARE (close_order) TEST_DECLARE (run_once) TEST_DECLARE (run_nowait) @@ -30,6 +29,7 @@ TEST_DECLARE (loop_alive) TEST_DECLARE (loop_close) TEST_DECLARE (loop_instant_close) TEST_DECLARE (loop_stop) +TEST_DECLARE (loop_stop_before_run) TEST_DECLARE (loop_update_time) TEST_DECLARE (loop_backend_timeout) TEST_DECLARE (loop_configure) @@ -91,9 +91,6 @@ TEST_DECLARE (ipc_send_recv_tcp) TEST_DECLARE (ipc_send_recv_tcp_inprocess) TEST_DECLARE (ipc_tcp_connection) TEST_DECLARE (ipc_send_zero) -#ifndef _WIN32 -TEST_DECLARE (ipc_closed_handle) -#endif TEST_DECLARE (tcp_alloc_cb_fail) TEST_DECLARE (tcp_ping_pong) TEST_DECLARE (tcp_ping_pong_vec) @@ -109,6 +106,7 @@ TEST_DECLARE (tcp_write_after_connect) TEST_DECLARE (tcp_writealot) TEST_DECLARE (tcp_write_fail) TEST_DECLARE (tcp_try_write) +TEST_DECLARE (tcp_write_in_a_row) TEST_DECLARE (tcp_try_write_error) TEST_DECLARE (tcp_write_queue_order) TEST_DECLARE (tcp_open) @@ -126,15 +124,20 @@ TEST_DECLARE (tcp_bind_error_inval) TEST_DECLARE (tcp_bind_localhost_ok) TEST_DECLARE (tcp_bind_invalid_flags) TEST_DECLARE (tcp_bind_writable_flags) +TEST_DECLARE (tcp_bind_or_listen_error_after_close) TEST_DECLARE (tcp_listen_without_bind) TEST_DECLARE (tcp_connect_error_fault) +TEST_DECLARE (tcp_connect6_error_fault) +TEST_DECLARE (tcp_connect6_link_local) TEST_DECLARE (tcp_connect_timeout) TEST_DECLARE (tcp_local_connect_timeout) TEST_DECLARE (tcp6_local_connect_timeout) TEST_DECLARE (tcp_close_while_connecting) +TEST_DECLARE (tcp_close_after_read_timeout) TEST_DECLARE (tcp_close) TEST_DECLARE (tcp_close_reset_accepted) TEST_DECLARE (tcp_close_reset_accepted_after_shutdown) +TEST_DECLARE (tcp_close_reset_accepted_after_socket_shutdown) TEST_DECLARE (tcp_close_reset_client) TEST_DECLARE (tcp_close_reset_client_after_shutdown) TEST_DECLARE (tcp_create_early) @@ -150,6 +153,8 @@ TEST_DECLARE (tcp_write_to_half_open_connection) TEST_DECLARE (tcp_unexpected_read) TEST_DECLARE (tcp_read_stop) TEST_DECLARE (tcp_read_stop_start) +TEST_DECLARE (tcp_reuseport) +TEST_DECLARE (tcp_rst) TEST_DECLARE (tcp_bind6_error_addrinuse) TEST_DECLARE (tcp_bind6_error_addrnotavail) TEST_DECLARE (tcp_bind6_error_fault) @@ -184,6 +189,8 @@ TEST_DECLARE (udp_open) TEST_DECLARE (udp_open_twice) TEST_DECLARE (udp_open_bound) TEST_DECLARE (udp_open_connect) +TEST_DECLARE (udp_recv_in_a_row) +TEST_DECLARE (udp_reuseport) #ifndef _WIN32 TEST_DECLARE (udp_send_unix) #endif @@ -192,13 +199,17 @@ TEST_DECLARE (udp_try_send) TEST_DECLARE (pipe_bind_error_addrinuse) TEST_DECLARE (pipe_bind_error_addrnotavail) TEST_DECLARE (pipe_bind_error_inval) +TEST_DECLARE (pipe_connect_close_multiple) TEST_DECLARE (pipe_connect_multiple) TEST_DECLARE (pipe_listen_without_bind) +TEST_DECLARE (pipe_bind_or_listen_error_after_close) +TEST_DECLARE (pipe_overlong_path) TEST_DECLARE (pipe_connect_bad_name) TEST_DECLARE (pipe_connect_to_file) TEST_DECLARE (pipe_connect_on_prepare) TEST_DECLARE (pipe_getsockname) TEST_DECLARE (pipe_getsockname_abstract) +TEST_DECLARE (pipe_getsockname_autobind) TEST_DECLARE (pipe_getsockname_blocking) TEST_DECLARE (pipe_pending_instances) TEST_DECLARE (pipe_sendmsg) @@ -219,6 +230,7 @@ TEST_DECLARE (timer_init) TEST_DECLARE (timer_again) TEST_DECLARE (timer_start_twice) TEST_DECLARE (timer_order) +TEST_DECLARE (timer_zero_timeout) TEST_DECLARE (timer_huge_timeout) TEST_DECLARE (timer_huge_repeat) TEST_DECLARE (timer_run_once) @@ -226,7 +238,11 @@ TEST_DECLARE (timer_from_check) TEST_DECLARE (timer_is_closing) TEST_DECLARE (timer_null_callback) TEST_DECLARE (timer_early_check) +TEST_DECLARE (timer_no_double_call_once) +TEST_DECLARE (timer_no_double_call_nowait) +TEST_DECLARE (timer_no_run_on_unref) TEST_DECLARE (idle_starvation) +TEST_DECLARE (idle_check) TEST_DECLARE (loop_handles) TEST_DECLARE (get_loadavg) TEST_DECLARE (walk_handles) @@ -273,10 +289,13 @@ TEST_DECLARE (process_title_threadsafe) TEST_DECLARE (cwd_and_chdir) TEST_DECLARE (get_memory) TEST_DECLARE (get_passwd) +TEST_DECLARE (get_passwd2) +TEST_DECLARE (get_group) TEST_DECLARE (handle_fileno) TEST_DECLARE (homedir) TEST_DECLARE (tmpdir) TEST_DECLARE (hrtime) +TEST_DECLARE (clock_gettime) TEST_DECLARE (getaddrinfo_fail) TEST_DECLARE (getaddrinfo_fail_sync) TEST_DECLARE (getaddrinfo_basic) @@ -308,6 +327,7 @@ TEST_DECLARE (spawn_detached) TEST_DECLARE (spawn_and_kill_with_std) TEST_DECLARE (spawn_and_ping) TEST_DECLARE (spawn_preserve_env) +TEST_DECLARE (spawn_same_stdout_stderr) TEST_DECLARE (spawn_setuid_fails) TEST_DECLARE (spawn_setgid_fails) TEST_DECLARE (spawn_stdout_to_file) @@ -320,6 +340,8 @@ TEST_DECLARE (spawn_reads_child_path) TEST_DECLARE (spawn_inherit_streams) TEST_DECLARE (spawn_quoted_path) TEST_DECLARE (spawn_tcp_server) +TEST_DECLARE (spawn_exercise_sigchld_issue) +TEST_DECLARE (spawn_relative_path) TEST_DECLARE (fs_poll) TEST_DECLARE (fs_poll_getpath) TEST_DECLARE (fs_poll_close_request) @@ -333,6 +355,7 @@ TEST_DECLARE (fs_file_nametoolong) TEST_DECLARE (fs_file_loop) TEST_DECLARE (fs_file_async) TEST_DECLARE (fs_file_sync) +TEST_DECLARE (fs_posix_delete) TEST_DECLARE (fs_file_write_null_buffer) TEST_DECLARE (fs_async_dir) TEST_DECLARE (fs_async_sendfile) @@ -340,6 +363,7 @@ TEST_DECLARE (fs_async_sendfile_nodata) TEST_DECLARE (fs_mkdtemp) TEST_DECLARE (fs_mkstemp) TEST_DECLARE (fs_fstat) +TEST_DECLARE (fs_fstat_stdio) TEST_DECLARE (fs_access) TEST_DECLARE (fs_chmod) TEST_DECLARE (fs_copyfile) @@ -368,10 +392,12 @@ TEST_DECLARE (fs_futime) TEST_DECLARE (fs_lutime) TEST_DECLARE (fs_file_open_append) TEST_DECLARE (fs_statfs) +TEST_DECLARE (fs_stat_batch_multiple) TEST_DECLARE (fs_stat_missing_path) TEST_DECLARE (fs_read_bufs) TEST_DECLARE (fs_read_file_eof) TEST_DECLARE (fs_event_watch_dir) +TEST_DECLARE (fs_event_watch_delete_dir) TEST_DECLARE (fs_event_watch_dir_recursive) #ifdef _WIN32 TEST_DECLARE (fs_event_watch_dir_short_path) @@ -388,19 +414,23 @@ TEST_DECLARE (fs_event_no_callback_after_close) TEST_DECLARE (fs_event_no_callback_on_close) TEST_DECLARE (fs_event_immediate_close) TEST_DECLARE (fs_event_close_with_pending_event) +TEST_DECLARE (fs_event_close_with_pending_delete_event) TEST_DECLARE (fs_event_close_in_callback) TEST_DECLARE (fs_event_start_and_close) -TEST_DECLARE (fs_event_error_reporting) TEST_DECLARE (fs_event_getpath) TEST_DECLARE (fs_event_stop_in_cb) TEST_DECLARE (fs_scandir_empty_dir) TEST_DECLARE (fs_scandir_non_existent_dir) TEST_DECLARE (fs_scandir_file) +TEST_DECLARE (fs_scandir_early_exit) TEST_DECLARE (fs_open_dir) TEST_DECLARE (fs_readdir_empty_dir) TEST_DECLARE (fs_readdir_file) TEST_DECLARE (fs_readdir_non_empty_dir) TEST_DECLARE (fs_readdir_non_existing_dir) +#ifdef _WIN32 +TEST_DECLARE (fs_readdir_symlink) +#endif TEST_DECLARE (fs_rename_to_existing_file) TEST_DECLARE (fs_write_multiple_bufs) TEST_DECLARE (fs_read_write_null_arguments) @@ -421,9 +451,11 @@ TEST_DECLARE (fs_file_flag_no_buffering) TEST_DECLARE (fs_open_readonly_acl) TEST_DECLARE (fs_fchmod_archive_readonly) TEST_DECLARE (fs_invalid_mkdir_name) +TEST_DECLARE (fs_wtf) #endif TEST_DECLARE (fs_get_system_error) TEST_DECLARE (strscpy) +TEST_DECLARE (strtok) TEST_DECLARE (threadpool_queue_work_simple) TEST_DECLARE (threadpool_queue_work_einval) TEST_DECLARE (threadpool_multiple_event_loops) @@ -433,6 +465,8 @@ TEST_DECLARE (threadpool_cancel_random) TEST_DECLARE (threadpool_cancel_work) TEST_DECLARE (threadpool_cancel_fs) TEST_DECLARE (threadpool_cancel_single) +TEST_DECLARE (threadpool_cancel_when_busy) +TEST_DECLARE (thread_detach) TEST_DECLARE (thread_local_storage) TEST_DECLARE (thread_stack_size) TEST_DECLARE (thread_stack_size_explicit) @@ -442,6 +476,10 @@ TEST_DECLARE (thread_rwlock) TEST_DECLARE (thread_rwlock_trylock) TEST_DECLARE (thread_create) TEST_DECLARE (thread_equal) +TEST_DECLARE (thread_affinity) +TEST_DECLARE (thread_priority) +TEST_DECLARE (thread_name) +TEST_DECLARE (thread_name_threadpool) TEST_DECLARE (dlerror) #if (defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))) && \ !defined(__sun) @@ -477,7 +515,11 @@ TEST_DECLARE (environment_creation) TEST_DECLARE (listen_with_simultaneous_accepts) TEST_DECLARE (listen_no_simultaneous_accepts) TEST_DECLARE (fs_stat_root) +TEST_DECLARE (fs_stat_no_permission) TEST_DECLARE (spawn_with_an_odd_path) +TEST_DECLARE (spawn_no_path) +TEST_DECLARE (spawn_no_ext) +TEST_DECLARE (spawn_path_no_ext) TEST_DECLARE (ipc_listen_after_bind_twice) TEST_DECLARE (win32_signum_number) #else @@ -519,6 +561,7 @@ TEST_DECLARE (fork_socketpair) TEST_DECLARE (fork_socketpair_started) TEST_DECLARE (fork_signal_to_child) TEST_DECLARE (fork_signal_to_child_closed) +TEST_DECLARE (fork_close_signal_in_child) #ifndef __APPLE__ /* This is forbidden in a fork child: The process has forked and you cannot use this CoreFoundation functionality safely. You MUST exec(). */ @@ -531,11 +574,16 @@ TEST_DECLARE (fork_threadpool_queue_work_simple) #endif #endif +TEST_DECLARE (iouring_pollhup) + +TEST_DECLARE (wtf8) TEST_DECLARE (idna_toascii) TEST_DECLARE (utf8_decode1) TEST_DECLARE (utf8_decode1_overrun) TEST_DECLARE (uname) +TEST_DECLARE (metrics_info_check) +TEST_DECLARE (metrics_pool_events) TEST_DECLARE (metrics_idle_time) TEST_DECLARE (metrics_idle_time_thread) TEST_DECLARE (metrics_idle_time_zero) @@ -543,9 +591,6 @@ TEST_DECLARE (metrics_idle_time_zero) TASK_LIST_START TEST_ENTRY_CUSTOM (platform_output, 0, 1, 5000) -#if 0 - TEST_ENTRY (callback_order) -#endif TEST_ENTRY (test_macros) TEST_ENTRY (close_order) TEST_ENTRY (run_once) @@ -554,6 +599,7 @@ TASK_LIST_START TEST_ENTRY (loop_close) TEST_ENTRY (loop_instant_close) TEST_ENTRY (loop_stop) + TEST_ENTRY (loop_stop_before_run) TEST_ENTRY (loop_update_time) TEST_ENTRY (loop_backend_timeout) TEST_ENTRY (loop_configure) @@ -627,9 +673,6 @@ TASK_LIST_START TEST_ENTRY (ipc_send_recv_tcp_inprocess) TEST_ENTRY (ipc_tcp_connection) TEST_ENTRY (ipc_send_zero) -#ifndef _WIN32 - TEST_ENTRY (ipc_closed_handle) -#endif TEST_ENTRY (tcp_alloc_cb_fail) @@ -669,6 +712,7 @@ TASK_LIST_START TEST_HELPER (tcp_write_fail, tcp4_echo_server) TEST_ENTRY (tcp_try_write) + TEST_ENTRY (tcp_write_in_a_row) TEST_ENTRY (tcp_try_write_error) TEST_ENTRY (tcp_write_queue_order) @@ -700,15 +744,20 @@ TASK_LIST_START TEST_ENTRY (tcp_bind_localhost_ok) TEST_ENTRY (tcp_bind_invalid_flags) TEST_ENTRY (tcp_bind_writable_flags) + TEST_ENTRY (tcp_bind_or_listen_error_after_close) TEST_ENTRY (tcp_listen_without_bind) TEST_ENTRY (tcp_connect_error_fault) + TEST_ENTRY (tcp_connect6_error_fault) + TEST_ENTRY (tcp_connect6_link_local) TEST_ENTRY (tcp_connect_timeout) TEST_ENTRY (tcp_local_connect_timeout) TEST_ENTRY (tcp6_local_connect_timeout) TEST_ENTRY (tcp_close_while_connecting) + TEST_ENTRY (tcp_close_after_read_timeout) TEST_ENTRY (tcp_close) TEST_ENTRY (tcp_close_reset_accepted) TEST_ENTRY (tcp_close_reset_accepted_after_shutdown) + TEST_ENTRY (tcp_close_reset_accepted_after_socket_shutdown) TEST_ENTRY (tcp_close_reset_client) TEST_ENTRY (tcp_close_reset_client_after_shutdown) TEST_ENTRY (tcp_create_early) @@ -728,6 +777,11 @@ TASK_LIST_START TEST_ENTRY (tcp_read_stop_start) + TEST_ENTRY (tcp_reuseport) + + TEST_ENTRY (tcp_rst) + TEST_HELPER (tcp_rst, tcp4_echo_server) + TEST_ENTRY (tcp_bind6_error_addrinuse) TEST_ENTRY (tcp_bind6_error_addrnotavail) TEST_ENTRY (tcp_bind6_error_fault) @@ -760,6 +814,8 @@ TASK_LIST_START TEST_ENTRY (udp_multicast_ttl) TEST_ENTRY (udp_sendmmsg_error) TEST_ENTRY (udp_try_send) + TEST_ENTRY (udp_recv_in_a_row) + TEST_ENTRY (udp_reuseport) TEST_ENTRY (udp_open) TEST_ENTRY (udp_open_twice) @@ -772,10 +828,14 @@ TASK_LIST_START TEST_ENTRY (pipe_bind_error_addrinuse) TEST_ENTRY (pipe_bind_error_addrnotavail) TEST_ENTRY (pipe_bind_error_inval) + TEST_ENTRY (pipe_connect_close_multiple) TEST_ENTRY (pipe_connect_multiple) TEST_ENTRY (pipe_listen_without_bind) + TEST_ENTRY (pipe_bind_or_listen_error_after_close) + TEST_ENTRY (pipe_overlong_path) TEST_ENTRY (pipe_getsockname) TEST_ENTRY (pipe_getsockname_abstract) + TEST_ENTRY (pipe_getsockname_autobind) TEST_ENTRY (pipe_getsockname_blocking) TEST_ENTRY (pipe_pending_instances) TEST_ENTRY (pipe_sendmsg) @@ -810,6 +870,7 @@ TASK_LIST_START TEST_ENTRY (timer_again) TEST_ENTRY (timer_start_twice) TEST_ENTRY (timer_order) + TEST_ENTRY (timer_zero_timeout) TEST_ENTRY (timer_huge_timeout) TEST_ENTRY (timer_huge_repeat) TEST_ENTRY (timer_run_once) @@ -817,8 +878,12 @@ TASK_LIST_START TEST_ENTRY (timer_is_closing) TEST_ENTRY (timer_null_callback) TEST_ENTRY (timer_early_check) + TEST_ENTRY (timer_no_double_call_once) + TEST_ENTRY (timer_no_double_call_nowait) + TEST_ENTRY (timer_no_run_on_unref) TEST_ENTRY (idle_starvation) + TEST_ENTRY (idle_check) TEST_ENTRY (ref) TEST_ENTRY (idle_ref) @@ -875,6 +940,8 @@ TASK_LIST_START TEST_ENTRY (get_memory) TEST_ENTRY (get_passwd) + TEST_ENTRY (get_passwd2) + TEST_ENTRY (get_group) TEST_ENTRY (get_loadavg) @@ -886,6 +953,8 @@ TASK_LIST_START TEST_ENTRY_CUSTOM (hrtime, 0, 0, 20000) + TEST_ENTRY (clock_gettime) + TEST_ENTRY_CUSTOM (getaddrinfo_fail, 0, 0, 10000) TEST_ENTRY_CUSTOM (getaddrinfo_fail_sync, 0, 0, 10000) @@ -938,6 +1007,7 @@ TASK_LIST_START TEST_ENTRY (spawn_and_kill_with_std) TEST_ENTRY (spawn_and_ping) TEST_ENTRY (spawn_preserve_env) + TEST_ENTRY (spawn_same_stdout_stderr) TEST_ENTRY (spawn_setuid_fails) TEST_ENTRY (spawn_setgid_fails) TEST_ENTRY (spawn_stdout_to_file) @@ -950,6 +1020,8 @@ TASK_LIST_START TEST_ENTRY (spawn_inherit_streams) TEST_ENTRY (spawn_quoted_path) TEST_ENTRY (spawn_tcp_server) + TEST_ENTRY (spawn_exercise_sigchld_issue) + TEST_ENTRY (spawn_relative_path) TEST_ENTRY (fs_poll) TEST_ENTRY (fs_poll_getpath) TEST_ENTRY (fs_poll_close_request) @@ -973,7 +1045,11 @@ TASK_LIST_START TEST_ENTRY (listen_with_simultaneous_accepts) TEST_ENTRY (listen_no_simultaneous_accepts) TEST_ENTRY (fs_stat_root) + TEST_ENTRY (fs_stat_no_permission) TEST_ENTRY (spawn_with_an_odd_path) + TEST_ENTRY (spawn_no_path) + TEST_ENTRY (spawn_no_ext) + TEST_ENTRY (spawn_path_no_ext) TEST_ENTRY (ipc_listen_after_bind_twice) TEST_ENTRY (win32_signum_number) #else @@ -998,6 +1074,7 @@ TASK_LIST_START TEST_ENTRY (fs_file_loop) TEST_ENTRY (fs_file_async) TEST_ENTRY (fs_file_sync) + TEST_ENTRY (fs_posix_delete) TEST_ENTRY (fs_file_write_null_buffer) TEST_ENTRY (fs_async_dir) TEST_ENTRY (fs_async_sendfile) @@ -1005,6 +1082,7 @@ TASK_LIST_START TEST_ENTRY (fs_mkdtemp) TEST_ENTRY (fs_mkstemp) TEST_ENTRY (fs_fstat) + TEST_ENTRY (fs_fstat_stdio) TEST_ENTRY (fs_access) TEST_ENTRY (fs_chmod) TEST_ENTRY (fs_copyfile) @@ -1013,6 +1091,7 @@ TASK_LIST_START TEST_ENTRY (fs_unlink_archive_readonly) #endif TEST_ENTRY (fs_chown) + TEST_ENTRY (fs_link) TEST_ENTRY (fs_utime) TEST_ENTRY (fs_utime_round) TEST_ENTRY (fs_futime) @@ -1031,11 +1110,13 @@ TASK_LIST_START TEST_ENTRY (fs_fd_hash) #endif TEST_ENTRY (fs_statfs) + TEST_ENTRY (fs_stat_batch_multiple) TEST_ENTRY (fs_stat_missing_path) TEST_ENTRY (fs_read_bufs) TEST_ENTRY (fs_read_file_eof) TEST_ENTRY (fs_file_open_append) TEST_ENTRY (fs_event_watch_dir) + TEST_ENTRY (fs_event_watch_delete_dir) TEST_ENTRY (fs_event_watch_dir_recursive) #ifdef _WIN32 TEST_ENTRY (fs_event_watch_dir_short_path) @@ -1052,19 +1133,23 @@ TASK_LIST_START TEST_ENTRY (fs_event_no_callback_on_close) TEST_ENTRY (fs_event_immediate_close) TEST_ENTRY (fs_event_close_with_pending_event) + TEST_ENTRY (fs_event_close_with_pending_delete_event) TEST_ENTRY (fs_event_close_in_callback) TEST_ENTRY (fs_event_start_and_close) - TEST_ENTRY_CUSTOM (fs_event_error_reporting, 0, 0, 60000) TEST_ENTRY (fs_event_getpath) TEST_ENTRY (fs_event_stop_in_cb) TEST_ENTRY (fs_scandir_empty_dir) TEST_ENTRY (fs_scandir_non_existent_dir) TEST_ENTRY (fs_scandir_file) + TEST_ENTRY (fs_scandir_early_exit) TEST_ENTRY (fs_open_dir) TEST_ENTRY (fs_readdir_empty_dir) TEST_ENTRY (fs_readdir_file) TEST_ENTRY (fs_readdir_non_empty_dir) TEST_ENTRY (fs_readdir_non_existing_dir) +#ifdef _WIN32 + TEST_ENTRY (fs_readdir_symlink) +#endif TEST_ENTRY (fs_rename_to_existing_file) TEST_ENTRY (fs_write_multiple_bufs) TEST_ENTRY (fs_write_alotof_bufs) @@ -1083,11 +1168,13 @@ TASK_LIST_START TEST_ENTRY (fs_open_readonly_acl) TEST_ENTRY (fs_fchmod_archive_readonly) TEST_ENTRY (fs_invalid_mkdir_name) + TEST_ENTRY (fs_wtf) #endif TEST_ENTRY (fs_get_system_error) TEST_ENTRY (get_osfhandle_valid_handle) TEST_ENTRY (open_osfhandle_valid_handle) TEST_ENTRY (strscpy) + TEST_ENTRY (strtok) TEST_ENTRY (threadpool_queue_work_simple) TEST_ENTRY (threadpool_queue_work_einval) TEST_ENTRY_CUSTOM (threadpool_multiple_event_loops, 0, 0, 60000) @@ -1097,6 +1184,8 @@ TASK_LIST_START TEST_ENTRY (threadpool_cancel_work) TEST_ENTRY (threadpool_cancel_fs) TEST_ENTRY (threadpool_cancel_single) + TEST_ENTRY (threadpool_cancel_when_busy) + TEST_ENTRY (thread_detach) TEST_ENTRY (thread_local_storage) TEST_ENTRY (thread_stack_size) TEST_ENTRY (thread_stack_size_explicit) @@ -1106,6 +1195,10 @@ TASK_LIST_START TEST_ENTRY (thread_rwlock_trylock) TEST_ENTRY (thread_create) TEST_ENTRY (thread_equal) + TEST_ENTRY (thread_affinity) + TEST_ENTRY (thread_priority) + TEST_ENTRY (thread_name) + TEST_ENTRY (thread_name_threadpool) TEST_ENTRY (dlerror) TEST_ENTRY (ip4_addr) TEST_ENTRY (ip6_addr_link_local) @@ -1126,6 +1219,7 @@ TASK_LIST_START TEST_ENTRY (fork_socketpair_started) TEST_ENTRY (fork_signal_to_child) TEST_ENTRY (fork_signal_to_child_closed) + TEST_ENTRY (fork_close_signal_in_child) #ifndef __APPLE__ TEST_ENTRY (fork_fs_events_child) TEST_ENTRY (fork_fs_events_child_dir) @@ -1136,6 +1230,9 @@ TASK_LIST_START #endif #endif + TEST_ENTRY (iouring_pollhup) + + TEST_ENTRY (wtf8) TEST_ENTRY (utf8_decode1) TEST_ENTRY (utf8_decode1_overrun) TEST_ENTRY (uname) @@ -1152,6 +1249,8 @@ TASK_LIST_START TEST_ENTRY (readable_on_eof) TEST_HELPER (readable_on_eof, tcp4_echo_server) + TEST_ENTRY (metrics_info_check) + TEST_ENTRY (metrics_pool_events) TEST_ENTRY (metrics_idle_time) TEST_ENTRY (metrics_idle_time_thread) TEST_ENTRY (metrics_idle_time_zero) diff --git a/test/test-loop-alive.c b/test/test-loop-alive.c index cf4d301930c..279cfc32270 100644 --- a/test/test-loop-alive.c +++ b/test/test-loop-alive.c @@ -37,7 +37,7 @@ static void work_cb(uv_work_t* req) { static void after_work_cb(uv_work_t* req, int status) { ASSERT(req); - ASSERT(status == 0); + ASSERT_OK(status); } @@ -51,17 +51,18 @@ TEST_IMPL(loop_alive) { ASSERT(uv_loop_alive(uv_default_loop())); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); ASSERT(!uv_loop_alive(uv_default_loop())); /* loops with requests are alive */ r = uv_queue_work(uv_default_loop(), &work_req, work_cb, after_work_cb); - ASSERT(r == 0); + ASSERT_OK(r); ASSERT(uv_loop_alive(uv_default_loop())); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); ASSERT(!uv_loop_alive(uv_default_loop())); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-loop-close.c b/test/test-loop-close.c index f0f3e627f97..cd1b76ce1b0 100644 --- a/test/test-loop-close.c +++ b/test/test-loop-close.c @@ -35,23 +35,23 @@ TEST_IMPL(loop_close) { uv_loop_t loop; loop.data = &loop; - ASSERT(0 == uv_loop_init(&loop)); - ASSERT(loop.data == (void*) &loop); + ASSERT_OK(uv_loop_init(&loop)); + ASSERT_PTR_EQ(loop.data, (void*) &loop); uv_timer_init(&loop, &timer_handle); uv_timer_start(&timer_handle, timer_cb, 100, 100); - ASSERT(UV_EBUSY == uv_loop_close(&loop)); + ASSERT_EQ(UV_EBUSY, uv_loop_close(&loop)); uv_run(&loop, UV_RUN_DEFAULT); uv_close((uv_handle_t*) &timer_handle, NULL); r = uv_run(&loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(loop.data == (void*) &loop); - ASSERT(0 == uv_loop_close(&loop)); - ASSERT(loop.data == (void*) &loop); + ASSERT_PTR_EQ(loop.data, (void*) &loop); + ASSERT_OK(uv_loop_close(&loop)); + ASSERT_PTR_EQ(loop.data, (void*) &loop); return 0; } @@ -62,14 +62,16 @@ static void loop_instant_close_work_cb(uv_work_t* req) { static void loop_instant_close_after_work_cb(uv_work_t* req, int status) { } +/* It's impossible to properly cleanup after this test because loop can't be + * closed while work has been queued. */ TEST_IMPL(loop_instant_close) { static uv_loop_t loop; static uv_work_t req; - ASSERT(0 == uv_loop_init(&loop)); - ASSERT(0 == uv_queue_work(&loop, - &req, - loop_instant_close_work_cb, - loop_instant_close_after_work_cb)); - MAKE_VALGRIND_HAPPY(); + ASSERT_OK(uv_loop_init(&loop)); + ASSERT_OK(uv_queue_work(&loop, + &req, + loop_instant_close_work_cb, + loop_instant_close_after_work_cb)); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-loop-configure.c b/test/test-loop-configure.c index d057c1ed8a7..1bc328431c9 100644 --- a/test/test-loop-configure.c +++ b/test/test-loop-configure.c @@ -24,15 +24,15 @@ static void timer_cb(uv_timer_t* handle) { TEST_IMPL(loop_configure) { uv_timer_t timer_handle; uv_loop_t loop; - ASSERT(0 == uv_loop_init(&loop)); + ASSERT_OK(uv_loop_init(&loop)); #ifdef _WIN32 - ASSERT(UV_ENOSYS == uv_loop_configure(&loop, UV_LOOP_BLOCK_SIGNAL, 0)); + ASSERT_EQ(UV_ENOSYS, uv_loop_configure(&loop, UV_LOOP_BLOCK_SIGNAL, 0)); #else - ASSERT(0 == uv_loop_configure(&loop, UV_LOOP_BLOCK_SIGNAL, SIGPROF)); + ASSERT_OK(uv_loop_configure(&loop, UV_LOOP_BLOCK_SIGNAL, SIGPROF)); #endif - ASSERT(0 == uv_timer_init(&loop, &timer_handle)); - ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 10, 0)); - ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT)); - ASSERT(0 == uv_loop_close(&loop)); + ASSERT_OK(uv_timer_init(&loop, &timer_handle)); + ASSERT_OK(uv_timer_start(&timer_handle, timer_cb, 10, 0)); + ASSERT_OK(uv_run(&loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_loop_close(&loop)); return 0; } diff --git a/test/test-loop-handles.c b/test/test-loop-handles.c index 05cb8466ca6..58368bf73ce 100644 --- a/test/test-loop-handles.c +++ b/test/test-loop-handles.c @@ -108,7 +108,7 @@ static int idle_2_is_active = 0; static void timer_cb(uv_timer_t* handle) { - ASSERT(handle == &timer_handle); + ASSERT_PTR_EQ(handle, &timer_handle); } @@ -116,7 +116,7 @@ static void idle_2_close_cb(uv_handle_t* handle) { fprintf(stderr, "%s", "IDLE_2_CLOSE_CB\n"); fflush(stderr); - ASSERT(handle == (uv_handle_t*)&idle_2_handle); + ASSERT_PTR_EQ(handle, (uv_handle_t*)&idle_2_handle); ASSERT(idle_2_is_active); @@ -129,7 +129,7 @@ static void idle_2_cb(uv_idle_t* handle) { fprintf(stderr, "%s", "IDLE_2_CB\n"); fflush(stderr); - ASSERT(handle == &idle_2_handle); + ASSERT_PTR_EQ(handle, &idle_2_handle); idle_2_cb_called++; @@ -144,14 +144,14 @@ static void idle_1_cb(uv_idle_t* handle) { fflush(stderr); ASSERT_NOT_NULL(handle); - ASSERT(idles_1_active > 0); + ASSERT_GT(idles_1_active, 0); /* Init idle_2 and make it active */ if (!idle_2_is_active && !uv_is_closing((uv_handle_t*)&idle_2_handle)) { r = uv_idle_init(uv_default_loop(), &idle_2_handle); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_idle_start(&idle_2_handle, idle_2_cb); - ASSERT(r == 0); + ASSERT_OK(r); idle_2_is_active = 1; idle_2_cb_started++; } @@ -160,7 +160,7 @@ static void idle_1_cb(uv_idle_t* handle) { if (idle_1_cb_called % 5 == 0) { r = uv_idle_stop((uv_idle_t*)handle); - ASSERT(r == 0); + ASSERT_OK(r); idles_1_active--; } } @@ -179,7 +179,7 @@ static void idle_1_close_cb(uv_handle_t* handle) { static void prepare_1_close_cb(uv_handle_t* handle) { fprintf(stderr, "%s", "PREPARE_1_CLOSE_CB"); fflush(stderr); - ASSERT(handle == (uv_handle_t*)&prepare_1_handle); + ASSERT_PTR_EQ(handle, (uv_handle_t*)&prepare_1_handle); prepare_1_close_cb_called++; } @@ -188,7 +188,7 @@ static void prepare_1_close_cb(uv_handle_t* handle) { static void check_close_cb(uv_handle_t* handle) { fprintf(stderr, "%s", "CHECK_CLOSE_CB\n"); fflush(stderr); - ASSERT(handle == (uv_handle_t*)&check_handle); + ASSERT_PTR_EQ(handle, (uv_handle_t*)&check_handle); check_close_cb_called++; } @@ -197,7 +197,7 @@ static void check_close_cb(uv_handle_t* handle) { static void prepare_2_close_cb(uv_handle_t* handle) { fprintf(stderr, "%s", "PREPARE_2_CLOSE_CB\n"); fflush(stderr); - ASSERT(handle == (uv_handle_t*)&prepare_2_handle); + ASSERT_PTR_EQ(handle, (uv_handle_t*)&prepare_2_handle); prepare_2_close_cb_called++; } @@ -208,13 +208,13 @@ static void check_cb(uv_check_t* handle) { fprintf(stderr, "%s", "CHECK_CB\n"); fflush(stderr); - ASSERT(handle == &check_handle); + ASSERT_PTR_EQ(handle, &check_handle); if (loop_iteration < ITERATIONS) { /* Make some idle watchers active */ for (i = 0; i < 1 + (loop_iteration % IDLE_COUNT); i++) { r = uv_idle_start(&idle_1_handles[i], idle_1_cb); - ASSERT(r == 0); + ASSERT_OK(r); idles_1_active++; } @@ -244,16 +244,16 @@ static void prepare_2_cb(uv_prepare_t* handle) { fprintf(stderr, "%s", "PREPARE_2_CB\n"); fflush(stderr); - ASSERT(handle == &prepare_2_handle); + ASSERT_PTR_EQ(handle, &prepare_2_handle); /* Prepare_2 gets started by prepare_1 when (loop_iteration % 2 == 0), and it * stops itself immediately. A started watcher is not queued until the next * round, so when this callback is made (loop_iteration % 2 == 0) cannot be * true. */ - ASSERT(loop_iteration % 2 != 0); + ASSERT_NE(0, loop_iteration % 2); r = uv_prepare_stop((uv_prepare_t*)handle); - ASSERT(r == 0); + ASSERT_OK(r); prepare_2_cb_called++; } @@ -264,11 +264,11 @@ static void prepare_1_cb(uv_prepare_t* handle) { fprintf(stderr, "%s", "PREPARE_1_CB\n"); fflush(stderr); - ASSERT(handle == &prepare_1_handle); + ASSERT_PTR_EQ(handle, &prepare_1_handle); if (loop_iteration % 2 == 0) { r = uv_prepare_start(&prepare_2_handle, prepare_2_cb); - ASSERT(r == 0); + ASSERT_OK(r); } prepare_1_cb_called++; @@ -283,23 +283,23 @@ TEST_IMPL(loop_handles) { int r; r = uv_prepare_init(uv_default_loop(), &prepare_1_handle); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_prepare_start(&prepare_1_handle, prepare_1_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_check_init(uv_default_loop(), &check_handle); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_check_start(&check_handle, check_cb); - ASSERT(r == 0); + ASSERT_OK(r); /* initialize only, prepare_2 is started by prepare_1_cb */ r = uv_prepare_init(uv_default_loop(), &prepare_2_handle); - ASSERT(r == 0); + ASSERT_OK(r); for (i = 0; i < IDLE_COUNT; i++) { /* initialize only, idle_1 handles are started by check_cb */ r = uv_idle_init(uv_default_loop(), &idle_1_handles[i]); - ASSERT(r == 0); + ASSERT_OK(r); } /* don't init or start idle_2, both is done by idle_1_cb */ @@ -307,31 +307,31 @@ TEST_IMPL(loop_handles) { /* The timer callback is there to keep the event loop polling unref it as it * is not supposed to keep the loop alive */ r = uv_timer_init(uv_default_loop(), &timer_handle); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&timer_handle, timer_cb, TIMEOUT, TIMEOUT); - ASSERT(r == 0); + ASSERT_OK(r); uv_unref((uv_handle_t*)&timer_handle); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(loop_iteration == ITERATIONS); + ASSERT_EQ(loop_iteration, ITERATIONS); - ASSERT(prepare_1_cb_called == ITERATIONS); - ASSERT(prepare_1_close_cb_called == 1); + ASSERT_EQ(prepare_1_cb_called, ITERATIONS); + ASSERT_EQ(1, prepare_1_close_cb_called); - ASSERT(prepare_2_cb_called == ITERATIONS / 2); - ASSERT(prepare_2_close_cb_called == 1); + ASSERT_EQ(prepare_2_cb_called, ITERATIONS / 2); + ASSERT_EQ(1, prepare_2_close_cb_called); - ASSERT(check_cb_called == ITERATIONS); - ASSERT(check_close_cb_called == 1); + ASSERT_EQ(check_cb_called, ITERATIONS); + ASSERT_EQ(1, check_close_cb_called); /* idle_1_cb should be called a lot */ - ASSERT(idle_1_close_cb_called == IDLE_COUNT); + ASSERT_EQ(idle_1_close_cb_called, IDLE_COUNT); - ASSERT(idle_2_close_cb_called == idle_2_cb_started); - ASSERT(idle_2_is_active == 0); + ASSERT_EQ(idle_2_close_cb_called, idle_2_cb_started); + ASSERT_OK(idle_2_is_active); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-loop-stop.c b/test/test-loop-stop.c index 14b8c11186c..a00a5b81ade 100644 --- a/test/test-loop-stop.c +++ b/test/test-loop-stop.c @@ -30,7 +30,7 @@ static int num_ticks = 10; static void prepare_cb(uv_prepare_t* handle) { - ASSERT(handle == &prepare_handle); + ASSERT_PTR_EQ(handle, &prepare_handle); prepare_called++; if (prepare_called == num_ticks) uv_prepare_stop(handle); @@ -38,7 +38,7 @@ static void prepare_cb(uv_prepare_t* handle) { static void timer_cb(uv_timer_t* handle) { - ASSERT(handle == &timer_handle); + ASSERT_PTR_EQ(handle, &timer_handle); timer_called++; if (timer_called == 1) uv_stop(uv_default_loop()); @@ -55,17 +55,29 @@ TEST_IMPL(loop_stop) { uv_timer_start(&timer_handle, timer_cb, 100, 100); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r != 0); - ASSERT(timer_called == 1); + ASSERT(r); + ASSERT_EQ(1, timer_called); r = uv_run(uv_default_loop(), UV_RUN_NOWAIT); - ASSERT(r != 0); - ASSERT(prepare_called > 1); + ASSERT(r); + ASSERT_GT(prepare_called, 1); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); - ASSERT(timer_called == 10); - ASSERT(prepare_called == 10); + ASSERT_OK(r); + ASSERT_EQ(10, timer_called); + ASSERT_EQ(10, prepare_called); + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} + + +TEST_IMPL(loop_stop_before_run) { + ASSERT_OK(uv_timer_init(uv_default_loop(), &timer_handle)); + ASSERT_OK(uv_timer_start(&timer_handle, (uv_timer_cb) abort, 0, 0)); + uv_stop(uv_default_loop()); + ASSERT_NE(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-loop-time.c b/test/test-loop-time.c index a2db42cceec..01e44ffbdad 100644 --- a/test/test-loop-time.c +++ b/test/test-loop-time.c @@ -28,9 +28,9 @@ TEST_IMPL(loop_update_time) { start = uv_now(uv_default_loop()); while (uv_now(uv_default_loop()) - start < 1000) - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_NOWAIT)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_NOWAIT)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -43,21 +43,27 @@ TEST_IMPL(loop_backend_timeout) { uv_timer_t timer; int r; + /* The default loop has some internal watchers to initialize. */ + loop->active_handles++; + r = uv_run(loop, UV_RUN_NOWAIT); + ASSERT_EQ(1, r); + loop->active_handles--; + ASSERT_OK(uv_loop_alive(loop)); + r = uv_timer_init(loop, &timer); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(!uv_loop_alive(loop)); - ASSERT(uv_backend_timeout(loop) == 0); + ASSERT_OK(uv_loop_alive(loop)); + ASSERT_OK(uv_backend_timeout(loop)); r = uv_timer_start(&timer, cb, 1000, 0); /* 1 sec */ - ASSERT(r == 0); - ASSERT(uv_backend_timeout(loop) > 100); /* 0.1 sec */ - ASSERT(uv_backend_timeout(loop) <= 1000); /* 1 sec */ + ASSERT_OK(r); + ASSERT_EQ(1000, uv_backend_timeout(loop)); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); - ASSERT(uv_backend_timeout(loop) == 0); + ASSERT_OK(r); + ASSERT_OK(uv_backend_timeout(loop)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-metrics.c b/test/test-metrics.c index f527494470e..361fcef5ced 100644 --- a/test/test-metrics.c +++ b/test/test-metrics.c @@ -25,6 +25,17 @@ #define UV_NS_TO_MS 1000000 +typedef struct { + uv_fs_t open_req; + uv_fs_t write_req; + uv_fs_t close_req; +} fs_reqs_t; + +static uint64_t last_events_count; +static char test_buf[] = "test-buffer\n"; +static fs_reqs_t fs_reqs; +static int pool_events_counter; + static void timer_spin_cb(uv_timer_t* handle) { uint64_t t; @@ -37,6 +48,9 @@ static void timer_spin_cb(uv_timer_t* handle) { TEST_IMPL(metrics_idle_time) { +#if defined(__OpenBSD__) + RETURN_SKIP("Test does not currently work in OpenBSD"); +#endif const uint64_t timeout = 1000; uv_timer_t timer; uint64_t idle_time; @@ -45,20 +59,20 @@ TEST_IMPL(metrics_idle_time) { cntr = 0; timer.data = &cntr; - ASSERT_EQ(0, uv_loop_configure(uv_default_loop(), UV_METRICS_IDLE_TIME)); - ASSERT_EQ(0, uv_timer_init(uv_default_loop(), &timer)); - ASSERT_EQ(0, uv_timer_start(&timer, timer_spin_cb, timeout, 0)); + ASSERT_OK(uv_loop_configure(uv_default_loop(), UV_METRICS_IDLE_TIME)); + ASSERT_OK(uv_timer_init(uv_default_loop(), &timer)); + ASSERT_OK(uv_timer_start(&timer, timer_spin_cb, timeout, 0)); - ASSERT_EQ(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT_GT(cntr, 0); idle_time = uv_metrics_idle_time(uv_default_loop()); /* Permissive check that the idle time matches within the timeout ±500 ms. */ - ASSERT((idle_time <= (timeout + 500) * UV_NS_TO_MS) && - (idle_time >= (timeout - 500) * UV_NS_TO_MS)); + ASSERT_LE(idle_time, (timeout + 500) * UV_NS_TO_MS); + ASSERT_GE(idle_time, (timeout - 500) * UV_NS_TO_MS); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -73,12 +87,12 @@ static void metrics_routine_cb(void* arg) { cntr = 0; timer.data = &cntr; - ASSERT_EQ(0, uv_loop_init(&loop)); - ASSERT_EQ(0, uv_loop_configure(&loop, UV_METRICS_IDLE_TIME)); - ASSERT_EQ(0, uv_timer_init(&loop, &timer)); - ASSERT_EQ(0, uv_timer_start(&timer, timer_spin_cb, timeout, 0)); + ASSERT_OK(uv_loop_init(&loop)); + ASSERT_OK(uv_loop_configure(&loop, UV_METRICS_IDLE_TIME)); + ASSERT_OK(uv_timer_init(&loop, &timer)); + ASSERT_OK(uv_timer_start(&timer, timer_spin_cb, timeout, 0)); - ASSERT_EQ(0, uv_run(&loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(&loop, UV_RUN_DEFAULT)); ASSERT_GT(cntr, 0); idle_time = uv_metrics_idle_time(&loop); @@ -90,7 +104,7 @@ static void metrics_routine_cb(void* arg) { ASSERT_GE(idle_time, (timeout - 500) * UV_NS_TO_MS); close_loop(&loop); - ASSERT_EQ(0, uv_loop_close(&loop)); + ASSERT_OK(uv_loop_close(&loop)); } @@ -99,7 +113,7 @@ TEST_IMPL(metrics_idle_time_thread) { int i; for (i = 0; i < 5; i++) { - ASSERT_EQ(0, uv_thread_create(&threads[i], metrics_routine_cb, NULL)); + ASSERT_OK(uv_thread_create(&threads[i], metrics_routine_cb, NULL)); } for (i = 0; i < 5; i++) { @@ -116,20 +130,261 @@ static void timer_noop_cb(uv_timer_t* handle) { TEST_IMPL(metrics_idle_time_zero) { + uv_metrics_t metrics; uv_timer_t timer; int cntr; cntr = 0; timer.data = &cntr; - ASSERT_EQ(0, uv_loop_configure(uv_default_loop(), UV_METRICS_IDLE_TIME)); - ASSERT_EQ(0, uv_timer_init(uv_default_loop(), &timer)); - ASSERT_EQ(0, uv_timer_start(&timer, timer_noop_cb, 0, 0)); + ASSERT_OK(uv_loop_configure(uv_default_loop(), UV_METRICS_IDLE_TIME)); + ASSERT_OK(uv_timer_init(uv_default_loop(), &timer)); + ASSERT_OK(uv_timer_start(&timer, timer_noop_cb, 0, 0)); - ASSERT_EQ(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT_GT(cntr, 0); - ASSERT_EQ(0, uv_metrics_idle_time(uv_default_loop())); + ASSERT_OK(uv_metrics_idle_time(uv_default_loop())); + + ASSERT_OK(uv_metrics_info(uv_default_loop(), &metrics)); + ASSERT_UINT64_EQ(cntr, metrics.loop_count); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} + + +static void close_cb(uv_fs_t* req) { + uv_metrics_t metrics; + + ASSERT_OK(uv_metrics_info(uv_default_loop(), &metrics)); + ASSERT_UINT64_EQ(3, metrics.loop_count); + ASSERT_UINT64_GT(metrics.events, last_events_count); + + uv_fs_req_cleanup(req); + last_events_count = metrics.events; +} + + +static void write_cb(uv_fs_t* req) { + uv_metrics_t metrics; + + ASSERT_OK(uv_metrics_info(uv_default_loop(), &metrics)); + ASSERT_UINT64_EQ(2, metrics.loop_count); + ASSERT_UINT64_GT(metrics.events, last_events_count); + ASSERT_EQ(req->result, sizeof(test_buf)); + + uv_fs_req_cleanup(req); + last_events_count = metrics.events; + + ASSERT_OK(uv_fs_close(uv_default_loop(), + &fs_reqs.close_req, + fs_reqs.open_req.result, + close_cb)); +} + + +static void create_cb(uv_fs_t* req) { + uv_metrics_t metrics; + + ASSERT_OK(uv_metrics_info(uv_default_loop(), &metrics)); + /* Event count here is still 0 so not going to check. */ + ASSERT_UINT64_EQ(1, metrics.loop_count); + ASSERT_GE(req->result, 0); + + uv_fs_req_cleanup(req); + last_events_count = metrics.events; + + uv_buf_t iov = uv_buf_init(test_buf, sizeof(test_buf)); + ASSERT_OK(uv_fs_write(uv_default_loop(), + &fs_reqs.write_req, + req->result, + &iov, + 1, + 0, + write_cb)); +} + + +static void prepare_cb(uv_prepare_t* handle) { + uv_metrics_t metrics; + + uv_prepare_stop(handle); + + ASSERT_OK(uv_metrics_info(uv_default_loop(), &metrics)); + ASSERT_UINT64_EQ(0, metrics.loop_count); + ASSERT_UINT64_EQ(0, metrics.events); + + ASSERT_OK(uv_fs_open(uv_default_loop(), + &fs_reqs.open_req, + "test_file", + UV_FS_O_WRONLY | UV_FS_O_CREAT, S_IRUSR | S_IWUSR, + create_cb)); +} + + +TEST_IMPL(metrics_info_check) { + uv_fs_t unlink_req; + uv_prepare_t prepare; + + uv_fs_unlink(NULL, &unlink_req, "test_file", NULL); + uv_fs_req_cleanup(&unlink_req); + + ASSERT_OK(uv_prepare_init(uv_default_loop(), &prepare)); + ASSERT_OK(uv_prepare_start(&prepare, prepare_cb)); + + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + uv_fs_unlink(NULL, &unlink_req, "test_file", NULL); + uv_fs_req_cleanup(&unlink_req); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} + + +static void fs_prepare_cb(uv_prepare_t* handle) { + uv_metrics_t metrics; + + ASSERT_OK(uv_metrics_info(uv_default_loop(), &metrics)); + + if (pool_events_counter == 1) + ASSERT_EQ(metrics.events, metrics.events_waiting); + + if (pool_events_counter < 7) + return; + + uv_prepare_stop(handle); + pool_events_counter = -42; +} + + +static void fs_stat_cb(uv_fs_t* req) { + uv_fs_req_cleanup(req); + pool_events_counter++; +} + + +static void fs_work_cb(uv_work_t* req) { +} + + +static void fs_after_work_cb(uv_work_t* req, int status) { + free(req); + pool_events_counter++; +} + + +static void fs_write_cb(uv_fs_t* req) { + uv_work_t* work1 = malloc(sizeof(*work1)); + uv_work_t* work2 = malloc(sizeof(*work2)); + pool_events_counter++; + + uv_fs_req_cleanup(req); + + ASSERT_OK(uv_queue_work(uv_default_loop(), + work1, + fs_work_cb, + fs_after_work_cb)); + ASSERT_OK(uv_queue_work(uv_default_loop(), + work2, + fs_work_cb, + fs_after_work_cb)); +} + + +static void fs_random_cb(uv_random_t* req, int status, void* buf, size_t len) { + pool_events_counter++; +} + + +static void fs_addrinfo_cb(uv_getaddrinfo_t* req, + int status, + struct addrinfo* res) { + uv_freeaddrinfo(req->addrinfo); + pool_events_counter++; +} + - MAKE_VALGRIND_HAPPY(); +TEST_IMPL(metrics_pool_events) { + uv_buf_t iov; + uv_fs_t open_req; + uv_fs_t stat1_req; + uv_fs_t stat2_req; + uv_fs_t unlink_req; + uv_fs_t write_req; + uv_getaddrinfo_t addrinfo_req; + uv_metrics_t metrics; + uv_prepare_t prepare; + uv_random_t random_req; + int fd; + char rdata; + + ASSERT_OK(uv_loop_configure(uv_default_loop(), UV_METRICS_IDLE_TIME)); + + uv_fs_unlink(NULL, &unlink_req, "test_file", NULL); + uv_fs_req_cleanup(&unlink_req); + + ASSERT_OK(uv_prepare_init(uv_default_loop(), &prepare)); + ASSERT_OK(uv_prepare_start(&prepare, fs_prepare_cb)); + + pool_events_counter = 0; + fd = uv_fs_open(NULL, + &open_req, "test_file", UV_FS_O_WRONLY | UV_FS_O_CREAT, + S_IRUSR | S_IWUSR, + NULL); + ASSERT_GT(fd, 0); + uv_fs_req_cleanup(&open_req); + + iov = uv_buf_init(test_buf, sizeof(test_buf)); + ASSERT_OK(uv_fs_write(uv_default_loop(), + &write_req, + fd, + &iov, + 1, + 0, + fs_write_cb)); + ASSERT_OK(uv_fs_stat(uv_default_loop(), + &stat1_req, + "test_file", + fs_stat_cb)); + ASSERT_OK(uv_fs_stat(uv_default_loop(), + &stat2_req, + "test_file", + fs_stat_cb)); + ASSERT_OK(uv_random(uv_default_loop(), + &random_req, + &rdata, + 1, + 0, + fs_random_cb)); + ASSERT_OK(uv_getaddrinfo(uv_default_loop(), + &addrinfo_req, + fs_addrinfo_cb, + "example.invalid", + NULL, + NULL)); + + /* Sleep for a moment to hopefully force the events to complete before + * entering the event loop. */ + uv_sleep(100); + + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT_OK(uv_metrics_info(uv_default_loop(), &metrics)); + /* It's possible for uv__work_done() to execute one extra time even though the + * QUEUE has already been cleared out. This has to do with the way we use an + * uv_async to tell the event loop thread to process the worker pool QUEUE. */ + ASSERT_GE(metrics.events, 7); + /* It's possible one of the other events also got stuck in the event queue, so + * check GE instead of EQ. Reason for 4 instead of 5 is because the call to + * uv_getaddrinfo() is racey and slow. So can't guarantee that it'll always + * execute before sleep completes. */ + ASSERT_GE(metrics.events_waiting, 4); + ASSERT_EQ(pool_events_counter, -42); + + uv_fs_unlink(NULL, &unlink_req, "test_file", NULL); + uv_fs_req_cleanup(&unlink_req); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-multiple-listen.c b/test/test-multiple-listen.c index 0b2851411a8..47f35f6c058 100644 --- a/test/test-multiple-listen.c +++ b/test/test-multiple-listen.c @@ -38,7 +38,7 @@ static void close_cb(uv_handle_t* handle) { static void connection_cb(uv_stream_t* tcp, int status) { - ASSERT(status == 0); + ASSERT_OK(status); uv_close((uv_handle_t*)&server, close_cb); connection_cb_called++; } @@ -48,25 +48,25 @@ static void start_server(void) { struct sockaddr_in addr; int r; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&server, 128, connection_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&server, 128, connection_cb); - ASSERT(r == 0); + ASSERT_OK(r); } static void connect_cb(uv_connect_t* req, int status) { ASSERT_NOT_NULL(req); - ASSERT(status == 0); + ASSERT_OK(status); free(req); uv_close((uv_handle_t*)&client, close_cb); connect_cb_called++; @@ -78,17 +78,17 @@ static void client_connect(void) { uv_connect_t* connect_req = malloc(sizeof *connect_req); int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); ASSERT_NOT_NULL(connect_req); r = uv_tcp_init(uv_default_loop(), &client); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_connect(connect_req, &client, (const struct sockaddr*) &addr, connect_cb); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -100,10 +100,10 @@ TEST_IMPL(multiple_listen) { uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(connection_cb_called == 1); - ASSERT(connect_cb_called == 1); - ASSERT(close_cb_called == 2); + ASSERT_EQ(1, connection_cb_called); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(2, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-mutexes.c b/test/test-mutexes.c index 975222ca122..ca3377324b2 100644 --- a/test/test-mutexes.c +++ b/test/test-mutexes.c @@ -40,7 +40,7 @@ TEST_IMPL(thread_mutex) { int r; r = uv_mutex_init(&mutex); - ASSERT(r == 0); + ASSERT_OK(r); uv_mutex_lock(&mutex); uv_mutex_unlock(&mutex); @@ -55,11 +55,11 @@ TEST_IMPL(thread_mutex_recursive) { int r; r = uv_mutex_init_recursive(&mutex); - ASSERT(r == 0); + ASSERT_OK(r); uv_mutex_lock(&mutex); uv_mutex_lock(&mutex); - ASSERT(0 == uv_mutex_trylock(&mutex)); + ASSERT_OK(uv_mutex_trylock(&mutex)); uv_mutex_unlock(&mutex); uv_mutex_unlock(&mutex); @@ -75,7 +75,7 @@ TEST_IMPL(thread_rwlock) { int r; r = uv_rwlock_init(&rwlock); - ASSERT(r == 0); + ASSERT_OK(r); uv_rwlock_rdlock(&rwlock); uv_rwlock_rdunlock(&rwlock); @@ -101,7 +101,7 @@ static void synchronize(void) { synchronize_nowait(); /* Wait for the other thread. Guard against spurious wakeups. */ for (current = step; current == step; uv_cond_wait(&condvar, &mutex)); - ASSERT(step == current + 1); + ASSERT_EQ(step, current + 1); } @@ -111,23 +111,23 @@ static void thread_rwlock_trylock_peer(void* unused) { uv_mutex_lock(&mutex); /* Write lock held by other thread. */ - ASSERT(UV_EBUSY == uv_rwlock_tryrdlock(&rwlock)); - ASSERT(UV_EBUSY == uv_rwlock_trywrlock(&rwlock)); + ASSERT_EQ(UV_EBUSY, uv_rwlock_tryrdlock(&rwlock)); + ASSERT_EQ(UV_EBUSY, uv_rwlock_trywrlock(&rwlock)); synchronize(); /* Read lock held by other thread. */ - ASSERT(0 == uv_rwlock_tryrdlock(&rwlock)); + ASSERT_OK(uv_rwlock_tryrdlock(&rwlock)); uv_rwlock_rdunlock(&rwlock); - ASSERT(UV_EBUSY == uv_rwlock_trywrlock(&rwlock)); + ASSERT_EQ(UV_EBUSY, uv_rwlock_trywrlock(&rwlock)); synchronize(); /* Acquire write lock. */ - ASSERT(0 == uv_rwlock_trywrlock(&rwlock)); + ASSERT_OK(uv_rwlock_trywrlock(&rwlock)); synchronize(); /* Release write lock and acquire read lock. */ uv_rwlock_wrunlock(&rwlock); - ASSERT(0 == uv_rwlock_tryrdlock(&rwlock)); + ASSERT_OK(uv_rwlock_tryrdlock(&rwlock)); synchronize(); uv_rwlock_rdunlock(&rwlock); @@ -139,22 +139,22 @@ static void thread_rwlock_trylock_peer(void* unused) { TEST_IMPL(thread_rwlock_trylock) { uv_thread_t thread; - ASSERT(0 == uv_cond_init(&condvar)); - ASSERT(0 == uv_mutex_init(&mutex)); - ASSERT(0 == uv_rwlock_init(&rwlock)); + ASSERT_OK(uv_cond_init(&condvar)); + ASSERT_OK(uv_mutex_init(&mutex)); + ASSERT_OK(uv_rwlock_init(&rwlock)); uv_mutex_lock(&mutex); - ASSERT(0 == uv_thread_create(&thread, thread_rwlock_trylock_peer, NULL)); + ASSERT_OK(uv_thread_create(&thread, thread_rwlock_trylock_peer, NULL)); /* Hold write lock. */ - ASSERT(0 == uv_rwlock_trywrlock(&rwlock)); + ASSERT_OK(uv_rwlock_trywrlock(&rwlock)); synchronize(); /* Releases the mutex to the other thread. */ /* Release write lock and acquire read lock. Pthreads doesn't support * the notion of upgrading or downgrading rwlocks, so neither do we. */ uv_rwlock_wrunlock(&rwlock); - ASSERT(0 == uv_rwlock_tryrdlock(&rwlock)); + ASSERT_OK(uv_rwlock_tryrdlock(&rwlock)); synchronize(); /* Release read lock. */ @@ -162,17 +162,17 @@ TEST_IMPL(thread_rwlock_trylock) { synchronize(); /* Write lock held by other thread. */ - ASSERT(UV_EBUSY == uv_rwlock_tryrdlock(&rwlock)); - ASSERT(UV_EBUSY == uv_rwlock_trywrlock(&rwlock)); + ASSERT_EQ(UV_EBUSY, uv_rwlock_tryrdlock(&rwlock)); + ASSERT_EQ(UV_EBUSY, uv_rwlock_trywrlock(&rwlock)); synchronize(); /* Read lock held by other thread. */ - ASSERT(0 == uv_rwlock_tryrdlock(&rwlock)); + ASSERT_OK(uv_rwlock_tryrdlock(&rwlock)); uv_rwlock_rdunlock(&rwlock); - ASSERT(UV_EBUSY == uv_rwlock_trywrlock(&rwlock)); + ASSERT_EQ(UV_EBUSY, uv_rwlock_trywrlock(&rwlock)); synchronize(); - ASSERT(0 == uv_thread_join(&thread)); + ASSERT_OK(uv_thread_join(&thread)); uv_rwlock_destroy(&rwlock); uv_mutex_unlock(&mutex); uv_mutex_destroy(&mutex); diff --git a/test/test-not-readable-nor-writable-on-read-error.c b/test/test-not-readable-nor-writable-on-read-error.c index ae951e39893..2c45a2da2e8 100644 --- a/test/test-not-readable-nor-writable-on-read-error.c +++ b/test/test-not-readable-nor-writable-on-read-error.c @@ -35,7 +35,7 @@ static int close_cb_called; static void write_cb(uv_write_t* req, int status) { write_cb_called++; - ASSERT(status == 0); + ASSERT_OK(status); } static void alloc_cb(uv_handle_t* handle, @@ -54,8 +54,8 @@ static void read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { read_cb_called++; ASSERT((nread < 0) && (nread != UV_EOF)); - ASSERT(0 == uv_is_writable(handle)); - ASSERT(0 == uv_is_readable(handle)); + ASSERT_OK(uv_is_writable(handle)); + ASSERT_OK(uv_is_readable(handle)); uv_close((uv_handle_t*) handle, close_cb); } @@ -65,10 +65,10 @@ static void connect_cb(uv_connect_t* req, int status) { uv_buf_t reset_me; connect_cb_called++; - ASSERT(status == 0); + ASSERT_OK(status); r = uv_read_start((uv_stream_t*) &tcp_client, alloc_cb, read_cb); - ASSERT(r == 0); + ASSERT_OK(r); reset_me = uv_buf_init(reset_me_cmd, sizeof(reset_me_cmd)); @@ -78,27 +78,27 @@ static void connect_cb(uv_connect_t* req, int status) { 1, write_cb); - ASSERT(r == 0); + ASSERT_OK(r); } TEST_IMPL(not_readable_nor_writable_on_read_error) { struct sockaddr_in sa; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &sa)); - ASSERT(0 == uv_loop_init(&loop)); - ASSERT(0 == uv_tcp_init(&loop, &tcp_client)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &sa)); + ASSERT_OK(uv_loop_init(&loop)); + ASSERT_OK(uv_tcp_init(&loop, &tcp_client)); - ASSERT(0 == uv_tcp_connect(&connect_req, - &tcp_client, - (const struct sockaddr*) &sa, - connect_cb)); + ASSERT_OK(uv_tcp_connect(&connect_req, + &tcp_client, + (const struct sockaddr*) &sa, + connect_cb)); - ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(&loop, UV_RUN_DEFAULT)); - ASSERT(connect_cb_called == 1); - ASSERT(read_cb_called == 1); - ASSERT(write_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(1, read_cb_called); + ASSERT_EQ(1, write_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(&loop); return 0; } diff --git a/test/test-not-writable-after-shutdown.c b/test/test-not-writable-after-shutdown.c index 9cd93703cea..066f8bbafeb 100644 --- a/test/test-not-writable-after-shutdown.c +++ b/test/test-not-writable-after-shutdown.c @@ -34,12 +34,12 @@ static void shutdown_cb(uv_shutdown_t* req, int status) { static void connect_cb(uv_connect_t* req, int status) { int r; - ASSERT(status == 0); + ASSERT_OK(status); r = uv_shutdown(&shutdown_req, req->handle, shutdown_cb); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(0 == uv_is_writable(req->handle)); + ASSERT_OK(uv_is_writable(req->handle)); } TEST_IMPL(not_writable_after_shutdown) { @@ -49,21 +49,21 @@ TEST_IMPL(not_writable_after_shutdown) { uv_tcp_t socket; uv_connect_t connect_req; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); loop = uv_default_loop(); r = uv_tcp_init(loop, &socket); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_connect(&connect_req, &socket, (const struct sockaddr*) &addr, connect_cb); - ASSERT(r == 0); + ASSERT_OK(r); - r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-osx-select.c b/test/test-osx-select.c index a0afda9181e..44e2c6d8dd2 100644 --- a/test/test-osx-select.c +++ b/test/test-osx-select.c @@ -62,7 +62,7 @@ TEST_IMPL(osx_select) { } r = uv_tty_init(uv_default_loop(), &tty, fd, 1); - ASSERT(r == 0); + ASSERT_OK(r); uv_read_start((uv_stream_t*) &tty, alloc_cb, read_cb); @@ -72,14 +72,14 @@ TEST_IMPL(osx_select) { "feel pretty happy\n"; for (i = 0, len = strlen(str); i < len; i++) { r = ioctl(fd, TIOCSTI, str + i); - ASSERT(r == 0); + ASSERT_OK(r); } uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(read_count == 3); + ASSERT_EQ(3, read_count); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -97,13 +97,13 @@ TEST_IMPL(osx_select_many_fds) { TEST_FILE_LIMIT(ARRAY_SIZE(tcps) + 100); r = uv_ip4_addr("127.0.0.1", 0, &addr); - ASSERT(r == 0); + ASSERT_OK(r); for (i = 0; i < ARRAY_SIZE(tcps); i++) { r = uv_tcp_init(uv_default_loop(), &tcps[i]); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&tcps[i], (const struct sockaddr *) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); uv_unref((uv_handle_t*) &tcps[i]); } @@ -115,10 +115,10 @@ TEST_IMPL(osx_select_many_fds) { } r = uv_tty_init(uv_default_loop(), &tty, fd, 1); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_start((uv_stream_t*) &tty, alloc_cb, read_cb); - ASSERT(r == 0); + ASSERT_OK(r); /* Emulate user-input */ str = "got some input\n" @@ -126,14 +126,14 @@ TEST_IMPL(osx_select_many_fds) { "feel pretty happy\n"; for (i = 0, len = strlen(str); i < len; i++) { r = ioctl(fd, TIOCSTI, str + i); - ASSERT(r == 0); + ASSERT_OK(r); } uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(read_count == 3); + ASSERT_EQ(3, read_count); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-ping-pong.c b/test/test-ping-pong.c index c598587d112..cd9fbf62bba 100644 --- a/test/test-ping-pong.c +++ b/test/test-ping-pong.c @@ -83,7 +83,7 @@ static void pinger_on_close(uv_handle_t* handle) { static void pinger_after_write(uv_write_t* req, int status) { - ASSERT_EQ(status, 0); + ASSERT_OK(status); free(req->data); free(req); } @@ -112,7 +112,7 @@ static void pinger_write_ping(pinger_t* pinger) { req = malloc(sizeof(*req)); ASSERT_NOT_NULL(req); req->data = NULL; - ASSERT_EQ(0, uv_write(req, stream, bufs, nbufs, pinger_after_write)); + ASSERT_OK(uv_write(req, stream, bufs, nbufs, pinger_after_write)); puts("PING"); } @@ -188,7 +188,7 @@ static void ponger_read_cb(uv_stream_t* stream, req = malloc(sizeof(*req)); ASSERT_NOT_NULL(req); req->data = buf->base; - ASSERT_EQ(0, uv_write(req, stream, &writebuf, 1, pinger_after_write)); + ASSERT_OK(uv_write(req, stream, &writebuf, 1, pinger_after_write)); } @@ -197,17 +197,17 @@ static void pinger_on_connect(uv_connect_t* req, int status) { pinger_on_connect_count++; - ASSERT_EQ(status, 0); + ASSERT_OK(status); ASSERT_EQ(1, uv_is_readable(req->handle)); ASSERT_EQ(1, uv_is_writable(req->handle)); - ASSERT_EQ(0, uv_is_closing((uv_handle_t *) req->handle)); + ASSERT_OK(uv_is_closing((uv_handle_t *) req->handle)); pinger_write_ping(pinger); - ASSERT_EQ(0, uv_read_start((uv_stream_t*) req->handle, - alloc_cb, - pinger_read_cb)); + ASSERT_OK(uv_read_start((uv_stream_t*) req->handle, + alloc_cb, + pinger_read_cb)); } @@ -218,7 +218,7 @@ static void tcp_pinger_v6_new(int vectored_writes) { pinger_t* pinger; - ASSERT_EQ(0, uv_ip6_addr("::1", TEST_PORT, &server_addr)); + ASSERT_OK(uv_ip6_addr("::1", TEST_PORT, &server_addr)); pinger = malloc(sizeof(*pinger)); ASSERT_NOT_NULL(pinger); pinger->vectored_writes = vectored_writes; @@ -229,7 +229,7 @@ static void tcp_pinger_v6_new(int vectored_writes) { /* Try to connect to the server and do NUM_PINGS ping-pongs. */ r = uv_tcp_init(uv_default_loop(), &pinger->stream.tcp); pinger->stream.tcp.data = pinger; - ASSERT_EQ(0, r); + ASSERT_OK(r); /* We are never doing multiple reads/connects at a time anyway, so these * handles can be pre-initialized. */ @@ -237,10 +237,10 @@ static void tcp_pinger_v6_new(int vectored_writes) { &pinger->stream.tcp, (const struct sockaddr*) &server_addr, pinger_on_connect); - ASSERT_EQ(0, r); + ASSERT_OK(r); /* Synchronous connect callbacks are not allowed. */ - ASSERT_EQ(pinger_on_connect_count, 0); + ASSERT_OK(pinger_on_connect_count); } @@ -249,7 +249,7 @@ static void tcp_pinger_new(int vectored_writes) { struct sockaddr_in server_addr; pinger_t* pinger; - ASSERT_EQ(0, uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); pinger = malloc(sizeof(*pinger)); ASSERT_NOT_NULL(pinger); pinger->vectored_writes = vectored_writes; @@ -260,7 +260,7 @@ static void tcp_pinger_new(int vectored_writes) { /* Try to connect to the server and do NUM_PINGS ping-pongs. */ r = uv_tcp_init(uv_default_loop(), &pinger->stream.tcp); pinger->stream.tcp.data = pinger; - ASSERT_EQ(0, r); + ASSERT_OK(r); /* We are never doing multiple reads/connects at a time anyway, so these * handles can be pre-initialized. */ @@ -268,10 +268,10 @@ static void tcp_pinger_new(int vectored_writes) { &pinger->stream.tcp, (const struct sockaddr*) &server_addr, pinger_on_connect); - ASSERT_EQ(0, r); + ASSERT_OK(r); /* Synchronous connect callbacks are not allowed. */ - ASSERT_EQ(pinger_on_connect_count, 0); + ASSERT_OK(pinger_on_connect_count); } @@ -289,7 +289,7 @@ static void pipe_pinger_new(int vectored_writes) { /* Try to connect to the server and do NUM_PINGS ping-pongs. */ r = uv_pipe_init(uv_default_loop(), &pinger->stream.pipe, 0); pinger->stream.pipe.data = pinger; - ASSERT_EQ(0, r); + ASSERT_OK(r); /* We are never doing multiple reads/connects at a time anyway, so these * handles can be pre-initialized. */ @@ -297,7 +297,7 @@ static void pipe_pinger_new(int vectored_writes) { pinger_on_connect); /* Synchronous connect callbacks are not allowed. */ - ASSERT_EQ(pinger_on_connect_count, 0); + ASSERT_OK(pinger_on_connect_count); } @@ -315,31 +315,31 @@ static void socketpair_pinger_new(int vectored_writes) { /* Try to make a socketpair and do NUM_PINGS ping-pongs. */ (void)uv_default_loop(); /* ensure WSAStartup has been performed */ - ASSERT_EQ(0, uv_socketpair(SOCK_STREAM, 0, fds, UV_NONBLOCK_PIPE, UV_NONBLOCK_PIPE)); + ASSERT_OK(uv_socketpair(SOCK_STREAM, 0, fds, UV_NONBLOCK_PIPE, UV_NONBLOCK_PIPE)); #ifndef _WIN32 /* On Windows, this is actually a UV_TCP, but libuv doesn't detect that. */ ASSERT_EQ(uv_guess_handle((uv_file) fds[0]), UV_NAMED_PIPE); ASSERT_EQ(uv_guess_handle((uv_file) fds[1]), UV_NAMED_PIPE); #endif - ASSERT_EQ(0, uv_tcp_init(uv_default_loop(), &pinger->stream.tcp)); + ASSERT_OK(uv_tcp_init(uv_default_loop(), &pinger->stream.tcp)); pinger->stream.pipe.data = pinger; - ASSERT_EQ(0, uv_tcp_open(&pinger->stream.tcp, fds[1])); + ASSERT_OK(uv_tcp_open(&pinger->stream.tcp, fds[1])); ponger = malloc(sizeof(*ponger)); ASSERT_NOT_NULL(ponger); ponger->data = NULL; - ASSERT_EQ(0, uv_tcp_init(uv_default_loop(), ponger)); - ASSERT_EQ(0, uv_tcp_open(ponger, fds[0])); + ASSERT_OK(uv_tcp_init(uv_default_loop(), ponger)); + ASSERT_OK(uv_tcp_open(ponger, fds[0])); pinger_write_ping(pinger); - ASSERT_EQ(0, uv_read_start((uv_stream_t*) &pinger->stream.tcp, - alloc_cb, - pinger_read_cb)); - ASSERT_EQ(0, uv_read_start((uv_stream_t*) ponger, - alloc_cb, - ponger_read_cb)); + ASSERT_OK(uv_read_start((uv_stream_t*) &pinger->stream.tcp, + alloc_cb, + pinger_read_cb)); + ASSERT_OK(uv_read_start((uv_stream_t*) ponger, + alloc_cb, + ponger_read_cb)); } @@ -349,14 +349,14 @@ static void pipe2_pinger_new(int vectored_writes) { uv_pipe_t* ponger; /* Try to make a pipe and do NUM_PINGS pings. */ - ASSERT_EQ(0, uv_pipe(fds, UV_NONBLOCK_PIPE, UV_NONBLOCK_PIPE)); + ASSERT_OK(uv_pipe(fds, UV_NONBLOCK_PIPE, UV_NONBLOCK_PIPE)); ASSERT_EQ(uv_guess_handle(fds[0]), UV_NAMED_PIPE); ASSERT_EQ(uv_guess_handle(fds[1]), UV_NAMED_PIPE); ponger = malloc(sizeof(*ponger)); ASSERT_NOT_NULL(ponger); - ASSERT_EQ(0, uv_pipe_init(uv_default_loop(), ponger, 0)); - ASSERT_EQ(0, uv_pipe_open(ponger, fds[0])); + ASSERT_OK(uv_pipe_init(uv_default_loop(), ponger, 0)); + ASSERT_OK(uv_pipe_open(ponger, fds[0])); pinger = malloc(sizeof(*pinger)); ASSERT_NOT_NULL(pinger); @@ -364,21 +364,21 @@ static void pipe2_pinger_new(int vectored_writes) { pinger->state = 0; pinger->pongs = 0; pinger->pong = PING; - ASSERT_EQ(0, uv_pipe_init(uv_default_loop(), &pinger->stream.pipe, 0)); - ASSERT_EQ(0, uv_pipe_open(&pinger->stream.pipe, fds[1])); + ASSERT_OK(uv_pipe_init(uv_default_loop(), &pinger->stream.pipe, 0)); + ASSERT_OK(uv_pipe_open(&pinger->stream.pipe, fds[1])); pinger->stream.pipe.data = pinger; /* record for close_cb */ ponger->data = pinger; /* record for read_cb */ pinger_write_ping(pinger); - ASSERT_EQ(0, uv_read_start((uv_stream_t*) ponger, alloc_cb, pinger_read_cb)); + ASSERT_OK(uv_read_start((uv_stream_t*) ponger, alloc_cb, pinger_read_cb)); } static int run_ping_pong_test(void) { uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT_EQ(completed_pingers, 1); + ASSERT_EQ(1, completed_pingers); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-pipe-bind-error.c b/test/test-pipe-bind-error.c index ce35052f537..16164a7ee90 100644 --- a/test/test-pipe-bind-error.c +++ b/test/test-pipe-bind-error.c @@ -33,6 +33,7 @@ static int close_cb_called = 0; +static int connect_cb_called = 0; static void close_cb(uv_handle_t* handle) { @@ -46,28 +47,28 @@ TEST_IMPL(pipe_bind_error_addrinuse) { int r; r = uv_pipe_init(uv_default_loop(), &server1, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_bind(&server1, TEST_PIPENAME); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_init(uv_default_loop(), &server2, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_bind(&server2, TEST_PIPENAME); - ASSERT(r == UV_EADDRINUSE); + ASSERT_EQ(r, UV_EADDRINUSE); r = uv_listen((uv_stream_t*)&server1, SOMAXCONN, NULL); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&server2, SOMAXCONN, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_close((uv_handle_t*)&server1, close_cb); uv_close((uv_handle_t*)&server2, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 2); + ASSERT_EQ(2, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -77,18 +78,18 @@ TEST_IMPL(pipe_bind_error_addrnotavail) { int r; r = uv_pipe_init(uv_default_loop(), &server, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_bind(&server, BAD_PIPENAME); - ASSERT(r == UV_EACCES); + ASSERT_EQ(r, UV_EACCES); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -98,19 +99,19 @@ TEST_IMPL(pipe_bind_error_inval) { int r; r = uv_pipe_init(uv_default_loop(), &server, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_bind(&server, TEST_PIPENAME); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_bind(&server, TEST_PIPENAME_2); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -123,17 +124,92 @@ TEST_IMPL(pipe_listen_without_bind) { int r; r = uv_pipe_init(uv_default_loop(), &server, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&server, SOMAXCONN, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} + +TEST_IMPL(pipe_bind_or_listen_error_after_close) { + uv_pipe_t server; + + ASSERT_OK(uv_pipe_init(uv_default_loop(), &server, 0)); + uv_close((uv_handle_t*) &server, NULL); + + ASSERT_EQ(uv_pipe_bind(&server, TEST_PIPENAME), UV_EINVAL); + + ASSERT_EQ(uv_listen((uv_stream_t*) &server, SOMAXCONN, NULL), UV_EINVAL); + + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} + + +static void connect_overlong_cb(uv_connect_t* connect_req, int status) { + ASSERT_EQ(status, UV_EINVAL); + connect_cb_called++; + uv_close((uv_handle_t*) connect_req->handle, close_cb); +} + + +TEST_IMPL(pipe_overlong_path) { + uv_pipe_t pipe; + uv_connect_t req; + + ASSERT_OK(uv_pipe_init(uv_default_loop(), &pipe, 0)); + +#ifndef _WIN32 + char path[512]; + memset(path, '@', sizeof(path)); + + /* On most platforms sun_path is smaller than the NAME_MAX + * Though there is nothing in the POSIX spec that says it needs to be. + * POSIX allows PATH_MAX length paths in saddr.sun_path BUT individual + * components of the path can only be NAME_MAX long. + * So in this case we end up with UV_ENAMETOOLONG error rather than + * UV_EINVAL. + * ref: https://github.com/libuv/libuv/issues/4231#issuecomment-2194612711 + * On AIX the sun_path is larger than the NAME_MAX + */ +#if defined(_AIX) && !defined(__PASE__) + ASSERT_EQ(UV_ENAMETOOLONG, + uv_pipe_bind2(&pipe, path, sizeof(path), UV_PIPE_NO_TRUNCATE)); + /* UV_ENAMETOOLONG is delayed in uv_pipe_connect2 and won't propagate until + * uv_run is called and causes timeouts, therefore in this case we skip calling + * uv_pipe_connect2 + */ +#else + ASSERT_EQ(UV_EINVAL, + uv_pipe_bind2(&pipe, path, sizeof(path), UV_PIPE_NO_TRUNCATE)); + ASSERT_EQ(UV_EINVAL, + uv_pipe_connect2(&req, + &pipe, + path, + sizeof(path), + UV_PIPE_NO_TRUNCATE, + (uv_connect_cb) abort)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); +#endif /*if defined(_AIX) && !defined(__PASE__)*/ +#endif /* ifndef _WIN32 */ + uv_pipe_connect(&req, + &pipe, + "", + (uv_connect_cb) connect_overlong_cb); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(1, close_cb_called); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-pipe-close-stdout-read-stdin.c b/test/test-pipe-close-stdout-read-stdin.c index 126be2cc46f..51bbf0f54e2 100644 --- a/test/test-pipe-close-stdout-read-stdin.c +++ b/test/test-pipe-close-stdout-read-stdin.c @@ -26,6 +26,10 @@ #include #include +#ifdef __APPLE__ +#include +#endif + #include "uv.h" #include "task.h" @@ -57,9 +61,15 @@ TEST_IMPL(pipe_close_stdout_read_stdin) { uv_pipe_t stdin_pipe; r = pipe(fd); - ASSERT(r == 0); + ASSERT_OK(r); + +#if defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH) + pid = -1; +#else + pid = fork(); +#endif - if ((pid = fork()) == 0) { + if (pid == 0) { /* * Make the read side of the pipe our stdin. * The write side will be closed by the parent process. @@ -70,24 +80,24 @@ TEST_IMPL(pipe_close_stdout_read_stdin) { ASSERT(-1 <= r && r <= 1); close(0); r = dup(fd[0]); - ASSERT(r != -1); + ASSERT_NE(r, -1); /* Create a stream that reads from the pipe. */ r = uv_pipe_init(uv_default_loop(), (uv_pipe_t *)&stdin_pipe, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_open((uv_pipe_t *)&stdin_pipe, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_start((uv_stream_t *)&stdin_pipe, alloc_buffer, read_stdin); - ASSERT(r == 0); + ASSERT_OK(r); /* * Because the other end of the pipe was closed, there should * be no event left to process after one run of the event loop. * Otherwise, it means that events were not processed correctly. */ - ASSERT(uv_run(uv_default_loop(), UV_RUN_NOWAIT) == 0); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_NOWAIT)); } else { /* * Close both ends of the pipe so that the child @@ -101,7 +111,7 @@ TEST_IMPL(pipe_close_stdout_read_stdin) { ASSERT(WIFEXITED(status) && WEXITSTATUS(status) == 0); } - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-pipe-connect-error.c b/test/test-pipe-connect-error.c index 0f1e2b1c1ed..ee6e0776f33 100644 --- a/test/test-pipe-connect-error.c +++ b/test/test-pipe-connect-error.c @@ -43,15 +43,17 @@ static void close_cb(uv_handle_t* handle) { static void connect_cb(uv_connect_t* connect_req, int status) { - ASSERT(status == UV_ENOENT); - uv_close((uv_handle_t*)connect_req->handle, close_cb); + ASSERT_EQ(status, UV_ENOENT); + uv_close((uv_handle_t*) connect_req->handle, close_cb); connect_cb_called++; } static void connect_cb_file(uv_connect_t* connect_req, int status) { - ASSERT(status == UV_ENOTSOCK || status == UV_ECONNREFUSED); - uv_close((uv_handle_t*)connect_req->handle, close_cb); + if (status != UV_ENOTSOCK) + if (status != UV_EACCES) + ASSERT_EQ(status, UV_ECONNREFUSED); + uv_close((uv_handle_t*) connect_req->handle, close_cb); connect_cb_called++; } @@ -62,15 +64,15 @@ TEST_IMPL(pipe_connect_bad_name) { int r; r = uv_pipe_init(uv_default_loop(), &client, 0); - ASSERT(r == 0); + ASSERT_OK(r); uv_pipe_connect(&req, &client, BAD_PIPENAME, connect_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 1); - ASSERT(connect_cb_called == 1); + ASSERT_EQ(1, close_cb_called); + ASSERT_EQ(1, connect_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -82,14 +84,14 @@ TEST_IMPL(pipe_connect_to_file) { int r; r = uv_pipe_init(uv_default_loop(), &client, 0); - ASSERT(r == 0); + ASSERT_OK(r); uv_pipe_connect(&req, &client, path, connect_cb_file); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 1); - ASSERT(connect_cb_called == 1); + ASSERT_EQ(1, close_cb_called); + ASSERT_EQ(1, connect_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-pipe-connect-multiple.c b/test/test-pipe-connect-multiple.c index 0a60d4a9642..69a09ec6cdd 100644 --- a/test/test-pipe-connect-multiple.c +++ b/test/test-pipe-connect-multiple.c @@ -29,7 +29,7 @@ static int connection_cb_called = 0; static int connect_cb_called = 0; -#define NUM_CLIENTS 4 +#define NUM_CLIENTS 10 typedef struct { uv_pipe_t pipe_handle; @@ -44,14 +44,14 @@ static uv_pipe_t connections[NUM_CLIENTS]; static void connection_cb(uv_stream_t* server, int status) { int r; uv_pipe_t* conn; - ASSERT(status == 0); + ASSERT_OK(status); conn = &connections[connection_cb_called]; r = uv_pipe_init(server->loop, conn, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_accept(server, (uv_stream_t*)conn); - ASSERT(r == 0); + ASSERT_OK(r); if (++connection_cb_called == NUM_CLIENTS && connect_cb_called == NUM_CLIENTS) { @@ -61,7 +61,7 @@ static void connection_cb(uv_stream_t* server, int status) { static void connect_cb(uv_connect_t* connect_req, int status) { - ASSERT(status == 0); + ASSERT_OK(status); if (++connect_cb_called == NUM_CLIENTS && connection_cb_called == NUM_CLIENTS) { uv_stop(connect_req->handle->loop); @@ -80,17 +80,17 @@ TEST_IMPL(pipe_connect_multiple) { loop = uv_default_loop(); r = uv_pipe_init(loop, &server_handle, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_bind(&server_handle, TEST_PIPENAME); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&server_handle, 128, connection_cb); - ASSERT(r == 0); + ASSERT_OK(r); for (i = 0; i < NUM_CLIENTS; i++) { r = uv_pipe_init(loop, &clients[i].pipe_handle, 0); - ASSERT(r == 0); + ASSERT_OK(r); uv_pipe_connect(&clients[i].conn_req, &clients[i].pipe_handle, TEST_PIPENAME, @@ -99,9 +99,80 @@ TEST_IMPL(pipe_connect_multiple) { uv_run(loop, UV_RUN_DEFAULT); - ASSERT(connection_cb_called == NUM_CLIENTS); - ASSERT(connect_cb_called == NUM_CLIENTS); + ASSERT_EQ(connection_cb_called, NUM_CLIENTS); + ASSERT_EQ(connect_cb_called, NUM_CLIENTS); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); + return 0; +} + + +static void connection_cb2(uv_stream_t* server, int status) { + int r; + uv_pipe_t* conn; + ASSERT_OK(status); + + conn = &connections[connection_cb_called]; + r = uv_pipe_init(server->loop, conn, 0); + ASSERT_OK(r); + + r = uv_accept(server, (uv_stream_t*)conn); + ASSERT_OK(r); + + uv_close((uv_handle_t*)conn, NULL); + if (++connection_cb_called == NUM_CLIENTS && + connect_cb_called == NUM_CLIENTS) { + uv_close((uv_handle_t*)&server_handle, NULL); + } +} + +static void connect_cb2(uv_connect_t* connect_req, int status) { + ASSERT_EQ(status, UV_ECANCELED); + if (++connect_cb_called == NUM_CLIENTS && + connection_cb_called == NUM_CLIENTS) { + uv_close((uv_handle_t*)&server_handle, NULL); + } +} + + +TEST_IMPL(pipe_connect_close_multiple) { +#if defined(NO_SELF_CONNECT) + RETURN_SKIP(NO_SELF_CONNECT); +#endif + int i; + int r; + uv_loop_t* loop; + + loop = uv_default_loop(); + + r = uv_pipe_init(loop, &server_handle, 0); + ASSERT_OK(r); + + r = uv_pipe_bind(&server_handle, TEST_PIPENAME); + ASSERT_OK(r); + + r = uv_listen((uv_stream_t*)&server_handle, 128, connection_cb2); + ASSERT_OK(r); + + for (i = 0; i < NUM_CLIENTS; i++) { + r = uv_pipe_init(loop, &clients[i].pipe_handle, 0); + ASSERT_OK(r); + uv_pipe_connect(&clients[i].conn_req, + &clients[i].pipe_handle, + TEST_PIPENAME, + connect_cb2); + } + + for (i = 0; i < NUM_CLIENTS; i++) { + uv_close((uv_handle_t*)&clients[i].pipe_handle, NULL); + } + + + uv_run(loop, UV_RUN_DEFAULT); + + ASSERT_EQ(connection_cb_called, NUM_CLIENTS); + ASSERT_EQ(connect_cb_called, NUM_CLIENTS); + + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-pipe-connect-prepare.c b/test/test-pipe-connect-prepare.c index 08b57cbf510..93d15a473f3 100644 --- a/test/test-pipe-connect-prepare.c +++ b/test/test-pipe-connect-prepare.c @@ -48,7 +48,7 @@ static void close_cb(uv_handle_t* handle) { static void connect_cb(uv_connect_t* connect_req, int status) { - ASSERT(status == UV_ENOENT); + ASSERT_EQ(status, UV_ENOENT); connect_cb_called++; uv_close((uv_handle_t*)&prepare_handle, close_cb); uv_close((uv_handle_t*)&pipe_handle, close_cb); @@ -56,7 +56,7 @@ static void connect_cb(uv_connect_t* connect_req, int status) { static void prepare_cb(uv_prepare_t* handle) { - ASSERT(handle == &prepare_handle); + ASSERT_PTR_EQ(handle, &prepare_handle); uv_pipe_connect(&conn_req, &pipe_handle, BAD_PIPENAME, connect_cb); } @@ -65,19 +65,19 @@ TEST_IMPL(pipe_connect_on_prepare) { int r; r = uv_pipe_init(uv_default_loop(), &pipe_handle, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_prepare_init(uv_default_loop(), &prepare_handle); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_prepare_start(&prepare_handle, prepare_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(close_cb_called == 2); - ASSERT(connect_cb_called == 1); + ASSERT_EQ(2, close_cb_called); + ASSERT_EQ(1, connect_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-pipe-getsockname.c b/test/test-pipe-getsockname.c index 79db8eba717..cc345ccd864 100644 --- a/test/test-pipe-getsockname.c +++ b/test/test-pipe-getsockname.c @@ -25,11 +25,6 @@ #include #include -#if defined(__linux__) - #include - #include -#endif - #ifndef _WIN32 # include /* close */ #else @@ -45,8 +40,8 @@ static int pipe_client_connect_cb_called = 0; static void pipe_close_cb(uv_handle_t* handle) { - ASSERT(handle == (uv_handle_t*) &pipe_client || - handle == (uv_handle_t*) &pipe_server); + ASSERT_NE(handle == (uv_handle_t*) &pipe_client || + handle == (uv_handle_t*) &pipe_server, 0); pipe_close_cb_called++; } @@ -56,15 +51,21 @@ static void pipe_client_connect_cb(uv_connect_t* req, int status) { size_t len; int r; - ASSERT(req == &connect_req); - ASSERT(status == 0); + ASSERT_PTR_EQ(req, &connect_req); + ASSERT_OK(status); len = sizeof buf; r = uv_pipe_getpeername(&pipe_client, buf, &len); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(buf[len - 1] != 0); - ASSERT(memcmp(buf, TEST_PIPENAME, len) == 0); + if (*buf == '\0') { /* Linux abstract socket. */ + const char expected[] = "\0" TEST_PIPENAME; + ASSERT_EQ(len, sizeof(expected) - 1); + ASSERT_MEM_EQ(buf, expected, len); + } else { + ASSERT_NE(0, buf[len - 1]); + ASSERT_MEM_EQ(buf, TEST_PIPENAME, len); + } len = sizeof buf; r = uv_pipe_getsockname(&pipe_client, buf, &len); @@ -72,17 +73,46 @@ static void pipe_client_connect_cb(uv_connect_t* req, int status) { pipe_client_connect_cb_called++; + uv_close((uv_handle_t*) &pipe_client, pipe_close_cb); + uv_close((uv_handle_t*) &pipe_server, pipe_close_cb); +} + + +#if defined(__linux__) +/* Socket name looks like \0[0-9a-f]{5}, e.g. "\0bad42" */ +static void check_is_autobind_abstract_socket_name(const char *p, size_t len) { + ASSERT_EQ(len, 6); + ASSERT_EQ(*p, '\0'); + + while (*p != '\0') { + ASSERT((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'f')); + p++; + } +} + + +static void pipe_client_autobind_connect_cb(uv_connect_t* req, int status) { + char buf[16]; + size_t len; + ASSERT_OK(status); + len = 5; + ASSERT_EQ(UV_ENOBUFS, uv_pipe_getpeername(&pipe_client, buf, &len)); + len = 6; + ASSERT_OK(uv_pipe_getpeername(&pipe_client, buf, &len)); + check_is_autobind_abstract_socket_name(buf, len); + pipe_client_connect_cb_called++; uv_close((uv_handle_t*) &pipe_client, pipe_close_cb); uv_close((uv_handle_t*) &pipe_server, pipe_close_cb); } +#endif /* defined(__linux__) */ static void pipe_server_connection_cb(uv_stream_t* handle, int status) { /* This function *may* be called, depending on whether accept or the * connection callback is called first. */ - ASSERT(status == 0); + ASSERT_OK(status); } @@ -91,123 +121,193 @@ TEST_IMPL(pipe_getsockname) { RETURN_SKIP(NO_SELF_CONNECT); #endif uv_loop_t* loop; + char namebuf[256]; char buf[1024]; + size_t namelen; size_t len; int r; + snprintf(namebuf, sizeof(namebuf), "%s-oob", TEST_PIPENAME); + namelen = sizeof(TEST_PIPENAME) - 1; + loop = uv_default_loop(); ASSERT_NOT_NULL(loop); r = uv_pipe_init(loop, &pipe_server, 0); - ASSERT(r == 0); + ASSERT_OK(r); + + r = uv_pipe_bind2(&pipe_server, "bad\0path", 8, 0); + ASSERT_EQ(r, UV_EINVAL); len = sizeof buf; r = uv_pipe_getsockname(&pipe_server, buf, &len); - ASSERT(r == UV_EBADF); + ASSERT_EQ(r, UV_EBADF); len = sizeof buf; r = uv_pipe_getpeername(&pipe_server, buf, &len); - ASSERT(r == UV_EBADF); + ASSERT_EQ(r, UV_EBADF); - r = uv_pipe_bind(&pipe_server, TEST_PIPENAME); - ASSERT(r == 0); + r = uv_pipe_bind2(&pipe_server, namebuf, namelen, 0); + ASSERT_OK(r); - len = sizeof buf; - r = uv_pipe_getsockname(&pipe_server, buf, &len); - ASSERT(r == 0); +#ifndef _WIN32 + ASSERT_STR_EQ(pipe_server.pipe_fname, TEST_PIPENAME); +#endif + + r = uv_pipe_getsockname(&pipe_server, NULL, &len); + ASSERT_EQ(r, UV_EINVAL); + + r = uv_pipe_getsockname(&pipe_server, buf, NULL); + ASSERT_EQ(r, UV_EINVAL); + + r = uv_pipe_getsockname(&pipe_server, NULL, NULL); + ASSERT_EQ(r, UV_EINVAL); + + len = sizeof(TEST_PIPENAME) - 1; + ASSERT_EQ(UV_ENOBUFS, uv_pipe_getsockname(&pipe_server, buf, &len)); + + len = sizeof(TEST_PIPENAME); + ASSERT_OK(uv_pipe_getsockname(&pipe_server, buf, &len)); - ASSERT(buf[len - 1] != 0); - ASSERT(buf[len] == '\0'); - ASSERT(memcmp(buf, TEST_PIPENAME, len) == 0); + ASSERT_NE(0, buf[len - 1]); + ASSERT_EQ(buf[len], '\0'); + ASSERT_OK(memcmp(buf, TEST_PIPENAME, len)); len = sizeof buf; r = uv_pipe_getpeername(&pipe_server, buf, &len); - ASSERT(r == UV_ENOTCONN); + ASSERT_EQ(r, UV_ENOTCONN); r = uv_listen((uv_stream_t*) &pipe_server, 0, pipe_server_connection_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_init(loop, &pipe_client, 0); - ASSERT(r == 0); + ASSERT_OK(r); len = sizeof buf; r = uv_pipe_getsockname(&pipe_client, buf, &len); - ASSERT(r == UV_EBADF); + ASSERT_EQ(r, UV_EBADF); len = sizeof buf; r = uv_pipe_getpeername(&pipe_client, buf, &len); - ASSERT(r == UV_EBADF); + ASSERT_EQ(r, UV_EBADF); - uv_pipe_connect(&connect_req, &pipe_client, TEST_PIPENAME, pipe_client_connect_cb); + r = uv_pipe_connect2(&connect_req, + &pipe_client, + namebuf, + namelen, + 0, + pipe_client_connect_cb); + ASSERT_OK(r); len = sizeof buf; r = uv_pipe_getsockname(&pipe_client, buf, &len); - ASSERT(r == 0 && len == 0); + ASSERT_EQ(r, 0); + ASSERT_EQ(len, 0); len = sizeof buf; r = uv_pipe_getpeername(&pipe_client, buf, &len); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(buf[len - 1] != 0); - ASSERT(memcmp(buf, TEST_PIPENAME, len) == 0); + ASSERT_NE(0, buf[len - 1]); + ASSERT_OK(memcmp(buf, TEST_PIPENAME, len)); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); - ASSERT(pipe_client_connect_cb_called == 1); - ASSERT(pipe_close_cb_called == 2); + ASSERT_OK(r); + ASSERT_EQ(1, pipe_client_connect_cb_called); + ASSERT_EQ(2, pipe_close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } TEST_IMPL(pipe_getsockname_abstract) { + /* TODO(bnoordhuis) Use unique name, susceptible to concurrent test runs. */ + static const char name[] = "\0" TEST_PIPENAME; #if defined(__linux__) - char buf[1024]; - size_t len; - int r; - int sock; - struct sockaddr_un sun; - socklen_t sun_len; - char abstract_pipe[] = "\0test-pipe"; - - sock = socket(AF_UNIX, SOCK_STREAM, 0); - ASSERT(sock != -1); - - sun_len = sizeof sun; - memset(&sun, 0, sun_len); - sun.sun_family = AF_UNIX; - memcpy(sun.sun_path, abstract_pipe, sizeof abstract_pipe); - - r = bind(sock, (struct sockaddr*)&sun, sun_len); - ASSERT(r == 0); - - r = uv_pipe_init(uv_default_loop(), &pipe_server, 0); - ASSERT(r == 0); - r = uv_pipe_open(&pipe_server, sock); - ASSERT(r == 0); - - len = sizeof buf; - r = uv_pipe_getsockname(&pipe_server, buf, &len); - ASSERT(r == 0); - - ASSERT(memcmp(buf, abstract_pipe, sizeof abstract_pipe) == 0); - - uv_close((uv_handle_t*)&pipe_server, pipe_close_cb); - - uv_run(uv_default_loop(), UV_RUN_DEFAULT); + char buf[256]; + size_t buflen; + + buflen = sizeof(buf); + memset(buf, 0, sizeof(buf)); + ASSERT_OK(uv_pipe_init(uv_default_loop(), &pipe_server, 0)); + ASSERT_OK(uv_pipe_bind2(&pipe_server, name, sizeof(name) - 1, 0)); + ASSERT_OK(uv_pipe_getsockname(&pipe_server, buf, &buflen)); + ASSERT_UINT64_EQ(sizeof(name) - 1, buflen); + ASSERT_MEM_EQ(name, buf, buflen); + ASSERT_OK(uv_listen((uv_stream_t*) &pipe_server, + 0, + pipe_server_connection_cb)); + ASSERT_OK(uv_pipe_init(uv_default_loop(), &pipe_client, 0)); + ASSERT_OK(uv_pipe_connect2(&connect_req, + &pipe_client, + name, + sizeof(name) - 1, + 0, + pipe_client_connect_cb)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_EQ(1, pipe_client_connect_cb_called); + ASSERT_EQ(2, pipe_close_cb_called); + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +#else + /* On other platforms it should simply fail with UV_EINVAL. */ + ASSERT_OK(uv_pipe_init(uv_default_loop(), &pipe_server, 0)); + ASSERT_EQ(UV_EINVAL, uv_pipe_bind2(&pipe_server, name, sizeof(name), 0)); + ASSERT_OK(uv_pipe_init(uv_default_loop(), &pipe_client, 0)); + uv_close((uv_handle_t*) &pipe_server, pipe_close_cb); + ASSERT_EQ(UV_EINVAL, uv_pipe_connect2(&connect_req, + &pipe_client, + name, + sizeof(name), + 0, + (uv_connect_cb) abort)); + uv_close((uv_handle_t*) &pipe_client, pipe_close_cb); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_EQ(2, pipe_close_cb_called); + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +#endif +} - close(sock); - ASSERT(pipe_close_cb_called == 1); - MAKE_VALGRIND_HAPPY(); +TEST_IMPL(pipe_getsockname_autobind) { +#if defined(__linux__) + char buf[256]; + size_t buflen; + + buflen = sizeof(buf); + memset(buf, 0, sizeof(buf)); + ASSERT_OK(uv_pipe_init(uv_default_loop(), &pipe_server, 0)); + ASSERT_OK(uv_pipe_bind2(&pipe_server, "", 0, 0)); + ASSERT_OK(uv_pipe_getsockname(&pipe_server, buf, &buflen)); + check_is_autobind_abstract_socket_name(buf, buflen); + ASSERT_OK(uv_listen((uv_stream_t*) &pipe_server, 0, + pipe_server_connection_cb)); + ASSERT_OK(uv_pipe_init(uv_default_loop(), &pipe_client, 0)); + ASSERT_OK(uv_pipe_connect2(&connect_req, &pipe_client, + buf, + 1 + strlen(&buf[1]), + 0, + pipe_client_autobind_connect_cb)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_EQ(1, pipe_client_connect_cb_called); + ASSERT_EQ(2, pipe_close_cb_called); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; #else - MAKE_VALGRIND_HAPPY(); + /* On other platforms it should simply fail with UV_EINVAL. */ + ASSERT_OK(uv_pipe_init(uv_default_loop(), &pipe_server, 0)); + ASSERT_EQ(UV_EINVAL, uv_pipe_bind2(&pipe_server, "", 0, 0)); + uv_close((uv_handle_t*) &pipe_server, pipe_close_cb); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_EQ(1, pipe_close_cb_called); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; #endif } + TEST_IMPL(pipe_getsockname_blocking) { #ifdef _WIN32 HANDLE readh, writeh; @@ -217,54 +317,54 @@ TEST_IMPL(pipe_getsockname_blocking) { int r; r = CreatePipe(&readh, &writeh, NULL, 65536); - ASSERT(r != 0); + ASSERT(r); r = uv_pipe_init(uv_default_loop(), &pipe_client, 0); - ASSERT(r == 0); + ASSERT_OK(r); readfd = _open_osfhandle((intptr_t)readh, _O_RDONLY); - ASSERT(r != -1); + ASSERT_NE(r, -1); r = uv_pipe_open(&pipe_client, readfd); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_start((uv_stream_t*) &pipe_client, (uv_alloc_cb) abort, (uv_read_cb) abort); - ASSERT(r == 0); + ASSERT_OK(r); Sleep(100); r = uv_read_stop((uv_stream_t*)&pipe_client); - ASSERT(r == 0); + ASSERT_OK(r); len1 = sizeof buf1; r = uv_pipe_getsockname(&pipe_client, buf1, &len1); - ASSERT(r == 0); - ASSERT(len1 == 0); /* It's an annonymous pipe. */ + ASSERT_OK(r); + ASSERT_OK(len1); /* It's an annonymous pipe. */ r = uv_read_start((uv_stream_t*)&pipe_client, (uv_alloc_cb) abort, (uv_read_cb) abort); - ASSERT(r == 0); + ASSERT_OK(r); Sleep(100); len2 = sizeof buf2; r = uv_pipe_getsockname(&pipe_client, buf2, &len2); - ASSERT(r == 0); - ASSERT(len2 == 0); /* It's an annonymous pipe. */ + ASSERT_OK(r); + ASSERT_OK(len2); /* It's an annonymous pipe. */ r = uv_read_stop((uv_stream_t*)&pipe_client); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(len1 == len2); - ASSERT(memcmp(buf1, buf2, len1) == 0); + ASSERT_EQ(len1, len2); + ASSERT_OK(memcmp(buf1, buf2, len1)); pipe_close_cb_called = 0; uv_close((uv_handle_t*)&pipe_client, pipe_close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(pipe_close_cb_called == 1); + ASSERT_EQ(1, pipe_close_cb_called); CloseHandle(writeh); #endif - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-pipe-pending-instances.c b/test/test-pipe-pending-instances.c index b6ff911a0f2..570ecd69b73 100644 --- a/test/test-pipe-pending-instances.c +++ b/test/test-pipe-pending-instances.c @@ -37,23 +37,23 @@ TEST_IMPL(pipe_pending_instances) { loop = uv_default_loop(); r = uv_pipe_init(loop, &pipe_handle, 0); - ASSERT(r == 0); + ASSERT_OK(r); uv_pipe_pending_instances(&pipe_handle, 8); r = uv_pipe_bind(&pipe_handle, TEST_PIPENAME); - ASSERT(r == 0); + ASSERT_OK(r); uv_pipe_pending_instances(&pipe_handle, 16); r = uv_listen((uv_stream_t*)&pipe_handle, 128, connection_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_close((uv_handle_t*)&pipe_handle, NULL); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-pipe-sendmsg.c b/test/test-pipe-sendmsg.c index 3bf427f8aa0..3958b05378c 100644 --- a/test/test-pipe-sendmsg.c +++ b/test/test-pipe-sendmsg.c @@ -45,12 +45,12 @@ static void set_nonblocking(uv_os_sock_t sock) { #ifdef _WIN32 unsigned long on = 1; r = ioctlsocket(sock, FIONBIO, &on); - ASSERT(r == 0); + ASSERT_OK(r); #else int flags = fcntl(sock, F_GETFL, 0); - ASSERT(flags >= 0); + ASSERT_GE(flags, 0); r = fcntl(sock, F_SETFL, flags | O_NONBLOCK); - ASSERT(r >= 0); + ASSERT_GE(r, 0); #endif } @@ -79,22 +79,22 @@ static void read_cb(uv_stream_t* handle, unsigned int i; p = (uv_pipe_t*) handle; - ASSERT(nread >= 0); + ASSERT_GE(nread, 0); while (uv_pipe_pending_count(p) != 0) { pending = uv_pipe_pending_type(p); - ASSERT(pending == UV_NAMED_PIPE); + ASSERT_EQ(pending, UV_NAMED_PIPE); - ASSERT(incoming_count < ARRAY_SIZE(incoming)); + ASSERT_LT(incoming_count, ARRAY_SIZE(incoming)); inc = &incoming[incoming_count++]; - ASSERT(0 == uv_pipe_init(p->loop, inc, 0)); - ASSERT(0 == uv_accept(handle, (uv_stream_t*) inc)); + ASSERT_OK(uv_pipe_init(p->loop, inc, 0)); + ASSERT_OK(uv_accept(handle, (uv_stream_t*) inc)); } if (incoming_count != ARRAY_SIZE(incoming)) return; - ASSERT(0 == uv_read_stop((uv_stream_t*) p)); + ASSERT_OK(uv_read_stop((uv_stream_t*) p)); uv_close((uv_handle_t*) p, close_cb); for (i = 0; i < ARRAY_SIZE(incoming); i++) uv_close((uv_handle_t*) &incoming[i], close_cb); @@ -115,12 +115,12 @@ TEST_IMPL(pipe_sendmsg) { unsigned int i; uv_buf_t buf; - ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, fds)); + ASSERT_OK(socketpair(AF_UNIX, SOCK_STREAM, 0, fds)); for (i = 0; i < ARRAY_SIZE(send_fds); i += 2) - ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, send_fds + i)); - ASSERT(i == ARRAY_SIZE(send_fds)); - ASSERT(0 == uv_pipe_init(uv_default_loop(), &p, 1)); - ASSERT(0 == uv_pipe_open(&p, fds[1])); + ASSERT_OK(socketpair(AF_UNIX, SOCK_STREAM, 0, send_fds + i)); + ASSERT_EQ(i, ARRAY_SIZE(send_fds)); + ASSERT_OK(uv_pipe_init(uv_default_loop(), &p, 1)); + ASSERT_OK(uv_pipe_open(&p, fds[1])); buf = uv_buf_init("X", 1); memset(&msg, 0, sizeof(msg)); @@ -130,7 +130,7 @@ TEST_IMPL(pipe_sendmsg) { msg.msg_control = (void*) scratch; msg.msg_controllen = CMSG_LEN(sizeof(send_fds)); - ASSERT(sizeof(scratch) >= msg.msg_controllen); + ASSERT_GE(sizeof(scratch), msg.msg_controllen); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; @@ -146,26 +146,26 @@ TEST_IMPL(pipe_sendmsg) { } set_nonblocking(fds[1]); - ASSERT(0 == uv_read_start((uv_stream_t*) &p, alloc_cb, read_cb)); + ASSERT_OK(uv_read_start((uv_stream_t*) &p, alloc_cb, read_cb)); do r = sendmsg(fds[0], &msg, 0); while (r == -1 && errno == EINTR); - ASSERT(r == 1); + ASSERT_EQ(1, r); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(ARRAY_SIZE(incoming) == incoming_count); - ASSERT(ARRAY_SIZE(incoming) + 1 == close_called); + ASSERT_EQ(ARRAY_SIZE(incoming), incoming_count); + ASSERT_EQ(ARRAY_SIZE(incoming) + 1, close_called); close(fds[0]); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } #else /* !_WIN32 */ TEST_IMPL(pipe_sendmsg) { - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-pipe-server-close.c b/test/test-pipe-server-close.c index 25305b397b2..ad7d792acc3 100644 --- a/test/test-pipe-server-close.c +++ b/test/test-pipe-server-close.c @@ -35,15 +35,15 @@ static int pipe_client_connect_cb_called = 0; static void pipe_close_cb(uv_handle_t* handle) { - ASSERT(handle == (uv_handle_t*) &pipe_client || - handle == (uv_handle_t*) &pipe_server); + ASSERT_NE(handle == (uv_handle_t*) &pipe_client || + handle == (uv_handle_t*) &pipe_server, 0); pipe_close_cb_called++; } static void pipe_client_connect_cb(uv_connect_t* req, int status) { - ASSERT(req == &connect_req); - ASSERT(status == 0); + ASSERT_PTR_EQ(req, &connect_req); + ASSERT_OK(status); pipe_client_connect_cb_called++; @@ -56,7 +56,7 @@ static void pipe_server_connection_cb(uv_stream_t* handle, int status) { /* This function *may* be called, depending on whether accept or the * connection callback is called first. */ - ASSERT(status == 0); + ASSERT_OK(status); } @@ -71,24 +71,24 @@ TEST_IMPL(pipe_server_close) { ASSERT_NOT_NULL(loop); r = uv_pipe_init(loop, &pipe_server, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_bind(&pipe_server, TEST_PIPENAME); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*) &pipe_server, 0, pipe_server_connection_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_init(loop, &pipe_client, 0); - ASSERT(r == 0); + ASSERT_OK(r); uv_pipe_connect(&connect_req, &pipe_client, TEST_PIPENAME, pipe_client_connect_cb); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); - ASSERT(pipe_client_connect_cb_called == 1); - ASSERT(pipe_close_cb_called == 2); + ASSERT_OK(r); + ASSERT_EQ(1, pipe_client_connect_cb_called); + ASSERT_EQ(2, pipe_close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-pipe-set-fchmod.c b/test/test-pipe-set-fchmod.c index 91e476652e0..9563e4fdd50 100644 --- a/test/test-pipe-set-fchmod.c +++ b/test/test-pipe-set-fchmod.c @@ -22,6 +22,7 @@ #include "uv.h" #include "task.h" +#include TEST_IMPL(pipe_set_chmod) { uv_pipe_t pipe_handle; @@ -34,28 +35,29 @@ TEST_IMPL(pipe_set_chmod) { loop = uv_default_loop(); r = uv_pipe_init(loop, &pipe_handle, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_bind(&pipe_handle, TEST_PIPENAME); - ASSERT(r == 0); + ASSERT_OK(r); /* No easy way to test if this works, we will only make sure that the call is * successful. */ r = uv_pipe_chmod(&pipe_handle, UV_READABLE); if (r == UV_EPERM) { - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); RETURN_SKIP("Insufficient privileges to alter pipe fmode"); } - ASSERT(r == 0); + ASSERT_OK(r); #ifndef _WIN32 - stat(TEST_PIPENAME, &stat_buf); + memset(&stat_buf, 0, sizeof(stat_buf)); + ASSERT_OK(stat(TEST_PIPENAME, &stat_buf)); ASSERT(stat_buf.st_mode & S_IRUSR); ASSERT(stat_buf.st_mode & S_IRGRP); ASSERT(stat_buf.st_mode & S_IROTH); #endif r = uv_pipe_chmod(&pipe_handle, UV_WRITABLE); - ASSERT(r == 0); + ASSERT_OK(r); #ifndef _WIN32 stat(TEST_PIPENAME, &stat_buf); ASSERT(stat_buf.st_mode & S_IWUSR); @@ -64,7 +66,7 @@ TEST_IMPL(pipe_set_chmod) { #endif r = uv_pipe_chmod(&pipe_handle, UV_WRITABLE | UV_READABLE); - ASSERT(r == 0); + ASSERT_OK(r); #ifndef _WIN32 stat(TEST_PIPENAME, &stat_buf); ASSERT(stat_buf.st_mode & S_IRUSR); @@ -76,15 +78,15 @@ TEST_IMPL(pipe_set_chmod) { #endif r = uv_pipe_chmod(NULL, UV_WRITABLE | UV_READABLE); - ASSERT(r == UV_EBADF); + ASSERT_EQ(r, UV_EBADF); r = uv_pipe_chmod(&pipe_handle, 12345678); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_close((uv_handle_t*)&pipe_handle, NULL); r = uv_pipe_chmod(&pipe_handle, UV_WRITABLE | UV_READABLE); - ASSERT(r == UV_EBADF); + ASSERT_EQ(r, UV_EBADF); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-pipe-set-non-blocking.c b/test/test-pipe-set-non-blocking.c index 8246afaf827..bf1c284189f 100644 --- a/test/test-pipe-set-non-blocking.c +++ b/test/test-pipe-set-non-blocking.c @@ -46,17 +46,13 @@ static void thread_main(void* arg) { uv_fs_req_cleanup(&req); } while (n > 0 || (n == -1 && uv_errno == UV_EINTR)); -#ifdef _WIN32 - ASSERT(n == UV_EOF); -#else - ASSERT(n == 0); -#endif + ASSERT_OK(n); } #ifdef _WIN32 static void write_cb(uv_write_t* req, int status) { - ASSERT(status == 0); + ASSERT_OK(status); req->handle = NULL; /* signal completion of write_cb */ } #endif @@ -81,15 +77,15 @@ TEST_IMPL(pipe_set_non_blocking) { uv_write_t write_req; #endif - ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0)); - ASSERT(0 == uv_pipe(fd, 0, 0)); - ASSERT(0 == uv_pipe_open(&pipe_handle, fd[1])); - ASSERT(0 == uv_stream_set_blocking((uv_stream_t*) &pipe_handle, 1)); + ASSERT_OK(uv_pipe_init(uv_default_loop(), &pipe_handle, 0)); + ASSERT_OK(uv_pipe(fd, 0, 0)); + ASSERT_OK(uv_pipe_open(&pipe_handle, fd[1])); + ASSERT_OK(uv_stream_set_blocking((uv_stream_t*) &pipe_handle, 1)); fd[1] = -1; /* fd[1] is owned by pipe_handle now. */ ctx.fd = fd[0]; - ASSERT(0 == uv_barrier_init(&ctx.barrier, 2)); - ASSERT(0 == uv_thread_create(&thread, thread_main, &ctx)); + ASSERT_OK(uv_barrier_init(&ctx.barrier, 2)); + ASSERT_OK(uv_thread_create(&thread, thread_main, &ctx)); uv_barrier_wait(&ctx.barrier); buf.len = sizeof(data); @@ -103,30 +99,33 @@ TEST_IMPL(pipe_set_non_blocking) { */ n = uv_try_write((uv_stream_t*) &pipe_handle, &buf, 1); #ifdef _WIN32 - ASSERT(n == UV_EAGAIN); /* E_NOTIMPL */ - ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &pipe_handle, &buf, 1, write_cb)); + ASSERT_EQ(n, UV_EAGAIN); /* E_NOTIMPL */ + ASSERT_OK(uv_write(&write_req, + (uv_stream_t*) &pipe_handle, + &buf, + 1, + write_cb)); ASSERT_NOT_NULL(write_req.handle); - ASSERT(1 == uv_run(uv_default_loop(), UV_RUN_ONCE)); /* queue write_cb */ - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); /* process write_cb */ + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_ONCE)); ASSERT_NULL(write_req.handle); /* check for signaled completion of write_cb */ n = buf.len; #endif - ASSERT(n == sizeof(data)); + ASSERT_EQ(n, sizeof(data)); nwritten += n; } uv_close((uv_handle_t*) &pipe_handle, NULL); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(0 == uv_thread_join(&thread)); + ASSERT_OK(uv_thread_join(&thread)); #ifdef _WIN32 - ASSERT(0 == _close(fd[0])); /* fd[1] is closed by uv_close(). */ + ASSERT_OK(_close(fd[0])); /* fd[1] is closed by uv_close(). */ #else - ASSERT(0 == close(fd[0])); /* fd[1] is closed by uv_close(). */ + ASSERT_OK(close(fd[0])); /* fd[1] is closed by uv_close(). */ #endif fd[0] = -1; uv_barrier_destroy(&ctx.barrier); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-platform-output.c b/test/test-platform-output.c index 341c7ae54ed..d9b39c744ff 100644 --- a/test/test-platform-output.c +++ b/test/test-platform-output.c @@ -25,11 +25,6 @@ TEST_IMPL(platform_output) { -/* TODO(gengjiawen): Fix test on QEMU. */ -#if defined(__QEMU__) - RETURN_SKIP("Test does not currently work in QEMU"); -#endif - char buffer[512]; size_t rss; size_t size; @@ -40,43 +35,46 @@ TEST_IMPL(platform_output) { uv_cpu_info_t* cpus; uv_interface_address_t* interfaces; uv_passwd_t pwd; + uv_group_t grp; uv_utsname_t uname; + unsigned par; + char* const* member; int count; int i; int err; err = uv_get_process_title(buffer, sizeof(buffer)); - ASSERT(err == 0); + ASSERT_OK(err); printf("uv_get_process_title: %s\n", buffer); size = sizeof(buffer); err = uv_cwd(buffer, &size); - ASSERT(err == 0); + ASSERT_OK(err); printf("uv_cwd: %s\n", buffer); err = uv_resident_set_memory(&rss); #if defined(__MSYS__) - ASSERT(err == UV_ENOSYS); + ASSERT_EQ(err, UV_ENOSYS); #else - ASSERT(err == 0); + ASSERT_OK(err); printf("uv_resident_set_memory: %llu\n", (unsigned long long) rss); #endif err = uv_uptime(&uptime); #if defined(__PASE__) - ASSERT(err == UV_ENOSYS); + ASSERT_EQ(err, UV_ENOSYS); #else - ASSERT(err == 0); - ASSERT(uptime > 0); + ASSERT_OK(err); + ASSERT_GT(uptime, 0); printf("uv_uptime: %f\n", uptime); #endif err = uv_getrusage(&rusage); - ASSERT(err == 0); - ASSERT(rusage.ru_utime.tv_sec >= 0); - ASSERT(rusage.ru_utime.tv_usec >= 0); - ASSERT(rusage.ru_stime.tv_sec >= 0); - ASSERT(rusage.ru_stime.tv_usec >= 0); + ASSERT_OK(err); + ASSERT_GE(rusage.ru_utime.tv_sec, 0); + ASSERT_GE(rusage.ru_utime.tv_usec, 0); + ASSERT_GE(rusage.ru_stime.tv_sec, 0); + ASSERT_GE(rusage.ru_stime.tv_usec, 0); printf("uv_getrusage:\n"); printf(" user: %llu sec %llu microsec\n", (unsigned long long) rusage.ru_utime.tv_sec, @@ -88,11 +86,54 @@ TEST_IMPL(platform_output) { printf(" maximum resident set size: %llu\n", (unsigned long long) rusage.ru_maxrss); + par = uv_available_parallelism(); + ASSERT_GE(par, 1); + printf("uv_available_parallelism: %u\n", par); + +#ifdef __linux__ + FILE* file; + int cgroup_version = 0; + unsigned int cgroup_par = 0; + uint64_t quota, period; + + // Attempt to parse cgroup v2 to deduce parallelism constraints + file = fopen("/sys/fs/cgroup/cpu.max", "r"); + if (file) { + if (fscanf(file, "%lu %lu", "a, &period) == 2 && quota > 0) { + cgroup_version = 2; + cgroup_par = (unsigned int)(quota / period); + } + fclose(file); + } + + // If cgroup v2 wasn't present, try parsing cgroup v1 + if (cgroup_version == 0) { + file = fopen("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_quota_us", "r"); + if (file) { + if (fscanf(file, "%lu", "a) == 1 && quota > 0 && quota < ~0ULL) { + fclose(file); + file = fopen("/sys/fs/cgroup/cpu,cpuacct/cpu.cfs_period_us", "r"); + if (file && fscanf(file, "%lu", &period) == 1) { + cgroup_version = 1; + cgroup_par = (unsigned int)(quota / period); + } + } + if (file) fclose(file); + } + } + + // If we found cgroup parallelism constraints, assert and print them + if (cgroup_par > 0) { + ASSERT_GE(par, cgroup_par); + printf("cgroup v%d available parallelism: %u\n", cgroup_version, cgroup_par); + } +#endif + err = uv_cpu_info(&cpus, &count); #if defined(__CYGWIN__) || defined(__MSYS__) - ASSERT(err == UV_ENOSYS); + ASSERT_EQ(err, UV_ENOSYS); #else - ASSERT(err == 0); + ASSERT_OK(err); printf("uv_cpu_info:\n"); for (i = 0; i < count; i++) { @@ -111,7 +152,7 @@ TEST_IMPL(platform_output) { uv_free_cpu_info(cpus, count); err = uv_interface_addresses(&interfaces, &count); - ASSERT(err == 0); + ASSERT_OK(err); printf("uv_interface_addresses:\n"); for (i = 0; i < count; i++) { @@ -147,30 +188,69 @@ TEST_IMPL(platform_output) { uv_free_interface_addresses(interfaces, count); err = uv_os_get_passwd(&pwd); - ASSERT(err == 0); + ASSERT_OK(err); + + err = uv_os_get_group(&grp, pwd.gid); +#if defined(_WIN32) + ASSERT_EQ(err, UV_ENOTSUP); + ASSERT_EQ(pwd.uid, (unsigned long) -1); + ASSERT_EQ(pwd.gid, (unsigned long) -1); + (void) member; + grp.groupname = "ENOTSUP"; +#else + ASSERT_OK(err); + ASSERT_EQ(pwd.gid, grp.gid); +#endif printf("uv_os_get_passwd:\n"); printf(" euid: %ld\n", pwd.uid); - printf(" gid: %ld\n", pwd.gid); + printf(" gid: %ld (%s)\n", pwd.gid, grp.groupname); +#if !defined(_WIN32) + printf(" members: ["); + for (member = grp.members; *member != NULL; member++) { + printf(" %s", *member); + } + printf(" ]\n"); +#endif printf(" username: %s\n", pwd.username); - printf(" shell: %s\n", pwd.shell); + if (pwd.shell != NULL) /* Not set on Windows */ + printf(" shell: %s\n", pwd.shell); printf(" home directory: %s\n", pwd.homedir); uv_os_free_passwd(&pwd); +#if !defined(_WIN32) + uv_os_free_group(&grp); +#endif pid = uv_os_getpid(); - ASSERT(pid > 0); + ASSERT_GT(pid, 0); printf("uv_os_getpid: %d\n", (int) pid); ppid = uv_os_getppid(); - ASSERT(ppid > 0); + ASSERT_GT(ppid, 0); printf("uv_os_getppid: %d\n", (int) ppid); err = uv_os_uname(&uname); - ASSERT(err == 0); + ASSERT_OK(err); printf("uv_os_uname:\n"); printf(" sysname: %s\n", uname.sysname); printf(" release: %s\n", uname.release); printf(" version: %s\n", uname.version); printf(" machine: %s\n", uname.machine); + ASSERT_OK(uv_getrusage_thread(&rusage)); + ASSERT_UINT64_GE(rusage.ru_utime.tv_sec, 0); + ASSERT_UINT64_GE(rusage.ru_utime.tv_usec, 0); + ASSERT_UINT64_GE(rusage.ru_stime.tv_sec, 0); + ASSERT_UINT64_GE(rusage.ru_stime.tv_usec, 0); + printf("uv_getrusage_thread:\n"); + printf(" user: %llu sec %llu microsec\n", + (unsigned long long) rusage.ru_utime.tv_sec, + (unsigned long long) rusage.ru_utime.tv_usec); + printf(" system: %llu sec %llu microsec\n", + (unsigned long long) rusage.ru_stime.tv_sec, + (unsigned long long) rusage.ru_stime.tv_usec); + printf(" page faults: %llu\n", (unsigned long long) rusage.ru_majflt); + printf(" maximum resident set size: %llu\n", + (unsigned long long) rusage.ru_maxrss); + return 0; } diff --git a/test/test-poll-close-doesnt-corrupt-stack.c b/test/test-poll-close-doesnt-corrupt-stack.c index 1d7e84f6039..0acb452a78b 100644 --- a/test/test-poll-close-doesnt-corrupt-stack.c +++ b/test/test-poll-close-doesnt-corrupt-stack.c @@ -59,12 +59,12 @@ static void NO_INLINE close_socket_and_verify_stack(void) { data[i] = MARKER; r = closesocket(sock); - ASSERT(r == 0); + ASSERT_OK(r); uv_sleep(VERIFY_AFTER); for (i = 0; i < ARRAY_SIZE(data); i++) - ASSERT(data[i] == MARKER); + ASSERT_EQ(data[i], MARKER); } #endif @@ -79,36 +79,36 @@ TEST_IMPL(poll_close_doesnt_corrupt_stack) { struct sockaddr_in addr; r = WSAStartup(MAKEWORD(2, 2), &wsa_data); - ASSERT(r == 0); + ASSERT_OK(r); sock = socket(AF_INET, SOCK_STREAM, 0); - ASSERT(sock != INVALID_SOCKET); + ASSERT_NE(sock, INVALID_SOCKET); on = 1; r = ioctlsocket(sock, FIONBIO, &on); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_ip4_addr("127.0.0.1", TEST_PORT, &addr); - ASSERT(r == 0); + ASSERT_OK(r); r = connect(sock, (const struct sockaddr*) &addr, sizeof addr); - ASSERT(r != 0); - ASSERT(WSAGetLastError() == WSAEWOULDBLOCK); + ASSERT(r); + ASSERT_EQ(WSAGetLastError(), WSAEWOULDBLOCK); r = uv_poll_init_socket(uv_default_loop(), &handle, sock); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_poll_start(&handle, UV_READABLE | UV_WRITABLE, poll_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_close((uv_handle_t*) &handle, close_cb); close_socket_and_verify_stack(); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; #endif } diff --git a/test/test-poll-close.c b/test/test-poll-close.c index 2eccddf5b0b..5843bf4516d 100644 --- a/test/test-poll-close.c +++ b/test/test-poll-close.c @@ -50,7 +50,7 @@ TEST_IMPL(poll_close) { { struct WSAData wsa_data; int r = WSAStartup(MAKEWORD(2, 2), &wsa_data); - ASSERT(r == 0); + ASSERT_OK(r); } #endif @@ -66,8 +66,8 @@ TEST_IMPL(poll_close) { uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == NUM_SOCKETS); + ASSERT_EQ(close_cb_called, NUM_SOCKETS); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-poll-closesocket.c b/test/test-poll-closesocket.c index 1a1c364112a..01f8f741018 100644 --- a/test/test-poll-closesocket.c +++ b/test/test-poll-closesocket.c @@ -39,11 +39,11 @@ static void close_cb(uv_handle_t* h) { static void poll_cb(uv_poll_t* h, int status, int events) { int r; - ASSERT(status == 0); - ASSERT(h == &handle); + ASSERT_OK(status); + ASSERT_PTR_EQ(h, &handle); r = uv_poll_start(&handle, UV_READABLE, poll_cb); - ASSERT(r == 0); + ASSERT_OK(r); closesocket(sock); uv_close((uv_handle_t*) &handle, close_cb); @@ -62,31 +62,31 @@ TEST_IMPL(poll_closesocket) { struct sockaddr_in addr; r = WSAStartup(MAKEWORD(2, 2), &wsa_data); - ASSERT(r == 0); + ASSERT_OK(r); sock = socket(AF_INET, SOCK_STREAM, 0); - ASSERT(sock != INVALID_SOCKET); + ASSERT_NE(sock, INVALID_SOCKET); on = 1; r = ioctlsocket(sock, FIONBIO, &on); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_ip4_addr("127.0.0.1", TEST_PORT, &addr); - ASSERT(r == 0); + ASSERT_OK(r); r = connect(sock, (const struct sockaddr*) &addr, sizeof addr); - ASSERT(r != 0); - ASSERT(WSAGetLastError() == WSAEWOULDBLOCK); + ASSERT(r); + ASSERT_EQ(WSAGetLastError(), WSAEWOULDBLOCK); r = uv_poll_init_socket(uv_default_loop(), &handle, sock); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_poll_start(&handle, UV_WRITABLE, poll_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; #endif } diff --git a/test/test-poll-multiple-handles.c b/test/test-poll-multiple-handles.c index fc2205ddec7..d9c4924dbfe 100644 --- a/test/test-poll-multiple-handles.c +++ b/test/test-poll-multiple-handles.c @@ -40,7 +40,7 @@ static void close_cb(uv_handle_t* handle) { static void poll_cb(uv_poll_t* handle, int status, int events) { /* Not a bound socket, linux immediately reports UV_READABLE, other OS do not */ - ASSERT(events == UV_READABLE); + ASSERT_EQ(events, UV_READABLE); } TEST_IMPL(poll_multiple_handles) { @@ -51,20 +51,24 @@ TEST_IMPL(poll_multiple_handles) { { struct WSAData wsa_data; int r = WSAStartup(MAKEWORD(2, 2), &wsa_data); - ASSERT(r == 0); + ASSERT_OK(r); } #endif sock = socket(AF_INET, SOCK_STREAM, 0); #ifdef _WIN32 - ASSERT(sock != INVALID_SOCKET); + ASSERT_NE(sock, INVALID_SOCKET); #else - ASSERT(sock != -1); + ASSERT_NE(sock, -1); #endif - ASSERT(0 == uv_poll_init_socket(uv_default_loop(), &first_poll_handle, sock)); - ASSERT(0 == uv_poll_init_socket(uv_default_loop(), &second_poll_handle, sock)); + ASSERT_OK(uv_poll_init_socket(uv_default_loop(), + &first_poll_handle, + sock)); + ASSERT_OK(uv_poll_init_socket(uv_default_loop(), + &second_poll_handle, + sock)); - ASSERT(0 == uv_poll_start(&first_poll_handle, UV_READABLE, poll_cb)); + ASSERT_OK(uv_poll_start(&first_poll_handle, UV_READABLE, poll_cb)); /* We may not start polling while another polling handle is active * on that fd. @@ -73,27 +77,28 @@ TEST_IMPL(poll_multiple_handles) { /* We do not track handles in an O(1) lookupable way on Windows, * so not checking that here. */ - ASSERT(uv_poll_start(&second_poll_handle, UV_READABLE, poll_cb) == UV_EEXIST); + ASSERT_EQ(uv_poll_start(&second_poll_handle, UV_READABLE, poll_cb), + UV_EEXIST); #endif /* After stopping the other polling handle, we now should be able to poll */ - ASSERT(0 == uv_poll_stop(&first_poll_handle)); - ASSERT(0 == uv_poll_start(&second_poll_handle, UV_READABLE, poll_cb)); + ASSERT_OK(uv_poll_stop(&first_poll_handle)); + ASSERT_OK(uv_poll_start(&second_poll_handle, UV_READABLE, poll_cb)); /* Closing an already stopped polling handle is safe in any case */ uv_close((uv_handle_t*) &first_poll_handle, close_cb); uv_unref((uv_handle_t*) &second_poll_handle); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(close_cb_called == 1); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_EQ(1, close_cb_called); uv_ref((uv_handle_t*) &second_poll_handle); ASSERT(uv_is_active((uv_handle_t*) &second_poll_handle)); uv_close((uv_handle_t*) &second_poll_handle, close_cb); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(close_cb_called == 2); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_EQ(2, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-poll-oob.c b/test/test-poll-oob.c index 77ffe31e962..b40c93c3708 100644 --- a/test/test-poll-oob.c +++ b/test/test-poll-oob.c @@ -70,7 +70,7 @@ static void poll_cb(uv_poll_t* handle, int status, int events) { int n; int fd; - ASSERT(0 == uv_fileno((uv_handle_t*)handle, &fd)); + ASSERT_OK(uv_fileno((uv_handle_t*)handle, &fd)); memset(buffer, 0, 5); if (events & UV_PRIORITIZED) { @@ -79,10 +79,10 @@ static void poll_cb(uv_poll_t* handle, int status, int events) { while (n == -1 && errno == EINTR); ASSERT(n >= 0 || errno != EINVAL); cli_pr_check = 1; - ASSERT(0 == uv_poll_stop(&poll_req[0])); - ASSERT(0 == uv_poll_start(&poll_req[0], - UV_READABLE | UV_WRITABLE, - poll_cb)); + ASSERT_OK(uv_poll_stop(&poll_req[0])); + ASSERT_OK(uv_poll_start(&poll_req[0], + UV_READABLE | UV_WRITABLE, + poll_cb)); } if (events & UV_READABLE) { if (fd == client_fd) { @@ -91,21 +91,21 @@ static void poll_cb(uv_poll_t* handle, int status, int events) { while (n == -1 && errno == EINTR); ASSERT(n >= 0 || errno != EINVAL); if (cli_rd_check == 1) { - ASSERT(strncmp(buffer, "world", n) == 0); - ASSERT(5 == n); + ASSERT_OK(strncmp(buffer, "world", n)); + ASSERT_EQ(5, n); cli_rd_check = 2; } if (cli_rd_check == 0) { - ASSERT(n == 4); - ASSERT(strncmp(buffer, "hello", n) == 0); + ASSERT_EQ(4, n); + ASSERT_OK(strncmp(buffer, "hello", n)); cli_rd_check = 1; do { do n = recv(server_fd, &buffer, 5, 0); while (n == -1 && errno == EINTR); if (n > 0) { - ASSERT(n == 5); - ASSERT(strncmp(buffer, "world", n) == 0); + ASSERT_EQ(5, n); + ASSERT_OK(strncmp(buffer, "world", n)); cli_rd_check = 2; } } while (n > 0); @@ -118,8 +118,8 @@ static void poll_cb(uv_poll_t* handle, int status, int events) { n = recv(server_fd, &buffer, 3, 0); while (n == -1 && errno == EINTR); ASSERT(n >= 0 || errno != EINVAL); - ASSERT(3 == n); - ASSERT(strncmp(buffer, "foo", n) == 0); + ASSERT_EQ(3, n); + ASSERT_OK(strncmp(buffer, "foo", n)); srv_rd_check = 1; uv_poll_stop(&poll_req[1]); } @@ -128,35 +128,39 @@ static void poll_cb(uv_poll_t* handle, int status, int events) { do { n = send(client_fd, "foo", 3, 0); } while (n < 0 && errno == EINTR); - ASSERT(3 == n); + ASSERT_EQ(3, n); } } static void connection_cb(uv_stream_t* handle, int status) { int r; - ASSERT(0 == status); - ASSERT(0 == uv_accept(handle, (uv_stream_t*) &peer_handle)); - ASSERT(0 == uv_fileno((uv_handle_t*) &peer_handle, &server_fd)); - ASSERT(0 == uv_poll_init_socket(uv_default_loop(), &poll_req[0], client_fd)); - ASSERT(0 == uv_poll_init_socket(uv_default_loop(), &poll_req[1], server_fd)); - ASSERT(0 == uv_poll_start(&poll_req[0], - UV_PRIORITIZED | UV_READABLE | UV_WRITABLE, - poll_cb)); - ASSERT(0 == uv_poll_start(&poll_req[1], - UV_READABLE, - poll_cb)); + ASSERT_OK(status); + ASSERT_OK(uv_accept(handle, (uv_stream_t*) &peer_handle)); + ASSERT_OK(uv_fileno((uv_handle_t*) &peer_handle, &server_fd)); + ASSERT_OK(uv_poll_init_socket(uv_default_loop(), + &poll_req[0], + client_fd)); + ASSERT_OK(uv_poll_init_socket(uv_default_loop(), + &poll_req[1], + server_fd)); + ASSERT_OK(uv_poll_start(&poll_req[0], + UV_PRIORITIZED | UV_READABLE | UV_WRITABLE, + poll_cb)); + ASSERT_OK(uv_poll_start(&poll_req[1], + UV_READABLE, + poll_cb)); do { r = send(server_fd, "hello", 5, MSG_OOB); } while (r < 0 && errno == EINTR); - ASSERT(5 == r); + ASSERT_EQ(5, r); do { r = send(server_fd, "world", 5, 0); } while (r < 0 && errno == EINTR); - ASSERT(5 == r); + ASSERT_EQ(5, r); - ASSERT(0 == uv_idle_start(&idle, idle_cb)); + ASSERT_OK(uv_idle_start(&idle, idle_cb)); } @@ -165,41 +169,41 @@ TEST_IMPL(poll_oob) { int r = 0; uv_loop_t* loop; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); loop = uv_default_loop(); - ASSERT(0 == uv_tcp_init(loop, &server_handle)); - ASSERT(0 == uv_tcp_init(loop, &client_handle)); - ASSERT(0 == uv_tcp_init(loop, &peer_handle)); - ASSERT(0 == uv_idle_init(loop, &idle)); - ASSERT(0 == uv_tcp_bind(&server_handle, (const struct sockaddr*) &addr, 0)); - ASSERT(0 == uv_listen((uv_stream_t*) &server_handle, 1, connection_cb)); + ASSERT_OK(uv_tcp_init(loop, &server_handle)); + ASSERT_OK(uv_tcp_init(loop, &client_handle)); + ASSERT_OK(uv_tcp_init(loop, &peer_handle)); + ASSERT_OK(uv_idle_init(loop, &idle)); + ASSERT_OK(uv_tcp_bind(&server_handle, (const struct sockaddr*) &addr, 0)); + ASSERT_OK(uv_listen((uv_stream_t*) &server_handle, 1, connection_cb)); /* Ensure two separate packets */ - ASSERT(0 == uv_tcp_nodelay(&client_handle, 1)); + ASSERT_OK(uv_tcp_nodelay(&client_handle, 1)); client_fd = socket(PF_INET, SOCK_STREAM, 0); - ASSERT(client_fd >= 0); + ASSERT_GE(client_fd, 0); do { errno = 0; r = connect(client_fd, (const struct sockaddr*)&addr, sizeof(addr)); } while (r == -1 && errno == EINTR); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(ticks == kMaxTicks); + ASSERT_EQ(ticks, kMaxTicks); /* Did client receive the POLLPRI message */ - ASSERT(cli_pr_check == 1); + ASSERT_EQ(1, cli_pr_check); /* Did client receive the POLLIN message */ - ASSERT(cli_rd_check == 2); + ASSERT_EQ(2, cli_rd_check); /* Could we write with POLLOUT and did the server receive our POLLOUT message * through POLLIN. */ - ASSERT(srv_rd_check == 1); + ASSERT_EQ(1, srv_rd_check); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-poll.c b/test/test-poll.c index 3bc422d2795..5161de25376 100644 --- a/test/test-poll.c +++ b/test/test-poll.c @@ -23,6 +23,7 @@ #ifdef _WIN32 # include +# define close _close #else # include # include @@ -106,9 +107,9 @@ static uv_os_sock_t create_bound_socket (struct sockaddr_in bind_addr) { sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); #ifdef _WIN32 - ASSERT(sock != INVALID_SOCKET); + ASSERT_NE(sock, INVALID_SOCKET); #else - ASSERT(sock >= 0); + ASSERT_GE(sock, 0); #endif #ifndef _WIN32 @@ -116,12 +117,12 @@ static uv_os_sock_t create_bound_socket (struct sockaddr_in bind_addr) { /* Allow reuse of the port. */ int yes = 1; r = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); - ASSERT(r == 0); + ASSERT_OK(r); } #endif r = bind(sock, (const struct sockaddr*) &bind_addr, sizeof bind_addr); - ASSERT(r == 0); + ASSERT_OK(r); return sock; } @@ -163,12 +164,12 @@ static connection_context_t* create_connection_context( r = uv_poll_init_socket(uv_default_loop(), &context->poll_handle, sock); context->open_handles++; context->poll_handle.data = context; - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_init(uv_default_loop(), &context->timer_handle); context->open_handles++; context->timer_handle.data = context; - ASSERT(r == 0); + ASSERT_OK(r); return context; } @@ -179,15 +180,15 @@ static void connection_close_cb(uv_handle_t* handle) { if (--context->open_handles == 0) { if (test_mode == DUPLEX || context->is_server_connection) { - ASSERT(context->read == TRANSFER_BYTES); + ASSERT_EQ(context->read, TRANSFER_BYTES); } else { - ASSERT(context->read == 0); + ASSERT_OK(context->read); } if (test_mode == DUPLEX || !context->is_server_connection) { - ASSERT(context->sent == TRANSFER_BYTES); + ASSERT_EQ(context->sent, TRANSFER_BYTES); } else { - ASSERT(context->sent == 0); + ASSERT_OK(context->sent); } closed_connections++; @@ -208,7 +209,7 @@ static void connection_poll_cb(uv_poll_t* handle, int status, int events) { unsigned int new_events; int r; - ASSERT(status == 0); + ASSERT_OK(status); ASSERT(events & context->events); ASSERT(!(events & ~context->events)); @@ -226,7 +227,7 @@ static void connection_poll_cb(uv_poll_t* handle, int status, int events) { do r = recv(context->sock, buffer, sizeof buffer, 0); while (r == -1 && errno == EINTR); - ASSERT(r >= 0); + ASSERT_GE(r, 0); if (r > 0) { context->read += r; @@ -306,7 +307,7 @@ static void connection_poll_cb(uv_poll_t* handle, int status, int events) { static char buffer[103]; int send_bytes = MIN(TRANSFER_BYTES - context->sent, sizeof buffer); - ASSERT(send_bytes > 0); + ASSERT_GT(send_bytes, 0); do r = send(context->sock, buffer, send_bytes, 0); @@ -318,7 +319,7 @@ static void connection_poll_cb(uv_poll_t* handle, int status, int events) { break; } - ASSERT(r > 0); + ASSERT_GT(r, 0); context->sent += r; valid_writable_wakeups++; break; @@ -330,7 +331,7 @@ static void connection_poll_cb(uv_poll_t* handle, int status, int events) { static char buffer[1234]; int send_bytes = MIN(TRANSFER_BYTES - context->sent, sizeof buffer); - ASSERT(send_bytes > 0); + ASSERT_GT(send_bytes, 0); do r = send(context->sock, buffer, send_bytes, 0); @@ -342,18 +343,18 @@ static void connection_poll_cb(uv_poll_t* handle, int status, int events) { break; } - ASSERT(r > 0); + ASSERT_GT(r, 0); valid_writable_wakeups++; context->sent += r; while (context->sent < TRANSFER_BYTES) { send_bytes = MIN(TRANSFER_BYTES - context->sent, sizeof buffer); - ASSERT(send_bytes > 0); + ASSERT_GT(send_bytes, 0); do r = send(context->sock, buffer, send_bytes, 0); while (r == -1 && errno == EINTR); - ASSERT(r != 0); + ASSERT(r); if (r < 0) { ASSERT(got_eagain()); @@ -403,7 +404,7 @@ static void connection_poll_cb(uv_poll_t* handle, int status, int events) { #else r = shutdown(context->sock, SHUT_WR); #endif - ASSERT(r == 0); + ASSERT_OK(r); context->sent_fin = 1; new_events &= ~UV_WRITABLE; } @@ -432,9 +433,9 @@ static void connection_poll_cb(uv_poll_t* handle, int status, int events) { /* Assert that uv_is_active works correctly for poll handles. */ if (context->events != 0) { - ASSERT(1 == uv_is_active((uv_handle_t*) handle)); + ASSERT_EQ(1, uv_is_active((uv_handle_t*) handle)); } else { - ASSERT(0 == uv_is_active((uv_handle_t*) handle)); + ASSERT_OK(uv_is_active((uv_handle_t*) handle)); } } @@ -444,7 +445,7 @@ static void delay_timer_cb(uv_timer_t* timer) { int r; /* Timer should auto stop. */ - ASSERT(0 == uv_is_active((uv_handle_t*) timer)); + ASSERT_OK(uv_is_active((uv_handle_t*) timer)); /* Add the requested events to the poll mask. */ ASSERT(context->delayed_events != 0); @@ -454,7 +455,7 @@ static void delay_timer_cb(uv_timer_t* timer) { r = uv_poll_start(&context->poll_handle, context->events, connection_poll_cb); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -471,7 +472,7 @@ static server_context_t* create_server_context( r = uv_poll_init_socket(uv_default_loop(), &context->poll_handle, sock); context->poll_handle.data = context; - ASSERT(r == 0); + ASSERT_OK(r); return context; } @@ -500,9 +501,9 @@ static void server_poll_cb(uv_poll_t* handle, int status, int events) { addr_len = sizeof addr; sock = accept(server_context->sock, (struct sockaddr*) &addr, &addr_len); #ifdef _WIN32 - ASSERT(sock != INVALID_SOCKET); + ASSERT_NE(sock, INVALID_SOCKET); #else - ASSERT(sock >= 0); + ASSERT_GE(sock, 0); #endif connection_context = create_connection_context(sock, 1); @@ -510,7 +511,7 @@ static void server_poll_cb(uv_poll_t* handle, int status, int events) { r = uv_poll_start(&connection_context->poll_handle, UV_READABLE | UV_WRITABLE | UV_DISCONNECT, connection_poll_cb); - ASSERT(r == 0); + ASSERT_OK(r); if (++server_context->connections == NUM_CLIENTS) { close_socket(server_context->sock); @@ -525,15 +526,15 @@ static void start_server(void) { uv_os_sock_t sock; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); sock = create_bound_socket(addr); context = create_server_context(sock); r = listen(sock, 100); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_poll_start(&context->poll_handle, UV_READABLE, server_poll_cb); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -544,8 +545,8 @@ static void start_client(void) { struct sockaddr_in addr; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); - ASSERT(0 == uv_ip4_addr("0.0.0.0", 0, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", 0, &addr)); sock = create_bound_socket(addr); context = create_connection_context(sock, 0); @@ -554,7 +555,7 @@ static void start_client(void) { r = uv_poll_start(&context->poll_handle, UV_READABLE | UV_WRITABLE | UV_DISCONNECT, connection_poll_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = connect(sock, (struct sockaddr*) &server_addr, sizeof server_addr); ASSERT(r == 0 || got_eagain()); @@ -568,7 +569,7 @@ static void start_poll_test(void) { { struct WSAData wsa_data; int r = WSAStartup(MAKEWORD(2, 2), &wsa_data); - ASSERT(r == 0); + ASSERT_OK(r); } #endif @@ -578,18 +579,18 @@ static void start_poll_test(void) { start_client(); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); /* Assert that at most five percent of the writable wakeups was spurious. */ - ASSERT(spurious_writable_wakeups == 0 || - (valid_writable_wakeups + spurious_writable_wakeups) / - spurious_writable_wakeups > 20); + ASSERT_NE(spurious_writable_wakeups == 0 || + (valid_writable_wakeups + spurious_writable_wakeups) / + spurious_writable_wakeups > 20, 0); - ASSERT(closed_connections == NUM_CLIENTS * 2); + ASSERT_EQ(closed_connections, NUM_CLIENTS * 2); #if !defined(__sun) && !defined(_AIX) && !defined(__MVS__) - ASSERT(disconnects == NUM_CLIENTS * 2); + ASSERT_EQ(disconnects, NUM_CLIENTS * 2); #endif - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); } @@ -625,29 +626,62 @@ TEST_IMPL(poll_unidirectional) { /* Windows won't let you open a directory so we open a file instead. - * OS X lets you poll a file so open the $PWD instead. Both fail - * on Linux so it doesn't matter which one we pick. Both succeed - * on FreeBSD, Solaris and AIX so skip the test on those platforms. + * OS X lets you poll a file so open the $PWD instead. Both fail + * on Linux so it doesn't matter which one we pick. Both succeed + * on Solaris and AIX so skip the test on those platforms. + * On *BSD/Darwin, we disallow polling of regular files, directories. + * In addition to regular files, we also disallow FIFOs on Darwin. */ +#ifdef __APPLE__ +#define TEST_POLL_FIFO_PATH "/tmp/uv-test-poll-fifo" +#endif TEST_IMPL(poll_bad_fdtype) { -#if !defined(__DragonFly__) && !defined(__FreeBSD__) && !defined(__sun) && \ - !defined(_AIX) && !defined(__MVS__) && !defined(__FreeBSD_kernel__) && \ - !defined(__OpenBSD__) && !defined(__CYGWIN__) && !defined(__MSYS__) && \ - !defined(__NetBSD__) +#if !defined(__sun) && \ + !defined(_AIX) && !defined(__MVS__) && \ + !defined(__CYGWIN__) && !defined(__MSYS__) uv_poll_t poll_handle; - int fd; + int fd[2]; #if defined(_WIN32) - fd = open("test/fixtures/empty_file", O_RDONLY); + fd[0] = _open("test/fixtures/empty_file", UV_FS_O_RDONLY); #else - fd = open(".", O_RDONLY); + fd[0] = open(".", UV_FS_O_RDONLY); +#endif + ASSERT_NE(fd[0], -1); + ASSERT_NE(0, uv_poll_init(uv_default_loop(), &poll_handle, fd[0])); + ASSERT_OK(close(fd[0])); +#if defined(__APPLE__) || \ + defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__OpenBSD__) || \ + defined(__NetBSD__) + fd[0] = open("test/fixtures/empty_file", UV_FS_O_RDONLY); + ASSERT_NE(fd[0], -1); + /* Regular files should be banned from kqueue. */ + ASSERT_NE(0, uv_poll_init(uv_default_loop(), &poll_handle, fd[0])); + ASSERT_OK(close(fd[0])); +#ifdef __APPLE__ + ASSERT_OK(pipe(fd)); + /* Pipes should be permitted in kqueue. */ + ASSERT_EQ(0, uv_poll_init(uv_default_loop(), &poll_handle, fd[0])); + ASSERT_OK(close(fd[0])); + ASSERT_OK(close(fd[1])); + + ASSERT_OK(mkfifo(TEST_POLL_FIFO_PATH, 0600)); + fd[0] = open(TEST_POLL_FIFO_PATH, O_RDONLY | O_NONBLOCK); + ASSERT_NE(fd[0], -1); + fd[1] = open(TEST_POLL_FIFO_PATH, O_WRONLY | O_NONBLOCK); + ASSERT_NE(fd[1], -1); + /* FIFOs should be banned from kqueue. */ + ASSERT_NE(0, uv_poll_init(uv_default_loop(), &poll_handle, fd[0])); + ASSERT_OK(close(fd[0])); + ASSERT_OK(close(fd[1])); + unlink(TEST_POLL_FIFO_PATH); +#endif #endif - ASSERT(fd != -1); - ASSERT(0 != uv_poll_init(uv_default_loop(), &poll_handle, fd)); - ASSERT(0 == close(fd)); #endif - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -658,17 +692,17 @@ TEST_IMPL(poll_nested_epoll) { int fd; fd = epoll_create(1); - ASSERT(fd != -1); + ASSERT_NE(fd, -1); - ASSERT(0 == uv_poll_init(uv_default_loop(), &poll_handle, fd)); - ASSERT(0 == uv_poll_start(&poll_handle, UV_READABLE, (uv_poll_cb) abort)); - ASSERT(0 != uv_run(uv_default_loop(), UV_RUN_NOWAIT)); + ASSERT_OK(uv_poll_init(uv_default_loop(), &poll_handle, fd)); + ASSERT_OK(uv_poll_start(&poll_handle, UV_READABLE, (uv_poll_cb) abort)); + ASSERT_NE(0, uv_run(uv_default_loop(), UV_RUN_NOWAIT)); uv_close((uv_handle_t*) &poll_handle, NULL); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(0 == close(fd)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(close(fd)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } #endif /* __linux__ */ @@ -680,17 +714,17 @@ TEST_IMPL(poll_nested_kqueue) { int fd; fd = kqueue(); - ASSERT(fd != -1); + ASSERT_NE(fd, -1); - ASSERT(0 == uv_poll_init(uv_default_loop(), &poll_handle, fd)); - ASSERT(0 == uv_poll_start(&poll_handle, UV_READABLE, (uv_poll_cb) abort)); - ASSERT(0 != uv_run(uv_default_loop(), UV_RUN_NOWAIT)); + ASSERT_OK(uv_poll_init(uv_default_loop(), &poll_handle, fd)); + ASSERT_OK(uv_poll_start(&poll_handle, UV_READABLE, (uv_poll_cb) abort)); + ASSERT_NE(0, uv_run(uv_default_loop(), UV_RUN_NOWAIT)); uv_close((uv_handle_t*) &poll_handle, NULL); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(0 == close(fd)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(close(fd)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } #endif /* UV_HAVE_KQUEUE */ diff --git a/test/test-process-priority.c b/test/test-process-priority.c index b3d0a85bdd7..941e4b36391 100644 --- a/test/test-process-priority.c +++ b/test/test-process-priority.c @@ -35,7 +35,7 @@ TEST_IMPL(process_priority) { /* Verify that passing a NULL pointer returns UV_EINVAL. */ r = uv_os_getpriority(0, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); /* Verify that all valid values work. */ for (i = UV_PRIORITY_HIGHEST; i <= UV_PRIORITY_LOW; i++) { @@ -46,38 +46,38 @@ TEST_IMPL(process_priority) { if (r == UV_EACCES) continue; - ASSERT(r == 0); - ASSERT(uv_os_getpriority(0, &priority) == 0); + ASSERT_OK(r); + ASSERT_OK(uv_os_getpriority(0, &priority)); /* Verify that the priority values match on Unix, and are range mapped on Windows. */ #ifndef _WIN32 - ASSERT(priority == i); + ASSERT_EQ(priority, i); #else /* On Windows, only elevated users can set UV_PRIORITY_HIGHEST. Other users will silently be set to UV_PRIORITY_HIGH. */ if (i < UV_PRIORITY_HIGH) ASSERT(priority == UV_PRIORITY_HIGHEST || priority == UV_PRIORITY_HIGH); else if (i < UV_PRIORITY_ABOVE_NORMAL) - ASSERT(priority == UV_PRIORITY_HIGH); + ASSERT_EQ(priority, UV_PRIORITY_HIGH); else if (i < UV_PRIORITY_NORMAL) - ASSERT(priority == UV_PRIORITY_ABOVE_NORMAL); + ASSERT_EQ(priority, UV_PRIORITY_ABOVE_NORMAL); else if (i < UV_PRIORITY_BELOW_NORMAL) - ASSERT(priority == UV_PRIORITY_NORMAL); + ASSERT_EQ(priority, UV_PRIORITY_NORMAL); else if (i < UV_PRIORITY_LOW) - ASSERT(priority == UV_PRIORITY_BELOW_NORMAL); + ASSERT_EQ(priority, UV_PRIORITY_BELOW_NORMAL); else - ASSERT(priority == UV_PRIORITY_LOW); + ASSERT_EQ(priority, UV_PRIORITY_LOW); #endif /* Verify that the current PID and 0 are equivalent. */ - ASSERT(uv_os_getpriority(uv_os_getpid(), &r) == 0); - ASSERT(priority == r); + ASSERT_OK(uv_os_getpriority(uv_os_getpid(), &r)); + ASSERT_EQ(priority, r); } /* Verify that invalid priorities return UV_EINVAL. */ - ASSERT(uv_os_setpriority(0, UV_PRIORITY_HIGHEST - 1) == UV_EINVAL); - ASSERT(uv_os_setpriority(0, UV_PRIORITY_LOW + 1) == UV_EINVAL); + ASSERT_EQ(uv_os_setpriority(0, UV_PRIORITY_HIGHEST - 1), UV_EINVAL); + ASSERT_EQ(uv_os_setpriority(0, UV_PRIORITY_LOW + 1), UV_EINVAL); return 0; } diff --git a/test/test-process-title-threadsafe.c b/test/test-process-title-threadsafe.c index 927643cc8c9..05baaf44a45 100644 --- a/test/test-process-title-threadsafe.c +++ b/test/test-process-title-threadsafe.c @@ -46,7 +46,7 @@ static void getter_thread_body(void* arg) { getter_sem = arg; while (UV_EAGAIN == uv_sem_trywait(getter_sem)) { - ASSERT(0 == uv_get_process_title(buffer, sizeof(buffer))); + ASSERT_OK(uv_get_process_title(buffer, sizeof(buffer))); /* The maximum size of the process title on some platforms depends on * the total size of the argv vector. It's therefore possible to read @@ -70,10 +70,10 @@ static void setter_thread_body(void* arg) { int i; for (i = 0; i < NUM_ITERATIONS; i++) { - ASSERT(0 == uv_set_process_title(titles[0])); - ASSERT(0 == uv_set_process_title(titles[1])); - ASSERT(0 == uv_set_process_title(titles[2])); - ASSERT(0 == uv_set_process_title(titles[3])); + ASSERT_OK(uv_set_process_title(titles[0])); + ASSERT_OK(uv_set_process_title(titles[1])); + ASSERT_OK(uv_set_process_title(titles[2])); + ASSERT_OK(uv_set_process_title(titles[3])); } } @@ -89,20 +89,19 @@ TEST_IMPL(process_title_threadsafe) { RETURN_SKIP("uv_(get|set)_process_title is not implemented."); #endif - ASSERT(0 == uv_set_process_title(titles[0])); + ASSERT_OK(uv_set_process_title(titles[0])); - ASSERT_EQ(0, uv_sem_init(&getter_sem, 0)); - ASSERT_EQ(0, - uv_thread_create(&getter_thread, getter_thread_body, &getter_sem)); + ASSERT_OK(uv_sem_init(&getter_sem, 0)); + ASSERT_OK(uv_thread_create(&getter_thread, getter_thread_body, &getter_sem)); for (i = 0; i < (int) ARRAY_SIZE(setter_threads); i++) - ASSERT(0 == uv_thread_create(&setter_threads[i], setter_thread_body, NULL)); + ASSERT_OK(uv_thread_create(&setter_threads[i], setter_thread_body, NULL)); for (i = 0; i < (int) ARRAY_SIZE(setter_threads); i++) - ASSERT(0 == uv_thread_join(&setter_threads[i])); + ASSERT_OK(uv_thread_join(&setter_threads[i])); uv_sem_post(&getter_sem); - ASSERT_EQ(0, uv_thread_join(&getter_thread)); + ASSERT_OK(uv_thread_join(&getter_thread)); uv_sem_destroy(&getter_sem); return 0; diff --git a/test/test-process-title.c b/test/test-process-title.c index 35a14809fb3..7178cf87da4 100644 --- a/test/test-process-title.c +++ b/test/test-process-title.c @@ -29,15 +29,15 @@ static void set_title(const char* title) { int err; err = uv_get_process_title(buffer, sizeof(buffer)); - ASSERT(err == 0); + ASSERT_OK(err); err = uv_set_process_title(title); - ASSERT(err == 0); + ASSERT_OK(err); err = uv_get_process_title(buffer, sizeof(buffer)); - ASSERT(err == 0); + ASSERT_OK(err); - ASSERT(strcmp(buffer, title) == 0); + ASSERT_OK(strcmp(buffer, title)); } @@ -47,15 +47,15 @@ static void uv_get_process_title_edge_cases(void) { /* Test a NULL buffer */ r = uv_get_process_title(NULL, 100); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); /* Test size of zero */ r = uv_get_process_title(buffer, 0); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); /* Test for insufficient buffer size */ r = uv_get_process_title(buffer, 1); - ASSERT(r == UV_ENOBUFS); + ASSERT_EQ(r, UV_ENOBUFS); } @@ -77,8 +77,8 @@ TEST_IMPL(process_title) { static void exit_cb(uv_process_t* process, int64_t status, int signo) { - ASSERT(status == 0); - ASSERT(signo == 0); + ASSERT_OK(status); + ASSERT_OK(signo); uv_close((uv_handle_t*) process, NULL); } @@ -97,7 +97,7 @@ TEST_IMPL(process_title_big_argv) { #endif exepath_size = sizeof(exepath) - 1; - ASSERT(0 == uv_exepath(exepath, &exepath_size)); + ASSERT_OK(uv_exepath(exepath, &exepath_size)); exepath[exepath_size] = '\0'; memset(jumbo, 'x', sizeof(jumbo) - 1); @@ -117,10 +117,10 @@ TEST_IMPL(process_title_big_argv) { options.args = args; options.exit_cb = exit_cb; - ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_spawn(uv_default_loop(), &process, &options)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -131,5 +131,5 @@ void process_title_big_argv(void) { /* Return value deliberately ignored. */ uv_get_process_title(buf, sizeof(buf)); - ASSERT(0 != strcmp(buf, "fail")); + ASSERT_NE(0, strcmp(buf, "fail")); } diff --git a/test/test-queue-foreach-delete.c b/test/test-queue-foreach-delete.c index 049ea776e34..b3a2d19c408 100644 --- a/test/test-queue-foreach-delete.c +++ b/test/test-queue-foreach-delete.c @@ -29,7 +29,7 @@ * The idea behind the test is as follows. * Certain handle types are stored in a queue internally. * Extra care should be taken for removal of a handle from the queue while iterating over the queue. - * (i.e., QUEUE_REMOVE() called within QUEUE_FOREACH()) + * (i.e., uv__queue_remove() called within uv__queue_foreach()) * This usually happens when someone closes or stops a handle from within its callback. * So we need to check that we haven't screwed the queue on close/stop. * To do so we do the following (for each handle type): @@ -54,7 +54,8 @@ * wrong foreach "next" | * * 4. The callback for handle #1 shouldn't be called because the handle #1 is stopped in the previous step. - * However, if QUEUE_REMOVE() is not handled properly within QUEUE_FOREACH(), the callback _will_ be called. + * However, if uv__queue_remove() is not handled properly within uv__queue_foreach(), the callback _will_ + * be called. */ static const unsigned first_handle_number_idle = 2; @@ -70,7 +71,7 @@ static const unsigned first_handle_number_fs_event = 0; static unsigned name##_cb_calls[3]; \ \ static void name##2_cb(__VA_ARGS__) { \ - ASSERT(handle == &(name)[2]); \ + ASSERT_PTR_EQ(handle, &(name)[2]); \ if (first_handle_number_##name == 2) { \ uv_close((uv_handle_t*)&(name)[2], NULL); \ uv_close((uv_handle_t*)&(name)[1], NULL); \ @@ -79,12 +80,12 @@ static const unsigned first_handle_number_fs_event = 0; } \ \ static void name##1_cb(__VA_ARGS__) { \ - ASSERT(handle == &(name)[1]); \ + ASSERT_PTR_EQ(handle, &(name)[1]); \ ASSERT(0 && "Shouldn't be called" && (&name[0])); \ } \ \ static void name##0_cb(__VA_ARGS__) { \ - ASSERT(handle == &(name)[0]); \ + ASSERT_PTR_EQ(handle, &(name)[0]); \ if (first_handle_number_##name == 0) { \ uv_close((uv_handle_t*)&(name)[0], NULL); \ uv_close((uv_handle_t*)&(name)[1], NULL); \ @@ -104,18 +105,18 @@ static const unsigned first_handle_number_fs_event = 0; for (i = 0; i < ARRAY_SIZE(name); i++) { \ int r; \ r = uv_##name##_init((loop), &(name)[i]); \ - ASSERT(r == 0); \ + ASSERT_OK(r); \ \ r = uv_##name##_start(&(name)[i], name##_cbs[i]); \ - ASSERT(r == 0); \ + ASSERT_OK(r); \ } \ } while (0) #define END_ASSERTS(name) \ do { \ - ASSERT(name##_cb_calls[0] == 1); \ - ASSERT(name##_cb_calls[1] == 0); \ - ASSERT(name##_cb_calls[2] == 1); \ + ASSERT_EQ(1, name##_cb_calls[0]); \ + ASSERT_OK(name##_cb_calls[1]); \ + ASSERT_EQ(1, name##_cb_calls[2]); \ } while (0) DEFINE_GLOBALS_AND_CBS(idle, uv_idle_t* handle) @@ -139,13 +140,13 @@ static void init_and_start_fs_events(uv_loop_t* loop) { for (i = 0; i < ARRAY_SIZE(fs_event); i++) { int r; r = uv_fs_event_init(loop, &fs_event[i]); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fs_event_start(&fs_event[i], (uv_fs_event_cb)fs_event_cbs[i], watched_dir, 0); - ASSERT(r == 0); + ASSERT_OK(r); } } @@ -155,10 +156,10 @@ static void helper_timer_cb(uv_timer_t* thandle) { /* fire all fs_events */ r = uv_fs_utime(thandle->loop, &fs_req, watched_dir, 0, 0, NULL); - ASSERT(r == 0); - ASSERT(fs_req.result == 0); - ASSERT(fs_req.fs_type == UV_FS_UTIME); - ASSERT(strcmp(fs_req.path, watched_dir) == 0); + ASSERT_OK(r); + ASSERT_OK(fs_req.result); + ASSERT_EQ(fs_req.fs_type, UV_FS_UTIME); + ASSERT_OK(strcmp(fs_req.path, watched_dir)); uv_fs_req_cleanup(&fs_req); helper_timer_cb_calls++; @@ -181,24 +182,24 @@ TEST_IMPL(queue_foreach_delete) { /* helper timer to trigger async and fs_event callbacks */ r = uv_timer_init(loop, &timer); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&timer, helper_timer_cb, 0, 0); - ASSERT(r == 0); + ASSERT_OK(r); #endif r = uv_run(loop, UV_RUN_NOWAIT); - ASSERT(r == 1); + ASSERT_EQ(1, r); END_ASSERTS(idle); END_ASSERTS(prepare); END_ASSERTS(check); #ifdef __linux__ - ASSERT(helper_timer_cb_calls == 1); + ASSERT_EQ(1, helper_timer_cb_calls); #endif - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-random.c b/test/test-random.c index 2e3ce4424d2..d82d05782c0 100644 --- a/test/test-random.c +++ b/test/test-random.c @@ -33,16 +33,16 @@ static void random_cb(uv_random_t* req, int status, void* buf, size_t buflen) { memset(zero, 0, sizeof(zero)); - ASSERT(0 == status); - ASSERT(buf == (void*) scratch); + ASSERT_OK(status); + ASSERT_PTR_EQ(buf, (void*) scratch); if (random_cb_called == 0) { - ASSERT(buflen == 0); - ASSERT(0 == memcmp(scratch, zero, sizeof(zero))); + ASSERT_OK(buflen); + ASSERT_OK(memcmp(scratch, zero, sizeof(zero))); } else { - ASSERT(buflen == sizeof(scratch)); + ASSERT_EQ(buflen, sizeof(scratch)); /* Buy a lottery ticket if you manage to trip this assertion. */ - ASSERT(0 != memcmp(scratch, zero, sizeof(zero))); + ASSERT_NE(0, memcmp(scratch, zero, sizeof(zero))); } random_cb_called++; @@ -54,23 +54,23 @@ TEST_IMPL(random_async) { uv_loop_t* loop; loop = uv_default_loop(); - ASSERT(UV_EINVAL == uv_random(loop, &req, scratch, sizeof(scratch), -1, - random_cb)); - ASSERT(UV_E2BIG == uv_random(loop, &req, scratch, -1, -1, random_cb)); + ASSERT_EQ(UV_EINVAL, uv_random(loop, &req, scratch, sizeof(scratch), -1, + random_cb)); + ASSERT_EQ(UV_E2BIG, uv_random(loop, &req, scratch, -1, -1, random_cb)); - ASSERT(0 == uv_random(loop, &req, scratch, 0, 0, random_cb)); - ASSERT(0 == random_cb_called); + ASSERT_OK(uv_random(loop, &req, scratch, 0, 0, random_cb)); + ASSERT_OK(random_cb_called); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(1 == random_cb_called); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(1, random_cb_called); - ASSERT(0 == uv_random(loop, &req, scratch, sizeof(scratch), 0, random_cb)); - ASSERT(1 == random_cb_called); + ASSERT_OK(uv_random(loop, &req, scratch, sizeof(scratch), 0, random_cb)); + ASSERT_EQ(1, random_cb_called); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(2 == random_cb_called); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(2, random_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -79,16 +79,16 @@ TEST_IMPL(random_sync) { char zero[256]; char buf[256]; - ASSERT(UV_EINVAL == uv_random(NULL, NULL, buf, sizeof(buf), -1, NULL)); - ASSERT(UV_E2BIG == uv_random(NULL, NULL, buf, -1, -1, NULL)); + ASSERT_EQ(UV_EINVAL, uv_random(NULL, NULL, buf, sizeof(buf), -1, NULL)); + ASSERT_EQ(UV_E2BIG, uv_random(NULL, NULL, buf, -1, -1, NULL)); memset(buf, 0, sizeof(buf)); - ASSERT(0 == uv_random(NULL, NULL, buf, sizeof(buf), 0, NULL)); + ASSERT_OK(uv_random(NULL, NULL, buf, sizeof(buf), 0, NULL)); /* Buy a lottery ticket if you manage to trip this assertion. */ memset(zero, 0, sizeof(zero)); - ASSERT(0 != memcmp(buf, zero, sizeof(zero))); + ASSERT_NE(0, memcmp(buf, zero, sizeof(zero))); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-readable-on-eof.c b/test/test-readable-on-eof.c index 68e84542477..2137ac5947f 100644 --- a/test/test-readable-on-eof.c +++ b/test/test-readable-on-eof.c @@ -35,7 +35,7 @@ static int close_cb_called; static void write_cb(uv_write_t* req, int status) { write_cb_called++; - ASSERT_EQ(status, 0); + ASSERT_OK(status); } static void alloc_cb(uv_handle_t* handle, @@ -54,16 +54,16 @@ static void read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { int r; ASSERT_EQ(nread, UV_EOF); - ASSERT_EQ(uv_is_readable(handle), 1); - ASSERT_EQ(uv_is_writable(handle), 1); + ASSERT_EQ(1, uv_is_readable(handle)); + ASSERT_EQ(1, uv_is_writable(handle)); if (++read_cb_called == 3) { uv_close((uv_handle_t*) handle, close_cb); - ASSERT_EQ(uv_is_readable(handle), 0); - ASSERT_EQ(uv_is_writable(handle), 0); + ASSERT_OK(uv_is_readable(handle)); + ASSERT_OK(uv_is_writable(handle)); } else { r = uv_read_start((uv_stream_t*) &tcp_client, alloc_cb, read_cb); - ASSERT_EQ(r, 0); + ASSERT_OK(r); } } @@ -72,7 +72,7 @@ static void connect_cb(uv_connect_t* req, int status) { uv_buf_t close_me; connect_cb_called++; - ASSERT_EQ(status, 0); + ASSERT_OK(status); read_cb((uv_stream_t*) &tcp_client, UV_EOF, NULL); @@ -84,28 +84,27 @@ static void connect_cb(uv_connect_t* req, int status) { 1, write_cb); - ASSERT_EQ(r, 0); + ASSERT_OK(r); } TEST_IMPL(readable_on_eof) { struct sockaddr_in sa; - ASSERT_EQ(uv_ip4_addr("127.0.0.1", TEST_PORT, &sa), 0); - ASSERT_EQ(uv_loop_init(&loop), 0); - ASSERT_EQ(uv_tcp_init(&loop, &tcp_client), 0); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &sa)); + ASSERT_OK(uv_loop_init(&loop)); + ASSERT_OK(uv_tcp_init(&loop, &tcp_client)); - ASSERT_EQ(uv_tcp_connect(&connect_req, + ASSERT_OK(uv_tcp_connect(&connect_req, &tcp_client, (const struct sockaddr*) &sa, - connect_cb), - 0); + connect_cb)); - ASSERT_EQ(uv_run(&loop, UV_RUN_DEFAULT), 0); + ASSERT_OK(uv_run(&loop, UV_RUN_DEFAULT)); - ASSERT_EQ(connect_cb_called, 1); - ASSERT_EQ(read_cb_called, 3); - ASSERT_EQ(write_cb_called, 1); - ASSERT_EQ(close_cb_called, 1); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(3, read_cb_called); + ASSERT_EQ(1, write_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(&loop); return 0; } diff --git a/test/test-ref.c b/test/test-ref.c index d24ea4a01e8..7a2c33790ab 100644 --- a/test/test-ref.c +++ b/test/test-ref.c @@ -47,9 +47,9 @@ static void close_cb(uv_handle_t* handle) { static void do_close(void* handle) { close_cb_called = 0; uv_close((uv_handle_t*)handle, close_cb); - ASSERT(close_cb_called == 0); + ASSERT_OK(close_cb_called); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, close_cb_called); } @@ -63,19 +63,19 @@ static void fail_cb2(void) { } -static void req_cb(uv_handle_t* req, int status) { +static void req_cb(uv_udp_send_t* req, int status) { req_cb_called++; } static void shutdown_cb(uv_shutdown_t* req, int status) { - ASSERT(req == &shutdown_req); + ASSERT_PTR_EQ(req, &shutdown_req); shutdown_cb_called++; } static void write_cb(uv_write_t* req, int status) { - ASSERT(req == &write_req); + ASSERT_PTR_EQ(req, &write_req); uv_shutdown(&shutdown_req, req->handle, shutdown_cb); write_cb_called++; } @@ -83,8 +83,8 @@ static void write_cb(uv_write_t* req, int status) { static void connect_and_write(uv_connect_t* req, int status) { uv_buf_t buf = uv_buf_init(buffer, sizeof buffer); - ASSERT(req == &connect_req); - ASSERT(status == 0); + ASSERT_PTR_EQ(req, &connect_req); + ASSERT_OK(status); uv_write(&write_req, req->handle, &buf, 1, write_cb); connect_cb_called++; } @@ -92,8 +92,8 @@ static void connect_and_write(uv_connect_t* req, int status) { static void connect_and_shutdown(uv_connect_t* req, int status) { - ASSERT(req == &connect_req); - ASSERT(status == 0); + ASSERT_PTR_EQ(req, &connect_req); + ASSERT_OK(status); uv_shutdown(&shutdown_req, req->handle, shutdown_cb); connect_cb_called++; } @@ -101,7 +101,7 @@ static void connect_and_shutdown(uv_connect_t* req, int status) { TEST_IMPL(ref) { uv_run(uv_default_loop(), UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -113,7 +113,7 @@ TEST_IMPL(idle_ref) { uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -124,7 +124,7 @@ TEST_IMPL(async_ref) { uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -136,7 +136,7 @@ TEST_IMPL(prepare_ref) { uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -148,7 +148,7 @@ TEST_IMPL(check_ref) { uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -165,7 +165,7 @@ TEST_IMPL(unref_in_prepare_cb) { uv_prepare_start(&h, prepare_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -176,7 +176,7 @@ TEST_IMPL(timer_ref) { uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -188,7 +188,7 @@ TEST_IMPL(timer_ref2) { uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -203,7 +203,7 @@ TEST_IMPL(fs_event_ref) { uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -215,7 +215,7 @@ TEST_IMPL(fs_poll_ref) { uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -226,7 +226,7 @@ TEST_IMPL(tcp_ref) { uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -238,7 +238,7 @@ TEST_IMPL(tcp_ref2) { uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -250,8 +250,8 @@ TEST_IMPL(tcp_ref2b) { uv_unref((uv_handle_t*)&h); uv_close((uv_handle_t*)&h, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 1); - MAKE_VALGRIND_HAPPY(); + ASSERT_EQ(1, close_cb_called); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -259,7 +259,7 @@ TEST_IMPL(tcp_ref2b) { TEST_IMPL(tcp_ref3) { struct sockaddr_in addr; uv_tcp_t h; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); uv_tcp_init(uv_default_loop(), &h); uv_tcp_connect(&connect_req, &h, @@ -267,10 +267,10 @@ TEST_IMPL(tcp_ref3) { connect_and_shutdown); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(connect_cb_called == 1); - ASSERT(shutdown_cb_called == 1); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(1, shutdown_cb_called); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -278,7 +278,7 @@ TEST_IMPL(tcp_ref3) { TEST_IMPL(tcp_ref4) { struct sockaddr_in addr; uv_tcp_t h; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); uv_tcp_init(uv_default_loop(), &h); uv_tcp_connect(&connect_req, &h, @@ -286,11 +286,11 @@ TEST_IMPL(tcp_ref4) { connect_and_write); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(connect_cb_called == 1); - ASSERT(write_cb_called == 1); - ASSERT(shutdown_cb_called == 1); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(1, write_cb_called); + ASSERT_EQ(1, shutdown_cb_called); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -301,7 +301,7 @@ TEST_IMPL(udp_ref) { uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -309,14 +309,14 @@ TEST_IMPL(udp_ref) { TEST_IMPL(udp_ref2) { struct sockaddr_in addr; uv_udp_t h; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); uv_udp_init(uv_default_loop(), &h); uv_udp_bind(&h, (const struct sockaddr*) &addr, 0); uv_udp_recv_start(&h, (uv_alloc_cb)fail_cb, (uv_udp_recv_cb)fail_cb); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -327,20 +327,20 @@ TEST_IMPL(udp_ref3) { uv_udp_send_t req; uv_udp_t h; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); uv_udp_init(uv_default_loop(), &h); uv_udp_send(&req, &h, &buf, 1, (const struct sockaddr*) &addr, - (uv_udp_send_cb) req_cb); + req_cb); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(req_cb_called == 1); + ASSERT_EQ(1, req_cb_called); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -351,7 +351,7 @@ TEST_IMPL(pipe_ref) { uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -363,7 +363,7 @@ TEST_IMPL(pipe_ref2) { uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -374,10 +374,10 @@ TEST_IMPL(pipe_ref3) { uv_pipe_connect(&connect_req, &h, TEST_PIPENAME, connect_and_shutdown); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(connect_cb_called == 1); - ASSERT(shutdown_cb_called == 1); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(1, shutdown_cb_called); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -388,11 +388,11 @@ TEST_IMPL(pipe_ref4) { uv_pipe_connect(&connect_req, &h, TEST_PIPENAME, connect_and_write); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(connect_cb_called == 1); - ASSERT(write_cb_called == 1); - ASSERT(shutdown_cb_called == 1); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(1, write_cb_called); + ASSERT_EQ(1, shutdown_cb_called); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -410,7 +410,7 @@ TEST_IMPL(process_ref) { exepath_size = sizeof(exepath); r = uv_exepath(exepath, &exepath_size); - ASSERT(r == 0); + ASSERT_OK(r); argv[0] = exepath; options.file = exepath; @@ -418,17 +418,17 @@ TEST_IMPL(process_ref) { options.exit_cb = NULL; r = uv_spawn(uv_default_loop(), &h, &options); - ASSERT(r == 0); + ASSERT_OK(r); uv_unref((uv_handle_t*)&h); uv_run(uv_default_loop(), UV_RUN_DEFAULT); r = uv_process_kill(&h, /* SIGTERM */ 15); - ASSERT(r == 0); + ASSERT_OK(r); do_close(&h); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -437,9 +437,9 @@ TEST_IMPL(has_ref) { uv_idle_t h; uv_idle_init(uv_default_loop(), &h); uv_ref((uv_handle_t*)&h); - ASSERT(uv_has_ref((uv_handle_t*)&h) == 1); + ASSERT_EQ(1, uv_has_ref((uv_handle_t*)&h)); uv_unref((uv_handle_t*)&h); - ASSERT(uv_has_ref((uv_handle_t*)&h) == 0); - MAKE_VALGRIND_HAPPY(); + ASSERT_OK(uv_has_ref((uv_handle_t*)&h)); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-run-nowait.c b/test/test-run-nowait.c index 43524f636d8..89f5f55b18a 100644 --- a/test/test-run-nowait.c +++ b/test/test-run-nowait.c @@ -27,7 +27,7 @@ static int timer_called = 0; static void timer_cb(uv_timer_t* handle) { - ASSERT(handle == &timer_handle); + ASSERT_PTR_EQ(handle, &timer_handle); timer_called = 1; } @@ -38,8 +38,9 @@ TEST_IMPL(run_nowait) { uv_timer_start(&timer_handle, timer_cb, 100, 100); r = uv_run(uv_default_loop(), UV_RUN_NOWAIT); - ASSERT(r != 0); - ASSERT(timer_called == 0); + ASSERT(r); + ASSERT_OK(timer_called); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-run-once.c b/test/test-run-once.c index 10cbf95e4ad..0ae0082c637 100644 --- a/test/test-run-once.c +++ b/test/test-run-once.c @@ -29,7 +29,7 @@ static int idle_counter; static void idle_cb(uv_idle_t* handle) { - ASSERT(handle == &idle_handle); + ASSERT_PTR_EQ(handle, &idle_handle); if (++idle_counter == NUM_TICKS) uv_idle_stop(handle); @@ -41,8 +41,8 @@ TEST_IMPL(run_once) { uv_idle_start(&idle_handle, idle_cb); while (uv_run(uv_default_loop(), UV_RUN_ONCE)); - ASSERT(idle_counter == NUM_TICKS); + ASSERT_EQ(idle_counter, NUM_TICKS); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-semaphore.c b/test/test-semaphore.c index ac03bb08f17..ad54808d623 100644 --- a/test/test-semaphore.c +++ b/test/test-semaphore.c @@ -40,7 +40,7 @@ static void worker(void* arg) { uv_sleep(c->delay); uv_mutex_lock(&c->mutex); - ASSERT(c->posted == 0); + ASSERT_OK(c->posted); uv_sem_post(&c->sem); c->posted = 1; uv_mutex_unlock(&c->mutex); @@ -53,17 +53,17 @@ TEST_IMPL(semaphore_1) { memset(&wc, 0, sizeof(wc)); - ASSERT(0 == uv_sem_init(&wc.sem, 0)); - ASSERT(0 == uv_mutex_init(&wc.mutex)); - ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + ASSERT_OK(uv_sem_init(&wc.sem, 0)); + ASSERT_OK(uv_mutex_init(&wc.mutex)); + ASSERT_OK(uv_thread_create(&thread, worker, &wc)); uv_sleep(100); uv_mutex_lock(&wc.mutex); - ASSERT(wc.posted == 1); + ASSERT_EQ(1, wc.posted); uv_sem_wait(&wc.sem); /* should not block */ uv_mutex_unlock(&wc.mutex); /* ergo, it should be ok to unlock after wait */ - ASSERT(0 == uv_thread_join(&thread)); + ASSERT_OK(uv_thread_join(&thread)); uv_mutex_destroy(&wc.mutex); uv_sem_destroy(&wc.sem); @@ -78,13 +78,13 @@ TEST_IMPL(semaphore_2) { memset(&wc, 0, sizeof(wc)); wc.delay = 100; - ASSERT(0 == uv_sem_init(&wc.sem, 0)); - ASSERT(0 == uv_mutex_init(&wc.mutex)); - ASSERT(0 == uv_thread_create(&thread, worker, &wc)); + ASSERT_OK(uv_sem_init(&wc.sem, 0)); + ASSERT_OK(uv_mutex_init(&wc.mutex)); + ASSERT_OK(uv_thread_create(&thread, worker, &wc)); uv_sem_wait(&wc.sem); - ASSERT(0 == uv_thread_join(&thread)); + ASSERT_OK(uv_thread_join(&thread)); uv_mutex_destroy(&wc.mutex); uv_sem_destroy(&wc.sem); @@ -95,15 +95,15 @@ TEST_IMPL(semaphore_2) { TEST_IMPL(semaphore_3) { uv_sem_t sem; - ASSERT(0 == uv_sem_init(&sem, 3)); + ASSERT_OK(uv_sem_init(&sem, 3)); uv_sem_wait(&sem); /* should not block */ uv_sem_wait(&sem); /* should not block */ - ASSERT(0 == uv_sem_trywait(&sem)); - ASSERT(UV_EAGAIN == uv_sem_trywait(&sem)); + ASSERT_OK(uv_sem_trywait(&sem)); + ASSERT_EQ(UV_EAGAIN, uv_sem_trywait(&sem)); uv_sem_post(&sem); - ASSERT(0 == uv_sem_trywait(&sem)); - ASSERT(UV_EAGAIN == uv_sem_trywait(&sem)); + ASSERT_OK(uv_sem_trywait(&sem)); + ASSERT_EQ(UV_EAGAIN, uv_sem_trywait(&sem)); uv_sem_destroy(&sem); diff --git a/test/test-shutdown-close.c b/test/test-shutdown-close.c index 78c369be2d9..306404afb45 100644 --- a/test/test-shutdown-close.c +++ b/test/test-shutdown-close.c @@ -37,7 +37,7 @@ static int close_cb_called = 0; static void shutdown_cb(uv_shutdown_t* req, int status) { - ASSERT(req == &shutdown_req); + ASSERT_PTR_EQ(req, &shutdown_req); ASSERT(status == 0 || status == UV_ECANCELED); shutdown_cb_called++; } @@ -51,14 +51,14 @@ static void close_cb(uv_handle_t* handle) { static void connect_cb(uv_connect_t* req, int status) { int r; - ASSERT(req == &connect_req); - ASSERT(status == 0); + ASSERT_PTR_EQ(req, &connect_req); + ASSERT_OK(status); r = uv_shutdown(&shutdown_req, req->handle, shutdown_cb); - ASSERT(r == 0); - ASSERT(0 == uv_is_closing((uv_handle_t*) req->handle)); + ASSERT_OK(r); + ASSERT_OK(uv_is_closing((uv_handle_t*) req->handle)); uv_close((uv_handle_t*) req->handle, close_cb); - ASSERT(1 == uv_is_closing((uv_handle_t*) req->handle)); + ASSERT_EQ(1, uv_is_closing((uv_handle_t*) req->handle)); connect_cb_called++; } @@ -69,22 +69,22 @@ TEST_IMPL(shutdown_close_tcp) { uv_tcp_t h; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &h); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_connect(&connect_req, &h, (const struct sockaddr*) &addr, connect_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(connect_cb_called == 1); - ASSERT(shutdown_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(1, shutdown_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -94,15 +94,15 @@ TEST_IMPL(shutdown_close_pipe) { int r; r = uv_pipe_init(uv_default_loop(), &h, 0); - ASSERT(r == 0); + ASSERT_OK(r); uv_pipe_connect(&connect_req, &h, TEST_PIPENAME, connect_cb); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(connect_cb_called == 1); - ASSERT(shutdown_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(1, shutdown_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-shutdown-eof.c b/test/test-shutdown-eof.c index 0abab9175e9..6669c29769b 100644 --- a/test/test-shutdown-eof.c +++ b/test/test-shutdown-eof.c @@ -46,7 +46,7 @@ static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { static void read_cb(uv_stream_t* t, ssize_t nread, const uv_buf_t* buf) { - ASSERT((uv_tcp_t*)t == &tcp); + ASSERT_PTR_EQ((uv_tcp_t*)t, &tcp); if (nread == 0) { free(buf->base); @@ -54,14 +54,14 @@ static void read_cb(uv_stream_t* t, ssize_t nread, const uv_buf_t* buf) { } if (!got_q) { - ASSERT(nread == 1); + ASSERT_EQ(1, nread); ASSERT(!got_eof); - ASSERT(buf->base[0] == 'Q'); + ASSERT_EQ(buf->base[0], 'Q'); free(buf->base); got_q = 1; puts("got Q"); } else { - ASSERT(nread == UV_EOF); + ASSERT_EQ(nread, UV_EOF); if (buf->base) { free(buf->base); } @@ -72,24 +72,24 @@ static void read_cb(uv_stream_t* t, ssize_t nread, const uv_buf_t* buf) { static void shutdown_cb(uv_shutdown_t *req, int status) { - ASSERT(req == &shutdown_req); + ASSERT_PTR_EQ(req, &shutdown_req); - ASSERT(called_connect_cb == 1); + ASSERT_EQ(1, called_connect_cb); ASSERT(!got_eof); - ASSERT(called_tcp_close_cb == 0); - ASSERT(called_timer_close_cb == 0); - ASSERT(called_timer_cb == 0); + ASSERT_OK(called_tcp_close_cb); + ASSERT_OK(called_timer_close_cb); + ASSERT_OK(called_timer_cb); called_shutdown_cb++; } static void connect_cb(uv_connect_t *req, int status) { - ASSERT(status == 0); - ASSERT(req == &connect_req); + ASSERT_OK(status); + ASSERT_PTR_EQ(req, &connect_req); /* Start reading from our connection so we can receive the EOF. */ - ASSERT_EQ(0, uv_read_start((uv_stream_t*)&tcp, alloc_cb, read_cb)); + ASSERT_OK(uv_read_start((uv_stream_t*)&tcp, alloc_cb, read_cb)); /* Check error handling. */ ASSERT_EQ(UV_EALREADY, uv_read_start((uv_stream_t*)&tcp, alloc_cb, read_cb)); @@ -107,37 +107,37 @@ static void connect_cb(uv_connect_t *req, int status) { uv_shutdown(&shutdown_req, (uv_stream_t*) &tcp, shutdown_cb); called_connect_cb++; - ASSERT(called_shutdown_cb == 0); + ASSERT_OK(called_shutdown_cb); } static void tcp_close_cb(uv_handle_t* handle) { - ASSERT(handle == (uv_handle_t*) &tcp); + ASSERT_PTR_EQ(handle, (uv_handle_t*) &tcp); - ASSERT(called_connect_cb == 1); + ASSERT_EQ(1, called_connect_cb); ASSERT(got_q); ASSERT(got_eof); - ASSERT(called_timer_cb == 1); + ASSERT_EQ(1, called_timer_cb); called_tcp_close_cb++; } static void timer_close_cb(uv_handle_t* handle) { - ASSERT(handle == (uv_handle_t*) &timer); + ASSERT_PTR_EQ(handle, (uv_handle_t*) &timer); called_timer_close_cb++; } static void timer_cb(uv_timer_t* handle) { - ASSERT(handle == &timer); + ASSERT_PTR_EQ(handle, &timer); uv_close((uv_handle_t*) handle, timer_close_cb); /* * The most important assert of the test: we have not received * tcp_close_cb yet. */ - ASSERT(called_tcp_close_cb == 0); + ASSERT_OK(called_tcp_close_cb); uv_close((uv_handle_t*) &tcp, tcp_close_cb); called_timer_cb++; @@ -158,11 +158,11 @@ TEST_IMPL(shutdown_eof) { qbuf.len = 1; r = uv_timer_init(uv_default_loop(), &timer); - ASSERT(r == 0); + ASSERT_OK(r); uv_timer_start(&timer, timer_cb, 100, 0); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); r = uv_tcp_init(uv_default_loop(), &tcp); ASSERT(!r); @@ -174,15 +174,15 @@ TEST_IMPL(shutdown_eof) { uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(called_connect_cb == 1); - ASSERT(called_shutdown_cb == 1); + ASSERT_EQ(1, called_connect_cb); + ASSERT_EQ(1, called_shutdown_cb); ASSERT(got_eof); ASSERT(got_q); - ASSERT(called_tcp_close_cb == 1); - ASSERT(called_timer_close_cb == 1); - ASSERT(called_timer_cb == 1); + ASSERT_EQ(1, called_tcp_close_cb); + ASSERT_EQ(1, called_timer_close_cb); + ASSERT_EQ(1, called_timer_cb); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-shutdown-simultaneous.c b/test/test-shutdown-simultaneous.c index 7de3bd42252..0dd8e353c86 100644 --- a/test/test-shutdown-simultaneous.c +++ b/test/test-shutdown-simultaneous.c @@ -44,8 +44,8 @@ static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { static void shutdown_cb(uv_shutdown_t *req, int status) { ASSERT_PTR_EQ(req, &shutdown_req); - ASSERT_EQ(called_connect_cb, 1); - ASSERT_EQ(called_tcp_close_cb, 0); + ASSERT_EQ(1, called_connect_cb); + ASSERT_OK(called_tcp_close_cb); } @@ -58,8 +58,8 @@ static void read_cb(uv_stream_t* t, ssize_t nread, const uv_buf_t* buf) { } if (!got_q) { - ASSERT_EQ(nread, 4); - ASSERT_EQ(got_eof, 0); + ASSERT_EQ(4, nread); + ASSERT_OK(got_eof); ASSERT_MEM_EQ(buf->base, "QQSS", 4); free(buf->base); got_q = 1; @@ -79,11 +79,11 @@ static void read_cb(uv_stream_t* t, ssize_t nread, const uv_buf_t* buf) { static void connect_cb(uv_connect_t *req, int status) { - ASSERT_EQ(status, 0); + ASSERT_OK(status); ASSERT_PTR_EQ(req, &connect_req); /* Start reading from our connection so we can receive the EOF. */ - ASSERT_EQ(0, uv_read_start((uv_stream_t*)&tcp, alloc_cb, read_cb)); + ASSERT_OK(uv_read_start((uv_stream_t*)&tcp, alloc_cb, read_cb)); /* Check error handling. */ ASSERT_EQ(UV_EALREADY, uv_read_start((uv_stream_t*)&tcp, alloc_cb, read_cb)); @@ -98,7 +98,7 @@ static void connect_cb(uv_connect_t *req, int status) { ASSERT_EQ(qbuf.len, uv_try_write((uv_stream_t*) &tcp, &qbuf, 1)); called_connect_cb++; - ASSERT_EQ(called_shutdown_cb, 0); + ASSERT_OK(called_shutdown_cb); } @@ -113,23 +113,23 @@ TEST_IMPL(shutdown_simultaneous) { qbuf.base = "QQSS"; qbuf.len = 4; - ASSERT_EQ(0, uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); r = uv_tcp_init(uv_default_loop(), &tcp); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_tcp_connect(&connect_req, &tcp, (const struct sockaddr*) &server_addr, connect_cb); - ASSERT_EQ(r, 0); + ASSERT_OK(r); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT_EQ(called_connect_cb, 1); - ASSERT_EQ(called_shutdown_cb, 1); - ASSERT_EQ(got_eof, 1); - ASSERT_EQ(got_q, 1); + ASSERT_EQ(1, called_connect_cb); + ASSERT_EQ(1, called_shutdown_cb); + ASSERT_EQ(1, got_eof); + ASSERT_EQ(1, got_q); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-shutdown-twice.c b/test/test-shutdown-twice.c index d7aae89914d..c21a98340bd 100644 --- a/test/test-shutdown-twice.c +++ b/test/test-shutdown-twice.c @@ -37,8 +37,8 @@ static void close_cb(uv_handle_t* handle) { } static void shutdown_cb(uv_shutdown_t* req, int status) { - ASSERT(req == &req1); - ASSERT(status == 0); + ASSERT_PTR_EQ(req, &req1); + ASSERT_OK(status); shutdown_cb_called++; uv_close((uv_handle_t*) req->handle, close_cb); } @@ -46,12 +46,12 @@ static void shutdown_cb(uv_shutdown_t* req, int status) { static void connect_cb(uv_connect_t* req, int status) { int r; - ASSERT(status == 0); + ASSERT_OK(status); r = uv_shutdown(&req1, req->handle, shutdown_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_shutdown(&req2, req->handle, shutdown_cb); - ASSERT(r != 0); + ASSERT(r); } @@ -63,23 +63,23 @@ TEST_IMPL(shutdown_twice) { uv_connect_t connect_req; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); loop = uv_default_loop(); r = uv_tcp_init(loop, &h); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_connect(&connect_req, &h, (const struct sockaddr*) &addr, connect_cb); - ASSERT(r == 0); + ASSERT_OK(r); - r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT_OK(r); - ASSERT(shutdown_cb_called == 1); + ASSERT_EQ(1, shutdown_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-signal-multiple-loops.c b/test/test-signal-multiple-loops.c index 7d61ff61e0d..e68eabf6fc2 100644 --- a/test/test-signal-multiple-loops.c +++ b/test/test-signal-multiple-loops.c @@ -50,30 +50,30 @@ enum signal_action { }; static uv_sem_t sem; -static uv_mutex_t counter_lock; -static volatile int stop = 0; +static uv_mutex_t lock; +static int stop = 0; -static volatile int signal1_cb_counter = 0; -static volatile int signal2_cb_counter = 0; -static volatile int loop_creation_counter = 0; +static int signal1_cb_counter = 0; +static int signal2_cb_counter = 0; +static int loop_creation_counter = 0; -static void increment_counter(volatile int* counter) { - uv_mutex_lock(&counter_lock); +static void increment_counter(int* counter) { + uv_mutex_lock(&lock); ++(*counter); - uv_mutex_unlock(&counter_lock); + uv_mutex_unlock(&lock); } static void signal1_cb(uv_signal_t* handle, int signum) { - ASSERT(signum == SIGUSR1); + ASSERT_EQ(signum, SIGUSR1); increment_counter(&signal1_cb_counter); uv_signal_stop(handle); } static void signal2_cb(uv_signal_t* handle, int signum) { - ASSERT(signum == SIGUSR2); + ASSERT_EQ(signum, SIGUSR2); increment_counter(&signal2_cb_counter); uv_signal_stop(handle); } @@ -89,25 +89,25 @@ static void signal_handling_worker(void* context) { action = (enum signal_action) (uintptr_t) context; - ASSERT(0 == uv_loop_init(&loop)); + ASSERT_OK(uv_loop_init(&loop)); /* Setup the signal watchers and start them. */ if (action == ONLY_SIGUSR1 || action == SIGUSR1_AND_SIGUSR2) { r = uv_signal_init(&loop, &signal1a); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_signal_start(&signal1a, signal1_cb, SIGUSR1); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_signal_init(&loop, &signal1b); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_signal_start(&signal1b, signal1_cb, SIGUSR1); - ASSERT(r == 0); + ASSERT_OK(r); } if (action == ONLY_SIGUSR2 || action == SIGUSR1_AND_SIGUSR2) { r = uv_signal_init(&loop, &signal2); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_signal_start(&signal2, signal2_cb, SIGUSR2); - ASSERT(r == 0); + ASSERT_OK(r); } /* Signal watchers are now set up. */ @@ -117,26 +117,26 @@ static void signal_handling_worker(void* context) { * will return when all signal watchers caught a signal. */ r = uv_run(&loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); /* Restart the signal watchers. */ if (action == ONLY_SIGUSR1 || action == SIGUSR1_AND_SIGUSR2) { r = uv_signal_start(&signal1a, signal1_cb, SIGUSR1); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_signal_start(&signal1b, signal1_cb, SIGUSR1); - ASSERT(r == 0); + ASSERT_OK(r); } if (action == ONLY_SIGUSR2 || action == SIGUSR1_AND_SIGUSR2) { r = uv_signal_start(&signal2, signal2_cb, SIGUSR2); - ASSERT(r == 0); + ASSERT_OK(r); } /* Wait for signals once more. */ uv_sem_post(&sem); r = uv_run(&loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); /* Close the watchers. */ if (action == ONLY_SIGUSR1 || action == SIGUSR1_AND_SIGUSR2) { @@ -150,7 +150,7 @@ static void signal_handling_worker(void* context) { /* Wait for the signal watchers to close. */ r = uv_run(&loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); uv_loop_close(&loop); } @@ -162,6 +162,8 @@ static void signal_unexpected_cb(uv_signal_t* handle, int signum) { static void loop_creating_worker(void* context) { + int done; + (void) context; do { @@ -171,24 +173,28 @@ static void loop_creating_worker(void* context) { loop = malloc(sizeof(*loop)); ASSERT_NOT_NULL(loop); - ASSERT(0 == uv_loop_init(loop)); + ASSERT_OK(uv_loop_init(loop)); r = uv_signal_init(loop, &signal); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_signal_start(&signal, signal_unexpected_cb, SIGTERM); - ASSERT(r == 0); + ASSERT_OK(r); uv_close((uv_handle_t*) &signal, NULL); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); uv_loop_close(loop); free(loop); increment_counter(&loop_creation_counter); - } while (!stop); + + uv_mutex_lock(&lock); + done = stop; + uv_mutex_unlock(&lock); + } while (!done); } @@ -202,8 +208,18 @@ TEST_IMPL(signal_multiple_loops) { #endif /* TODO(gengjiawen): Fix test on QEMU. */ #if defined(__QEMU__) - // See https://github.com/libuv/libuv/issues/2859 + /* See https://github.com/libuv/libuv/issues/2859 */ RETURN_SKIP("QEMU's signal emulation code is notoriously tricky"); +#endif +#if defined(__ASAN__) || defined(__MSAN__) + /* See https://github.com/libuv/libuv/issues/3956 */ + RETURN_SKIP("Test is too slow to run under ASan or MSan"); +#endif +#if defined(__TSAN__) + /* ThreadSanitizer complains - likely legitimately - about data races + * in uv__signal_compare() in src/unix/signal.c but that's pre-existing. + */ + RETURN_SKIP("Fix test under ThreadSanitizer"); #endif uv_thread_t loop_creating_threads[NUM_LOOP_CREATING_THREADS]; uv_thread_t signal_handling_threads[NUM_SIGNAL_HANDLING_THREADS]; @@ -213,17 +229,17 @@ TEST_IMPL(signal_multiple_loops) { int r; r = uv_sem_init(&sem, 0); - ASSERT(r == 0); + ASSERT_OK(r); - r = uv_mutex_init(&counter_lock); - ASSERT(r == 0); + r = uv_mutex_init(&lock); + ASSERT_OK(r); /* Create a couple of threads that create a destroy loops continuously. */ for (i = 0; i < NUM_LOOP_CREATING_THREADS; i++) { r = uv_thread_create(&loop_creating_threads[i], loop_creating_worker, NULL); - ASSERT(r == 0); + ASSERT_OK(r); } /* Create a couple of threads that actually handle signals. */ @@ -237,7 +253,7 @@ TEST_IMPL(signal_multiple_loops) { r = uv_thread_create(&signal_handling_threads[i], signal_handling_worker, (void*) (uintptr_t) action); - ASSERT(r == 0); + ASSERT_OK(r); } /* Wait until all threads have started and set up their signal watchers. */ @@ -245,9 +261,9 @@ TEST_IMPL(signal_multiple_loops) { uv_sem_wait(&sem); r = kill(getpid(), SIGUSR1); - ASSERT(r == 0); + ASSERT_OK(r); r = kill(getpid(), SIGUSR2); - ASSERT(r == 0); + ASSERT_OK(r); /* Wait for all threads to handle these signals. */ for (i = 0; i < NUM_SIGNAL_HANDLING_THREADS; i++) @@ -261,23 +277,25 @@ TEST_IMPL(signal_multiple_loops) { pthread_sigmask(SIG_SETMASK, &sigset, NULL); r = kill(getpid(), SIGUSR1); - ASSERT(r == 0); + ASSERT_OK(r); r = kill(getpid(), SIGUSR2); - ASSERT(r == 0); + ASSERT_OK(r); /* Wait for all signal handling threads to exit. */ for (i = 0; i < NUM_SIGNAL_HANDLING_THREADS; i++) { r = uv_thread_join(&signal_handling_threads[i]); - ASSERT(r == 0); + ASSERT_OK(r); } /* Tell all loop creating threads to stop. */ + uv_mutex_lock(&lock); stop = 1; + uv_mutex_unlock(&lock); /* Wait for all loop creating threads to exit. */ for (i = 0; i < NUM_LOOP_CREATING_THREADS; i++) { r = uv_thread_join(&loop_creating_threads[i]); - ASSERT(r == 0); + ASSERT_OK(r); } uv_sem_destroy(&sem); @@ -288,15 +306,15 @@ TEST_IMPL(signal_multiple_loops) { /* The division by three reflects the fact that we spawn three different * thread groups of (NUM_SIGNAL_HANDLING_THREADS / 3) threads each. */ - ASSERT(signal1_cb_counter == 8 * (NUM_SIGNAL_HANDLING_THREADS / 3)); - ASSERT(signal2_cb_counter == 4 * (NUM_SIGNAL_HANDLING_THREADS / 3)); + ASSERT_EQ(signal1_cb_counter, 8 * (NUM_SIGNAL_HANDLING_THREADS / 3)); + ASSERT_EQ(signal2_cb_counter, 4 * (NUM_SIGNAL_HANDLING_THREADS / 3)); /* We don't know exactly how much loops will be created and destroyed, but at * least there should be 1 for every loop creating thread. */ - ASSERT(loop_creation_counter >= NUM_LOOP_CREATING_THREADS); + ASSERT_GE(loop_creation_counter, NUM_LOOP_CREATING_THREADS); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-signal-pending-on-close.c b/test/test-signal-pending-on-close.c index 428a97ef5ae..42a4296704b 100644 --- a/test/test-signal-pending-on-close.c +++ b/test/test-signal-pending-on-close.c @@ -35,7 +35,7 @@ static int close_cb_called; static void stop_loop_cb(uv_signal_t* signal, int signum) { - ASSERT(signum == SIGPIPE); + ASSERT_EQ(signum, SIGPIPE); uv_stop(signal->loop); } @@ -50,7 +50,7 @@ static void close_cb(uv_handle_t *handle) { static void write_cb(uv_write_t* req, int status) { ASSERT_NOT_NULL(req); - ASSERT(status == UV_EPIPE); + ASSERT_EQ(status, UV_EPIPE); free(buf); uv_close((uv_handle_t *) &pipe_hdl, close_cb); uv_close((uv_handle_t *) &signal_hdl, close_cb); @@ -62,17 +62,17 @@ TEST_IMPL(signal_pending_on_close) { uv_buf_t buffer; int r; - ASSERT(0 == uv_loop_init(&loop)); + ASSERT_OK(uv_loop_init(&loop)); - ASSERT(0 == uv_signal_init(&loop, &signal_hdl)); + ASSERT_OK(uv_signal_init(&loop, &signal_hdl)); - ASSERT(0 == uv_signal_start(&signal_hdl, signal_cb, SIGPIPE)); + ASSERT_OK(uv_signal_start(&signal_hdl, signal_cb, SIGPIPE)); - ASSERT(0 == pipe(pipefds)); + ASSERT_OK(pipe(pipefds)); - ASSERT(0 == uv_pipe_init(&loop, &pipe_hdl, 0)); + ASSERT_OK(uv_pipe_init(&loop, &pipe_hdl, 0)); - ASSERT(0 == uv_pipe_open(&pipe_hdl, pipefds[1])); + ASSERT_OK(uv_pipe_open(&pipe_hdl, pipefds[1])); /* Write data large enough so it needs loop iteration */ buf = malloc(1<<24); @@ -81,38 +81,35 @@ TEST_IMPL(signal_pending_on_close) { buffer = uv_buf_init(buf, 1<<24); r = uv_write(&write_req, (uv_stream_t *) &pipe_hdl, &buffer, 1, write_cb); - ASSERT(0 == r); + ASSERT_OK(r); /* cause a SIGPIPE on write in next iteration */ close(pipefds[0]); - ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(&loop, UV_RUN_DEFAULT)); - ASSERT(0 == uv_loop_close(&loop)); + ASSERT_EQ(2, close_cb_called); - ASSERT(2 == close_cb_called); - - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(&loop); return 0; } TEST_IMPL(signal_close_loop_alive) { - ASSERT(0 == uv_loop_init(&loop)); - ASSERT(0 == uv_signal_init(&loop, &signal_hdl)); - ASSERT(0 == uv_signal_start(&signal_hdl, stop_loop_cb, SIGPIPE)); + ASSERT_OK(uv_loop_init(&loop)); + ASSERT_OK(uv_signal_init(&loop, &signal_hdl)); + ASSERT_OK(uv_signal_start(&signal_hdl, stop_loop_cb, SIGPIPE)); uv_unref((uv_handle_t*) &signal_hdl); - ASSERT(0 == uv_kill(uv_os_getpid(), SIGPIPE)); - ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_kill(uv_os_getpid(), SIGPIPE)); + ASSERT_OK(uv_run(&loop, UV_RUN_DEFAULT)); uv_close((uv_handle_t*) &signal_hdl, close_cb); - ASSERT(1 == uv_loop_alive(&loop)); + ASSERT_EQ(1, uv_loop_alive(&loop)); - ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT)); - ASSERT(0 == uv_loop_close(&loop)); - ASSERT(1 == close_cb_called); + ASSERT_OK(uv_run(&loop, UV_RUN_DEFAULT)); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(&loop); return 0; } diff --git a/test/test-signal.c b/test/test-signal.c index c2ce5ec0e0a..b1e24bb01a1 100644 --- a/test/test-signal.c +++ b/test/test-signal.c @@ -31,14 +31,14 @@ TEST_IMPL(kill_invalid_signum) { pid = uv_os_getpid(); - ASSERT(uv_kill(pid, -1) == UV_EINVAL); + ASSERT_EQ(uv_kill(pid, -1), UV_EINVAL); #ifdef _WIN32 /* NSIG is not available on all platforms. */ - ASSERT(uv_kill(pid, NSIG) == UV_EINVAL); + ASSERT_EQ(uv_kill(pid, NSIG), UV_EINVAL); #endif - ASSERT(uv_kill(pid, 4096) == UV_EINVAL); + ASSERT_EQ(uv_kill(pid, 4096), UV_EINVAL); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -55,21 +55,21 @@ TEST_IMPL(win32_signum_number) { loop = uv_default_loop(); uv_signal_init(loop, &signal); - ASSERT(uv_signal_start(&signal, signum_test_cb, 0) == UV_EINVAL); - ASSERT(uv_signal_start(&signal, signum_test_cb, SIGINT) == 0); - ASSERT(uv_signal_start(&signal, signum_test_cb, SIGBREAK) == 0); - ASSERT(uv_signal_start(&signal, signum_test_cb, SIGHUP) == 0); - ASSERT(uv_signal_start(&signal, signum_test_cb, SIGWINCH) == 0); - ASSERT(uv_signal_start(&signal, signum_test_cb, SIGILL) == 0); - ASSERT(uv_signal_start(&signal, signum_test_cb, SIGABRT_COMPAT) == 0); - ASSERT(uv_signal_start(&signal, signum_test_cb, SIGFPE) == 0); - ASSERT(uv_signal_start(&signal, signum_test_cb, SIGSEGV) == 0); - ASSERT(uv_signal_start(&signal, signum_test_cb, SIGTERM) == 0); - ASSERT(uv_signal_start(&signal, signum_test_cb, SIGABRT) == 0); - ASSERT(uv_signal_start(&signal, signum_test_cb, -1) == UV_EINVAL); - ASSERT(uv_signal_start(&signal, signum_test_cb, NSIG) == UV_EINVAL); - ASSERT(uv_signal_start(&signal, signum_test_cb, 1024) == UV_EINVAL); - MAKE_VALGRIND_HAPPY(); + ASSERT_EQ(uv_signal_start(&signal, signum_test_cb, 0), UV_EINVAL); + ASSERT_OK(uv_signal_start(&signal, signum_test_cb, SIGINT)); + ASSERT_OK(uv_signal_start(&signal, signum_test_cb, SIGBREAK)); + ASSERT_OK(uv_signal_start(&signal, signum_test_cb, SIGHUP)); + ASSERT_OK(uv_signal_start(&signal, signum_test_cb, SIGWINCH)); + ASSERT_OK(uv_signal_start(&signal, signum_test_cb, SIGILL)); + ASSERT_OK(uv_signal_start(&signal, signum_test_cb, SIGABRT_COMPAT)); + ASSERT_OK(uv_signal_start(&signal, signum_test_cb, SIGFPE)); + ASSERT_OK(uv_signal_start(&signal, signum_test_cb, SIGSEGV)); + ASSERT_OK(uv_signal_start(&signal, signum_test_cb, SIGTERM)); + ASSERT_OK(uv_signal_start(&signal, signum_test_cb, SIGABRT)); + ASSERT_EQ(uv_signal_start(&signal, signum_test_cb, -1), UV_EINVAL); + ASSERT_EQ(uv_signal_start(&signal, signum_test_cb, NSIG), UV_EINVAL); + ASSERT_EQ(uv_signal_start(&signal, signum_test_cb, 1024), UV_EINVAL); + MAKE_VALGRIND_HAPPY(loop); return 0; } #else @@ -100,7 +100,7 @@ struct signal_ctx { static void signal_cb(uv_signal_t* handle, int signum) { struct signal_ctx* ctx = container_of(handle, struct signal_ctx, handle); - ASSERT(signum == ctx->signum); + ASSERT_EQ(signum, ctx->signum); if (++ctx->ncalls == NSIGNALS) { if (ctx->stop_or_close == STOP) uv_signal_stop(handle); @@ -113,8 +113,8 @@ static void signal_cb(uv_signal_t* handle, int signum) { static void signal_cb_one_shot(uv_signal_t* handle, int signum) { struct signal_ctx* ctx = container_of(handle, struct signal_ctx, handle); - ASSERT(signum == ctx->signum); - ASSERT(++ctx->ncalls == 1); + ASSERT_EQ(signum, ctx->signum); + ASSERT_EQ(1, ++ctx->ncalls); if (ctx->stop_or_close == CLOSE) uv_close((uv_handle_t*)handle, NULL); } @@ -138,18 +138,18 @@ static void start_watcher(uv_loop_t* loop, ctx->signum = signum; ctx->stop_or_close = CLOSE; ctx->one_shot = one_shot; - ASSERT(0 == uv_signal_init(loop, &ctx->handle)); + ASSERT_OK(uv_signal_init(loop, &ctx->handle)); if (one_shot) - ASSERT(0 == uv_signal_start_oneshot(&ctx->handle, signal_cb_one_shot, signum)); + ASSERT_OK(uv_signal_start_oneshot(&ctx->handle, signal_cb_one_shot, signum)); else - ASSERT(0 == uv_signal_start(&ctx->handle, signal_cb, signum)); + ASSERT_OK(uv_signal_start(&ctx->handle, signal_cb, signum)); } static void start_timer(uv_loop_t* loop, int signum, struct timer_ctx* ctx) { ctx->ncalls = 0; ctx->signum = signum; - ASSERT(0 == uv_timer_init(loop, &ctx->handle)); - ASSERT(0 == uv_timer_start(&ctx->handle, timer_cb, 5, 5)); + ASSERT_OK(uv_timer_init(loop, &ctx->handle)); + ASSERT_OK(uv_timer_start(&ctx->handle, timer_cb, 5, 5)); } @@ -162,25 +162,25 @@ TEST_IMPL(we_get_signal) { start_timer(loop, SIGCHLD, &tc); start_watcher(loop, SIGCHLD, &sc, 0); sc.stop_or_close = STOP; /* stop, don't close the signal handle */ - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(tc.ncalls == NSIGNALS); - ASSERT(sc.ncalls == NSIGNALS); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(tc.ncalls, NSIGNALS); + ASSERT_EQ(sc.ncalls, NSIGNALS); start_timer(loop, SIGCHLD, &tc); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(tc.ncalls == NSIGNALS); - ASSERT(sc.ncalls == NSIGNALS); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(tc.ncalls, NSIGNALS); + ASSERT_EQ(sc.ncalls, NSIGNALS); sc.ncalls = 0; sc.stop_or_close = CLOSE; /* now close it when it's done */ uv_signal_start(&sc.handle, signal_cb, SIGCHLD); start_timer(loop, SIGCHLD, &tc); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(tc.ncalls == NSIGNALS); - ASSERT(sc.ncalls == NSIGNALS); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(tc.ncalls, NSIGNALS); + ASSERT_EQ(sc.ncalls, NSIGNALS); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -198,15 +198,15 @@ TEST_IMPL(we_get_signals) { start_watcher(loop, SIGUSR2, sc + 3, 0); start_timer(loop, SIGUSR1, tc + 0); start_timer(loop, SIGUSR2, tc + 1); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); for (i = 0; i < ARRAY_SIZE(sc); i++) - ASSERT(sc[i].ncalls == NSIGNALS); + ASSERT_EQ(sc[i].ncalls, NSIGNALS); for (i = 0; i < ARRAY_SIZE(tc); i++) - ASSERT(tc[i].ncalls == NSIGNALS); + ASSERT_EQ(tc[i].ncalls, NSIGNALS); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -219,23 +219,23 @@ TEST_IMPL(we_get_signal_one_shot) { start_timer(loop, SIGCHLD, &tc); start_watcher(loop, SIGCHLD, &sc, 1); sc.stop_or_close = NOOP; - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(tc.ncalls == NSIGNALS); - ASSERT(sc.ncalls == 1); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(tc.ncalls, NSIGNALS); + ASSERT_EQ(1, sc.ncalls); start_timer(loop, SIGCHLD, &tc); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(sc.ncalls == 1); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(1, sc.ncalls); sc.ncalls = 0; sc.stop_or_close = CLOSE; /* now close it when it's done */ uv_signal_start_oneshot(&sc.handle, signal_cb_one_shot, SIGCHLD); start_timer(loop, SIGCHLD, &tc); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(tc.ncalls == NSIGNALS); - ASSERT(sc.ncalls == 1); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(tc.ncalls, NSIGNALS); + ASSERT_EQ(1, sc.ncalls); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -252,10 +252,10 @@ TEST_IMPL(we_get_signals_mixed) { start_watcher(loop, SIGCHLD, sc + 1, 1); sc[0].stop_or_close = CLOSE; sc[1].stop_or_close = CLOSE; - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(tc.ncalls == NSIGNALS); - ASSERT(sc[0].ncalls == 1); - ASSERT(sc[1].ncalls == 1); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(tc.ncalls, NSIGNALS); + ASSERT_EQ(1, sc[0].ncalls); + ASSERT_EQ(1, sc[1].ncalls); /* 2 one-shot, 1 normal then remove normal */ start_timer(loop, SIGCHLD, &tc); @@ -265,11 +265,11 @@ TEST_IMPL(we_get_signals_mixed) { sc[1].stop_or_close = CLOSE; start_watcher(loop, SIGCHLD, sc + 2, 0); uv_close((uv_handle_t*)&(sc[2]).handle, NULL); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(tc.ncalls == NSIGNALS); - ASSERT(sc[0].ncalls == 1); - ASSERT(sc[1].ncalls == 1); - ASSERT(sc[2].ncalls == 0); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(tc.ncalls, NSIGNALS); + ASSERT_EQ(1, sc[0].ncalls); + ASSERT_EQ(1, sc[1].ncalls); + ASSERT_OK(sc[2].ncalls); /* 2 normal, 1 one-shot then remove one-shot */ start_timer(loop, SIGCHLD, &tc); @@ -279,11 +279,11 @@ TEST_IMPL(we_get_signals_mixed) { sc[1].stop_or_close = CLOSE; start_watcher(loop, SIGCHLD, sc + 2, 1); uv_close((uv_handle_t*)&(sc[2]).handle, NULL); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(tc.ncalls == NSIGNALS); - ASSERT(sc[0].ncalls == NSIGNALS); - ASSERT(sc[1].ncalls == NSIGNALS); - ASSERT(sc[2].ncalls == 0); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(tc.ncalls, NSIGNALS); + ASSERT_EQ(sc[0].ncalls, NSIGNALS); + ASSERT_EQ(sc[1].ncalls, NSIGNALS); + ASSERT_OK(sc[2].ncalls); /* 2 normal, 2 one-shot then remove 2 normal */ start_timer(loop, SIGCHLD, &tc); @@ -295,12 +295,12 @@ TEST_IMPL(we_get_signals_mixed) { sc[3].stop_or_close = CLOSE; uv_close((uv_handle_t*)&(sc[0]).handle, NULL); uv_close((uv_handle_t*)&(sc[1]).handle, NULL); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(tc.ncalls == NSIGNALS); - ASSERT(sc[0].ncalls == 0); - ASSERT(sc[1].ncalls == 0); - ASSERT(sc[2].ncalls == 1); - ASSERT(sc[2].ncalls == 1); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(tc.ncalls, NSIGNALS); + ASSERT_OK(sc[0].ncalls); + ASSERT_OK(sc[1].ncalls); + ASSERT_EQ(1, sc[2].ncalls); + ASSERT_EQ(1, sc[2].ncalls); /* 1 normal, 1 one-shot, 2 normal then remove 1st normal, 2nd normal */ start_timer(loop, SIGCHLD, &tc); @@ -311,14 +311,14 @@ TEST_IMPL(we_get_signals_mixed) { sc[3].stop_or_close = CLOSE; uv_close((uv_handle_t*)&(sc[0]).handle, NULL); uv_close((uv_handle_t*)&(sc[2]).handle, NULL); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(tc.ncalls == NSIGNALS); - ASSERT(sc[0].ncalls == 0); - ASSERT(sc[1].ncalls == 1); - ASSERT(sc[2].ncalls == 0); - ASSERT(sc[3].ncalls == NSIGNALS); - - MAKE_VALGRIND_HAPPY(); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(tc.ncalls, NSIGNALS); + ASSERT_OK(sc[0].ncalls); + ASSERT_EQ(1, sc[1].ncalls); + ASSERT_OK(sc[2].ncalls); + ASSERT_EQ(sc[3].ncalls, NSIGNALS); + + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-socket-buffer-size.c b/test/test-socket-buffer-size.c index 72f8c2524c0..64904e93d50 100644 --- a/test/test-socket-buffer-size.c +++ b/test/test-socket-buffer-size.c @@ -40,14 +40,14 @@ static void check_buffer_size(uv_handle_t* handle) { int value; value = 0; - ASSERT(0 == uv_recv_buffer_size(handle, &value)); - ASSERT(value > 0); + ASSERT_OK(uv_recv_buffer_size(handle, &value)); + ASSERT_GT(value, 0); value = 10000; - ASSERT(0 == uv_recv_buffer_size(handle, &value)); + ASSERT_OK(uv_recv_buffer_size(handle, &value)); value = 0; - ASSERT(0 == uv_recv_buffer_size(handle, &value)); + ASSERT_OK(uv_recv_buffer_size(handle, &value)); /* linux sets double the value */ ASSERT(value == 10000 || value == 20000); } @@ -56,22 +56,22 @@ static void check_buffer_size(uv_handle_t* handle) { TEST_IMPL(socket_buffer_size) { struct sockaddr_in addr; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); - ASSERT(0 == uv_tcp_init(uv_default_loop(), &tcp)); - ASSERT(0 == uv_tcp_bind(&tcp, (struct sockaddr*) &addr, 0)); + ASSERT_OK(uv_tcp_init(uv_default_loop(), &tcp)); + ASSERT_OK(uv_tcp_bind(&tcp, (struct sockaddr*) &addr, 0)); check_buffer_size((uv_handle_t*) &tcp); uv_close((uv_handle_t*) &tcp, close_cb); - ASSERT(0 == uv_udp_init(uv_default_loop(), &udp)); - ASSERT(0 == uv_udp_bind(&udp, (struct sockaddr*) &addr, 0)); + ASSERT_OK(uv_udp_init(uv_default_loop(), &udp)); + ASSERT_OK(uv_udp_bind(&udp, (struct sockaddr*) &addr, 0)); check_buffer_size((uv_handle_t*) &udp); uv_close((uv_handle_t*) &udp, close_cb); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(close_cb_called == 2); + ASSERT_EQ(2, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-spawn.c b/test/test-spawn.c index 9f2eb24b2d6..964c8a86c76 100644 --- a/test/test-spawn.c +++ b/test/test-spawn.c @@ -32,6 +32,9 @@ # include # include typedef BOOL (WINAPI *sCompareObjectHandles)(_In_ HANDLE, _In_ HANDLE); +# define unlink _unlink +# define putenv _putenv +# define close _close #else # include # include @@ -65,8 +68,8 @@ static void exit_cb(uv_process_t* process, int term_signal) { printf("exit_cb\n"); exit_cb_called++; - ASSERT(exit_status == 1); - ASSERT(term_signal == 0); + ASSERT_EQ(1, exit_status); + ASSERT_OK(term_signal); uv_close((uv_handle_t*) process, close_cb); } @@ -86,9 +89,9 @@ static void kill_cb(uv_process_t* process, printf("exit_cb\n"); exit_cb_called++; #ifdef _WIN32 - ASSERT(exit_status == 1); + ASSERT_EQ(1, exit_status); #else - ASSERT(exit_status == 0); + ASSERT_OK(exit_status); #endif #if defined(__APPLE__) || defined(__MVS__) /* @@ -108,7 +111,7 @@ static void kill_cb(uv_process_t* process, * This process should be dead. */ err = uv_kill(process->pid, 0); - ASSERT(err == UV_ESRCH); + ASSERT_EQ(err, UV_ESRCH); } static void detach_failure_cb(uv_process_t* process, @@ -130,7 +133,7 @@ static void on_read(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { if (nread > 0) { output_used += nread; } else if (nread < 0) { - ASSERT(nread == UV_EOF); + ASSERT_EQ(nread, UV_EOF); uv_close((uv_handle_t*) tcp, close_cb); } } @@ -143,20 +146,20 @@ static void on_read_once(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { static void write_cb(uv_write_t* req, int status) { - ASSERT(status == 0); + ASSERT_OK(status); uv_close((uv_handle_t*) req->handle, close_cb); } static void write_null_cb(uv_write_t* req, int status) { - ASSERT(status == 0); + ASSERT_OK(status); } static void init_process_options(char* test, uv_exit_cb exit_cb) { /* Note spawn_helper1 defined in test/run-tests.c */ int r = uv_exepath(exepath, &exepath_size); - ASSERT(r == 0); + ASSERT_OK(r); exepath[exepath_size] = '\0'; args[0] = exepath; args[1] = test; @@ -189,11 +192,11 @@ TEST_IMPL(spawn_fails) { r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == UV_ENOENT || r == UV_EACCES); - ASSERT(0 == uv_is_active((uv_handle_t*) &process)); + ASSERT_OK(uv_is_active((uv_handle_t*) &process)); uv_close((uv_handle_t*) &process, NULL); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -209,21 +212,21 @@ TEST_IMPL(spawn_fails_check_for_waitpid_cleanup) { r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == UV_ENOENT || r == UV_EACCES); - ASSERT(0 == uv_is_active((uv_handle_t*) &process)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_is_active((uv_handle_t*) &process)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); /* verify the child is successfully cleaned up within libuv */ do err = waitpid(process.pid, &status, 0); while (err == -1 && errno == EINTR); - ASSERT(err == -1); - ASSERT(errno == ECHILD); + ASSERT_EQ(err, -1); + ASSERT_EQ(errno, ECHILD); uv_close((uv_handle_t*) &process, NULL); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } #endif @@ -247,13 +250,13 @@ TEST_IMPL(spawn_empty_env) { options.env = env; env[0] = NULL; - ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_spawn(uv_default_loop(), &process, &options)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -264,15 +267,15 @@ TEST_IMPL(spawn_exit_code) { init_process_options("spawn_helper1", exit_cb); r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -292,20 +295,20 @@ TEST_IMPL(spawn_stdout) { options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */ + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(2, close_cb_called); /* Once for process once for the pipe. */ printf("output is: %s", output); - ASSERT(strcmp("hello world\n", output) == 0); + ASSERT_OK(strcmp("hello world\n", output)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -322,9 +325,9 @@ TEST_IMPL(spawn_stdout_to_file) { init_process_options("spawn_helper2", exit_cb); - r = uv_fs_open(NULL, &fs_req, "stdout_file", O_CREAT | O_RDWR, + r = uv_fs_open(NULL, &fs_req, "stdout_file", UV_FS_O_CREAT | UV_FS_O_RDWR, S_IRUSR | S_IWUSR, NULL); - ASSERT(r != -1); + ASSERT_NE(r, -1); uv_fs_req_cleanup(&fs_req); file = r; @@ -336,30 +339,30 @@ TEST_IMPL(spawn_stdout_to_file) { options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(1, close_cb_called); buf = uv_buf_init(output, sizeof(output)); r = uv_fs_read(NULL, &fs_req, file, &buf, 1, 0, NULL); - ASSERT(r == 12); + ASSERT_EQ(12, r); uv_fs_req_cleanup(&fs_req); r = uv_fs_close(NULL, &fs_req, file, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&fs_req); printf("output is: %s", output); - ASSERT(strcmp("hello world\n", output) == 0); + ASSERT_OK(strcmp("hello world\n", output)); /* Cleanup. */ unlink("stdout_file"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -376,9 +379,9 @@ TEST_IMPL(spawn_stdout_and_stderr_to_file) { init_process_options("spawn_helper6", exit_cb); - r = uv_fs_open(NULL, &fs_req, "stdout_file", O_CREAT | O_RDWR, + r = uv_fs_open(NULL, &fs_req, "stdout_file", UV_FS_O_CREAT | UV_FS_O_RDWR, S_IRUSR | S_IWUSR, NULL); - ASSERT(r != -1); + ASSERT_NE(r, -1); uv_fs_req_cleanup(&fs_req); file = r; @@ -392,30 +395,30 @@ TEST_IMPL(spawn_stdout_and_stderr_to_file) { options.stdio_count = 3; r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(1, close_cb_called); buf = uv_buf_init(output, sizeof(output)); r = uv_fs_read(NULL, &fs_req, file, &buf, 1, 0, NULL); - ASSERT(r == 27); + ASSERT_EQ(27, r); uv_fs_req_cleanup(&fs_req); r = uv_fs_close(NULL, &fs_req, file, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&fs_req); printf("output is: %s", output); - ASSERT(strcmp("hello world\nhello errworld\n", output) == 0); + ASSERT_OK(strcmp("hello world\nhello errworld\n", output)); /* Cleanup. */ unlink("stdout_file"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -440,10 +443,10 @@ TEST_IMPL(spawn_stdout_and_stderr_to_file2) { O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, NULL); - ASSERT(r != -1); + ASSERT_NE(r, -1); uv_fs_req_cleanup(&fs_req); file = dup2(r, STDERR_FILENO); - ASSERT(file != -1); + ASSERT_NE(file, -1); options.stdio = stdio; options.stdio[0].flags = UV_IGNORE; @@ -454,30 +457,30 @@ TEST_IMPL(spawn_stdout_and_stderr_to_file2) { options.stdio_count = 3; r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(1, close_cb_called); buf = uv_buf_init(output, sizeof(output)); r = uv_fs_read(NULL, &fs_req, file, &buf, 1, 0, NULL); - ASSERT(r == 27); + ASSERT_EQ(27, r); uv_fs_req_cleanup(&fs_req); r = uv_fs_close(NULL, &fs_req, file, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&fs_req); printf("output is: %s", output); - ASSERT(strcmp("hello world\nhello errworld\n", output) == 0); + ASSERT_OK(strcmp("hello world\nhello errworld\n", output)); /* Cleanup. */ unlink("stdout_file"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; #else RETURN_SKIP("Unix only test"); @@ -507,18 +510,18 @@ TEST_IMPL(spawn_stdout_and_stderr_to_file_swap) { O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, NULL); - ASSERT(r != -1); + ASSERT_NE(r, -1); uv_fs_req_cleanup(&fs_req); stdout_file = dup2(r, STDOUT_FILENO); - ASSERT(stdout_file != -1); + ASSERT_NE(stdout_file, -1); /* open 'stderr_file' and replace STDERR_FILENO with it */ r = uv_fs_open(NULL, &fs_req, "stderr_file", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, NULL); - ASSERT(r != -1); + ASSERT_NE(r, -1); uv_fs_req_cleanup(&fs_req); stderr_file = dup2(r, STDERR_FILENO); - ASSERT(stderr_file != -1); + ASSERT_NE(stderr_file, -1); /* now we're going to swap them: the child process' stdout will be our * stderr_file and vice versa */ @@ -531,45 +534,45 @@ TEST_IMPL(spawn_stdout_and_stderr_to_file_swap) { options.stdio_count = 3; r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(1, close_cb_called); buf = uv_buf_init(output, sizeof(output)); /* check the content of stdout_file */ r = uv_fs_read(NULL, &fs_req, stdout_file, &buf, 1, 0, NULL); - ASSERT(r >= 15); + ASSERT_GE(r, 15); uv_fs_req_cleanup(&fs_req); r = uv_fs_close(NULL, &fs_req, stdout_file, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&fs_req); printf("output is: %s", output); - ASSERT(strncmp("hello errworld\n", output, 15) == 0); + ASSERT_OK(strncmp("hello errworld\n", output, 15)); /* check the content of stderr_file */ r = uv_fs_read(NULL, &fs_req, stderr_file, &buf, 1, 0, NULL); - ASSERT(r >= 12); + ASSERT_GE(r, 12); uv_fs_req_cleanup(&fs_req); r = uv_fs_close(NULL, &fs_req, stderr_file, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_fs_req_cleanup(&fs_req); printf("output is: %s", output); - ASSERT(strncmp("hello world\n", output, 12) == 0); + ASSERT_OK(strncmp("hello world\n", output, 12)); /* Cleanup. */ unlink("stdout_file"); unlink("stderr_file"); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; #else RETURN_SKIP("Unix only test"); @@ -598,24 +601,24 @@ TEST_IMPL(spawn_stdin) { options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); buf.base = buffer; buf.len = sizeof(buffer); r = uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 3); /* Once for process twice for the pipe. */ - ASSERT(strcmp(buffer, output) == 0); + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(3, close_cb_called); /* Once for process twice for the pipe. */ + ASSERT_OK(strcmp(buffer, output)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -637,20 +640,20 @@ TEST_IMPL(spawn_stdio_greater_than_3) { options.stdio_count = 4; r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_start((uv_stream_t*) &pipe, on_alloc, on_read); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */ + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(2, close_cb_called); /* Once for process once for the pipe. */ printf("output from stdio[3] is: %s", output); - ASSERT(strcmp("fourth stdio!\n", output) == 0); + ASSERT_OK(strcmp("fourth stdio!\n", output)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -661,7 +664,7 @@ int spawn_tcp_server_helper(void) { int r; r = uv_tcp_init(uv_default_loop(), &tcp); - ASSERT(r == 0); + ASSERT_OK(r); #ifdef _WIN32 handle = _get_osfhandle(3); @@ -669,13 +672,13 @@ int spawn_tcp_server_helper(void) { handle = 3; #endif r = uv_tcp_open(&tcp, handle); - ASSERT(r == 0); + ASSERT_OK(r); /* Make sure that we can listen on a socket that was * passed down from the parent process */ r = uv_listen((uv_stream_t*) &tcp, SOMAXCONN, NULL); - ASSERT(r == 0); + ASSERT_OK(r); return 1; } @@ -692,21 +695,21 @@ TEST_IMPL(spawn_tcp_server) { init_process_options("spawn_tcp_server_helper", exit_cb); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); fd = -1; r = uv_tcp_init_ex(uv_default_loop(), &tcp_server, AF_INET); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); #ifdef _WIN32 r = uv_fileno((uv_handle_t*) &tcp_server, &handle); fd = _open_osfhandle((intptr_t) handle, 0); #else r = uv_fileno((uv_handle_t*) &tcp_server, &fd); #endif - ASSERT(r == 0); - ASSERT(fd > 0); + ASSERT_OK(r); + ASSERT_GT(fd, 0); options.stdio = stdio; options.stdio[0].flags = UV_INHERIT_FD; @@ -720,15 +723,15 @@ TEST_IMPL(spawn_tcp_server) { options.stdio_count = 4; r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -742,15 +745,15 @@ TEST_IMPL(spawn_ignored_stdio) { options.stdio_count = 0; r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -761,21 +764,21 @@ TEST_IMPL(spawn_and_kill) { init_process_options("spawn_helper4", kill_cb); r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_init(uv_default_loop(), &timer); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&timer, timer_cb, 500, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 2); /* Once for process and once for timer. */ + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(2, close_cb_called); /* Once for process and once for timer. */ - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -795,27 +798,27 @@ TEST_IMPL(spawn_preserve_env) { options.stdio_count = 2; r = putenv("ENV_TEST=testval"); - ASSERT(r == 0); + ASSERT_OK(r); /* Explicitly set options.env to NULL to test for env clobbering. */ options.env = NULL; r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 2); + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(2, close_cb_called); printf("output is: %s", output); - ASSERT(strcmp("testval", output) == 0); + ASSERT_OK(strcmp("testval", output)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -828,24 +831,24 @@ TEST_IMPL(spawn_detached) { options.flags |= UV_PROCESS_DETACHED; r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); uv_unref((uv_handle_t*) &process); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 0); + ASSERT_OK(exit_cb_called); - ASSERT(process.pid == uv_process_get_pid(&process)); + ASSERT_EQ(process.pid, uv_process_get_pid(&process)); r = uv_kill(process.pid, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_kill(process.pid, SIGTERM); - ASSERT(r == 0); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -861,13 +864,13 @@ TEST_IMPL(spawn_and_kill_with_std) { init_process_options("spawn_helper4", kill_cb); r = uv_pipe_init(uv_default_loop(), &in, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_init(uv_default_loop(), &out, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_init(uv_default_loop(), &err, 0); - ASSERT(r == 0); + ASSERT_OK(r); options.stdio = stdio; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; @@ -879,31 +882,31 @@ TEST_IMPL(spawn_and_kill_with_std) { options.stdio_count = 3; r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); buf = uv_buf_init(message, sizeof message); r = uv_write(&write, (uv_stream_t*) &in, &buf, 1, write_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_start((uv_stream_t*) &err, on_alloc, on_read); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_init(uv_default_loop(), &timer); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&timer, timer_cb, 500, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 5); /* process x 1, timer x 1, stdio x 3. */ + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(5, close_cb_called); /* process x 1, timer x 1, stdio x 3. */ - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -928,29 +931,29 @@ TEST_IMPL(spawn_and_ping) { options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); /* Sending signum == 0 should check if the * child process is still alive, not kill it. */ r = uv_process_kill(&process, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 0); + ASSERT_OK(exit_cb_called); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 1); - ASSERT(strcmp(output, "TEST") == 0); + ASSERT_EQ(1, exit_cb_called); + ASSERT_OK(strcmp(output, "TEST")); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -975,29 +978,29 @@ TEST_IMPL(spawn_same_stdout_stderr) { options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); /* Sending signum == 0 should check if the * child process is still alive, not kill it. */ r = uv_process_kill(&process, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 0); + ASSERT_OK(exit_cb_called); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 1); - ASSERT(strcmp(output, "TEST") == 0); + ASSERT_EQ(1, exit_cb_called); + ASSERT_OK(strcmp(output, "TEST")); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -1019,17 +1022,17 @@ TEST_IMPL(spawn_closed_process_io) { close(0); /* Close process stdin. */ - ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options)); + ASSERT_OK(uv_spawn(uv_default_loop(), &process, &options)); buf = uv_buf_init(buffer, sizeof(buffer)); - ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_cb)); + ASSERT_OK(uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_cb)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 2); /* process, child stdin */ + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(2, close_cb_called); /* process, child stdin */ - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -1049,41 +1052,41 @@ TEST_IMPL(kill) { sigset_t set; sigemptyset(&set); sigaddset(&set, SIGTERM); - ASSERT(0 == pthread_sigmask(SIG_BLOCK, &set, NULL)); + ASSERT_OK(pthread_sigmask(SIG_BLOCK, &set, NULL)); } - ASSERT(SIG_ERR != signal(SIGTERM, SIG_IGN)); + ASSERT_PTR_NE(SIG_ERR, signal(SIGTERM, SIG_IGN)); #endif r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); #ifndef _WIN32 { sigset_t set; sigemptyset(&set); sigaddset(&set, SIGTERM); - ASSERT(0 == pthread_sigmask(SIG_UNBLOCK, &set, NULL)); + ASSERT_OK(pthread_sigmask(SIG_UNBLOCK, &set, NULL)); } - ASSERT(SIG_ERR != signal(SIGTERM, SIG_DFL)); + ASSERT_PTR_NE(SIG_ERR, signal(SIGTERM, SIG_DFL)); #endif /* Sending signum == 0 should check if the * child process is still alive, not kill it. */ r = uv_kill(process.pid, 0); - ASSERT(r == 0); + ASSERT_OK(r); /* Kill the process. */ r = uv_kill(process.pid, SIGTERM); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -1108,7 +1111,7 @@ TEST_IMPL(spawn_detect_pipe_name_collisions_on_windows) { /* Create a pipe that'll cause a collision. */ snprintf(name, sizeof(name), - "\\\\.\\pipe\\uv\\%p-%d", + "\\\\.\\pipe\\uv\\%p-%lu", &out, GetCurrentProcessId()); pipe_handle = CreateNamedPipeA(name, @@ -1119,23 +1122,23 @@ TEST_IMPL(spawn_detect_pipe_name_collisions_on_windows) { 65536, 0, NULL); - ASSERT(pipe_handle != INVALID_HANDLE_VALUE); + ASSERT_PTR_NE(pipe_handle, INVALID_HANDLE_VALUE); r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 2); /* Once for process once for the pipe. */ + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(2, close_cb_called); /* Once for process once for the pipe. */ printf("output is: %s", output); - ASSERT(strcmp("hello world\n", output) == 0); + ASSERT_OK(strcmp("hello world\n", output)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -1197,7 +1200,7 @@ TEST_IMPL(argument_escaping) { cracked = CommandLineToArgvW(command_line, &num_args); for (i = 0; i < num_args; ++i) { wprintf(L"%d: %s\t%s\n", i, test_str[i], cracked[i]); - ASSERT(wcscmp(test_str[i], cracked[i]) == 0); + ASSERT_OK(wcscmp(test_str[i], cracked[i])); } LocalFree(cracked); @@ -1207,19 +1210,19 @@ TEST_IMPL(argument_escaping) { free(test_output); result = make_program_args(verbatim, 1, &verbatim_output); - ASSERT(result == 0); + ASSERT_OK(result); result = make_program_args(verbatim, 0, &non_verbatim_output); - ASSERT(result == 0); + ASSERT_OK(result); wprintf(L" verbatim_output: %s\n", verbatim_output); wprintf(L"non_verbatim_output: %s\n", non_verbatim_output); - ASSERT(wcscmp(verbatim_output, - L"cmd.exe /c c:\\path\\to\\node.exe --eval " - L"\"require('c:\\\\path\\\\to\\\\test.js')\"") == 0); - ASSERT(wcscmp(non_verbatim_output, - L"cmd.exe /c \"c:\\path\\to\\node.exe --eval " - L"\\\"require('c:\\\\path\\\\to\\\\test.js')\\\"\"") == 0); + ASSERT_OK(wcscmp(verbatim_output, + L"cmd.exe /c c:\\path\\to\\node.exe --eval " + L"\"require('c:\\\\path\\\\to\\\\test.js')\"")); + ASSERT_OK(wcscmp(non_verbatim_output, + L"cmd.exe /c \"c:\\path\\to\\node.exe --eval " + L"\\\"require('c:\\\\path\\\\to\\\\test.js')\\\"\"")); free(verbatim_output); free(non_verbatim_output); @@ -1303,7 +1306,7 @@ TEST_IMPL(environment_creation) { } result = make_program_env(environment, &env); - ASSERT(result == 0); + ASSERT_OK(result); for (str = env, prev = NULL; *str; prev = str, str += wcslen(str) + 1) { int found = 0; @@ -1326,9 +1329,7 @@ TEST_IMPL(environment_creation) { } } if (prev) { /* verify sort order */ -#if !defined(__MINGW32__) || defined(__MINGW64_VERSION_MAJOR) - ASSERT(CompareStringOrdinal(prev, -1, str, -1, TRUE) == 1); -#endif + ASSERT_EQ(1, CompareStringOrdinal(prev, -1, str, -1, TRUE)); } ASSERT(found); /* verify that we expected this variable */ } @@ -1359,11 +1360,100 @@ TEST_IMPL(spawn_with_an_odd_path) { options.file = options.args[0] = "program-that-had-better-not-exist"; r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == UV_ENOENT || r == UV_EACCES); - ASSERT(0 == uv_is_active((uv_handle_t*) &process)); + ASSERT_OK(uv_is_active((uv_handle_t*) &process)); uv_close((uv_handle_t*) &process, NULL); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} + + +TEST_IMPL(spawn_no_path) { + char* env[1]; + WCHAR* old_path = NULL; + DWORD old_path_len; + + if ((old_path_len = GetEnvironmentVariableW(L"PATH", NULL, 0)) > 0) { + old_path = malloc(old_path_len * sizeof(WCHAR)); + GetEnvironmentVariableW(L"PATH", old_path, old_path_len); + SetEnvironmentVariableW(L"PATH", NULL); + } + + init_process_options("spawn_helper1", exit_cb); + options.env = env; + env[0] = NULL; + + ASSERT_OK(uv_spawn(uv_default_loop(), &process, &options)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(1, close_cb_called); + + SetEnvironmentVariableW(L"PATH", old_path); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} + + +TEST_IMPL(spawn_no_ext) { + char new_exepath[1024]; + + init_process_options("spawn_helper1", exit_cb); + options.flags |= UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME; + snprintf(new_exepath, sizeof(new_exepath), "%.*s_no_ext", + (int) (exepath_size - sizeof(".exe") + 1), + exepath); + options.file = options.args[0] = new_exepath; + + ASSERT_OK(uv_spawn(uv_default_loop(), &process, &options)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(1, close_cb_called); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} + + +TEST_IMPL(spawn_path_no_ext) { + int r; + int len; + int file_len; + char file[64]; + char path[1024]; + char* env[2]; + + /* Set up the process, but make sure that the file to run is relative and + * requires a lookup into PATH. */ + init_process_options("spawn_helper1", exit_cb); + options.flags |= UV_PROCESS_WINDOWS_FILE_PATH_EXACT_NAME; + + /* Set up the PATH env variable */ + for (len = strlen(exepath), file_len = 0; + exepath[len - 1] != '/' && exepath[len - 1] != '\\'; + len--, file_len++); + snprintf(file, sizeof(file), "%.*s_no_ext", + (int) (file_len - sizeof(".exe") + 1), + exepath + len); + exepath[len] = 0; + snprintf(path, sizeof(path), "PATH=%s", exepath); + + env[0] = path; + env[1] = NULL; + + options.file = options.args[0] = file; + options.env = env; + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT(r == UV_ENOENT || r == UV_EACCES); + ASSERT_OK(uv_is_active((uv_handle_t*) &process)); + uv_close((uv_handle_t*) &process, NULL); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } #endif @@ -1398,15 +1488,15 @@ TEST_IMPL(spawn_setuid_setgid) { if (r == UV_EACCES) RETURN_SKIP("user 'nobody' cannot access the test runner"); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } #endif @@ -1424,15 +1514,15 @@ TEST_IMPL(spawn_setuid_fails) { struct passwd* pw; pw = getpwnam("nobody"); ASSERT_NOT_NULL(pw); - ASSERT(0 == setgid(pw->pw_gid)); - ASSERT(0 == setuid(pw->pw_uid)); + ASSERT_OK(setgid(pw->pw_gid)); + ASSERT_OK(setuid(pw->pw_uid)); } #endif /* !__PASE__ */ init_process_options("spawn_helper1", fail_cb); options.flags |= UV_PROCESS_SETUID; - /* On IBMi PASE, there is no root user. User may grant + /* On IBMi PASE, there is no root user. User may grant * root-like privileges, including setting uid to 0. */ #if defined(__PASE__) @@ -1449,17 +1539,17 @@ TEST_IMPL(spawn_setuid_fails) { r = uv_spawn(uv_default_loop(), &process, &options); #if defined(__CYGWIN__) - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); #else - ASSERT(r == UV_EPERM); + ASSERT_EQ(r, UV_EPERM); #endif r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(close_cb_called == 0); + ASSERT_OK(close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -1475,15 +1565,15 @@ TEST_IMPL(spawn_setgid_fails) { struct passwd* pw; pw = getpwnam("nobody"); ASSERT_NOT_NULL(pw); - ASSERT(0 == setgid(pw->pw_gid)); - ASSERT(0 == setuid(pw->pw_uid)); + ASSERT_OK(setgid(pw->pw_gid)); + ASSERT_OK(setuid(pw->pw_uid)); } #endif /* !__PASE__ */ init_process_options("spawn_helper1", fail_cb); options.flags |= UV_PROCESS_SETGID; - /* On IBMi PASE, there is no root user. User may grant + /* On IBMi PASE, there is no root user. User may grant * root-like privileges, including setting gid to 0. */ #if defined(__MVS__) || defined(__PASE__) @@ -1494,17 +1584,17 @@ TEST_IMPL(spawn_setgid_fails) { r = uv_spawn(uv_default_loop(), &process, &options); #if defined(__CYGWIN__) || defined(__MVS__) - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); #else - ASSERT(r == UV_EPERM); + ASSERT_EQ(r, UV_EPERM); #endif r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(close_cb_called == 0); + ASSERT_OK(close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } #endif @@ -1528,14 +1618,14 @@ TEST_IMPL(spawn_setuid_fails) { options.uid = (uv_uid_t) -42424242; r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == UV_ENOTSUP); + ASSERT_EQ(r, UV_ENOTSUP); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(close_cb_called == 0); + ASSERT_OK(close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -1549,14 +1639,14 @@ TEST_IMPL(spawn_setgid_fails) { options.gid = (uv_gid_t) -42424242; r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == UV_ENOTSUP); + ASSERT_EQ(r, UV_ENOTSUP); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(close_cb_called == 0); + ASSERT_OK(close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } #endif @@ -1564,13 +1654,13 @@ TEST_IMPL(spawn_setgid_fails) { TEST_IMPL(spawn_auto_unref) { init_process_options("spawn_helper1", NULL); - ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(0 == uv_is_closing((uv_handle_t*) &process)); + ASSERT_OK(uv_spawn(uv_default_loop(), &process, &options)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_is_closing((uv_handle_t*) &process)); uv_close((uv_handle_t*) &process, NULL); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(1 == uv_is_closing((uv_handle_t*) &process)); - MAKE_VALGRIND_HAPPY(); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_EQ(1, uv_is_closing((uv_handle_t*) &process)); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -1593,14 +1683,14 @@ TEST_IMPL(spawn_fs_open) { const char dev_null[] = "/dev/null"; #endif - r = uv_fs_open(NULL, &fs_req, dev_null, O_RDWR, 0, NULL); - ASSERT(r != -1); + r = uv_fs_open(NULL, &fs_req, dev_null, UV_FS_O_RDWR, 0, NULL); + ASSERT_NE(r, -1); fd = uv_get_osfhandle((uv_file) fs_req.result); uv_fs_req_cleanup(&fs_req); init_process_options("spawn_helper8", exit_cb); - ASSERT(0 == uv_pipe_init(uv_default_loop(), &in, 0)); + ASSERT_OK(uv_pipe_init(uv_default_loop(), &in, 0)); options.stdio = stdio; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; @@ -1609,31 +1699,37 @@ TEST_IMPL(spawn_fs_open) { /* make an inheritable copy */ #ifdef _WIN32 - ASSERT(0 != DuplicateHandle(GetCurrentProcess(), fd, GetCurrentProcess(), &dup_fd, - 0, /* inherit */ TRUE, DUPLICATE_SAME_ACCESS)); + ASSERT_NE(0, DuplicateHandle(GetCurrentProcess(), fd, GetCurrentProcess(), &dup_fd, + 0, /* inherit */ TRUE, DUPLICATE_SAME_ACCESS)); kernelbase_module = GetModuleHandleA("kernelbase.dll"); pCompareObjectHandles = (sCompareObjectHandles) GetProcAddress(kernelbase_module, "CompareObjectHandles"); - ASSERT(pCompareObjectHandles == NULL || pCompareObjectHandles(fd, dup_fd)); + ASSERT_NE(pCompareObjectHandles == NULL || + pCompareObjectHandles(fd, dup_fd), + 0); #else dup_fd = dup(fd); #endif - ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options)); + ASSERT_OK(uv_spawn(uv_default_loop(), &process, &options)); buf = uv_buf_init((char*) &fd, sizeof(fd)); - ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_null_cb)); + ASSERT_OK(uv_write(&write_req, + (uv_stream_t*) &in, + &buf, + 1, + write_null_cb)); buf = uv_buf_init((char*) &dup_fd, sizeof(fd)); - ASSERT(0 == uv_write(&write_req2, (uv_stream_t*) &in, &buf, 1, write_cb)); + ASSERT_OK(uv_write(&write_req2, (uv_stream_t*) &in, &buf, 1, write_cb)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(0 == uv_fs_close(NULL, &fs_req, r, NULL)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_fs_close(NULL, &fs_req, r, NULL)); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 2); /* One for `in`, one for process */ + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(2, close_cb_called); /* One for `in`, one for process */ - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -1647,9 +1743,9 @@ TEST_IMPL(closed_fd_events) { bufs[0] = uv_buf_init("", 1); /* create a pipe and share it with a child process */ - ASSERT(0 == uv_pipe(fd, 0, 0)); - ASSERT(fd[0] > 2); - ASSERT(fd[1] > 2); + ASSERT_OK(uv_pipe(fd, 0, 0)); + ASSERT_GT(fd[0], 2); + ASSERT_GT(fd[1], 2); /* spawn_helper4 blocks indefinitely. */ init_process_options("spawn_helper4", exit_cb); @@ -1660,55 +1756,54 @@ TEST_IMPL(closed_fd_events) { options.stdio[1].flags = UV_IGNORE; options.stdio[2].flags = UV_IGNORE; - ASSERT(0 == uv_spawn(uv_default_loop(), &process, &options)); + ASSERT_OK(uv_spawn(uv_default_loop(), &process, &options)); uv_unref((uv_handle_t*) &process); /* read from the pipe with uv */ - ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0)); - ASSERT(0 == uv_pipe_open(&pipe_handle, fd[0])); + ASSERT_OK(uv_pipe_init(uv_default_loop(), &pipe_handle, 0)); + ASSERT_OK(uv_pipe_open(&pipe_handle, fd[0])); /* uv_pipe_open() takes ownership of the file descriptor. */ fd[0] = -1; - ASSERT(0 == uv_read_start((uv_stream_t*) &pipe_handle, on_alloc, on_read_once)); + ASSERT_OK(uv_read_start((uv_stream_t*) &pipe_handle, + on_alloc, + on_read_once)); - ASSERT(1 == uv_fs_write(NULL, &req, fd[1], bufs, 1, -1, NULL)); - ASSERT(req.result == 1); + ASSERT_EQ(1, uv_fs_write(NULL, &req, fd[1], bufs, 1, -1, NULL)); + ASSERT_EQ(1, req.result); uv_fs_req_cleanup(&req); -#ifdef _WIN32 - ASSERT(1 == uv_run(uv_default_loop(), UV_RUN_ONCE)); -#endif - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_ONCE)); /* should have received just one byte */ - ASSERT(output_used == 1); + ASSERT_EQ(1, output_used); /* close the pipe and see if we still get events */ uv_close((uv_handle_t*) &pipe_handle, close_cb); - ASSERT(1 == uv_fs_write(NULL, &req, fd[1], bufs, 1, -1, NULL)); - ASSERT(req.result == 1); + ASSERT_EQ(1, uv_fs_write(NULL, &req, fd[1], bufs, 1, -1, NULL)); + ASSERT_EQ(1, req.result); uv_fs_req_cleanup(&req); - ASSERT(0 == uv_timer_init(uv_default_loop(), &timer)); - ASSERT(0 == uv_timer_start(&timer, timer_counter_cb, 10, 0)); + ASSERT_OK(uv_timer_init(uv_default_loop(), &timer)); + ASSERT_OK(uv_timer_start(&timer, timer_counter_cb, 10, 0)); /* see if any spurious events interrupt the timer */ if (1 == uv_run(uv_default_loop(), UV_RUN_ONCE)) /* have to run again to really trigger the timer */ - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_ONCE)); - ASSERT(timer_counter == 1); + ASSERT_EQ(1, timer_counter); /* cleanup */ - ASSERT(0 == uv_process_kill(&process, SIGTERM)); + ASSERT_OK(uv_process_kill(&process, SIGTERM)); #ifdef _WIN32 - ASSERT(0 == _close(fd[1])); + ASSERT_OK(_close(fd[1])); #else - ASSERT(0 == close(fd[1])); + ASSERT_OK(close(fd[1])); #endif - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -1770,15 +1865,15 @@ TEST_IMPL(spawn_reads_child_path) { options.env = env; r = uv_spawn(uv_default_loop(), &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -1802,27 +1897,27 @@ TEST_IMPL(spawn_inherit_streams) { init_process_options("spawn_helper9", exit_cb); loop = uv_default_loop(); - ASSERT(uv_pipe_init(loop, &pipe_stdin_child, 0) == 0); - ASSERT(uv_pipe_init(loop, &pipe_stdout_child, 0) == 0); - ASSERT(uv_pipe_init(loop, &pipe_stdin_parent, 0) == 0); - ASSERT(uv_pipe_init(loop, &pipe_stdout_parent, 0) == 0); - - ASSERT(uv_pipe(fds_stdin, 0, 0) == 0); - ASSERT(uv_pipe(fds_stdout, 0, 0) == 0); - - ASSERT(uv_pipe_open(&pipe_stdin_child, fds_stdin[0]) == 0); - ASSERT(uv_pipe_open(&pipe_stdout_child, fds_stdout[1]) == 0); - ASSERT(uv_pipe_open(&pipe_stdin_parent, fds_stdin[1]) == 0); - ASSERT(uv_pipe_open(&pipe_stdout_parent, fds_stdout[0]) == 0); + ASSERT_OK(uv_pipe_init(loop, &pipe_stdin_child, 0)); + ASSERT_OK(uv_pipe_init(loop, &pipe_stdout_child, 0)); + ASSERT_OK(uv_pipe_init(loop, &pipe_stdin_parent, 0)); + ASSERT_OK(uv_pipe_init(loop, &pipe_stdout_parent, 0)); + + ASSERT_OK(uv_pipe(fds_stdin, 0, 0)); + ASSERT_OK(uv_pipe(fds_stdout, 0, 0)); + + ASSERT_OK(uv_pipe_open(&pipe_stdin_child, fds_stdin[0])); + ASSERT_OK(uv_pipe_open(&pipe_stdout_child, fds_stdout[1])); + ASSERT_OK(uv_pipe_open(&pipe_stdin_parent, fds_stdin[1])); + ASSERT_OK(uv_pipe_open(&pipe_stdout_parent, fds_stdout[0])); ASSERT(uv_is_readable((uv_stream_t*) &pipe_stdin_child)); ASSERT(uv_is_writable((uv_stream_t*) &pipe_stdout_child)); ASSERT(uv_is_writable((uv_stream_t*) &pipe_stdin_parent)); ASSERT(uv_is_readable((uv_stream_t*) &pipe_stdout_parent)); /* Some systems (SVR4) open a bidirectional pipe, most don't. */ bidir = uv_is_writable((uv_stream_t*) &pipe_stdin_child); - ASSERT(uv_is_readable((uv_stream_t*) &pipe_stdout_child) == bidir); - ASSERT(uv_is_readable((uv_stream_t*) &pipe_stdin_parent) == bidir); - ASSERT(uv_is_writable((uv_stream_t*) &pipe_stdout_parent) == bidir); + ASSERT_EQ(uv_is_readable((uv_stream_t*) &pipe_stdout_child), bidir); + ASSERT_EQ(uv_is_readable((uv_stream_t*) &pipe_stdin_parent), bidir); + ASSERT_EQ(uv_is_writable((uv_stream_t*) &pipe_stdout_parent), bidir); child_stdio[0].flags = UV_INHERIT_STREAM; child_stdio[0].data.stream = (uv_stream_t *) &pipe_stdin_child; @@ -1833,7 +1928,7 @@ TEST_IMPL(spawn_inherit_streams) { options.stdio = child_stdio; options.stdio_count = 2; - ASSERT(uv_spawn(loop, &child_req, &options) == 0); + ASSERT_OK(uv_spawn(loop, &child_req, &options)); uv_close((uv_handle_t*) &pipe_stdin_child, NULL); uv_close((uv_handle_t*) &pipe_stdout_child, NULL); @@ -1848,21 +1943,21 @@ TEST_IMPL(spawn_inherit_streams) { &buf, 1, write_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_start((uv_stream_t*) &pipe_stdout_parent, on_alloc, on_read); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 3); + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(3, close_cb_called); r = memcmp(ubuf, output, sizeof ubuf); - ASSERT(r == 0); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -1886,11 +1981,49 @@ TEST_IMPL(spawn_quoted_path) { /* We test if libuv will not segfault. */ uv_spawn(uv_default_loop(), &process, &options); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; #endif } +TEST_IMPL(spawn_exercise_sigchld_issue) { + int r; + int i; + uv_process_options_t dummy_options = {0}; + uv_process_t dummy_processes[100]; + char* args[2]; + + init_process_options("spawn_helper1", exit_cb); + + r = uv_spawn(uv_default_loop(), &process, &options); + ASSERT_OK(r); + + // This test exercises a bug in the darwin kernel that causes SIGCHLD not to + // be delivered sometimes. Calling posix_spawn many times increases the + // likelihood of encountering this issue, so spin a few times to make this + // test more reliable. + dummy_options.file = args[0] = "program-that-had-better-not-exist"; + args[1] = NULL; + dummy_options.args = args; + dummy_options.exit_cb = fail_cb; + dummy_options.flags = 0; + for (i = 0; i < 100; i++) { + r = uv_spawn(uv_default_loop(), &dummy_processes[i], &dummy_options); + if (r != UV_ENOENT) + ASSERT_EQ(r, UV_EACCES); + uv_close((uv_handle_t*) &dummy_processes[i], close_cb); + } + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT_OK(r); + + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(101, close_cb_called); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} + /* Helper for child process of spawn_inherit_streams */ #ifndef _WIN32 void spawn_stdin_stdout(void) { @@ -1904,14 +2037,14 @@ void spawn_stdin_stdout(void) { if (r == 0) { return; } - ASSERT(r > 0); + ASSERT_GT(r, 0); c = r; pbuf = buf; while (c) { do { w = write(1, pbuf, (size_t)c); } while (w == -1 && errno == EINTR); - ASSERT(w >= 0); + ASSERT_GE(w, 0); pbuf = pbuf + w; c = c - w; } @@ -1923,14 +2056,14 @@ void spawn_stdin_stdout(void) { char* pbuf; HANDLE h_stdin = GetStdHandle(STD_INPUT_HANDLE); HANDLE h_stdout = GetStdHandle(STD_OUTPUT_HANDLE); - ASSERT(h_stdin != INVALID_HANDLE_VALUE); - ASSERT(h_stdout != INVALID_HANDLE_VALUE); + ASSERT_PTR_NE(h_stdin, INVALID_HANDLE_VALUE); + ASSERT_PTR_NE(h_stdout, INVALID_HANDLE_VALUE); for (;;) { DWORD n_read; DWORD n_written; DWORD to_write; if (!ReadFile(h_stdin, buf, sizeof buf, &n_read, NULL)) { - ASSERT(GetLastError() == ERROR_BROKEN_PIPE); + ASSERT_EQ(GetLastError(), ERROR_BROKEN_PIPE); return; } to_write = n_read; @@ -1943,3 +2076,37 @@ void spawn_stdin_stdout(void) { } } #endif /* !_WIN32 */ + +TEST_IMPL(spawn_relative_path) { + char* sep; + + init_process_options("spawn_helper1", exit_cb); + + exepath_size = sizeof(exepath) - 2; + ASSERT_OK(uv_exepath(exepath, &exepath_size)); + exepath[exepath_size] = '\0'; + + /* Poor man's basename(3). */ + sep = strrchr(exepath, '/'); + if (sep == NULL) + sep = strrchr(exepath, '\\'); + ASSERT_NOT_NULL(sep); + + /* Split into dirname and basename and make basename relative. */ + memmove(sep + 2, sep, 1 + strlen(sep)); + sep[0] = '\0'; + sep[1] = '.'; + sep[2] = '/'; + + options.cwd = exepath; + options.file = options.args[0] = sep + 1; + + ASSERT_OK(uv_spawn(uv_default_loop(), &process, &options)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(1, close_cb_called); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} diff --git a/test/test-stdio-over-pipes.c b/test/test-stdio-over-pipes.c index 1aed4712277..c41040ee2fd 100644 --- a/test/test-stdio-over-pipes.c +++ b/test/test-stdio-over-pipes.c @@ -52,8 +52,8 @@ static void exit_cb(uv_process_t* process, int term_signal) { printf("exit_cb\n"); exit_cb_called++; - ASSERT(exit_status == 0); - ASSERT(term_signal == 0); + ASSERT_OK(exit_status); + ASSERT_OK(term_signal); uv_close((uv_handle_t*)process, close_cb); uv_close((uv_handle_t*)&in, close_cb); uv_close((uv_handle_t*)&out, close_cb); @@ -62,7 +62,7 @@ static void exit_cb(uv_process_t* process, static void init_process_options(char* test, uv_exit_cb exit_cb) { int r = uv_exepath(exepath, &exepath_size); - ASSERT(r == 0); + ASSERT_OK(r); exepath[exepath_size] = '\0'; args[0] = exepath; args[1] = test; @@ -104,11 +104,11 @@ static void on_read(uv_stream_t* pipe, ssize_t nread, const uv_buf_t* rdbuf) { if (nread > 0) { output_used += nread; if (output_used % 12 == 0) { - ASSERT(memcmp("hello world\n", output, 12) == 0); + ASSERT_OK(memcmp("hello world\n", output, 12)); wrbuf = uv_buf_init(output, 12); req = malloc(sizeof(*req)); r = uv_write(req, (uv_stream_t*) &in, &wrbuf, 1, after_write); - ASSERT(r == 0); + ASSERT_OK(r); } } @@ -140,22 +140,22 @@ static void test_stdio_over_pipes(int overlapped) { options.stdio_count = 3; r = uv_spawn(loop, &process, &options); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); - ASSERT(r == 0); + ASSERT_OK(r); - r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT_OK(r); - ASSERT(on_read_cb_called > 1); - ASSERT(after_write_cb_called == 2); - ASSERT(exit_cb_called == 1); - ASSERT(close_cb_called == 3); - ASSERT(memcmp("hello world\nhello world\n", output, 24) == 0); - ASSERT(output_used == 24); + ASSERT_GT(on_read_cb_called, 1); + ASSERT_EQ(2, after_write_cb_called); + ASSERT_EQ(1, exit_cb_called); + ASSERT_EQ(3, close_cb_called); + ASSERT_OK(memcmp("hello world\nhello world\n", output, 24)); + ASSERT_EQ(24, output_used); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); } TEST_IMPL(stdio_over_pipes) { @@ -179,8 +179,8 @@ static uv_pipe_t stdin_pipe2; static uv_pipe_t stdout_pipe2; static void on_pipe_read(uv_stream_t* pipe, ssize_t nread, const uv_buf_t* buf) { - ASSERT(nread > 0); - ASSERT(memcmp("hello world\n", buf->base, nread) == 0); + ASSERT_GT(nread, 0); + ASSERT_OK(memcmp("hello world\n", buf->base, nread)); on_pipe_read_called++; free(buf->base); @@ -190,7 +190,7 @@ static void on_pipe_read(uv_stream_t* pipe, ssize_t nread, const uv_buf_t* buf) static void after_pipe_write(uv_write_t* req, int status) { - ASSERT(status == 0); + ASSERT_OK(status); after_write_called++; } @@ -222,17 +222,17 @@ int stdio_over_pipes_helper(void) { int r; uv_loop_t* loop = uv_default_loop(); - ASSERT(UV_NAMED_PIPE == uv_guess_handle(0)); - ASSERT(UV_NAMED_PIPE == uv_guess_handle(1)); + ASSERT_EQ(UV_NAMED_PIPE, uv_guess_handle(0)); + ASSERT_EQ(UV_NAMED_PIPE, uv_guess_handle(1)); r = uv_pipe_init(loop, &stdin_pipe1, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_init(loop, &stdout_pipe1, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_init(loop, &stdin_pipe2, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_pipe_init(loop, &stdout_pipe2, 0); - ASSERT(r == 0); + ASSERT_OK(r); uv_pipe_open(&stdin_pipe1, 0); uv_pipe_open(&stdout_pipe1, 1); @@ -256,15 +256,15 @@ int stdio_over_pipes_helper(void) { &buf[i], 1, after_pipe_write); - ASSERT(r == 0); + ASSERT_OK(r); } notify_parent_process(); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(after_write_called == 7 * (j + 1)); - ASSERT(on_pipe_read_called == j); - ASSERT(close_cb_called == 0); + ASSERT_EQ(after_write_called, 7 * (j + 1)); + ASSERT_EQ(on_pipe_read_called, j); + ASSERT_OK(close_cb_called); uv_ref((uv_handle_t*) &stdout_pipe1); uv_ref((uv_handle_t*) &stdin_pipe1); @@ -274,13 +274,13 @@ int stdio_over_pipes_helper(void) { r = uv_read_start((uv_stream_t*) (j == 0 ? &stdin_pipe1 : &stdin_pipe2), on_read_alloc, on_pipe_read); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(after_write_called == 7 * (j + 1)); - ASSERT(on_pipe_read_called == j + 1); - ASSERT(close_cb_called == 0); + ASSERT_EQ(after_write_called, 7 * (j + 1)); + ASSERT_EQ(on_pipe_read_called, j + 1); + ASSERT_OK(close_cb_called); } uv_close((uv_handle_t*)&stdin_pipe1, close_cb); @@ -290,10 +290,10 @@ int stdio_over_pipes_helper(void) { uv_run(loop, UV_RUN_DEFAULT); - ASSERT(after_write_called == 14); - ASSERT(on_pipe_read_called == 2); - ASSERT(close_cb_called == 4); + ASSERT_EQ(14, after_write_called); + ASSERT_EQ(2, on_pipe_read_called); + ASSERT_EQ(4, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-strscpy.c b/test/test-strscpy.c index 4e7db6ffec8..b4699cff056 100644 --- a/test/test-strscpy.c +++ b/test/test-strscpy.c @@ -29,25 +29,25 @@ TEST_IMPL(strscpy) { char d[4]; - ASSERT(0 == uv__strscpy(d, "", 0)); - ASSERT(0 == uv__strscpy(d, "x", 0)); + ASSERT_OK(uv__strscpy(d, "", 0)); + ASSERT_OK(uv__strscpy(d, "x", 0)); memset(d, 0, sizeof(d)); - ASSERT(1 == uv__strscpy(d, "x", sizeof(d))); - ASSERT(0 == memcmp(d, "x\0\0", sizeof(d))); + ASSERT_EQ(1, uv__strscpy(d, "x", sizeof(d))); + ASSERT_OK(memcmp(d, "x\0\0", sizeof(d))); memset(d, 0, sizeof(d)); - ASSERT(2 == uv__strscpy(d, "xy", sizeof(d))); - ASSERT(0 == memcmp(d, "xy\0", sizeof(d))); + ASSERT_EQ(2, uv__strscpy(d, "xy", sizeof(d))); + ASSERT_OK(memcmp(d, "xy\0", sizeof(d))); - ASSERT(3 == uv__strscpy(d, "xyz", sizeof(d))); - ASSERT(0 == memcmp(d, "xyz", sizeof(d))); + ASSERT_EQ(3, uv__strscpy(d, "xyz", sizeof(d))); + ASSERT_OK(memcmp(d, "xyz", sizeof(d))); - ASSERT(UV_E2BIG == uv__strscpy(d, "xyzz", sizeof(d))); - ASSERT(0 == memcmp(d, "xyz", sizeof(d))); + ASSERT_EQ(UV_E2BIG, uv__strscpy(d, "xyzz", sizeof(d))); + ASSERT_OK(memcmp(d, "xyz", sizeof(d))); - ASSERT(UV_E2BIG == uv__strscpy(d, "xyzzy", sizeof(d))); - ASSERT(0 == memcmp(d, "xyz", sizeof(d))); + ASSERT_EQ(UV_E2BIG, uv__strscpy(d, "xyzzy", sizeof(d))); + ASSERT_OK(memcmp(d, "xyz", sizeof(d))); return 0; } diff --git a/test/test-strtok.c b/test/test-strtok.c new file mode 100644 index 00000000000..f4e6cdf8b75 --- /dev/null +++ b/test/test-strtok.c @@ -0,0 +1,90 @@ +/* Copyright libuv project contributors. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to +* deal in the Software without restriction, including without limitation the +* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +* sell copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +* IN THE SOFTWARE. +*/ + +#include "uv.h" +#include "task.h" +#include + +#include "../src/strtok.h" +#include "../src/strtok.c" + +struct strtok_test_case { + const char* str; + const char* sep; +}; + +const char* tokens[] = { + "abc", + NULL, + + "abc", + "abf", + NULL, + + "This", + "is.a", + "test", + "of", + "the", + "string", + "tokenizer", + "function.", + NULL, + + "Hello", + "This-is-a-nice", + "-string", + NULL +}; + +#define ASSERT_STRCMP(x, y) \ + ASSERT_NE((x != NULL && y != NULL && strcmp(x, y) == 0) || (x == y && x == NULL), 0) + +TEST_IMPL(strtok) { + struct strtok_test_case tests[] = { + { "abc", "" }, + { "abc.abf", "." }, + { "This;is.a:test:of=the/string\\tokenizer-function.", "\\/:;=-" }, + { "Hello This-is-a-nice.-string", " ." }, + }; + size_t tokens_len = ARRAY_SIZE(tokens); + size_t tests_len = ARRAY_SIZE(tests); + size_t i; + size_t j; + char* itr; + char* tok_r; + char current_test[2048]; + + for (i = 0, j = 0; i < tests_len; i += 1) { + ASSERT_LT(j, tokens_len); + snprintf(current_test, sizeof(current_test), "%s", tests[i].str); + tok_r = uv__strtok(current_test, tests[i].sep, &itr); + ASSERT_STRCMP(tok_r, tokens[j]); + j++; + while (tok_r) { + ASSERT_LT(j, tokens_len); + tok_r = uv__strtok(NULL, tests[i].sep, &itr); + ASSERT_STRCMP(tok_r, tokens[j]); + j++; + } + } + return 0; +} diff --git a/test/test-tcp-alloc-cb-fail.c b/test/test-tcp-alloc-cb-fail.c index b6f4ca38850..ff1cfcb844a 100644 --- a/test/test-tcp-alloc-cb-fail.c +++ b/test/test-tcp-alloc-cb-fail.c @@ -42,7 +42,7 @@ static void close_cb(uv_handle_t* handle) { } static void write_cb(uv_write_t* req, int status) { - ASSERT(status == 0); + ASSERT_OK(status); } static void conn_alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { @@ -52,9 +52,9 @@ static void conn_alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { static void conn_read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { - ASSERT(nread == UV_ENOBUFS); + ASSERT_EQ(nread, UV_ENOBUFS); ASSERT_NULL(buf->base); - ASSERT(buf->len == 0); + ASSERT_OK(buf->len); uv_close((uv_handle_t*) &incoming, close_cb); uv_close((uv_handle_t*) &client, close_cb); @@ -65,23 +65,23 @@ static void connect_cb(uv_connect_t* req, int status) { int r; uv_buf_t buf; - ASSERT(status == 0); + ASSERT_OK(status); connect_cb_called++; buf = uv_buf_init(hello, sizeof(hello)); r = uv_write(&write_req, req->handle, &buf, 1, write_cb); - ASSERT(r == 0); + ASSERT_OK(r); } static void connection_cb(uv_stream_t* tcp, int status) { - ASSERT(status == 0); + ASSERT_OK(status); - ASSERT(0 == uv_tcp_init(tcp->loop, &incoming)); - ASSERT(0 == uv_accept(tcp, (uv_stream_t*) &incoming)); - ASSERT(0 == uv_read_start((uv_stream_t*) &incoming, - conn_alloc_cb, - conn_read_cb)); + ASSERT_OK(uv_tcp_init(tcp->loop, &incoming)); + ASSERT_OK(uv_accept(tcp, (uv_stream_t*) &incoming)); + ASSERT_OK(uv_read_start((uv_stream_t*) &incoming, + conn_alloc_cb, + conn_read_cb)); connection_cb_called++; } @@ -90,11 +90,11 @@ static void connection_cb(uv_stream_t* tcp, int status) { static void start_server(void) { struct sockaddr_in addr; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); - ASSERT(0 == uv_tcp_init(uv_default_loop(), &server)); - ASSERT(0 == uv_tcp_bind(&server, (struct sockaddr*) &addr, 0)); - ASSERT(0 == uv_listen((uv_stream_t*) &server, 128, connection_cb)); + ASSERT_OK(uv_tcp_init(uv_default_loop(), &server)); + ASSERT_OK(uv_tcp_bind(&server, (struct sockaddr*) &addr, 0)); + ASSERT_OK(uv_listen((uv_stream_t*) &server, 128, connection_cb)); } @@ -104,20 +104,20 @@ TEST_IMPL(tcp_alloc_cb_fail) { start_server(); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); - ASSERT(0 == uv_tcp_init(uv_default_loop(), &client)); - ASSERT(0 == uv_tcp_connect(&connect_req, - &client, - (struct sockaddr*) &addr, - connect_cb)); + ASSERT_OK(uv_tcp_init(uv_default_loop(), &client)); + ASSERT_OK(uv_tcp_connect(&connect_req, + &client, + (struct sockaddr*) &addr, + connect_cb)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(connect_cb_called == 1); - ASSERT(connection_cb_called == 1); - ASSERT(close_cb_called == 3); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(1, connection_cb_called); + ASSERT_EQ(3, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-tcp-bind-error.c b/test/test-tcp-bind-error.c index fdd1fe07450..89e4e33993f 100644 --- a/test/test-tcp-bind-error.c +++ b/test/test-tcp-bind-error.c @@ -36,7 +36,7 @@ static void close_cb(uv_handle_t* handle) { static void connect_cb(uv_connect_t* req, int status) { - ASSERT(status == UV_EADDRINUSE); + ASSERT_EQ(status, UV_EADDRINUSE); uv_close((uv_handle_t*) req->handle, close_cb); connect_cb_called++; } @@ -53,26 +53,26 @@ TEST_IMPL(tcp_bind_error_addrinuse_connect) { * (greatest common denominator across platforms) but the connect callback * should receive an UV_EADDRINUSE error. */ - ASSERT(0 == uv_tcp_init(uv_default_loop(), &conn)); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); - ASSERT(0 == uv_tcp_bind(&conn, (const struct sockaddr*) &addr, 0)); + ASSERT_OK(uv_tcp_init(uv_default_loop(), &conn)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_tcp_bind(&conn, (const struct sockaddr*) &addr, 0)); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT + 1, &addr)); - ASSERT(0 == uv_tcp_connect(&req, - &conn, - (const struct sockaddr*) &addr, - connect_cb)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT + 1, &addr)); + ASSERT_OK(uv_tcp_connect(&req, + &conn, + (const struct sockaddr*) &addr, + connect_cb)); addrlen = sizeof(addr); - ASSERT(UV_EADDRINUSE == uv_tcp_getsockname(&conn, - (struct sockaddr*) &addr, - &addrlen)); + ASSERT_EQ(UV_EADDRINUSE, uv_tcp_getsockname(&conn, + (struct sockaddr*) &addr, + &addrlen)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(connect_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -82,30 +82,30 @@ TEST_IMPL(tcp_bind_error_addrinuse_listen) { uv_tcp_t server1, server2; int r; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server1); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&server1, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_init(uv_default_loop(), &server2); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&server2, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&server1, 128, NULL); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&server2, 128, NULL); - ASSERT(r == UV_EADDRINUSE); + ASSERT_EQ(r, UV_EADDRINUSE); uv_close((uv_handle_t*)&server1, close_cb); uv_close((uv_handle_t*)&server2, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 2); + ASSERT_EQ(2, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -115,10 +115,10 @@ TEST_IMPL(tcp_bind_error_addrnotavail_1) { uv_tcp_t server; int r; - ASSERT(0 == uv_ip4_addr("127.255.255.255", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.255.255.255", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); /* It seems that Linux is broken here - bind succeeds. */ r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); @@ -128,9 +128,9 @@ TEST_IMPL(tcp_bind_error_addrnotavail_1) { uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -140,20 +140,20 @@ TEST_IMPL(tcp_bind_error_addrnotavail_2) { uv_tcp_t server; int r; - ASSERT(0 == uv_ip4_addr("4.4.4.4", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("4.4.4.4", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); - ASSERT(r == UV_EADDRNOTAVAIL); + ASSERT_EQ(r, UV_EADDRNOTAVAIL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -168,17 +168,17 @@ TEST_IMPL(tcp_bind_error_fault) { garbage_addr = (struct sockaddr_in*) &garbage; r = uv_tcp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&server, (const struct sockaddr*) garbage_addr, 0); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -190,23 +190,23 @@ TEST_IMPL(tcp_bind_error_inval) { uv_tcp_t server; int r; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr1)); - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT_2, &addr2)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr1)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT_2, &addr2)); r = uv_tcp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr1, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr2, 0); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -216,14 +216,14 @@ TEST_IMPL(tcp_bind_localhost_ok) { uv_tcp_t server; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -233,14 +233,14 @@ TEST_IMPL(tcp_bind_invalid_flags) { uv_tcp_t server; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, UV_TCP_IPV6ONLY); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -250,11 +250,11 @@ TEST_IMPL(tcp_listen_without_bind) { uv_tcp_t server; r = uv_tcp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&server, 128, NULL); - ASSERT(r == 0); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -267,33 +267,51 @@ TEST_IMPL(tcp_bind_writable_flags) { uv_shutdown_t shutdown_req; int r; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&server, 128, NULL); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(0 == uv_is_writable((uv_stream_t*) &server)); - ASSERT(0 == uv_is_readable((uv_stream_t*) &server)); + ASSERT_OK(uv_is_writable((uv_stream_t*) &server)); + ASSERT_OK(uv_is_readable((uv_stream_t*) &server)); buf = uv_buf_init("PING", 4); r = uv_write(&write_req, (uv_stream_t*) &server, &buf, 1, NULL); - ASSERT(r == UV_EPIPE); + ASSERT_EQ(r, UV_EPIPE); r = uv_shutdown(&shutdown_req, (uv_stream_t*) &server, NULL); - ASSERT(r == UV_ENOTCONN); + ASSERT_EQ(r, UV_ENOTCONN); r = uv_read_start((uv_stream_t*) &server, (uv_alloc_cb) abort, (uv_read_cb) abort); - ASSERT(r == UV_ENOTCONN); + ASSERT_EQ(r, UV_ENOTCONN); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} + +TEST_IMPL(tcp_bind_or_listen_error_after_close) { + uv_tcp_t tcp; + struct sockaddr_in addr; + + memset(&addr, 0, sizeof(addr)); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(9999); + addr.sin_family = AF_INET; + + ASSERT_OK(uv_tcp_init(uv_default_loop(), &tcp)); + uv_close((uv_handle_t*) &tcp, NULL); + ASSERT_EQ(uv_tcp_bind(&tcp, (struct sockaddr*) &addr, 0), UV_EINVAL); + ASSERT_EQ(uv_listen((uv_stream_t*) &tcp, 5, NULL), UV_EINVAL); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-tcp-bind6-error.c b/test/test-tcp-bind6-error.c index 86181b708e3..f44d3b7ee9a 100644 --- a/test/test-tcp-bind6-error.c +++ b/test/test-tcp-bind6-error.c @@ -42,31 +42,31 @@ TEST_IMPL(tcp_bind6_error_addrinuse) { if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); - ASSERT(0 == uv_ip6_addr("::", TEST_PORT, &addr)); + ASSERT_OK(uv_ip6_addr("::", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server1); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&server1, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_init(uv_default_loop(), &server2); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&server2, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&server1, 128, NULL); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&server2, 128, NULL); - ASSERT(r == UV_EADDRINUSE); + ASSERT_EQ(r, UV_EADDRINUSE); uv_close((uv_handle_t*)&server1, close_cb); uv_close((uv_handle_t*)&server2, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 2); + ASSERT_EQ(2, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -79,20 +79,20 @@ TEST_IMPL(tcp_bind6_error_addrnotavail) { if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); - ASSERT(0 == uv_ip6_addr("4:4:4:4:4:4:4:4", TEST_PORT, &addr)); + ASSERT_OK(uv_ip6_addr("4:4:4:4:4:4:4:4", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); - ASSERT(r == UV_EADDRNOTAVAIL); + ASSERT_EQ(r, UV_EADDRNOTAVAIL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -110,17 +110,17 @@ TEST_IMPL(tcp_bind6_error_fault) { garbage_addr = (struct sockaddr_in6*) &garbage; r = uv_tcp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&server, (const struct sockaddr*) garbage_addr, 0); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -135,23 +135,23 @@ TEST_IMPL(tcp_bind6_error_inval) { if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); - ASSERT(0 == uv_ip6_addr("::", TEST_PORT, &addr1)); - ASSERT(0 == uv_ip6_addr("::", TEST_PORT_2, &addr2)); + ASSERT_OK(uv_ip6_addr("::", TEST_PORT, &addr1)); + ASSERT_OK(uv_ip6_addr("::", TEST_PORT_2, &addr2)); r = uv_tcp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr1, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr2, 0); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -164,13 +164,13 @@ TEST_IMPL(tcp_bind6_localhost_ok) { if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); - ASSERT(0 == uv_ip6_addr("::1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip6_addr("::1", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-tcp-close-accept.c b/test/test-tcp-close-accept.c index 624262bcfe9..4988dd13275 100644 --- a/test/test-tcp-close-accept.c +++ b/test/test-tcp-close-accept.c @@ -47,7 +47,7 @@ static void close_cb(uv_handle_t* handle) { } static void write_cb(uv_write_t* req, int status) { - ASSERT(status == 0); + ASSERT_OK(status); write_cb_called++; } @@ -57,7 +57,7 @@ static void connect_cb(uv_connect_t* req, int status) { uv_stream_t* outgoing; if (req == &tcp_check_req) { - ASSERT(status != 0); + ASSERT(status); /* * Time to finish the test: close both the check and pending incoming @@ -68,14 +68,14 @@ static void connect_cb(uv_connect_t* req, int status) { return; } - ASSERT(status == 0); - ASSERT(connect_reqs <= req); - ASSERT(req <= connect_reqs + ARRAY_SIZE(connect_reqs)); + ASSERT_OK(status); + ASSERT_LE(connect_reqs, req); + ASSERT_LE(req, connect_reqs + ARRAY_SIZE(connect_reqs)); i = req - connect_reqs; buf = uv_buf_init("x", 1); outgoing = (uv_stream_t*) &tcp_outgoing[i]; - ASSERT(0 == uv_write(&write_reqs[i], outgoing, &buf, 1, write_cb)); + ASSERT_OK(uv_write(&write_reqs[i], outgoing, &buf, 1, write_cb)); } static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { @@ -89,9 +89,9 @@ static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { unsigned int i; pending_incoming = (uv_tcp_t*) stream - &tcp_incoming[0]; - ASSERT(pending_incoming < got_connections); - ASSERT(0 == uv_read_stop(stream)); - ASSERT(1 == nread); + ASSERT_LT(pending_incoming, got_connections); + ASSERT_OK(uv_read_stop(stream)); + ASSERT_EQ(1, nread); loop = stream->loop; read_cb_called++; @@ -106,19 +106,19 @@ static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { uv_close((uv_handle_t*) &tcp_server, close_cb); /* Create new fd that should be one of the closed incomings */ - ASSERT(0 == uv_tcp_init(loop, &tcp_check)); - ASSERT(0 == uv_tcp_connect(&tcp_check_req, - &tcp_check, - (const struct sockaddr*) &addr, - connect_cb)); - ASSERT(0 == uv_read_start((uv_stream_t*) &tcp_check, alloc_cb, read_cb)); + ASSERT_OK(uv_tcp_init(loop, &tcp_check)); + ASSERT_OK(uv_tcp_connect(&tcp_check_req, + &tcp_check, + (const struct sockaddr*) &addr, + connect_cb)); + ASSERT_OK(uv_read_start((uv_stream_t*) &tcp_check, alloc_cb, read_cb)); } static void connection_cb(uv_stream_t* server, int status) { unsigned int i; uv_tcp_t* incoming; - ASSERT(server == (uv_stream_t*) &tcp_server); + ASSERT_PTR_EQ(server, (uv_stream_t*) &tcp_server); /* Ignore tcp_check connection */ if (got_connections == ARRAY_SIZE(tcp_incoming)) @@ -126,8 +126,8 @@ static void connection_cb(uv_stream_t* server, int status) { /* Accept everyone */ incoming = &tcp_incoming[got_connections++]; - ASSERT(0 == uv_tcp_init(server->loop, incoming)); - ASSERT(0 == uv_accept(server, (uv_stream_t*) incoming)); + ASSERT_OK(uv_tcp_init(server->loop, incoming)); + ASSERT_OK(uv_accept(server, (uv_stream_t*) incoming)); if (got_connections != ARRAY_SIZE(tcp_incoming)) return; @@ -135,7 +135,7 @@ static void connection_cb(uv_stream_t* server, int status) { /* Once all clients are accepted - start reading */ for (i = 0; i < ARRAY_SIZE(tcp_incoming); i++) { incoming = &tcp_incoming[i]; - ASSERT(0 == uv_read_start((uv_stream_t*) incoming, alloc_cb, read_cb)); + ASSERT_OK(uv_read_start((uv_stream_t*) incoming, alloc_cb, read_cb)); } } @@ -162,32 +162,32 @@ TEST_IMPL(tcp_close_accept) { */ loop = uv_default_loop(); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); - ASSERT(0 == uv_tcp_init(loop, &tcp_server)); - ASSERT(0 == uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0)); - ASSERT(0 == uv_listen((uv_stream_t*) &tcp_server, - ARRAY_SIZE(tcp_outgoing), - connection_cb)); + ASSERT_OK(uv_tcp_init(loop, &tcp_server)); + ASSERT_OK(uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0)); + ASSERT_OK(uv_listen((uv_stream_t*) &tcp_server, + ARRAY_SIZE(tcp_outgoing), + connection_cb)); for (i = 0; i < ARRAY_SIZE(tcp_outgoing); i++) { client = tcp_outgoing + i; - ASSERT(0 == uv_tcp_init(loop, client)); - ASSERT(0 == uv_tcp_connect(&connect_reqs[i], - client, - (const struct sockaddr*) &addr, - connect_cb)); + ASSERT_OK(uv_tcp_init(loop, client)); + ASSERT_OK(uv_tcp_connect(&connect_reqs[i], + client, + (const struct sockaddr*) &addr, + connect_cb)); } uv_run(loop, UV_RUN_DEFAULT); - ASSERT(ARRAY_SIZE(tcp_outgoing) == got_connections); - ASSERT((ARRAY_SIZE(tcp_outgoing) + 2) == close_cb_called); - ASSERT(ARRAY_SIZE(tcp_outgoing) == write_cb_called); - ASSERT(1 == read_cb_called); + ASSERT_EQ(ARRAY_SIZE(tcp_outgoing), got_connections); + ASSERT_EQ((ARRAY_SIZE(tcp_outgoing) + 2), close_cb_called); + ASSERT_EQ(ARRAY_SIZE(tcp_outgoing), write_cb_called); + ASSERT_EQ(1, read_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-tcp-close-after-read-timeout.c b/test/test-tcp-close-after-read-timeout.c new file mode 100644 index 00000000000..1b81630157d --- /dev/null +++ b/test/test-tcp-close-after-read-timeout.c @@ -0,0 +1,183 @@ +/* Copyright libuv project and contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +static uv_tcp_t client; +static uv_tcp_t connection; +static uv_connect_t connect_req; +static uv_timer_t timer; + +static int read_cb_called; +static int on_close_called; + +static void on_connection(uv_stream_t* server, int status); + +static void on_client_connect(uv_connect_t* req, int status); +static void on_client_alloc(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf); +static void on_client_read(uv_stream_t* stream, + ssize_t nread, + const uv_buf_t* buf); +static void on_client_timeout(uv_timer_t* handle); + +static void on_close(uv_handle_t* handle); + + +static void on_client_connect(uv_connect_t* conn_req, int status) { + int r; + + r = uv_read_start((uv_stream_t*) &client, on_client_alloc, on_client_read); + ASSERT_OK(r); + + r = uv_timer_start(&timer, on_client_timeout, 1000, 0); + ASSERT_OK(r); +} + + +static void on_client_alloc(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[8]; + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void on_client_read(uv_stream_t* stream, ssize_t nread, + const uv_buf_t* buf) { + ASSERT_LT(nread, 0); + read_cb_called++; +} + + +static void on_client_timeout(uv_timer_t* handle) { + ASSERT_PTR_EQ(handle, &timer); + ASSERT_OK(read_cb_called); + uv_read_stop((uv_stream_t*) &client); + uv_close((uv_handle_t*) &client, on_close); + uv_close((uv_handle_t*) &timer, on_close); +} + + +static void on_connection_alloc(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[8]; + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void on_connection_read(uv_stream_t* stream, + ssize_t nread, + const uv_buf_t* buf) { + ASSERT_EQ(nread, UV_EOF); + read_cb_called++; + uv_close((uv_handle_t*) stream, on_close); +} + + +static void on_connection(uv_stream_t* server, int status) { + int r; + + ASSERT_OK(status); + ASSERT_OK(uv_accept(server, (uv_stream_t*) &connection)); + + r = uv_read_start((uv_stream_t*) &connection, + on_connection_alloc, + on_connection_read); + ASSERT_OK(r); +} + + +static void on_close(uv_handle_t* handle) { + ASSERT_NE(handle == (uv_handle_t*) &client || + handle == (uv_handle_t*) &connection || + handle == (uv_handle_t*) &timer, 0); + on_close_called++; +} + + +static void start_server(uv_loop_t* loop, uv_tcp_t* handle) { + struct sockaddr_in addr; + int r; + + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_tcp_init(loop, handle); + ASSERT_OK(r); + + r = uv_tcp_bind(handle, (const struct sockaddr*) &addr, 0); + ASSERT_OK(r); + + r = uv_listen((uv_stream_t*) handle, 128, on_connection); + ASSERT_OK(r); + + uv_unref((uv_handle_t*) handle); +} + + +/* Check that pending write requests have their callbacks + * invoked when the handle is closed. + */ +TEST_IMPL(tcp_close_after_read_timeout) { + struct sockaddr_in addr; + uv_tcp_t tcp_server; + uv_loop_t* loop; + int r; + + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + loop = uv_default_loop(); + + /* We can't use the echo server, it doesn't handle ECONNRESET. */ + start_server(loop, &tcp_server); + + r = uv_tcp_init(loop, &client); + ASSERT_OK(r); + + r = uv_tcp_connect(&connect_req, + &client, + (const struct sockaddr*) &addr, + on_client_connect); + ASSERT_OK(r); + + r = uv_tcp_init(loop, &connection); + ASSERT_OK(r); + + r = uv_timer_init(loop, &timer); + ASSERT_OK(r); + + ASSERT_OK(read_cb_called); + ASSERT_OK(on_close_called); + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT_OK(r); + + ASSERT_EQ(1, read_cb_called); + ASSERT_EQ(3, on_close_called); + + MAKE_VALGRIND_HAPPY(loop); + return 0; +} diff --git a/test/test-tcp-close-reset.c b/test/test-tcp-close-reset.c index 7ca55c4c7f9..74941789420 100644 --- a/test/test-tcp-close-reset.c +++ b/test/test-tcp-close-reset.c @@ -25,6 +25,12 @@ #include #include /* memset */ +#ifdef _WIN32 +# define INVALID_FD (INVALID_HANDLE_VALUE) +#else +# define INVALID_FD (-1) +#endif + static uv_loop_t* loop; static uv_tcp_t tcp_server; static uv_tcp_t tcp_client; @@ -56,18 +62,35 @@ static void do_write(uv_tcp_t* handle) { buf = uv_buf_init("PING", 4); for (i = 0; i < ARRAY_SIZE(write_reqs); i++) { r = uv_write(&write_reqs[i], (uv_stream_t*) handle, &buf, 1, write_cb); - ASSERT(r == 0); + ASSERT_OK(r); } } static void do_close(uv_tcp_t* handle) { + uv_os_fd_t fd; + int r; + if (shutdown_before_close == 1) { - ASSERT(0 == uv_shutdown(&shutdown_req, (uv_stream_t*) handle, shutdown_cb)); - ASSERT(UV_EINVAL == uv_tcp_close_reset(handle, close_cb)); + ASSERT_OK(uv_shutdown(&shutdown_req, + (uv_stream_t*) handle, + shutdown_cb)); + ASSERT_EQ(UV_EINVAL, uv_tcp_close_reset(handle, close_cb)); + } else if (shutdown_before_close == 2) { + r = uv_fileno((const uv_handle_t*) handle, &fd); + ASSERT_OK(r); +#ifdef _WIN32 + ASSERT_PTR_NE(fd, INVALID_FD); + ASSERT_OK(shutdown((SOCKET)fd, SD_BOTH)); +#else + ASSERT_NE(fd, INVALID_FD); + ASSERT_OK(shutdown(fd, SHUT_RDWR)); +#endif + ASSERT_OK(uv_tcp_close_reset(handle, close_cb)); } else { - ASSERT(0 == uv_tcp_close_reset(handle, close_cb)); - ASSERT(UV_ENOTCONN == uv_shutdown(&shutdown_req, (uv_stream_t*) handle, shutdown_cb)); + ASSERT_OK(uv_tcp_close_reset(handle, close_cb)); + ASSERT_EQ(UV_ENOTCONN, uv_shutdown(&shutdown_req, (uv_stream_t*) handle, + shutdown_cb)); } uv_close((uv_handle_t*) &tcp_server, NULL); @@ -80,14 +103,14 @@ static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { } static void read_cb2(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { - ASSERT((uv_tcp_t*)stream == &tcp_client); + ASSERT_PTR_EQ((uv_tcp_t*)stream, &tcp_client); if (nread == UV_EOF) uv_close((uv_handle_t*) stream, NULL); } static void connect_cb(uv_connect_t* conn_req, int status) { - ASSERT(conn_req == &connect_req); + ASSERT_PTR_EQ(conn_req, &connect_req); uv_read_start((uv_stream_t*) &tcp_client, alloc_cb, read_cb2); do_write(&tcp_client); if (client_close) @@ -97,33 +120,33 @@ static void connect_cb(uv_connect_t* conn_req, int status) { static void write_cb(uv_write_t* req, int status) { /* write callbacks should run before the close callback */ - ASSERT(close_cb_called == 0); - ASSERT(req->handle == (uv_stream_t*)&tcp_client); + ASSERT_OK(close_cb_called); + ASSERT_PTR_EQ(req->handle, (uv_stream_t*)&tcp_client); write_cb_called++; } static void close_cb(uv_handle_t* handle) { if (client_close) - ASSERT(handle == (uv_handle_t*) &tcp_client); + ASSERT_PTR_EQ(handle, (uv_handle_t*) &tcp_client); else - ASSERT(handle == (uv_handle_t*) &tcp_accepted); + ASSERT_PTR_EQ(handle, (uv_handle_t*) &tcp_accepted); close_cb_called++; } static void shutdown_cb(uv_shutdown_t* req, int status) { if (client_close) - ASSERT(req->handle == (uv_stream_t*) &tcp_client); + ASSERT_PTR_EQ(req->handle, (uv_stream_t*) &tcp_client); else - ASSERT(req->handle == (uv_stream_t*) &tcp_accepted); + ASSERT_PTR_EQ(req->handle, (uv_stream_t*) &tcp_accepted); shutdown_cb_called++; } static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { - ASSERT((uv_tcp_t*)stream == &tcp_accepted); + ASSERT_PTR_EQ((uv_tcp_t*)stream, &tcp_accepted); if (nread < 0) { uv_close((uv_handle_t*) stream, NULL); } else { @@ -135,10 +158,10 @@ static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { static void connection_cb(uv_stream_t* server, int status) { - ASSERT(status == 0); + ASSERT_OK(status); - ASSERT(0 == uv_tcp_init(loop, &tcp_accepted)); - ASSERT(0 == uv_accept(server, (uv_stream_t*) &tcp_accepted)); + ASSERT_OK(uv_tcp_init(loop, &tcp_accepted)); + ASSERT_OK(uv_accept(server, (uv_stream_t*) &tcp_accepted)); uv_read_start((uv_stream_t*) &tcp_accepted, alloc_cb, read_cb); } @@ -148,16 +171,16 @@ static void start_server(uv_loop_t* loop, uv_tcp_t* handle) { struct sockaddr_in addr; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_init(loop, handle); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(handle, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)handle, 128, connection_cb); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -165,16 +188,16 @@ static void do_connect(uv_loop_t* loop, uv_tcp_t* tcp_client) { struct sockaddr_in addr; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_init(loop, tcp_client); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_connect(&connect_req, tcp_client, (const struct sockaddr*) &addr, connect_cb); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -193,18 +216,18 @@ TEST_IMPL(tcp_close_reset_client) { do_connect(loop, &tcp_client); - ASSERT(write_cb_called == 0); - ASSERT(close_cb_called == 0); - ASSERT(shutdown_cb_called == 0); + ASSERT_OK(write_cb_called); + ASSERT_OK(close_cb_called); + ASSERT_OK(shutdown_cb_called); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(write_cb_called == 4); - ASSERT(close_cb_called == 1); - ASSERT(shutdown_cb_called == 0); + ASSERT_EQ(4, write_cb_called); + ASSERT_EQ(1, close_cb_called); + ASSERT_OK(shutdown_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -220,18 +243,18 @@ TEST_IMPL(tcp_close_reset_client_after_shutdown) { do_connect(loop, &tcp_client); - ASSERT(write_cb_called == 0); - ASSERT(close_cb_called == 0); - ASSERT(shutdown_cb_called == 0); + ASSERT_OK(write_cb_called); + ASSERT_OK(close_cb_called); + ASSERT_OK(shutdown_cb_called); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(write_cb_called == 4); - ASSERT(close_cb_called == 0); - ASSERT(shutdown_cb_called == 1); + ASSERT_EQ(4, write_cb_called); + ASSERT_OK(close_cb_called); + ASSERT_EQ(1, shutdown_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -247,18 +270,18 @@ TEST_IMPL(tcp_close_reset_accepted) { do_connect(loop, &tcp_client); - ASSERT(write_cb_called == 0); - ASSERT(close_cb_called == 0); - ASSERT(shutdown_cb_called == 0); + ASSERT_OK(write_cb_called); + ASSERT_OK(close_cb_called); + ASSERT_OK(shutdown_cb_called); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(write_cb_called == 4); - ASSERT(close_cb_called == 1); - ASSERT(shutdown_cb_called == 0); + ASSERT_EQ(4, write_cb_called); + ASSERT_EQ(1, close_cb_called); + ASSERT_OK(shutdown_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -274,17 +297,44 @@ TEST_IMPL(tcp_close_reset_accepted_after_shutdown) { do_connect(loop, &tcp_client); - ASSERT(write_cb_called == 0); - ASSERT(close_cb_called == 0); - ASSERT(shutdown_cb_called == 0); + ASSERT_OK(write_cb_called); + ASSERT_OK(close_cb_called); + ASSERT_OK(shutdown_cb_called); + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT_OK(r); + + ASSERT_EQ(4, write_cb_called); + ASSERT_OK(close_cb_called); + ASSERT_EQ(1, shutdown_cb_called); + + MAKE_VALGRIND_HAPPY(loop); + return 0; +} + +TEST_IMPL(tcp_close_reset_accepted_after_socket_shutdown) { + int r; + + loop = uv_default_loop(); + + start_server(loop, &tcp_server); + + client_close = 0; + shutdown_before_close = 2; + + do_connect(loop, &tcp_client); + + ASSERT_OK(write_cb_called); + ASSERT_OK(close_cb_called); + ASSERT_OK(shutdown_cb_called); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(write_cb_called == 4); - ASSERT(close_cb_called == 0); - ASSERT(shutdown_cb_called == 1); + ASSERT_EQ(4, write_cb_called); + ASSERT_EQ(1, close_cb_called); + ASSERT_OK(shutdown_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-tcp-close-while-connecting.c b/test/test-tcp-close-while-connecting.c index 8d0b8270645..ba5e46901a4 100644 --- a/test/test-tcp-close-while-connecting.c +++ b/test/test-tcp-close-while-connecting.c @@ -69,26 +69,26 @@ TEST_IMPL(tcp_close_while_connecting) { int r; loop = uv_default_loop(); - ASSERT(0 == uv_ip4_addr("1.2.3.4", TEST_PORT, &addr)); - ASSERT(0 == uv_tcp_init(loop, &tcp_handle)); + ASSERT_OK(uv_ip4_addr("1.2.3.4", TEST_PORT, &addr)); + ASSERT_OK(uv_tcp_init(loop, &tcp_handle)); r = uv_tcp_connect(&connect_req, &tcp_handle, (const struct sockaddr*) &addr, connect_cb); if (r == UV_ENETUNREACH) RETURN_SKIP("Network unreachable."); - ASSERT(r == 0); - ASSERT(0 == uv_timer_init(loop, &timer1_handle)); - ASSERT(0 == uv_timer_start(&timer1_handle, timer1_cb, 1, 0)); - ASSERT(0 == uv_timer_init(loop, &timer2_handle)); - ASSERT(0 == uv_timer_start(&timer2_handle, timer2_cb, 86400 * 1000, 0)); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - - ASSERT(connect_cb_called == 1); - ASSERT(timer1_cb_called == 1); - ASSERT(close_cb_called == 2); - - MAKE_VALGRIND_HAPPY(); + ASSERT_OK(r); + ASSERT_OK(uv_timer_init(loop, &timer1_handle)); + ASSERT_OK(uv_timer_start(&timer1_handle, timer1_cb, 1, 0)); + ASSERT_OK(uv_timer_init(loop, &timer2_handle)); + ASSERT_OK(uv_timer_start(&timer2_handle, timer2_cb, 86400 * 1000, 0)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(1, timer1_cb_called); + ASSERT_EQ(2, close_cb_called); + + MAKE_VALGRIND_HAPPY(loop); if (netunreach_errors > 0) RETURN_SKIP("Network unreachable."); diff --git a/test/test-tcp-close.c b/test/test-tcp-close.c index 5a7bd6893bf..ed19da6f783 100644 --- a/test/test-tcp-close.c +++ b/test/test-tcp-close.c @@ -49,7 +49,7 @@ static void connect_cb(uv_connect_t* conn_req, int status) { ASSERT_NOT_NULL(req); r = uv_write(req, (uv_stream_t*)&tcp_handle, &buf, 1, write_cb); - ASSERT(r == 0); + ASSERT_OK(r); } uv_close((uv_handle_t*)&tcp_handle, close_cb); @@ -58,21 +58,21 @@ static void connect_cb(uv_connect_t* conn_req, int status) { static void write_cb(uv_write_t* req, int status) { /* write callbacks should run before the close callback */ - ASSERT(close_cb_called == 0); - ASSERT(req->handle == (uv_stream_t*)&tcp_handle); + ASSERT_OK(close_cb_called); + ASSERT_PTR_EQ(req->handle, (uv_stream_t*)&tcp_handle); write_cb_called++; free(req); } static void close_cb(uv_handle_t* handle) { - ASSERT(handle == (uv_handle_t*)&tcp_handle); + ASSERT_PTR_EQ(handle, (uv_handle_t*)&tcp_handle); close_cb_called++; } static void connection_cb(uv_stream_t* server, int status) { - ASSERT(status == 0); + ASSERT_OK(status); } @@ -80,16 +80,16 @@ static void start_server(uv_loop_t* loop, uv_tcp_t* handle) { struct sockaddr_in addr; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_init(loop, handle); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(handle, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)handle, 128, connection_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_unref((uv_handle_t*)handle); } @@ -104,7 +104,7 @@ TEST_IMPL(tcp_close) { uv_loop_t* loop; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); loop = uv_default_loop(); @@ -112,25 +112,25 @@ TEST_IMPL(tcp_close) { start_server(loop, &tcp_server); r = uv_tcp_init(loop, &tcp_handle); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_connect(&connect_req, &tcp_handle, (const struct sockaddr*) &addr, connect_cb); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(write_cb_called == 0); - ASSERT(close_cb_called == 0); + ASSERT_OK(write_cb_called); + ASSERT_OK(close_cb_called); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); printf("%d of %d write reqs seen\n", write_cb_called, NUM_WRITE_REQS); - ASSERT(write_cb_called == NUM_WRITE_REQS); - ASSERT(close_cb_called == 1); + ASSERT_EQ(write_cb_called, NUM_WRITE_REQS); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-tcp-connect-error-after-write.c b/test/test-tcp-connect-error-after-write.c index 3f2e3572da9..7321259628d 100644 --- a/test/test-tcp-connect-error-after-write.c +++ b/test/test-tcp-connect-error-after-write.c @@ -37,14 +37,14 @@ static void close_cb(uv_handle_t* handle) { static void connect_cb(uv_connect_t* req, int status) { - ASSERT(status < 0); + ASSERT_LT(status, 0); connect_cb_called++; uv_close((uv_handle_t*)req->handle, close_cb); } static void write_cb(uv_write_t* req, int status) { - ASSERT(status < 0); + ASSERT_LT(status, 0); write_cb_called++; } @@ -55,6 +55,11 @@ static void write_cb(uv_write_t* req, int status) { * Related issue: https://github.com/joyent/libuv/issues/443 */ TEST_IMPL(tcp_connect_error_after_write) { +#ifdef _WIN32 + RETURN_SKIP("This test is disabled on Windows for now. " + "See https://github.com/joyent/libuv/issues/444\n"); +#else + uv_connect_t connect_req; struct sockaddr_in addr; uv_write_t write_req; @@ -62,37 +67,32 @@ TEST_IMPL(tcp_connect_error_after_write) { uv_buf_t buf; int r; -#ifdef _WIN32 - fprintf(stderr, "This test is disabled on Windows for now.\n"); - fprintf(stderr, "See https://github.com/joyent/libuv/issues/444\n"); - return 0; /* windows slackers... */ -#endif - - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); buf = uv_buf_init("TEST", 4); r = uv_tcp_init(uv_default_loop(), &conn); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_write(&write_req, (uv_stream_t*)&conn, &buf, 1, write_cb); - ASSERT(r == UV_EBADF); + ASSERT_EQ(r, UV_EBADF); r = uv_tcp_connect(&connect_req, &conn, (const struct sockaddr*) &addr, connect_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_write(&write_req, (uv_stream_t*)&conn, &buf, 1, write_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(connect_cb_called == 1); - ASSERT(write_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(1, write_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; +#endif } diff --git a/test/test-tcp-connect-error.c b/test/test-tcp-connect-error.c index dda30a58064..ee0f9ee5ce6 100644 --- a/test/test-tcp-connect-error.c +++ b/test/test-tcp-connect-error.c @@ -54,20 +54,20 @@ TEST_IMPL(tcp_connect_error_fault) { garbage_addr = (const struct sockaddr_in*) &garbage; r = uv_tcp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_connect(&req, &server, (const struct sockaddr*) garbage_addr, connect_cb); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(connect_cb_called == 0); - ASSERT(close_cb_called == 1); + ASSERT_OK(connect_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-tcp-connect-timeout.c b/test/test-tcp-connect-timeout.c index 0f968157127..4eb834e1afa 100644 --- a/test/test-tcp-connect-timeout.c +++ b/test/test-tcp-connect-timeout.c @@ -38,14 +38,14 @@ static void close_cb(uv_handle_t* handle); static void connect_cb(uv_connect_t* req, int status) { - ASSERT(req == &connect_req); - ASSERT(status == UV_ECANCELED); + ASSERT_PTR_EQ(req, &connect_req); + ASSERT_EQ(status, UV_ECANCELED); connect_cb_called++; } static void timer_cb(uv_timer_t* handle) { - ASSERT(handle == &timer); + ASSERT_PTR_EQ(handle, &timer); uv_close((uv_handle_t*)&conn, close_cb); uv_close((uv_handle_t*)&timer, close_cb); } @@ -64,16 +64,16 @@ TEST_IMPL(tcp_connect_timeout) { struct sockaddr_in addr; int r; - ASSERT(0 == uv_ip4_addr("8.8.8.8", 9999, &addr)); + ASSERT_OK(uv_ip4_addr("8.8.8.8", 9999, &addr)); r = uv_timer_init(uv_default_loop(), &timer); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&timer, timer_cb, 50, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_init(uv_default_loop(), &conn); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_connect(&connect_req, &conn, @@ -81,12 +81,12 @@ TEST_IMPL(tcp_connect_timeout) { connect_cb); if (r == UV_ENETUNREACH) RETURN_SKIP("Network unreachable."); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -105,7 +105,7 @@ static int is_supported_system(void) { int min_semver[3] = {10, 0, 16299}; int cnt; uv_utsname_t uname; - ASSERT_EQ(uv_os_uname(&uname), 0); + ASSERT_OK(uv_os_uname(&uname)); if (strcmp(uname.sysname, "Windows_NT") == 0) { cnt = sscanf(uname.release, "%d.%d.%d", &semver[0], &semver[1], &semver[2]); if (cnt != 3) { @@ -130,17 +130,17 @@ TEST_IMPL(tcp_local_connect_timeout) { if (!is_supported_system()) { RETURN_SKIP("Unsupported system"); } - ASSERT_EQ(0, uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_timer_init(uv_default_loop(), &timer); - ASSERT_EQ(r, 0); + ASSERT_OK(r); /* Give it 1s to timeout. */ r = uv_timer_start(&timer, timer_cb, 1000, 0); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_tcp_init(uv_default_loop(), &conn); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_tcp_connect(&connect_req, &conn, @@ -148,12 +148,12 @@ TEST_IMPL(tcp_local_connect_timeout) { connect_local_cb); if (r == UV_ENETUNREACH) RETURN_SKIP("Network unreachable."); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -168,17 +168,17 @@ TEST_IMPL(tcp6_local_connect_timeout) { RETURN_SKIP("IPv6 not supported"); } - ASSERT_EQ(0, uv_ip6_addr("::1", 9999, &addr)); + ASSERT_OK(uv_ip6_addr("::1", 9999, &addr)); r = uv_timer_init(uv_default_loop(), &timer); - ASSERT_EQ(r, 0); + ASSERT_OK(r); /* Give it 1s to timeout. */ r = uv_timer_start(&timer, timer_cb, 1000, 0); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_tcp_init(uv_default_loop(), &conn); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_tcp_connect(&connect_req, &conn, @@ -186,11 +186,11 @@ TEST_IMPL(tcp6_local_connect_timeout) { connect_local_cb); if (r == UV_ENETUNREACH) RETURN_SKIP("Network unreachable."); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT_EQ(r, 0); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-tcp-connect6-error.c b/test/test-tcp-connect6-error.c index 2f6e9cbce14..dc2fce82f89 100644 --- a/test/test-tcp-connect6-error.c +++ b/test/test-tcp-connect6-error.c @@ -23,6 +23,7 @@ #include "task.h" #include #include +#include static int connect_cb_called = 0; @@ -49,23 +50,80 @@ TEST_IMPL(tcp_connect6_error_fault) { int r; uv_connect_t req; + if (!can_ipv6()) + RETURN_SKIP("IPv6 not supported"); + garbage_addr = (const struct sockaddr_in6*) &garbage; r = uv_tcp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_connect(&req, &server, (const struct sockaddr*) garbage_addr, connect_cb); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_close((uv_handle_t*)&server, close_cb); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(connect_cb_called == 0); - ASSERT(close_cb_called == 1); + ASSERT_OK(connect_cb_called); + ASSERT_EQ(1, close_cb_called); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} + + +TEST_IMPL(tcp_connect6_link_local) { + uv_interface_address_t* ifs; + uv_interface_address_t* p; + struct sockaddr_in6 addr; + uv_connect_t req; + uv_tcp_t server; + int ok; + int n; + + if (!can_ipv6()) + RETURN_SKIP("IPv6 not supported"); + +#if defined(__QEMU__) + /* qemu's sockaddr_in6 translation is broken pre-qemu 8.0.0 + * when host endianness != guest endiannes. + * Fixed in https://github.com/qemu/qemu/commit/44cf6731d6b. + */ + RETURN_SKIP("Test does not currently work in QEMU"); +#endif /* defined(__QEMU__) */ + + /* Check there's an interface that routes link-local (fe80::/10) traffic. */ + ASSERT_OK(uv_interface_addresses(&ifs, &n)); + for (p = ifs; p < &ifs[n]; p++) + if (p->address.address6.sin6_family == AF_INET6) + if (!memcmp(&p->address.address6.sin6_addr, "\xfe\x80", 2)) + break; + ok = (p < &ifs[n]); + uv_free_interface_addresses(ifs, n); + + if (!ok) + RETURN_SKIP("IPv6 link-local traffic not supported"); + + ASSERT_OK(uv_ip6_addr("fe80::0bad:babe", 1337, &addr)); + ASSERT_OK(uv_tcp_init(uv_default_loop(), &server)); + + /* We're making two shaky assumptions here: + * 1. There is a network interface that routes IPv6 link-local traffic, and + * 2. There is no firewall rule that blackholes or otherwise hard-kills the + * connection attempt to the address above, i.e., we don't expect the + * connect() system call to fail synchronously. + */ + ASSERT_OK(uv_tcp_connect(&req, + &server, + (struct sockaddr*) &addr, + connect_cb)); + + uv_close((uv_handle_t*) &server, NULL); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-tcp-create-socket-early.c b/test/test-tcp-create-socket-early.c index f2bc60d7c7c..e8c1aaab276 100644 --- a/test/test-tcp-create-socket-early.c +++ b/test/test-tcp-create-socket-early.c @@ -32,7 +32,7 @@ static void on_connect(uv_connect_t* req, int status) { - ASSERT(status == 0); + ASSERT_OK(status); uv_close((uv_handle_t*) req->handle, NULL); } @@ -41,16 +41,16 @@ static void on_connection(uv_stream_t* server, int status) { uv_tcp_t* handle; int r; - ASSERT(status == 0); + ASSERT_OK(status); handle = malloc(sizeof(*handle)); ASSERT_NOT_NULL(handle); r = uv_tcp_init_ex(server->loop, handle, AF_INET); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_accept(server, (uv_stream_t*)handle); - ASSERT(r == UV_EBUSY); + ASSERT_EQ(r, UV_EBUSY); uv_close((uv_handle_t*) server, NULL); uv_close((uv_handle_t*) handle, (uv_close_cb)free); @@ -61,16 +61,16 @@ static void tcp_listener(uv_loop_t* loop, uv_tcp_t* server) { struct sockaddr_in addr; int r; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_tcp_init(loop, server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*) server, 128, on_connection); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -78,16 +78,16 @@ static void tcp_connector(uv_loop_t* loop, uv_tcp_t* client, uv_connect_t* req) struct sockaddr_in server_addr; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); r = uv_tcp_init(loop, client); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_connect(req, client, (const struct sockaddr*) &server_addr, on_connect); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -98,37 +98,39 @@ TEST_IMPL(tcp_create_early) { uv_os_fd_t fd; int r, namelen; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_init_ex(uv_default_loop(), &client, AF_INET); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fileno((const uv_handle_t*) &client, &fd); - ASSERT(r == 0); - ASSERT(fd != INVALID_FD); + ASSERT_OK(r); /* Windows returns WSAEINVAL if the socket is not bound */ #ifndef _WIN32 + ASSERT_NE(fd, INVALID_FD); namelen = sizeof sockname; r = uv_tcp_getsockname(&client, (struct sockaddr*) &sockname, &namelen); - ASSERT(r == 0); - ASSERT(sockname.sin_family == AF_INET); + ASSERT_OK(r); + ASSERT_EQ(sockname.sin_family, AF_INET); +#else + ASSERT_PTR_NE(fd, INVALID_FD); #endif r = uv_tcp_bind(&client, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); namelen = sizeof sockname; r = uv_tcp_getsockname(&client, (struct sockaddr*) &sockname, &namelen); - ASSERT(r == 0); - ASSERT(memcmp(&addr.sin_addr, - &sockname.sin_addr, - sizeof(addr.sin_addr)) == 0); + ASSERT_OK(r); + ASSERT_OK(memcmp(&addr.sin_addr, + &sockname.sin_addr, + sizeof(addr.sin_addr))); uv_close((uv_handle_t*) &client, NULL); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -142,38 +144,40 @@ TEST_IMPL(tcp_create_early_bad_bind) { if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_init_ex(uv_default_loop(), &client, AF_INET6); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fileno((const uv_handle_t*) &client, &fd); - ASSERT(r == 0); - ASSERT(fd != INVALID_FD); + ASSERT_OK(r); /* Windows returns WSAEINVAL if the socket is not bound */ #ifndef _WIN32 + ASSERT_NE(fd, INVALID_FD); { int namelen; struct sockaddr_in6 sockname; namelen = sizeof sockname; r = uv_tcp_getsockname(&client, (struct sockaddr*) &sockname, &namelen); - ASSERT(r == 0); - ASSERT(sockname.sin6_family == AF_INET6); + ASSERT_OK(r); + ASSERT_EQ(sockname.sin6_family, AF_INET6); } +#else + ASSERT_PTR_NE(fd, INVALID_FD); #endif r = uv_tcp_bind(&client, (const struct sockaddr*) &addr, 0); #if !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MSYS__) - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); #else - ASSERT(r == UV_EFAULT); + ASSERT_EQ(r, UV_EFAULT); #endif uv_close((uv_handle_t*) &client, NULL); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -183,14 +187,14 @@ TEST_IMPL(tcp_create_early_bad_domain) { int r; r = uv_tcp_init_ex(uv_default_loop(), &client, 47); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_tcp_init_ex(uv_default_loop(), &client, 1024); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -204,6 +208,6 @@ TEST_IMPL(tcp_create_early_accept) { uv_run(uv_default_loop(), UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-tcp-flags.c b/test/test-tcp-flags.c index 68afb39f456..16218a27f0a 100644 --- a/test/test-tcp-flags.c +++ b/test/test-tcp-flags.c @@ -33,20 +33,27 @@ TEST_IMPL(tcp_flags) { loop = uv_default_loop(); - r = uv_tcp_init(loop, &handle); - ASSERT(r == 0); + /* Use _ex to make sure the socket is created. */ + r = uv_tcp_init_ex(loop, &handle, AF_INET); + ASSERT_OK(r); r = uv_tcp_nodelay(&handle, 1); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_keepalive(&handle, 1, 60); - ASSERT(r == 0); + ASSERT_OK(r); + + r = uv_tcp_keepalive(&handle, 0, 0); + ASSERT_OK(r); + + r = uv_tcp_keepalive(&handle, 1, 0); + ASSERT_EQ(r, UV_EINVAL); uv_close((uv_handle_t*)&handle, NULL); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-tcp-oob.c b/test/test-tcp-oob.c index 53f8231e83e..7962fa93448 100644 --- a/test/test-tcp-oob.c +++ b/test/test-tcp-oob.c @@ -61,22 +61,22 @@ static void read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { #endif uv_os_fd_t fd; - ASSERT(nread >= 0); - ASSERT(0 == uv_fileno((uv_handle_t*)handle, &fd)); - ASSERT(0 == uv_idle_start(&idle, idle_cb)); + ASSERT_GE(nread, 0); + ASSERT_OK(uv_fileno((uv_handle_t*)handle, &fd)); + ASSERT_OK(uv_idle_start(&idle, idle_cb)); #ifdef __MVS__ /* Need to flush out the OOB data. Otherwise, this callback will get * triggered on every poll with nread = 0. */ - ASSERT(-1 != recv(fd, lbuf, sizeof(lbuf), MSG_OOB)); + ASSERT_NE(-1, recv(fd, lbuf, sizeof(lbuf), MSG_OOB)); #endif } static void connect_cb(uv_connect_t* req, int status) { - ASSERT(req->handle == (uv_stream_t*) &client_handle); - ASSERT(0 == status); + ASSERT_PTR_EQ(req->handle, (uv_stream_t*) &client_handle); + ASSERT_OK(status); } @@ -84,14 +84,14 @@ static void connection_cb(uv_stream_t* handle, int status) { int r; uv_os_fd_t fd; - ASSERT(0 == status); - ASSERT(0 == uv_accept(handle, (uv_stream_t*) &peer_handle)); - ASSERT(0 == uv_read_start((uv_stream_t*) &peer_handle, alloc_cb, read_cb)); + ASSERT_OK(status); + ASSERT_OK(uv_accept(handle, (uv_stream_t*) &peer_handle)); + ASSERT_OK(uv_read_start((uv_stream_t*) &peer_handle, alloc_cb, read_cb)); /* Send some OOB data */ - ASSERT(0 == uv_fileno((uv_handle_t*) &client_handle, &fd)); + ASSERT_OK(uv_fileno((uv_handle_t*) &client_handle, &fd)); - ASSERT(0 == uv_stream_set_blocking((uv_stream_t*) &client_handle, 1)); + ASSERT_OK(uv_stream_set_blocking((uv_stream_t*) &client_handle, 1)); /* The problem triggers only on a second message, it seem that xnu is not * triggering `kevent()` for the first one @@ -99,14 +99,14 @@ static void connection_cb(uv_stream_t* handle, int status) { do { r = send(fd, "hello", 5, MSG_OOB); } while (r < 0 && errno == EINTR); - ASSERT(5 == r); + ASSERT_EQ(5, r); do { r = send(fd, "hello", 5, MSG_OOB); } while (r < 0 && errno == EINTR); - ASSERT(5 == r); + ASSERT_EQ(5, r); - ASSERT(0 == uv_stream_set_blocking((uv_stream_t*) &client_handle, 0)); + ASSERT_OK(uv_stream_set_blocking((uv_stream_t*) &client_handle, 0)); } @@ -114,28 +114,28 @@ TEST_IMPL(tcp_oob) { struct sockaddr_in addr; uv_loop_t* loop; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); loop = uv_default_loop(); - ASSERT(0 == uv_tcp_init(loop, &server_handle)); - ASSERT(0 == uv_tcp_init(loop, &client_handle)); - ASSERT(0 == uv_tcp_init(loop, &peer_handle)); - ASSERT(0 == uv_idle_init(loop, &idle)); - ASSERT(0 == uv_tcp_bind(&server_handle, (const struct sockaddr*) &addr, 0)); - ASSERT(0 == uv_listen((uv_stream_t*) &server_handle, 1, connection_cb)); + ASSERT_OK(uv_tcp_init(loop, &server_handle)); + ASSERT_OK(uv_tcp_init(loop, &client_handle)); + ASSERT_OK(uv_tcp_init(loop, &peer_handle)); + ASSERT_OK(uv_idle_init(loop, &idle)); + ASSERT_OK(uv_tcp_bind(&server_handle, (const struct sockaddr*) &addr, 0)); + ASSERT_OK(uv_listen((uv_stream_t*) &server_handle, 1, connection_cb)); /* Ensure two separate packets */ - ASSERT(0 == uv_tcp_nodelay(&client_handle, 1)); + ASSERT_OK(uv_tcp_nodelay(&client_handle, 1)); - ASSERT(0 == uv_tcp_connect(&connect_req, - &client_handle, - (const struct sockaddr*) &addr, - connect_cb)); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_tcp_connect(&connect_req, + &client_handle, + (const struct sockaddr*) &addr, + connect_cb)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(ticks == kMaxTicks); + ASSERT_EQ(ticks, kMaxTicks); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-tcp-open.c b/test/test-tcp-open.c index 7e49139cd81..3fbcf2a5f23 100644 --- a/test/test-tcp-open.c +++ b/test/test-tcp-open.c @@ -46,7 +46,7 @@ static void startup(void) { #ifdef _WIN32 struct WSAData wsa_data; int r = WSAStartup(MAKEWORD(2, 2), &wsa_data); - ASSERT(r == 0); + ASSERT_OK(r); #endif } @@ -56,9 +56,9 @@ static uv_os_sock_t create_tcp_socket(void) { sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); #ifdef _WIN32 - ASSERT(sock != INVALID_SOCKET); + ASSERT_NE(sock, INVALID_SOCKET); #else - ASSERT(sock >= 0); + ASSERT_GE(sock, 0); #endif #ifndef _WIN32 @@ -66,7 +66,7 @@ static uv_os_sock_t create_tcp_socket(void) { /* Allow reuse of the port. */ int yes = 1; int r = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); - ASSERT(r == 0); + ASSERT_OK(r); } #endif @@ -81,7 +81,7 @@ static void close_socket(uv_os_sock_t sock) { #else r = close(sock); #endif - ASSERT(r == 0); + ASSERT_OK(r); } @@ -89,7 +89,7 @@ static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { static char slab[65536]; - ASSERT(suggested_size <= sizeof(slab)); + ASSERT_LE(suggested_size, sizeof(slab)); buf->base = slab; buf->len = sizeof(slab); } @@ -102,8 +102,8 @@ static void close_cb(uv_handle_t* handle) { static void shutdown_cb(uv_shutdown_t* req, int status) { - ASSERT(req == &shutdown_req); - ASSERT(status == 0); + ASSERT_PTR_EQ(req, &shutdown_req); + ASSERT_OK(status); /* Now we wait for the EOF */ shutdown_cb_called++; @@ -114,11 +114,11 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { ASSERT_NOT_NULL(tcp); if (nread >= 0) { - ASSERT(nread == 4); - ASSERT(memcmp("PING", buf->base, nread) == 0); + ASSERT_EQ(4, nread); + ASSERT_OK(memcmp("PING", buf->base, nread)); } else { - ASSERT(nread == UV_EOF); + ASSERT_EQ(nread, UV_EOF); uv_close((uv_handle_t*)tcp, close_cb); } } @@ -130,9 +130,9 @@ static void read1_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { if (nread >= 0) { for (i = 0; i < nread; ++i) - ASSERT(buf->base[i] == 'P'); + ASSERT_EQ(buf->base[i], 'P'); } else { - ASSERT(nread == UV_EOF); + ASSERT_EQ(nread, UV_EOF); printf("GOT EOF\n"); uv_close((uv_handle_t*)tcp, close_cb); } @@ -166,7 +166,7 @@ static void write1_cb(uv_write_t* req, int status) { buf = uv_buf_init("P", 1); r = uv_write(&write_req, req->handle, &buf, 1, write1_cb); - ASSERT(r == 0); + ASSERT_OK(r); write_cb_called++; } @@ -177,7 +177,7 @@ static void timer_cb(uv_timer_t* handle) { /* Shutdown on drain. */ r = uv_shutdown(&shutdown_req, (uv_stream_t*) &client, shutdown_cb); - ASSERT(r == 0); + ASSERT_OK(r); shutdown_requested++; } @@ -187,22 +187,22 @@ static void connect_cb(uv_connect_t* req, int status) { uv_stream_t* stream; int r; - ASSERT(req == &connect_req); - ASSERT(status == 0); + ASSERT_PTR_EQ(req, &connect_req); + ASSERT_OK(status); stream = req->handle; connect_cb_called++; r = uv_write(&write_req, stream, &buf, 1, write_cb); - ASSERT(r == 0); + ASSERT_OK(r); /* Shutdown on drain. */ r = uv_shutdown(&shutdown_req, stream, shutdown_cb); - ASSERT(r == 0); + ASSERT_OK(r); /* Start reading */ r = uv_read_start(stream, alloc_cb, read_cb); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -211,25 +211,25 @@ static void connect1_cb(uv_connect_t* req, int status) { uv_stream_t* stream; int r; - ASSERT(req == &connect_req); - ASSERT(status == 0); + ASSERT_PTR_EQ(req, &connect_req); + ASSERT_OK(status); stream = req->handle; connect_cb_called++; r = uv_timer_init(uv_default_loop(), &tm); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&tm, timer_cb, 2000, 0); - ASSERT(r == 0); + ASSERT_OK(r); buf = uv_buf_init("P", 1); r = uv_write(&write_req, stream, &buf, 1, write1_cb); - ASSERT(r == 0); + ASSERT_OK(r); /* Start reading */ r = uv_read_start(stream, alloc_cb, read1_cb); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -239,30 +239,30 @@ TEST_IMPL(tcp_open) { int r; uv_tcp_t client2; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); startup(); sock = create_tcp_socket(); r = uv_tcp_init(uv_default_loop(), &client); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_open(&client, sock); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_connect(&connect_req, &client, (const struct sockaddr*) &addr, connect_cb); - ASSERT(r == 0); + ASSERT_OK(r); #ifndef _WIN32 { r = uv_tcp_init(uv_default_loop(), &client2); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_open(&client2, sock); - ASSERT(r == UV_EEXIST); + ASSERT_EQ(r, UV_EEXIST); uv_close((uv_handle_t*) &client2, NULL); } @@ -272,12 +272,12 @@ TEST_IMPL(tcp_open) { uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(shutdown_cb_called == 1); - ASSERT(connect_cb_called == 1); - ASSERT(write_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, shutdown_cb_called); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(1, write_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -292,19 +292,19 @@ TEST_IMPL(tcp_open_twice) { sock2 = create_tcp_socket(); r = uv_tcp_init(uv_default_loop(), &client); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_open(&client, sock1); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_open(&client, sock2); - ASSERT(r == UV_EBUSY); + ASSERT_EQ(r, UV_EBUSY); close_socket(sock2); uv_close((uv_handle_t*) &client, NULL); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -317,17 +317,17 @@ TEST_IMPL(tcp_open_bound) { startup(); sock = create_tcp_socket(); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); - ASSERT(0 == uv_tcp_init(uv_default_loop(), &server)); + ASSERT_OK(uv_tcp_init(uv_default_loop(), &server)); - ASSERT(0 == bind(sock, (struct sockaddr*) &addr, sizeof(addr))); + ASSERT_OK(bind(sock, (struct sockaddr*) &addr, sizeof(addr))); - ASSERT(0 == uv_tcp_open(&server, sock)); + ASSERT_OK(uv_tcp_open(&server, sock)); - ASSERT(0 == uv_listen((uv_stream_t*) &server, 128, NULL)); + ASSERT_OK(uv_listen((uv_stream_t*) &server, 128, NULL)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -338,30 +338,36 @@ TEST_IMPL(tcp_open_connected) { uv_os_sock_t sock; uv_buf_t buf = uv_buf_init("PING", 4); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); startup(); sock = create_tcp_socket(); - ASSERT(0 == connect(sock, (struct sockaddr*) &addr, sizeof(addr))); + ASSERT_OK(connect(sock, (struct sockaddr*) &addr, sizeof(addr))); - ASSERT(0 == uv_tcp_init(uv_default_loop(), &client)); + ASSERT_OK(uv_tcp_init(uv_default_loop(), &client)); - ASSERT(0 == uv_tcp_open(&client, sock)); + ASSERT_OK(uv_tcp_open(&client, sock)); - ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &client, &buf, 1, write_cb)); + ASSERT_OK(uv_write(&write_req, + (uv_stream_t*) &client, + &buf, + 1, + write_cb)); - ASSERT(0 == uv_shutdown(&shutdown_req, (uv_stream_t*) &client, shutdown_cb)); + ASSERT_OK(uv_shutdown(&shutdown_req, + (uv_stream_t*) &client, + shutdown_cb)); - ASSERT(0 == uv_read_start((uv_stream_t*) &client, alloc_cb, read_cb)); + ASSERT_OK(uv_read_start((uv_stream_t*) &client, alloc_cb, read_cb)); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(shutdown_cb_called == 1); - ASSERT(write_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, shutdown_cb_called); + ASSERT_EQ(1, write_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -371,31 +377,31 @@ TEST_IMPL(tcp_write_ready) { uv_os_sock_t sock; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); startup(); sock = create_tcp_socket(); r = uv_tcp_init(uv_default_loop(), &client); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_open(&client, sock); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_connect(&connect_req, &client, (const struct sockaddr*) &addr, connect1_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(shutdown_cb_called == 1); - ASSERT(shutdown_requested == 1); - ASSERT(connect_cb_called == 1); - ASSERT(write_cb_called > 0); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, shutdown_cb_called); + ASSERT_EQ(1, shutdown_requested); + ASSERT_EQ(1, connect_cb_called); + ASSERT_GT(write_cb_called, 0); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-tcp-read-stop-start.c b/test/test-tcp-read-stop-start.c index 9bccbc12fc5..68d6f7c2ab5 100644 --- a/test/test-tcp-read-stop-start.c +++ b/test/test-tcp-read-stop-start.c @@ -33,14 +33,14 @@ static uv_connect_t connect_req; static void on_read2(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf); static void on_write_close_immediately(uv_write_t* req, int status) { - ASSERT(0 == status); + ASSERT_OK(status); uv_close((uv_handle_t*)req->handle, NULL); /* Close immediately */ free(req); } static void on_write(uv_write_t* req, int status) { - ASSERT(0 == status); + ASSERT_OK(status); free(req); } @@ -50,7 +50,7 @@ static void do_write(uv_stream_t* stream, uv_write_cb cb) { uv_buf_t buf; buf.base = "1234578"; buf.len = 8; - ASSERT(0 == uv_write(req, stream, &buf, 1, cb)); + ASSERT_OK(uv_write(req, stream, &buf, 1, cb)); } static void on_alloc(uv_handle_t* handle, @@ -62,22 +62,22 @@ static void on_alloc(uv_handle_t* handle, } static void on_read1(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { - ASSERT(nread >= 0); + ASSERT_GE(nread, 0); /* Do write on a half open connection to force WSAECONNABORTED (on Windows) * in the subsequent uv_read_start() */ do_write(stream, on_write); - ASSERT(0 == uv_read_stop(stream)); + ASSERT_OK(uv_read_stop(stream)); - ASSERT(0 == uv_read_start(stream, on_alloc, on_read2)); + ASSERT_OK(uv_read_start(stream, on_alloc, on_read2)); read_cb_called++; } static void on_read2(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { - ASSERT(nread < 0); + ASSERT_LT(nread, 0); uv_close((uv_handle_t*)stream, NULL); uv_close((uv_handle_t*)&server, NULL); @@ -86,17 +86,17 @@ static void on_read2(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { } static void on_connection(uv_stream_t* server, int status) { - ASSERT(0 == status); + ASSERT_OK(status); - ASSERT(0 == uv_tcp_init(server->loop, &connection)); + ASSERT_OK(uv_tcp_init(server->loop, &connection)); - ASSERT(0 == uv_accept(server, (uv_stream_t* )&connection)); + ASSERT_OK(uv_accept(server, (uv_stream_t* )&connection)); - ASSERT(0 == uv_read_start((uv_stream_t*)&connection, on_alloc, on_read1)); + ASSERT_OK(uv_read_start((uv_stream_t*)&connection, on_alloc, on_read1)); } static void on_connect(uv_connect_t* req, int status) { - ASSERT(0 == status); + ASSERT_OK(status); do_write((uv_stream_t*)&client, on_write_close_immediately); } @@ -107,30 +107,30 @@ TEST_IMPL(tcp_read_stop_start) { { /* Server */ struct sockaddr_in addr; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); - ASSERT(0 == uv_tcp_init(loop, &server)); + ASSERT_OK(uv_tcp_init(loop, &server)); - ASSERT(0 == uv_tcp_bind(&server, (struct sockaddr*) & addr, 0)); + ASSERT_OK(uv_tcp_bind(&server, (struct sockaddr*) & addr, 0)); - ASSERT(0 == uv_listen((uv_stream_t*)&server, 10, on_connection)); + ASSERT_OK(uv_listen((uv_stream_t*)&server, 10, on_connection)); } { /* Client */ struct sockaddr_in addr; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); - ASSERT(0 == uv_tcp_init(loop, &client)); + ASSERT_OK(uv_tcp_init(loop, &client)); - ASSERT(0 == uv_tcp_connect(&connect_req, &client, - (const struct sockaddr*) & addr, on_connect)); + ASSERT_OK(uv_tcp_connect(&connect_req, &client, + (const struct sockaddr*) & addr, on_connect)); } - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(read_cb_called >= 2); + ASSERT_GE(read_cb_called, 2); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-tcp-read-stop.c b/test/test-tcp-read-stop.c index 488e8fb49a9..114c5ec135a 100644 --- a/test/test-tcp-read-stop.c +++ b/test/test-tcp-read-stop.c @@ -40,21 +40,21 @@ static void write_cb(uv_write_t* req, int status) { static void timer_cb(uv_timer_t* handle) { uv_buf_t buf = uv_buf_init("PING", 4); - ASSERT(0 == uv_write(&write_req, - (uv_stream_t*) &tcp_handle, - &buf, - 1, - write_cb)); - ASSERT(0 == uv_read_stop((uv_stream_t*) &tcp_handle)); + ASSERT_OK(uv_write(&write_req, + (uv_stream_t*) &tcp_handle, + &buf, + 1, + write_cb)); + ASSERT_OK(uv_read_stop((uv_stream_t*) &tcp_handle)); } static void connect_cb(uv_connect_t* req, int status) { - ASSERT(0 == status); - ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 50, 0)); - ASSERT(0 == uv_read_start((uv_stream_t*) &tcp_handle, - (uv_alloc_cb) fail_cb, - (uv_read_cb) fail_cb)); + ASSERT_OK(status); + ASSERT_OK(uv_timer_start(&timer_handle, timer_cb, 50, 0)); + ASSERT_OK(uv_read_start((uv_stream_t*) &tcp_handle, + (uv_alloc_cb) fail_cb, + (uv_read_cb) fail_cb)); } @@ -62,15 +62,15 @@ TEST_IMPL(tcp_read_stop) { uv_connect_t connect_req; struct sockaddr_in addr; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); - ASSERT(0 == uv_timer_init(uv_default_loop(), &timer_handle)); - ASSERT(0 == uv_tcp_init(uv_default_loop(), &tcp_handle)); - ASSERT(0 == uv_tcp_connect(&connect_req, - &tcp_handle, - (const struct sockaddr*) &addr, - connect_cb)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - MAKE_VALGRIND_HAPPY(); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_timer_init(uv_default_loop(), &timer_handle)); + ASSERT_OK(uv_tcp_init(uv_default_loop(), &tcp_handle)); + ASSERT_OK(uv_tcp_connect(&connect_req, + &tcp_handle, + (const struct sockaddr*) &addr, + connect_cb)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-tcp-reuseport.c b/test/test-tcp-reuseport.c new file mode 100644 index 00000000000..f108b9bbe0f --- /dev/null +++ b/test/test-tcp-reuseport.c @@ -0,0 +1,248 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include +#include +#include + +#if !defined(__linux__) && !defined(__FreeBSD__) && \ + !defined(__DragonFly__) && !defined(__sun) && !defined(_AIX73) + +TEST_IMPL(tcp_reuseport) { + struct sockaddr_in addr; + uv_loop_t* loop; + uv_tcp_t handle; + int r; + + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + loop = uv_default_loop(); + ASSERT_NOT_NULL(loop); + + r = uv_tcp_init(loop, &handle); + ASSERT_OK(r); + + r = uv_tcp_bind(&handle, (const struct sockaddr*) &addr, UV_TCP_REUSEPORT); + ASSERT_EQ(r, UV_ENOTSUP); + + MAKE_VALGRIND_HAPPY(loop); + + return 0; +} + +#else + +#define NUM_LISTENING_THREADS 2 +#define MAX_TCP_CLIENTS 10 + +static uv_tcp_t tcp_connect_handles[MAX_TCP_CLIENTS]; +static uv_connect_t tcp_connect_requests[MAX_TCP_CLIENTS]; + +static uv_sem_t semaphore; + +static uv_mutex_t mutex; +static unsigned int accepted; + +static unsigned int thread_loop1_accepted; +static unsigned int thread_loop2_accepted; +static unsigned int connected; + +static uv_loop_t* main_loop; +static uv_loop_t thread_loop1; +static uv_loop_t thread_loop2; +static uv_tcp_t thread_handle1; +static uv_tcp_t thread_handle2; +static uv_timer_t thread_timer_handle1; +static uv_timer_t thread_timer_handle2; + +static void on_close(uv_handle_t* handle) { + free(handle); +} + +static void ticktack(uv_timer_t* timer) { + ASSERT(timer == &thread_timer_handle1 || timer == &thread_timer_handle2); + + int done = 0; + uv_mutex_lock(&mutex); + if (accepted == MAX_TCP_CLIENTS) { + done = 1; + } + uv_mutex_unlock(&mutex); + + if (done) { + uv_close((uv_handle_t*) timer, NULL); + if (timer->loop == &thread_loop1) + uv_close((uv_handle_t*) &thread_handle1, NULL); + if (timer->loop == &thread_loop2) + uv_close((uv_handle_t*) &thread_handle2, NULL); + } +} + +static void on_connection(uv_stream_t* server, int status) +{ + ASSERT_OK(status); + ASSERT(server == (uv_stream_t*) &thread_handle1 || \ + server == (uv_stream_t*) &thread_handle2); + + uv_tcp_t *client = malloc(sizeof(uv_tcp_t)); + ASSERT_OK(uv_tcp_init(server->loop, client)); + ASSERT_OK(uv_accept(server, (uv_stream_t*) client)); + uv_close((uv_handle_t*) client, on_close); + + if (server->loop == &thread_loop1) + thread_loop1_accepted++; + + if (server->loop == &thread_loop2) + thread_loop2_accepted++; + + uv_mutex_lock(&mutex); + accepted++; + uv_mutex_unlock(&mutex); +} + +static void on_connect(uv_connect_t* req, int status) { + ASSERT_OK(status); + ASSERT_NOT_NULL(req->handle); + ASSERT_PTR_EQ(req->handle->loop, main_loop); + + connected++; + uv_close((uv_handle_t*) req->handle, NULL); +} + +static void create_listener(uv_loop_t* loop, uv_tcp_t* handle) { + struct sockaddr_in addr; + int r; + + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_tcp_init(loop, handle); + ASSERT_OK(r); + + r = uv_tcp_bind(handle, (const struct sockaddr*) &addr, UV_TCP_REUSEPORT); + ASSERT_OK(r); + + r = uv_listen((uv_stream_t*) handle, 128, on_connection); + ASSERT_OK(r); +} + +static void run_event_loop(void* arg) { + int r; + uv_tcp_t* handle; + uv_timer_t* timer; + uv_loop_t* loop = (uv_loop_t*) arg; + ASSERT(loop == &thread_loop1 || loop == &thread_loop2); + + if (loop == &thread_loop1) { + handle = &thread_handle1; + timer = &thread_timer_handle1; + } else { + handle = &thread_handle2; + timer = &thread_timer_handle2; + } + + create_listener(loop, handle); + r = uv_timer_init(loop, timer); + ASSERT_OK(r); + r = uv_timer_start(timer, ticktack, 0, 10); + ASSERT_OK(r); + + /* Notify the main thread to start connecting. */ + uv_sem_post(&semaphore); + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT_OK(r); +} + +TEST_IMPL(tcp_reuseport) { + struct sockaddr_in addr; + int r; + int i; + + r = uv_mutex_init(&mutex); + ASSERT_OK(r); + + r = uv_sem_init(&semaphore, 0); + ASSERT_OK(r); + + main_loop = uv_default_loop(); + ASSERT_NOT_NULL(main_loop); + + /* Run event loops of listeners in separate threads. */ + uv_loop_init(&thread_loop1); + uv_loop_init(&thread_loop2); + uv_thread_t thread_loop_id1; + uv_thread_t thread_loop_id2; + uv_thread_create(&thread_loop_id1, run_event_loop, &thread_loop1); + uv_thread_create(&thread_loop_id2, run_event_loop, &thread_loop2); + + /* Wait until all threads to poll for accepting connections + * before we start to connect. Otherwise the incoming connections + * might not be distributed across all listening threads. */ + for (i = 0; i < NUM_LISTENING_THREADS; i++) + uv_sem_wait(&semaphore); + /* Now we know all threads are up and entering the uv_run(), + * but we still sleep a little bit just for dual fail-safe. */ + uv_sleep(100); + + /* Start connecting to the peers. */ + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + for (i = 0; i < MAX_TCP_CLIENTS; i++) { + r = uv_tcp_init(main_loop, &tcp_connect_handles[i]); + ASSERT_OK(r); + r = uv_tcp_connect(&tcp_connect_requests[i], + &tcp_connect_handles[i], + (const struct sockaddr*) &addr, + on_connect); + ASSERT_OK(r); + } + + r = uv_run(main_loop, UV_RUN_DEFAULT); + ASSERT_OK(r); + + /* Wait for all threads to exit. */ + uv_thread_join(&thread_loop_id1); + uv_thread_join(&thread_loop_id2); + + /* Verify if each listener per event loop accepted connections + * and the amount of accepted connections matches the one of + * connected connections. + */ + ASSERT_EQ(accepted, MAX_TCP_CLIENTS); + ASSERT_EQ(connected, MAX_TCP_CLIENTS); + ASSERT_GT(thread_loop1_accepted, 0); + ASSERT_GT(thread_loop2_accepted, 0); + ASSERT_EQ(thread_loop1_accepted + thread_loop2_accepted, connected); + + /* Clean up. */ + uv_mutex_destroy(&mutex); + + uv_sem_destroy(&semaphore); + + uv_loop_close(&thread_loop1); + uv_loop_close(&thread_loop2); + MAKE_VALGRIND_HAPPY(main_loop); + + return 0; +} + +#endif diff --git a/test/test-tcp-rst.c b/test/test-tcp-rst.c new file mode 100644 index 00000000000..7729f03e607 --- /dev/null +++ b/test/test-tcp-rst.c @@ -0,0 +1,110 @@ +/* Copyright libuv project and contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +static uv_tcp_t tcp; +static uv_connect_t connect_req; +static uv_buf_t qbuf; +static int called_alloc_cb; +static int called_connect_cb; +static int called_close_cb; + + +static void close_cb(uv_handle_t* handle) { + ASSERT_PTR_EQ(handle, (uv_handle_t*) &tcp); + called_close_cb++; +} + + +static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { + buf->base = malloc(size); + buf->len = size; + called_alloc_cb++; +} + + +static void read_cb(uv_stream_t* t, ssize_t nread, const uv_buf_t* buf) { + ASSERT_PTR_EQ((uv_tcp_t*) t, &tcp); + ASSERT_EQ(nread, UV_ECONNRESET); + + int fd; + ASSERT_OK(uv_fileno((uv_handle_t*) t, &fd)); + uv_handle_type type = uv_guess_handle(fd); + ASSERT_EQ(type, UV_TCP); + + uv_close((uv_handle_t *) t, close_cb); + free(buf->base); +} + + +static void connect_cb(uv_connect_t *req, int status) { + ASSERT_OK(status); + ASSERT_PTR_EQ(req, &connect_req); + + /* Start reading from the connection so we receive the RST in uv__read. */ + ASSERT_OK(uv_read_start((uv_stream_t*) &tcp, alloc_cb, read_cb)); + + /* Write 'QSH' to receive RST from the echo server. */ + ASSERT_EQ(qbuf.len, uv_try_write((uv_stream_t*) &tcp, &qbuf, 1)); + + called_connect_cb++; +} + + +/* + * This test has a client which connects to the echo_server and receives TCP + * RST. Test checks that uv_guess_handle still works on a reset TCP handle. + */ +TEST_IMPL(tcp_rst) { +#if defined(__OpenBSD__) + RETURN_SKIP("Test does not currently work in OpenBSD"); +#endif +#ifndef _WIN32 + struct sockaddr_in server_addr; + int r; + + qbuf.base = "QSH"; + qbuf.len = 3; + + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); + r = uv_tcp_init(uv_default_loop(), &tcp); + ASSERT_OK(r); + + r = uv_tcp_connect(&connect_req, + &tcp, + (const struct sockaddr*) &server_addr, + connect_cb); + ASSERT_OK(r); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT_EQ(1, called_alloc_cb); + ASSERT_EQ(1, called_connect_cb); + ASSERT_EQ(1, called_close_cb); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +#else + RETURN_SKIP("Unix only test"); +#endif +} diff --git a/test/test-tcp-shutdown-after-write.c b/test/test-tcp-shutdown-after-write.c index 463b4b0d79c..cd8c24dd66b 100644 --- a/test/test-tcp-shutdown-after-write.c +++ b/test/test-tcp-shutdown-after-write.c @@ -66,10 +66,10 @@ static void timer_cb(uv_timer_t* handle) { buf = uv_buf_init("TEST", 4); r = uv_write(&write_req, (uv_stream_t*)&conn, &buf, 1, write_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_shutdown(&shutdown_req, (uv_stream_t*)&conn, shutdown_cb); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -80,22 +80,22 @@ static void read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { static void connect_cb(uv_connect_t* req, int status) { int r; - ASSERT(status == 0); + ASSERT_OK(status); connect_cb_called++; r = uv_read_start((uv_stream_t*)&conn, alloc_cb, read_cb); - ASSERT(r == 0); + ASSERT_OK(r); } static void write_cb(uv_write_t* req, int status) { - ASSERT(status == 0); + ASSERT_OK(status); write_cb_called++; } static void shutdown_cb(uv_shutdown_t* req, int status) { - ASSERT(status == 0); + ASSERT_OK(status); shutdown_cb_called++; uv_close((uv_handle_t*)&conn, close_cb); } @@ -106,33 +106,33 @@ TEST_IMPL(tcp_shutdown_after_write) { uv_loop_t* loop; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); loop = uv_default_loop(); r = uv_timer_init(loop, &timer); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&timer, timer_cb, 125, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_init(loop, &conn); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_connect(&connect_req, &conn, (const struct sockaddr*) &addr, connect_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(connect_cb_called == 1); - ASSERT(write_cb_called == 1); - ASSERT(shutdown_cb_called == 1); - ASSERT(conn_close_cb_called == 1); - ASSERT(timer_close_cb_called == 1); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(1, write_cb_called); + ASSERT_EQ(1, shutdown_cb_called); + ASSERT_EQ(1, conn_close_cb_called); + ASSERT_EQ(1, timer_close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-tcp-try-write-error.c b/test/test-tcp-try-write-error.c index 2201d0ea61a..80a23ed7379 100644 --- a/test/test-tcp-try-write-error.c +++ b/test/test-tcp-try-write-error.c @@ -49,21 +49,21 @@ static void incoming_close_cb(uv_handle_t* handle) { r = uv_try_write((uv_stream_t*) &client, &buf, 1); fprintf(stderr, "uv_try_write error: %d %s\n", r, uv_strerror(r)); ASSERT(r == UV_EPIPE || r == UV_ECONNABORTED || r == UV_ECONNRESET); - ASSERT(client.write_queue_size == 0); + ASSERT_OK(client.write_queue_size); } static void connect_cb(uv_connect_t* req, int status) { - ASSERT(status == 0); + ASSERT_OK(status); connect_cb_called++; } static void connection_cb(uv_stream_t* tcp, int status) { - ASSERT(status == 0); + ASSERT_OK(status); - ASSERT(0 == uv_tcp_init(tcp->loop, &incoming)); - ASSERT(0 == uv_accept(tcp, (uv_stream_t*) &incoming)); + ASSERT_OK(uv_tcp_init(tcp->loop, &incoming)); + ASSERT_OK(uv_accept(tcp, (uv_stream_t*) &incoming)); connection_cb_called++; uv_close((uv_handle_t*) &incoming, incoming_close_cb); @@ -74,11 +74,11 @@ static void connection_cb(uv_stream_t* tcp, int status) { static void start_server(void) { struct sockaddr_in addr; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); - ASSERT(0 == uv_tcp_init(uv_default_loop(), &server)); - ASSERT(0 == uv_tcp_bind(&server, (struct sockaddr*) &addr, 0)); - ASSERT(0 == uv_listen((uv_stream_t*) &server, 128, connection_cb)); + ASSERT_OK(uv_tcp_init(uv_default_loop(), &server)); + ASSERT_OK(uv_tcp_bind(&server, (struct sockaddr*) &addr, 0)); + ASSERT_OK(uv_listen((uv_stream_t*) &server, 128, connection_cb)); } @@ -88,22 +88,22 @@ TEST_IMPL(tcp_try_write_error) { start_server(); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); - ASSERT(0 == uv_tcp_init(uv_default_loop(), &client)); - ASSERT(0 == uv_tcp_connect(&connect_req, - &client, - (struct sockaddr*) &addr, - connect_cb)); + ASSERT_OK(uv_tcp_init(uv_default_loop(), &client)); + ASSERT_OK(uv_tcp_connect(&connect_req, + &client, + (struct sockaddr*) &addr, + connect_cb)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); uv_close((uv_handle_t*) &client, close_cb); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(connect_cb_called == 1); - ASSERT(close_cb_called == 3); - ASSERT(connection_cb_called == 1); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(3, close_cb_called); + ASSERT_EQ(1, connection_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-tcp-try-write.c b/test/test-tcp-try-write.c index 97a1d6e3d57..afb20ec7df6 100644 --- a/test/test-tcp-try-write.c +++ b/test/test-tcp-try-write.c @@ -46,7 +46,7 @@ static void close_cb(uv_handle_t* handle) { static void connect_cb(uv_connect_t* req, int status) { int r; uv_buf_t buf; - ASSERT(status == 0); + ASSERT_OK(status); connect_cb_called++; do { @@ -87,24 +87,24 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { static void connection_cb(uv_stream_t* tcp, int status) { - ASSERT(status == 0); + ASSERT_OK(status); - ASSERT(0 == uv_tcp_init(tcp->loop, &incoming)); - ASSERT(0 == uv_accept(tcp, (uv_stream_t*) &incoming)); + ASSERT_OK(uv_tcp_init(tcp->loop, &incoming)); + ASSERT_OK(uv_accept(tcp, (uv_stream_t*) &incoming)); connection_cb_called++; - ASSERT(0 == uv_read_start((uv_stream_t*) &incoming, alloc_cb, read_cb)); + ASSERT_OK(uv_read_start((uv_stream_t*) &incoming, alloc_cb, read_cb)); } static void start_server(void) { struct sockaddr_in addr; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); - ASSERT(0 == uv_tcp_init(uv_default_loop(), &server)); - ASSERT(0 == uv_tcp_bind(&server, (struct sockaddr*) &addr, 0)); - ASSERT(0 == uv_listen((uv_stream_t*) &server, 128, connection_cb)); + ASSERT_OK(uv_tcp_init(uv_default_loop(), &server)); + ASSERT_OK(uv_tcp_bind(&server, (struct sockaddr*) &addr, 0)); + ASSERT_OK(uv_listen((uv_stream_t*) &server, 128, connection_cb)); } @@ -114,22 +114,22 @@ TEST_IMPL(tcp_try_write) { start_server(); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); - ASSERT(0 == uv_tcp_init(uv_default_loop(), &client)); - ASSERT(0 == uv_tcp_connect(&connect_req, - &client, - (struct sockaddr*) &addr, - connect_cb)); + ASSERT_OK(uv_tcp_init(uv_default_loop(), &client)); + ASSERT_OK(uv_tcp_connect(&connect_req, + &client, + (struct sockaddr*) &addr, + connect_cb)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(connect_cb_called == 1); - ASSERT(close_cb_called == 3); - ASSERT(connection_cb_called == 1); - ASSERT(bytes_read == bytes_written); - ASSERT(bytes_written > 0); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(3, close_cb_called); + ASSERT_EQ(1, connection_cb_called); + ASSERT_EQ(bytes_read, bytes_written); + ASSERT_GT(bytes_written, 0); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-tcp-unexpected-read.c b/test/test-tcp-unexpected-read.c index c7b981456be..aef7a2f75d2 100644 --- a/test/test-tcp-unexpected-read.c +++ b/test/test-tcp-unexpected-read.c @@ -60,14 +60,14 @@ static void read_cb(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { static void connect_cb(uv_connect_t* req, int status) { - ASSERT(req->handle == (uv_stream_t*) &client_handle); - ASSERT(0 == status); + ASSERT_PTR_EQ(req->handle, (uv_stream_t*) &client_handle); + ASSERT_OK(status); } static void write_cb(uv_write_t* req, int status) { - ASSERT(req->handle == (uv_stream_t*) &peer_handle); - ASSERT(0 == status); + ASSERT_PTR_EQ(req->handle, (uv_stream_t*) &peer_handle); + ASSERT_OK(status); } @@ -76,11 +76,11 @@ static void connection_cb(uv_stream_t* handle, int status) { buf = uv_buf_init("PING", 4); - ASSERT(0 == status); - ASSERT(0 == uv_accept(handle, (uv_stream_t*) &peer_handle)); - ASSERT(0 == uv_read_start((uv_stream_t*) &peer_handle, alloc_cb, read_cb)); - ASSERT(0 == uv_write(&write_req, (uv_stream_t*) &peer_handle, - &buf, 1, write_cb)); + ASSERT_OK(status); + ASSERT_OK(uv_accept(handle, (uv_stream_t*) &peer_handle)); + ASSERT_OK(uv_read_start((uv_stream_t*) &peer_handle, alloc_cb, read_cb)); + ASSERT_OK(uv_write(&write_req, (uv_stream_t*) &peer_handle, + &buf, 1, write_cb)); } @@ -88,30 +88,30 @@ TEST_IMPL(tcp_unexpected_read) { struct sockaddr_in addr; uv_loop_t* loop; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); loop = uv_default_loop(); - ASSERT(0 == uv_timer_init(loop, &timer_handle)); - ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 1000, 0)); - ASSERT(0 == uv_check_init(loop, &check_handle)); - ASSERT(0 == uv_check_start(&check_handle, check_cb)); - ASSERT(0 == uv_tcp_init(loop, &server_handle)); - ASSERT(0 == uv_tcp_init(loop, &client_handle)); - ASSERT(0 == uv_tcp_init(loop, &peer_handle)); - ASSERT(0 == uv_tcp_bind(&server_handle, (const struct sockaddr*) &addr, 0)); - ASSERT(0 == uv_listen((uv_stream_t*) &server_handle, 1, connection_cb)); - ASSERT(0 == uv_tcp_connect(&connect_req, - &client_handle, - (const struct sockaddr*) &addr, - connect_cb)); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_timer_init(loop, &timer_handle)); + ASSERT_OK(uv_timer_start(&timer_handle, timer_cb, 1000, 0)); + ASSERT_OK(uv_check_init(loop, &check_handle)); + ASSERT_OK(uv_check_start(&check_handle, check_cb)); + ASSERT_OK(uv_tcp_init(loop, &server_handle)); + ASSERT_OK(uv_tcp_init(loop, &client_handle)); + ASSERT_OK(uv_tcp_init(loop, &peer_handle)); + ASSERT_OK(uv_tcp_bind(&server_handle, (const struct sockaddr*) &addr, 0)); + ASSERT_OK(uv_listen((uv_stream_t*) &server_handle, 1, connection_cb)); + ASSERT_OK(uv_tcp_connect(&connect_req, + &client_handle, + (const struct sockaddr*) &addr, + connect_cb)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); /* This is somewhat inexact but the idea is that the event loop should not * start busy looping when the server sends a message and the client isn't * reading. */ - ASSERT(ticks <= 20); + ASSERT_LE(ticks, 20); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-tcp-write-after-connect.c b/test/test-tcp-write-after-connect.c index 8a698f44bd5..63845bc4525 100644 --- a/test/test-tcp-write-after-connect.c +++ b/test/test-tcp-write-after-connect.c @@ -32,13 +32,13 @@ uv_buf_t buf = { "HELLO", 4 }; static void write_cb(uv_write_t *req, int status) { - ASSERT(status == UV_ECANCELED); + ASSERT_EQ(status, UV_ECANCELED); uv_close((uv_handle_t*) req->handle, NULL); } static void connect_cb(uv_connect_t *req, int status) { - ASSERT(status == UV_ECONNREFUSED); + ASSERT_EQ(status, UV_ECONNREFUSED); } @@ -49,24 +49,24 @@ TEST_IMPL(tcp_write_after_connect) { #endif struct sockaddr_in sa; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &sa)); - ASSERT(0 == uv_loop_init(&loop)); - ASSERT(0 == uv_tcp_init(&loop, &tcp_client)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &sa)); + ASSERT_OK(uv_loop_init(&loop)); + ASSERT_OK(uv_tcp_init(&loop, &tcp_client)); - ASSERT(0 == uv_tcp_connect(&connection_request, - &tcp_client, - (const struct sockaddr *) - &sa, - connect_cb)); + ASSERT_OK(uv_tcp_connect(&connection_request, + &tcp_client, + (const struct sockaddr *) + &sa, + connect_cb)); - ASSERT(0 == uv_write(&write_request, - (uv_stream_t *)&tcp_client, - &buf, 1, - write_cb)); + ASSERT_OK(uv_write(&write_request, + (uv_stream_t *)&tcp_client, + &buf, 1, + write_cb)); uv_run(&loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(&loop); return 0; } diff --git a/test/test-tcp-write-fail.c b/test/test-tcp-write-fail.c index 58ee00faedb..530329a3ac6 100644 --- a/test/test-tcp-write-fail.c +++ b/test/test-tcp-write-fail.c @@ -41,13 +41,13 @@ static void close_socket(uv_tcp_t* sock) { int r; r = uv_fileno((uv_handle_t*)sock, &fd); - ASSERT(r == 0); + ASSERT_OK(r); #ifdef _WIN32 r = closesocket((uv_os_sock_t)fd); #else r = close(fd); #endif - ASSERT(r == 0); + ASSERT_OK(r); } @@ -60,7 +60,7 @@ static void close_cb(uv_handle_t* handle) { static void write_cb(uv_write_t* req, int status) { ASSERT_NOT_NULL(req); - ASSERT(status != 0); + ASSERT(status); fprintf(stderr, "uv_write error: %s\n", uv_strerror(status)); write_cb_called++; @@ -73,8 +73,8 @@ static void connect_cb(uv_connect_t* req, int status) { uv_stream_t* stream; int r; - ASSERT(req == &connect_req); - ASSERT(status == 0); + ASSERT_PTR_EQ(req, &connect_req); + ASSERT_OK(status); stream = req->handle; connect_cb_called++; @@ -84,7 +84,7 @@ static void connect_cb(uv_connect_t* req, int status) { buf = uv_buf_init("hello\n", 6); r = uv_write(&write_req, stream, &buf, 1, write_cb); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -93,23 +93,23 @@ TEST_IMPL(tcp_write_fail) { uv_tcp_t client; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &client); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_connect(&connect_req, &client, (const struct sockaddr*) &addr, connect_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(connect_cb_called == 1); - ASSERT(write_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(1, write_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-tcp-write-in-a-row.c b/test/test-tcp-write-in-a-row.c new file mode 100644 index 00000000000..5c17ed49613 --- /dev/null +++ b/test/test-tcp-write-in-a-row.c @@ -0,0 +1,143 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include + +#include "task.h" +#include "uv.h" + +static uv_tcp_t server; +static uv_tcp_t client; +static uv_tcp_t incoming; +static int connect_cb_called; +static int close_cb_called; +static int connection_cb_called; +static int write_cb_called; +static uv_write_t small_write; +static uv_write_t big_write; + +/* 10 MB, which is large than the send buffer size and the recv buffer */ +static char data[1024 * 1024 * 10]; + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + +static void write_cb(uv_write_t* w, int status) { + /* the small write should finish immediately after the big write */ + ASSERT_OK(uv_stream_get_write_queue_size((uv_stream_t*) &client)); + + write_cb_called++; + + if (write_cb_called == 2) { + /* we are done */ + uv_close((uv_handle_t*) &client, close_cb); + uv_close((uv_handle_t*) &incoming, close_cb); + uv_close((uv_handle_t*) &server, close_cb); + } +} + +static void connect_cb(uv_connect_t* _, int status) { + int r; + uv_buf_t buf; + size_t write_queue_size0, write_queue_size1; + + ASSERT_OK(status); + connect_cb_called++; + + /* fire a big write */ + buf = uv_buf_init(data, sizeof(data)); + r = uv_write(&small_write, (uv_stream_t*) &client, &buf, 1, write_cb); + ASSERT_OK(r); + + /* check that the write process gets stuck */ + write_queue_size0 = uv_stream_get_write_queue_size((uv_stream_t*) &client); + ASSERT_GT(write_queue_size0, 0); + + /* fire a small write, which should be queued */ + buf = uv_buf_init("A", 1); + r = uv_write(&big_write, (uv_stream_t*) &client, &buf, 1, write_cb); + ASSERT_OK(r); + + write_queue_size1 = uv_stream_get_write_queue_size((uv_stream_t*) &client); + ASSERT_EQ(write_queue_size1, write_queue_size0 + 1); +} + +static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { + static char base[1024]; + + buf->base = base; + buf->len = sizeof(base); +} + +static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) {} + +static void connection_cb(uv_stream_t* tcp, int status) { + ASSERT_OK(status); + connection_cb_called++; + + ASSERT_OK(uv_tcp_init(tcp->loop, &incoming)); + ASSERT_OK(uv_accept(tcp, (uv_stream_t*) &incoming)); + ASSERT_OK(uv_read_start((uv_stream_t*) &incoming, alloc_cb, read_cb)); +} + +static void start_server(void) { + struct sockaddr_in addr; + + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + ASSERT_OK(uv_tcp_init(uv_default_loop(), &server)); + ASSERT_OK(uv_tcp_bind(&server, (struct sockaddr*) &addr, 0)); + ASSERT_OK(uv_listen((uv_stream_t*) &server, 128, connection_cb)); +} + +TEST_IMPL(tcp_write_in_a_row) { +#if defined(_WIN32) + RETURN_SKIP("tcp_write_in_a_row does not work on Windows"); +#elif defined(__PASE__) + RETURN_SKIP("tcp_write_in_a_row does not work on IBM i PASE"); +#else + uv_connect_t connect_req; + struct sockaddr_in addr; + + start_server(); + + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + ASSERT_OK(uv_tcp_init(uv_default_loop(), &client)); + ASSERT_OK(uv_tcp_connect(&connect_req, + &client, + (struct sockaddr*) &addr, + connect_cb)); + + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(3, close_cb_called); + ASSERT_EQ(1, connection_cb_called); + ASSERT_EQ(2, write_cb_called); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +#endif +} diff --git a/test/test-tcp-write-queue-order.c b/test/test-tcp-write-queue-order.c index 1ff9c517cec..e838c88a6a2 100644 --- a/test/test-tcp-write-queue-order.c +++ b/test/test-tcp-write-queue-order.c @@ -67,7 +67,7 @@ static void connect_cb(uv_connect_t* req, int status) { int i; uv_buf_t buf; - ASSERT(status == 0); + ASSERT_OK(status); connect_cb_called++; buf = uv_buf_init(base, sizeof(base)); @@ -78,19 +78,19 @@ static void connect_cb(uv_connect_t* req, int status) { &buf, 1, write_cb); - ASSERT(r == 0); + ASSERT_OK(r); } } static void connection_cb(uv_stream_t* tcp, int status) { - ASSERT(status == 0); + ASSERT_OK(status); - ASSERT(0 == uv_tcp_init(tcp->loop, &incoming)); - ASSERT(0 == uv_accept(tcp, (uv_stream_t*) &incoming)); + ASSERT_OK(uv_tcp_init(tcp->loop, &incoming)); + ASSERT_OK(uv_accept(tcp, (uv_stream_t*) &incoming)); - ASSERT(0 == uv_timer_init(uv_default_loop(), &timer)); - ASSERT(0 == uv_timer_start(&timer, timer_cb, 1000, 0)); + ASSERT_OK(uv_timer_init(uv_default_loop(), &timer)); + ASSERT_OK(uv_timer_start(&timer, timer_cb, 1000, 0)); connection_cb_called++; } @@ -99,11 +99,11 @@ static void connection_cb(uv_stream_t* tcp, int status) { static void start_server(void) { struct sockaddr_in addr; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); - ASSERT(0 == uv_tcp_init(uv_default_loop(), &server)); - ASSERT(0 == uv_tcp_bind(&server, (struct sockaddr*) &addr, 0)); - ASSERT(0 == uv_listen((uv_stream_t*) &server, 128, connection_cb)); + ASSERT_OK(uv_tcp_init(uv_default_loop(), &server)); + ASSERT_OK(uv_tcp_bind(&server, (struct sockaddr*) &addr, 0)); + ASSERT_OK(uv_listen((uv_stream_t*) &server, 128, connection_cb)); } @@ -114,26 +114,26 @@ TEST_IMPL(tcp_write_queue_order) { start_server(); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); - ASSERT(0 == uv_tcp_init(uv_default_loop(), &client)); - ASSERT(0 == uv_tcp_connect(&connect_req, - &client, - (struct sockaddr*) &addr, - connect_cb)); - ASSERT(0 == uv_send_buffer_size((uv_handle_t*) &client, &buffer_size)); + ASSERT_OK(uv_tcp_init(uv_default_loop(), &client)); + ASSERT_OK(uv_tcp_connect(&connect_req, + &client, + (struct sockaddr*) &addr, + connect_cb)); + ASSERT_OK(uv_send_buffer_size((uv_handle_t*) &client, &buffer_size)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(connect_cb_called == 1); - ASSERT(connection_cb_called == 1); - ASSERT(write_callbacks > 0); - ASSERT(write_cancelled_callbacks > 0); - ASSERT(write_callbacks + - write_error_callbacks + - write_cancelled_callbacks == REQ_COUNT); - ASSERT(close_cb_called == 3); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(1, connection_cb_called); + ASSERT_GT(write_callbacks, 0); + ASSERT_GT(write_cancelled_callbacks, 0); + ASSERT_EQ(write_callbacks + + write_error_callbacks + + write_cancelled_callbacks, REQ_COUNT); + ASSERT_EQ(3, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-tcp-write-to-half-open-connection.c b/test/test-tcp-write-to-half-open-connection.c index ae4251317d8..2b2f5644e17 100644 --- a/test/test-tcp-write-to-half-open-connection.c +++ b/test/test-tcp-write-to-half-open-connection.c @@ -45,23 +45,23 @@ static void connection_cb(uv_stream_t* server, int status) { int r; uv_buf_t buf; - ASSERT(server == (uv_stream_t*)&tcp_server); - ASSERT(status == 0); + ASSERT_PTR_EQ(server, (uv_stream_t*)&tcp_server); + ASSERT_OK(status); r = uv_tcp_init(server->loop, &tcp_peer); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_accept(server, (uv_stream_t*)&tcp_peer); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_start((uv_stream_t*)&tcp_peer, alloc_cb, read_cb); - ASSERT(r == 0); + ASSERT_OK(r); buf.base = "hello\n"; buf.len = 6; r = uv_write(&write_req, (uv_stream_t*)&tcp_peer, &buf, 1, write_cb); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -88,8 +88,8 @@ static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { static void connect_cb(uv_connect_t* req, int status) { - ASSERT(req == &connect_req); - ASSERT(status == 0); + ASSERT_PTR_EQ(req, &connect_req); + ASSERT_OK(status); /* Close the client. */ uv_close((uv_handle_t*)&tcp_client, NULL); @@ -97,7 +97,7 @@ static void connect_cb(uv_connect_t* req, int status) { static void write_cb(uv_write_t* req, int status) { - ASSERT(status == 0); + ASSERT_OK(status); write_cb_called++; } @@ -107,35 +107,35 @@ TEST_IMPL(tcp_write_to_half_open_connection) { uv_loop_t* loop; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); loop = uv_default_loop(); ASSERT_NOT_NULL(loop); r = uv_tcp_init(loop, &tcp_server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_listen((uv_stream_t*)&tcp_server, 1, connection_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_init(loop, &tcp_client); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_connect(&connect_req, &tcp_client, (const struct sockaddr*) &addr, connect_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(write_cb_called > 0); - ASSERT(read_cb_called > 0); + ASSERT_GT(write_cb_called, 0); + ASSERT_GT(read_cb_called, 0); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-tcp-writealot.c b/test/test-tcp-writealot.c index 40dce96e8d8..fbfa498403c 100644 --- a/test/test-tcp-writealot.c +++ b/test/test-tcp-writealot.c @@ -65,19 +65,19 @@ static void close_cb(uv_handle_t* handle) { static void shutdown_cb(uv_shutdown_t* req, int status) { uv_tcp_t* tcp; - ASSERT(req == &shutdown_req); - ASSERT(status == 0); + ASSERT_PTR_EQ(req, &shutdown_req); + ASSERT_OK(status); tcp = (uv_tcp_t*)(req->handle); /* The write buffer should be empty by now. */ - ASSERT(tcp->write_queue_size == 0); + ASSERT_OK(tcp->write_queue_size); /* Now we wait for the EOF */ shutdown_cb_called++; /* We should have had all the writes called already. */ - ASSERT(write_cb_called == WRITES); + ASSERT_EQ(write_cb_called, WRITES); } @@ -88,7 +88,7 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { bytes_received_done += nread; } else { - ASSERT(nread == UV_EOF); + ASSERT_EQ(nread, UV_EOF); printf("GOT EOF\n"); uv_close((uv_handle_t*)tcp, close_cb); } @@ -115,8 +115,8 @@ static void connect_cb(uv_connect_t* req, int status) { uv_stream_t* stream; int i, j, r; - ASSERT(req == &connect_req); - ASSERT(status == 0); + ASSERT_PTR_EQ(req, &connect_req); + ASSERT_OK(status); stream = req->handle; connect_cb_called++; @@ -131,16 +131,16 @@ static void connect_cb(uv_connect_t* req, int status) { } r = uv_write(write_req, stream, send_bufs, CHUNKS_PER_WRITE, write_cb); - ASSERT(r == 0); + ASSERT_OK(r); } /* Shutdown on drain. */ r = uv_shutdown(&shutdown_req, stream, shutdown_cb); - ASSERT(r == 0); + ASSERT_OK(r); /* Start reading */ r = uv_read_start(stream, alloc_cb, read_cb); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -149,32 +149,37 @@ TEST_IMPL(tcp_writealot) { uv_tcp_t client; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); +#if defined(__MSAN__) || defined(__TSAN__) + RETURN_SKIP("Test is too slow to run under " + "MemorySanitizer or ThreadSanitizer"); +#endif + + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); send_buffer = calloc(1, TOTAL_BYTES); ASSERT_NOT_NULL(send_buffer); r = uv_tcp_init(uv_default_loop(), &client); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tcp_connect(&connect_req, &client, (const struct sockaddr*) &addr, connect_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(shutdown_cb_called == 1); - ASSERT(connect_cb_called == 1); - ASSERT(write_cb_called == WRITES); - ASSERT(close_cb_called == 1); - ASSERT(bytes_sent == TOTAL_BYTES); - ASSERT(bytes_sent_done == TOTAL_BYTES); - ASSERT(bytes_received_done == TOTAL_BYTES); + ASSERT_EQ(1, shutdown_cb_called); + ASSERT_EQ(1, connect_cb_called); + ASSERT_EQ(write_cb_called, WRITES); + ASSERT_EQ(1, close_cb_called); + ASSERT_EQ(bytes_sent, TOTAL_BYTES); + ASSERT_EQ(bytes_sent_done, TOTAL_BYTES); + ASSERT_EQ(bytes_received_done, TOTAL_BYTES); free(send_buffer); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-thread-affinity.c b/test/test-thread-affinity.c new file mode 100644 index 00000000000..d21487d9937 --- /dev/null +++ b/test/test-thread-affinity.c @@ -0,0 +1,154 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include + +#ifndef NO_CPU_AFFINITY + +static void check_affinity(void* arg) { + int r; + char* cpumask; + int cpumasksize; + uv_thread_t tid; + + cpumask = (char*)arg; + cpumasksize = uv_cpumask_size(); + ASSERT_GT(cpumasksize, 0); + tid = uv_thread_self(); + r = uv_thread_setaffinity(&tid, cpumask, NULL, cpumasksize); + ASSERT_OK(r); + r = uv_thread_setaffinity(&tid, cpumask + cpumasksize, cpumask, cpumasksize); + ASSERT_OK(r); +} + + +TEST_IMPL(thread_affinity) { + int t1first; + int t1second; + int t2first; + int t2second; + int cpumasksize; + char* cpumask; + int ncpus; + int r; + int c; + int i; + uv_thread_t threads[3]; + +#ifdef _WIN32 + /* uv_thread_self isn't defined for the main thread on Windows */ + threads[0] = GetCurrentThread(); +#else + threads[0] = uv_thread_self(); +#endif + cpumasksize = uv_cpumask_size(); + ASSERT_GT(cpumasksize, 0); + + cpumask = calloc(4 * cpumasksize, 1); + ASSERT(cpumask); + + r = uv_thread_getaffinity(&threads[0], cpumask, cpumasksize); + ASSERT_OK(r); + ASSERT(cpumask[0] && "test must be run with cpu 0 affinity"); + ncpus = 0; + while (cpumask[++ncpus]) { } + memset(cpumask, 0, 4 * cpumasksize); + + t1first = cpumasksize * 0; + t1second = cpumasksize * 1; + t2first = cpumasksize * 2; + t2second = cpumasksize * 3; + + cpumask[t1second + 0] = 1; + cpumask[t2first + 0] = 1; + cpumask[t1first + (ncpus >= 2)] = 1; + cpumask[t2second + (ncpus >= 2)] = 1; +#ifdef __linux__ + cpumask[t1second + 2] = 1; + cpumask[t2first + 2] = 1; + cpumask[t1first + 3] = 1; + cpumask[t2second + 3] = 1; +#else + if (ncpus >= 3) { + cpumask[t1second + 2] = 1; + cpumask[t2first + 2] = 1; + } + if (ncpus >= 4) { + cpumask[t1first + 3] = 1; + cpumask[t2second + 3] = 1; + } +#endif + + ASSERT_OK(uv_thread_create(threads + 1, + check_affinity, + &cpumask[t1first])); + ASSERT_OK(uv_thread_create(threads + 2, + check_affinity, + &cpumask[t2first])); + ASSERT_OK(uv_thread_join(threads + 1)); + ASSERT_OK(uv_thread_join(threads + 2)); + + ASSERT(cpumask[t1first + 0] == (ncpus == 1)); + ASSERT(cpumask[t1first + 1] == (ncpus >= 2)); + ASSERT_OK(cpumask[t1first + 2]); + ASSERT(cpumask[t1first + 3] == (ncpus >= 4)); + + ASSERT_EQ(1, cpumask[t2first + 0]); + ASSERT_OK(cpumask[t2first + 1]); + ASSERT(cpumask[t2first + 2] == (ncpus >= 3)); + ASSERT_OK(cpumask[t2first + 3]); + + c = uv_thread_getcpu(); + ASSERT_GE(c, 0); + + memset(cpumask, 0, cpumasksize); + cpumask[c] = 1; + r = uv_thread_setaffinity(&threads[0], cpumask, NULL, cpumasksize); + ASSERT_OK(r); + + memset(cpumask, 0, cpumasksize); + r = uv_thread_getaffinity(&threads[0], cpumask, cpumasksize); + ASSERT_OK(r); + for (i = 0; i < cpumasksize; i++) { + if (i == c) + ASSERT_EQ(1, cpumask[i]); + else + ASSERT_OK(cpumask[i]); + } + + free(cpumask); + + return 0; +} + +#else + +TEST_IMPL(thread_affinity) { + int cpumasksize; + cpumasksize = uv_cpumask_size(); + ASSERT_EQ(cpumasksize, UV_ENOTSUP); + return 0; +} + +#endif diff --git a/test/test-thread-equal.c b/test/test-thread-equal.c index f7bde71b3d9..3b2ba8df443 100644 --- a/test/test-thread-equal.c +++ b/test/test-thread-equal.c @@ -31,7 +31,7 @@ static void check_thread(void* arg) { #ifdef _WIN32 ASSERT_NOT_NULL(self_id); #endif - ASSERT(uv_thread_equal(&main_thread_id, &self_id) == 0); + ASSERT_OK(uv_thread_equal(&main_thread_id, &self_id)); *thread_id = uv_thread_self(); } @@ -41,11 +41,11 @@ TEST_IMPL(thread_equal) { #ifdef _WIN32 ASSERT_NOT_NULL(main_thread_id); #endif - ASSERT(0 != uv_thread_equal(&main_thread_id, &main_thread_id)); - ASSERT(0 == uv_thread_create(threads + 0, check_thread, subthreads + 0)); - ASSERT(0 == uv_thread_create(threads + 1, check_thread, subthreads + 1)); - ASSERT(0 == uv_thread_join(threads + 0)); - ASSERT(0 == uv_thread_join(threads + 1)); - ASSERT(0 == uv_thread_equal(subthreads + 0, subthreads + 1)); + ASSERT_NE(0, uv_thread_equal(&main_thread_id, &main_thread_id)); + ASSERT_OK(uv_thread_create(threads + 0, check_thread, subthreads + 0)); + ASSERT_OK(uv_thread_create(threads + 1, check_thread, subthreads + 1)); + ASSERT_OK(uv_thread_join(threads + 0)); + ASSERT_OK(uv_thread_join(threads + 1)); + ASSERT_OK(uv_thread_equal(subthreads + 0, subthreads + 1)); return 0; } diff --git a/test/test-thread-name.c b/test/test-thread-name.c new file mode 100644 index 00000000000..378d82cf81b --- /dev/null +++ b/test/test-thread-name.c @@ -0,0 +1,189 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" +#include "../src/uv-common.h" + +#include + +struct semaphores { + uv_sem_t main; + uv_sem_t worker; +}; + +static void thread_run(void* arg) { + int r; + char thread_name[16]; + struct semaphores* sem; + uv_thread_t thread; + + sem = arg; + +#ifdef _WIN32 + /* uv_thread_self isn't defined for the main thread on Windows. */ + thread = GetCurrentThread(); +#else + thread = uv_thread_self(); +#endif + + r = uv_thread_setname("worker-thread"); + ASSERT_OK(r); + + uv_sem_post(&sem->worker); + + r = uv_thread_getname(&thread, thread_name, sizeof(thread_name)); + ASSERT_OK(r); + + ASSERT_STR_EQ(thread_name, "worker-thread"); + + uv_sem_wait(&sem->main); +} + +TEST_IMPL(thread_name) { + int r; + uv_thread_t threads[2]; + char tn[UV_PTHREAD_MAX_NAMELEN_NP]; + char thread_name[UV_PTHREAD_MAX_NAMELEN_NP]; + char long_thread_name[UV_PTHREAD_MAX_NAMELEN_NP + 1]; + struct semaphores sem; + +#if defined(__ANDROID_API__) && __ANDROID_API__ < 26 || \ + defined(_AIX) || \ + defined(__MVS__) || \ + defined(__PASE__) + RETURN_SKIP("API not available on this platform"); +#endif + + ASSERT_OK(uv_sem_init(&sem.main, 0)); + ASSERT_OK(uv_sem_init(&sem.worker, 0)); + + memset(thread_name, 'a', sizeof(thread_name) - 1); + thread_name[sizeof(thread_name) - 1] = '\0'; + + memset(long_thread_name, 'a', sizeof(long_thread_name) - 1); + long_thread_name[sizeof(long_thread_name) - 1] = '\0'; + +#ifdef _WIN32 + /* uv_thread_self isn't defined for the main thread on Windows. */ + threads[0] = GetCurrentThread(); +#else + threads[0] = uv_thread_self(); +#endif + + r = uv_thread_getname(&threads[0], tn, sizeof(tn)); + ASSERT_OK(r); + + r = uv_thread_setname(long_thread_name); + ASSERT_OK(r); + + r = uv_thread_getname(&threads[0], tn, sizeof(tn)); + ASSERT_OK(r); + ASSERT_STR_EQ(tn, thread_name); + + r = uv_thread_setname(thread_name); + ASSERT_OK(r); + + r = uv_thread_getname(&threads[0], tn, sizeof(tn)); + ASSERT_OK(r); + ASSERT_STR_EQ(tn, thread_name); + + r = uv_thread_getname(&threads[0], tn, 3); + ASSERT_OK(r); + ASSERT_EQ(strlen(tn), 2); + ASSERT_OK(memcmp(thread_name, tn, 2)); + + /* Illumos doesn't support non-ASCII thread names. */ +#ifndef __illumos__ + r = uv_thread_setname("~½¬{½"); + ASSERT_OK(r); + + r = uv_thread_getname(&threads[0], tn, sizeof(tn)); + ASSERT_OK(r); + ASSERT_STR_EQ(tn, "~½¬{½"); +#endif + + ASSERT_OK(uv_thread_create(threads + 1, thread_run, &sem)); + + uv_sem_wait(&sem.worker); + + r = uv_thread_getname(threads + 1, tn, sizeof(tn)); + ASSERT_OK(r); + + ASSERT_STR_EQ(tn, "worker-thread"); + + uv_sem_post(&sem.main); + + ASSERT_OK(uv_thread_join(threads + 1)); + + uv_sem_destroy(&sem.main); + uv_sem_destroy(&sem.worker); + + return 0; +} + +#define MAX_THREADS 4 + +static void* executedThreads[MAX_THREADS] = { NULL }; +static int size; +static uv_loop_t* loop; + +static unsigned short int key_exists(void* key) { + size_t i; + for (i = 0; i < MAX_THREADS; i++) { + if (executedThreads[i] == key) { + return 1; + } + } + return 0; +} + +static void work_cb(uv_work_t* req) { + uv_thread_t thread = uv_thread_self(); + req->data = &thread; + char tn[UV_PTHREAD_MAX_NAMELEN_NP]; + ASSERT_OK(uv_thread_getname(&thread, tn, sizeof(tn))); + ASSERT_STR_EQ(tn, "libuv-worker"); +} + +static void after_work_cb(uv_work_t* req, int status) { + ASSERT_OK(status); + if (!key_exists(req->data)) { + executedThreads[size++] = req->data; + } + + if (size == MAX_THREADS) { + return; + } + + uv_queue_work(loop, req, work_cb, after_work_cb); +} + +TEST_IMPL(thread_name_threadpool) { + uv_work_t req; + loop = uv_default_loop(); + // Just to make sure all workers will be executed + // with the correct thread name + ASSERT_OK(uv_queue_work(loop, &req, work_cb, after_work_cb)); + uv_run(loop, UV_RUN_DEFAULT); + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} diff --git a/test/test-thread-priority.c b/test/test-thread-priority.c new file mode 100644 index 00000000000..0aaf297722b --- /dev/null +++ b/test/test-thread-priority.c @@ -0,0 +1,105 @@ +/* Copyright libuv contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include +#include +#include /* memset */ + +#ifdef __POSIX__ +#include +#include +#endif + +#ifdef _WIN32 +#include +#else +#include +#endif + +uv_sem_t sem; + +static void simple_task(void *args) { + uv_sem_wait(&sem); + printf("in simple_task\n"); +} + +TEST_IMPL(thread_priority) { + int priority; +#ifndef _WIN32 + int min; + int max; + int policy; + struct sched_param param; +#endif + uv_thread_t task_id; + + /* Verify that passing a NULL pointer returns UV_EINVAL. */ + ASSERT_EQ(UV_EINVAL, uv_thread_getpriority(0, NULL)); + ASSERT_OK(uv_sem_init(&sem, 1)); + uv_sem_wait(&sem); + ASSERT_OK(uv_thread_create(&task_id, simple_task, NULL)); + ASSERT_OK(uv_thread_getpriority(task_id, &priority)); + +#ifdef _WIN32 + ASSERT_EQ(priority, THREAD_PRIORITY_NORMAL); +#else + ASSERT_OK(pthread_getschedparam(task_id, &policy, ¶m)); +#ifdef __PASE__ + min = 1; + max = 127; +#else + min = sched_get_priority_min(policy); + max = sched_get_priority_max(policy); +#endif + ASSERT(priority >= min && priority <= max); +#endif + + ASSERT_OK(uv_thread_setpriority(task_id, UV_THREAD_PRIORITY_LOWEST)); + ASSERT_OK(uv_thread_getpriority(task_id, &priority)); + +#ifdef _WIN32 + ASSERT_EQ(priority, THREAD_PRIORITY_LOWEST); +#else + ASSERT_EQ(priority, min); +#endif + +/** + * test set nice value for the calling thread with default schedule policy +*/ +#ifdef __linux__ + ASSERT_OK(uv_thread_getpriority(pthread_self(), &priority)); + ASSERT_EQ(priority, 0); + ASSERT_OK(uv_thread_setpriority(pthread_self(), UV_THREAD_PRIORITY_LOWEST)); + ASSERT_OK(uv_thread_getpriority(pthread_self(), &priority)); + ASSERT_EQ(priority, (0 - UV_THREAD_PRIORITY_LOWEST * 2)); +#endif + + uv_sem_post(&sem); + + ASSERT_OK(uv_thread_join(&task_id)); + + uv_sem_destroy(&sem); + + return 0; +} \ No newline at end of file diff --git a/test/test-thread.c b/test/test-thread.c index 8de5a6f03a3..819bbd5c923 100644 --- a/test/test-thread.c +++ b/test/test-thread.c @@ -71,20 +71,16 @@ static void getaddrinfo_do(struct getaddrinfo_req* req) { "localhost", NULL, NULL); - ASSERT(r == 0); + ASSERT_OK(r); } static void getaddrinfo_cb(uv_getaddrinfo_t* handle, int status, struct addrinfo* res) { -/* TODO(gengjiawen): Fix test on QEMU. */ -#if defined(__QEMU__) - RETURN_SKIP("Test does not currently work in QEMU"); -#endif struct getaddrinfo_req* req; - ASSERT(status == 0); + ASSERT_OK(status); req = container_of(handle, struct getaddrinfo_req, handle); uv_freeaddrinfo(res); @@ -98,7 +94,7 @@ static void fs_do(struct fs_req* req) { int r; r = uv_fs_stat(req->loop, &req->handle, ".", fs_cb); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -119,7 +115,7 @@ static void do_work(void* arg) { size_t i; struct test_thread* thread = arg; - ASSERT(0 == uv_loop_init(&loop)); + ASSERT_OK(uv_loop_init(&loop)); for (i = 0; i < ARRAY_SIZE(getaddrinfo_reqs); i++) { struct getaddrinfo_req* req = getaddrinfo_reqs + i; @@ -135,14 +131,14 @@ static void do_work(void* arg) { fs_do(req); } - ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT)); - ASSERT(0 == uv_loop_close(&loop)); + ASSERT_OK(uv_run(&loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_loop_close(&loop)); thread->thread_called = 1; } static void thread_entry(void* arg) { - ASSERT(arg == (void *) 42); + ASSERT_PTR_EQ(arg, (void *) 42); thread_called++; } @@ -152,12 +148,12 @@ TEST_IMPL(thread_create) { int r; r = uv_thread_create(&tid, thread_entry, (void *) 42); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_thread_join(&tid); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(thread_called == 1); + ASSERT_EQ(1, thread_called); return 0; } @@ -180,13 +176,13 @@ TEST_IMPL(threadpool_multiple_event_loops) { for (i = 0; i < ARRAY_SIZE(threads); i++) { r = uv_thread_create(&threads[i].thread_id, do_work, &threads[i]); - ASSERT(r == 0); + ASSERT_OK(r); } for (i = 0; i < ARRAY_SIZE(threads); i++) { r = uv_thread_join(&threads[i].thread_id); - ASSERT(r == 0); - ASSERT(threads[i].thread_called == 1); + ASSERT_OK(r); + ASSERT_EQ(1, threads[i].thread_called); } return 0; @@ -196,7 +192,7 @@ TEST_IMPL(threadpool_multiple_event_loops) { static void tls_thread(void* arg) { ASSERT_NULL(uv_key_get(&tls_key)); uv_key_set(&tls_key, arg); - ASSERT(arg == uv_key_get(&tls_key)); + ASSERT_PTR_EQ(arg, uv_key_get(&tls_key)); uv_key_set(&tls_key, NULL); ASSERT_NULL(uv_key_get(&tls_key)); } @@ -205,14 +201,14 @@ static void tls_thread(void* arg) { TEST_IMPL(thread_local_storage) { char name[] = "main"; uv_thread_t threads[2]; - ASSERT(0 == uv_key_create(&tls_key)); + ASSERT_OK(uv_key_create(&tls_key)); ASSERT_NULL(uv_key_get(&tls_key)); uv_key_set(&tls_key, name); - ASSERT(name == uv_key_get(&tls_key)); - ASSERT(0 == uv_thread_create(threads + 0, tls_thread, threads + 0)); - ASSERT(0 == uv_thread_create(threads + 1, tls_thread, threads + 1)); - ASSERT(0 == uv_thread_join(threads + 0)); - ASSERT(0 == uv_thread_join(threads + 1)); + ASSERT_PTR_EQ(name, uv_key_get(&tls_key)); + ASSERT_OK(uv_thread_create(threads + 0, tls_thread, threads + 0)); + ASSERT_OK(uv_thread_create(threads + 1, tls_thread, threads + 1)); + ASSERT_OK(uv_thread_join(threads + 0)); + ASSERT_OK(uv_thread_join(threads + 1)); uv_key_delete(&tls_key); return 0; } @@ -226,30 +222,30 @@ static void thread_check_stack(void* arg) { * on MacOS. */ if (expected == 0) expected = 512 * 1024; - ASSERT(pthread_get_stacksize_np(pthread_self()) >= expected); + ASSERT_GE(pthread_get_stacksize_np(pthread_self()), expected); #elif defined(__linux__) && defined(__GLIBC__) size_t expected; struct rlimit lim; size_t stack_size; pthread_attr_t attr; - ASSERT(0 == getrlimit(RLIMIT_STACK, &lim)); + ASSERT_OK(getrlimit(RLIMIT_STACK, &lim)); if (lim.rlim_cur == RLIM_INFINITY) lim.rlim_cur = 2 << 20; /* glibc default. */ - ASSERT(0 == pthread_getattr_np(pthread_self(), &attr)); - ASSERT(0 == pthread_attr_getstacksize(&attr, &stack_size)); + ASSERT_OK(pthread_getattr_np(pthread_self(), &attr)); + ASSERT_OK(pthread_attr_getstacksize(&attr, &stack_size)); expected = arg == NULL ? 0 : ((uv_thread_options_t*)arg)->stack_size; if (expected == 0) expected = (size_t)lim.rlim_cur; - ASSERT(stack_size >= expected); - ASSERT(0 == pthread_attr_destroy(&attr)); + ASSERT_GE(stack_size, expected); + ASSERT_OK(pthread_attr_destroy(&attr)); #endif } TEST_IMPL(thread_stack_size) { uv_thread_t thread; - ASSERT(0 == uv_thread_create(&thread, thread_check_stack, NULL)); - ASSERT(0 == uv_thread_join(&thread)); + ASSERT_OK(uv_thread_create(&thread, thread_check_stack, NULL)); + ASSERT_OK(uv_thread_join(&thread)); return 0; } @@ -259,37 +255,52 @@ TEST_IMPL(thread_stack_size_explicit) { options.flags = UV_THREAD_HAS_STACK_SIZE; options.stack_size = 1024 * 1024; - ASSERT(0 == uv_thread_create_ex(&thread, &options, - thread_check_stack, &options)); - ASSERT(0 == uv_thread_join(&thread)); + ASSERT_OK(uv_thread_create_ex(&thread, &options, + thread_check_stack, &options)); + ASSERT_OK(uv_thread_join(&thread)); options.stack_size = 8 * 1024 * 1024; /* larger than most default os sizes */ - ASSERT(0 == uv_thread_create_ex(&thread, &options, - thread_check_stack, &options)); - ASSERT(0 == uv_thread_join(&thread)); + ASSERT_OK(uv_thread_create_ex(&thread, &options, + thread_check_stack, &options)); + ASSERT_OK(uv_thread_join(&thread)); options.stack_size = 0; - ASSERT(0 == uv_thread_create_ex(&thread, &options, - thread_check_stack, &options)); - ASSERT(0 == uv_thread_join(&thread)); + ASSERT_OK(uv_thread_create_ex(&thread, &options, + thread_check_stack, &options)); + ASSERT_OK(uv_thread_join(&thread)); + + options.stack_size = 42; + ASSERT_OK(uv_thread_create_ex(&thread, &options, + thread_check_stack, &options)); + ASSERT_OK(uv_thread_join(&thread)); #ifdef PTHREAD_STACK_MIN options.stack_size = PTHREAD_STACK_MIN - 42; /* unaligned size */ - ASSERT(0 == uv_thread_create_ex(&thread, &options, - thread_check_stack, &options)); - ASSERT(0 == uv_thread_join(&thread)); + ASSERT_OK(uv_thread_create_ex(&thread, &options, + thread_check_stack, &options)); + ASSERT_OK(uv_thread_join(&thread)); options.stack_size = PTHREAD_STACK_MIN / 2 - 42; /* unaligned size */ - ASSERT(0 == uv_thread_create_ex(&thread, &options, - thread_check_stack, &options)); - ASSERT(0 == uv_thread_join(&thread)); + ASSERT_OK(uv_thread_create_ex(&thread, &options, + thread_check_stack, &options)); + ASSERT_OK(uv_thread_join(&thread)); #endif /* unaligned size, should be larger than PTHREAD_STACK_MIN */ options.stack_size = 1234567; - ASSERT(0 == uv_thread_create_ex(&thread, &options, - thread_check_stack, &options)); - ASSERT(0 == uv_thread_join(&thread)); + ASSERT_OK(uv_thread_create_ex(&thread, &options, + thread_check_stack, &options)); + ASSERT_OK(uv_thread_join(&thread)); + + return 0; +} + +static void thread_detach_cb(void* arg) {} + +TEST_IMPL(thread_detach) { + uv_thread_t thread; + ASSERT_OK(uv_thread_create(&thread, thread_detach_cb, NULL)); + ASSERT_OK(uv_thread_detach(&thread)); return 0; } diff --git a/test/test-threadpool-cancel.c b/test/test-threadpool-cancel.c index 1e867c51cf9..544fbbc348a 100644 --- a/test/test-threadpool-cancel.c +++ b/test/test-threadpool-cancel.c @@ -22,6 +22,10 @@ #include "uv.h" #include "task.h" +#ifdef _WIN32 +# define putenv _putenv +#endif + #define INIT_CANCEL_INFO(ci, what) \ do { \ (ci)->reqs = (what); \ @@ -73,8 +77,8 @@ static void saturate_threadpool(void) { loop = uv_default_loop(); for (i = 0; i < ARRAY_SIZE(pause_reqs); i += 1) { - ASSERT(0 == uv_sem_init(pause_sems + i, 0)); - ASSERT(0 == uv_queue_work(loop, pause_reqs + i, work_cb, done_cb)); + ASSERT_OK(uv_sem_init(pause_sems + i, 0)); + ASSERT_OK(uv_queue_work(loop, pause_reqs + i, work_cb, done_cb)); } } @@ -87,8 +91,40 @@ static void unblock_threadpool(void) { } +static int known_broken(uv_req_t* req) { + if (req->type != UV_FS) + return 0; + +#ifdef __linux__ + /* TODO(bnoordhuis) make cancellation work with io_uring */ + switch (((uv_fs_t*) req)->fs_type) { + case UV_FS_CLOSE: + case UV_FS_FDATASYNC: + case UV_FS_FSTAT: + case UV_FS_FSYNC: + case UV_FS_LINK: + case UV_FS_LSTAT: + case UV_FS_MKDIR: + case UV_FS_OPEN: + case UV_FS_READ: + case UV_FS_RENAME: + case UV_FS_STAT: + case UV_FS_SYMLINK: + case UV_FS_WRITE: + case UV_FS_UNLINK: + return 1; + default: /* Squelch -Wswitch warnings. */ + break; + } +#endif + + return 0; +} + + static void fs_cb(uv_fs_t* req) { - ASSERT(req->result == UV_ECANCELED); + ASSERT_NE(known_broken((uv_req_t*) req) || \ + req->result == UV_ECANCELED, 0); uv_fs_req_cleanup(req); fs_cb_called++; } @@ -97,7 +133,7 @@ static void fs_cb(uv_fs_t* req) { static void getaddrinfo_cb(uv_getaddrinfo_t* req, int status, struct addrinfo* res) { - ASSERT(status == UV_EAI_CANCELED); + ASSERT_EQ(status, UV_EAI_CANCELED); ASSERT_NULL(res); uv_freeaddrinfo(res); /* Should not crash. */ } @@ -107,7 +143,7 @@ static void getnameinfo_cb(uv_getnameinfo_t* handle, int status, const char* hostname, const char* service) { - ASSERT(status == UV_EAI_CANCELED); + ASSERT_EQ(status, UV_EAI_CANCELED); ASSERT_NULL(hostname); ASSERT_NULL(service); } @@ -119,7 +155,7 @@ static void work2_cb(uv_work_t* req) { static void done2_cb(uv_work_t* req, int status) { - ASSERT(status == UV_ECANCELED); + ASSERT_EQ(status, UV_ECANCELED); done2_cb_called++; } @@ -133,7 +169,7 @@ static void timer_cb(uv_timer_t* handle) { for (i = 0; i < ci->nreqs; i++) { req = (uv_req_t*) ((char*) ci->reqs + i * ci->stride); - ASSERT(0 == uv_cancel(req)); + ASSERT(known_broken(req) || 0 == uv_cancel(req)); } uv_close((uv_handle_t*) &ci->timer_handle, NULL); @@ -143,7 +179,7 @@ static void timer_cb(uv_timer_t* handle) { static void nop_done_cb(uv_work_t* req, int status) { - ASSERT(status == UV_ECANCELED); + ASSERT_EQ(status, UV_ECANCELED); done_cb_called++; } @@ -153,9 +189,9 @@ static void nop_random_cb(uv_random_t* req, int status, void* buf, size_t len) { ri = container_of(req, struct random_info, random_req); - ASSERT(status == UV_ECANCELED); - ASSERT(buf == (void*) ri->buf); - ASSERT(len == sizeof(ri->buf)); + ASSERT_EQ(status, UV_ECANCELED); + ASSERT_PTR_EQ(buf, (void*) ri->buf); + ASSERT_EQ(len, sizeof(ri->buf)); done_cb_called++; } @@ -173,23 +209,23 @@ TEST_IMPL(threadpool_cancel_getaddrinfo) { saturate_threadpool(); r = uv_getaddrinfo(loop, reqs + 0, getaddrinfo_cb, "fail", NULL, NULL); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_getaddrinfo(loop, reqs + 1, getaddrinfo_cb, NULL, "fail", NULL); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_getaddrinfo(loop, reqs + 2, getaddrinfo_cb, "fail", "fail", NULL); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_getaddrinfo(loop, reqs + 3, getaddrinfo_cb, "fail", NULL, &hints); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(0 == uv_timer_init(loop, &ci.timer_handle)); - ASSERT(0 == uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(1 == timer_cb_called); + ASSERT_OK(uv_timer_init(loop, &ci.timer_handle)); + ASSERT_OK(uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(1, timer_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -202,30 +238,30 @@ TEST_IMPL(threadpool_cancel_getnameinfo) { int r; r = uv_ip4_addr("127.0.0.1", 80, &addr4); - ASSERT(r == 0); + ASSERT_OK(r); INIT_CANCEL_INFO(&ci, reqs); loop = uv_default_loop(); saturate_threadpool(); r = uv_getnameinfo(loop, reqs + 0, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_getnameinfo(loop, reqs + 1, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_getnameinfo(loop, reqs + 2, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_getnameinfo(loop, reqs + 3, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(0 == uv_timer_init(loop, &ci.timer_handle)); - ASSERT(0 == uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(1 == timer_cb_called); + ASSERT_OK(uv_timer_init(loop, &ci.timer_handle)); + ASSERT_OK(uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(1, timer_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -236,19 +272,19 @@ TEST_IMPL(threadpool_cancel_random) { saturate_threadpool(); loop = uv_default_loop(); - ASSERT(0 == uv_random(loop, - &req.random_req, - &req.buf, - sizeof(req.buf), - 0, - nop_random_cb)); - ASSERT(0 == uv_cancel((uv_req_t*) &req)); - ASSERT(0 == done_cb_called); + ASSERT_OK(uv_random(loop, + &req.random_req, + &req.buf, + sizeof(req.buf), + 0, + nop_random_cb)); + ASSERT_OK(uv_cancel((uv_req_t*) &req)); + ASSERT_OK(done_cb_called); unblock_threadpool(); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(1 == done_cb_called); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(1, done_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -264,15 +300,15 @@ TEST_IMPL(threadpool_cancel_work) { saturate_threadpool(); for (i = 0; i < ARRAY_SIZE(reqs); i++) - ASSERT(0 == uv_queue_work(loop, reqs + i, work2_cb, done2_cb)); + ASSERT_OK(uv_queue_work(loop, reqs + i, work2_cb, done2_cb)); - ASSERT(0 == uv_timer_init(loop, &ci.timer_handle)); - ASSERT(0 == uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(1 == timer_cb_called); - ASSERT(ARRAY_SIZE(reqs) == done2_cb_called); + ASSERT_OK(uv_timer_init(loop, &ci.timer_handle)); + ASSERT_OK(uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(1, timer_cb_called); + ASSERT_EQ(ARRAY_SIZE(reqs), done2_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -291,42 +327,42 @@ TEST_IMPL(threadpool_cancel_fs) { /* Needs to match ARRAY_SIZE(fs_reqs). */ n = 0; - ASSERT(0 == uv_fs_chmod(loop, reqs + n++, "/", 0, fs_cb)); - ASSERT(0 == uv_fs_chown(loop, reqs + n++, "/", 0, 0, fs_cb)); - ASSERT(0 == uv_fs_close(loop, reqs + n++, 0, fs_cb)); - ASSERT(0 == uv_fs_fchmod(loop, reqs + n++, 0, 0, fs_cb)); - ASSERT(0 == uv_fs_fchown(loop, reqs + n++, 0, 0, 0, fs_cb)); - ASSERT(0 == uv_fs_fdatasync(loop, reqs + n++, 0, fs_cb)); - ASSERT(0 == uv_fs_fstat(loop, reqs + n++, 0, fs_cb)); - ASSERT(0 == uv_fs_fsync(loop, reqs + n++, 0, fs_cb)); - ASSERT(0 == uv_fs_ftruncate(loop, reqs + n++, 0, 0, fs_cb)); - ASSERT(0 == uv_fs_futime(loop, reqs + n++, 0, 0, 0, fs_cb)); - ASSERT(0 == uv_fs_link(loop, reqs + n++, "/", "/", fs_cb)); - ASSERT(0 == uv_fs_lstat(loop, reqs + n++, "/", fs_cb)); - ASSERT(0 == uv_fs_mkdir(loop, reqs + n++, "/", 0, fs_cb)); - ASSERT(0 == uv_fs_open(loop, reqs + n++, "/", 0, 0, fs_cb)); - ASSERT(0 == uv_fs_read(loop, reqs + n++, 0, &iov, 1, 0, fs_cb)); - ASSERT(0 == uv_fs_scandir(loop, reqs + n++, "/", 0, fs_cb)); - ASSERT(0 == uv_fs_readlink(loop, reqs + n++, "/", fs_cb)); - ASSERT(0 == uv_fs_realpath(loop, reqs + n++, "/", fs_cb)); - ASSERT(0 == uv_fs_rename(loop, reqs + n++, "/", "/", fs_cb)); - ASSERT(0 == uv_fs_mkdir(loop, reqs + n++, "/", 0, fs_cb)); - ASSERT(0 == uv_fs_sendfile(loop, reqs + n++, 0, 0, 0, 0, fs_cb)); - ASSERT(0 == uv_fs_stat(loop, reqs + n++, "/", fs_cb)); - ASSERT(0 == uv_fs_symlink(loop, reqs + n++, "/", "/", 0, fs_cb)); - ASSERT(0 == uv_fs_unlink(loop, reqs + n++, "/", fs_cb)); - ASSERT(0 == uv_fs_utime(loop, reqs + n++, "/", 0, 0, fs_cb)); - ASSERT(0 == uv_fs_write(loop, reqs + n++, 0, &iov, 1, 0, fs_cb)); - ASSERT(n == ARRAY_SIZE(reqs)); - - ASSERT(0 == uv_timer_init(loop, &ci.timer_handle)); - ASSERT(0 == uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(n == fs_cb_called); - ASSERT(1 == timer_cb_called); - - - MAKE_VALGRIND_HAPPY(); + ASSERT_OK(uv_fs_chmod(loop, reqs + n++, "/", 0, fs_cb)); + ASSERT_OK(uv_fs_chown(loop, reqs + n++, "/", 0, 0, fs_cb)); + ASSERT_OK(uv_fs_close(loop, reqs + n++, 0, fs_cb)); + ASSERT_OK(uv_fs_fchmod(loop, reqs + n++, 0, 0, fs_cb)); + ASSERT_OK(uv_fs_fchown(loop, reqs + n++, 0, 0, 0, fs_cb)); + ASSERT_OK(uv_fs_fdatasync(loop, reqs + n++, 0, fs_cb)); + ASSERT_OK(uv_fs_fstat(loop, reqs + n++, 0, fs_cb)); + ASSERT_OK(uv_fs_fsync(loop, reqs + n++, 0, fs_cb)); + ASSERT_OK(uv_fs_ftruncate(loop, reqs + n++, 0, 0, fs_cb)); + ASSERT_OK(uv_fs_futime(loop, reqs + n++, 0, 0, 0, fs_cb)); + ASSERT_OK(uv_fs_link(loop, reqs + n++, "/", "/", fs_cb)); + ASSERT_OK(uv_fs_lstat(loop, reqs + n++, "/", fs_cb)); + ASSERT_OK(uv_fs_mkdir(loop, reqs + n++, "/", 0, fs_cb)); + ASSERT_OK(uv_fs_open(loop, reqs + n++, "/", 0, 0, fs_cb)); + ASSERT_OK(uv_fs_read(loop, reqs + n++, -1, &iov, 1, 0, fs_cb)); + ASSERT_OK(uv_fs_scandir(loop, reqs + n++, "/", 0, fs_cb)); + ASSERT_OK(uv_fs_readlink(loop, reqs + n++, "/", fs_cb)); + ASSERT_OK(uv_fs_realpath(loop, reqs + n++, "/", fs_cb)); + ASSERT_OK(uv_fs_rename(loop, reqs + n++, "/", "/", fs_cb)); + ASSERT_OK(uv_fs_mkdir(loop, reqs + n++, "/", 0, fs_cb)); + ASSERT_OK(uv_fs_sendfile(loop, reqs + n++, 0, 0, 0, 0, fs_cb)); + ASSERT_OK(uv_fs_stat(loop, reqs + n++, "/", fs_cb)); + ASSERT_OK(uv_fs_symlink(loop, reqs + n++, "/", "/", 0, fs_cb)); + ASSERT_OK(uv_fs_unlink(loop, reqs + n++, "/", fs_cb)); + ASSERT_OK(uv_fs_utime(loop, reqs + n++, "/", 0, 0, fs_cb)); + ASSERT_OK(uv_fs_write(loop, reqs + n++, -1, &iov, 1, 0, fs_cb)); + ASSERT_EQ(n, ARRAY_SIZE(reqs)); + + ASSERT_OK(uv_timer_init(loop, &ci.timer_handle)); + ASSERT_OK(uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(n, fs_cb_called); + ASSERT_EQ(1, timer_cb_called); + + + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -337,13 +373,46 @@ TEST_IMPL(threadpool_cancel_single) { saturate_threadpool(); loop = uv_default_loop(); - ASSERT(0 == uv_queue_work(loop, &req, (uv_work_cb) abort, nop_done_cb)); - ASSERT(0 == uv_cancel((uv_req_t*) &req)); - ASSERT(0 == done_cb_called); + ASSERT_OK(uv_queue_work(loop, &req, (uv_work_cb) abort, nop_done_cb)); + ASSERT_OK(uv_cancel((uv_req_t*) &req)); + ASSERT_OK(done_cb_called); unblock_threadpool(); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(1 == done_cb_called); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(1, done_cb_called); + + MAKE_VALGRIND_HAPPY(loop); + return 0; +} + + +static void after_busy_cb(uv_work_t* req, int status) { + ASSERT_OK(status); + done_cb_called++; +} + +static void busy_cb(uv_work_t* req) { + uv_sem_post((uv_sem_t*) req->data); + /* Assume that calling uv_cancel() takes less than 10ms. */ + uv_sleep(10); +} + +TEST_IMPL(threadpool_cancel_when_busy) { + uv_sem_t sem_lock; + uv_work_t req; + + req.data = &sem_lock; + + ASSERT_OK(uv_sem_init(&sem_lock, 0)); + ASSERT_OK(uv_queue_work(uv_default_loop(), &req, busy_cb, after_busy_cb)); + + uv_sem_wait(&sem_lock); + + ASSERT_EQ(uv_cancel((uv_req_t*) &req), UV_EBUSY); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_EQ(1, done_cb_called); + + uv_sem_destroy(&sem_lock); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-threadpool.c b/test/test-threadpool.c index e3d17d7546f..fb5e5732e6a 100644 --- a/test/test-threadpool.c +++ b/test/test-threadpool.c @@ -29,16 +29,16 @@ static char data; static void work_cb(uv_work_t* req) { - ASSERT(req == &work_req); - ASSERT(req->data == &data); + ASSERT_PTR_EQ(req, &work_req); + ASSERT_PTR_EQ(req->data, &data); work_cb_count++; } static void after_work_cb(uv_work_t* req, int status) { - ASSERT(status == 0); - ASSERT(req == &work_req); - ASSERT(req->data == &data); + ASSERT_OK(status); + ASSERT_PTR_EQ(req, &work_req); + ASSERT_PTR_EQ(req->data, &data); after_work_cb_count++; } @@ -48,13 +48,13 @@ TEST_IMPL(threadpool_queue_work_simple) { work_req.data = &data; r = uv_queue_work(uv_default_loop(), &work_req, work_cb, after_work_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(work_cb_count == 1); - ASSERT(after_work_cb_count == 1); + ASSERT_EQ(1, work_cb_count); + ASSERT_EQ(1, after_work_cb_count); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -64,13 +64,13 @@ TEST_IMPL(threadpool_queue_work_einval) { work_req.data = &data; r = uv_queue_work(uv_default_loop(), &work_req, NULL, after_work_cb); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(work_cb_count == 0); - ASSERT(after_work_cb_count == 0); + ASSERT_OK(work_cb_count); + ASSERT_OK(after_work_cb_count); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-timer-again.c b/test/test-timer-again.c index 834b59d718c..d7f0b83ab55 100644 --- a/test/test-timer-again.c +++ b/test/test-timer-again.c @@ -44,8 +44,8 @@ static void close_cb(uv_handle_t* handle) { static void repeat_1_cb(uv_timer_t* handle) { int r; - ASSERT(handle == &repeat_1); - ASSERT(uv_timer_get_repeat((uv_timer_t*)handle) == 50); + ASSERT_PTR_EQ(handle, &repeat_1); + ASSERT_EQ(50, uv_timer_get_repeat((uv_timer_t*)handle)); fprintf(stderr, "repeat_1_cb called after %ld ms\n", (long int)(uv_now(uv_default_loop()) - start_time)); @@ -54,7 +54,7 @@ static void repeat_1_cb(uv_timer_t* handle) { repeat_1_cb_called++; r = uv_timer_again(&repeat_2); - ASSERT(r == 0); + ASSERT_OK(r); if (repeat_1_cb_called == 10) { uv_close((uv_handle_t*)handle, close_cb); @@ -67,7 +67,7 @@ static void repeat_1_cb(uv_timer_t* handle) { static void repeat_2_cb(uv_timer_t* handle) { - ASSERT(handle == &repeat_2); + ASSERT_PTR_EQ(handle, &repeat_2); ASSERT(repeat_2_cb_allowed); fprintf(stderr, "repeat_2_cb called after %ld ms\n", @@ -77,7 +77,7 @@ static void repeat_2_cb(uv_timer_t* handle) { repeat_2_cb_called++; if (uv_timer_get_repeat(&repeat_2) == 0) { - ASSERT(0 == uv_is_active((uv_handle_t*) handle)); + ASSERT_OK(uv_is_active((uv_handle_t*) handle)); uv_close((uv_handle_t*)handle, close_cb); return; } @@ -85,7 +85,7 @@ static void repeat_2_cb(uv_timer_t* handle) { fprintf(stderr, "uv_timer_get_repeat %ld ms\n", (long int)uv_timer_get_repeat(&repeat_2)); fflush(stderr); - ASSERT(uv_timer_get_repeat(&repeat_2) == 100); + ASSERT_EQ(100, uv_timer_get_repeat(&repeat_2)); /* This shouldn't take effect immediately. */ uv_timer_set_repeat(&repeat_2, 0); @@ -96,46 +96,46 @@ TEST_IMPL(timer_again) { int r; start_time = uv_now(uv_default_loop()); - ASSERT(0 < start_time); + ASSERT_LT(0, start_time); /* Verify that it is not possible to uv_timer_again a never-started timer. */ r = uv_timer_init(uv_default_loop(), &dummy); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_again(&dummy); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_unref((uv_handle_t*)&dummy); /* Start timer repeat_1. */ r = uv_timer_init(uv_default_loop(), &repeat_1); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&repeat_1, repeat_1_cb, 50, 0); - ASSERT(r == 0); - ASSERT(uv_timer_get_repeat(&repeat_1) == 0); + ASSERT_OK(r); + ASSERT_OK(uv_timer_get_repeat(&repeat_1)); /* Actually make repeat_1 repeating. */ uv_timer_set_repeat(&repeat_1, 50); - ASSERT(uv_timer_get_repeat(&repeat_1) == 50); + ASSERT_EQ(50, uv_timer_get_repeat(&repeat_1)); /* * Start another repeating timer. It'll be again()ed by the repeat_1 so * it should not time out until repeat_1 stops. */ r = uv_timer_init(uv_default_loop(), &repeat_2); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&repeat_2, repeat_2_cb, 100, 100); - ASSERT(r == 0); - ASSERT(uv_timer_get_repeat(&repeat_2) == 100); + ASSERT_OK(r); + ASSERT_EQ(100, uv_timer_get_repeat(&repeat_2)); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(repeat_1_cb_called == 10); - ASSERT(repeat_2_cb_called == 2); - ASSERT(close_cb_called == 2); + ASSERT_EQ(10, repeat_1_cb_called); + ASSERT_EQ(2, repeat_2_cb_called); + ASSERT_EQ(2, close_cb_called); fprintf(stderr, "Test took %ld ms (expected ~700 ms)\n", (long int)(uv_now(uv_default_loop()) - start_time)); fflush(stderr); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-timer-from-check.c b/test/test-timer-from-check.c index a18c7e1fb99..e5f5cb2f60a 100644 --- a/test/test-timer-from-check.c +++ b/test/test-timer-from-check.c @@ -32,49 +32,49 @@ static int timer_cb_called; static void prepare_cb(uv_prepare_t* handle) { - ASSERT(0 == uv_prepare_stop(&prepare_handle)); - ASSERT(0 == prepare_cb_called); - ASSERT(1 == check_cb_called); - ASSERT(0 == timer_cb_called); + ASSERT_OK(uv_prepare_stop(&prepare_handle)); + ASSERT_OK(prepare_cb_called); + ASSERT_EQ(1, check_cb_called); + ASSERT_OK(timer_cb_called); prepare_cb_called++; } static void timer_cb(uv_timer_t* handle) { - ASSERT(0 == uv_timer_stop(&timer_handle)); - ASSERT(1 == prepare_cb_called); - ASSERT(1 == check_cb_called); - ASSERT(0 == timer_cb_called); + ASSERT_OK(uv_timer_stop(&timer_handle)); + ASSERT_EQ(1, prepare_cb_called); + ASSERT_EQ(1, check_cb_called); + ASSERT_OK(timer_cb_called); timer_cb_called++; } static void check_cb(uv_check_t* handle) { - ASSERT(0 == uv_check_stop(&check_handle)); - ASSERT(0 == uv_timer_stop(&timer_handle)); /* Runs before timer_cb. */ - ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 50, 0)); - ASSERT(0 == uv_prepare_start(&prepare_handle, prepare_cb)); - ASSERT(0 == prepare_cb_called); - ASSERT(0 == check_cb_called); - ASSERT(0 == timer_cb_called); + ASSERT_OK(uv_check_stop(&check_handle)); + ASSERT_OK(uv_timer_stop(&timer_handle)); /* Runs before timer_cb. */ + ASSERT_OK(uv_timer_start(&timer_handle, timer_cb, 50, 0)); + ASSERT_OK(uv_prepare_start(&prepare_handle, prepare_cb)); + ASSERT_OK(prepare_cb_called); + ASSERT_OK(check_cb_called); + ASSERT_OK(timer_cb_called); check_cb_called++; } TEST_IMPL(timer_from_check) { - ASSERT(0 == uv_prepare_init(uv_default_loop(), &prepare_handle)); - ASSERT(0 == uv_check_init(uv_default_loop(), &check_handle)); - ASSERT(0 == uv_check_start(&check_handle, check_cb)); - ASSERT(0 == uv_timer_init(uv_default_loop(), &timer_handle)); - ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 50, 0)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(1 == prepare_cb_called); - ASSERT(1 == check_cb_called); - ASSERT(1 == timer_cb_called); + ASSERT_OK(uv_prepare_init(uv_default_loop(), &prepare_handle)); + ASSERT_OK(uv_check_init(uv_default_loop(), &check_handle)); + ASSERT_OK(uv_check_start(&check_handle, check_cb)); + ASSERT_OK(uv_timer_init(uv_default_loop(), &timer_handle)); + ASSERT_OK(uv_timer_start(&timer_handle, timer_cb, 50, 0)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_EQ(1, prepare_cb_called); + ASSERT_EQ(1, check_cb_called); + ASSERT_EQ(1, timer_cb_called); uv_close((uv_handle_t*) &prepare_handle, NULL); uv_close((uv_handle_t*) &check_handle, NULL); uv_close((uv_handle_t*) &timer_handle, NULL); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); - MAKE_VALGRIND_HAPPY(); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_ONCE)); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-timer.c b/test/test-timer.c index d0921a96730..641d3a90775 100644 --- a/test/test-timer.c +++ b/test/test-timer.c @@ -25,9 +25,13 @@ static int once_cb_called = 0; static int once_close_cb_called = 0; +static int twice_cb_called = 0; +static int twice_close_cb_called = 0; static int repeat_cb_called = 0; static int repeat_close_cb_called = 0; static int order_cb_called = 0; +static int timer_check_double_call_called = 0; +static int zero_timeout_cb_calls = 0; static uint64_t start_time; static uv_timer_t tiny_timer; static uv_timer_t huge_timer1; @@ -38,7 +42,7 @@ static void once_close_cb(uv_handle_t* handle) { printf("ONCE_CLOSE_CB\n"); ASSERT_NOT_NULL(handle); - ASSERT(0 == uv_is_active(handle)); + ASSERT_OK(uv_is_active(handle)); once_close_cb_called++; } @@ -48,7 +52,7 @@ static void once_cb(uv_timer_t* handle) { printf("ONCE_CB %d\n", once_cb_called); ASSERT_NOT_NULL(handle); - ASSERT(0 == uv_is_active((uv_handle_t*) handle)); + ASSERT_OK(uv_is_active((uv_handle_t*) handle)); once_cb_called++; @@ -58,6 +62,27 @@ static void once_cb(uv_timer_t* handle) { uv_update_time(uv_default_loop()); } +static void twice_close_cb(uv_handle_t* handle) { + printf("TWICE_CLOSE_CB\n"); + + ASSERT_NOT_NULL(handle); + ASSERT_OK(uv_is_active(handle)); + + twice_close_cb_called++; +} + +static void twice_cb(uv_timer_t* handle) { + printf("TWICE_CB %d\n", twice_cb_called); + + ASSERT_NOT_NULL(handle); + ASSERT_OK(uv_is_active((uv_handle_t*) handle)); + + twice_cb_called++; + + uv_close((uv_handle_t*)handle, twice_close_cb); +} + + static void repeat_close_cb(uv_handle_t* handle) { printf("REPEAT_CLOSE_CB\n"); @@ -72,7 +97,7 @@ static void repeat_cb(uv_timer_t* handle) { printf("REPEAT_CB\n"); ASSERT_NOT_NULL(handle); - ASSERT(1 == uv_is_active((uv_handle_t*) handle)); + ASSERT_EQ(1, uv_is_active((uv_handle_t*) handle)); repeat_cb_called++; @@ -95,43 +120,43 @@ TEST_IMPL(timer) { int r; start_time = uv_now(uv_default_loop()); - ASSERT(0 < start_time); + ASSERT_LT(0, start_time); /* Let 10 timers time out in 500 ms total. */ for (i = 0; i < ARRAY_SIZE(once_timers); i++) { once = once_timers + i; r = uv_timer_init(uv_default_loop(), once); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(once, once_cb, i * 50, 0); - ASSERT(r == 0); + ASSERT_OK(r); } /* The 11th timer is a repeating timer that runs 4 times */ r = uv_timer_init(uv_default_loop(), &repeat); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&repeat, repeat_cb, 100, 100); - ASSERT(r == 0); + ASSERT_OK(r); /* The 12th timer should not do anything. */ r = uv_timer_init(uv_default_loop(), &never); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&never, never_cb, 100, 100); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_stop(&never); - ASSERT(r == 0); + ASSERT_OK(r); uv_unref((uv_handle_t*)&never); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(once_cb_called == 10); - ASSERT(once_close_cb_called == 10); + ASSERT_EQ(10, once_cb_called); + ASSERT_EQ(10, once_close_cb_called); printf("repeat_cb_called %d\n", repeat_cb_called); - ASSERT(repeat_cb_called == 5); - ASSERT(repeat_close_cb_called == 1); + ASSERT_EQ(5, repeat_cb_called); + ASSERT_EQ(1, repeat_close_cb_called); - ASSERT(500 <= uv_now(uv_default_loop()) - start_time); + ASSERT_LE(500, uv_now(uv_default_loop()) - start_time); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -141,17 +166,17 @@ TEST_IMPL(timer_start_twice) { int r; r = uv_timer_init(uv_default_loop(), &once); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&once, never_cb, 86400 * 1000, 0); - ASSERT(r == 0); - r = uv_timer_start(&once, once_cb, 10, 0); - ASSERT(r == 0); + ASSERT_OK(r); + r = uv_timer_start(&once, twice_cb, 10, 0); + ASSERT_OK(r); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(once_cb_called == 1); + ASSERT_EQ(1, twice_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -159,23 +184,23 @@ TEST_IMPL(timer_start_twice) { TEST_IMPL(timer_init) { uv_timer_t handle; - ASSERT(0 == uv_timer_init(uv_default_loop(), &handle)); - ASSERT(0 == uv_timer_get_repeat(&handle)); + ASSERT_OK(uv_timer_init(uv_default_loop(), &handle)); + ASSERT_OK(uv_timer_get_repeat(&handle)); ASSERT_UINT64_LE(0, uv_timer_get_due_in(&handle)); - ASSERT(0 == uv_is_active((uv_handle_t*) &handle)); + ASSERT_OK(uv_is_active((uv_handle_t*) &handle)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } static void order_cb_a(uv_timer_t *handle) { - ASSERT(order_cb_called++ == *(int*)handle->data); + ASSERT_EQ(order_cb_called++, *(int*)handle->data); } static void order_cb_b(uv_timer_t *handle) { - ASSERT(order_cb_called++ == *(int*)handle->data); + ASSERT_EQ(order_cb_called++, *(int*)handle->data); } @@ -187,39 +212,64 @@ TEST_IMPL(timer_order) { first = 0; second = 1; - ASSERT(0 == uv_timer_init(uv_default_loop(), &handle_a)); - ASSERT(0 == uv_timer_init(uv_default_loop(), &handle_b)); + ASSERT_OK(uv_timer_init(uv_default_loop(), &handle_a)); + ASSERT_OK(uv_timer_init(uv_default_loop(), &handle_b)); /* Test for starting handle_a then handle_b */ handle_a.data = &first; - ASSERT(0 == uv_timer_start(&handle_a, order_cb_a, 0, 0)); + ASSERT_OK(uv_timer_start(&handle_a, order_cb_a, 0, 0)); handle_b.data = &second; - ASSERT(0 == uv_timer_start(&handle_b, order_cb_b, 0, 0)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_timer_start(&handle_b, order_cb_b, 0, 0)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(order_cb_called == 2); + ASSERT_EQ(2, order_cb_called); - ASSERT(0 == uv_timer_stop(&handle_a)); - ASSERT(0 == uv_timer_stop(&handle_b)); + ASSERT_OK(uv_timer_stop(&handle_a)); + ASSERT_OK(uv_timer_stop(&handle_b)); /* Test for starting handle_b then handle_a */ order_cb_called = 0; handle_b.data = &first; - ASSERT(0 == uv_timer_start(&handle_b, order_cb_b, 0, 0)); + ASSERT_OK(uv_timer_start(&handle_b, order_cb_b, 0, 0)); handle_a.data = &second; - ASSERT(0 == uv_timer_start(&handle_a, order_cb_a, 0, 0)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_timer_start(&handle_a, order_cb_a, 0, 0)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(order_cb_called == 2); + ASSERT_EQ(2, order_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} + + +static void zero_timeout_cb(uv_timer_t* handle) { + ASSERT_OK(uv_timer_start(handle, zero_timeout_cb, 0, 0)); + uv_stop(handle->loop); + zero_timeout_cb_calls++; +} + + +TEST_IMPL(timer_zero_timeout) { + uv_timer_t timer; + uv_loop_t* loop; + + loop = uv_default_loop(); + ASSERT_OK(uv_timer_init(loop, &timer)); + ASSERT_OK(uv_timer_start(&timer, zero_timeout_cb, 0, 0)); + ASSERT_EQ(1, uv_run(loop, UV_RUN_DEFAULT)); /* because of uv_stop() */ + ASSERT_EQ(1, zero_timeout_cb_calls); + uv_close((uv_handle_t*) &timer, NULL); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_EQ(1, zero_timeout_cb_calls); + + MAKE_VALGRIND_HAPPY(loop); return 0; } static void tiny_timer_cb(uv_timer_t* handle) { - ASSERT(handle == &tiny_timer); + ASSERT_PTR_EQ(handle, &tiny_timer); uv_close((uv_handle_t*) &tiny_timer, NULL); uv_close((uv_handle_t*) &huge_timer1, NULL); uv_close((uv_handle_t*) &huge_timer2, NULL); @@ -227,17 +277,20 @@ static void tiny_timer_cb(uv_timer_t* handle) { TEST_IMPL(timer_huge_timeout) { - ASSERT(0 == uv_timer_init(uv_default_loop(), &tiny_timer)); - ASSERT(0 == uv_timer_init(uv_default_loop(), &huge_timer1)); - ASSERT(0 == uv_timer_init(uv_default_loop(), &huge_timer2)); - ASSERT(0 == uv_timer_start(&tiny_timer, tiny_timer_cb, 1, 0)); - ASSERT(0 == uv_timer_start(&huge_timer1, tiny_timer_cb, 0xffffffffffffLL, 0)); - ASSERT(0 == uv_timer_start(&huge_timer2, tiny_timer_cb, (uint64_t) -1, 0)); + ASSERT_OK(uv_timer_init(uv_default_loop(), &tiny_timer)); + ASSERT_OK(uv_timer_init(uv_default_loop(), &huge_timer1)); + ASSERT_OK(uv_timer_init(uv_default_loop(), &huge_timer2)); + ASSERT_OK(uv_timer_start(&tiny_timer, tiny_timer_cb, 1, 0)); + ASSERT_OK(uv_timer_start(&huge_timer1, + tiny_timer_cb, + 0xffffffffffffLL, + 0)); + ASSERT_OK(uv_timer_start(&huge_timer2, tiny_timer_cb, (uint64_t) -1, 0)); ASSERT_UINT64_EQ(1, uv_timer_get_due_in(&tiny_timer)); ASSERT_UINT64_EQ(281474976710655, uv_timer_get_due_in(&huge_timer1)); ASSERT_UINT64_LE(0, uv_timer_get_due_in(&huge_timer2)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - MAKE_VALGRIND_HAPPY(); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -246,9 +299,9 @@ static void huge_repeat_cb(uv_timer_t* handle) { static int ncalls; if (ncalls == 0) - ASSERT(handle == &huge_timer1); + ASSERT_PTR_EQ(handle, &huge_timer1); else - ASSERT(handle == &tiny_timer); + ASSERT_PTR_EQ(handle, &tiny_timer); if (++ncalls == 10) { uv_close((uv_handle_t*) &tiny_timer, NULL); @@ -258,12 +311,12 @@ static void huge_repeat_cb(uv_timer_t* handle) { TEST_IMPL(timer_huge_repeat) { - ASSERT(0 == uv_timer_init(uv_default_loop(), &tiny_timer)); - ASSERT(0 == uv_timer_init(uv_default_loop(), &huge_timer1)); - ASSERT(0 == uv_timer_start(&tiny_timer, huge_repeat_cb, 2, 2)); - ASSERT(0 == uv_timer_start(&huge_timer1, huge_repeat_cb, 1, (uint64_t) -1)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - MAKE_VALGRIND_HAPPY(); + ASSERT_OK(uv_timer_init(uv_default_loop(), &tiny_timer)); + ASSERT_OK(uv_timer_init(uv_default_loop(), &huge_timer1)); + ASSERT_OK(uv_timer_start(&tiny_timer, huge_repeat_cb, 2, 2)); + ASSERT_OK(uv_timer_start(&huge_timer1, huge_repeat_cb, 1, (uint64_t) -1)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -279,19 +332,19 @@ static void timer_run_once_timer_cb(uv_timer_t* handle) { TEST_IMPL(timer_run_once) { uv_timer_t timer_handle; - ASSERT(0 == uv_timer_init(uv_default_loop(), &timer_handle)); - ASSERT(0 == uv_timer_start(&timer_handle, timer_run_once_timer_cb, 0, 0)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); - ASSERT(1 == timer_run_once_timer_cb_called); + ASSERT_OK(uv_timer_init(uv_default_loop(), &timer_handle)); + ASSERT_OK(uv_timer_start(&timer_handle, timer_run_once_timer_cb, 0, 0)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_ONCE)); + ASSERT_EQ(1, timer_run_once_timer_cb_called); - ASSERT(0 == uv_timer_start(&timer_handle, timer_run_once_timer_cb, 1, 0)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); - ASSERT(2 == timer_run_once_timer_cb_called); + ASSERT_OK(uv_timer_start(&timer_handle, timer_run_once_timer_cb, 1, 0)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_ONCE)); + ASSERT_EQ(2, timer_run_once_timer_cb_called); uv_close((uv_handle_t*) &timer_handle, NULL); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_ONCE)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -299,12 +352,12 @@ TEST_IMPL(timer_run_once) { TEST_IMPL(timer_is_closing) { uv_timer_t handle; - ASSERT(0 == uv_timer_init(uv_default_loop(), &handle)); + ASSERT_OK(uv_timer_init(uv_default_loop(), &handle)); uv_close((uv_handle_t *)&handle, NULL); - ASSERT(UV_EINVAL == uv_timer_start(&handle, never_cb, 100, 100)); + ASSERT_EQ(UV_EINVAL, uv_timer_start(&handle, never_cb, 100, 100)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -312,10 +365,10 @@ TEST_IMPL(timer_is_closing) { TEST_IMPL(timer_null_callback) { uv_timer_t handle; - ASSERT(0 == uv_timer_init(uv_default_loop(), &handle)); - ASSERT(UV_EINVAL == uv_timer_start(&handle, NULL, 100, 100)); + ASSERT_OK(uv_timer_init(uv_default_loop(), &handle)); + ASSERT_EQ(UV_EINVAL, uv_timer_start(&handle, NULL, 100, 100)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -325,7 +378,7 @@ static uint64_t timer_early_check_expected_time; static void timer_early_check_cb(uv_timer_t* handle) { uint64_t hrtime = uv_hrtime() / 1000000; - ASSERT(hrtime >= timer_early_check_expected_time); + ASSERT_GE(hrtime, timer_early_check_expected_time); } @@ -335,13 +388,66 @@ TEST_IMPL(timer_early_check) { timer_early_check_expected_time = uv_now(uv_default_loop()) + timeout_ms; - ASSERT(0 == uv_timer_init(uv_default_loop(), &timer_handle)); - ASSERT(0 == uv_timer_start(&timer_handle, timer_early_check_cb, timeout_ms, 0)); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_timer_init(uv_default_loop(), &timer_handle)); + ASSERT_OK(uv_timer_start(&timer_handle, + timer_early_check_cb, + timeout_ms, + 0)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); uv_close((uv_handle_t*) &timer_handle, NULL); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} + +static void timer_check_double_call(uv_timer_t* handle) { + timer_check_double_call_called++; +} + +TEST_IMPL(timer_no_double_call_once) { + uv_timer_t timer_handle; + const uint64_t timeout_ms = 10; + + ASSERT_OK(uv_timer_init(uv_default_loop(), &timer_handle)); + ASSERT_OK(uv_timer_start(&timer_handle, + timer_check_double_call, + timeout_ms, + timeout_ms)); + uv_sleep(timeout_ms * 2); + ASSERT_EQ(1, uv_run(uv_default_loop(), UV_RUN_ONCE)); + ASSERT_EQ(1, timer_check_double_call_called); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} + +TEST_IMPL(timer_no_double_call_nowait) { + uv_timer_t timer_handle; + const uint64_t timeout_ms = 10; + + ASSERT_OK(uv_timer_init(uv_default_loop(), &timer_handle)); + ASSERT_OK(uv_timer_start(&timer_handle, + timer_check_double_call, + timeout_ms, + timeout_ms)); + uv_sleep(timeout_ms * 2); + ASSERT_EQ(1, uv_run(uv_default_loop(), UV_RUN_NOWAIT)); + ASSERT_EQ(1, timer_check_double_call_called); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} + +TEST_IMPL(timer_no_run_on_unref) { + uv_timer_t timer_handle; + + ASSERT_OK(uv_timer_init(uv_default_loop(), &timer_handle)); + ASSERT_OK(uv_timer_start(&timer_handle, (uv_timer_cb) abort, 0, 0)); + uv_unref((uv_handle_t*) &timer_handle); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-tmpdir.c b/test/test-tmpdir.c index 86f72e25431..c8fc8e06a8b 100644 --- a/test/test-tmpdir.c +++ b/test/test-tmpdir.c @@ -36,46 +36,53 @@ TEST_IMPL(tmpdir) { len = sizeof tmpdir; tmpdir[0] = '\0'; - ASSERT(strlen(tmpdir) == 0); + ASSERT_OK(strlen(tmpdir)); r = uv_os_tmpdir(tmpdir, &len); - ASSERT(r == 0); - ASSERT(strlen(tmpdir) == len); - ASSERT(len > 0); - ASSERT(tmpdir[len] == '\0'); + ASSERT_OK(r); + ASSERT_EQ(strlen(tmpdir), len); + ASSERT_GT(len, 0); + ASSERT_EQ(tmpdir[len], '\0'); if (len > 1) { last = tmpdir[len - 1]; #ifdef _WIN32 - ASSERT(last != '\\'); + ASSERT_NE(last, '\\'); #else - ASSERT(last != '/'); + ASSERT_NE(last, '/'); #endif } /* Test the case where the buffer is too small */ len = SMALLPATH; r = uv_os_tmpdir(tmpdir, &len); - ASSERT(r == UV_ENOBUFS); - ASSERT(len > SMALLPATH); + ASSERT_EQ(r, UV_ENOBUFS); + ASSERT_GT(len, SMALLPATH); /* Test invalid inputs */ r = uv_os_tmpdir(NULL, &len); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_os_tmpdir(tmpdir, NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); len = 0; r = uv_os_tmpdir(tmpdir, &len); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); #ifdef _WIN32 const char *name = "TMP"; char tmpdir_win[] = "C:\\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; r = uv_os_setenv(name, tmpdir_win); - ASSERT(r == 0); + ASSERT_OK(r); char tmpdirx[PATHMAX]; size_t lenx = sizeof tmpdirx; r = uv_os_tmpdir(tmpdirx, &lenx); - ASSERT(r == 0); + ASSERT_OK(r); + + /* Test empty environment variable */ + r = uv_os_setenv("TMP", ""); + ASSERT_EQ(r, 0); + len = sizeof tmpdir; + r = uv_os_tmpdir(tmpdir, &len); + ASSERT_EQ(r, UV_ENOENT); #endif return 0; diff --git a/test/test-tty-duplicate-key.c b/test/test-tty-duplicate-key.c index efd79e14786..871d580266a 100644 --- a/test/test-tty-duplicate-key.c +++ b/test/test-tty-duplicate-key.c @@ -73,7 +73,7 @@ static void tty_read(uv_stream_t* tty_in, ssize_t nread, const uv_buf_t* buf) { } uv_close((uv_handle_t*) tty_in, NULL); } else { - ASSERT(nread == 0); + ASSERT_OK(nread); } } @@ -150,25 +150,25 @@ TEST_IMPL(tty_duplicate_vt100_fn_key) { OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - ASSERT(handle != INVALID_HANDLE_VALUE); + ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE); ttyin_fd = _open_osfhandle((intptr_t) handle, 0); - ASSERT(ttyin_fd >= 0); - ASSERT(UV_TTY == uv_guess_handle(ttyin_fd)); + ASSERT_GE(ttyin_fd, 0); + ASSERT_EQ(UV_TTY, uv_guess_handle(ttyin_fd)); r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); /* Readable. */ - ASSERT(r == 0); + ASSERT_OK(r); ASSERT(uv_is_readable((uv_stream_t*) &tty_in)); ASSERT(!uv_is_writable((uv_stream_t*) &tty_in)); r = uv_read_start((uv_stream_t*)&tty_in, tty_alloc, tty_read); - ASSERT(r == 0); + ASSERT_OK(r); expect_str = ESC"[[A"; expect_nread = strlen(expect_str); /* Turn on raw mode. */ r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW); - ASSERT(r == 0); + ASSERT_OK(r); /* * Send F1 keystrokes. Test of issue cause by #2114 that vt100 fn key @@ -176,11 +176,11 @@ TEST_IMPL(tty_duplicate_vt100_fn_key) { */ make_key_event_records(VK_F1, 0, TRUE, records); WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written); - ASSERT(written == ARRAY_SIZE(records)); + ASSERT_EQ(written, ARRAY_SIZE(records)); uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -204,49 +204,49 @@ TEST_IMPL(tty_duplicate_alt_modifier_key) { OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - ASSERT(handle != INVALID_HANDLE_VALUE); + ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE); ttyin_fd = _open_osfhandle((intptr_t) handle, 0); - ASSERT(ttyin_fd >= 0); - ASSERT(UV_TTY == uv_guess_handle(ttyin_fd)); + ASSERT_GE(ttyin_fd, 0); + ASSERT_EQ(UV_TTY, uv_guess_handle(ttyin_fd)); r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); /* Readable. */ - ASSERT(r == 0); + ASSERT_OK(r); ASSERT(uv_is_readable((uv_stream_t*) &tty_in)); ASSERT(!uv_is_writable((uv_stream_t*) &tty_in)); r = uv_read_start((uv_stream_t*)&tty_in, tty_alloc, tty_read); - ASSERT(r == 0); + ASSERT_OK(r); expect_str = ESC"a"ESC"a"; expect_nread = strlen(expect_str); /* Turn on raw mode. */ r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW); - ASSERT(r == 0); + ASSERT_OK(r); /* Emulate transmission of M-a at normal console */ make_key_event_records(VK_MENU, 0, TRUE, alt_records); WriteConsoleInputW(handle, &alt_records[0], 1, &written); - ASSERT(written == 1); + ASSERT_EQ(1, written); make_key_event_records(L'A', LEFT_ALT_PRESSED, FALSE, records); WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written); - ASSERT(written == 2); + ASSERT_EQ(2, written); WriteConsoleInputW(handle, &alt_records[1], 1, &written); - ASSERT(written == 1); + ASSERT_EQ(1, written); /* Emulate transmission of M-a at WSL(#2111) */ make_key_event_records(VK_MENU, 0, TRUE, alt_records); WriteConsoleInputW(handle, &alt_records[0], 1, &written); - ASSERT(written == 1); + ASSERT_EQ(1, written); make_key_event_records(L'A', LEFT_ALT_PRESSED, TRUE, records); WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written); - ASSERT(written == 2); + ASSERT_EQ(2, written); WriteConsoleInputW(handle, &alt_records[1], 1, &written); - ASSERT(written == 1); + ASSERT_EQ(1, written); uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -270,25 +270,25 @@ TEST_IMPL(tty_composing_character) { OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - ASSERT(handle != INVALID_HANDLE_VALUE); + ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE); ttyin_fd = _open_osfhandle((intptr_t) handle, 0); - ASSERT(ttyin_fd >= 0); - ASSERT(UV_TTY == uv_guess_handle(ttyin_fd)); + ASSERT_GE(ttyin_fd, 0); + ASSERT_EQ(UV_TTY, uv_guess_handle(ttyin_fd)); r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); /* Readable. */ - ASSERT(r == 0); + ASSERT_OK(r); ASSERT(uv_is_readable((uv_stream_t*) &tty_in)); ASSERT(!uv_is_writable((uv_stream_t*) &tty_in)); r = uv_read_start((uv_stream_t*)&tty_in, tty_alloc, tty_read); - ASSERT(r == 0); + ASSERT_OK(r); expect_str = EUR_UTF8; expect_nread = strlen(expect_str); /* Turn on raw mode. */ r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW); - ASSERT(r == 0); + ASSERT_OK(r); /* Emulate EUR inputs by LEFT ALT+NUMPAD ASCII KeyComos */ make_key_event_records(VK_MENU, 0, FALSE, alt_records); @@ -296,21 +296,21 @@ TEST_IMPL(tty_composing_character) { WriteConsoleInputW(handle, &alt_records[0], 1, &written); make_key_event_records(VK_NUMPAD0, LEFT_ALT_PRESSED, FALSE, records); WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written); - ASSERT(written == ARRAY_SIZE(records)); + ASSERT_EQ(written, ARRAY_SIZE(records)); make_key_event_records(VK_NUMPAD1, LEFT_ALT_PRESSED, FALSE, records); WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written); - ASSERT(written == ARRAY_SIZE(records)); + ASSERT_EQ(written, ARRAY_SIZE(records)); make_key_event_records(VK_NUMPAD2, LEFT_ALT_PRESSED, FALSE, records); WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written); - ASSERT(written == ARRAY_SIZE(records)); + ASSERT_EQ(written, ARRAY_SIZE(records)); make_key_event_records(VK_NUMPAD8, LEFT_ALT_PRESSED, FALSE, records); WriteConsoleInputW(handle, records, ARRAY_SIZE(records), &written); - ASSERT(written == ARRAY_SIZE(records)); + ASSERT_EQ(written, ARRAY_SIZE(records)); WriteConsoleInputW(handle, &alt_records[1], 1, &written); uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-tty-escape-sequence-processing.c b/test/test-tty-escape-sequence-processing.c index 5f04291d244..4a041db11df 100644 --- a/test/test-tty-escape-sequence-processing.c +++ b/test/test-tty-escape-sequence-processing.c @@ -165,8 +165,8 @@ static void write_console(uv_tty_t* tty_out, char* src) { buf.len = strlen(buf.base); r = uv_try_write((uv_stream_t*) tty_out, &buf, 1); - ASSERT(r >= 0); - ASSERT((unsigned int) r == buf.len); + ASSERT_GE(r, 0); + ASSERT_EQ((unsigned int) r, buf.len); } static void setup_screen(uv_tty_t* tty_out) { @@ -178,8 +178,8 @@ static void setup_screen(uv_tty_t* tty_out) { origin.X = 0; origin.Y = info.srWindow.Top; ASSERT(FillConsoleOutputCharacter( - tty_out->handle, '.', length, origin, &number_of_written)); - ASSERT(length == number_of_written); + tty_out->handle, '.', length, origin, &number_of_written)); + ASSERT_EQ(length, number_of_written); } static void clear_screen(uv_tty_t* tty_out, struct screen_info* si) { @@ -192,10 +192,10 @@ static void clear_screen(uv_tty_t* tty_out, struct screen_info* si) { origin.Y = info.srWindow.Top; FillConsoleOutputCharacterA( tty_out->handle, ' ', length, origin, &number_of_written); - ASSERT(length == number_of_written); + ASSERT_EQ(length, number_of_written); FillConsoleOutputAttribute( tty_out->handle, si->default_attr, length, origin, &number_of_written); - ASSERT(length == number_of_written); + ASSERT_EQ(length, number_of_written); } static void free_screen(struct captured_screen* cs) { @@ -216,11 +216,11 @@ static void capture_screen(uv_tty_t* tty_out, struct captured_screen* cs) { cs->attributes = (WORD*) malloc(cs->si.length * sizeof(*cs->attributes)); ASSERT_NOT_NULL(cs->attributes); ASSERT(ReadConsoleOutputCharacter( - tty_out->handle, cs->text, cs->si.length, origin, &length)); - ASSERT((unsigned int) cs->si.length == length); + tty_out->handle, cs->text, cs->si.length, origin, &length)); + ASSERT_EQ((unsigned int) cs->si.length, length); ASSERT(ReadConsoleOutputAttribute( - tty_out->handle, cs->attributes, cs->si.length, origin, &length)); - ASSERT((unsigned int) cs->si.length == length); + tty_out->handle, cs->attributes, cs->si.length, origin, &length)); + ASSERT_EQ((unsigned int) cs->si.length, length); } static void make_expect_screen_erase(struct captured_screen* cs, @@ -261,8 +261,8 @@ static void make_expect_screen_erase(struct captured_screen* cs, } else { ASSERT(FALSE); } - ASSERT(start < end); - ASSERT(end - cs->text <= cs->si.length); + ASSERT_PTR_LT(start, end); + ASSERT_LE(end - cs->text, cs->si.length); for (; start < end; start++) { *start = ' '; } @@ -360,13 +360,13 @@ static void initialize_tty(uv_tty_t* tty_out) { NULL, CONSOLE_TEXTMODE_BUFFER, NULL); - ASSERT(handle != INVALID_HANDLE_VALUE); + ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE); ttyout_fd = _open_osfhandle((intptr_t) handle, 0); - ASSERT(ttyout_fd >= 0); - ASSERT(UV_TTY == uv_guess_handle(ttyout_fd)); + ASSERT_GE(ttyout_fd, 0); + ASSERT_EQ(UV_TTY, uv_guess_handle(ttyout_fd)); r = uv_tty_init(uv_default_loop(), tty_out, ttyout_fd, 0); /* Writable. */ - ASSERT(r == 0); + ASSERT_OK(r); } static void terminate_tty(uv_tty_t* tty_out) { @@ -394,16 +394,16 @@ TEST_IMPL(tty_cursor_up) { snprintf(buffer, sizeof(buffer), "%sA", CSI); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos_old.Y - 1 == cursor_pos.Y); - ASSERT(cursor_pos_old.X == cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y - 1, cursor_pos.Y); + ASSERT_EQ(cursor_pos_old.X, cursor_pos.X); /* cursor up nth times */ cursor_pos_old = cursor_pos; snprintf(buffer, sizeof(buffer), "%s%dA", CSI, si.height / 4); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos_old.Y - si.height / 4 == cursor_pos.Y); - ASSERT(cursor_pos_old.X == cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y - si.height / 4, cursor_pos.Y); + ASSERT_EQ(cursor_pos_old.X, cursor_pos.X); /* cursor up from Window top does nothing */ cursor_pos_old.X = 1; @@ -412,15 +412,15 @@ TEST_IMPL(tty_cursor_up) { snprintf(buffer, sizeof(buffer), "%sA", CSI); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos_old.Y == cursor_pos.Y); - ASSERT(cursor_pos_old.X == cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y); + ASSERT_EQ(cursor_pos_old.X, cursor_pos.X); ASSERT(!is_scrolling(&tty_out, si)); terminate_tty(&tty_out); uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -445,16 +445,16 @@ TEST_IMPL(tty_cursor_down) { snprintf(buffer, sizeof(buffer), "%sB", CSI); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos_old.Y + 1 == cursor_pos.Y); - ASSERT(cursor_pos_old.X == cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y + 1, cursor_pos.Y); + ASSERT_EQ(cursor_pos_old.X, cursor_pos.X); /* cursor down nth times */ cursor_pos_old = cursor_pos; snprintf(buffer, sizeof(buffer), "%s%dB", CSI, si.height / 4); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos_old.Y + si.height / 4 == cursor_pos.Y); - ASSERT(cursor_pos_old.X == cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y + si.height / 4, cursor_pos.Y); + ASSERT_EQ(cursor_pos_old.X, cursor_pos.X); /* cursor down from bottom line does nothing */ cursor_pos_old.X = si.width / 2; @@ -463,15 +463,15 @@ TEST_IMPL(tty_cursor_down) { snprintf(buffer, sizeof(buffer), "%sB", CSI); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos_old.Y == cursor_pos.Y); - ASSERT(cursor_pos_old.X == cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y); + ASSERT_EQ(cursor_pos_old.X, cursor_pos.X); ASSERT(!is_scrolling(&tty_out, si)); terminate_tty(&tty_out); uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -496,16 +496,16 @@ TEST_IMPL(tty_cursor_forward) { snprintf(buffer, sizeof(buffer), "%sC", CSI); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos_old.Y == cursor_pos.Y); - ASSERT(cursor_pos_old.X + 1 == cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y); + ASSERT_EQ(cursor_pos_old.X + 1, cursor_pos.X); /* cursor forward nth times */ cursor_pos_old = cursor_pos; snprintf(buffer, sizeof(buffer), "%s%dC", CSI, si.width / 4); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos_old.Y == cursor_pos.Y); - ASSERT(cursor_pos_old.X + si.width / 4 == cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y); + ASSERT_EQ(cursor_pos_old.X + si.width / 4, cursor_pos.X); /* cursor forward from end of line does nothing*/ cursor_pos_old.X = si.width; @@ -514,8 +514,8 @@ TEST_IMPL(tty_cursor_forward) { snprintf(buffer, sizeof(buffer), "%sC", CSI); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos_old.Y == cursor_pos.Y); - ASSERT(cursor_pos_old.X == cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y); + ASSERT_EQ(cursor_pos_old.X, cursor_pos.X); /* cursor forward from end of screen does nothing */ cursor_pos_old.X = si.width; @@ -524,15 +524,15 @@ TEST_IMPL(tty_cursor_forward) { snprintf(buffer, sizeof(buffer), "%sC", CSI); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos_old.Y == cursor_pos.Y); - ASSERT(cursor_pos_old.X == cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y); + ASSERT_EQ(cursor_pos_old.X, cursor_pos.X); ASSERT(!is_scrolling(&tty_out, si)); terminate_tty(&tty_out); uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -557,16 +557,16 @@ TEST_IMPL(tty_cursor_back) { snprintf(buffer, sizeof(buffer), "%sD", CSI); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos_old.Y == cursor_pos.Y); - ASSERT(cursor_pos_old.X - 1 == cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y); + ASSERT_EQ(cursor_pos_old.X - 1, cursor_pos.X); /* cursor back nth times */ cursor_pos_old = cursor_pos; snprintf(buffer, sizeof(buffer), "%s%dD", CSI, si.width / 4); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos_old.Y == cursor_pos.Y); - ASSERT(cursor_pos_old.X - si.width / 4 == cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y); + ASSERT_EQ(cursor_pos_old.X - si.width / 4, cursor_pos.X); /* cursor back from beginning of line does nothing */ cursor_pos_old.X = 1; @@ -575,8 +575,8 @@ TEST_IMPL(tty_cursor_back) { snprintf(buffer, sizeof(buffer), "%sD", CSI); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos_old.Y == cursor_pos.Y); - ASSERT(cursor_pos_old.X == cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y); + ASSERT_EQ(cursor_pos_old.X, cursor_pos.X); /* cursor back from top of screen does nothing */ cursor_pos_old.X = 1; @@ -585,15 +585,15 @@ TEST_IMPL(tty_cursor_back) { snprintf(buffer, sizeof(buffer), "%sD", CSI); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(1 == cursor_pos.Y); - ASSERT(1 == cursor_pos.X); + ASSERT_EQ(1, cursor_pos.Y); + ASSERT_EQ(1, cursor_pos.X); ASSERT(!is_scrolling(&tty_out, si)); terminate_tty(&tty_out); uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -618,16 +618,16 @@ TEST_IMPL(tty_cursor_next_line) { snprintf(buffer, sizeof(buffer), "%sE", CSI); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos_old.Y + 1 == cursor_pos.Y); - ASSERT(1 == cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y + 1, cursor_pos.Y); + ASSERT_EQ(1, cursor_pos.X); /* cursor next line nth times */ cursor_pos_old = cursor_pos; snprintf(buffer, sizeof(buffer), "%s%dE", CSI, si.height / 4); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos_old.Y + si.height / 4 == cursor_pos.Y); - ASSERT(1 == cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y + si.height / 4, cursor_pos.Y); + ASSERT_EQ(1, cursor_pos.X); /* cursor next line from buttom row moves beginning of line */ cursor_pos_old.X = si.width / 2; @@ -636,15 +636,15 @@ TEST_IMPL(tty_cursor_next_line) { snprintf(buffer, sizeof(buffer), "%sE", CSI); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos_old.Y == cursor_pos.Y); - ASSERT(1 == cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y); + ASSERT_EQ(1, cursor_pos.X); ASSERT(!is_scrolling(&tty_out, si)); terminate_tty(&tty_out); uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -669,16 +669,16 @@ TEST_IMPL(tty_cursor_previous_line) { snprintf(buffer, sizeof(buffer), "%sF", CSI); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos_old.Y - 1 == cursor_pos.Y); - ASSERT(1 == cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y - 1, cursor_pos.Y); + ASSERT_EQ(1, cursor_pos.X); /* cursor previous line nth times */ cursor_pos_old = cursor_pos; snprintf(buffer, sizeof(buffer), "%s%dF", CSI, si.height / 4); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos_old.Y - si.height / 4 == cursor_pos.Y); - ASSERT(1 == cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y - si.height / 4, cursor_pos.Y); + ASSERT_EQ(1, cursor_pos.X); /* cursor previous line from top of screen does nothing */ cursor_pos_old.X = 1; @@ -687,15 +687,15 @@ TEST_IMPL(tty_cursor_previous_line) { snprintf(buffer, sizeof(buffer), "%sD", CSI); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(1 == cursor_pos.Y); - ASSERT(1 == cursor_pos.X); + ASSERT_EQ(1, cursor_pos.Y); + ASSERT_EQ(1, cursor_pos.X); ASSERT(!is_scrolling(&tty_out, si)); terminate_tty(&tty_out); uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -720,28 +720,28 @@ TEST_IMPL(tty_cursor_horizontal_move_absolute) { snprintf(buffer, sizeof(buffer), "%sG", CSI); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(1 == cursor_pos.X); - ASSERT(cursor_pos_old.Y == cursor_pos.Y); + ASSERT_EQ(1, cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y); /* Move cursor to nth character */ snprintf(buffer, sizeof(buffer), "%s%dG", CSI, si.width / 4); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(si.width / 4 == cursor_pos.X); - ASSERT(cursor_pos_old.Y == cursor_pos.Y); + ASSERT_EQ(si.width / 4, cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y); /* Moving out of screen will fit within screen */ snprintf(buffer, sizeof(buffer), "%s%dG", CSI, si.width + 1); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(si.width == cursor_pos.X); - ASSERT(cursor_pos_old.Y == cursor_pos.Y); + ASSERT_EQ(si.width, cursor_pos.X); + ASSERT_EQ(cursor_pos_old.Y, cursor_pos.Y); terminate_tty(&tty_out); uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -766,38 +766,38 @@ TEST_IMPL(tty_cursor_move_absolute) { snprintf(buffer, sizeof(buffer), "%sH", CSI); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(1 == cursor_pos.X); - ASSERT(1 == cursor_pos.Y); + ASSERT_EQ(1, cursor_pos.X); + ASSERT_EQ(1, cursor_pos.Y); /* Move the cursor to the middle of the screen */ snprintf( buffer, sizeof(buffer), "%s%d;%df", CSI, si.height / 2, si.width / 2); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(si.width / 2 == cursor_pos.X); - ASSERT(si.height / 2 == cursor_pos.Y); + ASSERT_EQ(si.width / 2, cursor_pos.X); + ASSERT_EQ(si.height / 2, cursor_pos.Y); /* Moving out of screen will fit within screen */ snprintf( buffer, sizeof(buffer), "%s%d;%df", CSI, si.height / 2, si.width + 1); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(si.width == cursor_pos.X); - ASSERT(si.height / 2 == cursor_pos.Y); + ASSERT_EQ(si.width, cursor_pos.X); + ASSERT_EQ(si.height / 2, cursor_pos.Y); snprintf( buffer, sizeof(buffer), "%s%d;%df", CSI, si.height + 1, si.width / 2); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(si.width / 2 == cursor_pos.X); - ASSERT(si.height == cursor_pos.Y); + ASSERT_EQ(si.width / 2, cursor_pos.X); + ASSERT_EQ(si.height, cursor_pos.Y); ASSERT(!is_scrolling(&tty_out, si)); terminate_tty(&tty_out); uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -831,7 +831,7 @@ TEST_IMPL(tty_hide_show_cursor) { uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -905,7 +905,7 @@ TEST_IMPL(tty_erase) { uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -979,7 +979,7 @@ TEST_IMPL(tty_erase_line) { uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -1000,49 +1000,54 @@ TEST_IMPL(tty_set_cursor_shape) { set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE); snprintf(buffer, sizeof(buffer), "%s q", CSI); write_console(&tty_out, buffer); - ASSERT(get_cursor_size(&tty_out) == CURSOR_SIZE_LARGE); + ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_LARGE); /* cursor size large */ set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE); snprintf(buffer, sizeof(buffer), "%s1 q", CSI); write_console(&tty_out, buffer); - ASSERT(get_cursor_size(&tty_out) == CURSOR_SIZE_LARGE); + ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_LARGE); set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE); snprintf(buffer, sizeof(buffer), "%s2 q", CSI); write_console(&tty_out, buffer); - ASSERT(get_cursor_size(&tty_out) == CURSOR_SIZE_LARGE); + ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_LARGE); /* cursor size small */ set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE); snprintf(buffer, sizeof(buffer), "%s3 q", CSI); write_console(&tty_out, buffer); - ASSERT(get_cursor_size(&tty_out) == CURSOR_SIZE_SMALL); + ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_SMALL); set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE); snprintf(buffer, sizeof(buffer), "%s6 q", CSI); write_console(&tty_out, buffer); - ASSERT(get_cursor_size(&tty_out) == CURSOR_SIZE_SMALL); + ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_SMALL); /* Nothing occurs with arguments outside valid range */ set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE); snprintf(buffer, sizeof(buffer), "%s7 q", CSI); write_console(&tty_out, buffer); - ASSERT(get_cursor_size(&tty_out) == CURSOR_SIZE_MIDDLE); + ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_MIDDLE); /* restore cursor size if arguments is zero */ snprintf(buffer, sizeof(buffer), "%s0 q", CSI); write_console(&tty_out, buffer); - ASSERT(get_cursor_size(&tty_out) == saved_cursor_size); + ASSERT_EQ(get_cursor_size(&tty_out), saved_cursor_size); terminate_tty(&tty_out); uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } TEST_IMPL(tty_set_style) { +#if _MSC_VER >= 1920 && _MSC_VER <= 1929 + RETURN_SKIP("Broken on Microsoft Visual Studio 2019, to be investigated. " + "See: https://github.com/libuv/libuv/issues/3304"); +#else + uv_tty_t tty_out; uv_loop_t* loop; COORD cursor_pos; @@ -1070,11 +1075,6 @@ TEST_IMPL(tty_set_style) { WORD attr; int i, length; -#if _MSC_VER >= 1920 && _MSC_VER <= 1929 - RETURN_SKIP("Broken on Microsoft Visual Studio 2019, to be investigated. " - "See: https://github.com/libuv/libuv/issues/3304"); -#endif - loop = uv_default_loop(); initialize_tty(&tty_out); @@ -1121,8 +1121,8 @@ TEST_IMPL(tty_set_style) { ASSERT(compare_screen(&tty_out, &actual, &expect)); } - /* Set foregroud and background color */ - ASSERT(ARRAY_SIZE(fg_attrs) == ARRAY_SIZE(bg_attrs)); + /* Set foreground and background color */ + ASSERT_EQ(ARRAY_SIZE(fg_attrs), ARRAY_SIZE(bg_attrs)); length = ARRAY_SIZE(bg_attrs); for (i = 0; i < length; i++) { capture_screen(&tty_out, &expect); @@ -1237,8 +1237,9 @@ TEST_IMPL(tty_set_style) { uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; +#endif } @@ -1270,8 +1271,8 @@ TEST_IMPL(tty_save_restore_cursor_position) { snprintf(buffer, sizeof(buffer), "%su", CSI); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos.X == cursor_pos_old.X); - ASSERT(cursor_pos.Y == cursor_pos_old.Y); + ASSERT_EQ(cursor_pos.X, cursor_pos_old.X); + ASSERT_EQ(cursor_pos.Y, cursor_pos_old.Y); cursor_pos_old.X = si.width / 2; cursor_pos_old.Y = si.height / 2; @@ -1289,14 +1290,14 @@ TEST_IMPL(tty_save_restore_cursor_position) { snprintf(buffer, sizeof(buffer), "%s8", ESC); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos.X == cursor_pos_old.X); - ASSERT(cursor_pos.Y == cursor_pos_old.Y); + ASSERT_EQ(cursor_pos.X, cursor_pos_old.X); + ASSERT_EQ(cursor_pos.Y, cursor_pos_old.Y); terminate_tty(&tty_out); uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -1331,20 +1332,24 @@ TEST_IMPL(tty_full_reset) { write_console(&tty_out, buffer); capture_screen(&tty_out, &actual); ASSERT(compare_screen(&tty_out, &actual, &expect)); - ASSERT(get_cursor_size(&tty_out) == saved_cursor_size); - ASSERT(get_cursor_visibility(&tty_out) == saved_cursor_visibility); - ASSERT(actual.si.csbi.srWindow.Top == 0); + ASSERT_EQ(get_cursor_size(&tty_out), saved_cursor_size); + ASSERT_EQ(get_cursor_visibility(&tty_out), saved_cursor_visibility); + ASSERT_OK(actual.si.csbi.srWindow.Top); terminate_tty(&tty_out); uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } TEST_IMPL(tty_escape_sequence_processing) { +#if _MSC_VER >= 1920 && _MSC_VER <= 1929 + RETURN_SKIP("Broken on Microsoft Visual Studio 2019, to be investigated. " + "See: https://github.com/libuv/libuv/issues/3304"); +#else uv_tty_t tty_out; uv_loop_t* loop; COORD cursor_pos, cursor_pos_old; @@ -1353,16 +1358,11 @@ TEST_IMPL(tty_escape_sequence_processing) { struct captured_screen actual = {0}, expect = {0}; int dir; -#if _MSC_VER >= 1920 && _MSC_VER <= 1929 - RETURN_SKIP("Broken on Microsoft Visual Studio 2019, to be investigated. " - "See: https://github.com/libuv/libuv/issues/3304"); -#endif - loop = uv_default_loop(); initialize_tty(&tty_out); - /* CSI + finaly byte does not output anything */ + /* CSI + finally byte does not output anything */ cursor_pos.X = 1; cursor_pos.Y = 1; set_cursor_position(&tty_out, cursor_pos); @@ -1375,7 +1375,7 @@ TEST_IMPL(tty_escape_sequence_processing) { capture_screen(&tty_out, &actual); ASSERT(compare_screen(&tty_out, &actual, &expect)); - /* CSI(C1) + finaly byte does not output anything */ + /* CSI(C1) + finally byte does not output anything */ cursor_pos.X = 1; cursor_pos.Y = 1; set_cursor_position(&tty_out, cursor_pos); @@ -1388,7 +1388,7 @@ TEST_IMPL(tty_escape_sequence_processing) { capture_screen(&tty_out, &actual); ASSERT(compare_screen(&tty_out, &actual, &expect)); - /* CSI + intermediate byte + finaly byte does not output anything */ + /* CSI + intermediate byte + finally byte does not output anything */ cursor_pos.X = 1; cursor_pos.Y = 1; set_cursor_position(&tty_out, cursor_pos); @@ -1401,7 +1401,7 @@ TEST_IMPL(tty_escape_sequence_processing) { capture_screen(&tty_out, &actual); ASSERT(compare_screen(&tty_out, &actual, &expect)); - /* CSI + parameter byte + finaly byte does not output anything */ + /* CSI + parameter byte + finally byte does not output anything */ cursor_pos.X = 1; cursor_pos.Y = 1; set_cursor_position(&tty_out, cursor_pos); @@ -1520,8 +1520,8 @@ TEST_IMPL(tty_escape_sequence_processing) { snprintf(buffer, sizeof(buffer), "%s1;%dH", CSI, UINT16_MAX + 1); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos.X == 1); - ASSERT(cursor_pos.Y == 1); + ASSERT_EQ(1, cursor_pos.X); + ASSERT_EQ(1, cursor_pos.Y); /* Too many argument are ignored */ cursor_pos.X = 1; @@ -1554,18 +1554,18 @@ TEST_IMPL(tty_escape_sequence_processing) { expect.si.width / 2); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); - ASSERT(cursor_pos.X == 1); - ASSERT(cursor_pos.Y == 1); + ASSERT_EQ(1, cursor_pos.X); + ASSERT_EQ(1, cursor_pos.Y); /* Invalid sequence are ignored */ saved_cursor_size = get_cursor_size(&tty_out); set_cursor_size(&tty_out, CURSOR_SIZE_MIDDLE); snprintf(buffer, sizeof(buffer), "%s 1q", CSI); write_console(&tty_out, buffer); - ASSERT(get_cursor_size(&tty_out) == CURSOR_SIZE_MIDDLE); + ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_MIDDLE); snprintf(buffer, sizeof(buffer), "%s 1 q", CSI); write_console(&tty_out, buffer); - ASSERT(get_cursor_size(&tty_out) == CURSOR_SIZE_MIDDLE); + ASSERT_EQ(get_cursor_size(&tty_out), CURSOR_SIZE_MIDDLE); set_cursor_size(&tty_out, saved_cursor_size); /* #1874 2. */ @@ -1605,7 +1605,7 @@ TEST_IMPL(tty_escape_sequence_processing) { capture_screen(&tty_out, &actual); ASSERT(compare_screen(&tty_out, &actual, &expect)); - /* Finaly byte immedately after CSI [ are also output(#1874 1.) */ + /* Finally byte immedately after CSI [ are also output(#1874 1.) */ cursor_pos.X = expect.si.width / 2; cursor_pos.Y = expect.si.height / 2; set_cursor_position(&tty_out, cursor_pos); @@ -1620,8 +1620,9 @@ TEST_IMPL(tty_escape_sequence_processing) { uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; +#endif } #else diff --git a/test/test-tty.c b/test/test-tty.c index ff7d388d7c0..1b11303829a 100644 --- a/test/test-tty.c +++ b/test/test-tty.c @@ -28,7 +28,7 @@ #else /* Unix */ # include # include -# if (defined(__linux__) || defined(__GLIBC__)) && !defined(__ANDROID__) +# if defined(__linux__) && !defined(__ANDROID__) # include # elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) # include @@ -57,7 +57,7 @@ TEST_IMPL(tty) { OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - ASSERT(handle != INVALID_HANDLE_VALUE); + ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE); ttyin_fd = _open_osfhandle((intptr_t) handle, 0); handle = CreateFileA("conout$", @@ -67,7 +67,7 @@ TEST_IMPL(tty) { OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - ASSERT(handle != INVALID_HANDLE_VALUE); + ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE); ttyout_fd = _open_osfhandle((intptr_t) handle, 0); #else /* unix */ @@ -86,57 +86,53 @@ TEST_IMPL(tty) { } #endif - ASSERT(ttyin_fd >= 0); - ASSERT(ttyout_fd >= 0); + ASSERT_GE(ttyin_fd, 0); + ASSERT_GE(ttyout_fd, 0); - ASSERT(UV_UNKNOWN_HANDLE == uv_guess_handle(-1)); + ASSERT_EQ(UV_UNKNOWN_HANDLE, uv_guess_handle(-1)); - ASSERT(UV_TTY == uv_guess_handle(ttyin_fd)); - ASSERT(UV_TTY == uv_guess_handle(ttyout_fd)); + ASSERT_EQ(UV_TTY, uv_guess_handle(ttyin_fd)); + ASSERT_EQ(UV_TTY, uv_guess_handle(ttyout_fd)); - r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); /* Readable. */ - ASSERT(r == 0); + r = uv_tty_init(loop, &tty_in, ttyin_fd, 1); /* Readable. */ + ASSERT_OK(r); ASSERT(uv_is_readable((uv_stream_t*) &tty_in)); ASSERT(!uv_is_writable((uv_stream_t*) &tty_in)); - r = uv_tty_init(uv_default_loop(), &tty_out, ttyout_fd, 0); /* Writable. */ - ASSERT(r == 0); + r = uv_tty_init(loop, &tty_out, ttyout_fd, 0); /* Writable. */ + ASSERT_OK(r); ASSERT(!uv_is_readable((uv_stream_t*) &tty_out)); ASSERT(uv_is_writable((uv_stream_t*) &tty_out)); r = uv_tty_get_winsize(&tty_out, &width, &height); - ASSERT(r == 0); + ASSERT_OK(r); printf("width=%d height=%d\n", width, height); if (width == 0 && height == 0) { /* Some environments such as containers or Jenkins behave like this * sometimes */ - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return TEST_SKIP; } - /* - * Is it a safe assumption that most people have terminals larger than - * 10x10? - */ - ASSERT(width > 10); - ASSERT(height > 10); + ASSERT_GT(width, 0); + ASSERT_GT(height, 0); /* Turn on raw mode. */ r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW); - ASSERT(r == 0); + ASSERT_OK(r); /* Turn off raw mode. */ r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_NORMAL); - ASSERT(r == 0); + ASSERT_OK(r); /* Calling uv_tty_reset_mode() repeatedly should not clobber errno. */ errno = 0; - ASSERT(0 == uv_tty_reset_mode()); - ASSERT(0 == uv_tty_reset_mode()); - ASSERT(0 == uv_tty_reset_mode()); - ASSERT(0 == errno); + ASSERT_OK(uv_tty_reset_mode()); + ASSERT_OK(uv_tty_reset_mode()); + ASSERT_OK(uv_tty_reset_mode()); + ASSERT_OK(errno); /* TODO check the actual mode! */ @@ -145,7 +141,7 @@ TEST_IMPL(tty) { uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -158,11 +154,11 @@ static void tty_raw_alloc(uv_handle_t* handle, size_t size, uv_buf_t* buf) { static void tty_raw_read(uv_stream_t* tty_in, ssize_t nread, const uv_buf_t* buf) { if (nread > 0) { - ASSERT(nread == 1); - ASSERT(buf->base[0] == ' '); + ASSERT_EQ(1, nread ); + ASSERT_EQ(buf->base[0], ' '); uv_close((uv_handle_t*) tty_in, NULL); } else { - ASSERT(nread == 0); + ASSERT_OK(nread); } } @@ -183,25 +179,25 @@ TEST_IMPL(tty_raw) { OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - ASSERT(handle != INVALID_HANDLE_VALUE); + ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE); ttyin_fd = _open_osfhandle((intptr_t) handle, 0); - ASSERT(ttyin_fd >= 0); - ASSERT(UV_TTY == uv_guess_handle(ttyin_fd)); + ASSERT_GE(ttyin_fd, 0); + ASSERT_EQ(UV_TTY, uv_guess_handle(ttyin_fd)); - r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); /* Readable. */ - ASSERT(r == 0); + r = uv_tty_init(loop, &tty_in, ttyin_fd, 1); /* Readable. */ + ASSERT_OK(r); ASSERT(uv_is_readable((uv_stream_t*) &tty_in)); ASSERT(!uv_is_writable((uv_stream_t*) &tty_in)); r = uv_read_start((uv_stream_t*)&tty_in, tty_raw_alloc, tty_raw_read); - ASSERT(r == 0); + ASSERT_OK(r); /* Give uv_tty_line_read_thread time to block on ReadConsoleW */ Sleep(100); /* Turn on raw mode. */ r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW); - ASSERT(r == 0); + ASSERT_OK(r); /* Write ' ' that should be read in raw mode */ record.EventType = KEY_EVENT; @@ -215,7 +211,7 @@ TEST_IMPL(tty_raw) { uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -239,15 +235,15 @@ TEST_IMPL(tty_empty_write) { OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - ASSERT(handle != INVALID_HANDLE_VALUE); + ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE); ttyout_fd = _open_osfhandle((intptr_t) handle, 0); - ASSERT(ttyout_fd >= 0); + ASSERT_GE(ttyout_fd, 0); - ASSERT(UV_TTY == uv_guess_handle(ttyout_fd)); + ASSERT_EQ(UV_TTY, uv_guess_handle(ttyout_fd)); - r = uv_tty_init(uv_default_loop(), &tty_out, ttyout_fd, 0); /* Writable. */ - ASSERT(r == 0); + r = uv_tty_init(loop, &tty_out, ttyout_fd, 0); /* Writable. */ + ASSERT_OK(r); ASSERT(!uv_is_readable((uv_stream_t*) &tty_out)); ASSERT(uv_is_writable((uv_stream_t*) &tty_out)); @@ -255,13 +251,13 @@ TEST_IMPL(tty_empty_write) { bufs[0].base = &dummy[0]; r = uv_try_write((uv_stream_t*) &tty_out, bufs, 1); - ASSERT(r == 0); + ASSERT_OK(r); uv_close((uv_handle_t*) &tty_out, NULL); uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -285,15 +281,15 @@ TEST_IMPL(tty_large_write) { OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - ASSERT(handle != INVALID_HANDLE_VALUE); + ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE); ttyout_fd = _open_osfhandle((intptr_t) handle, 0); - ASSERT(ttyout_fd >= 0); + ASSERT_GE(ttyout_fd, 0); - ASSERT(UV_TTY == uv_guess_handle(ttyout_fd)); + ASSERT_EQ(UV_TTY, uv_guess_handle(ttyout_fd)); - r = uv_tty_init(uv_default_loop(), &tty_out, ttyout_fd, 0); /* Writable. */ - ASSERT(r == 0); + r = uv_tty_init(loop, &tty_out, ttyout_fd, 0); /* Writable. */ + ASSERT_OK(r); memset(dummy, '.', sizeof(dummy) - 1); dummy[sizeof(dummy) - 1] = '\n'; @@ -301,13 +297,13 @@ TEST_IMPL(tty_large_write) { bufs[0] = uv_buf_init(dummy, sizeof(dummy)); r = uv_try_write((uv_stream_t*) &tty_out, bufs, 1); - ASSERT(r == 10000); + ASSERT_EQ(10000, r); uv_close((uv_handle_t*) &tty_out, NULL); uv_run(loop, UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -325,22 +321,22 @@ TEST_IMPL(tty_raw_cancel) { OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - ASSERT(handle != INVALID_HANDLE_VALUE); + ASSERT_PTR_NE(handle, INVALID_HANDLE_VALUE); ttyin_fd = _open_osfhandle((intptr_t) handle, 0); - ASSERT(ttyin_fd >= 0); - ASSERT(UV_TTY == uv_guess_handle(ttyin_fd)); + ASSERT_GE(ttyin_fd, 0); + ASSERT_EQ(UV_TTY, uv_guess_handle(ttyin_fd)); r = uv_tty_init(uv_default_loop(), &tty_in, ttyin_fd, 1); /* Readable. */ - ASSERT(r == 0); + ASSERT_OK(r); r = uv_tty_set_mode(&tty_in, UV_TTY_MODE_RAW); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_start((uv_stream_t*)&tty_in, tty_raw_alloc, tty_raw_read); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_read_stop((uv_stream_t*) &tty_in); - ASSERT(r == 0); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } #endif @@ -354,35 +350,35 @@ TEST_IMPL(tty_file) { uv_tty_t tty_wo; int fd; - ASSERT(0 == uv_loop_init(&loop)); + ASSERT_OK(uv_loop_init(&loop)); fd = open("test/fixtures/empty_file", O_RDONLY); if (fd != -1) { - ASSERT(UV_EINVAL == uv_tty_init(&loop, &tty, fd, 1)); - ASSERT(0 == close(fd)); + ASSERT_EQ(UV_EINVAL, uv_tty_init(&loop, &tty, fd, 1)); + ASSERT_OK(close(fd)); /* test EBADF handling */ - ASSERT(UV_EINVAL == uv_tty_init(&loop, &tty, fd, 1)); + ASSERT_EQ(UV_EINVAL, uv_tty_init(&loop, &tty, fd, 1)); } /* Bug on AIX where '/dev/random' returns 1 from isatty() */ #ifndef _AIX fd = open("/dev/random", O_RDONLY); if (fd != -1) { - ASSERT(UV_EINVAL == uv_tty_init(&loop, &tty, fd, 1)); - ASSERT(0 == close(fd)); + ASSERT_EQ(UV_EINVAL, uv_tty_init(&loop, &tty, fd, 1)); + ASSERT_OK(close(fd)); } #endif /* _AIX */ fd = open("/dev/zero", O_RDONLY); if (fd != -1) { - ASSERT(UV_EINVAL == uv_tty_init(&loop, &tty, fd, 1)); - ASSERT(0 == close(fd)); + ASSERT_EQ(UV_EINVAL, uv_tty_init(&loop, &tty, fd, 1)); + ASSERT_OK(close(fd)); } fd = open("/dev/tty", O_RDWR); if (fd != -1) { - ASSERT(0 == uv_tty_init(&loop, &tty, fd, 1)); - ASSERT(0 == close(fd)); /* TODO: it's indeterminate who owns fd now */ + ASSERT_OK(uv_tty_init(&loop, &tty, fd, 1)); + ASSERT_OK(close(fd)); /* TODO: it's indeterminate who owns fd now */ ASSERT(uv_is_readable((uv_stream_t*) &tty)); ASSERT(uv_is_writable((uv_stream_t*) &tty)); uv_close((uv_handle_t*) &tty, NULL); @@ -392,8 +388,8 @@ TEST_IMPL(tty_file) { fd = open("/dev/tty", O_RDONLY); if (fd != -1) { - ASSERT(0 == uv_tty_init(&loop, &tty_ro, fd, 1)); - ASSERT(0 == close(fd)); /* TODO: it's indeterminate who owns fd now */ + ASSERT_OK(uv_tty_init(&loop, &tty_ro, fd, 1)); + ASSERT_OK(close(fd)); /* TODO: it's indeterminate who owns fd now */ ASSERT(uv_is_readable((uv_stream_t*) &tty_ro)); ASSERT(!uv_is_writable((uv_stream_t*) &tty_ro)); uv_close((uv_handle_t*) &tty_ro, NULL); @@ -403,8 +399,8 @@ TEST_IMPL(tty_file) { fd = open("/dev/tty", O_WRONLY); if (fd != -1) { - ASSERT(0 == uv_tty_init(&loop, &tty_wo, fd, 0)); - ASSERT(0 == close(fd)); /* TODO: it's indeterminate who owns fd now */ + ASSERT_OK(uv_tty_init(&loop, &tty_wo, fd, 0)); + ASSERT_OK(close(fd)); /* TODO: it's indeterminate who owns fd now */ ASSERT(!uv_is_readable((uv_stream_t*) &tty_wo)); ASSERT(uv_is_writable((uv_stream_t*) &tty_wo)); uv_close((uv_handle_t*) &tty_wo, NULL); @@ -413,10 +409,9 @@ TEST_IMPL(tty_file) { } - ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT)); - ASSERT(0 == uv_loop_close(&loop)); + ASSERT_OK(uv_run(&loop, UV_RUN_DEFAULT)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(&loop); #endif return 0; } @@ -433,7 +428,6 @@ TEST_IMPL(tty_pty) { #if defined(__APPLE__) || \ defined(__DragonFly__) || \ defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) || \ (defined(__linux__) && !defined(__ANDROID__)) || \ defined(__NetBSD__) || \ defined(__OpenBSD__) @@ -442,14 +436,14 @@ TEST_IMPL(tty_pty) { uv_loop_t loop; uv_tty_t master_tty, slave_tty; - ASSERT(0 == uv_loop_init(&loop)); + ASSERT_OK(uv_loop_init(&loop)); r = openpty(&master_fd, &slave_fd, NULL, NULL, &w); if (r != 0) RETURN_SKIP("No pty available, skipping."); - ASSERT(0 == uv_tty_init(&loop, &slave_tty, slave_fd, 0)); - ASSERT(0 == uv_tty_init(&loop, &master_tty, master_fd, 0)); + ASSERT_OK(uv_tty_init(&loop, &slave_tty, slave_fd, 0)); + ASSERT_OK(uv_tty_init(&loop, &master_tty, master_fd, 0)); ASSERT(uv_is_readable((uv_stream_t*) &slave_tty)); ASSERT(uv_is_writable((uv_stream_t*) &slave_tty)); ASSERT(uv_is_readable((uv_stream_t*) &master_tty)); @@ -457,18 +451,18 @@ TEST_IMPL(tty_pty) { /* Check if the file descriptor was reopened. If it is, * UV_HANDLE_BLOCKING_WRITES (value 0x100000) isn't set on flags. */ - ASSERT(0 == (slave_tty.flags & 0x100000)); + ASSERT_OK((slave_tty.flags & 0x100000)); /* The master_fd of a pty should never be reopened. */ ASSERT(master_tty.flags & 0x100000); - ASSERT(0 == close(slave_fd)); + ASSERT_OK(close(slave_fd)); uv_close((uv_handle_t*) &slave_tty, NULL); - ASSERT(0 == close(master_fd)); + ASSERT_OK(close(master_fd)); uv_close((uv_handle_t*) &master_tty, NULL); - ASSERT(0 == uv_run(&loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(&loop, UV_RUN_DEFAULT)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(&loop); #endif return 0; } diff --git a/test/test-udp-alloc-cb-fail.c b/test/test-udp-alloc-cb-fail.c index 6b0980163a5..ae4bbee325e 100644 --- a/test/test-udp-alloc-cb-fail.c +++ b/test/test-udp-alloc-cb-fail.c @@ -27,7 +27,7 @@ #include #define CHECK_HANDLE(handle) \ - ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + ASSERT_NE((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client, 0) static uv_udp_t server; static uv_udp_t client; @@ -60,7 +60,7 @@ static void cl_alloc_cb(uv_handle_t* handle, static void close_cb(uv_handle_t* handle) { CHECK_HANDLE(handle); - ASSERT(1 == uv_is_closing(handle)); + ASSERT_EQ(1, uv_is_closing(handle)); close_cb_called++; } @@ -71,8 +71,8 @@ static void cl_recv_cb(uv_udp_t* handle, const struct sockaddr* addr, unsigned flags) { CHECK_HANDLE(handle); - ASSERT(flags == 0); - ASSERT(nread == UV_ENOBUFS); + ASSERT_OK(flags); + ASSERT_EQ(nread, UV_ENOBUFS); cl_recv_cb_called++; @@ -84,11 +84,11 @@ static void cl_send_cb(uv_udp_send_t* req, int status) { int r; ASSERT_NOT_NULL(req); - ASSERT(status == 0); + ASSERT_OK(status); CHECK_HANDLE(req->handle); r = uv_udp_recv_start(req->handle, cl_alloc_cb, cl_recv_cb); - ASSERT(r == 0); + ASSERT_OK(r); cl_send_cb_called++; } @@ -96,7 +96,7 @@ static void cl_send_cb(uv_udp_send_t* req, int status) { static void sv_send_cb(uv_udp_send_t* req, int status) { ASSERT_NOT_NULL(req); - ASSERT(status == 0); + ASSERT_OK(status); CHECK_HANDLE(req->handle); uv_close((uv_handle_t*) req->handle, close_cb); @@ -126,21 +126,21 @@ static void sv_recv_cb(uv_udp_t* handle, } CHECK_HANDLE(handle); - ASSERT(flags == 0); + ASSERT_OK(flags); ASSERT_NOT_NULL(addr); - ASSERT(nread == 4); + ASSERT_EQ(4, nread); ASSERT(!memcmp("PING", rcvbuf->base, nread)); r = uv_udp_recv_stop(handle); - ASSERT(r == 0); + ASSERT_OK(r); req = malloc(sizeof *req); ASSERT_NOT_NULL(req); sndbuf = uv_buf_init("PONG", 4); r = uv_udp_send(req, handle, &sndbuf, 1, addr, sv_send_cb); - ASSERT(r == 0); + ASSERT_OK(r); sv_recv_cb_called++; } @@ -152,21 +152,21 @@ TEST_IMPL(udp_alloc_cb_fail) { uv_buf_t buf; int r; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_recv_start(&server, sv_alloc_cb, sv_recv_cb); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &client); - ASSERT(r == 0); + ASSERT_OK(r); buf = uv_buf_init("PING", 4); r = uv_udp_send(&req, @@ -175,22 +175,22 @@ TEST_IMPL(udp_alloc_cb_fail) { 1, (const struct sockaddr*) &addr, cl_send_cb); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(close_cb_called == 0); - ASSERT(cl_send_cb_called == 0); - ASSERT(cl_recv_cb_called == 0); - ASSERT(sv_send_cb_called == 0); - ASSERT(sv_recv_cb_called == 0); + ASSERT_OK(close_cb_called); + ASSERT_OK(cl_send_cb_called); + ASSERT_OK(cl_recv_cb_called); + ASSERT_OK(sv_send_cb_called); + ASSERT_OK(sv_recv_cb_called); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(cl_send_cb_called == 1); - ASSERT(cl_recv_cb_called == 1); - ASSERT(sv_send_cb_called == 1); - ASSERT(sv_recv_cb_called == 1); - ASSERT(close_cb_called == 2); + ASSERT_EQ(1, cl_send_cb_called); + ASSERT_EQ(1, cl_recv_cb_called); + ASSERT_EQ(1, sv_send_cb_called); + ASSERT_EQ(1, sv_recv_cb_called); + ASSERT_EQ(2, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-udp-bind.c b/test/test-udp-bind.c index a1e080ee70c..519f9b02fa2 100644 --- a/test/test-udp-bind.c +++ b/test/test-udp-bind.c @@ -33,29 +33,29 @@ TEST_IMPL(udp_bind) { uv_udp_t h1, h2; int r; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); loop = uv_default_loop(); r = uv_udp_init(loop, &h1); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_init(loop, &h2); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_bind(&h1, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_bind(&h2, (const struct sockaddr*) &addr, 0); - ASSERT(r == UV_EADDRINUSE); + ASSERT_EQ(r, UV_EADDRINUSE); uv_close((uv_handle_t*) &h1, NULL); uv_close((uv_handle_t*) &h2, NULL); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -66,28 +66,28 @@ TEST_IMPL(udp_bind_reuseaddr) { uv_udp_t h1, h2; int r; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); loop = uv_default_loop(); r = uv_udp_init(loop, &h1); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_init(loop, &h2); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_bind(&h1, (const struct sockaddr*) &addr, UV_UDP_REUSEADDR); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_bind(&h2, (const struct sockaddr*) &addr, UV_UDP_REUSEADDR); - ASSERT(r == 0); + ASSERT_OK(r); uv_close((uv_handle_t*) &h1, NULL); uv_close((uv_handle_t*) &h2, NULL); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-udp-connect.c b/test/test-udp-connect.c index cd159b68da4..88314acc69b 100644 --- a/test/test-udp-connect.c +++ b/test/test-udp-connect.c @@ -27,7 +27,7 @@ #include #define CHECK_HANDLE(handle) \ - ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + ASSERT_NE((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client, 0) static uv_udp_t server; static uv_udp_t client; @@ -45,7 +45,7 @@ static void alloc_cb(uv_handle_t* handle, uv_buf_t* buf) { static char slab[65536]; CHECK_HANDLE(handle); - ASSERT(suggested_size <= sizeof(slab)); + ASSERT_LE(suggested_size, sizeof(slab)); buf->base = slab; buf->len = sizeof(slab); } @@ -62,19 +62,19 @@ static void cl_send_cb(uv_udp_send_t* req, int status) { int r; ASSERT_NOT_NULL(req); - ASSERT(status == 0); + ASSERT_OK(status); CHECK_HANDLE(req->handle); if (++cl_send_cb_called == 1) { uv_udp_connect(&client, NULL); r = uv_udp_send(req, &client, &buf, 1, NULL, cl_send_cb); - ASSERT(r == UV_EDESTADDRREQ); + ASSERT_EQ(r, UV_EDESTADDRREQ); r = uv_udp_send(req, &client, &buf, 1, (const struct sockaddr*) &lo_addr, cl_send_cb); - ASSERT(r == 0); + ASSERT_OK(r); } } @@ -86,9 +86,9 @@ static void sv_recv_cb(uv_udp_t* handle, const struct sockaddr* addr, unsigned flags) { if (nread > 0) { - ASSERT(nread == 4); + ASSERT_EQ(4, nread); ASSERT_NOT_NULL(addr); - ASSERT(memcmp("EXIT", rcvbuf->base, nread) == 0); + ASSERT_OK(memcmp("EXIT", rcvbuf->base, nread)); if (++sv_recv_cb_called == 4) { uv_close((uv_handle_t*) &server, close_cb); uv_close((uv_handle_t*) &client, close_cb); @@ -98,9 +98,8 @@ static void sv_recv_cb(uv_udp_t* handle, TEST_IMPL(udp_connect) { -#if defined(__PASE__) - RETURN_SKIP( - "IBMi PASE's UDP connection can not be disconnected with AF_UNSPEC."); +#if defined(__OpenBSD__) + RETURN_SKIP("Test does not currently work in OpenBSD"); #endif uv_udp_send_t req; struct sockaddr_in ext_addr; @@ -108,90 +107,90 @@ TEST_IMPL(udp_connect) { int r; int addrlen; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &lo_addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &lo_addr)); r = uv_udp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_bind(&server, (const struct sockaddr*) &lo_addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_init(uv_default_loop(), &client); - ASSERT(r == 0); + ASSERT_OK(r); buf = uv_buf_init("EXIT", 4); /* connect() to INADDR_ANY fails on Windows with WSAEADDRNOTAVAIL */ - ASSERT_EQ(0, uv_ip4_addr("0.0.0.0", TEST_PORT, &tmp_addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &tmp_addr)); r = uv_udp_connect(&client, (const struct sockaddr*) &tmp_addr); #ifdef _WIN32 ASSERT_EQ(r, UV_EADDRNOTAVAIL); #else - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_udp_connect(&client, NULL); - ASSERT_EQ(r, 0); + ASSERT_OK(r); #endif - ASSERT(0 == uv_ip4_addr("8.8.8.8", TEST_PORT, &ext_addr)); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &lo_addr)); + ASSERT_OK(uv_ip4_addr("8.8.8.8", TEST_PORT, &ext_addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &lo_addr)); r = uv_udp_connect(&client, (const struct sockaddr*) &lo_addr); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_connect(&client, (const struct sockaddr*) &ext_addr); - ASSERT(r == UV_EISCONN); + ASSERT_EQ(r, UV_EISCONN); addrlen = sizeof(tmp_addr); r = uv_udp_getpeername(&client, (struct sockaddr*) &tmp_addr, &addrlen); - ASSERT(r == 0); + ASSERT_OK(r); /* To send messages in connected UDP sockets addr must be NULL */ r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &lo_addr); - ASSERT(r == UV_EISCONN); + ASSERT_EQ(r, UV_EISCONN); r = uv_udp_try_send(&client, &buf, 1, NULL); - ASSERT(r == 4); + ASSERT_EQ(4, r); r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &ext_addr); - ASSERT(r == UV_EISCONN); + ASSERT_EQ(r, UV_EISCONN); r = uv_udp_connect(&client, NULL); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_connect(&client, NULL); - ASSERT(r == UV_ENOTCONN); + ASSERT_EQ(r, UV_ENOTCONN); addrlen = sizeof(tmp_addr); r = uv_udp_getpeername(&client, (struct sockaddr*) &tmp_addr, &addrlen); - ASSERT(r == UV_ENOTCONN); + ASSERT_EQ(r, UV_ENOTCONN); /* To send messages in disconnected UDP sockets addr must be set */ r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &lo_addr); - ASSERT(r == 4); + ASSERT_EQ(4, r); r = uv_udp_try_send(&client, &buf, 1, NULL); - ASSERT(r == UV_EDESTADDRREQ); + ASSERT_EQ(r, UV_EDESTADDRREQ); r = uv_udp_connect(&client, (const struct sockaddr*) &lo_addr); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_send(&req, &client, &buf, 1, (const struct sockaddr*) &lo_addr, cl_send_cb); - ASSERT(r == UV_EISCONN); + ASSERT_EQ(r, UV_EISCONN); r = uv_udp_send(&req, &client, &buf, 1, NULL, cl_send_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 2); - ASSERT(sv_recv_cb_called == 4); - ASSERT(cl_send_cb_called == 2); + ASSERT_EQ(2, close_cb_called); + ASSERT_EQ(4, sv_recv_cb_called); + ASSERT_EQ(2, cl_send_cb_called); - ASSERT(client.send_queue_size == 0); - ASSERT(server.send_queue_size == 0); + ASSERT_OK(client.send_queue_size); + ASSERT_OK(server.send_queue_size); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-udp-connect6.c b/test/test-udp-connect6.c index 8e385af99b2..bbc4033c55f 100644 --- a/test/test-udp-connect6.c +++ b/test/test-udp-connect6.c @@ -27,7 +27,7 @@ #include #define CHECK_HANDLE(handle) \ - ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + ASSERT_NE((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client, 0) static uv_udp_t server; static uv_udp_t client; @@ -62,7 +62,7 @@ static void cl_send_cb(uv_udp_send_t* req, int status) { int r; ASSERT_NOT_NULL(req); - ASSERT_EQ(status, 0); + ASSERT_OK(status); CHECK_HANDLE(req->handle); if (++cl_send_cb_called == 1) { uv_udp_connect(&client, NULL); @@ -74,7 +74,7 @@ static void cl_send_cb(uv_udp_send_t* req, int status) { 1, (const struct sockaddr*) &lo_addr, cl_send_cb); - ASSERT_EQ(r, 0); + ASSERT_OK(r); } } @@ -86,9 +86,9 @@ static void sv_recv_cb(uv_udp_t* handle, const struct sockaddr* addr, unsigned flags) { if (nread > 0) { - ASSERT_EQ(nread, 4); + ASSERT_EQ(4, nread); ASSERT_NOT_NULL(addr); - ASSERT_EQ(memcmp("EXIT", rcvbuf->base, nread), 0); + ASSERT_OK(memcmp("EXIT", rcvbuf->base, nread)); if (++sv_recv_cb_called == 4) { uv_close((uv_handle_t*) &server, close_cb); uv_close((uv_handle_t*) &client, close_cb); @@ -98,9 +98,8 @@ static void sv_recv_cb(uv_udp_t* handle, TEST_IMPL(udp_connect6) { -#if defined(__PASE__) - RETURN_SKIP( - "IBMi PASE's UDP connection can not be disconnected with AF_UNSPEC."); +#if defined(__OpenBSD__) + RETURN_SKIP("Test does not currently work in OpenBSD"); #endif uv_udp_send_t req; struct sockaddr_in6 ext_addr; @@ -111,55 +110,55 @@ TEST_IMPL(udp_connect6) { if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); - ASSERT_EQ(0, uv_ip6_addr("::", TEST_PORT, &lo_addr)); + ASSERT_OK(uv_ip6_addr("::", TEST_PORT, &lo_addr)); r = uv_udp_init(uv_default_loop(), &server); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_udp_bind(&server, (const struct sockaddr*) &lo_addr, 0); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_udp_init(uv_default_loop(), &client); - ASSERT_EQ(r, 0); + ASSERT_OK(r); buf = uv_buf_init("EXIT", 4); /* connect() to INADDR_ANY fails on Windows wih WSAEADDRNOTAVAIL */ - ASSERT_EQ(0, uv_ip6_addr("::", TEST_PORT, &tmp_addr)); + ASSERT_OK(uv_ip6_addr("::", TEST_PORT, &tmp_addr)); r = uv_udp_connect(&client, (const struct sockaddr*) &tmp_addr); #ifdef _WIN32 ASSERT_EQ(r, UV_EADDRNOTAVAIL); #else - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_udp_connect(&client, NULL); - ASSERT_EQ(r, 0); + ASSERT_OK(r); #endif - ASSERT_EQ(0, uv_ip6_addr("2001:4860:4860::8888", TEST_PORT, &ext_addr)); - ASSERT_EQ(0, uv_ip6_addr("::1", TEST_PORT, &lo_addr)); + ASSERT_OK(uv_ip6_addr("2001:4860:4860::8888", TEST_PORT, &ext_addr)); + ASSERT_OK(uv_ip6_addr("::1", TEST_PORT, &lo_addr)); r = uv_udp_connect(&client, (const struct sockaddr*) &lo_addr); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_udp_connect(&client, (const struct sockaddr*) &ext_addr); ASSERT_EQ(r, UV_EISCONN); addrlen = sizeof(tmp_addr); r = uv_udp_getpeername(&client, (struct sockaddr*) &tmp_addr, &addrlen); - ASSERT_EQ(r, 0); + ASSERT_OK(r); /* To send messages in connected UDP sockets addr must be NULL */ r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &lo_addr); ASSERT_EQ(r, UV_EISCONN); r = uv_udp_try_send(&client, &buf, 1, NULL); - ASSERT_EQ(r, 4); + ASSERT_EQ(4, r); r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &ext_addr); ASSERT_EQ(r, UV_EISCONN); r = uv_udp_connect(&client, NULL); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_udp_connect(&client, NULL); ASSERT_EQ(r, UV_ENOTCONN); @@ -169,13 +168,13 @@ TEST_IMPL(udp_connect6) { /* To send messages in disconnected UDP sockets addr must be set */ r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &lo_addr); - ASSERT_EQ(r, 4); + ASSERT_EQ(4, r); r = uv_udp_try_send(&client, &buf, 1, NULL); ASSERT_EQ(r, UV_EDESTADDRREQ); r = uv_udp_connect(&client, (const struct sockaddr*) &lo_addr); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_udp_send(&req, &client, &buf, @@ -184,17 +183,17 @@ TEST_IMPL(udp_connect6) { cl_send_cb); ASSERT_EQ(r, UV_EISCONN); r = uv_udp_send(&req, &client, &buf, 1, NULL, cl_send_cb); - ASSERT_EQ(r, 0); + ASSERT_OK(r); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT_EQ(close_cb_called, 2); - ASSERT_EQ(sv_recv_cb_called, 4); - ASSERT_EQ(cl_send_cb_called, 2); + ASSERT_EQ(2, close_cb_called); + ASSERT_EQ(4, sv_recv_cb_called); + ASSERT_EQ(2, cl_send_cb_called); - ASSERT_EQ(client.send_queue_size, 0); - ASSERT_EQ(server.send_queue_size, 0); + ASSERT_OK(client.send_queue_size); + ASSERT_OK(server.send_queue_size); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-udp-create-socket-early.c b/test/test-udp-create-socket-early.c index f7e46abc98d..f2e166af9c2 100644 --- a/test/test-udp-create-socket-early.c +++ b/test/test-udp-create-socket-early.c @@ -38,37 +38,39 @@ TEST_IMPL(udp_create_early) { uv_os_fd_t fd; int r, namelen; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_udp_init_ex(uv_default_loop(), &client, AF_INET); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fileno((const uv_handle_t*) &client, &fd); - ASSERT(r == 0); - ASSERT(fd != INVALID_FD); + ASSERT_OK(r); /* Windows returns WSAEINVAL if the socket is not bound */ #ifndef _WIN32 + ASSERT_NE(fd, INVALID_FD); namelen = sizeof sockname; r = uv_udp_getsockname(&client, (struct sockaddr*) &sockname, &namelen); - ASSERT(r == 0); - ASSERT(sockname.sin_family == AF_INET); + ASSERT_OK(r); + ASSERT_EQ(sockname.sin_family, AF_INET); +#else + ASSERT_PTR_NE(fd, INVALID_FD); #endif r = uv_udp_bind(&client, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); namelen = sizeof sockname; r = uv_udp_getsockname(&client, (struct sockaddr*) &sockname, &namelen); - ASSERT(r == 0); - ASSERT(memcmp(&addr.sin_addr, - &sockname.sin_addr, - sizeof(addr.sin_addr)) == 0); + ASSERT_OK(r); + ASSERT_OK(memcmp(&addr.sin_addr, + &sockname.sin_addr, + sizeof(addr.sin_addr))); uv_close((uv_handle_t*) &client, NULL); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -82,38 +84,40 @@ TEST_IMPL(udp_create_early_bad_bind) { if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_udp_init_ex(uv_default_loop(), &client, AF_INET6); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_fileno((const uv_handle_t*) &client, &fd); - ASSERT(r == 0); - ASSERT(fd != INVALID_FD); + ASSERT_OK(r); /* Windows returns WSAEINVAL if the socket is not bound */ -#ifndef _WIN32 - { +#ifndef _WIN32 + ASSERT_NE(fd, INVALID_FD); + { int namelen; struct sockaddr_in6 sockname; namelen = sizeof sockname; r = uv_udp_getsockname(&client, (struct sockaddr*) &sockname, &namelen); - ASSERT(r == 0); - ASSERT(sockname.sin6_family == AF_INET6); + ASSERT_OK(r); + ASSERT_EQ(sockname.sin6_family, AF_INET6); } +#else + ASSERT_PTR_NE(fd, INVALID_FD); #endif r = uv_udp_bind(&client, (const struct sockaddr*) &addr, 0); #if !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MSYS__) - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); #else - ASSERT(r == UV_EFAULT); + ASSERT_EQ(r, UV_EFAULT); #endif uv_close((uv_handle_t*) &client, NULL); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -123,13 +127,13 @@ TEST_IMPL(udp_create_early_bad_domain) { int r; r = uv_udp_init_ex(uv_default_loop(), &client, 47); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); r = uv_udp_init_ex(uv_default_loop(), &client, 1024); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-udp-dgram-too-big.c b/test/test-udp-dgram-too-big.c index bd44c425287..8fae756e5c4 100644 --- a/test/test-udp-dgram-too-big.c +++ b/test/test-udp-dgram-too-big.c @@ -27,10 +27,10 @@ #include #define CHECK_HANDLE(handle) \ - ASSERT((uv_udp_t*)(handle) == &handle_) + ASSERT_PTR_EQ((uv_udp_t*)(handle), &handle_) #define CHECK_REQ(req) \ - ASSERT((req) == &req_); + ASSERT_PTR_EQ((req), &req_); static uv_udp_t handle_; static uv_udp_send_t req_; @@ -49,7 +49,7 @@ static void send_cb(uv_udp_send_t* req, int status) { CHECK_REQ(req); CHECK_HANDLE(req->handle); - ASSERT(status == UV_EMSGSIZE); + ASSERT_EQ(status, UV_EMSGSIZE); uv_close((uv_handle_t*)req->handle, close_cb); send_cb_called++; @@ -65,10 +65,10 @@ TEST_IMPL(udp_dgram_too_big) { memset(dgram, 42, sizeof dgram); /* silence valgrind */ r = uv_udp_init(uv_default_loop(), &handle_); - ASSERT(r == 0); + ASSERT_OK(r); buf = uv_buf_init(dgram, sizeof dgram); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_udp_send(&req_, &handle_, @@ -76,16 +76,16 @@ TEST_IMPL(udp_dgram_too_big) { 1, (const struct sockaddr*) &addr, send_cb); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(close_cb_called == 0); - ASSERT(send_cb_called == 0); + ASSERT_OK(close_cb_called); + ASSERT_OK(send_cb_called); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(send_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, send_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-udp-ipv6.c b/test/test-udp-ipv6.c index 7099953097c..8ad80b9b5a9 100644 --- a/test/test-udp-ipv6.c +++ b/test/test-udp-ipv6.c @@ -26,17 +26,17 @@ #include #include -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) +#if defined(__FreeBSD__) || defined(__NetBSD__) #include #endif -#define CHECK_HANDLE(handle) \ - ASSERT((uv_udp_t*)(handle) == &server \ - || (uv_udp_t*)(handle) == &client \ - || (uv_timer_t*)(handle) == &timeout) +#define CHECK_HANDLE(handle) \ + ASSERT_NE((uv_udp_t*)(handle) == &server \ + || (uv_udp_t*)(handle) == &client \ + || (uv_timer_t*)(handle) == &timeout, 0) #define CHECK_REQ(req) \ - ASSERT((req) == &req_); + ASSERT_PTR_EQ((req), &req_); static uv_udp_t client; static uv_udp_t server; @@ -49,7 +49,7 @@ static int recv_cb_called; static int close_cb_called; static uint16_t client_port; -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) +#if defined(__FreeBSD__) || defined(__NetBSD__) static int can_ipv6_ipv4_dual(void) { int v6only; size_t size = sizeof(int); @@ -81,7 +81,7 @@ static void close_cb(uv_handle_t* handle) { static void send_cb(uv_udp_send_t* req, int status) { CHECK_REQ(req); CHECK_HANDLE(req->handle); - ASSERT(status == 0); + ASSERT_OK(status); send_cb_called++; } @@ -92,7 +92,7 @@ static int is_from_client(const struct sockaddr* addr) { /* Debugging output, and filter out unwanted network traffic */ if (addr != NULL) { - ASSERT(addr->sa_family == AF_INET6); + ASSERT_EQ(addr->sa_family, AF_INET6); addr6 = (struct sockaddr_in6*) addr; r = uv_inet_ntop(addr->sa_family, &addr6->sin6_addr, dst, sizeof(dst)); if (r == 0) @@ -129,7 +129,7 @@ static void ipv6_recv_ok(uv_udp_t* handle, if (!is_from_client(addr) || (nread == 0 && addr == NULL)) return; - ASSERT(nread == 9); + ASSERT_EQ(9, nread); ASSERT(!memcmp(buf->base, data, 9)); recv_cb_called++; } @@ -151,31 +151,39 @@ static void do_test(uv_udp_recv_cb recv_cb, int bind_flags) { char dst[256]; int r; - ASSERT(0 == uv_ip6_addr("::0", TEST_PORT, &addr6)); + ASSERT_OK(uv_ip6_addr("::0", TEST_PORT, &addr6)); r = uv_udp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_bind(&server, (const struct sockaddr*) &addr6, bind_flags); - ASSERT(r == 0); + ASSERT_OK(r); addr6_len = sizeof(addr6); - ASSERT(uv_udp_getsockname(&server, (struct sockaddr*) &addr6, &addr6_len) == 0); - ASSERT(uv_inet_ntop(addr6.sin6_family, &addr6.sin6_addr, dst, sizeof(dst)) == 0); + ASSERT_OK(uv_udp_getsockname(&server, + (struct sockaddr*) &addr6, + &addr6_len)); + ASSERT_OK(uv_inet_ntop(addr6.sin6_family, + &addr6.sin6_addr, + dst, + sizeof(dst))); printf("on [%.*s]:%d\n", (int) sizeof(dst), dst, addr6.sin6_port); r = uv_udp_recv_start(&server, alloc_cb, recv_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_init(uv_default_loop(), &client); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); - ASSERT(uv_inet_ntop(addr.sin_family, &addr.sin_addr, dst, sizeof(dst)) == 0); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_inet_ntop(addr.sin_family, &addr.sin_addr, dst, sizeof(dst))); printf("to [%.*s]:%d\n", (int) sizeof(dst), dst, addr.sin_port); /* Create some unique data to send */ - ASSERT(9 == snprintf(data, sizeof(data), "PING%5u", uv_os_getpid() & 0xFFFF)); + ASSERT_EQ(9, snprintf(data, + sizeof(data), + "PING%5u", + uv_os_getpid() & 0xFFFF)); buf = uv_buf_init(data, 9); printf("sending %s\n", data); @@ -185,29 +193,29 @@ static void do_test(uv_udp_recv_cb recv_cb, int bind_flags) { 1, (const struct sockaddr*) &addr, send_cb); - ASSERT(r == 0); + ASSERT_OK(r); addr_len = sizeof(addr); - ASSERT(uv_udp_getsockname(&client, (struct sockaddr*) &addr, &addr_len) == 0); - ASSERT(uv_inet_ntop(addr.sin_family, &addr.sin_addr, dst, sizeof(dst)) == 0); + ASSERT_OK(uv_udp_getsockname(&client, (struct sockaddr*) &addr, &addr_len)); + ASSERT_OK(uv_inet_ntop(addr.sin_family, &addr.sin_addr, dst, sizeof(dst))); printf("from [%.*s]:%d\n", (int) sizeof(dst), dst, addr.sin_port); client_port = addr.sin_port; r = uv_timer_init(uv_default_loop(), &timeout); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&timeout, timeout_cb, 500, 0); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(close_cb_called == 0); - ASSERT(send_cb_called == 0); - ASSERT(recv_cb_called == 0); + ASSERT_OK(close_cb_called); + ASSERT_OK(send_cb_called); + ASSERT_OK(recv_cb_called); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 3); + ASSERT_EQ(3, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); } @@ -220,7 +228,7 @@ TEST_IMPL(udp_dual_stack) { if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) +#if defined(__FreeBSD__) || defined(__NetBSD__) if (!can_ipv6_ipv4_dual()) RETURN_SKIP("IPv6-IPv4 dual stack not supported"); #elif defined(__OpenBSD__) @@ -231,8 +239,8 @@ TEST_IMPL(udp_dual_stack) { printf("recv_cb_called %d\n", recv_cb_called); printf("send_cb_called %d\n", send_cb_called); - ASSERT(recv_cb_called == 1); - ASSERT(send_cb_called == 1); + ASSERT_EQ(1, recv_cb_called); + ASSERT_EQ(1, send_cb_called); return 0; } @@ -244,8 +252,8 @@ TEST_IMPL(udp_ipv6_only) { do_test(ipv6_recv_fail, UV_UDP_IPV6ONLY); - ASSERT(recv_cb_called == 0); - ASSERT(send_cb_called == 1); + ASSERT_OK(recv_cb_called); + ASSERT_EQ(1, send_cb_called); return 0; } diff --git a/test/test-udp-mmsg.c b/test/test-udp-mmsg.c index fb241151072..73213c43d97 100644 --- a/test/test-udp-mmsg.c +++ b/test/test-udp-mmsg.c @@ -27,16 +27,17 @@ #include #define CHECK_HANDLE(handle) \ - ASSERT((uv_udp_t*)(handle) == &recver || (uv_udp_t*)(handle) == &sender) + ASSERT_NE((uv_udp_t*)(handle) == &recver || (uv_udp_t*)(handle) == &sender, 0) -#define BUFFER_MULTIPLIER 4 +#define BUFFER_MULTIPLIER 20 #define MAX_DGRAM_SIZE (64 * 1024) -#define NUM_SENDS 8 -#define EXPECTED_MMSG_ALLOCS (NUM_SENDS / BUFFER_MULTIPLIER) +#define NUM_SENDS 40 static uv_udp_t recver; static uv_udp_t sender; static int recv_cb_called; +static int received_datagrams; +static int read_bytes; static int close_cb_called; static int alloc_cb_called; @@ -68,28 +69,35 @@ static void close_cb(uv_handle_t* handle) { static void recv_cb(uv_udp_t* handle, - ssize_t nread, - const uv_buf_t* rcvbuf, - const struct sockaddr* addr, - unsigned flags) { + ssize_t nread, + const uv_buf_t* rcvbuf, + const struct sockaddr* addr, + unsigned flags) { ASSERT_GE(nread, 0); + read_bytes += nread; /* free and return if this is a mmsg free-only callback invocation */ if (flags & UV_UDP_MMSG_FREE) { - ASSERT_EQ(nread, 0); + ASSERT_OK(nread); ASSERT_NULL(addr); free(rcvbuf->base); return; } - ASSERT_EQ(nread, 4); - ASSERT_NOT_NULL(addr); - ASSERT_MEM_EQ("PING", rcvbuf->base, nread); + if (nread == 0) { + /* There can be no more available data for the time being. */ + ASSERT_NULL(addr); + } else { + ASSERT_EQ(4, nread); + ASSERT_NOT_NULL(addr); + ASSERT_MEM_EQ("PING", rcvbuf->base, nread); + received_datagrams++; + } recv_cb_called++; - if (recv_cb_called == NUM_SENDS) { - uv_close((uv_handle_t*)handle, close_cb); - uv_close((uv_handle_t*)&sender, close_cb); + if (received_datagrams == NUM_SENDS) { + uv_close((uv_handle_t*) handle, close_cb); + uv_close((uv_handle_t*) &sender, close_cb); } /* Don't free if the buffer could be reused via mmsg */ @@ -103,40 +111,40 @@ TEST_IMPL(udp_mmsg) { uv_buf_t buf; int i; - ASSERT_EQ(0, uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); - ASSERT_EQ(0, uv_udp_init_ex(uv_default_loop(), &recver, - AF_UNSPEC | UV_UDP_RECVMMSG)); + ASSERT_OK(uv_udp_init_ex(uv_default_loop(), &recver, + AF_UNSPEC | UV_UDP_RECVMMSG)); - ASSERT_EQ(0, uv_udp_bind(&recver, (const struct sockaddr*) &addr, 0)); + ASSERT_OK(uv_udp_bind(&recver, (const struct sockaddr*) &addr, 0)); - ASSERT_EQ(0, uv_udp_recv_start(&recver, alloc_cb, recv_cb)); + ASSERT_OK(uv_udp_recv_start(&recver, alloc_cb, recv_cb)); - ASSERT_EQ(0, uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); - ASSERT_EQ(0, uv_udp_init(uv_default_loop(), &sender)); + ASSERT_OK(uv_udp_init(uv_default_loop(), &sender)); buf = uv_buf_init("PING", 4); for (i = 0; i < NUM_SENDS; i++) { ASSERT_EQ(4, uv_udp_try_send(&sender, &buf, 1, (const struct sockaddr*) &addr)); } - ASSERT_EQ(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT_EQ(close_cb_called, 2); - ASSERT_EQ(recv_cb_called, NUM_SENDS); + ASSERT_EQ(2, close_cb_called); + ASSERT_EQ(received_datagrams, NUM_SENDS); - ASSERT_EQ(sender.send_queue_size, 0); - ASSERT_EQ(recver.send_queue_size, 0); + ASSERT_OK(sender.send_queue_size); + ASSERT_OK(recver.send_queue_size); printf("%d allocs for %d recvs\n", alloc_cb_called, recv_cb_called); /* On platforms that don't support mmsg, each recv gets its own alloc */ if (uv_udp_using_recvmmsg(&recver)) - ASSERT_EQ(alloc_cb_called, EXPECTED_MMSG_ALLOCS); + ASSERT_EQ(read_bytes, NUM_SENDS * 4); /* we're sending 4 bytes per datagram */ else ASSERT_EQ(alloc_cb_called, recv_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-udp-multicast-interface.c b/test/test-udp-multicast-interface.c index bd9a61c98aa..2c558c8b0f1 100644 --- a/test/test-udp-multicast-interface.c +++ b/test/test-udp-multicast-interface.c @@ -27,7 +27,7 @@ #include #define CHECK_HANDLE(handle) \ - ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + ASSERT_NE((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client, 0) static uv_udp_t server; static uv_udp_t client; @@ -65,17 +65,17 @@ TEST_IMPL(udp_multicast_interface) { struct sockaddr_in addr; struct sockaddr_in baddr; - ASSERT(0 == uv_ip4_addr("239.255.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("239.255.0.1", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(0 == uv_ip4_addr("0.0.0.0", 0, &baddr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", 0, &baddr)); r = uv_udp_bind(&server, (const struct sockaddr*)&baddr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_set_multicast_interface(&server, "0.0.0.0"); - ASSERT(r == 0); + ASSERT_OK(r); /* server sends "PING" */ buf = uv_buf_init("PING", 4); @@ -85,20 +85,20 @@ TEST_IMPL(udp_multicast_interface) { 1, (const struct sockaddr*)&addr, sv_send_cb); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(close_cb_called == 0); - ASSERT(sv_send_cb_called == 0); + ASSERT_OK(close_cb_called); + ASSERT_OK(sv_send_cb_called); /* run the loop till all events are processed */ uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(sv_send_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, sv_send_cb_called); + ASSERT_EQ(1, close_cb_called); - ASSERT(client.send_queue_size == 0); - ASSERT(server.send_queue_size == 0); + ASSERT_OK(client.send_queue_size); + ASSERT_OK(server.send_queue_size); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-udp-multicast-interface6.c b/test/test-udp-multicast-interface6.c index be11514c805..54fc87ed4e9 100644 --- a/test/test-udp-multicast-interface6.c +++ b/test/test-udp-multicast-interface6.c @@ -27,7 +27,7 @@ #include #define CHECK_HANDLE(handle) \ - ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + ASSERT_NE((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client, 0) static uv_udp_t server; static uv_udp_t client; @@ -44,7 +44,7 @@ static void close_cb(uv_handle_t* handle) { static void sv_send_cb(uv_udp_send_t* req, int status) { ASSERT_NOT_NULL(req); - ASSERT(status == 0); + ASSERT_OK(status); CHECK_HANDLE(req->handle); sv_send_cb_called++; @@ -68,21 +68,21 @@ TEST_IMPL(udp_multicast_interface6) { if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); - ASSERT(0 == uv_ip6_addr("::1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip6_addr("::1", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(0 == uv_ip6_addr("::", 0, &baddr)); + ASSERT_OK(uv_ip6_addr("::", 0, &baddr)); r = uv_udp_bind(&server, (const struct sockaddr*)&baddr, 0); - ASSERT(r == 0); + ASSERT_OK(r); -#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#if defined(__APPLE__) || defined(__FreeBSD__) r = uv_udp_set_multicast_interface(&server, "::1%lo0"); #else r = uv_udp_set_multicast_interface(&server, NULL); #endif - ASSERT(r == 0); + ASSERT_OK(r); /* server sends "PING" */ buf = uv_buf_init("PING", 4); @@ -92,17 +92,17 @@ TEST_IMPL(udp_multicast_interface6) { 1, (const struct sockaddr*)&addr, sv_send_cb); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(close_cb_called == 0); - ASSERT(sv_send_cb_called == 0); + ASSERT_OK(close_cb_called); + ASSERT_OK(sv_send_cb_called); /* run the loop till all events are processed */ uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(sv_send_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, sv_send_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-udp-multicast-join.c b/test/test-udp-multicast-join.c index 9e603a8455f..58b055561c6 100644 --- a/test/test-udp-multicast-join.c +++ b/test/test-udp-multicast-join.c @@ -27,7 +27,7 @@ #include #define CHECK_HANDLE(handle) \ - ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + ASSERT_NE((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client, 0) #define MULTICAST_ADDR "239.255.0.1" @@ -36,10 +36,9 @@ static uv_udp_t client; static uv_udp_send_t req; static uv_udp_send_t req_ss; +static int darwin_ebusy_errors; static int cl_recv_cb_called; - static int sv_send_cb_called; - static int close_cb_called; static void alloc_cb(uv_handle_t* handle, @@ -47,7 +46,7 @@ static void alloc_cb(uv_handle_t* handle, uv_buf_t* buf) { static char slab[65536]; CHECK_HANDLE(handle); - ASSERT(suggested_size <= sizeof(slab)); + ASSERT_LE(suggested_size, sizeof(slab)); buf->base = slab; buf->len = sizeof(slab); } @@ -61,7 +60,7 @@ static void close_cb(uv_handle_t* handle) { static void sv_send_cb(uv_udp_send_t* req, int status) { ASSERT_NOT_NULL(req); - ASSERT(status == 0); + ASSERT_OK(status); CHECK_HANDLE(req->handle); sv_send_cb_called++; @@ -77,7 +76,7 @@ static int do_send(uv_udp_send_t* send_req) { buf = uv_buf_init("PING", 4); - ASSERT(0 == uv_ip4_addr(MULTICAST_ADDR, TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr(MULTICAST_ADDR, TEST_PORT, &addr)); /* client sends "PING" */ return uv_udp_send(send_req, @@ -95,7 +94,7 @@ static void cl_recv_cb(uv_udp_t* handle, const struct sockaddr* addr, unsigned flags) { CHECK_HANDLE(handle); - ASSERT(flags == 0); + ASSERT_OK(flags); if (nread < 0) { ASSERT(0 && "unexpected error"); @@ -108,7 +107,7 @@ static void cl_recv_cb(uv_udp_t* handle, } ASSERT_NOT_NULL(addr); - ASSERT(nread == 4); + ASSERT_EQ(4, nread); ASSERT(!memcmp("PING", buf->base, nread)); cl_recv_cb_called++; @@ -121,61 +120,80 @@ static void cl_recv_cb(uv_udp_t* handle, char source_addr[64]; r = uv_ip4_name((const struct sockaddr_in*)addr, source_addr, sizeof(source_addr)); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_set_membership(&server, MULTICAST_ADDR, NULL, UV_LEAVE_GROUP); - ASSERT(r == 0); + ASSERT_OK(r); -#if !defined(__OpenBSD__) && !defined(__NetBSD__) +#if !defined(__NetBSD__) r = uv_udp_set_source_membership(&server, MULTICAST_ADDR, NULL, source_addr, UV_JOIN_GROUP); - ASSERT(r == 0); +#if defined(__APPLE__) + if (r == UV_EBUSY) { + uv_close((uv_handle_t*) &server, close_cb); + darwin_ebusy_errors++; + return; + } +#endif + ASSERT_OK(r); #endif r = do_send(&req_ss); - ASSERT(r == 0); + ASSERT_OK(r); } } TEST_IMPL(udp_multicast_join) { +#if defined(__OpenBSD__) + RETURN_SKIP("Test does not currently work in OpenBSD"); +#endif int r; struct sockaddr_in addr; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_init(uv_default_loop(), &client); - ASSERT(r == 0); + ASSERT_OK(r); /* bind to the desired port */ r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); /* join the multicast channel */ r = uv_udp_set_membership(&server, MULTICAST_ADDR, NULL, UV_JOIN_GROUP); if (r == UV_ENODEV) RETURN_SKIP("No multicast support."); - ASSERT(r == 0); + if (r == UV_ENOEXEC) + RETURN_SKIP("No multicast support (likely a firewall issue)."); + ASSERT_OK(r); +#if defined(__ANDROID__) + /* It returns an ENOSYS error */ + RETURN_SKIP("Test does not currently work in ANDROID"); +#endif r = uv_udp_recv_start(&server, alloc_cb, cl_recv_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = do_send(&req); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(close_cb_called == 0); - ASSERT(cl_recv_cb_called == 0); - ASSERT(sv_send_cb_called == 0); + ASSERT_OK(close_cb_called); + ASSERT_OK(cl_recv_cb_called); + ASSERT_OK(sv_send_cb_called); /* run the loop till all events are processed */ uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(cl_recv_cb_called == 2); - ASSERT(sv_send_cb_called == 2); - ASSERT(close_cb_called == 2); + if (darwin_ebusy_errors > 0) + RETURN_SKIP("Unexplained macOS IP_ADD_SOURCE_MEMBERSHIP EBUSY bug"); + + ASSERT_EQ(2, cl_recv_cb_called); + ASSERT_EQ(2, sv_send_cb_called); + ASSERT_EQ(2, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-udp-multicast-join6.c b/test/test-udp-multicast-join6.c index e67c5ee59bb..430e4e3321e 100644 --- a/test/test-udp-multicast-join6.c +++ b/test/test-udp-multicast-join6.c @@ -28,12 +28,12 @@ #define CHECK_HANDLE(handle) \ - ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + ASSERT_NE((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client, 0) #if defined(__APPLE__) || \ defined(_AIX) || \ defined(__MVS__) || \ - defined(__FreeBSD_kernel__) || \ + defined(__FreeBSD__) || \ defined(__NetBSD__) || \ defined(__OpenBSD__) #define MULTICAST_ADDR "ff02::1%lo0" @@ -59,7 +59,7 @@ static void alloc_cb(uv_handle_t* handle, uv_buf_t* buf) { static char slab[65536]; CHECK_HANDLE(handle); - ASSERT(suggested_size <= sizeof(slab)); + ASSERT_LE(suggested_size, sizeof(slab)); buf->base = slab; buf->len = sizeof(slab); } @@ -73,7 +73,7 @@ static void close_cb(uv_handle_t* handle) { static void sv_send_cb(uv_udp_send_t* req, int status) { ASSERT_NOT_NULL(req); - ASSERT(status == 0); + ASSERT_OK(status); CHECK_HANDLE(req->handle); sv_send_cb_called++; @@ -89,7 +89,7 @@ static int do_send(uv_udp_send_t* send_req) { buf = uv_buf_init("PING", 4); - ASSERT(0 == uv_ip6_addr(MULTICAST_ADDR, TEST_PORT, &addr)); + ASSERT_OK(uv_ip6_addr(MULTICAST_ADDR, TEST_PORT, &addr)); /* client sends "PING" */ return uv_udp_send(send_req, @@ -107,7 +107,7 @@ static void cl_recv_cb(uv_udp_t* handle, const struct sockaddr* addr, unsigned flags) { CHECK_HANDLE(handle); - ASSERT(flags == 0); + ASSERT_OK(flags); if (nread < 0) { ASSERT(0 && "unexpected error"); @@ -120,7 +120,7 @@ static void cl_recv_cb(uv_udp_t* handle, } ASSERT_NOT_NULL(addr); - ASSERT(nread == 4); + ASSERT_EQ(4, nread); ASSERT(!memcmp("PING", buf->base, nread)); cl_recv_cb_called++; @@ -133,16 +133,16 @@ static void cl_recv_cb(uv_udp_t* handle, char source_addr[64]; r = uv_ip6_name((const struct sockaddr_in6*)addr, source_addr, sizeof(source_addr)); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_set_membership(&server, MULTICAST_ADDR, INTERFACE_ADDR, UV_LEAVE_GROUP); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_set_source_membership(&server, MULTICAST_ADDR, INTERFACE_ADDR, source_addr, UV_JOIN_GROUP); - ASSERT(r == 0); + ASSERT_OK(r); r = do_send(&req_ss); - ASSERT(r == 0); + ASSERT_OK(r); } } @@ -173,47 +173,52 @@ TEST_IMPL(udp_multicast_join6) { if (!can_ipv6_external()) RETURN_SKIP("No external IPv6 interface available"); - ASSERT(0 == uv_ip6_addr("::", TEST_PORT, &addr)); + ASSERT_OK(uv_ip6_addr("::", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_init(uv_default_loop(), &client); - ASSERT(r == 0); + ASSERT_OK(r); /* bind to the desired port */ r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_set_membership(&server, MULTICAST_ADDR, INTERFACE_ADDR, UV_JOIN_GROUP); if (r == UV_ENODEV) { - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); RETURN_SKIP("No ipv6 multicast route"); } - ASSERT(r == 0); + ASSERT_OK(r); + +#if defined(__ANDROID__) + /* It returns an ENOSYS error */ + RETURN_SKIP("Test does not currently work in ANDROID"); +#endif /* TODO(gengjiawen): Fix test on QEMU. */ #if defined(__QEMU__) RETURN_SKIP("Test does not currently work in QEMU"); #endif r = uv_udp_recv_start(&server, alloc_cb, cl_recv_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = do_send(&req); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(close_cb_called == 0); - ASSERT(cl_recv_cb_called == 0); - ASSERT(sv_send_cb_called == 0); + ASSERT_OK(close_cb_called); + ASSERT_OK(cl_recv_cb_called); + ASSERT_OK(sv_send_cb_called); /* run the loop till all events are processed */ uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(cl_recv_cb_called == 2); - ASSERT(sv_send_cb_called == 2); - ASSERT(close_cb_called == 2); + ASSERT_EQ(2, cl_recv_cb_called); + ASSERT_EQ(2, sv_send_cb_called); + ASSERT_EQ(2, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-udp-multicast-ttl.c b/test/test-udp-multicast-ttl.c index fbddd90914c..50bc54a066f 100644 --- a/test/test-udp-multicast-ttl.c +++ b/test/test-udp-multicast-ttl.c @@ -27,7 +27,7 @@ #include #define CHECK_HANDLE(handle) \ - ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + ASSERT_NE((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client, 0) static uv_udp_t server; static uv_udp_t client; @@ -60,35 +60,35 @@ TEST_IMPL(udp_multicast_ttl) { struct sockaddr_in addr; r = uv_udp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(0 == uv_ip4_addr("0.0.0.0", 0, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", 0, &addr)); r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_set_multicast_ttl(&server, 32); - ASSERT(r == 0); + ASSERT_OK(r); /* server sends "PING" */ buf = uv_buf_init("PING", 4); - ASSERT(0 == uv_ip4_addr("239.255.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("239.255.0.1", TEST_PORT, &addr)); r = uv_udp_send(&req, &server, &buf, 1, (const struct sockaddr*) &addr, sv_send_cb); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(close_cb_called == 0); - ASSERT(sv_send_cb_called == 0); + ASSERT_OK(close_cb_called); + ASSERT_OK(sv_send_cb_called); /* run the loop till all events are processed */ uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(sv_send_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, sv_send_cb_called); + ASSERT_EQ(1, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-udp-open.c b/test/test-udp-open.c index f5136b6d4f2..6fddc93717b 100644 --- a/test/test-udp-open.c +++ b/test/test-udp-open.c @@ -41,7 +41,7 @@ static void startup(void) { #ifdef _WIN32 struct WSAData wsa_data; int r = WSAStartup(MAKEWORD(2, 2), &wsa_data); - ASSERT(r == 0); + ASSERT_OK(r); #endif } @@ -51,9 +51,9 @@ static uv_os_sock_t create_udp_socket(void) { sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); #ifdef _WIN32 - ASSERT(sock != INVALID_SOCKET); + ASSERT_NE(sock, INVALID_SOCKET); #else - ASSERT(sock >= 0); + ASSERT_GE(sock, 0); #endif #ifndef _WIN32 @@ -61,7 +61,7 @@ static uv_os_sock_t create_udp_socket(void) { /* Allow reuse of the port. */ int yes = 1; int r = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes); - ASSERT(r == 0); + ASSERT_OK(r); } #endif @@ -76,7 +76,7 @@ static void close_socket(uv_os_sock_t sock) { #else r = close(sock); #endif - ASSERT(r == 0); + ASSERT_OK(r); } @@ -84,7 +84,7 @@ static void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { static char slab[65536]; - ASSERT(suggested_size <= sizeof(slab)); + ASSERT_LE(suggested_size, sizeof(slab)); buf->base = slab; buf->len = sizeof(slab); } @@ -113,14 +113,14 @@ static void recv_cb(uv_udp_t* handle, return; } - ASSERT(flags == 0); + ASSERT_OK(flags); ASSERT_NOT_NULL(addr); - ASSERT(nread == 4); - ASSERT(memcmp("PING", buf->base, nread) == 0); + ASSERT_EQ(4, nread); + ASSERT_OK(memcmp("PING", buf->base, nread)); r = uv_udp_recv_stop(handle); - ASSERT(r == 0); + ASSERT_OK(r); uv_close((uv_handle_t*) handle, close_cb); } @@ -128,7 +128,7 @@ static void recv_cb(uv_udp_t* handle, static void send_cb(uv_udp_send_t* req, int status) { ASSERT_NOT_NULL(req); - ASSERT(status == 0); + ASSERT_OK(status); send_cb_called++; uv_close((uv_handle_t*)req->handle, close_cb); @@ -142,22 +142,22 @@ TEST_IMPL(udp_open) { uv_os_sock_t sock; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); startup(); sock = create_udp_socket(); r = uv_udp_init(uv_default_loop(), &client); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_open(&client, sock); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_bind(&client, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_recv_start(&client, alloc_cb, recv_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_send(&send_req, &client, @@ -165,15 +165,15 @@ TEST_IMPL(udp_open) { 1, (const struct sockaddr*) &addr, send_cb); - ASSERT(r == 0); + ASSERT_OK(r); #ifndef _WIN32 { r = uv_udp_init(uv_default_loop(), &client2); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_open(&client2, sock); - ASSERT(r == UV_EEXIST); + ASSERT_EQ(r, UV_EEXIST); uv_close((uv_handle_t*) &client2, NULL); } @@ -183,12 +183,12 @@ TEST_IMPL(udp_open) { uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(send_cb_called == 1); - ASSERT(close_cb_called == 1); + ASSERT_EQ(1, send_cb_called); + ASSERT_EQ(1, close_cb_called); - ASSERT(client.send_queue_size == 0); + ASSERT_OK(client.send_queue_size); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -203,19 +203,19 @@ TEST_IMPL(udp_open_twice) { sock2 = create_udp_socket(); r = uv_udp_init(uv_default_loop(), &client); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_open(&client, sock1); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_open(&client, sock2); - ASSERT(r == UV_EBUSY); + ASSERT_EQ(r, UV_EBUSY); close_socket(sock2); uv_close((uv_handle_t*) &client, NULL); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -225,27 +225,27 @@ TEST_IMPL(udp_open_bound) { uv_os_sock_t sock; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); startup(); sock = create_udp_socket(); r = bind(sock, (struct sockaddr*) &addr, sizeof(addr)); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_init(uv_default_loop(), &client); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_open(&client, sock); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_recv_start(&client, alloc_cb, recv_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_close((uv_handle_t*) &client, NULL); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -257,28 +257,28 @@ TEST_IMPL(udp_open_connect) { uv_os_sock_t sock; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); startup(); sock = create_udp_socket(); r = uv_udp_init(uv_default_loop(), &client); - ASSERT(r == 0); + ASSERT_OK(r); r = connect(sock, (const struct sockaddr*) &addr, sizeof(addr)); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_open(&client, sock); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_recv_start(&server, alloc_cb, recv_cb); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_send(&send_req, &client, @@ -286,16 +286,16 @@ TEST_IMPL(udp_open_connect) { 1, NULL, send_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(send_cb_called == 1); - ASSERT(close_cb_called == 2); + ASSERT_EQ(1, send_cb_called); + ASSERT_EQ(2, close_cb_called); - ASSERT(client.send_queue_size == 0); + ASSERT_OK(client.send_queue_size); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } @@ -315,20 +315,20 @@ TEST_IMPL(udp_send_unix) { memset(&addr, 0, sizeof addr); addr.sun_family = AF_UNIX; - ASSERT(strlen(TEST_PIPENAME) < sizeof(addr.sun_path)); + ASSERT_LT(strlen(TEST_PIPENAME), sizeof(addr.sun_path)); memcpy(addr.sun_path, TEST_PIPENAME, strlen(TEST_PIPENAME)); fd = socket(AF_UNIX, SOCK_STREAM, 0); - ASSERT(fd >= 0); + ASSERT_GE(fd, 0); unlink(TEST_PIPENAME); - ASSERT(0 == bind(fd, (const struct sockaddr*)&addr, sizeof addr)); - ASSERT(0 == listen(fd, 1)); + ASSERT_OK(bind(fd, (const struct sockaddr*)&addr, sizeof addr)); + ASSERT_OK(listen(fd, 1)); r = uv_udp_init(loop, &handle); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_open(&handle, fd); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(loop, UV_RUN_DEFAULT); r = uv_udp_send(&req, @@ -337,14 +337,14 @@ TEST_IMPL(udp_send_unix) { 1, (const struct sockaddr*) &addr, NULL); - ASSERT(r == 0); + ASSERT_OK(r); uv_close((uv_handle_t*)&handle, NULL); uv_run(loop, UV_RUN_DEFAULT); close(fd); unlink(TEST_PIPENAME); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } #endif diff --git a/test/test-udp-options.c b/test/test-udp-options.c index 3ea51baf40b..bb4a24ca7d1 100644 --- a/test/test-udp-options.c +++ b/test/test-udp-options.c @@ -36,58 +36,58 @@ static int udp_options_test(const struct sockaddr* addr) { loop = uv_default_loop(); r = uv_udp_init(loop, &h); - ASSERT(r == 0); + ASSERT_OK(r); uv_unref((uv_handle_t*)&h); /* don't keep the loop alive */ r = uv_udp_bind(&h, addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_set_broadcast(&h, 1); r |= uv_udp_set_broadcast(&h, 1); r |= uv_udp_set_broadcast(&h, 0); r |= uv_udp_set_broadcast(&h, 0); - ASSERT(r == 0); + ASSERT_OK(r); /* values 1-255 should work */ for (i = 1; i <= 255; i++) { r = uv_udp_set_ttl(&h, i); #if defined(__MVS__) if (addr->sa_family == AF_INET6) - ASSERT(r == 0); + ASSERT_OK(r); else - ASSERT(r == UV_ENOTSUP); + ASSERT_EQ(r, UV_ENOTSUP); #else - ASSERT(r == 0); + ASSERT_OK(r); #endif } for (i = 0; i < (int) ARRAY_SIZE(invalid_ttls); i++) { r = uv_udp_set_ttl(&h, invalid_ttls[i]); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); } r = uv_udp_set_multicast_loop(&h, 1); r |= uv_udp_set_multicast_loop(&h, 1); r |= uv_udp_set_multicast_loop(&h, 0); r |= uv_udp_set_multicast_loop(&h, 0); - ASSERT(r == 0); + ASSERT_OK(r); /* values 0-255 should work */ for (i = 0; i <= 255; i++) { r = uv_udp_set_multicast_ttl(&h, i); - ASSERT(r == 0); + ASSERT_OK(r); } /* anything >255 should fail */ r = uv_udp_set_multicast_ttl(&h, 256); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); /* don't test ttl=-1, it's a valid value on some platforms */ r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_OK(r); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } @@ -95,7 +95,7 @@ static int udp_options_test(const struct sockaddr* addr) { TEST_IMPL(udp_options) { struct sockaddr_in addr; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); return udp_options_test((const struct sockaddr*) &addr); } @@ -106,7 +106,7 @@ TEST_IMPL(udp_options6) { if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); - ASSERT(0 == uv_ip6_addr("::", TEST_PORT, &addr)); + ASSERT_OK(uv_ip6_addr("::", TEST_PORT, &addr)); return udp_options_test((const struct sockaddr*) &addr); } @@ -119,42 +119,42 @@ TEST_IMPL(udp_no_autobind) { loop = uv_default_loop(); /* Test a lazy initialized socket. */ - ASSERT(0 == uv_udp_init(loop, &h)); - ASSERT(UV_EBADF == uv_udp_set_multicast_ttl(&h, 32)); - ASSERT(UV_EBADF == uv_udp_set_broadcast(&h, 1)); + ASSERT_OK(uv_udp_init(loop, &h)); + ASSERT_EQ(UV_EBADF, uv_udp_set_multicast_ttl(&h, 32)); + ASSERT_EQ(UV_EBADF, uv_udp_set_broadcast(&h, 1)); #if defined(__MVS__) - ASSERT(UV_ENOTSUP == uv_udp_set_ttl(&h, 1)); + ASSERT_EQ(UV_ENOTSUP, uv_udp_set_ttl(&h, 1)); #else - ASSERT(UV_EBADF == uv_udp_set_ttl(&h, 1)); + ASSERT_EQ(UV_EBADF, uv_udp_set_ttl(&h, 1)); #endif - ASSERT(UV_EBADF == uv_udp_set_multicast_loop(&h, 1)); + ASSERT_EQ(UV_EBADF, uv_udp_set_multicast_loop(&h, 1)); /* TODO(gengjiawen): Fix test on QEMU. */ #if defined(__QEMU__) RETURN_SKIP("Test does not currently work in QEMU"); #endif - ASSERT(UV_EBADF == uv_udp_set_multicast_interface(&h, "0.0.0.0")); + ASSERT_EQ(UV_EBADF, uv_udp_set_multicast_interface(&h, "0.0.0.0")); uv_close((uv_handle_t*) &h, NULL); /* Test a non-lazily initialized socket. */ - ASSERT(0 == uv_udp_init_ex(loop, &h2, AF_INET | UV_UDP_RECVMMSG)); - ASSERT(0 == uv_udp_set_multicast_ttl(&h2, 32)); - ASSERT(0 == uv_udp_set_broadcast(&h2, 1)); + ASSERT_OK(uv_udp_init_ex(loop, &h2, AF_INET | UV_UDP_RECVMMSG)); + ASSERT_OK(uv_udp_set_multicast_ttl(&h2, 32)); + ASSERT_OK(uv_udp_set_broadcast(&h2, 1)); #if defined(__MVS__) /* zOS only supports setting ttl for IPv6 sockets. */ - ASSERT(UV_ENOTSUP == uv_udp_set_ttl(&h2, 1)); + ASSERT_EQ(UV_ENOTSUP, uv_udp_set_ttl(&h2, 1)); #else - ASSERT(0 == uv_udp_set_ttl(&h2, 1)); + ASSERT_OK(uv_udp_set_ttl(&h2, 1)); #endif - ASSERT(0 == uv_udp_set_multicast_loop(&h2, 1)); - ASSERT(0 == uv_udp_set_multicast_interface(&h2, "0.0.0.0")); + ASSERT_OK(uv_udp_set_multicast_loop(&h2, 1)); + ASSERT_OK(uv_udp_set_multicast_interface(&h2, "0.0.0.0")); uv_close((uv_handle_t*) &h2, NULL); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-udp-recv-in-a-row.c b/test/test-udp-recv-in-a-row.c new file mode 100644 index 00000000000..0d97e5bf529 --- /dev/null +++ b/test/test-udp-recv-in-a-row.c @@ -0,0 +1,128 @@ +/* Copyright The libuv project and contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include +#include +#include + +static uv_udp_t server; +static uv_udp_t client; +static uv_check_t check_handle; +static uv_buf_t buf; +static struct sockaddr_in addr; +static char send_data[10]; +static int check_cb_called; + +#define N 5 +static int recv_cnt; + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[sizeof(send_data)]; + buf->base = slab; + buf->len = sizeof(slab); +} + +static void sv_recv_cb(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* rcvbuf, + const struct sockaddr* addr, + unsigned flags) { + /* |nread| can be zero when the kernel drops an incoming datagram after + * marking the file descriptor as readable but before libuv has a chance + * to receive it. Libuv still invokes the uv_udp_recv_cb callback to give + * back the memory from the uv_alloc_cb callback. + * + * See https://github.com/libuv/libuv/issues/4219. + */ + recv_cnt++; + if (nread > 0) + ASSERT_EQ(nread, sizeof(send_data)); +} + +static void check_cb(uv_check_t* handle) { + ASSERT_PTR_EQ(&check_handle, handle); + + /** + * sv_recv_cb() is called with nread set to zero to indicate + * there is no more udp packet in the kernel, so the actual + * recv_cnt is up to one larger than N. UDP being what it is, + * packets can get dropped so don't assume an exact count. + */ + ASSERT_GE(recv_cnt, 1); + ASSERT_LE(recv_cnt, N+1); + check_cb_called = 1; + + /* we are done */ + ASSERT_OK(uv_check_stop(handle)); + uv_close((uv_handle_t*) &client, NULL); + uv_close((uv_handle_t*) &check_handle, NULL); + uv_close((uv_handle_t*) &server, NULL); +} + + +TEST_IMPL(udp_recv_in_a_row) { + int i, r; + + ASSERT_OK(uv_check_init(uv_default_loop(), &check_handle)); + ASSERT_OK(uv_check_start(&check_handle, check_cb)); + + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + ASSERT_OK(uv_udp_init(uv_default_loop(), &server)); + ASSERT_OK(uv_udp_bind(&server, (const struct sockaddr*) &addr, 0)); + ASSERT_OK(uv_udp_recv_start(&server, alloc_cb, sv_recv_cb)); + + ASSERT_OK(uv_udp_init(uv_default_loop(), &client)); + + /* send N-1 udp packets */ + buf = uv_buf_init(send_data, sizeof(send_data)); + for (i = 0; i < N - 1; i ++) { + r = uv_udp_try_send(&client, + &buf, + 1, + (const struct sockaddr*) &addr); + ASSERT_EQ(sizeof(send_data), r); + } + + /* send an empty udp packet */ + buf = uv_buf_init(NULL, 0); + r = uv_udp_try_send(&client, + &buf, + 1, + (const struct sockaddr*) &addr); + ASSERT_OK(r); + + /* check_cb() asserts that the N packets can be received + * before it gets called. + */ + + ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT(check_cb_called); + + MAKE_VALGRIND_HAPPY(uv_default_loop()); + return 0; +} diff --git a/test/test-udp-reuseport.c b/test/test-udp-reuseport.c new file mode 100644 index 00000000000..7d4db40806f --- /dev/null +++ b/test/test-udp-reuseport.c @@ -0,0 +1,287 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "task.h" + +#include +#include +#include + +#if !defined(__linux__) && !defined(__FreeBSD__) && \ + !defined(__DragonFly__) && !defined(__sun) && !defined(_AIX73) + +TEST_IMPL(udp_reuseport) { + struct sockaddr_in addr1, addr2, addr3; + uv_loop_t* loop; + uv_udp_t handle1, handle2, handle3; + int r; + + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr1)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT_2, &addr2)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT_3, &addr3)); + + loop = uv_default_loop(); + ASSERT_NOT_NULL(loop); + + r = uv_udp_init(loop, &handle1); + ASSERT_OK(r); + + r = uv_udp_bind(&handle1, (const struct sockaddr*) &addr1, UV_UDP_REUSEADDR); + ASSERT_OK(r); + + r = uv_udp_init(loop, &handle2); + ASSERT_OK(r); + + r = uv_udp_bind(&handle2, (const struct sockaddr*) &addr2, UV_UDP_REUSEPORT); + ASSERT_EQ(r, UV_ENOTSUP); + + r = uv_udp_init(loop, &handle3); + ASSERT_OK(r); + + /* For platforms where SO_REUSEPORTs don't have the capability of + * load balancing, specifying both UV_UDP_REUSEADDR and UV_UDP_REUSEPORT + * in flags will fail, returning an UV_ENOTSUP error. */ + r = uv_udp_bind(&handle3, (const struct sockaddr*) &addr3, + UV_UDP_REUSEADDR | UV_UDP_REUSEPORT); + ASSERT_EQ(r, UV_ENOTSUP); + + MAKE_VALGRIND_HAPPY(loop); + + return 0; +} + +#else + +#define NUM_RECEIVING_THREADS 2 +#define MAX_UDP_DATAGRAMS 10 + +static uv_udp_t udp_send_handles[MAX_UDP_DATAGRAMS]; +static uv_udp_send_t udp_send_requests[MAX_UDP_DATAGRAMS]; + +static uv_sem_t semaphore; + +static uv_mutex_t mutex; +static unsigned int received; + +static unsigned int thread_loop1_recv; +static unsigned int thread_loop2_recv; +static unsigned int sent; + +static uv_loop_t* main_loop; +static uv_loop_t thread_loop1; +static uv_loop_t thread_loop2; +static uv_udp_t thread_handle1; +static uv_udp_t thread_handle2; +static uv_timer_t thread_timer_handle1; +static uv_timer_t thread_timer_handle2; + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + buf->base = malloc(suggested_size); + buf->len = (int) suggested_size; +} + +static void ticktack(uv_timer_t* timer) { + int done = 0; + + ASSERT(timer == &thread_timer_handle1 || timer == &thread_timer_handle2); + + uv_mutex_lock(&mutex); + if (received == MAX_UDP_DATAGRAMS) { + done = 1; + } + uv_mutex_unlock(&mutex); + + if (done) { + uv_close((uv_handle_t*) timer, NULL); + if (timer->loop == &thread_loop1) + uv_close((uv_handle_t*) &thread_handle1, NULL); + if (timer->loop == &thread_loop2) + uv_close((uv_handle_t*) &thread_handle2, NULL); + } +} + +static void on_recv(uv_udp_t* handle, + ssize_t nr, + const uv_buf_t* buf, + const struct sockaddr* addr, + unsigned flags) { + ASSERT_OK(flags); + ASSERT(handle == &thread_handle1 || handle == &thread_handle2); + + ASSERT_GE(nr, 0); + + if (nr == 0) { + ASSERT_NULL(addr); + free(buf->base); + return; + } + + ASSERT_NOT_NULL(addr); + ASSERT_EQ(5, nr); + ASSERT(!memcmp("Hello", buf->base, nr)); + free(buf->base); + + if (handle->loop == &thread_loop1) + thread_loop1_recv++; + + if (handle->loop == &thread_loop2) + thread_loop2_recv++; + + uv_mutex_lock(&mutex); + received++; + uv_mutex_unlock(&mutex); +} + +static void on_send(uv_udp_send_t* req, int status) { + ASSERT_OK(status); + ASSERT_PTR_EQ(req->handle->loop, main_loop); + + if (++sent == MAX_UDP_DATAGRAMS) + uv_close((uv_handle_t*) req->handle, NULL); +} + +static void bind_socket_and_prepare_recv(uv_loop_t* loop, uv_udp_t* handle) { + struct sockaddr_in addr; + int r; + + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_udp_init(loop, handle); + ASSERT_OK(r); + + /* For platforms where SO_REUSEPORTs have the capability of + * load balancing, specifying both UV_UDP_REUSEADDR and + * UV_UDP_REUSEPORT in flags is allowed and SO_REUSEPORT will + * always override the behavior of SO_REUSEADDR. */ + r = uv_udp_bind(handle, (const struct sockaddr*) &addr, + UV_UDP_REUSEADDR | UV_UDP_REUSEPORT); + ASSERT_OK(r); + + r = uv_udp_recv_start(handle, alloc_cb, on_recv); + ASSERT_OK(r); +} + +static void run_event_loop(void* arg) { + int r; + uv_udp_t* handle; + uv_timer_t* timer; + uv_loop_t* loop = (uv_loop_t*) arg; + ASSERT(loop == &thread_loop1 || loop == &thread_loop2); + + if (loop == &thread_loop1) { + handle = &thread_handle1; + timer = &thread_timer_handle1; + } else { + handle = &thread_handle2; + timer = &thread_timer_handle2; + } + + bind_socket_and_prepare_recv(loop, handle); + r = uv_timer_init(loop, timer); + ASSERT_OK(r); + r = uv_timer_start(timer, ticktack, 0, 10); + ASSERT_OK(r); + + /* Notify the main thread to start sending data. */ + uv_sem_post(&semaphore); + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT_OK(r); +} + +TEST_IMPL(udp_reuseport) { + struct sockaddr_in addr; + uv_buf_t buf; + int r; + int i; + + r = uv_mutex_init(&mutex); + ASSERT_OK(r); + + r = uv_sem_init(&semaphore, 0); + ASSERT_OK(r); + + main_loop = uv_default_loop(); + ASSERT_NOT_NULL(main_loop); + + /* Run event loops of receiving sockets in separate threads. */ + uv_loop_init(&thread_loop1); + uv_loop_init(&thread_loop2); + uv_thread_t thread_loop_id1; + uv_thread_t thread_loop_id2; + uv_thread_create(&thread_loop_id1, run_event_loop, &thread_loop1); + uv_thread_create(&thread_loop_id2, run_event_loop, &thread_loop2); + + /* Wait until all threads to poll for receiving datagrams + * before we start to sending. Otherwise the incoming datagrams + * might not be distributed across all receiving threads. */ + for (i = 0; i < NUM_RECEIVING_THREADS; i++) + uv_sem_wait(&semaphore); + /* Now we know all threads are up and entering the uv_run(), + * but we still sleep a little bit just for dual fail-safe. */ + uv_sleep(100); + + /* Start sending datagrams to the peers. */ + buf = uv_buf_init("Hello", 5); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + for (i = 0; i < MAX_UDP_DATAGRAMS; i++) { + r = uv_udp_init(main_loop, &udp_send_handles[i]); + ASSERT_OK(r); + r = uv_udp_send(&udp_send_requests[i], + &udp_send_handles[i], + &buf, + 1, + (const struct sockaddr*) &addr, + on_send); + ASSERT_OK(r); + } + + r = uv_run(main_loop, UV_RUN_DEFAULT); + ASSERT_OK(r); + + /* Wait for all threads to exit. */ + uv_thread_join(&thread_loop_id1); + uv_thread_join(&thread_loop_id2); + + /* Verify if each receiving socket per event loop received datagrams + * and the amount of received datagrams matches the one of sent datagrams. + */ + ASSERT_EQ(received, MAX_UDP_DATAGRAMS); + ASSERT_EQ(sent, MAX_UDP_DATAGRAMS); + ASSERT_GT(thread_loop1_recv, 0); + ASSERT_GT(thread_loop2_recv, 0); + ASSERT_EQ(thread_loop1_recv + thread_loop2_recv, sent); + + /* Clean up. */ + uv_mutex_destroy(&mutex); + + uv_sem_destroy(&semaphore); + + uv_loop_close(&thread_loop1); + uv_loop_close(&thread_loop2); + MAKE_VALGRIND_HAPPY(main_loop); + + return 0; +} + +#endif diff --git a/test/test-udp-send-and-recv.c b/test/test-udp-send-and-recv.c index d60209059b9..b24fe1d0532 100644 --- a/test/test-udp-send-and-recv.c +++ b/test/test-udp-send-and-recv.c @@ -27,7 +27,7 @@ #include #define CHECK_HANDLE(handle) \ - ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + ASSERT_NE((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client, 0) static uv_udp_t server; static uv_udp_t client; @@ -46,7 +46,7 @@ static void alloc_cb(uv_handle_t* handle, uv_buf_t* buf) { static char slab[65536]; CHECK_HANDLE(handle); - ASSERT(suggested_size <= sizeof(slab)); + ASSERT_LE(suggested_size, sizeof(slab)); buf->base = slab; buf->len = sizeof(slab); } @@ -54,7 +54,7 @@ static void alloc_cb(uv_handle_t* handle, static void close_cb(uv_handle_t* handle) { CHECK_HANDLE(handle); - ASSERT(1 == uv_is_closing(handle)); + ASSERT_EQ(1, uv_is_closing(handle)); close_cb_called++; } @@ -65,7 +65,7 @@ static void cl_recv_cb(uv_udp_t* handle, const struct sockaddr* addr, unsigned flags) { CHECK_HANDLE(handle); - ASSERT(flags == 0); + ASSERT_OK(flags); if (nread < 0) { ASSERT(0 && "unexpected error"); @@ -78,7 +78,7 @@ static void cl_recv_cb(uv_udp_t* handle, } ASSERT_NOT_NULL(addr); - ASSERT(nread == 4); + ASSERT_EQ(4, nread); ASSERT(!memcmp("PONG", buf->base, nread)); cl_recv_cb_called++; @@ -91,11 +91,11 @@ static void cl_send_cb(uv_udp_send_t* req, int status) { int r; ASSERT_NOT_NULL(req); - ASSERT(status == 0); + ASSERT_OK(status); CHECK_HANDLE(req->handle); r = uv_udp_recv_start(req->handle, alloc_cb, cl_recv_cb); - ASSERT(r == 0); + ASSERT_OK(r); cl_send_cb_called++; } @@ -103,7 +103,7 @@ static void cl_send_cb(uv_udp_send_t* req, int status) { static void sv_send_cb(uv_udp_send_t* req, int status) { ASSERT_NOT_NULL(req); - ASSERT(status == 0); + ASSERT_OK(status); CHECK_HANDLE(req->handle); uv_close((uv_handle_t*) req->handle, close_cb); @@ -133,10 +133,10 @@ static void sv_recv_cb(uv_udp_t* handle, } CHECK_HANDLE(handle); - ASSERT(flags == 0); + ASSERT_OK(flags); ASSERT_NOT_NULL(addr); - ASSERT(nread == 4); + ASSERT_EQ(4, nread); ASSERT(!memcmp("PING", rcvbuf->base, nread)); /* FIXME? `uv_udp_recv_stop` does what it says: recv_cb is not called @@ -144,14 +144,14 @@ static void sv_recv_cb(uv_udp_t* handle, * either... Not sure I like that but it's consistent with `uv_read_stop`. */ r = uv_udp_recv_stop(handle); - ASSERT(r == 0); + ASSERT_OK(r); req = malloc(sizeof *req); ASSERT_NOT_NULL(req); sndbuf = uv_buf_init("PONG", 4); r = uv_udp_send(req, handle, &sndbuf, 1, addr, sv_send_cb); - ASSERT(r == 0); + ASSERT_OK(r); sv_recv_cb_called++; } @@ -163,21 +163,21 @@ TEST_IMPL(udp_send_and_recv) { uv_buf_t buf; int r; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &client); - ASSERT(r == 0); + ASSERT_OK(r); /* client sends "PING", expects "PONG" */ buf = uv_buf_init("PING", 4); @@ -188,25 +188,25 @@ TEST_IMPL(udp_send_and_recv) { 1, (const struct sockaddr*) &addr, cl_send_cb); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(close_cb_called == 0); - ASSERT(cl_send_cb_called == 0); - ASSERT(cl_recv_cb_called == 0); - ASSERT(sv_send_cb_called == 0); - ASSERT(sv_recv_cb_called == 0); + ASSERT_OK(close_cb_called); + ASSERT_OK(cl_send_cb_called); + ASSERT_OK(cl_recv_cb_called); + ASSERT_OK(sv_send_cb_called); + ASSERT_OK(sv_recv_cb_called); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(cl_send_cb_called == 1); - ASSERT(cl_recv_cb_called == 1); - ASSERT(sv_send_cb_called == 1); - ASSERT(sv_recv_cb_called == 1); - ASSERT(close_cb_called == 2); + ASSERT_EQ(1, cl_send_cb_called); + ASSERT_EQ(1, cl_recv_cb_called); + ASSERT_EQ(1, sv_send_cb_called); + ASSERT_EQ(1, sv_recv_cb_called); + ASSERT_EQ(2, close_cb_called); - ASSERT(client.send_queue_size == 0); - ASSERT(server.send_queue_size == 0); + ASSERT_OK(client.send_queue_size); + ASSERT_OK(server.send_queue_size); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-udp-send-hang-loop.c b/test/test-udp-send-hang-loop.c index 072070b60e5..763bb28bdb0 100644 --- a/test/test-udp-send-hang-loop.c +++ b/test/test-udp-send-hang-loop.c @@ -27,7 +27,7 @@ #include #define CHECK_OBJECT(handle, type, parent) \ - ASSERT((type*)(handle) == &(parent)) + ASSERT_PTR_EQ((type*)(handle), &(parent)) static uv_udp_t client; static uv_idle_t idle_handle; @@ -46,7 +46,7 @@ static void idle_cb(uv_idle_t* handle) { ASSERT_NULL(send_req.handle); CHECK_OBJECT(handle, uv_idle_t, idle_handle); - ASSERT(0 == uv_idle_stop(handle)); + ASSERT_OK(uv_idle_stop(handle)); /* It probably would have stalled by now if it's going to stall at all. */ if (++loop_hang_called > 1000) { @@ -61,7 +61,7 @@ static void idle_cb(uv_idle_t* handle) { 1, (const struct sockaddr*) &addr, send_cb); - ASSERT(r == 0); + ASSERT_OK(r); } @@ -72,28 +72,28 @@ static void send_cb(uv_udp_send_t* req, int status) { CHECK_OBJECT(req, uv_udp_send_t, send_req); req->handle = NULL; - ASSERT(0 == uv_idle_start(&idle_handle, idle_cb)); + ASSERT_OK(uv_idle_start(&idle_handle, idle_cb)); } TEST_IMPL(udp_send_hang_loop) { - ASSERT(0 == uv_idle_init(uv_default_loop(), &idle_handle)); + ASSERT_OK(uv_idle_init(uv_default_loop(), &idle_handle)); - /* 192.0.2.0/8 is "TEST-NET" and reserved for documentation. + /* 192.0.2.0/24 is "TEST-NET" and reserved for documentation. * Good for us, though. Since we want to have something unreachable. */ - ASSERT(0 == uv_ip4_addr("192.0.2.3", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("192.0.2.3", TEST_PORT, &addr)); - ASSERT(0 == uv_udp_init(uv_default_loop(), &client)); + ASSERT_OK(uv_udp_init(uv_default_loop(), &client)); buf = uv_buf_init(send_data, sizeof(send_data)); - ASSERT(0 == uv_idle_start(&idle_handle, idle_cb)); + ASSERT_OK(uv_idle_start(&idle_handle, idle_cb)); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(loop_hang_called > 1000); + ASSERT_GT(loop_hang_called, 1000); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-udp-send-immediate.c b/test/test-udp-send-immediate.c index a1c95d34384..8e7f83ff05b 100644 --- a/test/test-udp-send-immediate.c +++ b/test/test-udp-send-immediate.c @@ -27,7 +27,7 @@ #include #define CHECK_HANDLE(handle) \ - ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + ASSERT_NE((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client, 0) static uv_udp_t server; static uv_udp_t client; @@ -42,7 +42,7 @@ static void alloc_cb(uv_handle_t* handle, uv_buf_t* buf) { static char slab[65536]; CHECK_HANDLE(handle); - ASSERT(suggested_size <= sizeof(slab)); + ASSERT_LE(suggested_size, sizeof(slab)); buf->base = slab; buf->len = sizeof(slab); } @@ -50,14 +50,14 @@ static void alloc_cb(uv_handle_t* handle, static void close_cb(uv_handle_t* handle) { CHECK_HANDLE(handle); - ASSERT(1 == uv_is_closing(handle)); + ASSERT_EQ(1, uv_is_closing(handle)); close_cb_called++; } static void cl_send_cb(uv_udp_send_t* req, int status) { ASSERT_NOT_NULL(req); - ASSERT(status == 0); + ASSERT_OK(status); CHECK_HANDLE(req->handle); cl_send_cb_called++; @@ -80,10 +80,10 @@ static void sv_recv_cb(uv_udp_t* handle, } CHECK_HANDLE(handle); - ASSERT(flags == 0); + ASSERT_OK(flags); ASSERT_NOT_NULL(addr); - ASSERT(nread == 4); + ASSERT_EQ(4, nread); ASSERT(memcmp("PING", rcvbuf->base, nread) == 0 || memcmp("PANG", rcvbuf->base, nread) == 0); @@ -100,21 +100,21 @@ TEST_IMPL(udp_send_immediate) { uv_buf_t buf; int r; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &client); - ASSERT(r == 0); + ASSERT_OK(r); /* client sends "PING", then "PANG" */ buf = uv_buf_init("PING", 4); @@ -125,7 +125,7 @@ TEST_IMPL(udp_send_immediate) { 1, (const struct sockaddr*) &addr, cl_send_cb); - ASSERT(r == 0); + ASSERT_OK(r); buf = uv_buf_init("PANG", 4); @@ -135,14 +135,14 @@ TEST_IMPL(udp_send_immediate) { 1, (const struct sockaddr*) &addr, cl_send_cb); - ASSERT(r == 0); + ASSERT_OK(r); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(cl_send_cb_called == 2); - ASSERT(sv_recv_cb_called == 2); - ASSERT(close_cb_called == 2); + ASSERT_EQ(2, cl_send_cb_called); + ASSERT_EQ(2, sv_recv_cb_called); + ASSERT_EQ(2, close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-udp-send-unreachable.c b/test/test-udp-send-unreachable.c index c67a23b3852..0a2f4a47a97 100644 --- a/test/test-udp-send-unreachable.c +++ b/test/test-udp-send-unreachable.c @@ -27,7 +27,7 @@ #include #define CHECK_HANDLE(handle) \ - ASSERT((uv_udp_t*)(handle) == &client || (uv_udp_t*)(handle) == &client2) + ASSERT_NE((uv_udp_t*)(handle) == &client || (uv_udp_t*)(handle) == &client2, 0) static uv_udp_t client; static uv_udp_t client2; @@ -61,8 +61,8 @@ static void close_cb(uv_handle_t* handle) { static void send_cb(uv_udp_send_t* req, int status) { ASSERT_NOT_NULL(req); - ASSERT(status == 0); - ASSERT_EQ(status, 0); + ASSERT_OK(status); + ASSERT_OK(status); CHECK_HANDLE(req->handle); send_cb_called++; } @@ -115,24 +115,24 @@ TEST_IMPL(udp_send_unreachable) { can_recverr = 1; #endif - ASSERT_EQ(0, uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); - ASSERT_EQ(0, uv_ip4_addr("127.0.0.1", TEST_PORT_2, &addr2)); - ASSERT_EQ(0, uv_ip4_addr("127.0.0.1", TEST_PORT_3, &addr3)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT_2, &addr2)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT_3, &addr3)); r = uv_timer_init( uv_default_loop(), &timer ); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_timer_start( &timer, timer_cb, 1000, 0 ); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_udp_init(uv_default_loop(), &client); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_udp_bind(&client, (const struct sockaddr*) &addr2, 0); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_udp_recv_start(&client, alloc_cb, recv_cb); - ASSERT_EQ(r, 0); + ASSERT_OK(r); /* client sends "PING", then "PANG" */ buf = uv_buf_init("PING", 4); @@ -143,7 +143,7 @@ TEST_IMPL(udp_send_unreachable) { 1, (const struct sockaddr*) &addr, send_cb); - ASSERT_EQ(r, 0); + ASSERT_OK(r); buf = uv_buf_init("PANG", 4); @@ -153,19 +153,19 @@ TEST_IMPL(udp_send_unreachable) { 1, (const struct sockaddr*) &addr, send_cb); - ASSERT_EQ(r, 0); + ASSERT_OK(r); if (can_recverr) { r = uv_udp_init(uv_default_loop(), &client2); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_udp_bind(&client2, (const struct sockaddr*) &addr3, UV_UDP_LINUX_RECVERR); - ASSERT_EQ(r, 0); + ASSERT_OK(r); r = uv_udp_recv_start(&client2, alloc_cb, recv_cb); - ASSERT_EQ(r, 0); + ASSERT_OK(r); /* client sends "PING", then "PANG" */ buf = uv_buf_init("PING", 4); @@ -176,7 +176,7 @@ TEST_IMPL(udp_send_unreachable) { 1, (const struct sockaddr*) &addr, send_cb_recverr); - ASSERT_EQ(r, 0); + ASSERT_OK(r); buf = uv_buf_init("PANG", 4); @@ -186,16 +186,16 @@ TEST_IMPL(udp_send_unreachable) { 1, (const struct sockaddr*) &addr, send_cb_recverr); - ASSERT_EQ(r, 0); + ASSERT_OK(r); } uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT_EQ(send_cb_called, (long)(can_recverr ? 4 : 2)); ASSERT_EQ(recv_cb_called, alloc_cb_called); - ASSERT_EQ(timer_cb_called, 1); + ASSERT_EQ(1, timer_cb_called); ASSERT_EQ(close_cb_called, (long)(can_recverr ? 3 : 2)); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-udp-sendmmsg-error.c b/test/test-udp-sendmmsg-error.c index c8a411b2dee..0b647585e4f 100644 --- a/test/test-udp-sendmmsg-error.c +++ b/test/test-udp-sendmmsg-error.c @@ -55,21 +55,21 @@ TEST_IMPL(udp_sendmmsg_error) { uv_buf_t buf; int i; - ASSERT_EQ(0, uv_udp_init(uv_default_loop(), &client)); - ASSERT_EQ(0, uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); - ASSERT_EQ(0, uv_udp_connect(&client, (const struct sockaddr*)&addr)); + ASSERT_OK(uv_udp_init(uv_default_loop(), &client)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_udp_connect(&client, (const struct sockaddr*)&addr)); buf = uv_buf_init("TEST", 4); for (i = 0; i < DATAGRAMS; ++i) - ASSERT_EQ(0, uv_udp_send(&req[i], &client, &buf, 1, NULL, send_cb)); + ASSERT_OK(uv_udp_send(&req[i], &client, &buf, 1, NULL, send_cb)); uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT_EQ(1, close_cb_called); ASSERT_EQ(DATAGRAMS, send_cb_called); - ASSERT_EQ(0, client.send_queue_size); + ASSERT_OK(client.send_queue_size); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-udp-try-send.c b/test/test-udp-try-send.c index 85caaaca41d..6181fbbbffc 100644 --- a/test/test-udp-try-send.c +++ b/test/test-udp-try-send.c @@ -27,7 +27,7 @@ #include #define CHECK_HANDLE(handle) \ - ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + ASSERT_NE((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client, 0) static uv_udp_t server; static uv_udp_t client; @@ -42,7 +42,7 @@ static void alloc_cb(uv_handle_t* handle, uv_buf_t* buf) { static char slab[65536]; CHECK_HANDLE(handle); - ASSERT(suggested_size <= sizeof(slab)); + ASSERT_LE(suggested_size, sizeof(slab)); buf->base = slab; buf->len = sizeof(slab); } @@ -60,21 +60,25 @@ static void sv_recv_cb(uv_udp_t* handle, const uv_buf_t* rcvbuf, const struct sockaddr* addr, unsigned flags) { - ASSERT(nread > 0); - if (nread == 0) { ASSERT_NULL(addr); return; } - ASSERT(nread == 4); + ASSERT_EQ(4, nread); ASSERT_NOT_NULL(addr); - ASSERT(memcmp("EXIT", rcvbuf->base, nread) == 0); - uv_close((uv_handle_t*) handle, close_cb); - uv_close((uv_handle_t*) &client, close_cb); + if (!memcmp("EXIT", rcvbuf->base, nread)) { + uv_close((uv_handle_t*) handle, close_cb); + uv_close((uv_handle_t*) &client, close_cb); + } else { + ASSERT_MEM_EQ(rcvbuf->base, "HELO", 4); + } sv_recv_cb_called++; + + if (sv_recv_cb_called == 2) + uv_udp_recv_stop(handle); } @@ -84,38 +88,62 @@ TEST_IMPL(udp_try_send) { uv_buf_t buf; int r; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_bind(&server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb); - ASSERT(r == 0); + ASSERT_OK(r); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); r = uv_udp_init(uv_default_loop(), &client); - ASSERT(r == 0); + ASSERT_OK(r); buf = uv_buf_init(buffer, sizeof(buffer)); + + r = uv_udp_try_send(&client, &buf, 0, (const struct sockaddr*) &addr); + ASSERT_EQ(r, UV_EINVAL); + r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &addr); - ASSERT(r == UV_EMSGSIZE); + ASSERT_EQ(r, UV_EMSGSIZE); + + uv_buf_t* bufs[] = {&buf, &buf}; + unsigned int nbufs[] = {1, 1}; + struct sockaddr* addrs[] = { + (struct sockaddr*) &addr, + (struct sockaddr*) &addr, + }; + + ASSERT_EQ(0, sv_recv_cb_called); + + buf = uv_buf_init("HELO", 4); + r = uv_udp_try_send2(&client, 2, bufs, nbufs, addrs, /*flags*/0); + ASSERT_EQ(r, 2); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT_EQ(2, sv_recv_cb_called); + + r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb); + ASSERT_OK(r); buf = uv_buf_init("EXIT", 4); r = uv_udp_try_send(&client, &buf, 1, (const struct sockaddr*) &addr); - ASSERT(r == 4); + ASSERT_EQ(4, r); uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(close_cb_called == 2); - ASSERT(sv_recv_cb_called == 1); + ASSERT_EQ(2, close_cb_called); + ASSERT_EQ(3, sv_recv_cb_called); - ASSERT(client.send_queue_size == 0); - ASSERT(server.send_queue_size == 0); + ASSERT_OK(client.send_queue_size); + ASSERT_OK(server.send_queue_size); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(uv_default_loop()); return 0; } diff --git a/test/test-uname.c b/test/test-uname.c index 105a17fe677..a384e9f8422 100644 --- a/test/test-uname.c +++ b/test/test-uname.c @@ -39,28 +39,28 @@ TEST_IMPL(uname) { /* Verify that NULL is handled properly. */ r = uv_os_uname(NULL); - ASSERT(r == UV_EINVAL); + ASSERT_EQ(r, UV_EINVAL); /* Verify the happy path. */ r = uv_os_uname(&buffer); - ASSERT(r == 0); + ASSERT_OK(r); #ifndef _WIN32 - ASSERT(uname(&buf) != -1); - ASSERT(strcmp(buffer.sysname, buf.sysname) == 0); - ASSERT(strcmp(buffer.version, buf.version) == 0); + ASSERT_NE(uname(&buf), -1); + ASSERT_OK(strcmp(buffer.sysname, buf.sysname)); + ASSERT_OK(strcmp(buffer.version, buf.version)); # ifdef _AIX snprintf(temp, sizeof(temp), "%s.%s", buf.version, buf.release); - ASSERT(strcmp(buffer.release, temp) == 0); + ASSERT_OK(strcmp(buffer.release, temp)); # else - ASSERT(strcmp(buffer.release, buf.release) == 0); + ASSERT_OK(strcmp(buffer.release, buf.release)); # endif /* _AIX */ # if defined(_AIX) || defined(__PASE__) - ASSERT(strcmp(buffer.machine, "ppc64") == 0); + ASSERT_OK(strcmp(buffer.machine, "ppc64")); # else - ASSERT(strcmp(buffer.machine, buf.machine) == 0); + ASSERT_OK(strcmp(buffer.machine, buf.machine)); # endif /* defined(_AIX) || defined(__PASE__) */ #endif /* _WIN32 */ diff --git a/test/test-walk-handles.c b/test/test-walk-handles.c index 4b0ca6ebc55..86fcb04907e 100644 --- a/test/test-walk-handles.c +++ b/test/test-walk-handles.c @@ -31,7 +31,7 @@ static uv_timer_t timer; static void walk_cb(uv_handle_t* handle, void* arg) { - ASSERT(arg == (void*)magic_cookie); + ASSERT_PTR_EQ(arg, (void*)magic_cookie); if (handle == (uv_handle_t*)&timer) { seen_timer_handle++; @@ -42,7 +42,7 @@ static void walk_cb(uv_handle_t* handle, void* arg) { static void timer_cb(uv_timer_t* handle) { - ASSERT(handle == &timer); + ASSERT_PTR_EQ(handle, &timer); uv_walk(handle->loop, walk_cb, magic_cookie); uv_close((uv_handle_t*)handle, NULL); @@ -56,22 +56,22 @@ TEST_IMPL(walk_handles) { loop = uv_default_loop(); r = uv_timer_init(loop, &timer); - ASSERT(r == 0); + ASSERT_OK(r); r = uv_timer_start(&timer, timer_cb, 1, 0); - ASSERT(r == 0); + ASSERT_OK(r); /* Start event loop, expect to see the timer handle in walk_cb. */ - ASSERT(seen_timer_handle == 0); + ASSERT_OK(seen_timer_handle); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); - ASSERT(seen_timer_handle == 1); + ASSERT_OK(r); + ASSERT_EQ(1, seen_timer_handle); /* Loop is finished, walk_cb should not see our timer handle. */ seen_timer_handle = 0; uv_walk(loop, walk_cb, magic_cookie); - ASSERT(seen_timer_handle == 0); + ASSERT_OK(seen_timer_handle); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/test/test-watcher-cross-stop.c b/test/test-watcher-cross-stop.c index b26deb8d88c..8f79abb7b03 100644 --- a/test/test-watcher-cross-stop.c +++ b/test/test-watcher-cross-stop.c @@ -76,22 +76,22 @@ TEST_IMPL(watcher_cross_stop) { TEST_FILE_LIMIT(ARRAY_SIZE(sockets) + 32); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_OK(uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); memset(big_string, 'A', sizeof(big_string)); buf = uv_buf_init(big_string, sizeof(big_string)); for (i = 0; i < ARRAY_SIZE(sockets); i++) { - ASSERT(0 == uv_udp_init(loop, &sockets[i])); - ASSERT(0 == uv_udp_bind(&sockets[i], - (const struct sockaddr*) &addr, - UV_UDP_REUSEADDR)); - ASSERT(0 == uv_udp_recv_start(&sockets[i], alloc_cb, recv_cb)); - ASSERT(0 == uv_udp_send(&reqs[i], - &sockets[i], - &buf, - 1, - (const struct sockaddr*) &addr, - send_cb)); + ASSERT_OK(uv_udp_init(loop, &sockets[i])); + ASSERT_OK(uv_udp_bind(&sockets[i], + (const struct sockaddr*) &addr, + UV_UDP_REUSEADDR)); + ASSERT_OK(uv_udp_recv_start(&sockets[i], alloc_cb, recv_cb)); + ASSERT_OK(uv_udp_send(&reqs[i], + &sockets[i], + &buf, + 1, + (const struct sockaddr*) &addr, + send_cb)); } while (recv_cb_called == 0) @@ -100,13 +100,13 @@ TEST_IMPL(watcher_cross_stop) { for (i = 0; i < ARRAY_SIZE(sockets); i++) uv_close((uv_handle_t*) &sockets[i], close_cb); - ASSERT(recv_cb_called > 0); + ASSERT_GT(recv_cb_called, 0); uv_run(loop, UV_RUN_DEFAULT); - ASSERT(ARRAY_SIZE(sockets) == send_cb_called); - ASSERT(ARRAY_SIZE(sockets) == close_cb_called); + ASSERT_EQ(ARRAY_SIZE(sockets), send_cb_called); + ASSERT_EQ(ARRAY_SIZE(sockets), close_cb_called); - MAKE_VALGRIND_HAPPY(); + MAKE_VALGRIND_HAPPY(loop); return 0; } diff --git a/tsansupp.txt b/tsansupp.txt new file mode 100644 index 00000000000..bde4060803e --- /dev/null +++ b/tsansupp.txt @@ -0,0 +1,2 @@ +# glibc reads `count` field unsynchronized, not a libuv bug +race:pthread_barrier_destroy