From b94a7f6ffcc01af7960df07ea0d370e91339813e Mon Sep 17 00:00:00 2001 From: kangchongtao Date: Wed, 31 Aug 2022 10:11:17 +0800 Subject: [PATCH] =?UTF-8?q?libuv=E5=8D=87=E7=BA=A71.44.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: kangchongtao --- .gitattributes | 15 - .github/ISSUE_TEMPLATE.md | 2 +- .github/stale.yml | 7 +- .github/workflows/CI.yml | 118 +++ .github/workflows/sanitizer.yml | 26 + .gitignore | 6 +- .mailmap | 14 + .readthedocs.yaml | 11 + AUTHORS | 96 +- CMakeLists.txt | 336 +++++-- CONTRIBUTING.md | 11 +- ChangeLog | 750 ++++++++++++++- LICENSE | 4 - LINKS.md | 101 ++ MAINTAINERS.md | 3 + Makefile.am | 63 +- README.md | 237 ++--- SUPPORTED_PLATFORMS.md | 15 +- android-configure-arm | 23 - android-configure-arm64 | 23 - android-configure-x86 | 23 - android-configure-x86_64 | 25 - appveyor.yml | 32 - common.gypi | 213 ---- configure.ac | 28 +- docs/code/.gitignore | 3 + docs/code/default-loop/main.c | 12 + docs/code/dns/main.c | 4 +- docs/code/multi-echo-server/main.c | 2 +- docs/requirements.txt | 42 + docs/src/api.rst | 1 + docs/src/async.rst | 2 +- docs/src/check.rst | 12 +- docs/src/design.rst | 2 +- docs/src/errors.rst | 22 +- docs/src/fs.rst | 29 +- docs/src/guide/basics.rst | 27 +- docs/src/guide/eventloops.rst | 2 + docs/src/guide/filesystem.rst | 41 +- docs/src/guide/introduction.rst | 20 +- docs/src/guide/networking.rst | 13 +- docs/src/guide/processes.rst | 23 +- docs/src/guide/threads.rst | 20 +- docs/src/guide/utilities.rst | 15 +- docs/src/handle.rst | 4 +- docs/src/idle.rst | 12 +- docs/src/index.rst | 6 +- docs/src/loop.rst | 9 +- docs/src/metrics.rst | 27 + docs/src/misc.rst | 109 ++- docs/src/pipe.rst | 18 + docs/src/poll.rst | 61 +- docs/src/prepare.rst | 12 +- docs/src/process.rst | 75 +- docs/src/request.rst | 11 +- docs/src/sphinx-plugins/manpage.py | 2 +- docs/src/static/diagrams.key/Index.zip | Bin 130 -> 71160 bytes docs/src/stream.rst | 25 +- docs/src/tcp.rst | 24 +- docs/src/timer.rst | 7 + docs/src/tty.rst | 5 +- docs/src/udp.rst | 81 +- gyp_uv.py | 73 -- include/uv.h | 106 +- include/uv/android-ifaddrs.h | 54 -- include/uv/errno.h | 14 +- include/uv/tree.h | 2 +- include/uv/unix.h | 4 +- include/uv/version.h | 6 +- include/uv/win.h | 32 +- libuv-static.pc.in | 12 + m4/.gitignore | 5 + m4/as_case.m4 | 21 + m4/ax_pthread.m4 | 485 ++++++++++ m4/libuv-check-flags.m4 | 320 ++++++ src/fs-poll.c | 2 +- src/idna.c | 50 +- src/inet.c | 3 +- src/random.c | 2 +- src/strscpy.c | 21 + src/strscpy.h | 23 +- src/threadpool.c | 11 +- src/timer.c | 16 +- src/unix/aix-common.c | 110 +-- src/unix/aix.c | 72 +- src/unix/android-ifaddrs.c | 712 -------------- src/unix/async.c | 35 +- src/unix/atomic-ops.h | 6 +- src/unix/bsd-ifaddrs.c | 18 +- src/unix/bsd-proctitle.c | 6 + src/unix/core.c | 303 ++++-- src/unix/darwin-proctitle.c | 3 +- src/unix/darwin-stub.h | 113 +++ src/unix/darwin.c | 176 +++- src/unix/dl.c | 2 +- src/unix/epoll.c | 422 ++++++++ src/unix/freebsd.c | 67 +- src/unix/fs.c | 370 +++++-- src/unix/fsevents.c | 77 +- src/unix/getaddrinfo.c | 3 - src/unix/hurd.c | 167 ++++ src/unix/ibmi.c | 86 +- src/unix/internal.h | 72 +- src/unix/kqueue.c | 81 +- src/unix/linux-core.c | 601 ++++-------- src/unix/linux-inotify.c | 2 +- src/unix/linux-syscalls.c | 125 ++- src/unix/linux-syscalls.h | 10 +- src/unix/loop.c | 36 +- src/unix/no-proctitle.c | 3 + src/unix/openbsd.c | 6 +- src/unix/os390-proctitle.c | 136 +++ src/unix/os390-syscalls.c | 116 +-- src/unix/os390-syscalls.h | 9 +- src/unix/os390.c | 248 ++--- src/unix/pipe.c | 56 +- src/unix/poll.c | 14 +- src/unix/posix-poll.c | 42 +- src/unix/process.c | 910 ++++++++++++++---- src/unix/proctitle.c | 14 +- src/unix/pthread-fixes.c | 6 +- src/unix/qnx.c | 137 +++ src/unix/signal.c | 8 +- src/unix/stream.c | 263 +++-- src/unix/sunos.c | 48 +- src/unix/tcp.c | 60 +- src/unix/thread.c | 108 ++- src/unix/tty.c | 53 +- src/unix/udp.c | 243 +++-- src/uv-common.c | 158 +++ src/uv-common.h | 56 +- src/uv-data-getter-setters.c | 21 + src/win/async.c | 10 +- src/win/atomicops-inl.h | 8 +- src/win/core.c | 182 +++- src/win/detect-wakeup.c | 21 + src/win/error.c | 5 +- src/win/fs-event.c | 73 +- src/win/fs-fd-hash-inl.h | 24 +- src/win/fs.c | 451 ++++++--- src/win/handle-inl.h | 26 +- src/win/handle.c | 26 +- src/win/internal.h | 144 ++- src/win/loop-watcher.c | 4 +- src/win/pipe.c | 388 ++++++-- src/win/poll.c | 210 ++-- src/win/process-stdio.c | 96 -- src/win/process.c | 35 +- src/win/req-inl.h | 34 +- src/win/signal.c | 17 +- src/win/stream-inl.h | 8 +- src/win/stream.c | 45 +- src/win/tcp.c | 445 ++++++--- src/win/thread.c | 107 +- src/win/tty.c | 176 ++-- src/win/udp.c | 142 ++- src/win/util.c | 360 +++---- src/win/winapi.c | 24 +- src/win/winapi.h | 34 +- src/win/winsock.c | 28 +- test/benchmark-async-pummel.c | 2 +- test/benchmark-async.c | 2 +- test/benchmark-list.h | 11 +- test/benchmark-million-async.c | 2 +- test/benchmark-million-timers.c | 2 +- test/benchmark-multi-accept.c | 8 +- test/benchmark-ping-udp.c | 8 +- test/benchmark-pound.c | 8 +- test/benchmark-pump.c | 2 + test/benchmark-queue-work.c | 68 ++ test/benchmark-tcp-write-batch.c | 4 +- test/benchmark-udp-pummel.c | 14 +- test/blackhole-server.c | 3 +- test/dns-server.c | 340 ------- test/echo-server.c | 64 +- test/run-benchmarks.c | 15 +- test/run-tests.c | 59 +- test/runner-unix.c | 20 +- test/runner-win.c | 4 +- test/runner.c | 42 +- test/runner.h | 6 +- test/task.h | 142 ++- test/test-active.c | 2 +- test/test-async.c | 2 +- test/test-callback-stack.c | 2 +- test/test-close-fd.c | 34 +- test/test-close-order.c | 2 +- test/test-connection-fail.c | 10 + test/test-cwd-and-chdir.c | 3 +- test/test-default-loop-close.c | 4 +- test/test-delayed-accept.c | 18 +- test/test-dlerror.c | 20 +- test/test-env-vars.c | 24 + test/test-error.c | 11 +- test/test-fs-copyfile.c | 7 +- test/test-fs-event.c | 57 +- test/test-fs-open-flags.c | 44 +- test/test-fs-poll.c | 4 +- test/test-fs-readdir.c | 16 +- test/test-fs.c | 394 ++++++-- test/test-get-currentexe.c | 40 +- test/test-get-passwd.c | 35 +- test/test-getaddrinfo.c | 32 +- test/test-gethostname.c | 2 +- test/test-getnameinfo.c | 21 +- test/test-getsockname.c | 2 +- test/test-getters-setters.c | 35 +- test/test-homedir.c | 2 +- test/test-idna.c | 19 + test/test-ip-name.c | 65 ++ test/test-ip6-addr.c | 6 +- test/test-ipc-heavy-traffic-deadlock-bug.c | 2 +- test/test-ipc-send-recv.c | 12 +- test/test-ipc.c | 429 +++------ test/test-list.h | 94 +- test/test-loop-handles.c | 4 +- test/test-loop-time.c | 24 +- test/test-metrics.c | 135 +++ test/test-multiple-listen.c | 6 +- ...-not-readable-nor-writable-on-read-error.c | 104 ++ test/test-not-writable-after-shutdown.c | 69 ++ test/test-ping-pong.c | 240 ++++- test/test-pipe-bind-error.c | 2 +- test/test-pipe-connect-error.c | 2 +- test/test-pipe-connect-prepare.c | 2 +- test/test-pipe-getsockname.c | 10 +- test/test-pipe-server-close.c | 2 +- test/test-pipe-set-non-blocking.c | 81 +- test/test-platform-output.c | 11 + test/test-poll-multiple-handles.c | 99 ++ test/test-poll.c | 17 +- test/test-process-title-threadsafe.c | 32 +- test/test-readable-on-eof.c | 111 +++ test/test-ref.c | 2 +- test/test-shutdown-eof.c | 8 +- test/test-shutdown-simultaneous.c | 135 +++ test/test-signal-multiple-loops.c | 7 +- test/test-signal-pending-on-close.c | 4 +- test/test-spawn.c | 259 ++--- test/test-tcp-alloc-cb-fail.c | 2 +- test/test-tcp-bind-error.c | 51 +- test/test-tcp-bind6-error.c | 2 +- test/test-tcp-close.c | 2 +- test/test-tcp-connect-error.c | 4 +- test/test-tcp-connect-timeout.c | 105 ++ test/test-tcp-connect6-error.c | 4 +- test/test-tcp-create-socket-early.c | 2 +- test/test-tcp-open.c | 17 +- test/test-tcp-read-stop-start.c | 136 +++ test/test-tcp-write-after-connect.c | 5 + test/test-tcp-write-fail.c | 4 +- test/test-tcp-write-to-half-open-connection.c | 2 +- test/test-tcp-writealot.c | 8 +- test/test-test-macros.c | 42 + test/test-thread-equal.c | 6 + test/test-thread.c | 20 +- test/test-threadpool-cancel.c | 6 +- test/test-timer-again.c | 2 +- test/test-timer.c | 12 +- test/test-tmpdir.c | 2 +- test/test-tty-duplicate-key.c | 2 +- test/test-tty-escape-sequence-processing.c | 20 +- test/test-tty.c | 10 + test/test-udp-alloc-cb-fail.c | 10 +- test/test-udp-connect.c | 15 +- test/test-udp-connect6.c | 200 ++++ test/test-udp-mmsg.c | 149 +++ test/test-udp-multicast-interface.c | 7 +- test/test-udp-multicast-interface6.c | 7 +- test/test-udp-multicast-join.c | 6 +- test/test-udp-multicast-join6.c | 10 +- test/test-udp-multicast-ttl.c | 2 +- test/test-udp-open.c | 16 +- test/test-udp-options.c | 6 +- test/test-udp-send-and-recv.c | 14 +- test/test-udp-send-hang-loop.c | 4 +- test/test-udp-send-immediate.c | 6 +- test/test-udp-send-unreachable.c | 93 +- test/test-udp-sendmmsg-error.c | 75 ++ test/test-udp-try-send.c | 4 +- test/test.gyp | 295 ------ tools/make_dist_html.py | 8 +- uv.gyp | 368 ------- uv_win_longpath.manifest | 8 + vcbuild.bat | 184 ---- 285 files changed, 12545 insertions(+), 6787 deletions(-) create mode 100644 .github/workflows/CI.yml create mode 100644 .github/workflows/sanitizer.yml create mode 100644 .readthedocs.yaml create mode 100644 LINKS.md delete mode 100644 android-configure-arm delete mode 100644 android-configure-arm64 delete mode 100644 android-configure-x86 delete mode 100644 android-configure-x86_64 delete mode 100644 appveyor.yml delete mode 100644 common.gypi create mode 100644 docs/code/.gitignore create mode 100644 docs/code/default-loop/main.c create mode 100644 docs/requirements.txt create mode 100644 docs/src/metrics.rst delete mode 100644 gyp_uv.py delete mode 100644 include/uv/android-ifaddrs.h create mode 100644 libuv-static.pc.in create mode 100644 m4/.gitignore create mode 100644 m4/as_case.m4 create mode 100644 m4/ax_pthread.m4 create mode 100644 m4/libuv-check-flags.m4 delete mode 100644 src/unix/android-ifaddrs.c create mode 100644 src/unix/darwin-stub.h create mode 100644 src/unix/epoll.c create mode 100644 src/unix/hurd.c create mode 100644 src/unix/os390-proctitle.c create mode 100644 src/unix/qnx.c create mode 100644 test/benchmark-queue-work.c delete mode 100644 test/dns-server.c create mode 100644 test/test-ip-name.c create mode 100644 test/test-metrics.c create mode 100644 test/test-not-readable-nor-writable-on-read-error.c create mode 100644 test/test-not-writable-after-shutdown.c create mode 100644 test/test-poll-multiple-handles.c create mode 100644 test/test-readable-on-eof.c create mode 100644 test/test-shutdown-simultaneous.c create mode 100644 test/test-tcp-read-stop-start.c create mode 100644 test/test-test-macros.c create mode 100644 test/test-udp-connect6.c create mode 100644 test/test-udp-mmsg.c create mode 100644 test/test-udp-sendmmsg-error.c delete mode 100644 test/test.gyp delete mode 100644 uv.gyp create mode 100644 uv_win_longpath.manifest delete mode 100644 vcbuild.bat diff --git a/.gitattributes b/.gitattributes index bdad9ff..89297cb 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,16 +1 @@ test/fixtures/lorem_ipsum.txt text eol=lf -*.tgz filter=lfs diff=lfs merge=lfs -text -*.trp filter=lfs diff=lfs merge=lfs -text -*.apk filter=lfs diff=lfs merge=lfs -text -*.jar filter=lfs diff=lfs merge=lfs -text -*.mp4 filter=lfs diff=lfs merge=lfs -text -*.zip filter=lfs diff=lfs merge=lfs -text -*.asm filter=lfs diff=lfs merge=lfs -text -*.8svn filter=lfs diff=lfs merge=lfs -text -*.9svn filter=lfs diff=lfs merge=lfs -text -*.dylib filter=lfs diff=lfs merge=lfs -text -*.exe filter=lfs diff=lfs merge=lfs -text -*.a filter=lfs diff=lfs merge=lfs -text -*.so filter=lfs diff=lfs merge=lfs -text -*.bin filter=lfs diff=lfs merge=lfs -text -*.dll filter=lfs diff=lfs merge=lfs -text diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 4253879..43934de 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -2,7 +2,7 @@ If you want to report a bug, you are in the right place! If you need help or have a question, go here: -https://github.com/libuv/help/issues/new +https://github.com/libuv/libuv/discussions If you are reporting a libuv test failure, please ensure that you are not running the test as root. diff --git a/.github/stale.yml b/.github/stale.yml index d0ba3a4..6f2fb66 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,9 +1,9 @@ # Number of days of inactivity before an issue becomes stale -daysUntilStale: 21 +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: 120 +daysUntilClose: false # Issues with these labels will never be considered stale exemptLabels: - v2 @@ -18,7 +18,6 @@ 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. It will be closed if no further activity occurs. Thank you - for your contributions. + 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.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..19fc0cf --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,118 @@ +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-24 .. + $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 new file mode 100644 index 0000000..c0a54b2 --- /dev/null +++ b/.github/workflows/sanitizer.yml @@ -0,0 +1,26 @@ +name: Sanitizer checks + +on: [push, pull_request] + +jobs: + sanitizers: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup + run: | + sudo apt-get install ninja-build + - name: Envinfo + run: npx envinfo + - name: TSAN + 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 + run: | + mkdir build-asan + (cd build-asan && cmake .. -G Ninja -DBUILD_TESTING=ON -DASAN=ON -DCMAKE_BUILD_TYPE=Debug) + cmake --build build-asan + ./build-asan/uv_run_tests_a diff --git a/.gitignore b/.gitignore index b6abc2a..7eb4932 100644 --- a/.gitignore +++ b/.gitignore @@ -37,11 +37,6 @@ vgcore.* Makefile Makefile.in -# Generated by gyp for android -*.target.mk -/android-toolchain - -/out/ /build/ /test/.libs/ @@ -72,6 +67,7 @@ ipch # Clion / IntelliJ project files /.idea/ +cmake-build-debug/ *.xcodeproj *.xcworkspace diff --git a/.mailmap b/.mailmap index 87d63be..b23377c 100644 --- a/.mailmap +++ b/.mailmap @@ -1,7 +1,9 @@ A. Hauptmann +AJ Heller Aaron Bieber Alan Gutierrez Andrius Bentkus +Andy Fiddaman Bert Belder Bert Belder Bert Belder @@ -10,6 +12,9 @@ Brian White Brian White Caleb James DeLisle Christoph Iserlohn +Darshan Sen +Darshan Sen +David Carlier Devchandra Meetei Leishangthem Fedor Indutny Frank Denis @@ -17,6 +22,8 @@ Imran Iqbal Isaac Z. Schlueter Jason Williams Jesse Gorzinski +Jesse Gorzinski +Juan José Arboleda Justin Venus Keno Fischer Keno Fischer @@ -26,10 +33,12 @@ Maciej Małecki Marc Schlaich Michael Michael Neumann +Michael Penick Nicholas Vavilov Nick Logan Rasmus Christian Pedersen Rasmus Christian Pedersen +Richard Lau Robert Mustacchi Ryan Dahl Ryan Emery @@ -40,11 +49,16 @@ Santiago Gimeno Saúl Ibarra Corretgé Saúl Ibarra Corretgé Shigeki Ohtsu +Shuowang (Wayne) Zhang +TK-one Timothy J. Fontaine Yasuhiro Matsumoto Yazhong Liu Yuki Okumura +cjihrig +gengjiawen jBarz jBarz ptlomholt +tjarlama <59913901+tjarlama@users.noreply.github.com> zlargon diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..e53b9f3 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,11 @@ +version: 2 + +sphinx: + builder: html + configuration: null + fail_on_warning: false + +python: + version: 3.8 + install: + - requirements: docs/requirements.txt diff --git a/AUTHORS b/AUTHORS index 491eded..c1a98db 100644 --- a/AUTHORS +++ b/AUTHORS @@ -114,7 +114,6 @@ Dylan Cali Austin Foxley Benjamin Saunders Geoffry Song -Rasmus Christian Pedersen William Light Oleg Efimov Lars Gierth @@ -123,7 +122,6 @@ Justin Venus Kristian Evensen Linus Mårtensson Navaneeth Kedaram Nambiathan -Yorkie StarWing thierry-FreeBSD Isaiah Norton @@ -212,7 +210,7 @@ guworks RossBencina Roger A. Light chenttuuvv -Richard Lau +Richard Lau ronkorving Corbin Simpson Zachary Hamm @@ -418,3 +416,95 @@ Dominique Dumont Manuel BACHMANN Marek Vavrusa TK-one +Irek Fakhrutdinov +Lin Zhang +毛毛 +Sk Sajidul Kadir +twosee +Rikard Falkeborn +Yash Ladha +James Ross +Colin Finck +Shohei YOSHIDA +Philip Chimento +Michal Artazov +Jeroen Roovers +MasterDuke17 +Alexander Tokmakov +Arenoros +lander0s +Turbinya +OleksandrKvl +Carter Li +Juan Sebastian velez Posada +escherstair +Evan Lucas +tjarlama <59913901+tjarlama@users.noreply.github.com> +司徒玟琅 +YuMeiJie +Aleksej Lebedev +Nikolay Mitev +Ulrik Strid +Elad Lahav +Elad Nachmias +Darshan Sen +Simon Kadisch +Momtchil Momtchev +Ethel Weston <66453757+ethelweston@users.noreply.github.com> +Drew DeVault +Mark Klein +schamberg97 <50446906+schamberg97@users.noreply.github.com> +Bob Weinand +Issam E. Maghni +Juan Pablo Canepa +Shuowang (Wayne) Zhang +Ondřej Surý +Juan José Arboleda +Zhao Zhili +Brandon Cheng +Matvii Hodovaniuk +Hayden +yiyuaner +bbara +SeverinLeonhardt +Andy Fiddaman +Romain Roffé +Eagle Liang +Ricky Zhou +Simon Kissane +James M Snell +Ali Mohammad Pur +Erkhes N <71805796+rexes-ND@users.noreply.github.com> +Joshua M. Clulow +Guilherme Íscaro +Martin Storsjö +Claes Nästén +Mohamed Edrah <43171151+MSE99@users.noreply.github.com> +Supragya Raj +Ikko Ashimine +Sylvain Corlay +earnal +YAKSH BARIYA +Ofek Lev +~locpyl-tidnyd <81016946+locpyl-tidnyd@users.noreply.github.com> +Evan Miller +Petr Menšík +Nicolas Noble +AJ Heller +Stacey Marshall +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> diff --git a/CMakeLists.txt b/CMakeLists.txt index 03d889c..ac52412 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,3 @@ -# TODO: determine CMAKE_SYSTEM_NAME on OS/390. Currently assumes "OS/390". cmake_minimum_required(VERSION 3.4) project(libuv LANGUAGES C) @@ -25,6 +24,33 @@ cmake_dependent_option(LIBUV_BUILD_BENCH "Build the benchmarks when building unit tests and we are the root project" ON "LIBUV_BUILD_TESTS" OFF) +# Qemu Build +option(QEMU "build for qemu" OFF) +if(QEMU) + add_definitions(-D__QEMU__=1) +endif() + +option(ASAN "Enable AddressSanitizer (ASan)" OFF) +option(TSAN "Enable ThreadSanitizer (TSan)" 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.") +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") +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") +endif() + # Compiler check string(CONCAT is-msvc $, @@ -32,6 +58,18 @@ string(CONCAT is-msvc $) check_c_compiler_flag(/W4 UV_LINT_W4) +check_c_compiler_flag(/wd4100 UV_LINT_NO_UNUSED_PARAMETER_MSVC) +check_c_compiler_flag(/wd4127 UV_LINT_NO_CONDITIONAL_CONSTANT_MSVC) +check_c_compiler_flag(/wd4201 UV_LINT_NO_NONSTANDARD_MSVC) +check_c_compiler_flag(/wd4206 UV_LINT_NO_NONSTANDARD_EMPTY_TU_MSVC) +check_c_compiler_flag(/wd4210 UV_LINT_NO_NONSTANDARD_FILE_SCOPE_MSVC) +check_c_compiler_flag(/wd4232 UV_LINT_NO_NONSTANDARD_NONSTATIC_DLIMPORT_MSVC) +check_c_compiler_flag(/wd4456 UV_LINT_NO_HIDES_LOCAL) +check_c_compiler_flag(/wd4457 UV_LINT_NO_HIDES_PARAM) +check_c_compiler_flag(/wd4459 UV_LINT_NO_HIDES_GLOBAL) +check_c_compiler_flag(/wd4706 UV_LINT_NO_CONDITIONAL_ASSIGNMENT_MSVC) +check_c_compiler_flag(/wd4996 UV_LINT_NO_UNSAFE_MSVC) + check_c_compiler_flag(-Wall UV_LINT_WALL) # DO NOT use this under MSVC # TODO: Place these into its own function @@ -39,18 +77,47 @@ check_c_compiler_flag(-Wno-unused-parameter UV_LINT_NO_UNUSED_PARAMETER) check_c_compiler_flag(-Wstrict-prototypes UV_LINT_STRICT_PROTOTYPES) check_c_compiler_flag(-Wextra UV_LINT_EXTRA) -set(lint-no-unused-parameter $<$:-Wno-unused-parameter>) +check_c_compiler_flag(/utf-8 UV_LINT_UTF8_MSVC) + +set(lint-no-unused-parameter $<$:-Wno-unused-parameter>) set(lint-strict-prototypes $<$:-Wstrict-prototypes>) set(lint-extra $<$:-Wextra>) set(lint-w4 $<$:/W4>) +set(lint-no-unused-parameter-msvc $<$:/wd4100>) +set(lint-no-conditional-constant-msvc $<$:/wd4127>) +set(lint-no-nonstandard-msvc $<$:/wd4201>) +set(lint-no-nonstandard-empty-tu-msvc $<$:/wd4206>) +set(lint-no-nonstandard-file-scope-msvc $<$:/wd4210>) +set(lint-no-nonstandard-nonstatic-dlimport-msvc $<$:/wd4232>) +set(lint-no-hides-local-msvc $<$:/wd4456>) +set(lint-no-hides-param-msvc $<$:/wd4457>) +set(lint-no-hides-global-msvc $<$:/wd4459>) +set(lint-no-conditional-assignment-msvc $<$:/wd4706>) +set(lint-no-unsafe-msvc $<$:/wd4996>) # Unfortunately, this one is complicated because MSVC and clang-cl support -Wall # but using it is like calling -Weverything string(CONCAT lint-default $< $,$>:-Wall >) +set(lint-utf8-msvc $<$:/utf-8>) list(APPEND uv_cflags ${lint-strict-prototypes} ${lint-extra} ${lint-default} ${lint-w4}) list(APPEND uv_cflags ${lint-no-unused-parameter}) +list(APPEND uv_cflags ${lint-no-unused-parameter-msvc}) +list(APPEND uv_cflags ${lint-no-conditional-constant-msvc}) +list(APPEND uv_cflags ${lint-no-nonstandard-msvc}) +list(APPEND uv_cflags ${lint-no-nonstandard-empty-tu-msvc}) +list(APPEND uv_cflags ${lint-no-nonstandard-file-scope-msvc}) +list(APPEND uv_cflags ${lint-no-nonstandard-nonstatic-dlimport-msvc}) +list(APPEND uv_cflags ${lint-no-hides-local-msvc}) +list(APPEND uv_cflags ${lint-no-hides-param-msvc}) +list(APPEND uv_cflags ${lint-no-hides-global-msvc}) +list(APPEND uv_cflags ${lint-no-conditional-assignment-msvc}) +list(APPEND uv_cflags ${lint-no-unsafe-msvc}) +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>) set(uv_sources src/fs-poll.c @@ -65,22 +132,11 @@ set(uv_sources src/version.c) if(WIN32) - if (CMAKE_SYSTEM_VERSION VERSION_GREATER 10) # Windows 10 - set(windows-version 0x0A00) - elseif (CMAKE_SYSTEM_VERSION VERSION_GREATER 6.3) # Windows 8.1 - set(windows-version 0x0603) - elseif (CMAKE_SYSTEM_VERSION VERSION_GREATER 6.2) # Windows 8 - set(windows-version 0x0602) - elseif (CMAKE_SYSTEM_VERSION VERSION_GREATER 6.1) # Windows 7 - set(windows-version 0x0601) - elseif (CMAKE_SYSTEM_VERSION VERSION_GREATER 6.0) # Windows Vista - set(windows-version 0x0600) - else() - message(FATAL_ERROR "Windows Vista is the minimum version supported") - endif() - list(APPEND uv_defines WIN32_LEAN_AND_MEAN _WIN32_WINNT=${windows-version}) + list(APPEND uv_defines WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0602) list(APPEND uv_libraries - $<$:psapi> + psapi + user32 + advapi32 iphlpapi userenv ws2_32) @@ -114,7 +170,7 @@ if(WIN32) list(APPEND uv_test_sources src/win/snprintf.c test/runner-win.c) else() list(APPEND uv_defines _FILE_OFFSET_BITS=64 _LARGEFILE_SOURCE) - if(NOT CMAKE_SYSTEM_NAME STREQUAL "Android") + if(NOT CMAKE_SYSTEM_NAME MATCHES "Android|OS390|QNX") # TODO: This should be replaced with find_package(Threads) if possible # Android has pthread as part of its c library, not as a separate # libpthread.so. @@ -147,15 +203,18 @@ if(CMAKE_SYSTEM_NAME STREQUAL "AIX") _ALL_SOURCE _LINUX_SOURCE_COMPAT _THREAD_SAFE - _XOPEN_SOURCE=500) + _XOPEN_SOURCE=500 + HAVE_SYS_AHAFS_EVPRODS_H) list(APPEND uv_libraries perfstat) - list(APPEND uv_sources src/unix/aix.c) + list(APPEND uv_sources + src/unix/aix.c + src/unix/aix-common.c) endif() if(CMAKE_SYSTEM_NAME STREQUAL "Android") - list(APPEND uv_libs dl) + 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 @@ -164,10 +223,10 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Android") src/unix/random-getentropy.c src/unix/random-getrandom.c src/unix/random-sysctl-linux.c - src/unix/sysinfo-loadavg.c) + src/unix/epoll.c) endif() -if(APPLE OR CMAKE_SYSTEM_NAME MATCHES "Android|Linux|OS/390") +if(APPLE OR CMAKE_SYSTEM_NAME MATCHES "Android|Linux") list(APPEND uv_sources src/unix/proctitle.c) endif() @@ -177,7 +236,6 @@ endif() if(CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD|NetBSD|OpenBSD") list(APPEND uv_sources src/unix/posix-hrtime.c src/unix/bsd-proctitle.c) - list(APPEND uv_libraries kvm) endif() if(APPLE OR CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD|NetBSD|OpenBSD") @@ -200,6 +258,17 @@ if(APPLE) src/unix/fsevents.c) endif() +if(CMAKE_SYSTEM_NAME STREQUAL "GNU") + 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) @@ -210,21 +279,25 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") src/unix/procfs-exepath.c src/unix/random-getrandom.c src/unix/random-sysctl-linux.c - src/unix/sysinfo-loadavg.c) + src/unix/epoll.c) endif() if(CMAKE_SYSTEM_NAME STREQUAL "NetBSD") list(APPEND uv_sources src/unix/netbsd.c) + list(APPEND uv_libraries kvm) endif() if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") list(APPEND uv_sources src/unix/openbsd.c) endif() -if(CMAKE_SYSTEM_NAME STREQUAL "OS/390") - list(APPEND uv_defines PATH_MAX=255) +if(CMAKE_SYSTEM_NAME STREQUAL "OS390") + enable_language(CXX) + list(APPEND uv_defines PATH_MAX=1024) list(APPEND uv_defines _AE_BIMODAL) list(APPEND uv_defines _ALL_SOURCE) + list(APPEND uv_defines _ENHANCED_ASCII_EXT=0xFFFFFFFF) + list(APPEND uv_defines _ISOC99_SOURCE) list(APPEND uv_defines _LARGE_TIME_API) list(APPEND uv_defines _OPEN_MSGQ_EXT) list(APPEND uv_defines _OPEN_SYS_FILE_EXT) @@ -234,18 +307,75 @@ if(CMAKE_SYSTEM_NAME STREQUAL "OS/390") list(APPEND uv_defines _UNIX03_SOURCE) list(APPEND uv_defines _UNIX03_THREADS) list(APPEND uv_defines _UNIX03_WITHDRAWN) + 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/pthread-barrier.c src/unix/os390.c - src/unix/os390-syscalls.c) + src/unix/os390-syscalls.c + src/unix/os390-proctitle.c) + list(APPEND uv_cflags + -q64 + -qascii + -qexportall + -qgonumber + -qlongname + -qlibansi + -qfloat=IEEE + -qtune=10 + -qarch=10 + -qasm + -qasmlib=sys1.maclib:sys1.modgen) + find_library(ZOSLIB + NAMES zoslib + PATHS ${ZOSLIB_DIR} + PATH_SUFFIXES lib + ) + list(APPEND uv_libraries ${ZOSLIB}) +endif() + +if(CMAKE_SYSTEM_NAME STREQUAL "OS400") + list(APPEND uv_defines + _ALL_SOURCE + _LINUX_SOURCE_COMPAT + _THREAD_SAFE + _XOPEN_SOURCE=500) + list(APPEND uv_sources + src/unix/aix-common.c + src/unix/ibmi.c + src/unix/no-fsevents.c + src/unix/posix-poll.c) endif() if(CMAKE_SYSTEM_NAME STREQUAL "SunOS") - list(APPEND uv_defines __EXTENSIONS__ _XOPEN_SOURCE=500) + list(APPEND uv_defines __EXTENSIONS__ _XOPEN_SOURCE=500 _REENTRANT) list(APPEND uv_libraries kstat nsl sendfile socket) - list(APPEND uv_sources src/unix/no-proctitle.c src/unix/sunos.c) + list(APPEND uv_sources + src/unix/no-proctitle.c + src/unix/sunos.c) +endif() + +if(CMAKE_SYSTEM_NAME STREQUAL "Haiku") + list(APPEND uv_defines _BSD_SOURCE) + list(APPEND uv_libraries bsd network) + list(APPEND uv_sources + src/unix/haiku.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) +endif() + +if(CMAKE_SYSTEM_NAME STREQUAL "QNX") + list(APPEND uv_sources + src/unix/posix-hrtime.c + src/unix/posix-poll.c + src/unix/qnx.c + src/unix/bsd-ifaddrs.c + src/unix/no-proctitle.c + src/unix/no-fsevents.c) + list(APPEND uv_libraries socket) endif() if(APPLE OR CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD|Linux|NetBSD|OpenBSD") @@ -266,6 +396,10 @@ target_include_directories(uv $ 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}) add_library(uv_a STATIC ${uv_sources}) @@ -277,9 +411,44 @@ target_include_directories(uv_a $ PRIVATE $) +if(CMAKE_SYSTEM_NAME STREQUAL "OS390") + target_include_directories(uv_a PUBLIC $) + set_target_properties(uv_a PROPERTIES LINKER_LANGUAGE CXX) +endif() target_link_libraries(uv_a ${uv_libraries}) if(LIBUV_BUILD_TESTS) + # Small hack: use ${uv_test_sources} now to get the runner skeleton, + # before the actual tests are added. + add_executable( + uv_run_benchmarks_a + ${uv_test_sources} + test/benchmark-async-pummel.c + test/benchmark-async.c + 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 + test/benchmark-ping-pongs.c + test/benchmark-ping-udp.c + test/benchmark-pound.c + test/benchmark-pump.c + test/benchmark-sizes.c + test/benchmark-spawn.c + test/benchmark-tcp-write-batch.c + test/benchmark-thread.c + test/benchmark-udp-pummel.c + test/blackhole-server.c + test/echo-server.c + test/run-benchmarks.c + test/runner.c) + target_compile_definitions(uv_run_benchmarks_a PRIVATE ${uv_defines}) + target_compile_options(uv_run_benchmarks_a PRIVATE ${uv_cflags}) + target_link_libraries(uv_run_benchmarks_a uv_a ${uv_test_libraries}) + list(APPEND uv_test_sources test/blackhole-server.c test/echo-server.c @@ -331,6 +500,7 @@ if(LIBUV_BUILD_TESTS) test/test-idna.c test/test-ip4-addr.c test/test-ip6-addr.c + test/test-ip-name.c test/test-ipc-heavy-traffic-deadlock-bug.c test/test-ipc-send-recv.c test/test-ipc.c @@ -340,8 +510,11 @@ if(LIBUV_BUILD_TESTS) test/test-loop-handles.c test/test-loop-stop.c test/test-loop-time.c + test/test-metrics.c test/test-multiple-listen.c test/test-mutexes.c + test/test-not-readable-nor-writable-on-read-error.c + test/test-not-writable-after-shutdown.c test/test-osx-select.c test/test-pass-always.c test/test-ping-pong.c @@ -360,6 +533,7 @@ if(LIBUV_BUILD_TESTS) test/test-poll-close-doesnt-corrupt-stack.c test/test-poll-close.c test/test-poll-closesocket.c + test/test-poll-multiple-handles.c test/test-poll-oob.c test/test-poll.c test/test-process-priority.c @@ -367,12 +541,14 @@ if(LIBUV_BUILD_TESTS) test/test-process-title.c test/test-queue-foreach-delete.c test/test-random.c + test/test-readable-on-eof.c test/test-ref.c test/test-run-nowait.c test/test-run-once.c test/test-semaphore.c test/test-shutdown-close.c test/test-shutdown-eof.c + test/test-shutdown-simultaneous.c test/test-shutdown-twice.c test/test-signal-multiple-loops.c test/test-signal-pending-on-close.c @@ -397,6 +573,7 @@ if(LIBUV_BUILD_TESTS) test/test-tcp-oob.c test/test-tcp-open.c test/test-tcp-read-stop.c + test/test-tcp-read-stop-start.c test/test-tcp-shutdown-after-write.c test/test-tcp-try-write.c test/test-tcp-try-write-error.c @@ -406,6 +583,7 @@ if(LIBUV_BUILD_TESTS) test/test-tcp-write-queue-order.c test/test-tcp-write-to-half-open-connection.c test/test-tcp-writealot.c + test/test-test-macros.c test/test-thread-equal.c test/test-thread.c test/test-threadpool-cancel.c @@ -420,9 +598,11 @@ if(LIBUV_BUILD_TESTS) test/test-udp-alloc-cb-fail.c test/test-udp-bind.c test/test-udp-connect.c + test/test-udp-connect6.c test/test-udp-create-socket-early.c test/test-udp-dgram-too-big.c test/test-udp-ipv6.c + test/test-udp-mmsg.c test/test-udp-multicast-interface.c test/test-udp-multicast-interface6.c test/test-udp-multicast-join.c @@ -433,13 +613,14 @@ if(LIBUV_BUILD_TESTS) test/test-udp-send-and-recv.c test/test-udp-send-hang-loop.c test/test-udp-send-immediate.c + test/test-udp-sendmmsg-error.c test/test-udp-send-unreachable.c test/test-udp-try-send.c test/test-uname.c test/test-walk-handles.c test/test-watcher-cross-stop.c) - add_executable(uv_run_tests ${uv_test_sources}) + add_executable(uv_run_tests ${uv_test_sources} uv_win_longpath.manifest) target_compile_definitions(uv_run_tests PRIVATE ${uv_defines} USING_UV_SHARED=1) target_compile_options(uv_run_tests PRIVATE ${uv_cflags}) @@ -447,46 +628,67 @@ if(LIBUV_BUILD_TESTS) add_test(NAME uv_test COMMAND uv_run_tests WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) - add_executable(uv_run_tests_a ${uv_test_sources}) + if(CMAKE_SYSTEM_NAME STREQUAL "OS390") + set_tests_properties(uv_test PROPERTIES ENVIRONMENT + "LIBPATH=${CMAKE_BINARY_DIR}:$ENV{LIBPATH}") + 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}) - target_link_libraries(uv_run_tests_a uv_a ${uv_test_libraries}) + if(QEMU) + target_link_libraries(uv_run_tests_a uv_a ${uv_test_libraries} -static) + else() + target_link_libraries(uv_run_tests_a uv_a ${uv_test_libraries}) + endif() add_test(NAME uv_test_a COMMAND uv_run_tests_a WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + if(CMAKE_SYSTEM_NAME STREQUAL "OS390") + set_target_properties(uv_run_benchmarks_a PROPERTIES LINKER_LANGUAGE CXX) + set_target_properties(uv_run_tests PROPERTIES LINKER_LANGUAGE CXX) + set_target_properties(uv_run_tests_a PROPERTIES LINKER_LANGUAGE CXX) + endif() endif() -if(UNIX) - # Now for some gibbering horrors from beyond the stars... - foreach(lib IN LISTS uv_libraries) - list(APPEND LIBS "-l${lib}") - endforeach() - string(REPLACE ";" " " LIBS "${LIBS}") - # Consider setting project version via project() call? - 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) - - install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR}) - install(FILES ${PROJECT_BINARY_DIR}/libuv.pc - DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) - install(TARGETS uv LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) - install(TARGETS uv_a ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +# Now for some gibbering horrors from beyond the stars... +foreach(lib IN LISTS uv_libraries) + list(APPEND LIBS "-l${lib}") +endforeach() +string(REPLACE ";" " " LIBS "${LIBS}") +# Consider setting project version via project() call? +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 + 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}) +install(TARGETS uv_a EXPORT libuvConfig + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +install(EXPORT libuvConfig DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libuv) + +if(MSVC) + set(CMAKE_DEBUG_POSTFIX d) endif() -if(WIN32) - install(DIRECTORY include/ DESTINATION include) - install(FILES LICENSE DESTINATION .) - install(TARGETS uv uv_a - RUNTIME DESTINATION lib/$ - ARCHIVE DESTINATION lib/$) -endif() +message(STATUS "summary of build options: + Install prefix: ${CMAKE_INSTALL_PREFIX} + Target system: ${CMAKE_SYSTEM_NAME} + Compiler: + C compiler: ${CMAKE_C_COMPILER} (${CMAKE_C_COMPILER_ID}) + CFLAGS: ${CMAKE_C_FLAGS_${_build_type}} ${CMAKE_C_FLAGS} +") diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6f8c45a..d37c51d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,11 +23,11 @@ The stable branch is effectively frozen; patches that change the libuv API/ABI or affect the run-time behavior of applications get rejected. In case of doubt, open an issue in the [issue tracker][], post your question -to the [libuv mailing list], or contact one of [project maintainers][] on [IRC][]. +to the [libuv discussions forum], or message the [libuv mailing list]. -Especially do so if you plan to work on something big. Nothing is more -frustrating than seeing your hard work go to waste because your vision -does not align with that of a project maintainers. +Especially do so if you plan to work on something big. Nothing is more +frustrating than seeing your hard work go to waste because your vision does not +align with that of the [project maintainers]. ### BRANCH @@ -142,7 +142,6 @@ Bug fixes and features should come with tests. Add your tests in the If you add a new test file, it needs to be registered in three places: - `CMakeLists.txt`: add the file's name to the `uv_test_sources` list. - `Makefile.am`: add the file's name to the `test_run_tests_SOURCES` list. -- `uv.gyp`: add the file's name to the `sources` list in the `run-tests` target. Look at other tests to see how they should be structured (license boilerplate, the way entry points are declared, etc.). @@ -167,6 +166,6 @@ not send out notifications when you add commits. [issue tracker]: https://github.com/libuv/libuv/issues [libuv mailing list]: http://groups.google.com/group/libuv -[IRC]: http://webchat.freenode.net/?channels=libuv +[libuv discussions forum]: https://github.com/libuv/libuv/discussions [Google C/C++ style guide]: https://google.github.io/styleguide/cppguide.html [project maintainers]: https://github.com/libuv/libuv/blob/master/MAINTAINERS.md diff --git a/ChangeLog b/ChangeLog index ae958e0..ea28356 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,752 @@ -2020.03.12, Version 1.35.0 (Stable) +2022.03.09, Version 1.44.1 (Stable) + +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: + +* run test named ip6_sin6_len (Jameson Nash) + +* docs: fix wrong information about scheduling (Mohamed Edrah) + +* unix: protect fork in uv_spawn from signals (Jameson Nash) + +* drop only successfully sent packets post sendmmsg (Supragya Raj) + +* test: fix typo in test-tty-escape-sequence-processing.c (Ikko Ashimine) + +* cmake: use standard installation layout always (Sylvain Corlay) + +* win,spawn: allow UNC path with forward slash (earnal) + +* win,fsevent: fix uv_fs_event_stop() assert (Ben Noordhuis) + +* unix: remove redundant include in unix.h (Juan José Arboleda) + +* doc: mark SmartOS as Tier 3 support (Ben Noordhuis) + +* doc: fix broken links for netbsd's sysctl manpage (YAKSH BARIYA) + +* misc: adjust stalebot deadline (Ben Noordhuis) + +* test: remove `dns-server.c` as it is not used anywhere (Darshan Sen) + +* build: fix non-cmake android builds (YAKSH BARIYA) + +* doc: replace pyuv with uvloop (Ofek Lev) + +* asan: fix some tests (Jameson Nash) + +* build: add experimental TSAN configuration (Jameson Nash) + +* pipe: remove useless assertion (~locpyl-tidnyd) + +* bsd: destroy mutex in uv__process_title_cleanup() (Darshan Sen) + +* build: add windows build to CI (Darshan Sen) + +* win,fs: fix error code in uv_fs_read() and uv_fs_write() (Darshan Sen) + +* build: add macos-latest to ci matrix (Ben Noordhuis) + +* udp: fix &/&& typo in macro condition (Evan Miller) + +* build: install cmake package module (Petr Menšík) + +* win: fix build for mingw32 (Nicolas Noble) + +* build: fix build failures with MinGW new headers (erw7) + +* build: fix win build with cmake versions before v3.14 (AJ Heller) + +* unix: support aarch64 in uv_cpu_info() (Juan José Arboleda) + +* linux: work around CIFS EPERM bug (Ben Noordhuis) + +* sunos: Oracle Developer Studio support (Stacey Marshall) + +* Revert "sunos: Oracle Developer Studio support (cjihrig) + +* sunos: Oracle Developer Studio support (Stacey Marshall) + +* stream: permit read after seeing EOF (Jameson Nash) + +* thread: initialize uv_thread_self for all threads (Jameson Nash) + +* kqueue: ignore write-end closed notifications (Jameson Nash) + +* macos: fix the cfdata length in uv__get_cpu_speed (Jesper Storm Bache) + +* unix,win: add uv_ip_name to get name from sockaddr (Campbell He) + +* win,test: fix a few typos (AJ Heller) + +* zos: use destructor for uv__threadpool_cleanup() (Wayne Zhang) + +* linux: use MemAvailable instead of MemFree (Andrey Hohutkin) + +* freebsd: call dlerror() only if necessary (Jameson Nash) + +* bsd,windows,zos: fix udp disconnect EINVAL (deal) + + +2021.07.21, Version 1.42.0 (Stable), 6ce14710da7079eb248868171f6343bc409ea3a4 + +Changes since version 1.41.0: + +* doc: fix code highlighting (Darshan Sen) + +* test: move to ASSERT_NULL and ASSERT_NOT_NULL test macros (tjarlama) + +* zos: build in ascii code page (Shuowang (Wayne) Zhang) + +* zos: don't use nanosecond timestamp fields (Shuowang (Wayne) Zhang) + +* zos: introduce zoslib (Shuowang (Wayne) Zhang) + +* zos: use strnlen() from zoslib (Shuowang (Wayne) Zhang) + +* zos: use nanosleep() from zoslib (Shuowang (Wayne) Zhang) + +* zos: use __getargv() from zoslib to get exe path (Shuowang (Wayne) Zhang) + +* zos: treat __rfim_utok as binary (Shuowang (Wayne) Zhang) + +* zos: use execvpe() to set environ explictly (Shuowang (Wayne) Zhang) + +* zos: use custom proctitle implementation (Shuowang (Wayne) Zhang) + +* doc: add instructions for building on z/OS (Shuowang (Wayne) Zhang) + +* linux,udp: enable full ICMP error reporting (Ondřej Surý) + +* test: fix test-udp-send-unreachable (Ondřej Surý) + +* include: fix typo in documentation (Tobias Nießen) + +* chore: use for(;;) instead of while (Yash Ladha) + +* test: remove string + int warning on udp-pummel (Juan José Arboleda) + +* cmake: fix linker flags (Zhao Zhili) + +* test: fix stack-use-after-scope (Zhao Zhili) + +* unix: expose thread_stack_size() internally (Brandon Cheng) + +* darwin: use RLIMIT_STACK for fsevents pthread (Brandon Cheng) + +* darwin: abort on pthread_attr_init fail (Brandon Cheng) + +* benchmark: remove unreachable code (Matvii Hodovaniuk) + +* macos: fix memleaks in uv__get_cpu_speed (George Zhao) + +* Make Thread Sanitizer aware of file descriptor close in uv__close() (Ondřej + Surý) + +* darwin: fix iOS compilation and functionality (Hayden) + +* linux: work around copy_file_range() cephfs bug (Ben Noordhuis) + +* zos: implement uv_get_constrained_memory() (Shuowang (Wayne) Zhang) + +* zos: fix uv_get_free_memory() (Shuowang (Wayne) Zhang) + +* zos: use CVTRLSTG to get total memory accurately (Shuowang (Wayne) Zhang) + +* ibmi: Handle interface names longer than 10 chars (Kevin Adler) + +* docs: update read-the-docs version of sphinx (Jameson Nash) + +* unix: refactor uv_try_write (twosee) + +* linux-core: add proper divide by zero assert (yiyuaner) + +* misc: remove unnecessary _GNU_SOURCE macros (Darshan Sen) + +* test: log to stdout to conform TAP spec (bbara) + +* win,fs: fix C4090 warning with MSVC (SeverinLeonhardt) + +* build: some systems provide dlopen() in libc (Andy Fiddaman) + +* include: add EOVERFLOW status code mapping (Darshan Sen) + +* unix,fs: use uv__load_relaxed and uv__store_relaxed (Darshan Sen) + +* win: fix string encoding issue of uv_os_gethostname (Eagle Liang) + +* unix,process: add uv__write_errno helper function (Ricky Zhou) + +* Re-merge "unix,stream: clear read/write states on close/eof" (Jameson Nash) + +* unix,core: fix errno handling in uv__getpwuid_r (Darshan Sen) + +* errors: map ESOCKTNOSUPPORT errno (Ryan Liptak) + +* doc: uv_read_stop always succeeds (Simon Kissane) + +* inet: fix inconsistent return value of inet_ntop6 (twosee) + +* darwin: fix -Wsometimes-uninitialized warning (twosee) + +* stream: introduce uv_try_write2 function (twosee) + +* poll,win: UV_PRIORITIZED option should not assert (twosee) + +* src: DragonFlyBSD has mmsghdr struct too (David Carlier) + +* cleanup,win: Remove _WIN32 guards on threadpool (James M Snell) + +* freebsd: fix an incompatible pointer type warning (Darshan Sen) + +* core: Correct the conditionals for {cloexec,nonblock}_ioctl (Ali Mohammad + Pur) + +* win,tcp: make uv_close work more like unix (Jameson Nash) + +* doc: more accurate list of valid send_handle's (twosee) + +* win,tcp: translate system errors correctly (twosee) + +* unix: implement cpu_relax() on ppc64 (Ben Noordhuis) + +* docs: move list of project links under PR control (Jameson Nash) + +* test: wrong pointer arithmetic multiplier (Erkhes N) + +* doc: switch discussion forum to github (Jameson Nash) + +* idna: fix OOB read in punycode decoder (Ben Noordhuis) + +* build: make sure -fvisibility=hidden is set (Santiago Gimeno) + +* illumos: event ports to epoll (tjarlama) + +* illumos,tty: UV_TTY_MODE_IO waits for 4 bytes (Joshua M. Clulow) + +* doc: add vtjnash GPG ID (Jameson Nash) + +* linux: read CPU model information on ppc (Richard Lau) + +* darwin: fix uv_barrier race condition (Guilherme Íscaro) + +* unix,stream: fix loop hang after uv_shutdown (Jameson Nash) + +* doc,udp: note that suggested_size is 1 max-sized dgram (Ryan Liptak) + +* mingw: fix building for ARM/AArch64 (Martin Storsjö) + +* unix: strnlen is not available on Solaris 10 (Claes Nästén) + +* sunos: restore use of event ports (Andy Fiddaman) + +* sunos,cmake: use thread-safe errno (Andy Fiddaman) + + +2021.02.14, Version 1.41.0 (Stable), 1dff88e5161cba5c59276d2070d2e304e4dcb242 + +Changes since version 1.40.0: + +* mailmap: update contact information for richardlau (Richard Lau) + +* build: add asan checks (gengjiawen) + +* unix: report bind error in uv_tcp_connect() (Ben Noordhuis) + +* doc: uv_tcp_bind() never returns UV_EADDRINUSE (Ben Noordhuis) + +* test: fix pump and tcp_write_batch benchmarks (Santiago Gimeno) + +* doc: mark IBM i as Tier 2 support (Jesse Gorzinski) + +* doc,poll: add notes (repeated cb & cancel pending cb) (Elad Nachmias) + +* linux: fix -Wincompatible-pointer-types warning (Ben Noordhuis) + +* linux: fix -Wsign-compare warning (Ben Noordhuis) + +* android: add system call api guards (Ben Noordhuis) + +* unix,win: harmonize uv_read_start() error handling (Ben Noordhuis) + +* unix,win: more uv_read_start() argument validation (Ben Noordhuis) + +* build: turn on -fno-strict-aliasing (Ben Noordhuis) + +* stream: add uv_pipe and uv_socketpair to the API (Jameson Nash) + +* unix,win: initialize timer `timeout` field (Ben Noordhuis) + +* bsd-ifaddrs: improve comments (Darshan Sen) + +* test: remove unnecessary uv_fs_stat() calls (Ben Noordhuis) + +* fs: fix utime/futime timestamp rounding errors (Ben Noordhuis) + +* test: ensure reliable floating point comparison (Jameson Nash) + +* unix,fs: fix uv_fs_sendfile() (Santiago Gimeno) + +* unix: fix uv_fs_stat when using statx (Simon Kadisch) + +* linux,macos: fix uv_set_process_title regression (Momtchil Momtchev) + +* doc: clarify UDP errors and recvmmsg (Ethel Weston) + +* test-getaddrinfo: use example.invalid (Drew DeVault) + +* Revert "build: fix android autotools build" (Bernardo Ramos) + +* unix,fs: on DVS fs, statx returns EOPNOTSUPP (Mark Klein) + +* win, fs: mkdir really return UV_EINVAL for invalid names (Nicholas Vavilov) + +* tools: migrate tools/make_dist_html.py to python3 (Dominique Dumont) + +* unix: fix uv_uptime() on linux (schamberg97) + +* unix: check for partial copy_file_range support (Momtchil Momtchev) + +* win: bump minimum supported version to windows 8 (Ben Noordhuis) + +* poll,unix: ensure safety of rapid fd reuse (Bob Weinand) + +* test: fix some warnings (Issam E. Maghni) + +* unix: fix uv_uptime() regression (Santiago Gimeno) + +* doc: fix versionadded metadata (cjihrig) + +* test: fix 'incompatible pointer types' warnings (cjihrig) + +* unix: check for EXDEV in uv__fs_sendfile() (Darshan Sen) + + +2020.09.26, Version 1.40.0 (Stable), 4e69e333252693bd82d6338d6124f0416538dbfc + +Changes since version 1.39.0: + +* udp: add UV_UDP_MMSG_FREE recv_cb flag (Ryan Liptak) + +* include: re-map UV__EPROTO from 4046 to -4046 (YuMeiJie) + +* doc: correct UV_UDP_MMSG_FREE version added (cjihrig) + +* doc: add uv_metrics_idle_time() version metadata (Ryan Liptak) + +* win,tty: pass through utf-16 surrogate pairs (Mustafa M) + +* unix: fix DragonFly BSD build (Aleksej Lebedev) + +* win,udp: fix error code returned by connect() (Santiago Gimeno) + +* src: suppress user_timeout maybe-uninitialized (Daniel Bevenius) + +* test: fix compiler warning (Vladimír Čunát) + +* build: fix the Haiku cmake build (David Carlier) + +* linux: fix i386 sendmmsg/recvmmsg support (Ben Noordhuis) + +* build: add libuv-static pkg-config file (Nikolay Mitev) + +* unix,win: add uv_timer_get_due_in() (Ulrik Strid) + +* build,unix: add QNX support (Elad Lahav) + +* include: remove incorrect UV__ERR() for EPROTO (cjihrig) + + +2020.08.26, Version 1.39.0 (Stable), 25f4b8b8a3c0f934158cd37a37b0525d75ca488e + +Changes since version 1.38.1: + +* unix: use relaxed loads/stores for clock id (Ben Noordhuis) + +* build,win: link to user32.lib and advapi32.lib (George Zhao) + +* unix: squelch harmless valgrind warning (ssrlive) + +* include: fx c++ style comments warnings (Turbinya) + +* build,cmake: Change installation location on MinGW (erw7) + +* linux: use copy_file_range for uv_fs_copyfile when possible (Carter Li) + +* win,tcp: avoid reinserting a pending request ( + +* docs: improve the descriptions for get memory info (Juan Sebastian velez + Posada) + +* test: add udp-mmsg test (Ryan Liptak) + +* udp: add uv_udp_using_recvmmsg query (Ryan Liptak) + +* doc: add more error constants (TK-one) + +* zos: fix potential event loop stall (Trevor Norris) + +* include: add internal fields struct to uv_loop_t (Trevor Norris) + +* core: add API to measure event loop idle time (Trevor Norris) + +* win,fs: use CreateDirectoryW instead of _wmkdir (Mustafa M) + +* win,nfc: fix integer comparison signedness (escherstair) + +* win,nfc: use + +* win,nfc: removed some unused variables (escherstair) + +* win,nfc: add missing return statement (escherstair) + +* win,nfc: disable clang-format for + +* darwin: use IOKit for uv_cpu_info (Evan Lucas) + +* test: fix thread race in process_title_threadsafe (Ben Noordhuis) + +* win,fs: avoid implicit access to _doserrno (Jameson Nash) + +* test: give hrtime test a custom 20s timeout (Jameson Nash) + +* build: add more failed test, for qemu version bump (gengjiawen) + +* unix: handle src, dest same in uv_fs_copyfile() (cjihrig) + +* unix: error when uv_setup_args() is not called (Ryan Liptak) + +* aix: protect uv_exepath() from uv_set_process_title() (Richard Lau) + +* fs: clobber req->path on uv_fs_mkstemp() error (tjarlama) + +* cmake: fix compile error C2001 on Chinese Windows (司徒玟琅) + +* test: avoid double evaluation in ASSERT_BASE macro (tjarlama) + +* tcp: fail instantly if local port is unbound (Bartosz Sosnowski) + +* doc: fix most sphinx warnings (Jameson Nash) + +* nfci: address some style nits (Jameson Nash) + +* unix: don't use _POSIX_PATH_MAX (Ben Noordhuis) + + +2020.07.04, Version 1.38.1 (Stable), e8b989ea1f7f9d4083511a2caec7791e9abd1871 + +Changes since version 1.38.0: + +* test: use last matching qemu version (cjihrig) + +* win, util: rearrange uv_hrtime (Bartosz Sosnowski) + +* test: skip signal_multiple_loops test on QEMU (gengjiawen) + +* build: add android build to CI (gengjiawen) + +* test: extend fs_event_error_reporting timeout (cjihrig) + +* build: link libkvm on netbsd only (Alexander Tokmakov) + +* linux: refactor /proc file reader logic (Ben Noordhuis) + +* linux: read load average from /proc/loadavg (Ben Noordhuis) + +* android: remove patch code for below 21 (gengjiawen) + +* win: fix visual studio 2008 build (Arenoros) + +* win,tty: fix deadlock caused by inconsistent state (lander0s) + +* unix: use relaxed loads/stores for feature checks (Ben Noordhuis) + +* build: don't .gitignore m4/ax_pthread.m4 (Ben Noordhuis) + +* unix: fix gcc atomics feature check (Ben Noordhuis) + +* darwin: work around clock jumping back in time (Ben Noordhuis) + +* udp: fix write_queue cleanup on sendmmsg error (Santiago Gimeno) + +* src: build fix for Android (David Carlier) + + +2020.05.18, Version 1.38.0 (Stable), 1ab9ea3790378f9f25c4e78e9e2b511c75f9c9ed + +Changes since version 1.37.0: + +* test: skip poll_duplex and poll_unidirectional on PASE (Xu Meng) + +* linux: make cpu_times consistently be milliseconds (James Ross) + +* win: DRY uv_poll_start() and uv_poll_stop() (Ben Noordhuis) + +* win: DRY uv_poll_close() (Ben Noordhuis) + +* unix,win: add uv_library_shutdown() (Ben Noordhuis) + +* unix: yield cpu when spinlocking on async handle (Ben Noordhuis) + +* win: remove dep on GetQueuedCompletionStatusEx (Colin Finck) + +* doc: correct source lines (Shohei YOSHIDA) + +* build,android: fix typo (twosee) + +* doc: uv_cancel() handles uv_random_t requests (Philip Chimento) + +* doc: fix unescaped character (Philip Chimento) + +* build,cmake: fix compilation on old MinGW (erw7) + +* build: remove unnessesary MSVC warnings (Bartosz Sosnowski) + +* win: make uv_udp_init_ex() accept UV_UDP_RECVMMSG (Ben Noordhuis) + +* unix: simplify uv__udp_init_ex() (Ben Noordhuis) + +* win: remove MAX_PATH limitations (Bartosz Sosnowski) + +* build, win: add long path aware manifest (Bartosz Sosnowski) + +* doc: check/idle/prepare functions always succeed (Ben Noordhuis) + +* darwin: fix build with non-apple compilers (Ben Noordhuis) + +* win: support environment variables > 32767 chars (Ben Noordhuis) + +* unix: fully initialize struct msghdr (Ben Noordhuis) + +* doc: add uv_replace_allocator thread safety warning (twosee) + +* unix: fix int overflow when copying large files (Michal Artazov) + +* fs: report original error (Bartosz Sosnowski) + +* win, fs: add IO_REPARSE_TAG_APPEXECLINK support (Bartosz Sosnowski) + +* doc: fix formatting (Ben Noordhuis) + +* unix: fix memory leak when uv_loop_init() fails (Anna Henningsen) + +* unix: shrink uv_udp_set_source_membership() stack (Ben Noordhuis) + +* unix,win: fix wrong sizeof argument to memcpy() (Ben Noordhuis) + +* build: check for libraries not provided by libc (Jeroen Roovers) + +* doc: fix the order of arguments to calloc() (MasterDuke17) + +* unix: don't abort when getrlimit() fails (Ben Noordhuis) + +* test: support common user profile on IBMi (Xu Meng) + +* build: test on more platforms via QEMU in CI (gengjiawen) + + +2020.04.20, Version 1.37.0 (Stable), 02a9e1be252b623ee032a3137c0b0c94afbe6809 + +Changes since version 1.36.0: + +* timer: remove redundant check in heap compare (Yash Ladha) + +* udp: add flag to enable recvmmsg(2) explicitly (Saúl Ibarra Corretgé) + + +2020.04.16, Version 1.36.0 (Stable), 533b738838ad8407032e14b6772b29ef9af63cfa + +Changes since version 1.35.0: + +* build: add aix-common.c for AIX cmake build (Jesse Gorzinski) + +* zos: explicitly mark message queue events (Irek Fakhrutdinov) + +* zos: move mq check out of loop to save cpu cycles (Irek Fakhrutdinov) + +* zos: add checks to ensure behavior of epoll_wait (Irek Fakhrutdinov) + +* src: add uv__reallocf() (Ben Noordhuis) + +* build: ibmi support for cmake (Jesse Gorzinski) + +* build: fix gyp build for Android API >= 28 (Lin Zhang) + +* udp: return recvmmsg-ed datagrams in order (Saúl Ibarra Corretgé) + +* zos,test: fix spawn_empty_env for shared library build (Richard Lau) + +* zos: fix non-Release builds (Richard Lau) + +* zos: fix return value on expired nanosleep() call (Richard Lau) + +* build: fix z/OS cmake build (Richard Lau) + +* test: add a bunch of ASSERT macros (Santiago Gimeno) + +* test: remove unused extern declaration (Ben Noordhuis) + +* test: canonicalize argv[0] in exepath test (Ben Noordhuis) + +* test: simplify platform_init() (Ben Noordhuis) + +* ibmi: Fix isatty EBADF handling and refactor (Kevin Adler) + +* test: Test EBADF tty handling (Kevin Adler) + +* build: make cmake build benchmarks (Ben Noordhuis) + +* win: use RtlGenRandom from advapi32.dll directly (Ben Noordhuis) + +* android: fix OOB write in uv_interface_addresses() (Lin Zhang) + +* test: pass test when hostname is single character (毛毛) + +* ibmi: set the highest process priority to -10 (Xu Meng) + +* build: remove support for gyp (Ben Noordhuis) + +* doc: add note to README on cross-compiling (Ben Noordhuis) + +* fs: add uv_fs_lutime() (Sk Sajidul Kadir) + +* unix: implement cpu_relax() for arm (David Carlier) + +* linux: fix uv__accept4() (twosee) + +* win: handle file paths in uv_fs_statfs() (erw7) + +* unix: fix uv_os_environ() null pointer check (Rikard Falkeborn) + +* win: fix uv_os_environ() null pointer check (Rikard Falkeborn) + +* unix: fix compilation on macOS 32-bit architectures (Brad King) + +* win: replace alloca() with stack-based array (Ben Noordhuis) + + +2020.03.12, Version 1.35.0 (Stable), e45f1ec38db882f8dc17b51f51a6684027034609 Changes since version 1.34.2: diff --git a/LICENSE b/LICENSE index 28f1733..eb126da 100644 --- a/LICENSE +++ b/LICENSE @@ -64,7 +64,3 @@ The externally maintained libraries used by libuv are: - 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/LINKS.md b/LINKS.md new file mode 100644 index 0000000..b8204e5 --- /dev/null +++ b/LINKS.md @@ -0,0 +1,101 @@ +### Apps / VM +* [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. +* [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)) +* [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 +* [libstorj](https://github.com/Storj/libstorj): Library for interacting with Storj network +* [libuv_message_framing](https://github.com/litesync/libuv_message_framing) Message-based communication for libuv +* [luaw](https://github.com/raksoras/luaw): Lua web server backed by libuv +* [Luvit](http://luvit.io): Node.JS for the Lua Inventor +* [mo](https://github.com/wehu/mo): Scheme (guile) + libuv runtime +* [MoarVM](https://github.com/MoarVM/MoarVM): a VM for [Rakudo](http://rakudo.org/) [Raku](http://raku.org) +* [Mysocks](https://github.com/zhou0/mysocks): a cross-platform [Shadowsocks](https://shadowsocks.org) client +* [mediasoup](http://mediasoup.org): Powerful WebRTC SFU for Node.js +* [Neovim](https://neovim.io/): A major refactor of Vim. +* [node9](https://github.com/jvburnes/node9): A portable, hybrid, distributed OS based on Inferno, LuaJIT and Libuv +* [node.js](http://www.nodejs.org/): Javascript (using Google's V8) + libuv +* [node.native](https://github.com/d5/node.native): node.js-like API for C++11 +* [nodeuv](https://github.com/nodeuv): An organization with several c++ wrappers for libs which are used in node.js. +* [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 +* [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 +* [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. +* [Trevi](https://github.com/Yoseob/Trevi): A powerful Swift Web Application Server Framework Project +* [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 +* [Wren CLI](https://github.com/wren-lang/wren-cli): For io, process, scheduler and timer modules + +### Other +* [libtuv](https://github.com/Samsung/libtuv): libuv fork for IoT and embedded systems + +### Bindings +* [Ring](http://ring-lang.net) + * [RingLibuv](http://ring-lang.sourceforge.net/doc1.7/libuv.html) +* Ruby + * [libuv](https://github.com/cotag/libuv) + * [uvrb](https://github.com/avalanche123/uvrb) + * [ruv](https://github.com/aq1018/ruv) + * [rbuv](https://github.com/rbuv/rbuv) + * [mruby-uv](https://github.com/mattn/mruby-uv): mruby binding +* Lua + * [luv](https://github.com/creationix/luv) + * [lev](https://github.com/connectFree/lev) + * [lluv](https://github.com/moteus/lua-lluv) +* C++11 + * [uvpp](https://github.com/larroy/uvpp) - Not complete, exposes very few aspects of `libuv` +* C++17 + * [uvw](https://github.com/skypjack/uvw) - Header-only, event based, tiny and easy to use *libuv* wrapper in modern C++. +* Python + * [Pyuv](https://github.com/saghul/pyuv) + * [uvloop](https://github.com/MagicStack/uvloop) - Ultra fast asyncio event loop. + * [gevent](http://www.gevent.org) - Coroutine-based concurrency library for Python +* C# + * [NetUV](http://github.com/StormHub/NetUV) + * [LibuvSharp](http://github.com/txdv/LibuvSharp) +* Perl 5 + * [UV](https://metacpan.org/pod/UV) +* [Raku](https://raku.org/) + * [MoarVM](https://github.com/MoarVM/MoarVM) [uses](http://6guts.wordpress.com/2013/05/31/moarvm-a-virtual-machine-for-nqp-and-rakudo/) libuv +* PHP + * [php-uv](https://github.com/bwoebi/php-uv) +* Go + * [go-uv](https://github.com/mattn/go-uv) +* OCaml + * [luv](https://github.com/aantron/luv) + * [uwt](https://github.com/fdopen/uwt) +* ooc + * [ooc-uv](https://github.com/nddrylliog/ooc-uv) +* dylan + * [uv-dylan](https://github.com/waywardmonkeys/uv-dylan) +* R + * [httpuv](https://github.com/rstudio/httpuv): HTTP and WebSocket server library for R + * [fs](https://fs.r-lib.org/): Cross-platform file system operations +* Java + * [libuv-java](https://java.net/projects/avatar-js/sources/libuv-java/show): Java bindings +* Nim + * [nimuv](https://github.com/2vg/nimuv): Nim bindings +* Lisp + * [cl-libuv](https://github.com/orthecreedence/cl-libuv) Common Lisp bindings + * [cl-async](https://github.com/orthecreedence/cl-async) Common Lisp async abstraction on top of cl-libuv +* [Céu](http://www.ceu-lang.org) + * [Céu-libuv](https://github.com/fsantanna/ceu-libuv) +* Delphi + * [node.pas](https://github.com/vovach777/node.pas) NodeJS-like ecosystem +* Haskell + * [Z.Haskell](https://z.haskell.world) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 268251e..fa7c26e 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -16,6 +16,9 @@ libuv is currently managed by the following individuals: * **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) + - GPG key: CFBB 9CA9 A5BE AFD7 0E2B 3C5A 79A6 7C55 A367 9C8B (pubkey2022-vtjnash) +* **Jiawen Geng** ([@gengjiawen](https://github.com/gengjiawen)) * **John Barboza** ([@jbarz](https://github.com/jbarz)) * **Kaoru Takanashi** ([@erw7](https://github.com/erw7)) - GPG Key: 5804 F999 8A92 2AFB A398 47A0 7183 5090 6134 887F (pubkey-erw7) diff --git a/Makefile.am b/Makefile.am index 68bef4c..d1ec1a5 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 \ @@ -56,7 +56,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=0x0600 + -D_WIN32_WINNT=0x0602 libuv_la_SOURCES += src/win/async.c \ src/win/atomicops-inl.h \ src/win/core.c \ @@ -123,23 +123,15 @@ EXTRA_DIST = test/fixtures/empty_file \ include \ docs \ img \ - android-configure-arm \ - android-configure-arm64 \ - android-configure-x86 \ - android-configure-x86_64 \ CONTRIBUTING.md \ LICENSE \ - README.md \ - vcbuild.bat \ - common.gypi \ - gyp_uv.py \ - uv.gyp + README.md TESTS = test/run-tests check_PROGRAMS = test/run-tests -test_run_tests_CFLAGS = +test_run_tests_CFLAGS = $(AM_CFLAGS) if SUNOS # Can't be turned into a CC_CHECK_CFLAGS in configure.ac, it makes compilers @@ -147,9 +139,8 @@ 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/dns-server.c \ test/echo-server.c \ test/run-tests.c \ test/runner.c \ @@ -201,6 +192,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-idna.c \ test/test-ip4-addr.c \ test/test-ip6-addr.c \ + test/test-ip-name.c \ test/test-ipc-heavy-traffic-deadlock-bug.c \ test/test-ipc-send-recv.c \ test/test-ipc.c \ @@ -211,8 +203,11 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-loop-stop.c \ test/test-loop-time.c \ test/test-loop-configure.c \ + test/test-metrics.c \ test/test-multiple-listen.c \ test/test-mutexes.c \ + test/test-not-readable-nor-writable-on-read-error.c \ + test/test-not-writable-after-shutdown.c \ test/test-osx-select.c \ test/test-pass-always.c \ test/test-ping-pong.c \ @@ -232,18 +227,21 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-poll-close.c \ test/test-poll-close-doesnt-corrupt-stack.c \ test/test-poll-closesocket.c \ + test/test-poll-multiple-handles.c \ test/test-poll-oob.c \ test/test-process-priority.c \ test/test-process-title.c \ test/test-process-title-threadsafe.c \ test/test-queue-foreach-delete.c \ test/test-random.c \ + test/test-readable-on-eof.c \ test/test-ref.c \ test/test-run-nowait.c \ test/test-run-once.c \ test/test-semaphore.c \ test/test-shutdown-close.c \ test/test-shutdown-eof.c \ + test/test-shutdown-simultaneous.c \ test/test-shutdown-twice.c \ test/test-signal-multiple-loops.c \ test/test-signal-pending-on-close.c \ @@ -267,6 +265,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-tcp-flags.c \ test/test-tcp-open.c \ test/test-tcp-read-stop.c \ + test/test-tcp-read-stop-start.c \ test/test-tcp-shutdown-after-write.c \ test/test-tcp-unexpected-read.c \ test/test-tcp-oob.c \ @@ -277,6 +276,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-tcp-try-write.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-threadpool-cancel.c \ @@ -291,9 +291,11 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-udp-alloc-cb-fail.c \ test/test-udp-bind.c \ test/test-udp-connect.c \ + test/test-udp-connect6.c \ test/test-udp-create-socket-early.c \ test/test-udp-dgram-too-big.c \ test/test-udp-ipv6.c \ + test/test-udp-mmsg.c \ test/test-udp-multicast-interface.c \ test/test-udp-multicast-interface6.c \ test/test-udp-multicast-join.c \ @@ -304,6 +306,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-udp-send-and-recv.c \ test/test-udp-send-hang-loop.c \ test/test-udp-send-immediate.c \ + test/test-udp-sendmmsg-error.c \ test/test-udp-send-unreachable.c \ test/test-udp-try-send.c \ test/test-uname.c \ @@ -346,7 +349,8 @@ test_run_tests_CFLAGS += -D__EXTENSIONS__ \ endif if OS390 -test_run_tests_CFLAGS += -D_UNIX03_THREADS \ +test_run_tests_CFLAGS += -D_ISOC99_SOURCE \ + -D_UNIX03_THREADS \ -D_UNIX03_SOURCE \ -D_OPEN_SYS_IF_EXT=1 \ -D_OPEN_SYS_SOCK_IPV6 \ @@ -380,21 +384,12 @@ uvinclude_HEADERS += include/uv/posix.h libuv_la_SOURCES += src/unix/aix-common.c \ src/unix/ibmi.c \ src/unix/posix-poll.c \ - src/unix/no-fsevents.c \ - src/unix/no-proctitle.c + src/unix/no-fsevents.c endif if ANDROID -uvinclude_HEADERS += include/uv/android-ifaddrs.h -libuv_la_SOURCES += src/unix/android-ifaddrs.c \ - src/unix/linux-core.c \ - src/unix/linux-inotify.c \ - src/unix/linux-syscalls.c \ - src/unix/procfs-exepath.c \ - src/unix/pthread-fixes.c \ - src/unix/random-getrandom.c \ - src/unix/random-sysctl-linux.c \ - src/unix/sysinfo-loadavg.c +libuv_la_CFLAGS += -D_GNU_SOURCE +libuv_la_SOURCES += src/unix/pthread-fixes.c endif if CYGWIN @@ -416,8 +411,9 @@ uvinclude_HEADERS += include/uv/darwin.h libuv_la_CFLAGS += -D_DARWIN_USE_64_BIT_INODE=1 libuv_la_CFLAGS += -D_DARWIN_UNLIMITED_SELECT=1 libuv_la_SOURCES += src/unix/bsd-ifaddrs.c \ - src/unix/darwin.c \ src/unix/darwin-proctitle.c \ + src/unix/darwin-stub.h \ + src/unix/darwin.c \ src/unix/fsevents.c \ src/unix/kqueue.c \ src/unix/proctitle.c \ @@ -459,9 +455,12 @@ 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 @@ -475,7 +474,7 @@ libuv_la_SOURCES += src/unix/linux-core.c \ src/unix/proctitle.c \ src/unix/random-getrandom.c \ src/unix/random-sysctl-linux.c \ - src/unix/sysinfo-loadavg.c + src/unix/epoll.c test_run_tests_LDFLAGS += -lutil endif diff --git a/README.md b/README.md index 9c785da..06486fe 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ libuv is a multi-platform support library with a focus on asynchronous I/O. It was primarily developed for use by [Node.js][], but it's also used by [Luvit](http://luvit.io/), [Julia](http://julialang.org/), -[pyuv](https://github.com/saghul/pyuv), and [others](https://github.com/libuv/libuv/wiki/Projects-that-use-libuv). +[uvloop](https://github.com/MagicStack/uvloop), and [others](https://github.com/libuv/libuv/blob/v1.x/LINKS.md). ## Feature highlights @@ -48,9 +48,8 @@ The documentation is licensed under the CC BY 4.0 license. Check the [LICENSE-do ## Community - * [Support](https://github.com/libuv/help) + * [Support](https://github.com/libuv/libuv/discussions) * [Mailing list](http://groups.google.com/group/libuv) - * [IRC chatroom (#libuv@irc.freenode.org)](http://webchat.freenode.net?channels=libuv&uio=d4) ## Documentation @@ -152,47 +151,14 @@ $ gpg --verify libuv-1.7.0.tar.gz.sign ## Build Instructions -For GCC there are two build methods: via autotools or via [GYP][]. -GYP is a meta-build system which can generate MSVS, Makefile, and XCode -backends. It is best used for integration into other projects. +For UNIX-like platforms, including macOS, there are two build methods: +autotools or [CMake][]. -To build with autotools: - -```bash -$ sh autogen.sh -$ ./configure -$ make -$ make check -$ make install -``` +For Windows, [CMake][] is the only supported build method and has the +following prerequisites: -To build with [CMake](https://cmake.org/): +
-```bash -$ mkdir -p out/cmake ; cd out/cmake # create build directory -$ cmake ../.. -DBUILD_TESTING=ON # generate project with test -$ cmake --build . # build -$ ctest -C Debug --output-on-failure # run tests - -# Or manually run tests: -$ ./out/cmake/uv_run_tests # shared library build -$ ./out/cmake/uv_run_tests_a # static library build -``` - -To build with GYP, first run: - -```bash -$ git clone https://chromium.googlesource.com/external/gyp build/gyp -``` - -### Windows - -Prerequisites: - -* [Python 2.6 or 2.7][] as it is required - by [GYP][]. - If python is not in your path, set the environment variable `PYTHON` to its - location. For example: `set PYTHON=C:\Python27\python.exe` * One of: * [Visual C++ Build Tools][] * [Visual Studio 2015 Update 3][], all editions @@ -205,67 +171,44 @@ Prerequisites: [Git for Windows][] includes Git Bash and tools which can be included in the global `PATH`. -To build, launch a git shell (e.g. Cmd or PowerShell), run `vcbuild.bat` -(to build with VS2017 you need to explicitly add a `vs2017` argument), -which will checkout the GYP code into `build/gyp`, generate `uv.sln` -as well as the necesery related project files, and start building. - -```console -> vcbuild -``` - -Or: +
-```console -> vcbuild vs2017 -``` - -To run the tests: - -```console -> vcbuild test -``` - -To see all the options that could passed to `vcbuild`: - -```console -> vcbuild help -vcbuild.bat [debug/release] [test/bench] [clean] [noprojgen] [nobuild] [vs2017] [x86/x64] [static/shared] -Examples: - vcbuild.bat : builds debug build - vcbuild.bat test : builds debug build and runs tests - vcbuild.bat release bench: builds release build and runs benchmarks -``` - - -### Unix - -For Debug builds (recommended) run: +To build with autotools: ```bash -$ ./gyp_uv.py -f make -$ make -C out +$ sh autogen.sh +$ ./configure +$ make +$ make check +$ make install ``` -For Release builds run: +To build with [CMake][]: ```bash -$ ./gyp_uv.py -f make -$ BUILDTYPE=Release make -C out -``` +$ mkdir -p build -Run `./gyp_uv.py -f make -Dtarget_arch=x32` to build [x32][] binaries. +$ (cd build && cmake .. -DBUILD_TESTING=ON) # generate project with tests +$ cmake --build build # add `-j ` with cmake >= 3.12 -### OS X +# Run tests: +$ (cd build && ctest -C Debug --output-on-failure) -Run: +# Or manually run tests: +$ build/uv_run_tests # shared library build +$ build/uv_run_tests_a # static library build +``` + +To cross-compile with [CMake][] (unsupported but generally works): ```bash -$ ./gyp_uv.py -f xcode -$ xcodebuild -ARCHS="x86_64" -project out/uv.xcodeproj -configuration Release -alltargets +$ cmake ../.. \ + -DCMAKE_SYSTEM_NAME=Windows \ + -DCMAKE_SYSTEM_VERSION=6.1 \ + -DCMAKE_C_COMPILER=i686-w64-mingw32-gcc ``` -Using Homebrew: +### Install with Homebrew ```bash $ brew install --HEAD libuv @@ -277,110 +220,48 @@ 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"). -### Android - -Run: - -For arm - -```bash -$ source ./android-configure-arm NDK_PATH gyp [API_LEVEL] -$ make -C out -``` - -or for arm64 - -```bash -$ source ./android-configure-arm64 NDK_PATH gyp [API_LEVEL] -$ make -C out -``` - -or for x86 - -```bash -$ source ./android-configure-x86 NDK_PATH gyp [API_LEVEL] -$ make -C out -``` - -or for x86_64 - -```bash -$ source ./android-configure-x86_64 NDK_PATH gyp [API_LEVEL] -$ make -C out -``` - -The default API level is 24, but a different one can be selected as follows: - -```bash -$ source ./android-configure-arm ~/android-ndk-r15b gyp 21 -$ make -C out -``` - -Note for UNIX users: compile your project with `-D_LARGEFILE_SOURCE` and -`-D_FILE_OFFSET_BITS=64`. GYP builds take care of that automatically. - -### Using Ninja - -To use ninja for build on ninja supported platforms, run: - -```bash -$ ./gyp_uv.py -f ninja -$ ninja -C out/Debug #for debug build OR -$ ninja -C out/Release -``` - - ### Running tests -#### Build - -Build (includes tests): - -```bash -$ ./gyp_uv.py -f make -$ make -C out -``` - -#### Run all tests - -```bash -$ ./out/Debug/run-tests -``` - Some tests are timing sensitive. Relaxing test timeouts may be necessary on slow or overloaded machines: ```bash -$ env UV_TEST_TIMEOUT_MULTIPLIER=2 ./out/Debug/run-tests # 10s instead of 5s +$ env UV_TEST_TIMEOUT_MULTIPLIER=2 build/uv_run_tests # 10s instead of 5s ``` #### Run one test The list of all tests is in `test/test-list.h`. -This invocation will cause the `run-tests` driver to fork and execute `TEST_NAME` in a child process: +This invocation will cause the test driver to fork and execute `TEST_NAME` in +a child process: ```bash -$ ./out/Debug/run-tests TEST_NAME +$ build/uv_run_tests_a TEST_NAME ``` -This invocation will cause the `run-tests` driver to execute the test within the `run-tests` process: +This invocation will cause the test driver to execute the test in +the same process: ```bash -$ ./out/Debug/run-tests TEST_NAME TEST_NAME +$ build/uv_run_tests_a TEST_NAME TEST_NAME ``` #### Debugging tools -When running the test from within the `run-tests` process (`run-tests TEST_NAME TEST_NAME`), tools like gdb and valgrind work normally. -When running the test from a child of the `run-tests` process (`run-tests TEST_NAME`), use these tools in a fork-aware manner. +When running the test from within the test driver process +(`build/uv_run_tests_a TEST_NAME TEST_NAME`), tools like gdb and valgrind +work normally. + +When running the test from a child of the test driver process +(`build/uv_run_tests_a TEST_NAME`), use these tools in a fork-aware manner. ##### Fork-aware gdb Use the [follow-fork-mode](https://sourceware.org/gdb/onlinedocs/gdb/Forks.html) setting: ``` -$ gdb --args out/Debug/run-tests TEST_NAME +$ gdb --args build/uv_run_tests_a TEST_NAME (gdb) set follow-fork-mode child ... @@ -391,18 +272,29 @@ $ gdb --args out/Debug/run-tests TEST_NAME Use the `--trace-children=yes` parameter: ```bash -$ valgrind --trace-children=yes -v --tool=memcheck --leak-check=full --track-origins=yes --leak-resolution=high --show-reachable=yes --log-file=memcheck-%p.log out/Debug/run-tests TEST_NAME +$ valgrind --trace-children=yes -v --tool=memcheck --leak-check=full --track-origins=yes --leak-resolution=high --show-reachable=yes --log-file=memcheck-%p.log build/uv_run_tests_a TEST_NAME ``` ### Running benchmarks See the section on running tests. -The benchmark driver is `out/Debug/run-benchmarks` and the benchmarks are listed in `test/benchmark-list.h`. +The benchmark driver is `./uv_run_benchmarks_a` and the benchmarks are +listed in `test/benchmark-list.h`. ## Supported Platforms Check the [SUPPORTED_PLATFORMS file](SUPPORTED_PLATFORMS.md). +### `-fno-strict-aliasing` + +It is recommended to turn on the `-fno-strict-aliasing` compiler flag in +projects that use libuv. The use of ad hoc "inheritance" in the libuv API +may not be safe in the presence of compiler optimizations that depend on +strict aliasing. + +MSVC does not have an equivalent flag but it also does not appear to need it +at the time of writing (December 2019.) + ### AIX Notes AIX compilation using IBM XL C/C++ requires version 12.1 or greater. @@ -413,10 +305,15 @@ that is detected by `autoconf`. [IBM documentation](http://www.ibm.com/developerworks/aix/library/au-aix_event_infrastructure/) describes the package in more detail. -AIX support for filesystem events is not compiled when building with `gyp`. - ### z/OS Notes +z/OS compilation requires [ZOSLIB](https://github.com/ibmruntimes/zoslib) to be installed. When building with [CMake][], use the flag `-DZOSLIB_DIR` to specify the path to [ZOSLIB](https://github.com/ibmruntimes/zoslib): + +```bash +$ (cd build && cmake .. -DBUILD_TESTING=ON -DZOSLIB_DIR=/path/to/zoslib) +$ cmake --build build +``` + z/OS creates System V semaphores and message queues. These persist on the system after the process terminates unless the event loop is closed. @@ -426,12 +323,10 @@ Use the `ipcrm` command to manually clear up System V resources. See the [guidelines for contributing][]. +[CMake]: https://cmake.org/ [node.js]: http://nodejs.org/ -[GYP]: http://code.google.com/p/gyp/ [guidelines for contributing]: https://github.com/libuv/libuv/blob/master/CONTRIBUTING.md [libuv_banner]: https://raw.githubusercontent.com/libuv/libuv/master/img/banner.png -[x32]: https://en.wikipedia.org/wiki/X32_ABI -[Python 2.6 or 2.7]: https://www.python.org/downloads/ [Visual C++ Build Tools]: https://visualstudio.microsoft.com/visual-cpp-build-tools/ [Visual Studio 2015 Update 3]: https://www.visualstudio.com/vs/older-downloads/ [Visual Studio 2017]: https://www.visualstudio.com/downloads/ diff --git a/SUPPORTED_PLATFORMS.md b/SUPPORTED_PLATFORMS.md index 29e4844..3a58f18 100644 --- a/SUPPORTED_PLATFORMS.md +++ b/SUPPORTED_PLATFORMS.md @@ -3,15 +3,15 @@ | 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 7 | MSVC 2008 and later are supported | +| macOS | Tier 1 | macOS >= 10.15 | Current and previous macOS release | +| Windows | Tier 1 | >= Windows 8 | VS 2015 and later are supported | | FreeBSD | Tier 1 | >= 10 | | | 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 2 | >= 14.4 | Maintainers: @libuv/smartos | -| Android | Tier 3 | NDK >= r15b | | -| IBM i | Tier 3 | >= IBM i 7.2 | Maintainers: @libuv/ibmi | +| SmartOS | Tier 3 | >= 14.4 | | +| Android | Tier 3 | NDK >= r15b | Android 7.0, `-DANDROID_PLATFORM=android-24` | | MinGW | Tier 3 | MinGW32 and MinGW-w64 | | | SunOS | Tier 3 | Solaris 121 and later | | | Other | Tier 3 | N/A | | @@ -47,8 +47,9 @@ All functionality related to the new platform must be implemented in its own file inside ``src/unix/`` unless it's already done in a common file, in which case adding an `ifdef` is fine. -Two build systems are supported: autotools and GYP. Ideally both need to be -supported, but if GYP does not support the new platform it can be left out. +Two build systems are supported: autotools and cmake. Ideally both need to be +supported, but if one of the two does not support the new platform it can be +left out. ### Windows diff --git a/android-configure-arm b/android-configure-arm deleted file mode 100644 index 331fdd9..0000000 --- a/android-configure-arm +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -export TOOLCHAIN=$PWD/android-toolchain-arm -mkdir -p $TOOLCHAIN -API=${3:-24} -$1/build/tools/make-standalone-toolchain.sh \ - --toolchain=arm-linux-androideabi-4.9 \ - --arch=arm \ - --install-dir=$TOOLCHAIN \ - --platform=android-$API \ - --force -export PATH=$TOOLCHAIN/bin:$PATH -export AR=arm-linux-androideabi-ar -export CC=arm-linux-androideabi-gcc -export CXX=arm-linux-androideabi-g++ -export LINK=arm-linux-androideabi-g++ -export PLATFORM=android -export CFLAGS="-D__ANDROID_API__=$API" - -if [[ $2 == 'gyp' ]] - then - ./gyp_uv.py -Dtarget_arch=arm -DOS=android -f make-android -fi diff --git a/android-configure-arm64 b/android-configure-arm64 deleted file mode 100644 index 1acd905..0000000 --- a/android-configure-arm64 +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -export TOOLCHAIN=$PWD/android-toolchain-arm64 -mkdir -p $TOOLCHAIN -API=${3:-24} -$1/build/tools/make-standalone-toolchain.sh \ - --toolchain=aarch64-linux-android-4.9 \ - --arch=arm64 \ - --install-dir=$TOOLCHAIN \ - --platform=android-$API \ - --force -export PATH=$TOOLCHAIN/bin:$PATH -export AR=aarch64-linux-android-ar -export CC=aarch64-linux-android-gcc -export CXX=aarch64-linux-android-g++ -export LINK=aarch64-linux-android-g++ -export PLATFORM=android -export CFLAGS="-D__ANDROID_API__=$API" - -if [[ $2 == 'gyp' ]] - then - ./gyp_uv.py -Dtarget_arch=arm64 -DOS=android -f make-android -fi diff --git a/android-configure-x86 b/android-configure-x86 deleted file mode 100644 index a149715..0000000 --- a/android-configure-x86 +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -export TOOLCHAIN=$PWD/android-toolchain-x86 -mkdir -p $TOOLCHAIN -API=${3:-24} -$1/build/tools/make-standalone-toolchain.sh \ - --toolchain=x86-4.9 \ - --arch=x86 \ - --install-dir=$TOOLCHAIN \ - --platform=android-$API \ - --force -export PATH=$TOOLCHAIN/bin:$PATH -export AR=i686-linux-android-ar -export CC=i686-linux-android-gcc -export CXX=i686-linux-android-g++ -export LINK=i686-linux-android-g++ -export PLATFORM=android -export CFLAGS="-D__ANDROID_API__=$API" - -if [[ $2 == 'gyp' ]] - then - ./gyp_uv.py -Dtarget_arch=x86 -DOS=android -f make-android -fi diff --git a/android-configure-x86_64 b/android-configure-x86_64 deleted file mode 100644 index ff04595..0000000 --- a/android-configure-x86_64 +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -export TOOLCHAIN=$PWD/android-toolchain-x86_64 -mkdir -p $TOOLCHAIN -API=${3:-24} -$1/build/tools/make-standalone-toolchain.sh \ - --toolchain=x86_64-4.9 \ - --arch=x86_64 \ - --install-dir=$TOOLCHAIN \ - --platform=android-$API \ - --force -export PATH=$TOOLCHAIN/bin:$PATH -export AR=x86_64-linux-android-ar -export CC=x86_64-linux-android-gcc -export CXX=x86_64-linux-android-g++ -export LINK=x86_64-linux-android-g++ -export PLATFORM=android -export CFLAGS="-D__ANDROID_API__=$API -fPIC" -export CXXFLAGS="-D__ANDROID_API__=$API -fPIC" -export LDFLAGS="-fPIC" - -if [[ $2 == 'gyp' ]] - then - ./gyp_uv.py -Dtarget_arch=x86_64 -DOS=android -f make-android -fi diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 1b018a5..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,32 +0,0 @@ -version: v1.18.0.build{build} - -init: - - git config --global core.autocrlf true - -install: - - cinst -y nsis - -matrix: - fast_finish: true - allow_failures: - - platform: x86 - configuration: Release - - platform: x64 - configuration: Release - -platform: - - x86 - - x64 - -configuration: - - Release - -build_script: - # Fixed tag version number if using a tag. - - cmd: if "%APPVEYOR_REPO_TAG%" == "true" set APPVEYOR_BUILD_VERSION=%APPVEYOR_REPO_TAG_NAME% - # vcbuild overwrites the platform variable. - - cmd: set ARCH=%platform% - - cmd: vcbuild.bat release %ARCH% shared - -cache: - - C:\projects\libuv\build\gyp diff --git a/common.gypi b/common.gypi deleted file mode 100644 index 8e2aa71..0000000 --- a/common.gypi +++ /dev/null @@ -1,213 +0,0 @@ -{ - 'variables': { - 'target_arch%': 'ia32', # set v8's target architecture - 'host_arch%': 'ia32', # set v8's host architecture - 'uv_library%': 'static_library', # allow override to 'shared_library' for DLL/.so builds - 'msvs_multi_core_compile': '0', # we do enable multicore compiles, but not using the V8 way - }, - - 'target_defaults': { - 'default_configuration': 'Debug', - 'configurations': { - 'Debug': { - 'defines': [ 'DEBUG', '_DEBUG' ], - 'cflags': [ '-g' ], - 'msvs_settings': { - 'VCCLCompilerTool': { - 'target_conditions': [ - ['uv_library=="static_library"', { - 'RuntimeLibrary': 1, # /MTd static debug - }, { - 'RuntimeLibrary': 3, # /MDd DLL debug - }], - ], - 'Optimization': 0, # /Od, no optimization - 'MinimalRebuild': 'false', - 'OmitFramePointers': 'false', - 'BasicRuntimeChecks': 3, # /RTC1 - }, - 'VCLinkerTool': { - 'LinkIncremental': 2, # enable incremental linking - }, - }, - 'xcode_settings': { - 'GCC_OPTIMIZATION_LEVEL': '0', - }, - 'conditions': [ - ['OS != "zos"', { - 'cflags': [ '-O0', '-fno-common', '-fwrapv' ] - }], - ['OS == "android"', { - 'cflags': [ '-fPIE' ], - 'ldflags': [ '-fPIE', '-pie' ] - }] - ] - }, - 'Release': { - 'defines': [ 'NDEBUG' ], - 'cflags': [ - '-O3', - ], - 'msvs_settings': { - 'VCCLCompilerTool': { - 'target_conditions': [ - ['uv_library=="static_library"', { - 'RuntimeLibrary': 0, # /MT static release - }, { - 'RuntimeLibrary': 2, # /MD DLL release - }], - ], - 'Optimization': 3, # /Ox, full optimization - 'FavorSizeOrSpeed': 1, # /Ot, favour speed over size - 'InlineFunctionExpansion': 2, # /Ob2, inline anything eligible - 'WholeProgramOptimization': 'true', # /GL, whole program optimization, needed for LTCG - 'OmitFramePointers': 'true', - 'EnableFunctionLevelLinking': 'true', - 'EnableIntrinsicFunctions': 'true', - }, - 'VCLibrarianTool': { - 'AdditionalOptions': [ - '/LTCG', # link time code generation - ], - }, - 'VCLinkerTool': { - 'LinkTimeCodeGeneration': 1, # link-time code generation - 'OptimizeReferences': 2, # /OPT:REF - 'EnableCOMDATFolding': 2, # /OPT:ICF - 'LinkIncremental': 1, # disable incremental linking - }, - }, - 'conditions': [ - ['OS != "zos"', { - 'cflags': [ - '-fdata-sections', - '-ffunction-sections', - '-fno-common', - '-fomit-frame-pointer', - ], - }], - ] - } - }, - 'msvs_settings': { - 'VCCLCompilerTool': { - 'StringPooling': 'true', # pool string literals - 'DebugInformationFormat': 3, # Generate a PDB - 'WarningLevel': 3, - 'BufferSecurityCheck': 'true', - 'ExceptionHandling': 1, # /EHsc - 'SuppressStartupBanner': 'true', - 'WarnAsError': 'false', - 'AdditionalOptions': [ - '/MP', # compile across multiple CPUs - ], - }, - 'VCLibrarianTool': { - }, - 'VCLinkerTool': { - 'GenerateDebugInformation': 'true', - 'RandomizedBaseAddress': 2, # enable ASLR - 'DataExecutionPrevention': 2, # enable DEP - 'AllowIsolation': 'true', - 'SuppressStartupBanner': 'true', - 'target_conditions': [ - ['_type=="executable"', { - 'SubSystem': 1, # console executable - }], - ], - }, - }, - 'conditions': [ - ['OS == "win"', { - 'msvs_cygwin_shell': 0, # prevent actions from trying to use cygwin - 'defines': [ - 'WIN32', - # we don't really want VC++ warning us about - # how dangerous C functions are... - '_CRT_SECURE_NO_DEPRECATE', - # ... or that C implementations shouldn't use - # POSIX names - '_CRT_NONSTDC_NO_DEPRECATE', - ], - 'target_conditions': [ - ['target_arch=="x64"', { - 'msvs_configuration_platform': 'x64' - }] - ] - }], - ['OS in "freebsd dragonflybsd linux openbsd solaris android aix"', { - 'cflags': [ '-Wall' ], - 'cflags_cc': [ '-fno-rtti', '-fno-exceptions' ], - 'target_conditions': [ - ['_type=="static_library"', { - 'standalone_static_library': 1, # disable thin archive which needs binutils >= 2.19 - }], - ], - 'conditions': [ - [ 'host_arch != target_arch and target_arch=="ia32"', { - 'cflags': [ '-m32' ], - 'ldflags': [ '-m32' ], - }], - [ 'target_arch=="x32"', { - 'cflags': [ '-mx32' ], - 'ldflags': [ '-mx32' ], - }], - [ 'OS=="linux"', { - 'cflags': [ '-ansi' ], - }], - [ 'OS=="solaris"', { - 'cflags': [ '-pthreads' ], - 'ldflags': [ '-pthreads' ], - }], - [ 'OS not in "solaris android zos"', { - 'cflags': [ '-pthread' ], - 'ldflags': [ '-pthread' ], - }], - [ 'OS=="aix" and target_arch=="ppc64"', { - 'cflags': [ '-maix64' ], - 'ldflags': [ '-maix64' ], - }], - ], - }], - ['OS=="mac"', { - 'xcode_settings': { - 'ALWAYS_SEARCH_USER_PATHS': 'NO', - 'GCC_CW_ASM_SYNTAX': 'NO', # No -fasm-blocks - 'GCC_DYNAMIC_NO_PIC': 'NO', # No -mdynamic-no-pic - # (Equivalent to -fPIC) - 'GCC_ENABLE_CPP_EXCEPTIONS': 'NO', # -fno-exceptions - 'GCC_ENABLE_CPP_RTTI': 'NO', # -fno-rtti - 'GCC_ENABLE_PASCAL_STRINGS': 'NO', # No -mpascal-strings - 'GCC_THREADSAFE_STATICS': 'NO', # -fno-threadsafe-statics - 'PREBINDING': 'NO', # No -Wl,-prebind - 'USE_HEADERMAP': 'NO', - 'WARNING_CFLAGS': [ - '-Wall', - '-Wendif-labels', - '-W', - '-Wno-unused-parameter', - '-Wstrict-prototypes', - ], - }, - 'conditions': [ - ['target_arch=="ia32"', { - 'xcode_settings': {'ARCHS': ['i386']}, - }], - ['target_arch=="x64"', { - 'xcode_settings': {'ARCHS': ['x86_64']}, - }], - ], - 'target_conditions': [ - ['_type!="static_library"', { - 'xcode_settings': {'OTHER_LDFLAGS': ['-Wl,-search_paths_first']}, - }], - ], - }], - ['OS=="solaris"', { - 'cflags': [ '-fno-omit-frame-pointer' ], - # pull in V8's postmortem metadata - 'ldflags': [ '-Wl,-z,allextract' ] - }], - ], - }, -} diff --git a/configure.ac b/configure.ac index d05ec87..bd1e9c6 100644 --- a/configure.ac +++ b/configure.ac @@ -13,7 +13,7 @@ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. AC_PREREQ(2.57) -AC_INIT([libuv], [1.35.0], [https://github.com/libuv/libuv/issues]) +AC_INIT([libuv], [1.44.1], [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]) @@ -24,7 +24,11 @@ AC_ENABLE_SHARED AC_ENABLE_STATIC AC_PROG_CC AM_PROG_CC_C_O -CC_FLAG_VISIBILITY #[-fvisibility=hidden] + +CC_ATTRIBUTE_VISIBILITY([default], [ + CC_FLAG_VISIBILITY([CFLAGS="${CFLAGS} -fvisibility=hidden"]) +]) +CC_CHECK_CFLAGS_APPEND([-fno-strict-aliasing]) CC_CHECK_CFLAGS_APPEND([-g]) CC_CHECK_CFLAGS_APPEND([-std=gnu89]) CC_CHECK_CFLAGS_APPEND([-Wall]) @@ -38,15 +42,17 @@ m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) AC_PROG_LIBTOOL m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) LT_INIT -# TODO(bnoordhuis) Check for -pthread vs. -pthreads -AC_CHECK_LIB([dl], [dlopen]) -AC_CHECK_LIB([kstat], [kstat_lookup]) -AC_CHECK_LIB([nsl], [gethostbyname]) -AC_CHECK_LIB([perfstat], [perfstat_cpu]) -AC_CHECK_LIB([pthread], [pthread_mutex_init]) -AC_CHECK_LIB([rt], [clock_gettime]) -AC_CHECK_LIB([sendfile], [sendfile]) -AC_CHECK_LIB([socket], [socket]) +AX_PTHREAD([ + LIBS="$LIBS $PTHREAD_LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +]) +AC_SEARCH_LIBS([dlopen], [dl]) +AC_SEARCH_LIBS([kstat_lookup], [kstat]) +AC_SEARCH_LIBS([gethostbyname], [nsl]) +AC_SEARCH_LIBS([perfstat_cpu], [perfstat]) +AC_SEARCH_LIBS([clock_gettime], [rt]) +AC_SEARCH_LIBS([sendfile], [sendfile]) +AC_SEARCH_LIBS([socket], [socket]) AC_SYS_LARGEFILE AM_CONDITIONAL([AIX], [AS_CASE([$host_os],[aix*], [true], [false])]) AM_CONDITIONAL([ANDROID], [AS_CASE([$host_os],[linux-android*],[true], [false])]) diff --git a/docs/code/.gitignore b/docs/code/.gitignore new file mode 100644 index 0000000..c46ecde --- /dev/null +++ b/docs/code/.gitignore @@ -0,0 +1,3 @@ +*/* +!*.c +!*.h diff --git a/docs/code/default-loop/main.c b/docs/code/default-loop/main.c new file mode 100644 index 0000000..e00a4d2 --- /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/dns/main.c b/docs/code/dns/main.c index 77a7005..2d63f1a 100644 --- a/docs/code/dns/main.c +++ b/docs/code/dns/main.c @@ -69,8 +69,8 @@ int main() { hints.ai_flags = 0; uv_getaddrinfo_t resolver; - fprintf(stderr, "irc.freenode.net is... "); - int r = uv_getaddrinfo(loop, &resolver, on_resolved, "irc.freenode.net", "6667", &hints); + fprintf(stderr, "irc.libera.chat is... "); + int r = uv_getaddrinfo(loop, &resolver, on_resolved, "irc.libera.chat", "6667", &hints); if (r) { fprintf(stderr, "getaddrinfo call error %s\n", uv_err_name(r)); diff --git a/docs/code/multi-echo-server/main.c b/docs/code/multi-echo-server/main.c index 25f4961..b938a7d 100644 --- a/docs/code/multi-echo-server/main.c +++ b/docs/code/multi-echo-server/main.c @@ -70,7 +70,7 @@ void setup_workers() { child_worker_count = cpu_count; - workers = calloc(sizeof(struct child_worker), cpu_count); + workers = calloc(cpu_count, sizeof(struct child_worker)); while (cpu_count--) { struct child_worker *worker = &workers[cpu_count]; uv_pipe_init(loop, &worker->pipe, 1); diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..8386e01 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,42 @@ +# primary +Sphinx==3.5.4 + +# 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 +sphinxcontrib-devhelp==1.0.2 +sphinxcontrib-htmlhelp==1.0.3 +sphinxcontrib-jsmath==1.0.1 +sphinxcontrib-qthelp==1.0.3 +sphinxcontrib-serializinghtml==1.1.4 +urllib3==1.25.8 +webencodings==0.5.1 diff --git a/docs/src/api.rst b/docs/src/api.rst index 22f0640..c8e837d 100644 --- a/docs/src/api.rst +++ b/docs/src/api.rst @@ -32,4 +32,5 @@ API documentation dll threading misc + metrics diff --git a/docs/src/async.rst b/docs/src/async.rst index bf61169..029c051 100644 --- a/docs/src/async.rst +++ b/docs/src/async.rst @@ -51,7 +51,7 @@ API loop thread. .. note:: - :c:func:`uv_async_send` is `async-signal-safe `_. + :c:func:`uv_async_send` is `async-signal-safe `_. It's safe to call this function from a signal handler. .. warning:: diff --git a/docs/src/check.rst b/docs/src/check.rst index 36c93cf..33aab55 100644 --- a/docs/src/check.rst +++ b/docs/src/check.rst @@ -33,14 +33,22 @@ API .. c:function:: int uv_check_init(uv_loop_t* loop, uv_check_t* check) - Initialize the handle. + Initialize the handle. This function always succeeds. + + :returns: 0 .. c:function:: int uv_check_start(uv_check_t* check, uv_check_cb cb) - Start the handle with the given callback. + Start the handle with the given callback. This function always succeeds, + except when `cb` is `NULL`. + + :returns: 0 on success, or `UV_EINVAL` when `cb == NULL`. .. c:function:: int uv_check_stop(uv_check_t* check) Stop the handle, the callback will no longer be called. + This function always succeeds. + + :returns: 0 .. seealso:: The :c:type:`uv_handle_t` API functions also apply. diff --git a/docs/src/design.rst b/docs/src/design.rst index a23e33a..0f5580c 100644 --- a/docs/src/design.rst +++ b/docs/src/design.rst @@ -125,7 +125,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 b8f971f..c7240f3 100644 --- a/docs/src/errors.rst +++ b/docs/src/errors.rst @@ -251,6 +251,10 @@ Error constants operation not supported on socket +.. c:macro:: UV_EOVERFLOW + + value too large for defined data type + .. c:macro:: UV_EPERM operation not permitted @@ -319,11 +323,27 @@ Error constants too many links +.. c:macro:: UV_ENOTTY + + inappropriate ioctl for device + +.. c:macro:: UV_EFTYPE + + inappropriate file type or format + +.. c:macro:: UV_EILSEQ + + illegal byte sequence + +.. c:macro:: UV_ESOCKTNOSUPPORT + + socket type not supported + API --- -.. c:function:: UV_ERRNO_MAP(iter_macro) +.. c:macro:: UV_ERRNO_MAP(iter_macro) Macro that expands to a series of invocations of `iter_macro` for each of the error constants above. `iter_macro` is invoked with two diff --git a/docs/src/fs.rst b/docs/src/fs.rst index 2943ead..0bf2abe 100644 --- a/docs/src/fs.rst +++ b/docs/src/fs.rst @@ -58,7 +58,7 @@ Data types uv_timespec_t st_birthtim; } uv_stat_t; -.. c:type:: uv_fs_type +.. c:enum:: uv_fs_type File system request type. @@ -100,7 +100,8 @@ Data types UV_FS_OPENDIR, UV_FS_READDIR, UV_FS_CLOSEDIR, - UV_FS_MKSTEMP + UV_FS_MKSTEMP, + UV_FS_LUTIME } uv_fs_type; .. c:type:: uv_statfs_t @@ -121,7 +122,7 @@ Data types uint64_t f_spare[4]; } uv_statfs_t; -.. c:type:: uv_dirent_t +.. c:enum:: uv_dirent_t Cross platform (reduced) equivalent of ``struct dirent``. Used in :c:func:`uv_fs_scandir_next`. @@ -402,12 +403,17 @@ API .. c:function:: int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb) .. c:function:: int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, double mtime, uv_fs_cb cb) +.. c:function:: int uv_fs_lutime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb) - Equivalent to :man:`utime(2)` and :man:`futimes(3)` respectively. + Equivalent to :man:`utime(2)`, :man:`futimes(3)` and :man:`lutimes(3)` respectively. .. note:: - AIX: This function only works for AIX 7.1 and newer. It can still be called on older - versions but will return ``UV_ENOSYS``. + z/OS: `uv_fs_lutime()` is not implemented for z/OS. It can still be called but will return + ``UV_ENOSYS``. + + .. note:: + AIX: `uv_fs_futime()` and `uv_fs_lutime()` functions only work for AIX 7.1 and newer. + They can still be called on older versions but will return ``UV_ENOSYS``. .. versionchanged:: 1.10.0 sub-second precission is supported on Windows @@ -486,6 +492,13 @@ API .. versionadded:: 1.19.0 +.. c:function:: int uv_fs_get_system_error(const uv_fs_t* req) + + Returns the platform specific error code - `GetLastError()` value on Windows + and `-(req->result)` on other platforms. + + .. versionadded:: 1.38.0 + .. c:function:: void* uv_fs_get_ptr(const uv_fs_t* req) Returns `req->ptr`. @@ -522,8 +535,8 @@ Helper functions For a OS-dependent handle, get the file descriptor in the C runtime. On UNIX, returns the ``os_fd`` intact. On Windows, this calls `_open_osfhandle `_. - Note that the return value is still owned by the CRT, - any attempts to close it or to use it after closing the handle may lead to malfunction. + Note that this consumes the argument, any attempts to close it or to use it + after closing the return value may lead to malfunction. .. versionadded:: 1.23.0 diff --git a/docs/src/guide/basics.rst b/docs/src/guide/basics.rst index 1da8d5e..2b21d73 100644 --- a/docs/src/guide/basics.rst +++ b/docs/src/guide/basics.rst @@ -42,7 +42,7 @@ as other activities and other I/O operations are kept waiting. One of the standard solutions is to use threads. Each blocking I/O operation is started in a separate thread (or in a thread pool). When the blocking function -gets invoked in the thread, the processor can schedule another thread to run, +gets invoked in the thread, the operating system can schedule another thread to run, which actually needs the CPU. The approach followed by libuv uses another style, which is the **asynchronous, @@ -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 @@ -87,6 +87,7 @@ nothing, except start a loop which will exit immediately. .. rubric:: helloworld/main.c .. literalinclude:: ../../code/helloworld/main.c + :language: c :linenos: This program quits immediately because it has no events to process. A libuv @@ -108,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 @@ -118,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. @@ -133,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 @@ -168,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 @@ -202,6 +218,7 @@ event watchers are active. .. rubric:: idle-basic/main.c .. literalinclude:: ../../code/idle-basic/main.c + :language: c :emphasize-lines: 6,10,14-17 Storing context diff --git a/docs/src/guide/eventloops.rst b/docs/src/guide/eventloops.rst index fd9df5f..12244ff 100644 --- a/docs/src/guide/eventloops.rst +++ b/docs/src/guide/eventloops.rst @@ -20,6 +20,7 @@ these things can be a bit difficult to understand, so let's look at .. rubric:: src/unix/core.c - uv_run .. literalinclude:: ../../../src/unix/core.c + :language: c :linenos: :lines: 304-324 :emphasize-lines: 10,19,21 @@ -43,6 +44,7 @@ iteration of the loop still takes places. .. rubric:: uvstop/main.c .. literalinclude:: ../../code/uvstop/main.c + :language: c :linenos: :emphasize-lines: 11 diff --git a/docs/src/guide/filesystem.rst b/docs/src/guide/filesystem.rst index 63dbbe4..c0bfbf5 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*. @@ -33,7 +33,7 @@ A file descriptor is obtained using int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb) ``flags`` and ``mode`` are standard -`Unix flags `_. +`Unix flags `_. libuv takes care of converting to the appropriate Windows flags. File descriptors are closed using @@ -54,6 +54,7 @@ a callback for when the file is opened: .. rubric:: uvcat/main.c - opening a file .. literalinclude:: ../../code/uvcat/main.c + :language: c :linenos: :lines: 41-53 :emphasize-lines: 4, 6-7 @@ -63,8 +64,9 @@ The ``result`` field of a ``uv_fs_t`` is the file descriptor in case of the .. rubric:: uvcat/main.c - read callback .. 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 @@ -87,8 +89,9 @@ callbacks. .. rubric:: uvcat/main.c - write callback .. literalinclude:: ../../code/uvcat/main.c + :language: c :linenos: - :lines: 16-24 + :lines: 17-24 :emphasize-lines: 6 .. warning:: @@ -100,6 +103,7 @@ We set the dominos rolling in ``main()``: .. rubric:: uvcat/main.c .. literalinclude:: ../../code/uvcat/main.c + :language: c :linenos: :lines: 55- :emphasize-lines: 2 @@ -128,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); @@ -145,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); @@ -154,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: @@ -186,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 @@ -203,8 +210,9 @@ opened as bidirectional by default. .. rubric:: uvtee/main.c - read on pipes .. 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 @@ -218,6 +226,7 @@ these buffers. .. rubric:: uvtee/main.c - reading buffers .. literalinclude:: ../../code/uvtee/main.c + :language: c :linenos: :lines: 19-22,44-60 @@ -242,6 +251,7 @@ point there is nothing to be read. Most applications will just ignore this. .. rubric:: uvtee/main.c - Write to pipe .. literalinclude:: ../../code/uvtee/main.c + :language: c :linenos: :lines: 9-13,23-42 @@ -278,10 +288,18 @@ 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 .. literalinclude:: ../../code/onchange/main.c + :language: c :linenos: :lines: 26- :emphasize-lines: 15 @@ -292,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, @@ -311,15 +329,16 @@ 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()``. .. rubric:: onchange/main.c - file change notification callback .. literalinclude:: ../../code/onchange/main.c + :language: c :linenos: :lines: 9-24 diff --git a/docs/src/guide/introduction.rst b/docs/src/guide/introduction.rst index 0a3af43..819e9f7 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,29 +47,27 @@ 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 .. _node.js: https://www.nodejs.org .. _libev was removed: https://github.com/joyent/libuv/issues/485 .. _Rust: https://www.rust-lang.org -.. _variety: https://github.com/libuv/libuv/wiki/Projects-that-use-libuv +.. _variety: https://github.com/libuv/libuv/blob/v1.x/LINKS.md diff --git a/docs/src/guide/networking.rst b/docs/src/guide/networking.rst index e61f899..dcb5643 100644 --- a/docs/src/guide/networking.rst +++ b/docs/src/guide/networking.rst @@ -38,6 +38,7 @@ Here is a simple echo server .. rubric:: tcp-echo-server/main.c - The listen socket .. literalinclude:: ../../code/tcp-echo-server/main.c + :language: c :linenos: :lines: 68- :emphasize-lines: 4-5,7-10 @@ -60,6 +61,7 @@ In this case we also establish interest in reading from this stream. .. rubric:: tcp-echo-server/main.c - Accepting the client .. literalinclude:: ../../code/tcp-echo-server/main.c + :language: c :linenos: :lines: 51-66 :emphasize-lines: 9-10 @@ -108,6 +110,7 @@ address from a `DHCP`_ server -- DHCP Discover. .. rubric:: udp-dhcp/main.c - Setup and send UDP packets .. literalinclude:: ../../code/udp-dhcp/main.c + :language: c :linenos: :lines: 7-11,104- :emphasize-lines: 8,10-11,17-18,21 @@ -143,6 +146,7 @@ the OS will discard the data that could not fit* (That's UDP for you!). .. rubric:: udp-dhcp/main.c - Reading packets .. literalinclude:: ../../code/udp-dhcp/main.c + :language: c :linenos: :lines: 17-40 :emphasize-lines: 1,23 @@ -189,10 +193,11 @@ Querying DNS libuv provides asynchronous DNS resolution. For this it provides its own ``getaddrinfo`` replacement [#]_. In the callback you can perform normal socket operations on the retrieved addresses. Let's connect to -Freenode to see an example of DNS resolution. +Libera.chat to see an example of DNS resolution. .. rubric:: dns/main.c .. literalinclude:: ../../code/dns/main.c + :language: c :linenos: :lines: 61- :emphasize-lines: 12 @@ -200,7 +205,7 @@ Freenode to see an example of DNS resolution. If ``uv_getaddrinfo`` returns non-zero, something went wrong in the setup and your callback won't be invoked at all. All arguments can be freed immediately after ``uv_getaddrinfo`` returns. The `hostname`, `servname` and `hints` -structures are documented in `the getaddrinfo man page `_. The +structures are documented in `the getaddrinfo man page `_. The callback can be ``NULL`` in which case the function will run synchronously. In the resolver callback, you can pick any IP from the linked list of ``struct @@ -209,6 +214,7 @@ call ``uv_freeaddrinfo`` in the callback. .. rubric:: dns/main.c .. literalinclude:: ../../code/dns/main.c + :language: c :linenos: :lines: 42-60 :emphasize-lines: 8,16 @@ -227,6 +233,7 @@ useful to allow your service to bind to IP addresses when it starts. .. rubric:: interfaces/main.c .. literalinclude:: ../../code/interfaces/main.c + :language: c :linenos: :emphasize-lines: 9,17 @@ -235,7 +242,7 @@ interface has multiple IPv4/IPv6 addresses, the name will be reported multiple times, with each address being reported once. .. _c-ares: https://c-ares.haxx.se -.. _getaddrinfo: https://www.kernel.org/doc/man-pages/online/pages/man3/getaddrinfo.3.html +.. _getaddrinfo: https://man7.org/linux/man-pages/man3/getaddrinfo.3.html .. _User Datagram Protocol: https://en.wikipedia.org/wiki/User_Datagram_Protocol .. _DHCP: https://tools.ietf.org/html/rfc2131 diff --git a/docs/src/guide/processes.rst b/docs/src/guide/processes.rst index a780627..c1278f1 100644 --- a/docs/src/guide/processes.rst +++ b/docs/src/guide/processes.rst @@ -27,6 +27,7 @@ exits. This is achieved using ``uv_spawn``. .. rubric:: spawn/main.c .. literalinclude:: ../../code/spawn/main.c + :language: c :linenos: :lines: 6-8,15- :emphasize-lines: 11,13-17 @@ -54,6 +55,7 @@ which caused the exit. .. rubric:: spawn/main.c .. literalinclude:: ../../code/spawn/main.c + :language: c :linenos: :lines: 9-12 :emphasize-lines: 3 @@ -104,6 +106,7 @@ does not affect it. .. rubric:: detach/main.c .. literalinclude:: ../../code/detach/main.c + :language: c :linenos: :lines: 9-30 :emphasize-lines: 12,19 @@ -140,6 +143,7 @@ stop watching. Here is a small example demonstrating the various possibilities: .. rubric:: signal/main.c .. literalinclude:: ../../code/signal/main.c + :language: c :linenos: :emphasize-lines: 17-18,27-28 @@ -172,6 +176,7 @@ which is: .. rubric:: proc-streams/test.c .. literalinclude:: ../../code/proc-streams/test.c + :language: c The actual program ``proc-streams`` runs this while sharing only ``stderr``. The file descriptors of the child process are set using the ``stdio`` field in @@ -199,6 +204,7 @@ Then we set the ``fd`` to ``stderr``. .. rubric:: proc-streams/main.c .. literalinclude:: ../../code/proc-streams/main.c + :language: c :linenos: :lines: 15-17,27- :emphasize-lines: 6,10,11,12 @@ -217,12 +223,14 @@ A sample CGI script/executable is: .. rubric:: cgi/tick.c .. literalinclude:: ../../code/cgi/tick.c + :language: c The CGI server combines the concepts from this chapter and :doc:`networking` so that every client is sent ten ticks after which that connection is closed. .. rubric:: cgi/main.c .. literalinclude:: ../../code/cgi/main.c + :language: c :linenos: :lines: 49-63 :emphasize-lines: 10 @@ -232,6 +240,7 @@ Here we simply accept the TCP connection and pass on the socket (*stream*) to .. rubric:: cgi/main.c .. literalinclude:: ../../code/cgi/main.c + :language: c :linenos: :lines: 16, 25-45 :emphasize-lines: 8-9,18,20 @@ -266,10 +275,10 @@ Domain Socket`_, or derived from `mkfifo(1)`_, or it could actually be a This is intended for the purpose of allowing multiple libuv processes to communicate with IPC. This is discussed below. -.. _pipe(7): http://man7.org/linux/man-pages/man7/pipe.7.html -.. _mkfifo(1): http://man7.org/linux/man-pages/man1/mkfifo.1.html -.. _socketpair(2): http://man7.org/linux/man-pages/man2/socketpair.2.html -.. _Unix Domain Socket: http://man7.org/linux/man-pages/man7/unix.7.html +.. _pipe(7): https://man7.org/linux/man-pages/man7/pipe.7.html +.. _mkfifo(1): https://man7.org/linux/man-pages/man1/mkfifo.1.html +.. _socketpair(2): https://man7.org/linux/man-pages/man2/socketpair.2.html +.. _Unix Domain Socket: https://man7.org/linux/man-pages/man7/unix.7.html .. _Named Pipe: https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipes @@ -291,6 +300,7 @@ messaging is no different from TCP, so we'll re-use the echo server example. .. rubric:: pipe-echo-server/main.c .. literalinclude:: ../../code/pipe-echo-server/main.c + :language: c :linenos: :lines: 70- :emphasize-lines: 5,10,14 @@ -330,6 +340,7 @@ it by the master. .. rubric:: multi-echo-server/worker.c .. literalinclude:: ../../code/multi-echo-server/worker.c + :language: c :linenos: :lines: 7-9,81- :emphasize-lines: 6-8 @@ -343,6 +354,7 @@ standard input of the worker, we connect the pipe to ``stdin`` using .. rubric:: multi-echo-server/worker.c .. literalinclude:: ../../code/multi-echo-server/worker.c + :language: c :linenos: :lines: 51-79 :emphasize-lines: 10,15,20 @@ -361,6 +373,7 @@ allow load balancing. .. rubric:: multi-echo-server/main.c .. literalinclude:: ../../code/multi-echo-server/main.c + :language: c :linenos: :lines: 9-13 @@ -369,6 +382,7 @@ master and the individual process. .. rubric:: multi-echo-server/main.c .. literalinclude:: ../../code/multi-echo-server/main.c + :language: c :linenos: :lines: 51,61-95 :emphasize-lines: 17,20-21 @@ -387,6 +401,7 @@ worker in the round-robin. .. rubric:: multi-echo-server/main.c .. literalinclude:: ../../code/multi-echo-server/main.c + :language: c :linenos: :lines: 31-49 :emphasize-lines: 9,12-13 diff --git a/docs/src/guide/threads.rst b/docs/src/guide/threads.rst index fde07bb..3990e44 100644 --- a/docs/src/guide/threads.rst +++ b/docs/src/guide/threads.rst @@ -39,6 +39,7 @@ wait for it to close using ``uv_thread_join()``. .. rubric:: thread-create/main.c .. literalinclude:: ../../code/thread-create/main.c + :language: c :linenos: :lines: 26-36 :emphasize-lines: 3-7 @@ -55,6 +56,7 @@ thread, scheduled pre-emptively by the operating system: .. rubric:: thread-create/main.c .. literalinclude:: ../../code/thread-create/main.c + :language: c :linenos: :lines: 6-14 :emphasize-lines: 2 @@ -124,6 +126,7 @@ example. .. rubric:: locks/main.c - simple rwlocks .. literalinclude:: ../../code/locks/main.c + :language: c :linenos: :emphasize-lines: 13,16,27,31,42,55 @@ -208,6 +211,7 @@ event loop from performing other activities. .. rubric:: queue-work/main.c - lazy fibonacci .. literalinclude:: ../../code/queue-work/main.c + :language: c :linenos: :lines: 17-29 @@ -221,6 +225,7 @@ The trigger is ``uv_queue_work``: .. rubric:: queue-work/main.c .. literalinclude:: ../../code/queue-work/main.c + :language: c :linenos: :lines: 31-44 :emphasize-lines: 10 @@ -248,6 +253,7 @@ up a signal handler for termination. .. rubric:: queue-cancel/main.c .. literalinclude:: ../../code/queue-cancel/main.c + :language: c :linenos: :lines: 43- @@ -256,6 +262,7 @@ When the user triggers the signal by pressing ``Ctrl+C`` we send .. rubric:: queue-cancel/main.c .. literalinclude:: ../../code/queue-cancel/main.c + :language: c :linenos: :lines: 33-41 :emphasize-lines: 6 @@ -265,6 +272,7 @@ with ``status`` set to ``UV_ECANCELED``. .. rubric:: queue-cancel/main.c .. literalinclude:: ../../code/queue-cancel/main.c + :language: c :linenos: :lines: 28-31 :emphasize-lines: 2 @@ -292,8 +300,9 @@ informing the user of the status of running downloads. .. rubric:: progress/main.c .. literalinclude:: ../../code/progress/main.c + :language: c :linenos: - :lines: 7-8,34- + :lines: 7-8,35- :emphasize-lines: 2,11 The async thread communication works *on loops* so although any thread can be @@ -317,8 +326,9 @@ with the async watcher whenever it receives a message. .. rubric:: progress/main.c .. literalinclude:: ../../code/progress/main.c + :language: c :linenos: - :lines: 10-23 + :lines: 10-24 :emphasize-lines: 7-8 In the download function, we modify the progress indicator and queue the message @@ -327,8 +337,9 @@ non-blocking and will return immediately. .. rubric:: progress/main.c .. literalinclude:: ../../code/progress/main.c + :language: c :linenos: - :lines: 30-33 + :lines: 31-34 The callback is a standard libuv pattern, extracting the data from the watcher. @@ -336,8 +347,9 @@ Finally it is important to remember to clean up the watcher. .. rubric:: progress/main.c .. literalinclude:: ../../code/progress/main.c + :language: c :linenos: - :lines: 25-28 + :lines: 26-29 :emphasize-lines: 3 After this example, which showed the abuse of the ``data`` field, bnoordhuis_ diff --git a/docs/src/guide/utilities.rst b/docs/src/guide/utilities.rst index a9fe56c..4657b1b 100644 --- a/docs/src/guide/utilities.rst +++ b/docs/src/guide/utilities.rst @@ -87,6 +87,7 @@ JS object and can be ref/unrefed. .. rubric:: ref-timer/main.c .. literalinclude:: ../../code/ref-timer/main.c + :language: c :linenos: :lines: 5-8, 17- :emphasize-lines: 9 @@ -111,6 +112,7 @@ idle watcher to keep the UI operational. .. rubric:: idle-compute/main.c .. literalinclude:: ../../code/idle-compute/main.c + :language: c :linenos: :lines: 5-9, 34- :emphasize-lines: 13 @@ -123,6 +125,7 @@ keep calling the idle callback again. .. rubric:: idle-compute/main.c .. literalinclude:: ../../code/idle-compute/main.c + :language: c :linenos: :lines: 10-19 @@ -215,6 +218,7 @@ progress with the download whenever libuv notifies of I/O readiness. .. rubric:: uvwget/main.c - The setup .. literalinclude:: ../../code/uvwget/main.c + :language: c :linenos: :lines: 1-9,140- :emphasize-lines: 7,21,24-25 @@ -235,6 +239,7 @@ So we add each argument as an URL .. rubric:: uvwget/main.c - Adding urls .. literalinclude:: ../../code/uvwget/main.c + :language: c :linenos: :lines: 39-56 :emphasize-lines: 13-14 @@ -243,7 +248,7 @@ We let libcurl directly write the data to a file, but much more is possible if you so desire. ``start_timeout`` will be called immediately the first time by libcurl, so -things are set in motion. This simply starts a libuv `timer `_ which +things are set in motion. This simply starts a libuv `timer <#timers>`_ which drives ``curl_multi_socket_action`` with ``CURL_SOCKET_TIMEOUT`` whenever it times out. ``curl_multi_socket_action`` is what drives libcurl, and what we call whenever sockets change state. But before we go into that, we need to poll @@ -251,6 +256,7 @@ on sockets whenever ``handle_socket`` is called. .. rubric:: uvwget/main.c - Setting up polling .. literalinclude:: ../../code/uvwget/main.c + :language: c :linenos: :lines: 102-140 :emphasize-lines: 9,11,15,21,24 @@ -271,6 +277,7 @@ mask with the new value. ``curl_perform`` is the crux of this program. .. rubric:: uvwget/main.c - Driving libcurl. .. literalinclude:: ../../code/uvwget/main.c + :language: c :linenos: :lines: 81-95 :emphasize-lines: 2,6-7,12 @@ -288,6 +295,7 @@ transfers are done. .. rubric:: uvwget/main.c - Reading transfer status. .. literalinclude:: ../../code/uvwget/main.c + :language: c :linenos: :lines: 58-79 :emphasize-lines: 6,9-10,13-14 @@ -312,6 +320,7 @@ Let us first look at the interface provided to plugin authors. .. rubric:: plugin/plugin.h .. literalinclude:: ../../code/plugin/plugin.h + :language: c :linenos: You can similarly add more functions that plugin authors can use to do useful @@ -319,6 +328,7 @@ things in your application [#]_. A sample plugin using this API is: .. rubric:: plugin/hello.c .. literalinclude:: ../../code/plugin/hello.c + :language: c :linenos: Our interface defines that all plugins should have an ``initialize`` function @@ -340,6 +350,7 @@ This is done by using ``uv_dlopen`` to first load the shared library .. rubric:: plugin/main.c .. literalinclude:: ../../code/plugin/main.c + :language: c :linenos: :lines: 7- :emphasize-lines: 15, 18, 24 @@ -393,6 +404,7 @@ Here is a simple example which prints white text on a red background: .. rubric:: tty/main.c .. literalinclude:: ../../code/tty/main.c + :language: c :linenos: :emphasize-lines: 11-12,14,17,27 @@ -403,6 +415,7 @@ escape codes. .. rubric:: tty-gravity/main.c .. literalinclude:: ../../code/tty-gravity/main.c + :language: c :linenos: :emphasize-lines: 19,25,38 diff --git a/docs/src/handle.rst b/docs/src/handle.rst index 943c51d..0edb7d7 100644 --- a/docs/src/handle.rst +++ b/docs/src/handle.rst @@ -20,7 +20,7 @@ Data types The base libuv handle type. -.. c:type:: uv_handle_type +.. c:enum:: uv_handle_type The kind of the libuv handle. @@ -104,7 +104,7 @@ Public members API --- -.. c:function:: UV_HANDLE_TYPE_MAP(iter_macro) +.. c:macro:: UV_HANDLE_TYPE_MAP(iter_macro) Macro that expands to a series of invocations of `iter_macro` for each of the handle types. `iter_macro` is invoked with two diff --git a/docs/src/idle.rst b/docs/src/idle.rst index 1f51c4a..b7a0507 100644 --- a/docs/src/idle.rst +++ b/docs/src/idle.rst @@ -41,14 +41,22 @@ API .. c:function:: int uv_idle_init(uv_loop_t* loop, uv_idle_t* idle) - Initialize the handle. + Initialize the handle. This function always succeeds. + + :returns: 0 .. c:function:: int uv_idle_start(uv_idle_t* idle, uv_idle_cb cb) - Start the handle with the given callback. + Start the handle with the given callback. This function always succeeds, + except when `cb` is `NULL`. + + :returns: 0 on success, or `UV_EINVAL` when `cb == NULL`. .. c:function:: int uv_idle_stop(uv_idle_t* idle) Stop the handle, the callback will no longer be called. + This function always succeeds. + + :returns: 0 .. seealso:: The :c:type:`uv_handle_t` API functions also apply. diff --git a/docs/src/index.rst b/docs/src/index.rst index f696dc1..5bdb4be 100644 --- a/docs/src/index.rst +++ b/docs/src/index.rst @@ -7,7 +7,7 @@ Overview libuv is a multi-platform support library with a focus on asynchronous I/O. It was primarily developed for use by `Node.js`_, but it's also used by `Luvit`_, -`Julia`_, `pyuv`_, and `others`_. +`Julia`_, `uvloop`_, and `others`_. .. note:: In case you find errors in this documentation you can help by sending @@ -16,8 +16,8 @@ was primarily developed for use by `Node.js`_, but it's also used by `Luvit`_, .. _Node.js: https://nodejs.org .. _Luvit: https://luvit.io .. _Julia: https://julialang.org -.. _pyuv: https://github.com/saghul/pyuv -.. _others: https://github.com/libuv/libuv/wiki/Projects-that-use-libuv +.. _uvloop: https://github.com/MagicStack/uvloop +.. _others: https://github.com/libuv/libuv/blob/v1.x/LINKS.md Features diff --git a/docs/src/loop.rst b/docs/src/loop.rst index d642ac1..0f5ddfb 100644 --- a/docs/src/loop.rst +++ b/docs/src/loop.rst @@ -16,7 +16,7 @@ Data types Loop data type. -.. c:type:: uv_run_mode +.. c:enum:: uv_run_mode Mode used to run the loop with :c:func:`uv_run`. @@ -68,6 +68,13 @@ API to suppress unnecessary wakeups when using a sampling profiler. Requesting other signals will fail with UV_EINVAL. + - UV_METRICS_IDLE_TIME: Accumulate the amount of idle time the event loop + spends in the event provider. + + This option is necessary to use :c:func:`uv_metrics_idle_time`. + + .. versionchanged:: 1.39.0 added the UV_METRICS_IDLE_TIME option. + .. c:function:: int uv_loop_close(uv_loop_t* loop) Releases all internal loop resources. Call this function only when the loop diff --git a/docs/src/metrics.rst b/docs/src/metrics.rst new file mode 100644 index 0000000..696c620 --- /dev/null +++ b/docs/src/metrics.rst @@ -0,0 +1,27 @@ + +.. _metrics: + +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. + +API +--- + +.. c:function:: uint64_t uv_metrics_idle_time(uv_loop_t* loop) + + Retrieve the amount of time the event loop has been idle in the kernel's + event provider (e.g. ``epoll_wait``). The call is thread safe. + + The return value is the accumulated time spent idle in the kernel's event + provider starting from when the :c:type:`uv_loop_t` was configured to + collect the idle time. + + .. note:: + The event loop will not begin accumulating the event provider's idle + time until calling :c:type:`uv_loop_configure` with + :c:type:`UV_METRICS_IDLE_TIME`. + + .. versionadded:: 1.39.0 diff --git a/docs/src/misc.rst b/docs/src/misc.rst index d9bf3ae..bae4481 100644 --- a/docs/src/misc.rst +++ b/docs/src/misc.rst @@ -131,11 +131,11 @@ Data types char* model; int speed; struct uv_cpu_times_s { - uint64_t user; - uint64_t nice; - uint64_t sys; - uint64_t idle; - uint64_t irq; + uint64_t user; /* milliseconds */ + uint64_t nice; /* milliseconds */ + uint64_t sys; /* milliseconds */ + uint64_t idle; /* milliseconds */ + uint64_t irq; /* milliseconds */ } cpu_times; } uv_cpu_info_t; @@ -233,6 +233,24 @@ API sure the allocator is changed while no memory was allocated with the previous allocator, or that they are compatible. + .. warning:: Allocator must be thread-safe. + +.. c:function:: void uv_library_shutdown(void); + + .. versionadded:: 1.38.0 + + Release any global state that libuv is holding onto. Libuv will normally + do so automatically when it is unloaded but it can be instructed to perform + cleanup manually. + + .. warning:: Only call :c:func:`uv_library_shutdown()` once. + + .. warning:: Don't call :c:func:`uv_library_shutdown()` when there are + still event loops or I/O requests active. + + .. warning:: Don't call libuv functions after calling + :c:func:`uv_library_shutdown()`. + .. c:function:: uv_buf_t uv_buf_init(char* base, unsigned int len) Constructor for :c:type:`uv_buf_t`. @@ -243,9 +261,9 @@ API .. c:function:: char** uv_setup_args(int argc, char** argv) - Store the program arguments. Required for getting / setting the process title. - Libuv may take ownership of the memory that `argv` points to. This function - should be called exactly once, at program start-up. + Store the program arguments. Required for getting / setting the process title + or the executable path. Libuv may take ownership of the memory that `argv` + points to. This function should be called exactly once, at program start-up. Example: @@ -257,29 +275,44 @@ API .. c:function:: int uv_get_process_title(char* buffer, size_t size) Gets the title of the current process. You *must* call `uv_setup_args` - before calling this function. If `buffer` is `NULL` or `size` is zero, - `UV_EINVAL` is returned. If `size` cannot accommodate the process title and - terminating `NULL` character, the function returns `UV_ENOBUFS`. + before calling this function on Unix and AIX systems. If `uv_setup_args` + has not been called on systems that require it, then `UV_ENOBUFS` is + returned. If `buffer` is `NULL` or `size` is zero, `UV_EINVAL` is returned. + If `size` cannot accommodate the process title and terminating `nul` + character, the function returns `UV_ENOBUFS`. + + .. note:: + On BSD systems, `uv_setup_args` is needed for getting the initial process + title. The process title returned will be an empty string until either + `uv_setup_args` or `uv_set_process_title` is called. .. versionchanged:: 1.18.1 now thread-safe on all supported platforms. + .. versionchanged:: 1.39.0 now returns an error if `uv_setup_args` is needed + but hasn't been called. + .. c:function:: int uv_set_process_title(const char* title) Sets the current process title. You *must* call `uv_setup_args` before - calling this function. On platforms with a fixed size buffer for the process - title the contents of `title` will be copied to the buffer and truncated if - larger than the available space. Other platforms will return `UV_ENOMEM` if - they cannot allocate enough space to duplicate the contents of `title`. + calling this function on Unix and AIX systems. If `uv_setup_args` has not + been called on systems that require it, then `UV_ENOBUFS` is returned. On + platforms with a fixed size buffer for the process title the contents of + `title` will be copied to the buffer and truncated if larger than the + available space. Other platforms will return `UV_ENOMEM` if they cannot + allocate enough space to duplicate the contents of `title`. .. versionchanged:: 1.18.1 now thread-safe on all supported platforms. + .. versionchanged:: 1.39.0 now returns an error if `uv_setup_args` is needed + but hasn't been called. + .. c:function:: int uv_resident_set_memory(size_t* rss) Gets the resident set size (RSS) for the current process. .. 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) @@ -301,11 +334,30 @@ 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`. @@ -344,6 +396,10 @@ API Convert a binary structure containing an IPv6 address to a string. +.. c:function:: int uv_ip_name(const struct sockaddr *src, char *dst, size_t size) + + Convert a binary structure containing an IPv4 address or an IPv6 address to a string. + .. c:function:: int uv_inet_ntop(int af, const void* src, char* dst, size_t size) .. c:function:: int uv_inet_pton(int af, const char* src, void* dst) @@ -407,7 +463,8 @@ API .. c:function:: int uv_exepath(char* buffer, size_t* size) - Gets the executable path. + Gets the executable path. You *must* call `uv_setup_args` before calling + this function. .. c:function:: int uv_cwd(char* buffer, size_t* size) @@ -484,11 +541,11 @@ API .. c:function:: uint64_t uv_get_free_memory(void) - Gets memory information (in bytes). + Gets the amount of free memory available in the system, as reported by the kernel (in bytes). .. c:function:: uint64_t uv_get_total_memory(void) - Gets memory information (in bytes). + Gets the total amount of physical memory in the system (in bytes). .. c:function:: uint64_t uv_get_constrained_memory(void) @@ -499,7 +556,7 @@ API .. note:: This function currently only returns a non-zero value on Linux, based - on cgroups if it is present. + on cgroups if it is present, and on z/OS based on RLIMIT_MEMLIMIT. .. versionadded:: 1.29.0 @@ -649,6 +706,16 @@ API On Windows, setting `PRIORITY_HIGHEST` will only work for elevated user, for others it will be silently reduced to `PRIORITY_HIGH`. + .. note:: + On IBM i PASE, the highest process priority is -10. The constant + `UV_PRIORITY_HIGHEST` is -10, `UV_PRIORITY_HIGH` is -7, + `UV_PRIORITY_ABOVE_NORMAL` is -4, `UV_PRIORITY_NORMAL` is 0, + `UV_PRIORITY_BELOW_NORMAL` is 15 and `UV_PRIORITY_LOW` is 39. + + .. note:: + On IBM i PASE, you are not allowed to change your priority unless you + have the \*JOBCTL special authority (even to lower it). + .. versionadded:: 1.23.0 .. c:function:: int uv_os_uname(uv_utsname_t* buffer) @@ -688,7 +755,7 @@ API :man:`sysctl(2)`. - FreeBSD: `getrandom(2) _`, or `/dev/urandom` after reading from `/dev/random` once. - - NetBSD: `KERN_ARND` `sysctl(3) _` + - NetBSD: `KERN_ARND` `sysctl(7) _` - macOS, OpenBSD: `getentropy(2) _` if available, or `/dev/urandom` after reading from `/dev/random` once. - AIX: `/dev/random`. diff --git a/docs/src/pipe.rst b/docs/src/pipe.rst index 6437a9d..5fa83b8 100644 --- a/docs/src/pipe.rst +++ b/docs/src/pipe.rst @@ -118,3 +118,21 @@ API function is blocking. .. versionadded:: 1.16.0 + +.. c:function:: int uv_pipe(uv_file fds[2], int read_flags, int write_flags) + + Create a pair of connected pipe handles. + Data may be written to `fds[1]` and read from `fds[0]`. + The resulting handles can be passed to `uv_pipe_open`, used with `uv_spawn`, + or for any other purpose. + + Valid values for `flags` are: + + - UV_NONBLOCK_PIPE: Opens the specified socket handle for `OVERLAPPED` + or `FIONBIO`/`O_NONBLOCK` I/O usage. + This is recommended for handles that will be used by libuv, + and not usually recommended otherwise. + + Equivalent to :man:`pipe(2)` with the `O_CLOEXEC` flag set. + + .. versionadded:: 1.41.0 diff --git a/docs/src/poll.rst b/docs/src/poll.rst index aba8915..93a101e 100644 --- a/docs/src/poll.rst +++ b/docs/src/poll.rst @@ -86,36 +86,63 @@ API .. c:function:: int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb) Starts polling the file descriptor. `events` is a bitmask made up of - UV_READABLE, UV_WRITABLE, UV_PRIORITIZED and UV_DISCONNECT. As soon as an - event is detected the callback will be called with `status` set to 0, and the - detected events set on the `events` field. + `UV_READABLE`, `UV_WRITABLE`, `UV_PRIORITIZED` and `UV_DISCONNECT`. As soon + as an event is detected the callback will be called with `status` set to 0, + and the detected events set on the `events` field. - The UV_PRIORITIZED event is used to watch for sysfs interrupts or TCP out-of-band - messages. + The `UV_PRIORITIZED` event is used to watch for sysfs interrupts or TCP + out-of-band messages. - The UV_DISCONNECT event is optional in the sense that it may not be - reported and the user is free to ignore it, but it can help optimize the shutdown - path because an extra read or write call might be avoided. + The `UV_DISCONNECT` event is optional in the sense that it may not be + reported and the user is free to ignore it, but it can help optimize the + shutdown path because an extra read or write call might be avoided. If an error happens while polling, `status` will be < 0 and corresponds - with one of the UV_E* error codes (see :ref:`errors`). The user should + 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. + anyway, the callback *may* be called reporting an error status, but this is + **not** guaranteed. .. note:: - Calling :c:func:`uv_poll_start` on a handle that is already active is fine. Doing so - will update the events mask that is being watched for. + Calling :c:func:`uv_poll_start` on a handle that is already active is + fine. Doing so will update the events mask that is being watched for. .. note:: - Though UV_DISCONNECT can be set, it is unsupported on AIX and as such will not be set - on the `events` field in the callback. + Though `UV_DISCONNECT` can be set, it is unsupported on AIX and as such + will not be set on the `events` field in the callback. - .. versionchanged:: 1.9.0 Added the UV_DISCONNECT event. - .. versionchanged:: 1.14.0 Added the UV_PRIORITIZED event. + .. note:: + If one of the events `UV_READABLE` or `UV_WRITABLE` are set, the + callback will be called again, as long as the given fd/socket remains + readable or writable accordingly. Particularly in each of the following + scenarios: + + * The callback has been called because the socket became + readable/writable and the callback did not conduct a read/write on + this socket at all. + * The callback committed a read on the socket, and has not read all the + available data (when `UV_READABLE` is set). + * The callback committed a write on the socket, but it remained + writable afterwards (when `UV_WRITABLE` is set). + * The socket has already became readable/writable before calling + :c:func:`uv_poll_start` on a poll handle associated with this socket, + and since then the state of the socket did not changed. + + In all of the above listed scenarios, the socket remains readable or + writable and hence the callback will be called again (depending on the + events set in the bitmask). This behaviour is known as level + triggering. + + .. versionchanged:: 1.9.0 Added the `UV_DISCONNECT` event. + .. versionchanged:: 1.14.0 Added the `UV_PRIORITIZED` event. .. c:function:: int uv_poll_stop(uv_poll_t* poll) Stop polling the file descriptor, the callback will no longer be called. + .. note:: + Calling :c:func:`uv_poll_stop` is effective immediately: any pending + callback is also canceled, even if the socket state change notification + was already pending. + .. seealso:: The :c:type:`uv_handle_t` API functions also apply. diff --git a/docs/src/prepare.rst b/docs/src/prepare.rst index aca5815..5e0d247 100644 --- a/docs/src/prepare.rst +++ b/docs/src/prepare.rst @@ -33,14 +33,22 @@ API .. c:function:: int uv_prepare_init(uv_loop_t* loop, uv_prepare_t* prepare) - Initialize the handle. + Initialize the handle. This function always succeeds. + + :returns: 0 .. c:function:: int uv_prepare_start(uv_prepare_t* prepare, uv_prepare_cb cb) - Start the handle with the given callback. + Start the handle with the given callback. This function always succeeds, + except when `cb` is `NULL`. + + :returns: 0 on success, or `UV_EINVAL` when `cb == NULL`. .. c:function:: int uv_prepare_stop(uv_prepare_t* prepare) Stop the handle, the callback will no longer be called. + This function always succeeds. + + :returns: 0 .. seealso:: The :c:type:`uv_handle_t` API functions also apply. diff --git a/docs/src/process.rst b/docs/src/process.rst index f2b3be2..8acf7db 100644 --- a/docs/src/process.rst +++ b/docs/src/process.rst @@ -102,72 +102,104 @@ Data types } data; } uv_stdio_container_t; -.. c:type:: uv_stdio_flags +.. c:enum:: uv_stdio_flags Flags specifying how a stdio should be transmitted to the child process. :: 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 * flags may be specified to create a duplex data stream. */ UV_READABLE_PIPE = 0x10, - UV_WRITABLE_PIPE = 0x20 + UV_WRITABLE_PIPE = 0x20, + /* - * Open the child pipe handle in overlapped mode on Windows. - * On Unix it is silently ignored. - */ - UV_OVERLAPPED_PIPE = 0x40 + * 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, + * if the child is not designed to handle to encounter this mode, + * but can also be significantly more efficient. + */ + UV_NONBLOCK_PIPE = 0x40 } uv_stdio_flags; Public members ^^^^^^^^^^^^^^ -.. c:member:: uv_process_t.pid +.. c:member:: int uv_process_t.pid The PID of the spawned process. It's set after calling :c:func:`uv_spawn`. .. note:: The :c:type:`uv_handle_t` members also apply. -.. c:member:: uv_process_options_t.exit_cb +.. c:member:: uv_exit_cb uv_process_options_t.exit_cb Callback called after the process exits. -.. c:member:: uv_process_options_t.file +.. c:member:: const char* uv_process_options_t.file Path pointing to the program to be executed. -.. c:member:: uv_process_options_t.args +.. c:member:: char** uv_process_options_t.args 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`. -.. c:member:: uv_process_options_t.env +.. c:member:: char** uv_process_options_t.env Environment for the new process. If NULL the parents environment is used. -.. c:member:: uv_process_options_t.cwd +.. c:member:: const char* uv_process_options_t.cwd Current working directory for the subprocess. -.. c:member:: uv_process_options_t.flags +.. c:member:: unsigned int uv_process_options_t.flags Various flags that control how :c:func:`uv_spawn` behaves. See :c:type:`uv_process_flags`. -.. c:member:: uv_process_options_t.stdio_count -.. c:member:: uv_process_options_t.stdio +.. c:member:: int uv_process_options_t.stdio_count +.. c:member:: uv_stdio_container_t* uv_process_options_t.stdio The `stdio` field points to an array of :c:type:`uv_stdio_container_t` structs that describe the file descriptors that will be made available to @@ -178,8 +210,8 @@ Public members On Windows file descriptors greater than 2 are available to the child process only if the child processes uses the MSVCRT runtime. -.. c:member:: uv_process_options_t.uid -.. c:member:: uv_process_options_t.gid +.. c:member:: uv_uid_t uv_process_options_t.uid +.. c:member:: uv_gid_t uv_process_options_t.gid Libuv can change the child process' user/group id. This happens only when the appropriate bits are set in the flags fields. @@ -188,14 +220,13 @@ Public members This is not supported on Windows, :c:func:`uv_spawn` will fail and set the error to ``UV_ENOTSUP``. -.. c:member:: uv_stdio_container_t.flags +.. c:member:: uv_stdio_flags uv_stdio_container_t.flags - Flags specifying how the stdio container should be passed to the child. See - :c:type:`uv_stdio_flags`. + Flags specifying how the stdio container should be passed to the child. -.. c:member:: uv_stdio_container_t.data +.. c:member:: union @0 uv_stdio_container_t.data - Union containing either the stream or fd to be passed on to the child + Union containing either the `stream` or `fd` to be passed on to the child process. diff --git a/docs/src/request.rst b/docs/src/request.rst index 5603828..a041443 100644 --- a/docs/src/request.rst +++ b/docs/src/request.rst @@ -53,7 +53,7 @@ Public members API --- -.. c:function:: UV_REQ_TYPE_MAP(iter_macro) +.. c:macro:: UV_REQ_TYPE_MAP(iter_macro) Macro that expands to a series of invocations of `iter_macro` for each of the request types. `iter_macro` is invoked with two @@ -69,8 +69,8 @@ API Returns 0 on success, or an error code < 0 on failure. Only cancellation of :c:type:`uv_fs_t`, :c:type:`uv_getaddrinfo_t`, - :c:type:`uv_getnameinfo_t` and :c:type:`uv_work_t` requests is - currently supported. + :c:type:`uv_getnameinfo_t`, :c:type:`uv_random_t` and :c:type:`uv_work_t` + requests is currently supported. Cancelled requests have their callbacks invoked some time in the future. It's **not** safe to free the memory associated with the request until the @@ -80,8 +80,9 @@ API * A :c:type:`uv_fs_t` request has its req->result field set to `UV_ECANCELED`. - * A :c:type:`uv_work_t`, :c:type:`uv_getaddrinfo_t` or c:type:`uv_getnameinfo_t` - request has its callback invoked with status == `UV_ECANCELED`. + * A :c:type:`uv_work_t`, :c:type:`uv_getaddrinfo_t`, + :c:type:`uv_getnameinfo_t` or :c:type:`uv_random_t` request has its + callback invoked with status == `UV_ECANCELED`. .. c:function:: size_t uv_req_size(uv_req_type type) diff --git a/docs/src/sphinx-plugins/manpage.py b/docs/src/sphinx-plugins/manpage.py index 672b002..6570aea 100644 --- a/docs/src/sphinx-plugins/manpage.py +++ b/docs/src/sphinx-plugins/manpage.py @@ -18,7 +18,7 @@ from string import Template def make_link_node(rawtext, app, name, manpage_num, options): ref = app.config.man_url_regex if not ref: - ref = "http://man7.org/linux/man-pages/man%s/%s.%s.html" %(manpage_num, name, manpage_num) + ref = "https://man7.org/linux/man-pages/man%s/%s.%s.html" %(manpage_num, name, manpage_num) else: s = Template(ref) ref = s.substitute(num=manpage_num, topic=name) diff --git a/docs/src/static/diagrams.key/Index.zip b/docs/src/static/diagrams.key/Index.zip index 5e786ba7cc79e2ca024bb3136e088258e53cd3a1..17aedace14faf8f2990df3232565328901209d5a 100644 GIT binary patch literal 71160 zcmcG%30xD``agV57Mz3tAu|&Lf=!TE%rzK7h(KjY2>j8j)+$}3#WpBbS_CW5+TPj= z?)z4EMMcG3>%La2R;$*nYPE|?)w-|Ly3`Hd@0ozr{`CHO|L^4`o>y2Bo&S-SGzlh4?A7@V zII61;l2f8Icr2QGx*FXB-FN!?x`#TQoU74=CaIN)a_*ikRF+p5A?NPvEOPEUygv&>AiuM>B)_Pz+3Vf%OY)j^DDf6{@%HSZM-#eWPKFzJ!`v3XGQOOD zMBn_hj1N_+<=i7(ggStbGLJ||ss}BX^Qk10yh4KIR{AT`ZemcL4 z|B4^akL4HfOZlOEKYl9Tm!HM2<`?pV`GNcregr>*pUIEnC-D>bZ~2}4V!l5=hF{Ci z=2!7s`7Qi%ehNR4AI?|tbNLPYXnsAvgI~w5;K%Xv_zHdjznkCAPv$H6HT)2M7;l$p zgVig7@q&Hvp0At2TDRA8G`zvRf2Wx7wkcNn$G=nJs9I^TrRC(Ln7`iFDaCsC>O0{E z+b;hXQ_Mq-SW>K~EKR}ctdm^VdHKf5)aa#J4bq%=tFi`mg(`XtGhyl}YA)nbVT3hZp%_ze(6yyLsDP zZL;+Z&Ft_|w%0qIOg7KFo04Mv;6XWlzx~#sSo5I*F2$;8))bGheqL;n*<7(c+4}OH zuaiPH+uTzYw=l<~_er*{nLNCrI@p>UP`w95IP*cSH4q{V%GN3rdU_C-H!c>sc)y4KRa|d z*}8sa8TKnI$tmV9>FQ)_uMRFe(EMS#6mxm-uw<)p#;+|xezbYE7o?b{x5E>j>$)U- zp6!FUmbhSuKH0hs)mho?uzyP040QkIZCi=7c}cWuImiCYx8? z`Xk9YWziIs!8X%xidvnmdxIkw(5_>xTFZ#&8sHj7QUW4dy@4ICQW;6YsoH2H7NvM* z_xM!%TZFuxktuV(MOtOa#=J$E{=@reius*Y-W2P#pV|z=aogbecyagY&=l+IjSn^X z5hvL`s^25U{9BBeVx8Y%uNY5=U7)xf`|14Jf2Ek;xI6~mxx?6OG@B_M{u`Bd0W5ei|h!z~0Y^L>%kaqoeEc%1r27m^F22rIjn1`Smf}NqDVW8ol5mXs9 zl80Cv#X~HP#*s0g&p~5B<8aw{-Yw@QU}qv|5@<4L3ROC$@;Cg7AuB3)ET-Y$bkGb? zCCwo-v7QB*4XOgo0nNqnd05W}EdVV9EdnhDEdhN2S_)bQT27V575vtE4bk>``Yzdk zV_$+cf;K^{e#LLqP!m~61IQ{IS`AtQT8jr>$3p{b#?BVdR?yd=ZJ_O-Z$LXhJ3+fZ z-%@3Di`{%^q|@ni+imuAYes_GZFQu%99DO{+ip#>CAe*IE{`WZJq`W0o%PjrKA!O0 zN?sSp{SmF-r)pQvBHPDvBvj7r;~Rl=4Y~6+RfA6xEV@489C4MnT3jv;5U%hw{Eq_O zXpV?STqTC$0U~IS2JP0ET9ynD@CjCn=>IkY%k}Qi`jDSN8ueJ(jAfm0 z>kW(Siq1|upouFwBZ(q55>HY|`@os%YES<(49Cp0?8q2TjLYtHxvd_X$6<9in>(y7 zPxEwZye%!wX1Ckp-Eq!tqzCCm`cN6yVGt$IQWGgYAQ4R|(S^i#2{(BKH@TEsNNg^5 zbGI|yf&R#7o?&X4nP7FrII)Opo|a&~ii`Q#po+nj8du2ilDv zPtqXiq-!)ts1R`y8>8z$m#IJw(}oxi(#dZe=M%N^12bFqyYBvM%{SPmguQ zdRz`yg3IwIXS8z`62&gwj#hq0E3cuI-_gn_Te>~XmJa^1+2a$e4wu_$b*FngR=dLy zYm4*5*qv$3yNQM5HQZnDa9{DdlviV`lSfjEeW6$;-sSrW*Z9%mkNi;KrtnbcC(IL% z34_GR;w15N;f8QjSStL&eV3yWQ=x2k?+01q`dMqveea(JVej^=#^oR zsL5B@p5(HUi>52&sOdB*Gu_A6QbY8sC8^8A_UGvBYcxt4eC(^-#C|`lZ&2kM$1sR4 zln+o=edhKXe06%OyxTK^cy4v#3x2i@A}7Nb-XJytIbZNg-6$*&Z}M{>)Xww6gt@|P z*Zu7;K&I~l?E)=@oGv0aPsKDHCP19c6(G*e^9Nqg-saztiCYdvN6Cyg(VMU1oo?o? zr-RbsVjYeQN4nMRaYFjKG7_xu&D}O@OnOFh9M5PT6Ppu7UXNZ+Ig#7x#O2p<`K!46 zIzC3KJuM+7AB}0HvLlM z6nLdC@k(D}KlKoQT9_lw5mw=yPVvixTa~fqth>czl_`syG94tRPZg6n*gkZsSaC~; z$ucYx-cj(}s>EKT=~p60(biXJ>nrT1juu9sf98l&(bhz9s<75IS=lRL4|HHJvewl} zm?$R7xe?eIZQ7%lDhBt`P8HsvV&FGi^d4$Fj~9CxFLpk+6U|C@rNyV)oN?BSG)D}y zQUZD?J;v18=uC)@OOJQOx{^APm!msDXa^>!*ib)<%iH4ev*?$JvF=!VjML+^CM0-l zR);-3-Rg?9+pVqyo5K~Co*wIV#bc-s&n0bH-(+$dl2!G-n;{GnX9?qkL&65(I=@Po zB+M832#dun!V2*KKV1Amye;$-&k0`(Wx{-Mt#CvbARaayAzuWxRFNZF%w&MK87aeh z800kr8Hc>rAZ(T7dbtb;LQPhAGa0lX2TV24H)^uNG>V+VngN5w5DaUv-3QzKuzefb zOo7kG_AG2eslTxSL$$@R+_<3oP3P%hl!=Zcs4KZGh_ zys%IBov#$G@)g2*;Rj)pFjyQRE)}N0GM908#%xvC-$N&n)AScofu{i!0c2T2e!%v6Y%@{FpwKNa8I$ybX(L#H0>r2i zlA2*Lf$XE|c5}r3#=0`7dDB3FqX8A`a$c)$U1+(xh*K!kNqhcKK zF?N?d#_Enuhlop0fX=li*sSrf%^mTvE>F5WA-3DA2_y?w57V8 zPdD)#X&rrzU{EgCL}yXsWwg*9EnH^I8t<|hI=$otu2Z@{AnWu>Yh`CJ8d*Ze5bKxM><`u z*tF*94y!XGa4*-|(LCN7?{c`UPUx8!S9)^@^)~kCpNOF6_VBvkV6UyCMRrGLm#u+| zfV-2MNmFgMgoJcR8>=HW1AxmFW6F$Ai?P~m&7JP%adxPvj02S#Ik!gU>_HT#jrJZS z@`%wcJ1R#DN5c0xrv;2kv&W`665`X)7p^!ol!h%EG+mtAndV5#NORfU>D}5iMk`0< z(LG355WE>WHTP3!V4x?M3*zQc?hP)BYskI8B^yb*r<^P8_sx9$k&F;Vz=*ZZl{Vhu zlDwWB3i7+;N&c1lGV-~mx^*M&whkax>2t6wp%&d$aUs7#xFlW?uZx$(YvOvQlvW54 z7G0E$xFgl-te)P&;(X>y(J2ax3iCpkSBH@5lqO1UEjBDbk_>@e7TYV`1>VBOI9aFV>gkjR%f6%*#c^S& zUcH8@zo6=5^$TjaWHvtn{xv#1I&}b)kVu&)#!TxKpo?$oLUo)%D>G2|GE$)^2}!X5 zJ;&4V$m2q2Hx3q*B{w>W2%%Z(K#oT1hz4t!QmfeMR=(}j60Aj7S@hq+PPbadDU&^< z1sYze*HRHnJlTOD)uGfD7R4B`f*B!wd5cP2kE2n-7}3tD{pch-PGYdAt>D|yo??B) zRx0{Kp)HRZ-A_$3#RB08K zG$+ot0P9o3a=lh2a?7J5sgP+=RPghfL^cgoXXTd^c46!ZAiHcGA}+Nfrk zeyrfkk~O(VcA9Mp($-{C1bPnISo=1#pI}4lfgY!gVwr8DhGlxKLgbc3C9~dN)Ozv{$<+%I;k1Cc|=*v~+fpQSEEP zE620=Z#I#@2g9^s8j;DFnM2$G*Kk#f=2)b1#a2u^J zM-^KXqxmg*yG*5yiBJF~tWjjT>DQ5EsUERsSK=|q`$SM91Bjz}c#XxlFi65J`gq#P z^j=9dWASQFwv1Ed$~g6VSi=n0;&+`tw<%yaSr9j;Ndt_(;C>oHE~bU_Bj>u#p-GkW zOB&y`(kBcm!LAq6oRt*Iwv|*Pay6o}K&IKM|AEIkohnilEei~7*}YD>L%!)*AXEIn z+Y4j?NnLjnRYb^cm6n`L#L<)Pwz~=Io~@ER*&1CStCc4$3!aiEul9mGS<*`e;q{0~ zl&|y`g|-k6rX-tUxqC9UE;^^m%{H=pE4G*86dZ;qiTp}p`mY$%Pc1Kk?`VNFrp4c7 zdBMl>-LJM@@GOph_;g!e&YCZN}*b*10qlJOrOh$pqgnS?fTs6ccEcIPb=I-vR(sR z_;ie(Ek5j$_()Qa3xQ1KUVcL5{wBBRwu@V!bhnC|#ckpr{MSB}`v?E`RIbA(JT^1o zu|-S-J_RjpALP>sjg9Wof`Yt~;#!&UwWNf%OGKsJNu3x2dtj+ZhNU+Ss9}b3O!0cW-G= ztnv%-SCaQ`UeBgRudxdgJjTMjl2S;VqC#Vr(tM-x4Loa8W3l(${E`w}(KW9C%i^Lg z-h#a1rbbm9DOSEt)U=bfsQ80Yqbhw~abEWxc|DEZyuv(hp|QBMxJO=Lm;B=5Jau}3 zF|X8Eke{2^v&h)Fr?*%+=$)QjAo084E@qEgUxq`eR>gmxrzej!v zo~pCa+a2fR738syRB3lf9-0#vYIhhh~F~N*mR3 zf8v%g5-W}YD+0HK9O}XpFz$-b&)?-oW337X#Q8YuSxOj?=U$kB_!Kd8?-P3106h#a zqzXtE){8(xTyXO`IJ7mf3f{IFt({wHYvrjLwLp>AL{5Opz_$ zBbFnN!BqmF6|pcR=0jGaB}q6M$cki|A|dt6k&00D{0}vcVbu~FH3o6pd_W@qMWS={ zLrkFTbec*9-PTYDhDo#g`( z+T-axy zCs!n(f946Pa|=L!0slxiEdC%K5f6%o#P5M*0a+gXO~_I!bq+GAbEr=0INEFLq)vy@ z+!E^FtybzBmZZ)PlGOQLk~#|*^nV(e9C?n+c{Vcn0-ShA6uHAbL%|#(VUB6~<9}hi zy6?aYg*#7Ujx+y(?fzdG3e%)-IPL$|Pyp8cL6R@3SK@u*C5SuX8+9r%QdbMT%F_Z= z+G7A+2NScYrh-9$Y^u7hplQncMhOD+VHQO;We^~n1|+?gO_eYy60%|F^hv|XSY~Ha zLxnW(E26U>n-tj?_$}}F27Xtj|GP48+##m_FA7uiO#grLg#I5QgJsbjdViAy0fxwY z5WqG?k~>p;a;IgaH|PHj1-L#Yy7JdNK>L3|{;ypA4>-VIjz|6;0!A|x!nkb}1%Gus z3_AxXfQ)U0{XowqX$efg%pXXhsgh)w8V$tI!gZLLKg|?A6!0|biCbdiJs@xyApzhS z_CidI#Crt_GB1c}{A*9}-q#9??mKaxI9xaYt-n{?1AV`r8QH`CJKo#NcyAAQPdTc6 zy{EPOeu>`>Nc^_%nOeSp*#h5*%VquK|5Kv}2UlnQMWK5;BR8WM8uq`?>^_~Ydb9c+ zDAKNZrSIlL^Lk6)EzL9LGo{@nAIA&4utoAp)E%Cq@_i_w_(>e`KQ+6N{=53E=jisw z8r+yy3~13KuS=e>a{<)8`t|(ce5ig2Xh8Wh@bI+uZ)z;eFD}5N_w256N0tC-0I+yF z8%?6>TT)1KwfS8EHcVXPhd>{NNG~aF8aye=_#tkTpIcf`+CAWy2biZDkVK(3uc+84 zfe4p8DhOR)YmU0Q8R72O=R*%cy%>5JD@^)};Y1|;xYMnJZiIf+2Ey1dySxlLT>B3~ z>{-}BspA-Q$gFjFDBmU|5gv~~j|kWx1Tr?O^LI>=*kRI>VvOlp2|CpIJD_o&=kI_R zJ5Vde9=i|b?b9`8x`{Q6d)A{jC=f^`S!s~%^p_}8WVS*&0CPc&E zkZFo%x;g$SD<#guq?CtAsXcB8p^5Env}HF^X14v@O%|L4xiqtzEOMeGk0wGMz1q$D zlH`YI^7n0$IyXeib0!YJn=b3tB<{E{8LP(-A#X|E2mm?*8500_ z;0#c}T!UmHJH4sQN@4H-A`0-}u*@)7QZ17qqH5tm%frO-!5<7D?2MR-a~*nlah=|8JPAPnyJJebSy_vT@ST9#_X?4xd-g=Tr<;H<>Sa^?cms z5a#g`w^cpcySNJqn)@8^)Ar}sQXWt)^NlT7=EnuQjR650HBU*hN&Zge`@_hB@9%SI zWPKiOdkj2nK++gjkqq^I>yuYa`e-1nb|g9&5*V*<+eM~0k!E(JE15H(iww>H2fKj- zNF&Li)WRX)kQ@RI8aZC#zVVFvx_Fy2D9|MC?>hl&Jpyg!Khq=d%DU$>-}s+oWGC2- zjLlXl@U96}J`m6rdygT&PjwJrtR(ZsCjKOo+yMPm;I0b{1N`(C?wa9nhlWD;Gh!Za zpU*JMn+rp=na5k=ZJl52%Vg9OBD4-dCL$#Mt!SScLBaWH|AOba`K^ZdLpC#l^SQP` z$zksU6Y#iAX`mpX~a2!L4hc2S~sv ztB(Wa*tl=N29A&eprfE)L6`Bv=1}~A{0w1A7&chgk0tORfHrS;_z-v~Z|iEMz%c#K z*d3u~uw=M?Gjl%TUV65?rt~8Zlzxh``qV(mv2ou3HHahR0O%;_SI}krusM`QlHJ&t z5{5J89~Jp2 z9B3Jrn0=1#K*0ZZp2z>}$AJw)Ls(290*OK5#E)CLLe(LjF#NC~OyoAu-sC_)Zx$<9 zhD1ypEw7Cgbha&%xS3u0KZ6DE?cFyx_Y=)S!w5cPzaetDODyZUfbKV@R!8CSY|gaF z`w2IK4-MH5^Rma|n?5v%+(X*CWNwr%;`!-a6&^65X7NE%H4L`dkXo&p>_mZ8I^liJ721^gOHB{ub zqP276NBm{=cDi&XwWy7>iVy8p z-c(`9_42_<|2b-;d?IGV#y|}|M@{riImt5RDE=_?|2i_%zinR0M@2o~H%5D8IMnAm zMJJDp%y%vBZBfh%t2oNFo7|?xBG0wRwe!$>^Jwo^JWh)|($lsBdhX>O#nG?+^Rz_{ zp*-B@kl!_m3FV`vcfxNgjv0C5cGT$ja!$lq;LnwdOF*JxsMLanqH!ay2pwD-3M{4gRFeHLht3`Xn)Or60cic7D;@ zB{kcqX&W*L-i_Wy74?TSe&8QAV;KjIxKfXv(O*<6+R$ka{K=435Bz-$))x)+MMGzt z+bGxa<+Pfne%qUp-GMt*;U_i2_&uSo&gM>*JH(UPvQLJki3?5=GOX1};=6bc_-PM& z3f%?nH~*&oS%|!OyBOOQ6&2L%pr##Ui1*gU&6-2{-FoL{B|?^gYgBNh1%9nE)S~uj zyQ$)kK6gkmymh-mH~0i$h-lr0DE%| z2_fxs{PBq{t{bl*Ve?!yhQLmSKnb!kPrIe+!yw)yuCA4~b zEZXQ^)R@P!(4$huLXXnYsbrzY7d3{R03FC9!mXVQFCWw|OvE({+hTFB(~5c;J-0hN zv1Nc}XZR@Ia-K6}Pk;b8S7we_f1ayp8L+-(fUq;XKDRU6a+VA0Xz`oP-+ZB>NrWL( z3BPjbho#Yqn=iD2Ua#QY4bO9Aw%w@f)GmM=IQ3y~5BWYiTcaU6!`u7GF6KW1}xd%Dz8Jv5D_Rju= zD6%@n?K*vcdm88etBdB=1$ZJDJ;xhilYssi1-T2LO5lVu4DO$0A z)GGY*;%OSFt|tXAt#(Mt8@kgW!HO6iP1Z_bP57#Gw7rMLG%rfV!MR#T##1>1Q4BC# zU^xSbR_0Sb@*A}M4P6NC&%$=~b!K~YJ1KIv5Rp5Lf1SQ3FMSQPKg`Wk;pX=};zCO} zv#D$K{q^VR``YMUxD?x4$YOgmtK<=jw=H0?J^x%8r$KDbe-w-DEv$|0DHc$%AJ;cc zH1(tXBnNkNKccACW%naSqN~eCh_A~>Q0nj@m9Nzq=-EC4J=|xYhu0bCTnKl=ok@ko zc&)$yWTz63Xsa4(&Qy@|bem#w+VV^q9LF+_7h5w8sikU!L{fa1`l##E=Wd6_ZKnSB%OKURftAMkvG#=Dh75(P?hTp*-TXq>}`(s1Y#hu zNSU)O<3jR5F|FsSq%& zQe`Z7_=RLReZg8{S_ch0(HWX`(}%J_56*z+e|^L}}d4-@95ts=WmDcgn1DaQRj>k8jWc*vGFnEMdQ3in<6e42bm_D<~Axb zRW+V%n%QVmYsVP|e5sv{GyGY>QdG!q9WHb!206?PVb za8}cbC_OjMgcu_;u|BANgCXk?{8-bUNn5Fw?Qno>T_FWQ1BL(?&1mQTQ|KNsJ|}OB z8CfuNY6Yb(ybglXmuq4ApXpnze92qPmH9Yz%ZAIxnnVyw+zQw?sN)m1Sg5vrDWcL{ zkUrH7GXi0}`+P0&&$Z6*Mf|nQ{MoAVr&b_{c@gd;s+b#*m_#{k^xc{yioYRARG1qf za&se+l8}#_L~WXOMFsFB!-EXtrzU}#5)>6tNePMtGS`RN=~3#LvNj(YH#^`XDu1-W z-LXDg6>|_3{F$2eDJ+Ltc1?dxyVD)Ijkbw<>-wq%ZgEh%_gw16oB|r`&94u*#d}LQ zTBy`;edqP18f6q!21L>N@bsEEoI#rfYv@0tqlji{+|KDIH6VJX*`;)<&eD19zOc1dr zNidYQJf9Gu(#}j$3X>a&+~h_%S>&H4qvD2PeiA2ZTLib(4a4bXV4vt_G_nt~02>YB z)&J^#E9~TlBCvLKuqoPjb(n7hicn+s8*!;^Y?>4A z2c*3crX7WwjEcTTNQ>TW#oI&7e)doq_#-EjUdDx!8jF{WJ6?u*);70$`kxdGZt;cB z2dge~1JiV$yjIVC8K5ILY0PCJ;8bo>}*+ zGQZ{%x%(42*_1q1#chZfv2m>KfSMz1Uda1b%>gz3K0Kh-ZUCQbh#spVITe%Jg$ik2 z@JZ)_BIIRs#%8X!^ZVBht3wZ~p-?Y)We@LaE*p;v&b#&M%tv$DfytA9@Iz$%ZoS~7 znuGG(gYwABMrJ@>HtKY@?iye~UN?>r8yYkkW#o?vBeuu}UF zL*`-SsfJttCR_9eDD!UTx|HVWe&I;ZYi<4Zw#5TfC9C`ua~fN2`WxPPL+KGuyuJOO z#ubY?q=~ZeU!|B6*MG{!$EPyp=a_?`JEunF?^gMf*~yJ%q{_Zf5oT;~87I%5rLzTT z7XRT!#)|R^u+><_5#!8%!k5BM%xwG*o@9D=SZV@v%3x$OW9$XaQ#p^RA~W4+xe6Us z6}?b_2{t%8AW-ISgf{}&M<3>;+)5hKDBv|ee?08W=$Q&F(>ay)%pi?onZZ+&8HB&d znL*l0bWvqA26gM`%pirlqotQ{sbvt^6#c3^u(1Yxysq)OgJFlqMta28nuNOE3p-hV zF$C;_u!FD3@rJL-YZ_~E8*8-d@RI9l7ReJAvEcIgN`>XDS~FI-VsxHW>rd!No2u`1 zMLLGTPO#-BY3V$nquRz=A>t)}Cg!dfnMHbq9$a>WC9g=iIGch?D`Ax4bU>v;YP`+j z=&mvYlT3s7C5^Xf!~QjPXPOwfW0T2bG#zg05gQgxP3^y*tSnFKxnE)5ro~g{N#9`! z9&t%ckSsAshGteK#|1)lE=%53pZo5Y0No8uO2=R zZRc)zo!_si7t*+12%J2ZjBxUV-q&d0<+&DE-avZ|L*`oa=dwS&JkD`4GNo;C0GZWk z++W<|fAl)I{}H|sPVPwQ1)$JKkb(XC6zed#&;OuQtlL?it(`3^woM_QD-y%C0dwQ6ZwpAp{d6+tY^rtpJ6*{ro)P7F~C#>^5Z@+ zPbFnzK9SMkID}`gu`nLugr>`{GqrY{iqqMUpy1?(?95wJgmfc*gdLVlqBI`S<0>j&xk=?63C z{Pn--!K=;a>&GH}{q+dg&uH&o7qHJNs&m^7)H8?NU}=)U0BMrJ!at>fMfGcpG~ zbC!Y6OZPeS$N)BiF}>%G6pa6-&Xx3ddJmC2N^kucj6cPqar6`m#$S4_00@u#FEUVolXbblj_?r&-*3;YL<)bl3z45aOoBDw8P^MG=@vdU+qVAvnWTpsw6BB^;~ zQlu{gj}%md;NkVzE{*@~^k7n?&y|E<#)yoy`m=V)Hk9LlCn*!o9#f>xl~i`;NT$z+ z1XgA3mECb>GUs=uEOMJP>bGIk$3=P@NKPNK`M)0aZ6-a_=kv<^XODReOZV-#iy@g^ zGMF8Md@-v02Jq&V`WTn7>S?fQzr-Ds?8x3h5mJcTp&*t^*d&%sJ17Uk(Ro=$ES-WB zh8>d3+d&V$a+ze72a$O#FSEqlky@6RYZ(n^1;6AD%hMnHzvSt+s(5qkV#p#!JjfTa ze=R@nBUA$_@lA`HPf&rP3y%sysQGZPw(bxR(3U8$(v~P4Rd4~ETxHOdSY%tNA#x8d z`HU?vvFowyQV+Oi51);R6;bby?gX`TN>M?hj8duY0-XVG9Sv`0TSW;8NZW|z1NT-M z5?@xye8}xQdvPQ)?I7hdNg-2$o83QB5{Sm&x2&%kXwz`Wf;O;H5H$WTlbE`8`6#X) z6cM9mOOJR7amf|rtTc(~!V`#VFeWiw)ZG#n@YUjP;tlb4@q%#Eml1s7Z_1Ba#8u4@ z*KZ7QU3`qVTC~^JMF3vQ>sf+IOm$P3et#NkU6ioaEeUJgsLcg>Y6{alCR}a;YWcVR za|+Y*CNFKo*0TEBc11>yQzVC|pr0N{sCQB=Du4gNwSqplG$9W5Y7L^WFl+nGgF6R`afHavFU^3ku`J!sY9C8 z3?(54FID0FWpHfE!_i;Jb7K`ICmD;$3kniZvr?YDQkqG$TGAk^|4boHM}FQbD;YxC zB_qExXtU;N?U25!e@96>^m!c-fja$gSB}{MT`GR`X^6XWrXhC83~M9}v8E0o?W{vc z2!`C05Ypl+#Om6}RAMTf8IwF5E@@@S*rXyeIyF7~*}FF8KT3lpd*&$AImBFzIoZ zNsrr9S#k$6h+io!D#@$0%3$HLo=!#lK<4>1;-m0iuVd2L{R?sf5 zI4`e@(MskvFDc<9sVD-q^|(iEPm+ zj%wH88P-K(?{!lgS#FJM8gja9YSza+;M_BQ;MyK(>;`i$`gB=^G43U@eECZ(IC$3= z94wMt!s~sA4t{T6o^Km-l5d)`WXg@{DZ@19BxYjtlp!?OQ>kSWm+UN(OOkk$k}ork((*POM$O09kXm@~C>{I{th*jXN^^1>mThQ1$}F9u)bOPw zCccagl_Mh}6w<-`^Q({8^rdk~1?!W@<}m&NCMTQN?4?IUV~$K8#Q@SH83RaZvK=WZwE_3UZ%`-3<^v&HkL7l%BOc!Us)p!f|^u%uISO zOP2msP8OoII};0(Ozt2#bb};!Ho)wwO*?K%-5DZk)XNot&YuGBbPmtO!4`>RjGDz81(uqSIZY!_ede$`)d_hHH)D)%_8 zSjyd}mzX7U^SQ9%zp`YWmAhXh4X~;@js4%6GR6>eoaCfYwf9YYo-4&|<02GgTa`Pa z0F(A#8R-!pJxUo7ftmMD!l1bf2ASR?)u#8}dB%+AxsPW&C(I#bra8>z!ks+xZ`y*S zBKzPLW<4iVit)LXGyuV(n92YxNlmPzB(;4d3#p%Q7s$9ddNt-dqk_UW2&xwt>t+xb z_7%Vu%H2%@BE;PRQW6s8IwJ|GKo&Mf;*mM}oB~5_igo&PXFC7F<`B%o90C|x-XuJv zjrnvLHhC(pK2;t$Pm)ygm}3F6oFz&1UrZ_ZbDHzBB-IE1VVZMAIFnP-T<22^pv*=} z9RSK>w~MK2@KzsRyOe+wE@gk_h{eR``HP;OQh-@cvnAf1{q&TAWldQU!!8*rC!F@l zy=PWVc-#I{+*Gg}qXrB|_2*-_0-dnN|}CxlXHF3na-n7K8EV;hB-@;AS+jrD}jRqr1j#Z zMx(?FD^xpNZISh5cRCY}#Xwfu@DEJPX4Sh`4m7Ksc$f;pO_qp#0|$S{fe|!>jK+Ev zh}Ba19{VGyTDbB8PP&J35OY9RLAR(2Io1M7GqM_Af^dj=d8%>tI2>SQ7O!J_GFrVx ztthz`gnS@YD-YuU1XzV~Y$4{|DBrAm0yI4?*3dBRG#08AHN1lKGTU{iFrR4!A-4NN>i9j|~Fd#QCUV zhx-d;0@kW1wc{CsaK8|go(n=BpkNy4G^h`1G>yf&0<;jsvL;w*y1TgUKCYjJ`?30g zk8u0}w)=qwV0$p=I_-j5PiiT#Vkq`6q0b(IKF9eiPh%p8^*u{`{|Tk-PK9Ie_2rGk zqQ+Ml#gN+}y;6gFXu=NaEkQUql4`i$!ga1O1`B!v`B_va+%e!pWXnnY#meNd@5J8W zCg=|kt0H&{bOYHQSHLi$GUEV?ozJeLyuJ zHcC%`PJ*W68pa|0KzBihacm3?3Y*{1Qi%Q;Zpy>Q)q}b87?@H)ROzF$6dcB;H!+BY z;Ic{BX3PVY#XwL?eLWfbx3T>_eq%EtX5jTuan;ecp^frf--eJ&7lU+F5t0ALA{)-D zU207z9rOl5>1>ujJDS}r!m4I$a;lGHm1qf0CprP^Oa!k%d$ zF*NzyJh82k7!u(hZAh$f)psrp+1|NBQ#H5h|)V>B{|! z`2MZv>ZrBD31Rfa%$#m0s(_*b=y86U zIE~*W+!205NxXsL8sV-mO!!LJEKKKbp<3t+VGar-{=pwZY0d4ZZ90Pgg&!zP;uoPf z;I}ABS0PsOi^L`3OrbwYK+Y4F^E1Uu!Ug^&iVw2t+zW+uD8E<1e<_^crwOO|q2gg- zB!8EmF8s)^N6pj&{25^a%J5y{M+qlToAh_oCLJVGG@tcG%`1`^#aU{wD{>&d0 zMx#FJ0AVS=S{#R}w|j+I{26|aumn}he?!s0rNTpgh_C_$NvEP1>0*AJ_@gj}-zxMG z2Mdo-1ak^1mVYPgKykt8;%I(=_!G+b{mviZw+W-gd%_hIBwQ^_=9dU-g(3VH@pF{? zTPX||4x+l<=i+>RF{;bn;jal_pm^pGaXdduSj1oBCx}D&KBzc5mG39)MtR1C{8Vuu ze;XxFXYdon+o+*_1cd{uP+4vk>h)gd$BKveEy7Z9C+fPM7S8fth!go!!bqX7_!Iwt zzlWNz=lEgb3aPZhxR1mA({0RF@T(gp^OxpH=WEzd%7z&ukZn6>wnet@f@I0sj z+e`s|4q6GNbR7+_N}ywKoMDXH*k_7zCx|KTQ=qRvvmvvXxEuk@%|!buY_s#1;)atz zOgXOrv1_;Dftk2nhU3$5E)%y$ux4VODOIK-7T`Aq8W=*kgzMIWSRq6v#{1C>JhK|- zHH7$A3`__R+f3C2N#b}LwwW4Q4H^Vug%{^T zfwRJlOm)n~d6%)hT&c$0He-(!nPjCJkAoKB&daf00-6XK0Gf^-K&fCn9aD4yx?PYI z&eYm#&*C_f?t4Is(X&@^%?Mm`2EQ{%brN?{LLY>X;n)h1l*$gA!xYLAsa>@M=9prd z2bu`l0Gfa{SjpmHp!I+=3=*-=Js$U;B(=F8M-JfE@c=DMkDSIYtY0sH*f;qR`%Is( zzGvU-Al6K`>;o|s`a9lsFwSFx>Jat^VX)1^Hp2tsKo9W*Ut`TwHyaauai1+{li>+g z<98v>Jpy9bf&o6(7Hf9`w!guefj$Nbmx9*d{Nq@&v9t^{n7)lM*AR7=gYbO5H`|Ui zQ~zb4d$>0%+|0n$Y#e7_g#p$1AO-;0cO8lEIs)xZ!v1$ykHdKzrQ<%3Iu7kKkjTan zd-w10`xNxUGOSt8GnlXmzfT7-zF;b!L4YZEPo|IAJ=oYgfooRbcoo_kgZ(ei9&49j zCD;A3=-p31ozT4B=qnolr z)Y}f6^|PQ0SL>DCqS_+)!|JdWck}iTOUSbDRANg`C6Ng@=PIug)B(X>CC6$i@8@+! zbw~+wzQ$TnmZ_weZnIfi=anEyB(Dojm=V60*n{_Sn9X6Ls0tGq5$&8s|~IO~Vrg3>&1ul(Y-W6)l?i1Mh87#R&wEEGaQ zNGzU#Y)zU&;*xt1Ar5Cu7M(4q5NGX%nNIc=l$;A6Lt+!h5OO8_9rq35U_RLCf-X!g z7UD^xdr|W(mJ2&Y)J3YpQI}ZtnUGe5+75*=8_9_96C^J91kF)A6cU<}n8e9IXW^6G z-%)xGYFXDnEtv8JZ)d!*RXJ8`EkrFYR{`Oj^(k>=bdZrl%0^95ETZ%KAEy;{N0kWI zoAyXKa;;|=i&>>kYmCjRR4L!p-~m>L&w!g)aANZg|6Zlsbc@^zcf0$@shd>xw`=md zd*3znR&p^NgwZ_ymxl&<1BtU^edScGwY4`l&!kW+<;#{(`94SH{#u67F{5|L zDxIqVITRkU$^*6;5?&O&M^1|r_j&KagpUM=H5U~nl~>uLo~rPRE|AkiGNA251nTEvgJj^G~U_UkJws91P>=aCcU21Rr$j9yX3oYlY5a; z8;`a|CarfT$xppRmTp|&HesZmGLeDd5$@@1bZ$AGHyI!Pn|q<`)KcDzey;Yn)uD2h)0HF2vV=7)-{WL&r>{J1%0`wX%s=WBPljOLNk>s+Bz zRm)stP)?xx3I#{rB>m1#`1lI3*{{e+P2y*A66^d-t{5bkCM4hUBRv|Xse=cQ&lBI| zh&{tokIW4}&c_D z2}<6ji^gkyuhaW4TN;M{r6=#*qMje?W~xkM$xQUAPmI#{(QC7MuXPWXZRt#|CF1d8?N_LxO00ZOb%5A14iNHtcqjK<*_M7} zOj0n|FBSh6QPK?eH!?bnOxr%j+=a8o#5f-kvZ8l+=?c@kWW>gQxnINQHr>ICMYz}E zD+@(&Y+kR(UpObQNqFO6K=&lTkoi{YZJ-Y*G^aPm;eIAN}j_h>Q`0 zddSM~r5P95Fc2C~;)pY`P;Q^Z0avU_IznPI##4g-=f>@jDGSMET|<9ULoT|FG(!!W!e0X~|S`eN_Vt6?~d zAjL4ZG_ak9_0I}Rs6TNe4hSRpuhQNohyjg8za*jr(OvE zh}aW9LKaB)Yi?dnKO*B2TVbq+_EK9aImJY=aZV7iCBB1^X8Csji7h=T+~9T5>K9vH z%n_S7qA$u8XOl&VLnyHa^mk%4EO8;h__~FXyGdP%xLU8g3JovJ1)}uI(ZbH6r@|+XyNv^*k}BOP^1x({TuT}>t~D(sUnLE4my*D>966HXfZLnCN!IEn zaV;IuKghi)WN+1os+V+O;@&ao2~1tz<~tS<2WZs8t?n7x=r^_WzGAEx@j`Iuzy0$v zSqBfyzAJ}MX-M`eISnI|^NRxiB__32zII>_`*pEpjY#53kCKH+gWc!osSTt81kbX}pl18|j%jrAhR#FENo6vzQvou$QA2ttGyd8u8P2*njfEAmQ^{mk zlZAnWI^98i@Kk3g441r)<_^RW-GS=baJq3EOgvp9br^asrng^{n;I5`HhoH#_x6n2 zL$^aB&viFqD(hVT`uZd6Q*8($6Z`L}`&5&|dz4;tH6nMCipEvq(=O31;8bn!aoWIB z?tU-G{h+>LhS)g4KWT9jW&TI{YX8dVisjnZ^+1rY@mhPIMS7E+zTI+v4?H^h9lC(x*SM_j_;BqNegloT3=+(RaWt_ z`%@CQoFje1O&O;MJ9eP)JWZQEc`CL&xc9_W7VmjdmAGCOdQzht&kjyaY^iLxL$$Fb zx!FH3^lXsxOc2@L-&5DU7xMU)6IT6O%fRlU{#um-J&WwcKf})_WOU|-}64d=l$^<&b@Qz=FWcax#xV& zxyasuMW^*C?w3U58w8r07Pv1G>x=#XaibqR#iROu;Ew>W(_xKKs?!^~1knM3-05x^ z^lN672xgvl_0#r6s5vpTeqfpIgJ4M>_sz@*B1IuaQl*<3rXEu4D;!Tv3`F0XKH8+V z*a`9uHR!5)AtKuo=#Tf+?kI7(9G&PsmKv(=buO?9&C_>;FBaBQs?c{#t(L+yrRq?M zd6q(wcfO=J0Ys=7Xj}IWBr{SWY{6@(b3##Eyhnl`&>G*N+{K7G7y4;q<6X5eD4b8sv8)3$^%1+>f2P5=e~w<3%&fP?4xb3Kn(aB_G! zq8d_=0jxm1d5tJG?6E>rBPc{ME&#j%3(^S6M9EYXB8`?ZFNN2o=x#u(8F73meI6#ggc6?g@v zSE5Rr67_^BT{R|Oi^;cO(w&$BW`8HKNPEVInDP^htj9>OC)~i4cQI?}V2luT)?$?)&a8DH?;!dj6b3aultA zA~aih2Z^)=Xg#;FS5g2Ak>Of9+IA2&4|41{?8rQd(3zI>0$13;v8%xFTcTmLiQj)c z*l9XpPjy;n7R_{yc?37y_DGCGi-Fx$dZ%}t84>wjZeP60i%_%DYyUL%tkbkNf49?$ zW*;JvHa7zuYD^v3;4R*PT9~8g;4La_NhLiV!*l>Yk4ZtRSC&q4w(H${1 z4M%fu1Sp7V9IeICKK#+6sZ-wv=hKt$e_j$0ei3fz==27>`~K`9BGf7VEpgR;5xNBj zF2m{w{_2Etfy?4Ec!eaU8(cneJgOnp)$i=cGz?JX&C>T@{^Vbn_5%Y71a4*=X%jX=SEG~J@=Ry<-t zF^7<}_8c~}WLzrgmU#-r(Vak7GyoN#5;T5#Tl5Mh3FjfD*eVQDplzj%M9Phn2)Ev# zY2V2pvVKnVpz;Y~Cx$UTDUMy1xE0@F*l*M%Y3u_GO%tIxBCJ~UC=lz9trcNAMOaZW ztowu6OyDBzj>gik7ehM1qIrGLAWR-rD$&DN=z;~r$SYFw1UdK!=Sa7pO_C9u>u>lO z4AeAOJ%$x0s=mWO<%b;*VIKmJhm6({a$>daXSw<;%!4UiyI5ThovDWgI)I&F%5=Mi zs>Nk(GIXJ2Cs3ToAu`a2mdJ3FCbV+bU}Bm}htay_aw=6e^HXH>(ntfJ!#QU>7N+Z} z2#hlzSks{s=AmU+IEdle>3M3ni+9q0sEwk)B%{h0R$lx984#{19AW5D{9@w*q+9?- z@lGxuSO&bF+BgR(=MZq=v$PyKyKr1& z976O5gqi|7kyJ8==vnMzXCvus{9Kw(g;`hPA0AJgjiPn$;4(QE)METd>0E4?0+92D z;+aJZrvB_xuM7IQz9KX&Jpl_$rBQmqtYvUI=?UmVYK7d&C_s0sHZk4bQyaR$!!KHh z!b?t$b7}W>oa9)DJTy{ReLi1lqR483Fo-6qL=I=hCW?T%CnAG#PGNT&jS)RZO)S!4 z(^b?cRs06HJBaKhU#-Q6<9ZaQH5hl{GFiV-zHzE^0q*QQ&DuJB6sgT+p$3U$=!N6=wGmji2~N~mi= zjlD4_B}TL7^a|D&!xFyxF#W|jrG>6aWRmS`FjU$n-OYkq9Z=z&K4IXfrj6&am+eYk}5TInZ!ddmPCh1 zq^ZCl^40po0r0t!hs?_%-euV~5$X`rpGeG|*LNEWC|*Q>QI7pg3^e&OvEp|PB{$w< z@J7mfhcO@0#ly%PF# zViBguEs8)YwkSfNrlN@F(RvYrt2ih^$3^H27taMXh|pD$@x0!?_ePr2R8jD%)4Jt= zDdtmLi&8%ht$In%p4a=%>pkN|6c%I~!&DuVGUxSNkA6}I8GcX_s_>(ckcD9gGVb&G zqGFuAqz9_Gq>q|eGuvsJ^VV#q6_ZiNWpIQGZ-g_K^w}?Cb+i~V*a{dkq#Gd@#dd@% zz0nP>6q5xu)onyvy(Yw7(l=ew+pWv4I8A5QUU6CjmtN6chf?Ks=13QACE2g_sbA~E3+BYT^`|8Sxs@auhmBxi$5s+{qXkgHd=!K3S-y}N7g&+q zl%;^Dc9H!;=BMm0WYL=S0fCgfD+JOlGkyuHn51SxXCX2mH=LmGKF$2rexAANWpw*NF+bO zRP`8i`s59ayjvOpnlU!bB9J%DQgKOUKUXrqXlabayMPTD)V|TQ`T2-)PzW(a!zRXf`=RnsrHLxtgTgA8oXS7~hFzXJ{=mv}Nyr z>_IPx0_WqT5MsshP!{&Q6TJ|M-ywHt<8o|Di#uMC*`Ui;T1a(B>hiG0d7;$2P*w?^ z%a&TD1)RSRr8t_Cki;wtHZ~{G6-u@_$xka_kBHmNF+$`~n%g-D+`w(&qFm;uXOP4K zSxG1JQyfYNNhbtFT;gx*pE9YHm(gS+6&<|N5RcE700e%OXb%w{N?GL&S!*}hwH&I6S)!JWU)GpKNeX#uR%Cn*L z3nmmoW+jq6!Fo@w>4Z<~gq=a1$mNN6CpB5y)2uL}28$!u*sHSZ2ING?yWO z-0h&csL_g9uy3cqywkuwi1a;(G!JL))&Ps)kgU@mw$62ZStn{N5 z+~>i!UZW4TpR@OLnLc#%bXm_@d)~}xhUQdUbHZ2hAW}%bl5)@HD{13bQr%b5@CTz| zb?cYEf9d8>XebNL{wR4$%d9haC$=|^)UXrV`>AGPdnC&GH%zzEV4t<{lG8Nyxo@3T z{KX&WU*%B7-La`sKW88h_lxJ$N`v<+kH2NeOo0Q5D6@!Sr%HWL2kygE?nE1>N`;%i z7YjFm&vG|msx%j>!YzPGQ>8V)KeL!WvoI6ea~BSq+zf=7DvkYm5}e&NC!F2YA?K$z zK*tS@!%IF@+B8*a4?4lPOkJl&!Ybdtn2dRHc0f?-fFMl}u?7Hy#>vTsPet*VMiwQL z(9h){ntD0eJu-)WJ_rVO3B;!pEkrr|$9_vO7jxH|-q zC%Yv_qY6=_o>1uk1Xb3jW8E?PJ9vFbO zP=^NKXGG571EW6?Vf7;HI}vt6WNb%!u9zDxYMJ4KSuYCpGGk(dv?fAG+tEV$c>iCv zp&xsHuxCz>hD9K4Jf5E(ho87%%6q0Iz0i_2w4_&C(pxR5ntWxHd0!8kGClJ~e&*MS zY`kMdyn`B=MXU_O$#@4*I%6}dOb1%1Oh;?U5r(XIhdXZsJNLZ`W*!Yqwd z$3b+Q-;GpM!q+ikP=dQY;0^VnN>!O-1GRP}qC8Ebr4tA&SEqY9)?)}T9b;lKt;6N@=JfWCiUG8Xde4ZAA!#N(gzNIW9;h7BAapC;>#j* z$~FtLT^41I&t@-+u3i?MJi@;DEKu#6u(mm=e-+}c+|-$$GwcvqM7x4yuZqa5a5R=W zP;Tq_YRne9G#8BFj~B2ffA*>Q#pppu263F(?4xFLOVZB*20H?yV1)R- z5D?~)Xy|PpuWVm#3wP{}tokN{oEe9^jAb)QsOAC%>SPd;&km%DY>5`sN1EGKjTv$9 z%fYBML)@RmX}n@$SCEFBIToq*O^B+6(}V?6;$<>=%UE?rVfk>1o<7TWW^Bv=OVo3$ z$7%66uSY$HN=QqICKe{vDQ>f+1U@xO38b3i;Km5ifUPBH6}hDcvpq+%NF;-RnN;Ew zq@}C|OV|Xe$CQ|72z&s7z6BMNrQDo>i8T<5(npfYVG5Wh41mk$pcx!2h=UVwunaZ5 zIfoiqqKh?<>l4kX2Izt&lU%=ZV8I^^wlO*GhBoY&9D$n08}<3QpoIv z9g?i(_J3o#=Jxi+xRp**%8u1eYueuRv`Y?UT<882XCMz*&55xC?aWDeZ zb%U*DplP29H$qL`SO$um4}bnc2Ut5e5&?5GlwhDYoa)QX&3aZ;g`UKe!{sQ$tNg5}x50q0La6-~=Ys{0hsngS$ z+C`|dx!u~BLFVgP``(PbFyR{A&1F4v+f8?r`L1i>JXg;*cTtvq-77Pl%HJ03*%{Ed zEx5yJTx=y@vyrM78^Gndm^`A2Jk8#mIQiy8R7aL8(RCfzV5@^N=k}prQrZ|XJD#o` zPZJ-tp9VRgZR2UQ%JyAfo3hmXiw30(3ocWZvNfXA8WHuQhV7#R+TTZq-wE=S;wV9< zT~u0%+p|jEbDF*x)abN+6sW%G;&ug*?^T-b^ieOT(Hv<-MO)}fh6q_REQxnXqB_Ui~Y0^_J|$&=l{ZRaV`Ue0hZRx#%=P9)j$8A z$qf~frRHQ1&1Al*GJCb*4X3Go&R0(B*HI-jrFCBm+dq|?3O0Tf-Xh|0^;$$cF3Zh6 zhAYaa$}X$ev*AEB!0$=T_oU3pZh~%?T_*D_lL_}@8P^yTG%b_a#}w5&O>2ISa9OYK z+Xl0aWit0N8NOa)StcV-XpzOdOlCZxr9X>fPiXzL`-GP1>b0!k-ngz_c=5!1LZ0(^ z;5Q=639Wt7z9^T;lJ||%dgDfHdV~hK;5D%gZd_wYBFg05V-&oWJ0}( z-X zNP>Rr`wy}^n%&bA=pcpdhp7CHX3vRd8#|iWs5DOV`eK-fEHJzp!MmM zOo{{FLYse58BM$Bf34&Cp-P&4+vF$j+a`M8@Tgve#C6jG}w~8BXW5Srl5F?G7 z_4E6Mk4~t{8*9qH)Uh}5(wjKjE=-`tc466vvAO|bwprzCRtcY6vx@uVnpI8BD*H+u zm`E{?-jho3ls44Sx6nvZl&qKx z2wK`u#1^2?0`lQonrW_W7&nfRNkaz$Fh=Cz!{%l+ z+C(?2i5QuLy7vK0E5%<~UQ40oXz1p{(sPzS)MzdHhgzV6u(dtCmia^NZ9jJ3`)vEM zeyV9d7GYU&U0axLi^FndH7O0Gzc;`btPG@&4zr(j*<2?1%XXJl6P7q^q%N*2ClEfx zLCo6rGA9y38Nj%<7;N}q{}CWF1rFGOGs}%HMvyw4?Z01GVR-3^ zqpOPa5#y*?FV-<9WybH`K>)KG8!H&;dKOSTX@?@R)Y=muB zhI`i_6vyt$Xxf#*n)876oAc0nWOoIKUPPuXzpHy!2HjU-Ge+j$$@Y}?YBWYt#z=O} zK%m++17ULXjqN~&!YK=r$vo1kFNkD% zDtFkoUHWj{VXcU=4b&ZT!#PGR6EGRYE+-Oc&=q=ks zk2SMpoBXuDY!fa@&5Yed&WQ(&H942(l^h35%7q!Jl}1b1ChjHM9O&l^^ z-xy>rK-=Zdn9HAGHwXHl0%(;s2Xe30>m<|{I>6{iUv#AKe({w!spHu;<-iZ*o0|?nA?Q5)O?4I`m{yhNwl-2V~=$vfuWA`(gGJJ1V$$?AqzfEARD3(1hOXr z{DLA2;3*Xn0nxy>!)YKoo$;cm1rqp)ORz_4czd)}GDJHAX1g+!gtg=1d)#+pp@0Cu zuy-|1&H-$9;F8#!z(iBP+GkzXy7MCdfQ`;9L3f=0!9J}aUgO+W!3j;wxvfGBF>dY+ zaQsk;;sD{r)+B(OXN=`sUU~2a%!^^9hk+>dFCJZiU(|sow?2|W#?n|UWZJ6EV2WL9 zjqT5k^r-cOI$ooUWI4mrzIETn^T|7IV2Py^R{o z<$&P`Qo#7|*kTUBJ^tGU^OPqBbIvhPi3Jt6`$wC3r`9%e0G_AyM%D12;Ovbddt7fc zFRpjDoqq=ge3Omw7C!zHo($h_eflK8+)%{@ng$90H|oBV!drX-;!Kf=@F~AX`9U{n zt6Cxn0py@T4cY+?ZjdQ_P2V`PA9@;DgsPO1z>25!4W1oxXQn(C8JSe=6sQXBACn6|F_Qn?u*lfO44b!Z>u$wnT=u3QU!-_VPNAZyF_MQBCEJ1 zCjh3x-jV}tyCp|o+m6XlqXk$Zb4*5Gm2(o|C+snqpR$k1{+(GGwV0(*i&+}An59vR zSsJXIMms(YOh+|6f?U?@`5{|95Jj3BFrMNJ#b}q`Y;Icj?r%IUvv<Z38hK%0qFqmz)s<*4n*6^lF_31{Xzc2pJNpFhWF0a&%~hD;~z2P6&pTH?$h~ zr&Q0q9kox;*qSKD*$hB2W^8C$4&Wu$FbkE6PU#k<(HNr0f>q8A8bUZ9=$cUa`;hC7 z5Tb;@*}17(Mljd*CGu>oLH+n9I75K(^4e;erR~201h8wn#>7XxiZ=k4ilkmD6}e> z=43u>CVt;Gw-$At-n|d+0>(R0B|UMXTGc&@7Xw2$9F#Xm57sL1a3PIVIyDEq6q}dp zP=dQ&1&~X2xz11C<+`XRXh`aUO0Oc%aqJYwj&kgvD)uo5QVjaDe+PmD9EU8|r$S2B zrxK{irz$(EL$h`5!$1^%Q^!LUe+@?e9hf5XNf^6FW!|G=KMC`F5@w&5KgVf$|N3#K z^={2Q;}6O@l?tkGz0pIpKvN*Ye^L+CzK3c~aJ!9_e-ifoH|)=9pqii6_ExCJ@kHGw z74x(DF)mOyI!x%~pgKQYfL$OQ!gutu+6+G)Q+v#5B1&W~>stX<`ZW@YGk@kVuJ<(S z&&pAU(z9~UG8J@x8%ZV{t=FBEYj`P|rq9U}6ReLtS~wpybDfTpslO$Wj$X^Oma3CP zbo+zglcCIqCaW(SgMA3ZG3xL9vu=QGI4WTQeh@tG4MUmW^RJ} zw8T9#I;EAwP0WdW0o~LMi7~WHNo6#(L!sb&0Lfeh0U{rLSBnJ zFSf61U?dljuCzwxd1O%=VP&L-$Z_bpb)2o<0)83Afs*&|!HF?>W%U+M2XSL4csy#@ zdJ9l(y@eo&8#NW6R6Y#sv&ij?+yZbIPXvl%%2o6SS^*E^iC`a7wkM($9;QbGj&XPx zQfuM3j^_~^FN(5|Wp-Wfh}7Y2F&(7b(7$pLnhyMiZM`GhQ5Jf>)PHu6S=c)QYJ6$S#%wbcph{zBD*RMAlN-Ue+InNQP2hIp{9MU? z6ziv&k75Z8F~fE%6`iARr9M^!c1~tqi?qCMK!ojPDtk+1zNM;|80opC;(BFJ?&Tj= z>#n7uf2Aig1*CU)igBBj9iHN+s^KXJIse9(Dr@59MH`$Z~%Id4K3Y|q)(g*us{OwBSLMw3V zp;A|}2Keb@^XX)+X&7>0lkXFrq}+Y)N;Y*R4HG(pb$0h#?Ok+Ad-8&d-5Q>`H5_i@ zUfZgekmVdEG4us0E5khjQ2EAgB$~&3W656~b7o2?yE+DF%IX-X{S&!V#*6{)Y5-Ox zI?pLOSd3L2@-3L>t(e9wfS#jAc6tI`oZXep$pD(1lYy3}PcOeMr%P2rk-*o(4%~TL z{x_zp=wJ_e)#WldW*%@_V@Hpm?{w59=(rntTkhu!Bdxn3)DgldNi{flbi)pcS~&w>f1ss1^6a3G%|uZMhFx;Cp61%iZkT@?2;eZuaB0 z8(~w037c*xH8Gv`>^jMnBPfYb_Zu?V|8~#%pWGfD_U&p1cH z=3##F=3(UJK@Q;0CBYZEJ$*k~t=s3&@IZED-~AF;Q;Fn6ww+2LDbb~TS}r-}!Mb_LWzO(sljQ^*l+ z9MJ8_9J-yXe@=k6Q(;LxpWxd$Z|nayzMbsz{9gd>f4}==QP;>2;%m zh!Rvzwt=99PY~_)rx5MLZ)CEH(X}g2N@5p}@YCYOBjBxQuZrt!OGz4Xh@?;40aUeZX&0%j6-`fD#o8`4Sm-Q0*ER8v4 zrtSs8_+=!c{MeiSvZ?ESj9}_&mm!$C+GT*LYgL3m7gj}7^eAf$bp=zGQAe6XX89(t z_hWqbV>oNq4uWZy(K0OEj}e9?ey1=j5sZ&uS|$wbGFnVs4h!qB)Br!HH=olpqnQ?i z*J#E!nh`oSC<7s2G}Abm(T!%pzx`#1%Q~muGhpr-&A3N1Xc76Eg6SalCXY0(OJI{n z`l%*)B*JwoVk|vJf)$a=!NTO?#~HjO14{0F3HJEJOqc1-c8|-NbFVLbrJrtJ0yj)Q z#Tm%MV9j$!evJPpkg0`(j*K^tjAxHYeaAQ;CABZ?RC6SAjDu6w4GUmy^b&3dzhAf= ze3l!fk4fFfr1Xtm+&~TZNdZ)u(aW6C3lNi>KzcI7GR1DXc5l_f@2G0CZzgdUI+omm}B$j){!{V%8H4km?6MJyxRr!(7! zqS55Gp?t&!@_3Gfuq;dm3|-&ytwSBJ1Xk7QVZ^8+F9kEDQMS)|y8m2+;^^;#ZNsDT zKkMl!7|{4xPpaQycF9XXwM$-t${&-%qq6pZi#p7E|CduGoF+Emb|TtGzNTl$cHW2M z=rbkk!*PDv|8N|VK=I$0?!(-~gIf~POr})_oz~vxK1idMjMN3SR|TP_WWDh;epMyy%S>F z>vL}!3y*e3FPnD}7*oBA1gh~af+{;~7R=Ve>7F(gIjDbiw0#!}rna=0itcOKyy-x- zdDEfRp7q%vez4gXsPmzte9$8t)Y4+`^19ASOMn(iOVEn^Y|Q<8C%Q;!drQRrw%$*^ z-`1ly$eko}O>Yz7+-y#S`>9rhqur6&Xtk%#ixSzZ`v3g|GWr#Asg>EZKGh6C27KmG zcJJGLTqbctUzc^nXI@&>5z64M-6h>5Q?0VSWOY}A^F3YN$M%bweaX7wB`ej1iV{Go zR?rwyuMB6iCId~*nv6cMyy+$5$1k&YHI};?7BU~dJnQ3^(Z}2qn=Z=i z5?nJ~1ZD1zU)Jn(>p2V@Y)=<$n=V3|ZKpI7a z@rQw3Bj1kOHP0qG;r?oBW|oc>oe*fZ=mdIodIIxjly}*@xfXW&R6niXK9%X5qYFA= z?wq3xe8R}iIkeVb?=!l$%T&20-epaEV=kRBNp~%Z`)r=#4CLXn;W_orbN*vO<^mjl zRDbiR{_M3VA5?&E=2{edGmY1xgb#)<7Csn0%Z&inqH>`sd@4}sT2#%oDEyoq+H#6c zaFb#vk?`gxc2(dn%)U2E!*+0+Ao&1n!?Fh@ zLBFLQ12>`!hZ^o zse>x^sb>3B_FaYVT}8H4RCiv+ygh)s;qNMh8_w?$Zn#iMP+6{wV{-#aa|76i3ZR;Y zitIj#b$jH@uLA@Lg!v{;xZSXyzx_}{&{ICU&;0DZ)Wy}zGg9`~0ghh>z_+xt9Mqj$VLl5V1~cNju63=VznW<;Y70h%@KxZ8cHew}$}LzC9%N13esn^%6M#!tVk5wwxB~bF))$ zr9_I#H39xZz|n;nDO)Y~Rky(2s`<%iwY;fXZeN-Wn3`JyGF{detKZ>ZZ>Kd-)ln^n zUWTW^&VQO;lXk{oxh}y0xIW;vng_012HXbR`heT!0kj`^_yZJW<&2xq0gz>GSHzMwzdi*}oV#nS~}bjgc}A&A^J$b-M2fmHGo zd&bETF4LMmAO~Fc)m}kRghLnt-Qud3yGErFR6ysF!V$ycEBcGSxwIEuTDN~pmaQxz zGqVkAh`nlvec~^fxsCUiOmnko?pbMWIR2Zq+AkB2fvIPM7CatQeaef+Qi+Qpgb~DY z8%uE^-N?a%v-f|Auf|AKJ+4R7x7roy6N<}*m0P`|Mi!Q_`@RHX_I*i=Bv9$TFEuj2 zTC8<7hW<*0{yO~_zClYJ{1R;-PZ-dTy0h!>nIclJwWce$fOxMgdV<#{3V0u12$>5= zeGaq#>#zTS*;lB+$CqJWq>bvmDZ7Mw~ z1F7Mt%n0d^>maPMPFatyjRwsz_1Z>)4-;r%i~J5nH)Eqte$d%v`rx>m_gwLW-*hiJ{s6=wGl!|E z=fN@t1LyW6AZ1#esWXWK%D^M)^cSQJ=DkQ=>=rESjYh6 zQ*cOH+>)+nN!Pce+gj4yE$P9Q^ly$#N^k53*N-?&2W}s6T8~~ee<7`_7bCx46CfB~ z#&K{unJhyha=Z+6(n_2&WT?f_shWfZ1UWOXfDrn-A(+Po36|hQ-Vz)w0EIYXa0oS6 z-pU#b+Xgl936JwpNe4a8`|XDcK%c~?fj&jV;J8F--^8b)V|fV&4Wa*uw*kKO+>X(^1>3d*5Q;7F7rhXX)5v%5acM(~709ZOiW>s~CJ3y{ zXv@FR)E`Ruj&y6$2e#D~93ox~&NZ zBNbSvdSe%R`g&vRFSwsze}iph8*V>`paS)?pBAqcp-VPk)vx@Fl6ZL4n4i%+_}`hk zq>YD?Oj*+AdfNc@eWL1p0(#lkj~T%D{*hq_=VzyTVts!qgee4Ijj`}{hGN-Y#{Obi zEN_$7C^_sOVBL@&{!I|3NKL$Bn=Y|Ul|g7Wh@lrF&7UgcF+W&=0Qr&5n6eZrn+j3y z$rvf`P6!F-P6%ERQ)RQJ%HZT?l^(~y+QEr;zT;SHSvJNAMNnt|l@I$`)tpzgexNmQ zcuUk$uU%vp?Rvp!3jIytvi@;DYf2e{b6T2zLhLRIU{8nzYCa)W+y)Cv(z4VOVq~9u zLhR`z_K8GL^*qF7T(9dbYthuxePq#zPol6|UlSDdkurD-jI>u~pXZgiObZ=y2>sqh z7V~I&)S2IcIK}-5CE&L;B#+;g7PPj7-z`AoBjpo4KA5~~b#y1vZ}Lu+Pk-P}RO26F z1^15G(@(1fpG9_}ryh>Qi zrX?DdrL)?P%)5fC{}Gvo>(Izvo&`ihFY&fJ%Bt7(87+W3zo&wv1(7FdLF7qV5P1>~ zkr&N{xN6bqQXMG!93$Z}?YZ!d)4FR!{Pct9WLzQCkuo&=Flb3r7^XKHX!XY!zo06-u4g@$@2 zs1>BI><5kH-$VNDlU=kB5sQzOHR3-Z`g9p#t@mg*jD`lw%+5g!=$Sn^{$!^ZKX?i#$}qk__ovf z{hDH;LewBR z-Y1b<6f|K^sAmj5$2{$pHHKrA}P#pY#cK&(EfEA zctD&#deLcpZS6E|)GTOGP=fp|%8xzaU>e&KZv0)z+4T2>`^mc}yaj3c#Hb&_ z!JD~2N8t6zim%`LWlbHtEw3so-D%=B27wfj#tR}vqu3`R|W(1F$d z-@2eaL7g%a!;p+)YL3C#hY56HVpwT<5Dvj>(t|jxv&i^m7!R#Od#NwO;6vu|brAFq z9Z=el7-9k*Fc~r+z zxo@dF_Vt-?nOi%+WxjQ7Mt$zybHrb}(Bi_f+fqH7JO~AT68^UAV%E4i= zx1?%$l;Dtchr{wL#@_22WD8Gq2Z=K8+1c<^Kl#E_(GJoW5tog&(xnE%Dr=+5G~kq; z3Q4uxN{BBubeB^F2Krt~eo!hoG>rVvx~-ujTVMdHEHFUZ@8-`*0Ql&#$l_jTFgrEE zPrIi^p!&j}N;gAvw%pCQEd+I6$!^BaY{tn~^c?Iiz!CKp@1?;-eP8#ygy^E;J;mlT zCwh1y#qPars=7$1cNBoSv#317jkr-l?sq9%LBw$z7Q|yiaWfVK*ibBp!-ir(e)3{L zPh&$LQUC`1jbp!X>?X&qQGRSF4lnjUVM8tbC|q&C+>f%i3W~{|u?lqRjI}gI?0F-Q z?MFHKQJEib89#9)09N5kYN3tQqas|Uop<-bbOpU@{GD=y$g8?VG5sj6J9$CnuPF2+ z{U{&o5%j7z^Z*5|)(g~Ft#9!e9!~j&Q^KGNgJ*bpF%p>J7_-;dF00|jl%11?@^Xy5;v z+7<{FV|h53r3o`lum?D*JV0A&(`aYVt%fsAC=uMkwpy;BZtg zLf?td4fDgn*-1Lb1ga`Y#{^~AzmLvynyUT?c3Dq;pJ^mi4lmlj7diqiiqGS;O*_zc zK=mAFE9=$Tw{TMpQu+ z7zG3Ki6U4Nz}x*ItfcjVtgyJ&^Pb2(*jQEHCfu#2q}^ZwNOtN1fl{iIK!3b|(;vUx z_Obr>*(7vUvWhC|LZ%s0OS=eqV{??%KH@vE%XBq!AcVFW(vh4@fUr-0kf2*mW*MD7 zM(ymwUciASU%=5C&TzM*jXNIT_H*n1wFB&-o@a1&M;n3Qmc4F!2#Wx1%BT88LzH7r z9)x+DkQZH-D!GsstpG`BR^*i_S>5S19yY7HK(n&CgDAc!FaK69{Qj$BWuVNxDZ`hE z$l4eJl*Cs?^9o&RhjmyY#CH-y3>I}UgpL#fHx_yyD|G`(|C3Ue+S8qUfgt>9U3?N3 z%mofu6&cOUm)Q4_ezcKwdoN4QG4n{Dtkx*wBisf>N9=u zm~7;n(&TSa2q>E9coUyK0VZyWeDV#cRzkzt>Hr1LKi`9Q$#kFZQIrQ^ z4|_PEjV~f!=zGxCN9MlTJ98Y7pQ&V{N(5?zhzwCB=!8v;muF*{DjaV0I%Ai`q|wP9 z8ATo$MWOcWN)o7MCCRBs$csZfHFB_I)W~ZgZO*@hx&I8L&vdp;R}x3lE#4Jl*jtj+ zTM{-k1?Ws_3c4Mcz3Y~Zf_UyM)z43jR6h#JmPh6>Z}-LTQRE3JdV%UWjzf7T(7%u5 zuyz+IwmiX4-tvV0PlY}72dl(eVjtoyu@CW<*oSya>_a@4e%{ja3mJH+ujAbFU6Lz; zx8^^(qEQ_)dHgq*LZD{_(=pSyrx1~B$4r4Pbj+;SQ`j0F`$j)Uwymm2!|o~c?J49U zR4!GW+7iq3kB59*a zH%0MmQG88Nd@Em}F$e2(am<)P4%2WtlPQXS>@75=kZ%oqbT1ch!zH*jj43paDNOw8 zc7n_FIp?k4cLwSDVJMkHLA#Ekc<9geTL}G>^BD)F5Oj=KLB3YT1Ub<}@^C!0Eey9N zxOay^;F|%ui*wmN`0_r4QKBRRd&A3JOP0t=vt2r}kZKcT`&Va>Ug%yLHrsV7+Xc%c zy~hLu+M+_-FNLz7MhJBG(+DD%sGK!luQR7x^mOWcy*;AK>O@n>D6sS8y!r~cEQp=2 zzdBz}1!>7Yf-Dnzub;11Yly{K%KXO^w3oh|p-7P_`cYU#c*|lHZKUi~bepGQEzG1A_5~frZDI0l~-Lq{APSHvs7P z-2nLY?;QTcu`AMeyo{ZvUolT_%4YA+1FF404|X?BZ}aPITb#;$yA2zs66kK63SP&z z$y^IF_f?eX0G6b?|0?R7Irmip9OBC^Pe(V?FEXEpK@><}Nsyb{meWbT$9^8>r|i$e z=>1c02m|?`%F;XqFc+;E**t~zhNb+z0AeZLd;?-BR#x_OC6RHY?ob#9y?BZn1r)>Z*pR z*+XGIXbcd0U#M-p)m0ydLfb99256-Y2?{8omLTl%KY)G$%5veCfZBjchr((Og|+td zG;X8n#_AZT0vHac!uK#w(w3g#P?#BhxZ}uFu=Agu>arFenhH3MLt&YR!r+-y=I7gO zzmmD%YuT(x=B!ECkl`Y4xWCtOL(KPD%lBGTMV^u|zXzlDEo0(QlIQne1uWH9%#KWP zj!Ypml#(o;7X_f@ekFxNMorX{AR{ZoJ-L3sTy4)0QlX`d2uM+kqfP z%8?|XZAX&eg?-DrFGwwO!)i~uy+IJmZf}s~MH1WrLyb(m%D68Gea9Ar0nr5za+uvR z&A4S6a(jMTX^B~Y8ViTPSJx}lR;#ka(cRxt>t{Umf3!m*x;-a7mtZ&Q(iZx zyq-TMzG#`m;s~#TX)WX-tifGLsdEZ+S@UMO4Z3!|;?u+4OLZIU2}EV0DMo*m|yTCkC)RSNLgh&lTvR?FMdpwasPy z+#9Srt6CcT%(2g`^;79HYvJZ@vmK2< zx9IVV@o2=%1-K!Bf$bFcB!;>kp)}u=Q#Uu$qvquw1-~apYHvrE6O_3f#EI*~0&R$yO0<*VBrK>v>8K70OW!D} zoPbQ+2xEeReQ5-c4tRnBHc|9sU@J+PJWDee#bpCSI?Z7FcacRd)3w?Hm-X`QT)I36 zcG$N@Z%i}IvR62*bI}g^^yVhbU|~+xlG-56MxoidL*4jtnBQc=%mzv~qtMO5Y^PEv zczUV>`~TN(UR51olMk7b4>54I{LJQE)4xy0rME7Bysa6`^)YydDs?!t!Sw}$%#gv~ zY0Eb5V1Qa0Mi+FW^93R0amiKX##ROJpl6fAz?;LBsaQuOSjVQW&x=>A%f{cT`%pwJ z)R#`n%R16_v0@!+vW0}AH!Mqzpg8mXU}{^iWx;ebliB_@Cp})>{&Ka7L zoUkkv3_jIlq?Z|)kUDrDEE_d4A;0ar_VWubCzxgxL0G4i>R7VBk-VJjYx^#0iahrE zFP@(uM(5l+GXrgn86XSAe9Sgx9B#~jo3zNbU1GlkYVRFEGC=f%QJVD0+{#~`6P=M_ z*1;MyEq+Ny)d@Lvv~)sVgCg=BMGh5joM_%JqF=rQu^1X^heWe zVG>)dA+v2R?Ru8JrKkUx!`{*ZodUjd$m*R#&?Zod$&$VJe!9lM;`{xy9^VgcV+%I^ zjm0n6Xn(l>B@kok7dx#lc3NW2f3xsUBb1(f28_saMuQP~#I*fJl9{z&V~gJSZy5)a z2zukk8LBG9e{9HHfK!q7GfVriRVluz6wnttVu-3WYUVPho@s>2oB|TINNsHDwn5Z$ z@wQFYst75R2(KTc{E!Cq1qrdPY z*w6nMb_f!3eTun0g<7yNeExu?PV3TBRc1ifz0u$CMt^v8HMVMtbAShyPQwlHOtl3i zo3B!I+ZZS|p6_9djmfW7c*f!Ekv^42`mn<-^l*#2{{_ns3)*MvfuU>k5KHQ{_Qnl8 zkfW>zX8S|QUTeSqT6@puDGgtaG$wo5N!XN0oI2naaz-HYr_$R{Kqp!`#A8E&pK2Ql zaB*s8bpiMih@l30is>S{B)Z?+(a{vw1^UBxH)ztRu>+xy6JDO;4CFz0;kmzE$XtiR z)V*!4dz+oB@Xb|#G~#|kzJD$cIE<4rz6e1X%-+d^0KxRA+10o_} zP`pY685BkTv0w`t?8a~nQBkqQ-jdwho6FF9?{)f61ZmQHN2G&F2N4hi=^*{BgC&}H z^Z)+ed*AoogIP1@oW1utYxnlsYbnV;meG_~nh8GU7ko_ZIMs`o$v1_MFy9pHrbiq| z*G22@rR!?h%l@!UjXw!%gAKROU!os}M=0u{wwl#5VylL5lqavLGMcXYw$sYrIQOdw z_j8@ryZd*Y7Cim5(}D*d_fr3F+#>OnVF7<3*YkAUeXK9)<}FbMdFQ&V)_2{OA?U7$ zHMmH_C0+*NQ5TlRADBq-<)7NOpomj`2X z3R`!g%|{UWZ!uHwYEl)`2awde`*)bA{v!l#DM#umdZ#z(Eq>=MjA`9RFeXxM(y6yr zuShK@e`V)K{P=11|5iK{ptKHoPyo*C(}^(vA;A81uDUv z{^Im3=*Xd(Dr}IOGY++C*?(&sN;3ju{9m^XHRC|r>Oh-2J^R)2k5A9)e>gq60LJEk zStHeSH&Eb^o{XvzX;vA`Xp_5iGOBZ@^ja9D>au`6Y^AFoSV4b_K&2g{?m4hRg1L@% zk%|t%mJR6hIk@7(=fuGkP=K9b=8SyQ1<>bgg)c4UaS3}&5w4;Dj=AYL%tBOF%>1kd zpUXe1fq!#q1L0P>5^Z3vR)rrn901$2+N^ZtoYAP#l{2L)xuq+WWbs-sFM;zFwP`A( zR%~?=C3dM+N&B5icf~JOksz^2x!*xzyZ`n9Bu33y`JYi@r2e*O#7{Q zr%%uglJC$BlJC$BlJC$BlJC$BlJC(CiEGZuQk=J{Xr;4S@T4 zr&H@a?n_!(Tzx(9-guDIobjM<)B*i2wv%?624)gze1iIo`X|7z^Wp0ML6_Swbpi1n z`1NNqiL@UHQnCN<_y|cCQZ>T=1R;UC6%Q$W3K=sFqOAi9DcXi$358}P_uFx;$YCXD z5|xLQ05w6w==oD$!Zpkz$d&$?rEonrTqb?#V{o^|{*BXCwb&bwj)}_e;S;1Qm?VL4 zpb`d`ApvA(HF3}eCqK1BW=L)`m|(WTb}Zw;-}PjbdOQk^0TTPeEj+^Se}jH;~vehawA1Xxyr{ zuN|Pln5dsM0V0+`S3Z?Ows*a4;LVfB;Hyjm3V8FRN}2wQi&sTR!KoW*gLDF z^ZyET!K8rZIws{0mbcm~6Zy_C_tlr=X01n{tgmLz~F7AbPJ0x%UEHteOq zT&{!qSV?);HnejCeCeFnwQX+sf6X@JK>b2dIe_vWj2A#z!TbQmqXkpsx5|*?4N?u) zO2vy?XSPvRZlk=@yJfr`^lot|bC|q6lmpjO~v&{L_Q7FdBeoOO*vnQfb}kU{S80vP&jzz z%B7okO`2})=qOQwV_3U)ygP=qdk3KLckfuad&im&aC`#iXpS$_4B82{i?`5fzL+Uj z`tS)WC-Fp;i&pMf!k4SfT&h*F=Yz7&6f_nhg61f4X?VFL3J+bbwN@HGdiTs7a({`YEKu2UHuQ@{Dm&c8 za0J@cX&YzcB;S56%Sl2`rD^`~^*cR1U3+Lhd`;J$)6*l6UA;;|;)Jr{*IKXQ&V!Pl zGoVJH*5Nz(Y5KJ_IO4F>Q0K}ed$nLq@9m}@oM~91#-)SSJUDE%I&jfask`{RcboI? zR`t+T)&X*JUVq0`>aw*2T*>dm7Q$lQM+c}C#>c7VQh(f=hh@Gq;s~nmQ|c8u z>e4v=m6E!ci!4}0mQfVPU1U|T)mzq!oj`}gPDtPwAqI!BsFKpCouai$PMRV;Z!egu zxO7(POA=B}z+nj?Y(1_}kS3(1zP)zNYG)Lra~z3vsX~Y@O=tAm1~D0kOrxe2y48y?P1J^I-t0y<_Sqo1 zgvqt~d*p)b%{2NmJ;*Ra<21yD93xHI%r}ljs+gqK7IJ;oa3>s4fw}IXZ^R{F9sqyd zK`VEE(ehm<=!HfkF@W|$LjSy0?;4;uj7jAg1QUBpA$0-txR!aFgKIvf5pfO(S$5Sc@D43m)pVah;72 zHDW}PAjiyZV$@6(+=L`c?-n7uV3fyp#fXII7(3r^zxK-~mf}rvGO_WpXqmsIOQ#cd zT3pdy0w1DXeuN>NJ@7UENHT_EHT+PgMi?4`N=L-MQD zp$P`BaQx|ml2Oxg5g48ibQX4I2zna60BI)4#O}(7nkXxOBsxdj&$55o8RT5J7o-`( zHX2P9@?0OpKCsvjg3&AA`TWZ+e zX`Nxy%NNNKkwq4)(U^V@eQ-$!mmKGSEOweVZ#fY@lI@q|@`A4mE~8kMp{s9$(9=#J zJWN<;)kfynnD+e(2&{+=O0spR(>sI4u`Hu3cagBYd5Rl} z5urtmmfqRDl zvcgps>VOQ;g53a{U{Lj+)Y^BBc=|xpyrXLpBdj0rrWsV2Nh|dq@~W{G~2KhI@l+;{3@9YzTZeM8^;vDzfRboSRo2ru)# z->WIxRq(O_))E7c#}N)a9TUP%jEEY`$SWPNEJy*>?}LojhkPcSGtBFzMaG0}N^XPG zV5-yKPAD?|bq-4ZqZb{K&C|R9jBpo>K9~s?)oKC1e(ab*(~RGkb*du|N>2EL8vlr) zV)qX07er&8JDw(ZJ z#3gY;A9Ekn>p_a-V;l7&;u2cx1xccNgs;EbpzXwq@?dQAN;g?cZQ=!9&8}sTB;n)J zqeWv+Lw^N!dDs!d@vs4Vk07o9Cj{eGkGlQi%hm!rYwU=TjlYmrkrm9&g&LETH=CWr zw>u9`xtNlEbKtWlxy_{lHG)-=+wUGU2=JpSEvUs zD!`Vuqz_fU4#`dvUL*LEOqi}p>`aWX z9vP~9p32YI3Q?{%K4SPLytKm2;)Q@lq!taOa0VR%Zh%WIGy}6!=A|KKqatl0sjI#w zJvJ=tFcc{sOU~edMRB9oXn+vfDWB*V?${J-EET(sdRTHsK?xOY-R_zEXBgpGob2_$ zcD!dFWC$A*={xem*nUI997%#bWaA&!Sr=ad<+=`B2Fv_1hNr4I3yFCD=5}jG=jg{E zcrSMBPS(U|QA$WSHwq){GYaE`k1WQ|Lyia*;7?R&^JoTANLxDrQ(=*Pqc}cFTb+s; zuO3Iy8tylpP%Lzc{=K@0N5Prl$O(>3*ii6+)6BM;@oH@ZOO_Z^kh&q^A}jbudBjSn z$Xy+tW@T;h0$Yq=Idh!)+N;{uL5#hZLklq6C3rSn87DNwh@gS=7jZ>VrGeOpkp~09 zy?J2k_;{j{YXglvuc@?j@)et5cDh9cg86imM%6~xih41^!S!KW+OvX!N^Fxv$a3m- zsl)~+n{n?X-YFvS;j5Q1IT?-<7`!fhC4`%)>2PN~vJpOo#;CE=-&NF6A8s7df5egj z6@^tKaGG|1BN8L*I2m0-iFGyNkYybz;Pm26xUEyz5J#G@_8(8aAMr4X3Qi%a;KZA( zGLP>V68?=a7g;9u*!xCahKxyCiRJ7nXZNJ^53V9DO7WwDktI_SVK?n~Zu?K=Qn5pV--m*SzCITG!y< zWOdq<8N&)n+a?2_L5q^dIei=$>$qn%yyhobw0dKkkN~??X#S`yCYqDWZpH|!+JdA9 zQ!nnzLZn2?A!Rf2EZa+?I)&d8E?r)OY4%g`t084Mj8XmaHx0Ipfn4@sA|r1yHgJ?> zRgN7+p4d?%L0+D7LslU-9U}r_t)k3bY)mB}kdR@cKiMyeeAL6{!mGjqSx#mn=}nAG zG%cCCt7y+#DOl?Z5~d|CFfUp_;J)>?R7t zZtlu+D$aHa85AiIt|I{zS$(rUlGCS68IZDJX2pXEG5NfDj7WLq9^ql#Jz0bu2aU6A zgSd%Yo~><^h=URLSre@#*~0dE?D&mzRY<;p?1YMmS$AGGBvMuSa703x+ZK?VkKHmd ze;pt!E(n2Uhj`dm#B+sGumch)nlW|A?SW&X!{bEJDZ;hO_F;F-TSR?vzOf+QdINWH-EpAg88akc-Fd60d&>r|ug7Gdw1#R;2njot`OJ}V*7C)+Kb z`J@!^DFiFn%x!w2QXmgf1d>HFGC3>4p`wQm*=RjfQjy6O;7Ch*E;>2JA_@cq6_03EKXU0WP2vs=A zueYxvimwL3yJ1a+HC|m+QAthwjYL>zSEp^MTiMqjLl|bS@570#b1BOe93nzI%Og6U zra87UIOrvI!@#GzF3;Wm4c8vniKZPh24)h>u^Y$*+jP-(*vEHzG+(fa2<>zWb#1yI z?2esYq*y9}_iesCA5qoesI&%COFK;xnRC*-?#xFoBC?_@28_sQgTPn9T=roFZITMc zTqmgn43;RH<>xO{{&E4n6c5d6(tkPL-;(UmC~(0DZc$?@*WS!wHycG@%M2oWQwoYY zIo#JUJDCn~^?*@GG&YMmvvTy^L;TFm`&~r}M5=cUyR)==G>(yj+FK7AK6}OyKugijJBEqyF@()_&je1yaX1y*^oKWbPUxhvuc!mb8g>-&FO_eriFR6C4tE*r` zzvBL&VfH~sNlzR^zrS^{z8j~<_eoYO=OE!4?Vmm4W7U%mEzuw}RDtG=vBjw|{6rW^ z*%t_GrVFc;;9cR^P5qME2Lo|E&xLyk+k{yjpBtR~g3*HdsF@Ulp(3xW*qb9x} zvc4ez%SRnpzQL>dye!|YLUt@P9p1e~c1{lp#=E_oP%8PJN=OF`kZWJ)(hbzEUrM&h$*qpAyaLDg(54Dezp5G`)>4SB6 zLwKZm2|w&Y)0+!rO)*psl~y*!!L*4kEVVK5i%aop1!yyh%X7?2W}-P((5^ZP#v=<; zlLZVjiNa;9le@~NEj?t@S@<^2N^mU{I*JmkIlX?ZH13 zAl>2%SaIPgjv@)db~Ylp)lt~njoG6~OxMUKE4ucT&!7m#|5m=wc(YzYmn~5G{IOi6 z#Im}{f$4G>V=3j02RyHQmEHp4Pw1W1?|z+O*h_f#dYaC7R7E*pN)qu(j3v9g z-FyUX0sO^;i{s0@!rl}|KPZ(wQ1t5j9m=Z8W`xp&u)Z#&(B38PiJmcfL}kesy449v z5(fn^TXPr11PAm6ao2$09oY3U!o$gatCOY#?SlQ1GA z$s($?)x4~ik%bx<6Z$=VF$Gn{A^aR@`21DH(?YRugU6NmC4Fsy!akT>)VNHI zM;E&^Ld_U6_St1QQkKxw7scH|c=kp$aPvCGXgabCyIp08eqE!<-TXU59M68_nRRmw z%|;d#GnN_+CA!8Bj$87bVPKclTpi`kpST8PYzhLkC0D;}s~U${aKyuDWw71WzF zG=ohUjds^{#tc;OpwZ8;4eo9lwzj_oVoyO`@5yz}D2{y2hS&y{l!c7BKj*Q2M$?SL z2IXCC4e7~f5V;0?X>nW*i`z*>+4L&v}^qR-l~=@}PVw`g12ynGgCYLcOYim*_H z2*(Vw=T5EIX04=(`oXlU$wxQfO)SP?eU@ON=($$_dmX{*dX*CImykBdPymxtdHohH z4c-ZCw+X%~!A*&b_G*c?It6huT@3Xm$zI^oU(mr*AuO{zgMHGvGOq)_E^@u&exr3l zSx6ZlDzFu&J=QL}H~Jd56_a)9g>!dClV!AkLOgsrVv!_lXUCJv2-%9ja%kEzuG3?X zca4bXd~6vv@Obt%1T)73`kbmJhhBC*=NMt*na_LSlhFj|#g$K8z80 z?oEZh1J22M!zhK3W|Z3RCiFA4^(L&mq@{1*^>6>pYLSYf#DECm5qZV%6Age5k3vy^n zaXKuPEy0Ug1-sIs-qgGbau=x*F|6^5@uZg()QeKcT~AZNEJ@_^D&|?cfS$*hDCd+I zo8pRiVe7a)r|G_*Kf79ZnFt+^D4q54Yo5}(05H={3bSFZV=g_`94{3=^U;@0lVi^J zv1P~u`p=5qcEOajD}RAuMysN~*hF2J*3u$dlo)%bU}dVKvjr`-!k^>>ps+#-z87Bbam_ zE9`ocUNGdshd~e9JgBtyjg!asr^Oi-==Ej`Yy(?r!@2o1wiKGLT_KMg6>msw{TGJ9 zH9V1<;@=l;^^sy)D)!M(2_`sJ=@Xq9n>Q?iG8|XcJ(X;g>3Q_TQjB{HMw1SMmQ@9D zym*Z8?@Tvu_UxW~Y8;130nO?0Vk&c}+e7diBfOkn`-S@nnyFRNs0>q5D1ABh(6?1c zwVZSX)RF}ZrvA7~SY$(FD1S3dfHShDGjsFgsp2w{OSiL2Thi^_~Eg` zeP@Dc%3x4rRwMkRf;jCf1$+q}h!yS$czQAN}NqbZYVH#`0a-;caqT@tzXzg&0 zM`!pv2vftbH{j-3HrF@?3t&yhtIxhAq~5-|ieWD^D~7+iW{<%U>3vl3lk>#Un@FlS zB!COU+;F-ms7W}H&0Rvova)Rw!;`XBLP^cht2Rn`Q59Pm-M}4%7O(bs%S5f4(>ZW* z)(g^qIqQ%Y)Zs1shDfd$8xM}Hj1@7yK@cZ>uP)E~Lz&U+tAxjNxVcrqc#Sl*PYN8o z14h^n?6SDx#KYh`r~40*Q-rMhD4VfX-#*{1qrSF<_W&lGeGmLX8+rCLA8F)@-PmP6 z<$OnlHQs=bW9eU(olTi6R?r#;OuO-&kmU4S{DEJkply>DPZ zHh+>WPxv`Uq&|qL9K8brFLiP+Vex<1a`;6mPfw;u9B;oC30rVWw6yi+Wp5Hkd7C$h zSL}6dK(5rEmaP1484L#}^yC zr=*&SlIJxQ;~OBsW?@oT^=Pc;S)e=r7b0TTt0Z_(ZW_f?w;<>`Tiij)d03 zB>UvR$DHoJLo3E&)bHZY_If8Jdcauces5;2xuC0zDkFumj8_^|bv&F38BG%nKnM29 z!`vm_&u4&PjZ87!LxaV^rDelo{8dD_Z$Vr|LqQIHVhO|WG#!65RP5IE)Q7v6a4pKS z;$`$d*975aFriU*pjkApqm~bYF`LH66N!WTtP4<}va#z17P$@KJ|*6KnCFTDM_glz ztRD#UzCoGL`k6iM@GbLx%7H?iP}N?YWLZBRVEhl6eldJ>!(;438Z5bQotb~~wm38J z)-UtW66_ubVe8I&no$0RcY=tt_KzPK<_D{R5Ydb~`mGa_kNjdh__a_9oCAbiPX~Ib zOXW~3+S1UlT z7+Z|ub01Bnmeg^JV2dJWlsl7`^^_cZc0>2y{-P(zs$RgIPw+};=v#&DqPSW=iDj^(A$WjFbA80 zKo^_#ycTvDOi8^3W09GYg3VCiR^XE~+M=DtSI*wOVseGpeeJ@fYg>LcG5KmAYz{$> zchTgk$=q7xW~U`KFl$o>!hs}#B!K1t%?Bc9bmBnNUIl|i&?|~7s^R7GG^yj4Ony2F z+h@xz(~jY~k6{H39Ay=K7U38TSC&xx0jD)BM3q+^5nu7|IP#469yP+!<{))q2^v*WHnnw`6a@`XPN4+w;7HM+Tg$;Z zUAdwffwJJqIJOy9KQ|)>iX6%SR+&J-lp`pTvJZ{G>fIFFvw?&_BA^EB2jzN26oOF@ z3%s}hg;h9|5CMpnl3K zSb$vxt0ZSoALR_1f%tIYxg|vdCdL}Dh^wI7qXLf$zFH)RVN zhTmuk5gh>WTZO{GXA1aA1!{tcQxLrLIgmYe6$H78YRDJ^1;aBT&_jv|dIrDA6cy9} zzvQlx1@LhJ%UPY2AJ8ZW(FD(Wf%rfk@J1W(R|nTsK!HG{|4?}L6t1V?J+I+CuOKyj zDR#(?5sF^G`(`LRkk4G?2#{6-k`70cAZs{;3ji_&8UcC*&)-lEA#b=A0$(q9)`1g%?QJ39jWp=@fnR00KJ08gB=Ls+$W*Ko?M=yn5MOj>dyG5k%gKqCwj( zCj2LL5e4k_nJfxrq- z=5^wqb9v?mQj@V50;W&EDHk}og0~*H z_l5gNphO@)pg5pP@DH^M(u06fh(lg0K&)}p1;2L2YtiI>E<|LRGDiiRVCNbh*ML_G zpkfNHoVE}W987}48y`?gpmdRMRhcUdWZCY6YqC^Df$U^P6oS9!aNiDWTH)Rqu500X z0)A(KI)EG~hENI!@UIJ{lt2K7LTOYBT;(`4IL9`NNI*XSIW3xFh_r#gUr&=uuQ*wW6=yn`8$^Y zbAJO&7vDN{?#BzajDOL*e8c2|`r_{{Ub`n6LRFf;m%YZug)?vPv}aeM7P2KuL}4*ngD{^gFv zJu8F0->hx4CetUcUby7+!QT`YT)o58T4aT&6w;99MoH`bJ^#WN(CED>)7@qseWJw< zTB_gdP$XEVQ|>5gF_~JrTANPoF}`}~$}c-Ff|cIeCI^i#-aWPZz$q=Qbhvn9Q}3%cSOMF9(?q;)@_$k>(N2L9L zycId3eNPO(a_0hLSnb}!^x&)eT}8`{Hf^+Z3zRa(pqr#9&_f8L<8eoH*skgS7X-FLm(zwfMtI!2@Cgk4h1XcdcH*?pkB zNoncx@>4yEit_zGVQ=EXy}1dVP^^QlpmnAmMxv(@0At~bpSw*_8T zl-&ZLIg@j%BN5}C40Z!9p^O}%(QCM?`jaQz+veT+)nbZw*VIwF_cn}ebbi=4S4OlY zt)b8zrqRFEzPIfA=auLA^Te!vlT-LXeCs-QC`;?#6*{TZi@)F$8a?RF8`tB@21fh# zmn2vE8b4aIpVzOb$=!xYJ-yU$@0EmR7&?{eU?Q-yW`@*BDIE`(K*(CpuWYGMj{p-AcN!&}8I51Z__Z~QX5Z@0GJN%;cY>#WEcSMs+t@c1_ zfZO-qZG9=pN?!fVnU|8-a#GG?H2UHCjy2XB)O{jzEsUGRiWGiy-W|%=i8gN8IaiDI zjzcXzj^i}CFXNHL{vUrmG2JO+YAi{M-OjtFr44ITH}2lLab^F?f0WO~{a>H~nqDN- zGrs-9&$Y)})APlna%J?bOU3{3V*ZBZ^Q6X{;8zzRb!z{YYqUwc(q{5&yX>KFFtM%Z zcevKxAWfbIScM{qL{@$J_ZGbMemkASS^Y8m5m>vO6+?(5IMMOZA54_qm7N)#=W2gm84H)_z{e-zfF%h89tk_Z+@;Un}zq2 z^l557-}XNA$1<${oCv8AfA9PfvV){gnaJ<{9Fxzc#=gMsJt80hNdlYMAN}0}{O{X% zyr1sP!6W}z;m)7?NoPJpgQQQTIJWmagO8)4`?;v}AECO+0RxggW#Y2`b4+wT$3)C; zusIphf}~HG)E)jDlZ~Haa!R{aCje4`Bv20W(O=QG@0q-vSkIwH=cd-5O_#izo%NQI z2$2LDE~}hrW@VNBWV6|1LE=;`a~zxfw`rSG4qjd94P^|O!1tIB-0 zoq4AHt>s|S7pT3LNm{Z#Yz|8%%oTC*aV-mQa2$e9YzU&iO)*BU=!~b;fyHtUzt&uU Ol66Y@k0%5OM*RWh$|qU? diff --git a/docs/src/stream.rst b/docs/src/stream.rst index 2ccb59b..0b42c4b 100644 --- a/docs/src/stream.rst +++ b/docs/src/stream.rst @@ -139,6 +139,11 @@ API be made several times until there is no more data to read or :c:func:`uv_read_stop` is called. + .. versionchanged:: 1.38.0 :c:func:`uv_read_start()` now consistently + returns `UV_EALREADY` when called twice, and `UV_EINVAL` when the + stream is closing. With older libuv versions, it returns `UV_EALREADY` + on Windows but not UNIX, and `UV_EINVAL` on UNIX but not Windows. + .. c:function:: int uv_read_stop(uv_stream_t*) Stop reading data from the stream. The :c:type:`uv_read_cb` callback will @@ -146,6 +151,11 @@ API This function is idempotent and may be safely called on a stopped stream. + This function will always succeed; hence, checking its return value is + unnecessary. A non-zero return indicates that finishing releasing resources + may be pending on the next input event on that TTY on Windows, and does not + indicate failure. + .. c:function:: int uv_write(uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb) Write data to stream. Buffers are written in order. Example: @@ -183,8 +193,9 @@ API initialized with `ipc` == 1. .. note:: - `send_handle` must be a TCP socket or pipe, which is a server or a connection (listening - or connected state). Bound sockets or pipes will be assumed to be servers. + `send_handle` must be a TCP, pipe and UDP handle on Unix, or a TCP + handle on Windows, which is a server or a connection (listening or + connected state). Bound sockets or pipes will be assumed to be servers. .. c:function:: int uv_try_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs) @@ -197,6 +208,16 @@ API * < 0: negative error code (``UV_EAGAIN`` is returned if no data can be sent immediately). +.. c:function:: int uv_try_write2(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle) + + Same as :c:func:`uv_try_write` and extended write function for sending + handles over a pipe like c:func:`uv_write2`. + + Try to send a handle is not supported on Windows, + where it returns ``UV_EAGAIN``. + + .. versionadded:: 1.42.0 + .. c:function:: int uv_is_readable(const uv_stream_t* handle) Returns 1 if the stream is readable, 0 otherwise. diff --git a/docs/src/tcp.rst b/docs/src/tcp.rst index 3cc8efa..cccc86b 100644 --- a/docs/src/tcp.rst +++ b/docs/src/tcp.rst @@ -81,10 +81,9 @@ API initialized ``struct sockaddr_in`` or ``struct sockaddr_in6``. When the port is already taken, you can expect to see an ``UV_EADDRINUSE`` - error from either :c:func:`uv_tcp_bind`, :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`. That is, + a successful call to this function does not guarantee that the call + to :c:func:`uv_listen` or :c:func:`uv_tcp_connect` will succeed as well. `flags` can contain ``UV_TCP_IPV6ONLY``, in which case dual-stack support is disabled and only IPv6 is used. @@ -128,3 +127,20 @@ API :c:func:`uv_tcp_close_reset` calls is not allowed. .. versionadded:: 1.32.0 + +.. c:function:: int uv_socketpair(int type, int protocol, uv_os_sock_t socket_vector[2], int flags0, int flags1) + + Create a pair of connected sockets with the specified properties. + The resulting handles can be passed to `uv_tcp_open`, used with `uv_spawn`, + or for any other purpose. + + Valid values for `flags0` and `flags1` are: + + - UV_NONBLOCK_PIPE: Opens the specified socket handle for `OVERLAPPED` + or `FIONBIO`/`O_NONBLOCK` I/O usage. + This is recommended for handles that will be used by libuv, + and not usually recommended otherwise. + + Equivalent to :man:`socketpair(2)` with a domain of AF_UNIX. + + .. versionadded:: 1.41.0 diff --git a/docs/src/timer.rst b/docs/src/timer.rst index e163e28..070fa79 100644 --- a/docs/src/timer.rst +++ b/docs/src/timer.rst @@ -78,4 +78,11 @@ API Get the timer repeat value. +.. c:function:: uint64_t uv_timer_get_due_in(const uv_timer_t* handle) + + Get the timer due value or 0 if it has expired. The time is relative to + :c:func:`uv_now()`. + + .. versionadded:: 1.40.0 + .. seealso:: The :c:type:`uv_handle_t` API functions also apply. diff --git a/docs/src/tty.rst b/docs/src/tty.rst index ad379da..f1acfdc 100644 --- a/docs/src/tty.rst +++ b/docs/src/tty.rst @@ -16,7 +16,7 @@ Data types TTY handle type. -.. c:type:: uv_tty_mode_t +.. c:enum:: uv_tty_mode_t .. versionadded:: 1.2.0 @@ -33,7 +33,8 @@ Data types UV_TTY_MODE_IO } uv_tty_mode_t; -.. c:type:: uv_tty_vtermstate_t +.. c:enum:: uv_tty_vtermstate_t + Console virtual terminal mode type: :: diff --git a/docs/src/udp.rst b/docs/src/udp.rst index 2ca0736..009767d 100644 --- a/docs/src/udp.rst +++ b/docs/src/udp.rst @@ -41,13 +41,30 @@ Data types * (provided they all set the flag) but only the last one to bind will receive * any traffic, in effect "stealing" the port from the previous listener. */ - UV_UDP_REUSEADDR = 4 + UV_UDP_REUSEADDR = 4, /* - * Indicates that the message was received by recvmmsg and that it's not at - * the beginning of the buffer allocated by alloc_cb - so the buffer provided + * Indicates that the message was received by recvmmsg, so the buffer provided * must not be freed by the recv_cb callback. */ - UV_UDP_MMSG_CHUNK = 8 + UV_UDP_MMSG_CHUNK = 8, + /* + * Indicates that the buffer provided has been fully utilized by recvmmsg and + * that it should now be freed by the recv_cb callback. When this flag is set + * in uv_udp_recv_cb, nread will always be 0 and addr will always be NULL. + */ + UV_UDP_MMSG_FREE = 16, + /* + * 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 + * 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. + */ + UV_UDP_RECVMMSG = 256 }; .. c:type:: void (*uv_udp_send_cb)(uv_udp_send_t* req, int status) @@ -64,7 +81,8 @@ Data types * `nread`: Number of bytes that have been received. 0 if there is no more data to read. Note that 0 may also mean that an empty datagram was received (in this case `addr` is not NULL). < 0 if - a transmission error was detected. + a transmission error was detected; if using :man:`recvmmsg(2)` no more + chunks will be received and the buffer can be freed safely. * `buf`: :c:type:`uv_buf_t` with the received data. * `addr`: ``struct sockaddr*`` containing the address of the sender. Can be NULL. Valid for the duration of the callback only. @@ -72,16 +90,23 @@ Data types The callee is responsible for freeing the buffer, libuv does not reuse it. The buffer may be a null buffer (where `buf->base` == NULL and `buf->len` == 0) - on error. Don't free the buffer when the UV_UDP_MMSG_CHUNK flag is set. - The final callback receives the whole buffer (containing the first chunk) - with the UV_UDP_MMSG_CHUNK flag cleared. + on error. + + When using :man:`recvmmsg(2)`, chunks will have the `UV_UDP_MMSG_CHUNK` flag set, + those must not be freed. If no errors occur, there will be a final callback with + `nread` set to 0, `addr` set to NULL and the buffer pointing at the initially + allocated data with the `UV_UDP_MMSG_CHUNK` flag cleared and the `UV_UDP_MMSG_FREE` + flag set. If a UDP socket error occurs, `nread` will be < 0. In either scenario, + the callee can now safely free the provided buffer. + + .. versionchanged:: 1.40.0 added the `UV_UDP_MMSG_FREE` flag. .. note:: The receive callback will be called with `nread` == 0 and `addr` == NULL when there is nothing to read, and with `nread` == 0 and `addr` != NULL when an empty UDP packet is received. -.. c:type:: uv_membership +.. c:enum:: uv_membership Membership type for a multicast address. @@ -122,12 +147,17 @@ API .. c:function:: int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) - Initialize the handle with the specified flags. At the moment the lower 8 bits - of the `flags` parameter are used as the socket domain. A socket will be created - for the given domain. If the specified domain is ``AF_UNSPEC`` no socket is created, - just like :c:func:`uv_udp_init`. + Initialize the handle with the specified flags. The lower 8 bits of the `flags` + parameter are used as the socket domain. A socket will be created for the given domain. + If the specified domain is ``AF_UNSPEC`` no socket is created, just like :c:func:`uv_udp_init`. + + The remaining bits can be used to set one of these flags: + + * `UV_UDP_RECVMMSG`: if set, and the platform supports it, :man:`recvmmsg(2)` will + be used. .. versionadded:: 1.7.0 + .. versionchanged:: 1.37.0 added the `UV_UDP_RECVMMSG` flag. .. c:function:: int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) @@ -156,7 +186,8 @@ API with the address and port to bind to. :param flags: Indicate how the socket will be bound, - ``UV_UDP_IPV6ONLY`` and ``UV_UDP_REUSEADDR`` are supported. + ``UV_UDP_IPV6ONLY``, ``UV_UDP_REUSEADDR``, and ``UV_UDP_RECVERR`` + are supported. :returns: 0 on success, or an error code < 0 on failure. @@ -373,6 +404,28 @@ API :returns: 0 on success, or an error code < 0 on failure. + .. note:: + When using :man:`recvmmsg(2)`, the number of messages received at a time is limited + by the number of max size dgrams that will fit into the buffer allocated in `alloc_cb`, and + `suggested_size` in `alloc_cb` for udp_recv is always set to the size of 1 max size dgram. + + .. versionchanged:: 1.35.0 added support for :man:`recvmmsg(2)` on supported platforms). + The use of this feature requires a buffer larger than + 2 * 64KB to be passed to `alloc_cb`. + .. versionchanged:: 1.37.0 :man:`recvmmsg(2)` support is no longer enabled implicitly, + it must be explicitly requested by passing the `UV_UDP_RECVMMSG` flag to + :c:func:`uv_udp_init_ex`. + .. versionchanged:: 1.39.0 :c:func:`uv_udp_using_recvmmsg` can be used in `alloc_cb` to + determine if a buffer sized for use with :man:`recvmmsg(2)` should be + allocated for the current handle/platform. + +.. c:function:: int uv_udp_using_recvmmsg(uv_udp_t* handle) + + Returns 1 if the UDP handle was created with the `UV_UDP_RECVMMSG` flag + and the platform supports :man:`recvmmsg(2)`, 0 otherwise. + + .. versionadded:: 1.39.0 + .. c:function:: int uv_udp_recv_stop(uv_udp_t* handle) Stop listening for incoming datagrams. diff --git a/gyp_uv.py b/gyp_uv.py deleted file mode 100644 index c2add5c..0000000 --- a/gyp_uv.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python - -import os -import platform -import sys - -try: - import multiprocessing.synchronize - gyp_parallel_support = True -except ImportError: - gyp_parallel_support = False - - -CC = os.environ.get('CC', 'cc') -script_dir = os.path.dirname(__file__) -uv_root = os.path.normpath(script_dir) -output_dir = os.path.join(os.path.abspath(uv_root), 'out') - -sys.path.insert(0, os.path.join(uv_root, 'build', 'gyp', 'pylib')) -try: - import gyp -except ImportError: - print('You need to install gyp in build/gyp first. See the README.') - sys.exit(42) - - -def host_arch(): - machine = platform.machine() - if machine == 'i386': return 'ia32' - if machine == 'AMD64': return 'x64' - if machine == 'x86_64': return 'x64' - if machine.startswith('arm'): return 'arm' - if machine.startswith('mips'): return 'mips' - return machine # Return as-is and hope for the best. - - -def run_gyp(args): - rc = gyp.main(args) - if rc != 0: - print('Error running GYP') - sys.exit(rc) - - -if __name__ == '__main__': - args = sys.argv[1:] - args.extend('-I common.gypi test/test.gyp'.split(' ')) - args.append('--depth=' + uv_root) - - # There's a bug with windows which doesn't allow this feature. - if sys.platform != 'win32': - if '-f' not in args: - args.extend('-f make'.split()) - if 'eclipse' not in args and 'ninja' not in args: - args.extend(['-Goutput_dir=' + output_dir]) - args.extend(['--generator-output', output_dir]) - - if not any(a.startswith('-Dhost_arch=') for a in args): - args.append('-Dhost_arch=%s' % host_arch()) - - if not any(a.startswith('-Dtarget_arch=') for a in args): - args.append('-Dtarget_arch=%s' % host_arch()) - - if not any(a.startswith('-Duv_library=') for a in args): - args.append('-Duv_library=static_library') - - # Some platforms (OpenBSD for example) don't have multiprocessing.synchronize - # so gyp must be run with --no-parallel - if not gyp_parallel_support: - args.append('--no-parallel') - - gyp_args = list(args) - print(gyp_args) - run_gyp(gyp_args) diff --git a/include/uv.h b/include/uv.h index defc0ac..ee1c94c 100644 --- a/include/uv.h +++ b/include/uv.h @@ -45,6 +45,8 @@ extern "C" { # endif #elif __GNUC__ >= 4 # define UV_EXTERN __attribute__((visibility("default"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) /* Sun Studio >= 8 */ +# define UV_EXTERN __global #else # define UV_EXTERN /* nothing */ #endif @@ -126,6 +128,7 @@ extern "C" { XX(ENOTEMPTY, "directory not empty") \ XX(ENOTSOCK, "socket operation on non-socket") \ XX(ENOTSUP, "operation not supported on socket") \ + XX(EOVERFLOW, "value too large for defined data type") \ XX(EPERM, "operation not permitted") \ XX(EPIPE, "broken pipe") \ XX(EPROTO, "protocol error") \ @@ -148,6 +151,7 @@ extern "C" { XX(ENOTTY, "inappropriate ioctl for device") \ XX(EFTYPE, "inappropriate file type or format") \ XX(EILSEQ, "illegal byte sequence") \ + XX(ESOCKTNOSUPPORT, "socket type not supported") \ #define UV_HANDLE_TYPE_MAP(XX) \ XX(ASYNC, async) \ @@ -247,7 +251,8 @@ typedef struct uv_utsname_s uv_utsname_t; typedef struct uv_statfs_s uv_statfs_t; typedef enum { - UV_LOOP_BLOCK_SIGNAL + UV_LOOP_BLOCK_SIGNAL = 0, + UV_METRICS_IDLE_TIME } uv_loop_option; typedef enum { @@ -265,6 +270,8 @@ typedef void* (*uv_realloc_func)(void* ptr, size_t size); typedef void* (*uv_calloc_func)(size_t count, size_t size); typedef void (*uv_free_func)(void* ptr); +UV_EXTERN void uv_library_shutdown(void); + UV_EXTERN int uv_replace_allocator(uv_malloc_func malloc_func, uv_realloc_func realloc_func, uv_calloc_func calloc_func, @@ -472,6 +479,12 @@ UV_EXTERN int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd); UV_EXTERN uv_buf_t uv_buf_init(char* base, unsigned int len); +UV_EXTERN int uv_pipe(uv_file fds[2], int read_flags, int write_flags); +UV_EXTERN int uv_socketpair(int type, + int protocol, + uv_os_sock_t socket_vector[2], + int flags0, + int flags1); #define UV_STREAM_FIELDS \ /* number of bytes queued for writing */ \ @@ -517,6 +530,10 @@ UV_EXTERN int uv_write2(uv_write_t* req, UV_EXTERN int uv_try_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs); +UV_EXTERN int uv_try_write2(uv_stream_t* handle, + const uv_buf_t bufs[], + unsigned int nbufs, + uv_stream_t* send_handle); /* uv_write_t is a subclass of uv_req_t. */ struct uv_write_s { @@ -607,11 +624,28 @@ enum uv_udp_flags { */ UV_UDP_REUSEADDR = 4, /* - * Indicates that the message was received by recvmmsg and that it's not at - * the beginning of the buffer allocated by alloc_cb - so the buffer provided + * Indicates that the message was received by recvmmsg, so the buffer provided * must not be freed by the recv_cb callback. */ - UV_UDP_MMSG_CHUNK = 8 + UV_UDP_MMSG_CHUNK = 8, + /* + * Indicates that the buffer provided has been fully utilized by recvmmsg and + * that it should now be freed by the recv_cb callback. When this flag is set + * in uv_udp_recv_cb, nread will always be 0 and addr will always be NULL. + */ + UV_UDP_MMSG_FREE = 16, + /* + * 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 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. + */ + UV_UDP_RECVMMSG = 256 }; typedef void (*uv_udp_send_cb)(uv_udp_send_t* req, int status); @@ -687,6 +721,7 @@ UV_EXTERN int uv_udp_try_send(uv_udp_t* handle, UV_EXTERN int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb); +UV_EXTERN int uv_udp_using_recvmmsg(const uv_udp_t* handle); UV_EXTERN int uv_udp_recv_stop(uv_udp_t* handle); UV_EXTERN size_t uv_udp_get_send_queue_size(const uv_udp_t* handle); UV_EXTERN size_t uv_udp_get_send_queue_count(const uv_udp_t* handle); @@ -857,6 +892,7 @@ UV_EXTERN int uv_timer_stop(uv_timer_t* handle); UV_EXTERN int uv_timer_again(uv_timer_t* handle); UV_EXTERN void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat); UV_EXTERN uint64_t uv_timer_get_repeat(const uv_timer_t* handle); +UV_EXTERN uint64_t uv_timer_get_due_in(const uv_timer_t* handle); /* @@ -918,10 +954,13 @@ typedef enum { UV_WRITABLE_PIPE = 0x20, /* - * Open the child pipe handle in overlapped mode on Windows. - * On Unix it is silently ignored. + * 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, + * if the child is not designed to handle to encounter this mode, + * but can also be significantly more efficient. */ - UV_OVERLAPPED_PIPE = 0x40 + UV_NONBLOCK_PIPE = 0x40, + UV_OVERLAPPED_PIPE = 0x40 /* old name, for compatibility */ } uv_stdio_flags; typedef struct uv_stdio_container_s { @@ -1065,11 +1104,11 @@ UV_EXTERN int uv_cancel(uv_req_t* req); struct uv_cpu_times_s { - uint64_t user; - uint64_t nice; - uint64_t sys; - uint64_t idle; - uint64_t irq; + uint64_t user; /* milliseconds */ + uint64_t nice; /* milliseconds */ + uint64_t sys; /* milliseconds */ + uint64_t idle; /* milliseconds */ + uint64_t irq; /* milliseconds */ }; struct uv_cpu_info_s { @@ -1094,8 +1133,8 @@ 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; }; @@ -1183,16 +1222,27 @@ UV_EXTERN void uv_os_free_passwd(uv_passwd_t* pwd); UV_EXTERN uv_pid_t uv_os_getpid(void); UV_EXTERN uv_pid_t uv_os_getppid(void); -#define UV_PRIORITY_LOW 19 -#define UV_PRIORITY_BELOW_NORMAL 10 -#define UV_PRIORITY_NORMAL 0 -#define UV_PRIORITY_ABOVE_NORMAL -7 -#define UV_PRIORITY_HIGH -14 -#define UV_PRIORITY_HIGHEST -20 +#if defined(__PASE__) +/* On IBM i PASE, the highest process priority is -10 */ +# define UV_PRIORITY_LOW 39 /* RUNPTY(99) */ +# define UV_PRIORITY_BELOW_NORMAL 15 /* RUNPTY(50) */ +# define UV_PRIORITY_NORMAL 0 /* RUNPTY(20) */ +# define UV_PRIORITY_ABOVE_NORMAL -4 /* RUNTY(12) */ +# define UV_PRIORITY_HIGH -7 /* RUNPTY(6) */ +# define UV_PRIORITY_HIGHEST -10 /* RUNPTY(1) */ +#else +# define UV_PRIORITY_LOW 19 +# define UV_PRIORITY_BELOW_NORMAL 10 +# define UV_PRIORITY_NORMAL 0 +# define UV_PRIORITY_ABOVE_NORMAL -7 +# define UV_PRIORITY_HIGH -14 +# define UV_PRIORITY_HIGHEST -20 +#endif UV_EXTERN int uv_os_getpriority(uv_pid_t pid, int* priority); UV_EXTERN int uv_os_setpriority(uv_pid_t pid, int priority); +UV_EXTERN 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); @@ -1227,6 +1277,7 @@ UV_EXTERN int uv_os_gethostname(char* buffer, size_t* size); UV_EXTERN int uv_os_uname(uv_utsname_t* buffer); +UV_EXTERN uint64_t uv_metrics_idle_time(uv_loop_t* loop); typedef enum { UV_FS_UNKNOWN = -1, @@ -1265,7 +1316,8 @@ typedef enum { UV_FS_READDIR, UV_FS_CLOSEDIR, UV_FS_STATFS, - UV_FS_MKSTEMP + UV_FS_MKSTEMP, + UV_FS_LUTIME } uv_fs_type; struct uv_dir_s { @@ -1290,6 +1342,7 @@ struct uv_fs_s { UV_EXTERN uv_fs_type uv_fs_get_type(const uv_fs_t*); UV_EXTERN ssize_t uv_fs_get_result(const uv_fs_t*); +UV_EXTERN int uv_fs_get_system_error(const uv_fs_t*); UV_EXTERN void* uv_fs_get_ptr(const uv_fs_t*); UV_EXTERN const char* uv_fs_get_path(const uv_fs_t*); UV_EXTERN uv_stat_t* uv_fs_get_statbuf(uv_fs_t*); @@ -1438,6 +1491,12 @@ UV_EXTERN int uv_fs_futime(uv_loop_t* loop, double atime, double mtime, uv_fs_cb cb); +UV_EXTERN int uv_fs_lutime(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + double atime, + double mtime, + uv_fs_cb cb); UV_EXTERN int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, @@ -1602,6 +1661,7 @@ UV_EXTERN int uv_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr); UV_EXTERN int uv_ip4_name(const struct sockaddr_in* src, char* dst, size_t size); UV_EXTERN int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size); +UV_EXTERN int uv_ip_name(const struct sockaddr* src, char* dst, size_t size); UV_EXTERN int uv_inet_ntop(int af, const void* src, char* dst, size_t size); UV_EXTERN int uv_inet_pton(int af, const char* src, void* dst); @@ -1750,9 +1810,11 @@ struct uv_loop_s { unsigned int active_handles; void* handle_queue[2]; union { - void* unused[2]; + void* unused; unsigned int count; } active_reqs; + /* Internal storage for future extensions. */ + void* internal_fields; /* Internal flag to signal loop stop. */ unsigned int stop_flag; UV_LOOP_PRIVATE_FIELDS diff --git a/include/uv/android-ifaddrs.h b/include/uv/android-ifaddrs.h deleted file mode 100644 index 9cd19fe..0000000 --- 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/errno.h b/include/uv/errno.h index 165fd11..71906b3 100644 --- a/include/uv/errno.h +++ b/include/uv/errno.h @@ -317,7 +317,7 @@ #if defined(EPROTO) && !defined(_WIN32) # define UV__EPROTO UV__ERR(EPROTO) #else -# define UV__EPROTO UV__ERR(4046) +# define UV__EPROTO (-4046) #endif #if defined(EPROTONOSUPPORT) && !defined(_WIN32) @@ -445,4 +445,16 @@ # define UV__EILSEQ (-4027) #endif +#if defined(EOVERFLOW) && !defined(_WIN32) +# define UV__EOVERFLOW UV__ERR(EOVERFLOW) +#else +# define UV__EOVERFLOW (-4026) +#endif + +#if defined(ESOCKTNOSUPPORT) && !defined(_WIN32) +# define UV__ESOCKTNOSUPPORT UV__ERR(ESOCKTNOSUPPORT) +#else +# define UV__ESOCKTNOSUPPORT (-4025) +#endif + #endif /* UV_ERRNO_H_ */ diff --git a/include/uv/tree.h b/include/uv/tree.h index f936416..2b28835 100644 --- a/include/uv/tree.h +++ b/include/uv/tree.h @@ -251,7 +251,7 @@ void name##_SPLAY_MINMAX(struct name *head, int __comp) \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \ __left = __right = &__node; \ \ - while (1) { \ + for (;;) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ diff --git a/include/uv/unix.h b/include/uv/unix.h index 3a13163..ea37d78 100644 --- a/include/uv/unix.h +++ b/include/uv/unix.h @@ -65,10 +65,10 @@ # include "uv/bsd.h" #elif defined(__CYGWIN__) || \ defined(__MSYS__) || \ + defined(__HAIKU__) || \ + defined(__QNX__) || \ defined(__GNU__) # include "uv/posix.h" -#elif defined(__HAIKU__) -# include "uv/posix.h" #endif #ifndef NI_MAXHOST diff --git a/include/uv/version.h b/include/uv/version.h index 78a2115..56ac1bf 100644 --- a/include/uv/version.h +++ b/include/uv/version.h @@ -26,13 +26,13 @@ * Versions with the same major number are ABI stable. API is allowed to * evolve between minor releases, but only in a backwards compatible way. * Make sure you update the -soname directives in configure.ac - * and uv.gyp whenever you bump UV_VERSION_MAJOR or UV_VERSION_MINOR (but + * whenever you bump UV_VERSION_MAJOR or UV_VERSION_MINOR (but * not UV_VERSION_PATCH.) */ #define UV_VERSION_MAJOR 1 -#define UV_VERSION_MINOR 35 -#define UV_VERSION_PATCH 0 +#define UV_VERSION_MINOR 44 +#define UV_VERSION_PATCH 1 #define UV_VERSION_IS_RELEASE 1 #define UV_VERSION_SUFFIX "" diff --git a/include/uv/win.h b/include/uv/win.h index f5f1d3a..62be4b0 100644 --- a/include/uv/win.h +++ b/include/uv/win.h @@ -45,7 +45,14 @@ typedef struct pollfd { #endif #include +// 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 #include +#undef _TCP_INITIAL_RTO_PARAMETERS +#undef TCP_INITIAL_RTO_PARAMETERS +#undef PTCP_INITIAL_RTO_PARAMETERS #include #include @@ -216,7 +223,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 /** @@ -256,21 +263,14 @@ typedef union { } unused_; /* TODO: retained for ABI compatibility; remove me in v2.x. */ } uv_cond_t; -typedef union { - struct { - unsigned int num_readers_; - CRITICAL_SECTION num_readers_lock_; - HANDLE write_semaphore_; - } state_; - /* TODO: remove me in v2.x. */ - struct { - SRWLOCK unused_; - } unused1_; - /* TODO: remove me in v2.x. */ - struct { - uv_mutex_t unused1_; - uv_mutex_t unused2_; - } unused2_; +typedef struct { + SRWLOCK read_write_lock_; + /* TODO: retained for ABI compatibility; remove me in v2.x */ +#ifdef _WIN64 + unsigned char padding_[72]; +#else + unsigned char padding_[44]; +#endif } uv_rwlock_t; typedef struct { diff --git a/libuv-static.pc.in b/libuv-static.pc.in new file mode 100644 index 0000000..ea62548 --- /dev/null +++ b/libuv-static.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=${prefix} +libdir=@libdir@ +includedir=@includedir@ + +Name: libuv-static +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@ +Cflags: -I${includedir} diff --git a/m4/.gitignore b/m4/.gitignore new file mode 100644 index 0000000..bb91e50 --- /dev/null +++ b/m4/.gitignore @@ -0,0 +1,5 @@ +# Ignore libtoolize-generated files. +*.m4 +!as_case.m4 +!ax_pthread.m4 +!libuv-check-flags.m4 diff --git a/m4/as_case.m4 b/m4/as_case.m4 new file mode 100644 index 0000000..c7ae0f0 --- /dev/null +++ b/m4/as_case.m4 @@ -0,0 +1,21 @@ +# AS_CASE(WORD, [PATTERN1], [IF-MATCHED1]...[DEFAULT]) +# ---------------------------------------------------- +# Expand into +# | case WORD in +# | PATTERN1) IF-MATCHED1 ;; +# | ... +# | *) DEFAULT ;; +# | esac +m4_define([_AS_CASE], +[m4_if([$#], 0, [m4_fatal([$0: too few arguments: $#])], + [$#], 1, [ *) $1 ;;], + [$#], 2, [ $1) m4_default([$2], [:]) ;;], + [ $1) m4_default([$2], [:]) ;; +$0(m4_shiftn(2, $@))])dnl +]) +m4_defun([AS_CASE], +[m4_ifval([$2$3], +[case $1 in +_AS_CASE(m4_shift($@)) +esac])]) + diff --git a/m4/ax_pthread.m4 b/m4/ax_pthread.m4 new file mode 100644 index 0000000..5fbf9fe --- /dev/null +++ b/m4/ax_pthread.m4 @@ -0,0 +1,485 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_pthread.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +# +# DESCRIPTION +# +# This macro figures out how to build C programs using POSIX threads. It +# sets the PTHREAD_LIBS output variable to the threads library and linker +# flags, and the PTHREAD_CFLAGS output variable to any special C compiler +# flags that are needed. (The user can also force certain compiler +# flags/libs to be tested by setting these environment variables.) +# +# Also sets PTHREAD_CC to any special C compiler that is needed for +# multi-threaded programs (defaults to the value of CC otherwise). (This +# is necessary on AIX to use the special cc_r compiler alias.) +# +# NOTE: You are assumed to not only compile your program with these flags, +# but also to link with them as well. For example, you might link with +# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# +# If you are only building threaded programs, you may wish to use these +# variables in your default LIBS, CFLAGS, and CC: +# +# LIBS="$PTHREAD_LIBS $LIBS" +# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CC="$PTHREAD_CC" +# +# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant +# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to +# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# +# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the +# PTHREAD_PRIO_INHERIT symbol is defined when compiling with +# PTHREAD_CFLAGS. +# +# ACTION-IF-FOUND is a list of shell commands to run if a threads library +# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it +# is not found. If ACTION-IF-FOUND is not specified, the default action +# will define HAVE_PTHREAD. +# +# Please let the authors know if this macro fails on any platform, or if +# you have any other suggestions or comments. This macro was based on work +# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help +# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by +# Alejandro Forero Cuervo to the autoconf macro repository. We are also +# grateful for the helpful feedback of numerous users. +# +# Updated for Autoconf 2.68 by Daniel Richard G. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2011 Daniel Richard G. +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 24 + +AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) +AC_DEFUN([AX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_REQUIRE([AC_PROG_CC]) +AC_REQUIRE([AC_PROG_SED]) +AC_LANG_PUSH([C]) +ax_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on Tru64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then + ax_pthread_save_CC="$CC" + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) + AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) + AC_MSG_RESULT([$ax_pthread_ok]) + if test "x$ax_pthread_ok" = "xno"; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + CC="$ax_pthread_save_CC" + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 +# (Note: HP C rejects this with "bad form for `-t' option") +# -pthreads: Solaris/gcc (Note: HP C also rejects) +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads and +# -D_REENTRANT too), HP C (must be checked before -lpthread, which +# is present but should not be used directly; and before -mthreads, +# because the compiler interprets this as "-mt" + "-hreads") +# -mthreads: Mingw32/gcc, Lynx/gcc +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case $host_os in + + freebsd*) + + # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) + # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) + + ax_pthread_flags="-kthread lthread $ax_pthread_flags" + ;; + + hpux*) + + # From the cc(1) man page: "[-mt] Sets various -D flags to enable + # multi-threading and also sets -lpthread." + + ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" + ;; + + openedition*) + + # IBM z/OS requires a feature-test macro to be defined in order to + # enable POSIX threads at all, so give the user a hint if this is + # not set. (We don't define these ourselves, as they can affect + # other portions of the system API in unpredictable ways.) + + AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], + [ +# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) + AX_PTHREAD_ZOS_MISSING +# endif + ], + [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) + ;; + + solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (N.B.: The stubs are missing + # pthread_cleanup_push, or rather a function called by this macro, + # so we could check for that, but who knows whether they'll stub + # that too in a future libc.) So we'll check first for the + # standard Solaris way of linking pthreads (-mt -lpthread). + + ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags" + ;; +esac + +# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) + +AS_IF([test "x$GCC" = "xyes"], + [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"]) + +# The presence of a feature test macro requesting re-entrant function +# definitions is, on some systems, a strong hint that pthreads support is +# correctly enabled + +case $host_os in + darwin* | hpux* | linux* | osf* | solaris*) + ax_pthread_check_macro="_REENTRANT" + ;; + + aix*) + ax_pthread_check_macro="_THREAD_SAFE" + ;; + + *) + ax_pthread_check_macro="--" + ;; +esac +AS_IF([test "x$ax_pthread_check_macro" = "x--"], + [ax_pthread_check_cond=0], + [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) + +# Are we compiling with Clang? + +AC_CACHE_CHECK([whether $CC is Clang], + [ax_cv_PTHREAD_CLANG], + [ax_cv_PTHREAD_CLANG=no + # Note that Autoconf sets GCC=yes for Clang as well as GCC + if test "x$GCC" = "xyes"; then + AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], + [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ +# if defined(__clang__) && defined(__llvm__) + AX_PTHREAD_CC_IS_CLANG +# endif + ], + [ax_cv_PTHREAD_CLANG=yes]) + fi + ]) +ax_pthread_clang="$ax_cv_PTHREAD_CLANG" + +ax_pthread_clang_warning=no + +# Clang needs special handling, because older versions handle the -pthread +# option in a rather... idiosyncratic way + +if test "x$ax_pthread_clang" = "xyes"; then + + # Clang takes -pthread; it has never supported any other flag + + # (Note 1: This will need to be revisited if a system that Clang + # supports has POSIX threads in a separate library. This tends not + # to be the way of modern systems, but it's conceivable.) + + # (Note 2: On some systems, notably Darwin, -pthread is not needed + # to get POSIX threads support; the API is always present and + # active. We could reasonably leave PTHREAD_CFLAGS empty. But + # -pthread does define _REENTRANT, and while the Darwin headers + # ignore this macro, third-party headers might not.) + + PTHREAD_CFLAGS="-pthread" + PTHREAD_LIBS= + + ax_pthread_ok=yes + + # However, older versions of Clang make a point of warning the user + # that, in an invocation where only linking and no compilation is + # taking place, the -pthread option has no effect ("argument unused + # during compilation"). They expect -pthread to be passed in only + # when source code is being compiled. + # + # Problem is, this is at odds with the way Automake and most other + # C build frameworks function, which is that the same flags used in + # compilation (CFLAGS) are also used in linking. Many systems + # supported by AX_PTHREAD require exactly this for POSIX threads + # support, and in fact it is often not straightforward to specify a + # flag that is used only in the compilation phase and not in + # linking. Such a scenario is extremely rare in practice. + # + # Even though use of the -pthread flag in linking would only print + # a warning, this can be a nuisance for well-run software projects + # that build with -Werror. So if the active version of Clang has + # this misfeature, we search for an option to squash it. + + AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], + [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], + [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown + # Create an alternate version of $ac_link that compiles and + # links in two steps (.c -> .o, .o -> exe) instead of one + # (.c -> exe), because the warning occurs only in the second + # step + ax_pthread_save_ac_link="$ac_link" + ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' + ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"` + ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" + ax_pthread_save_CFLAGS="$CFLAGS" + for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do + AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) + CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" + ac_link="$ax_pthread_save_ac_link" + AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], + [ac_link="$ax_pthread_2step_ac_link" + AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], + [break]) + ]) + done + ac_link="$ax_pthread_save_ac_link" + CFLAGS="$ax_pthread_save_CFLAGS" + AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) + ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" + ]) + + case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in + no | unknown) ;; + *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; + esac + +fi # $ax_pthread_clang = yes + +if test "x$ax_pthread_ok" = "xno"; then +for ax_pthread_try_flag in $ax_pthread_flags; do + + case $ax_pthread_try_flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -mt,pthread) + AC_MSG_CHECKING([whether pthreads work with -mt -lpthread]) + PTHREAD_CFLAGS="-mt" + PTHREAD_LIBS="-lpthread" + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) + PTHREAD_CFLAGS="$ax_pthread_try_flag" + ;; + + pthread-config) + AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) + AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) + PTHREAD_LIBS="-l$ax_pthread_try_flag" + ;; + esac + + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include +# if $ax_pthread_check_cond +# error "$ax_pthread_check_macro must be defined" +# endif + static void routine(void *a) { a = 0; } + static void *start_routine(void *a) { return a; }], + [pthread_t th; pthread_attr_t attr; + pthread_create(&th, 0, start_routine, 0); + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_cleanup_pop(0) /* ; */])], + [ax_pthread_ok=yes], + []) + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + AC_MSG_RESULT([$ax_pthread_ok]) + AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$ax_pthread_ok" = "xyes"; then + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_CACHE_CHECK([for joinable pthread attribute], + [ax_cv_PTHREAD_JOINABLE_ATTR], + [ax_cv_PTHREAD_JOINABLE_ATTR=unknown + for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], + [int attr = $ax_pthread_attr; return attr /* ; */])], + [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], + []) + done + ]) + AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ + test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ + test "x$ax_pthread_joinable_attr_defined" != "xyes"], + [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], + [$ax_cv_PTHREAD_JOINABLE_ATTR], + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + ax_pthread_joinable_attr_defined=yes + ]) + + AC_CACHE_CHECK([whether more special flags are required for pthreads], + [ax_cv_PTHREAD_SPECIAL_FLAGS], + [ax_cv_PTHREAD_SPECIAL_FLAGS=no + case $host_os in + solaris*) + ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" + ;; + esac + ]) + AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ + test "x$ax_pthread_special_flags_added" != "xyes"], + [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" + ax_pthread_special_flags_added=yes]) + + AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], + [ax_cv_PTHREAD_PRIO_INHERIT], + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[int i = PTHREAD_PRIO_INHERIT;]])], + [ax_cv_PTHREAD_PRIO_INHERIT=yes], + [ax_cv_PTHREAD_PRIO_INHERIT=no]) + ]) + AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ + test "x$ax_pthread_prio_inherit_defined" != "xyes"], + [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) + ax_pthread_prio_inherit_defined=yes + ]) + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + # More AIX lossage: compile with *_r variant + if test "x$GCC" != "xyes"; then + case $host_os in + aix*) + AS_CASE(["x/$CC"], + [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], + [#handle absolute path differently from PATH based program lookup + AS_CASE(["x$CC"], + [x/*], + [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], + [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) + ;; + esac + fi +fi + +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" + +AC_SUBST([PTHREAD_LIBS]) +AC_SUBST([PTHREAD_CFLAGS]) +AC_SUBST([PTHREAD_CC]) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test "x$ax_pthread_ok" = "xyes"; then + ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) + : +else + ax_pthread_ok=no + $2 +fi +AC_LANG_POP +])dnl AX_PTHREAD diff --git a/m4/libuv-check-flags.m4 b/m4/libuv-check-flags.m4 new file mode 100644 index 0000000..46b9dd8 --- /dev/null +++ b/m4/libuv-check-flags.m4 @@ -0,0 +1,320 @@ +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 +dnl the Free Software Foundation; either version 3, or (at your option) +dnl any later version. +dnl +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +dnl 02110-1301, USA. +dnl +dnl As a special exception, the copyright owners of the +dnl macro gives unlimited permission to copy, distribute and modify the +dnl configure scripts that are the output of Autoconf when processing the +dnl Macro. You need not follow the terms of the GNU General Public +dnl License when using or distributing such scripts, even though portions +dnl of the text of the Macro appear in them. The GNU General Public +dnl License (GPL) does govern all other use of the material that +dnl constitutes the Autoconf Macro. +dnl +dnl This special exception to the GPL applies to versions of the +dnl Autoconf Macro released by this project. When you make and +dnl distribute a modified version of the Autoconf Macro, you may extend +dnl this special exception to the GPL to apply to your modified version as +dnl well. + +dnl Check if the flag is supported by compiler +dnl CC_CHECK_CFLAGS_SILENT([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) + +AC_DEFUN([CC_CHECK_CFLAGS_SILENT], [ + AC_CACHE_VAL(AS_TR_SH([cc_cv_cflags_$1]), + [ac_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $1" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([int a;])], + [eval "AS_TR_SH([cc_cv_cflags_$1])='yes'"], + [eval "AS_TR_SH([cc_cv_cflags_$1])='no'"]) + CFLAGS="$ac_save_CFLAGS" + ]) + + AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes], + [$2], [$3]) +]) + +dnl Check if the flag is supported by compiler (cacheable) +dnl CC_CHECK_CFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) + +AC_DEFUN([CC_CHECK_CFLAGS], [ + AC_CACHE_CHECK([if $CC supports $1 flag], + AS_TR_SH([cc_cv_cflags_$1]), + CC_CHECK_CFLAGS_SILENT([$1]) dnl Don't execute actions here! + ) + + AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes], + [$2], [$3]) +]) + +dnl CC_CHECK_CFLAG_APPEND(FLAG, [action-if-found], [action-if-not-found]) +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]), + CC_CHECK_CFLAGS_SILENT([$1]) dnl Don't execute actions here! + ) + + AS_IF([eval test x$]AS_TR_SH([cc_cv_cflags_$1])[ = xyes], + [AM_CFLAGS="$AM_CFLAGS $1"; DEBUG_CFLAGS="$DEBUG_CFLAGS $1"; $2], [$3]) +]) + +dnl CC_CHECK_CFLAGS_APPEND([FLAG1 FLAG2], [action-if-found], [action-if-not]) +AC_DEFUN([CC_CHECK_CFLAGS_APPEND], [ + for flag in $1; do + CC_CHECK_CFLAG_APPEND($flag, [$2], [$3]) + done +]) + +dnl Check if the flag is supported by linker (cacheable) +dnl CC_CHECK_LDFLAGS([FLAG], [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND]) + +AC_DEFUN([CC_CHECK_LDFLAGS], [ + AC_CACHE_CHECK([if $CC supports $1 flag], + AS_TR_SH([cc_cv_ldflags_$1]), + [ac_save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $1" + AC_LANG_PUSH([C]) + AC_LINK_IFELSE([AC_LANG_SOURCE([int main() { return 1; }])], + [eval "AS_TR_SH([cc_cv_ldflags_$1])='yes'"], + [eval "AS_TR_SH([cc_cv_ldflags_$1])="]) + AC_LANG_POP([C]) + LDFLAGS="$ac_save_LDFLAGS" + ]) + + AS_IF([eval test x$]AS_TR_SH([cc_cv_ldflags_$1])[ = xyes], + [$2], [$3]) +]) + +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], [ + dnl We check $host for which systems to enable this for. + AC_REQUIRE([AC_CANONICAL_HOST]) + + case $host in + dnl FreeBSD (et al.) does not complete linking for shared objects when pthreads + dnl are requested, as different implementations are present; to avoid problems + dnl use -Wl,-z,defs only for those platform not behaving this way. + *-freebsd* | *-openbsd*) ;; + *) + dnl First of all check for the --no-undefined variant of GNU ld. This allows + dnl for a much more readable commandline, so that people can understand what + dnl it does without going to look for what the heck -z defs does. + for possible_flags in "-Wl,--no-undefined" "-Wl,-z,defs"; do + CC_CHECK_LDFLAGS([$possible_flags], [LDFLAGS_NOUNDEFINED="$possible_flags"]) + break + done + ;; + esac + + AC_SUBST([LDFLAGS_NOUNDEFINED]) +]) + +dnl Check for a -Werror flag or equivalent. -Werror is the GCC +dnl and ICC flag that tells the compiler to treat all the warnings +dnl as fatal. We usually need this option to make sure that some +dnl constructs (like attributes) are not simply ignored. +dnl +dnl Other compilers don't support -Werror per se, but they support +dnl an equivalent flag: +dnl - Sun Studio compiler supports -errwarn=%all +AC_DEFUN([CC_CHECK_WERROR], [ + AC_CACHE_CHECK( + [for $CC way to treat warnings as errors], + [cc_cv_werror], + [CC_CHECK_CFLAGS_SILENT([-Werror], [cc_cv_werror=-Werror], + [CC_CHECK_CFLAGS_SILENT([-errwarn=%all], [cc_cv_werror=-errwarn=%all])]) + ]) +]) + +AC_DEFUN([CC_CHECK_ATTRIBUTE], [ + AC_REQUIRE([CC_CHECK_WERROR]) + AC_CACHE_CHECK([if $CC supports __attribute__(( ifelse([$2], , [$1], [$2]) ))], + AS_TR_SH([cc_cv_attribute_$1]), + [ac_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $cc_cv_werror" + AC_LANG_PUSH([C]) + AC_COMPILE_IFELSE([AC_LANG_SOURCE([$3])], + [eval "AS_TR_SH([cc_cv_attribute_$1])='yes'"], + [eval "AS_TR_SH([cc_cv_attribute_$1])='no'"]) + AC_LANG_POP([C]) + CFLAGS="$ac_save_CFLAGS" + ]) + + AS_IF([eval test x$]AS_TR_SH([cc_cv_attribute_$1])[ = xyes], + [AC_DEFINE( + AS_TR_CPP([SUPPORT_ATTRIBUTE_$1]), 1, + [Define this if the compiler supports __attribute__(( ifelse([$2], , [$1], [$2]) ))] + ) + $4], + [$5]) +]) + +AC_DEFUN([CC_ATTRIBUTE_CONSTRUCTOR], [ + CC_CHECK_ATTRIBUTE( + [constructor],, + [void __attribute__((constructor)) ctor() { int a; }], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_FORMAT], [ + CC_CHECK_ATTRIBUTE( + [format], [format(printf, n, n)], + [void __attribute__((format(printf, 1, 2))) printflike(const char *fmt, ...) { fmt = (void *)0; }], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_FORMAT_ARG], [ + CC_CHECK_ATTRIBUTE( + [format_arg], [format_arg(printf)], + [char *__attribute__((format_arg(1))) gettextlike(const char *fmt) { fmt = (void *)0; }], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_VISIBILITY], [ + CC_CHECK_ATTRIBUTE( + [visibility_$1], [visibility("$1")], + [void __attribute__((visibility("$1"))) $1_function() { }], + [$2], [$3]) +]) + +AC_DEFUN([CC_ATTRIBUTE_NONNULL], [ + CC_CHECK_ATTRIBUTE( + [nonnull], [nonnull()], + [void __attribute__((nonnull())) some_function(void *foo, void *bar) { foo = (void*)0; bar = (void*)0; }], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_UNUSED], [ + CC_CHECK_ATTRIBUTE( + [unused], , + [void some_function(void *foo, __attribute__((unused)) void *bar);], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_SENTINEL], [ + CC_CHECK_ATTRIBUTE( + [sentinel], , + [void some_function(void *foo, ...) __attribute__((sentinel));], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_DEPRECATED], [ + CC_CHECK_ATTRIBUTE( + [deprecated], , + [void some_function(void *foo, ...) __attribute__((deprecated));], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_ALIAS], [ + CC_CHECK_ATTRIBUTE( + [alias], [weak, alias], + [void other_function(void *foo) { } + void some_function(void *foo) __attribute__((weak, alias("other_function")));], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_MALLOC], [ + CC_CHECK_ATTRIBUTE( + [malloc], , + [void * __attribute__((malloc)) my_alloc(int n);], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_PACKED], [ + CC_CHECK_ATTRIBUTE( + [packed], , + [struct astructure { char a; int b; long c; void *d; } __attribute__((packed));], + [$1], [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_CONST], [ + CC_CHECK_ATTRIBUTE( + [const], , + [int __attribute__((const)) twopow(int n) { return 1 << n; } ], + [$1], [$2]) +]) + +AC_DEFUN([CC_FLAG_VISIBILITY], [ + AC_REQUIRE([CC_CHECK_WERROR]) + AC_CACHE_CHECK([if $CC supports -fvisibility=hidden], + [cc_cv_flag_visibility], + [cc_flag_visibility_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $cc_cv_werror" + CC_CHECK_CFLAGS_SILENT([-fvisibility=hidden], + cc_cv_flag_visibility='yes', + cc_cv_flag_visibility='no') + CFLAGS="$cc_flag_visibility_save_CFLAGS"]) + + AS_IF([test "x$cc_cv_flag_visibility" = "xyes"], + [AC_DEFINE([SUPPORT_FLAG_VISIBILITY], 1, + [Define this if the compiler supports the -fvisibility flag]) + $1], + [$2]) +]) + +AC_DEFUN([CC_FUNC_EXPECT], [ + AC_REQUIRE([CC_CHECK_WERROR]) + AC_CACHE_CHECK([if compiler has __builtin_expect function], + [cc_cv_func_expect], + [ac_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $cc_cv_werror" + AC_LANG_PUSH([C]) + AC_COMPILE_IFELSE([AC_LANG_SOURCE( + [int some_function() { + int a = 3; + return (int)__builtin_expect(a, 3); + }])], + [cc_cv_func_expect=yes], + [cc_cv_func_expect=no]) + AC_LANG_POP([C]) + CFLAGS="$ac_save_CFLAGS" + ]) + + AS_IF([test "x$cc_cv_func_expect" = "xyes"], + [AC_DEFINE([SUPPORT__BUILTIN_EXPECT], 1, + [Define this if the compiler supports __builtin_expect() function]) + $1], + [$2]) +]) + +AC_DEFUN([CC_ATTRIBUTE_ALIGNED], [ + AC_REQUIRE([CC_CHECK_WERROR]) + AC_CACHE_CHECK([highest __attribute__ ((aligned ())) supported], + [cc_cv_attribute_aligned], + [ac_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $cc_cv_werror" + AC_LANG_PUSH([C]) + for cc_attribute_align_try in 64 32 16 8 4 2; do + AC_COMPILE_IFELSE([AC_LANG_SOURCE([ + int main() { + static char c __attribute__ ((aligned($cc_attribute_align_try))) = 0; + return c; + }])], [cc_cv_attribute_aligned=$cc_attribute_align_try; break]) + done + AC_LANG_POP([C]) + CFLAGS="$ac_save_CFLAGS" + ]) + + if test "x$cc_cv_attribute_aligned" != "x"; then + AC_DEFINE_UNQUOTED([ATTRIBUTE_ALIGNED_MAX], [$cc_cv_attribute_aligned], + [Define the highest alignment supported]) + fi +]) diff --git a/src/fs-poll.c b/src/fs-poll.c index 89864e2..1bac1c5 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 diff --git a/src/idna.c b/src/idna.c index 13ffac6..93d982c 100644 --- a/src/idna.c +++ b/src/idna.c @@ -19,7 +19,9 @@ #include "uv.h" #include "idna.h" +#include #include +#include /* UINT_MAX */ static unsigned uv__utf8_decode1_slow(const char** p, const char* pe, @@ -32,7 +34,7 @@ static unsigned uv__utf8_decode1_slow(const char** p, if (a > 0xF7) return -1; - switch (*p - pe) { + switch (pe - *p) { default: if (a > 0xEF) { min = 0x10000; @@ -62,6 +64,8 @@ static unsigned uv__utf8_decode1_slow(const char** p, a = 0; break; } + /* Fall through. */ + case 0: return -1; /* Invalid continuation byte. */ } @@ -88,6 +92,8 @@ static unsigned uv__utf8_decode1_slow(const char** p, unsigned uv__utf8_decode1(const char** p, const char* pe) { unsigned a; + assert(*p < pe); + a = (unsigned char) *(*p)++; if (a < 128) @@ -96,9 +102,6 @@ unsigned uv__utf8_decode1(const char** p, const char* pe) { return uv__utf8_decode1_slow(p, pe, a); } -#define foreach_codepoint(c, p, pe) \ - for (; (void) (*p <= pe && (c = uv__utf8_decode1(p, pe))), *p <= pe;) - static int uv__idna_toascii_label(const char* s, const char* se, char** d, char* de) { static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz0123456789"; @@ -121,15 +124,22 @@ static int uv__idna_toascii_label(const char* s, const char* se, ss = s; todo = 0; - foreach_codepoint(c, &s, se) { + /* Note: after this loop we've visited all UTF-8 characters and know + * they're legal so we no longer need to check for decode errors. + */ + while (s < se) { + c = uv__utf8_decode1(&s, se); + + if (c == UINT_MAX) + return UV_EINVAL; + if (c < 128) h++; - else if (c == (unsigned) -1) - return UV_EINVAL; else todo++; } + /* Only write "xn--" when there are non-ASCII characters. */ if (todo > 0) { if (*d < de) *(*d)++ = 'x'; if (*d < de) *(*d)++ = 'n'; @@ -137,9 +147,13 @@ static int uv__idna_toascii_label(const char* s, const char* se, if (*d < de) *(*d)++ = '-'; } + /* Write ASCII characters. */ x = 0; s = ss; - foreach_codepoint(c, &s, se) { + while (s < se) { + c = uv__utf8_decode1(&s, se); + assert(c != UINT_MAX); + if (c > 127) continue; @@ -166,10 +180,15 @@ static int uv__idna_toascii_label(const char* s, const char* se, while (todo > 0) { m = -1; s = ss; - foreach_codepoint(c, &s, se) + + while (s < se) { + c = uv__utf8_decode1(&s, se); + assert(c != UINT_MAX); + if (c >= n) if (c < m) m = c; + } x = m - n; y = h + 1; @@ -181,7 +200,10 @@ static int uv__idna_toascii_label(const char* s, const char* se, n = m; s = ss; - foreach_codepoint(c, &s, se) { + while (s < se) { + c = uv__utf8_decode1(&s, se); + assert(c != UINT_MAX); + if (c < n) if (++delta == 0) return UV_E2BIG; /* Overflow. */ @@ -245,8 +267,6 @@ static int uv__idna_toascii_label(const char* s, const char* se, return 0; } -#undef foreach_codepoint - long uv__idna_toascii(const char* s, const char* se, char* d, char* de) { const char* si; const char* st; @@ -256,10 +276,14 @@ long uv__idna_toascii(const char* s, const char* se, char* d, char* de) { ds = d; - for (si = s; si < se; /* empty */) { + si = s; + while (si < se) { st = si; c = uv__utf8_decode1(&si, se); + if (c == UINT_MAX) + return UV_EINVAL; + if (c != '.') if (c != 0x3002) /* 。 */ if (c != 0xFF0E) /* . */ diff --git a/src/inet.c b/src/inet.c index 698ab23..ddabf22 100644 --- a/src/inet.c +++ b/src/inet.c @@ -141,8 +141,9 @@ static int inet_ntop6(const unsigned char *src, char *dst, size_t size) { if (best.base != -1 && (best.base + best.len) == ARRAY_SIZE(words)) *tp++ = ':'; *tp++ = '\0'; - if (UV_E2BIG == uv__strscpy(dst, tmp, size)) + if ((size_t) (tp - tmp) > size) return UV_ENOSPC; + uv__strscpy(dst, tmp, size); return 0; } diff --git a/src/random.c b/src/random.c index 491bf70..e75f77d 100644 --- a/src/random.c +++ b/src/random.c @@ -33,7 +33,7 @@ static int uv__random(void* buf, size_t buflen) { #if defined(__PASE__) rc = uv__random_readpath("/dev/urandom", buf, buflen); -#elif defined(_AIX) +#elif defined(_AIX) || defined(__QNX__) rc = uv__random_readpath("/dev/random", buf, buflen); #elif defined(__APPLE__) || defined(__OpenBSD__) || \ (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) diff --git a/src/strscpy.c b/src/strscpy.c index 2a2bdce..20df6fc 100644 --- a/src/strscpy.c +++ b/src/strscpy.c @@ -1,3 +1,24 @@ +/* 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 "strscpy.h" #include /* SSIZE_MAX */ diff --git a/src/strscpy.h b/src/strscpy.h index fbe0a39..e8d4724 100644 --- a/src/strscpy.h +++ b/src/strscpy.h @@ -1,3 +1,24 @@ +/* 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_STRSCPY_H_ #define UV_STRSCPY_H_ @@ -7,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/threadpool.c b/src/threadpool.c index a8f433f..e804c7c 100644 --- a/src/threadpool.c +++ b/src/threadpool.c @@ -160,14 +160,20 @@ static void post(QUEUE* q, enum uv__work_kind kind) { } -#ifndef _WIN32 -UV_DESTRUCTOR(static void cleanup(void)) { +#ifdef __MVS__ +/* TODO(itodorov) - zos: revisit when Woz compiler is available. */ +__attribute__((destructor)) +#endif +void uv__threadpool_cleanup(void) { unsigned int i; if (nthreads == 0) return; +#ifndef __MVS__ + /* TODO(gabylb) - zos: revisit when Woz compiler is available. */ post(&exit_message, UV__WORK_CPU); +#endif for (i = 0; i < nthreads; i++) if (uv_thread_join(threads + i)) @@ -182,7 +188,6 @@ UV_DESTRUCTOR(static void cleanup(void)) { threads = NULL; nthreads = 0; } -#endif static void init_threads(void) { diff --git a/src/timer.c b/src/timer.c index 9da513f..bc680e7 100644 --- a/src/timer.c +++ b/src/timer.c @@ -51,18 +51,14 @@ static int timer_less_than(const struct heap_node* ha, /* Compare start_id when both have the same timeout. start_id is * allocated with loop->timer_counter in uv_timer_start(). */ - if (a->start_id < b->start_id) - return 1; - if (b->start_id < a->start_id) - return 0; - - return 0; + return a->start_id < b->start_id; } int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) { uv__handle_init(loop, (uv_handle_t*)handle, UV_TIMER); handle->timer_cb = NULL; + handle->timeout = 0; handle->repeat = 0; return 0; } @@ -135,6 +131,14 @@ uint64_t uv_timer_get_repeat(const uv_timer_t* handle) { } +uint64_t uv_timer_get_due_in(const uv_timer_t* handle) { + if (handle->loop->time >= handle->timeout) + return 0; + + return handle->timeout - handle->loop->time; +} + + int uv__next_timeout(const uv_loop_t* loop) { const struct heap_node* heap_node; const uv_timer_t* handle; diff --git a/src/unix/aix-common.c b/src/unix/aix-common.c index c18a529..abc4c90 100644 --- a/src/unix/aix-common.c +++ b/src/unix/aix-common.c @@ -22,42 +22,23 @@ #include "uv.h" #include "internal.h" -#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 -#include -#include -#include -#include -#include +extern char* original_exepath; +extern uv_mutex_t process_title_mutex; +extern uv_once_t process_title_mutex_once; +extern void init_process_title_mutex_once(void); uint64_t uv__hrtime(uv_clocktype_t type) { uint64_t G = 1000000000; @@ -78,80 +59,31 @@ uint64_t uv__hrtime(uv_clocktype_t type) { */ int uv_exepath(char* buffer, size_t* size) { int res; - char args[PATH_MAX]; - char abspath[PATH_MAX]; - size_t abspath_size; + char args[UV__PATH_MAX]; + size_t cached_len; struct procsinfo pi; if (buffer == NULL || size == NULL || *size == 0) return UV_EINVAL; - pi.pi_pid = getpid(); - res = getargs(&pi, sizeof(pi), args, sizeof(args)); - if (res < 0) - return UV_EINVAL; - - /* - * Possibilities for args: - * i) an absolute path such as: /home/user/myprojects/nodejs/node - * ii) a relative path such as: ./node or ../myprojects/nodejs/node - * iii) a bare filename such as "node", after exporting PATH variable - * to its location. - */ - - /* Case i) and ii) absolute or relative paths */ - if (strchr(args, '/') != NULL) { - if (realpath(args, abspath) != abspath) - return UV__ERR(errno); - - abspath_size = strlen(abspath); - + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + if (original_exepath != NULL) { + cached_len = strlen(original_exepath); *size -= 1; - if (*size > abspath_size) - *size = abspath_size; - - memcpy(buffer, abspath, *size); + if (*size > cached_len) + *size = cached_len; + memcpy(buffer, original_exepath, *size); buffer[*size] = '\0'; - + uv_mutex_unlock(&process_title_mutex); return 0; - } else { - /* Case iii). Search PATH environment variable */ - char trypath[PATH_MAX]; - char *clonedpath = NULL; - char *token = NULL; - char *path = getenv("PATH"); - - if (path == NULL) - return UV_EINVAL; - - clonedpath = uv__strdup(path); - if (clonedpath == NULL) - return UV_ENOMEM; - - token = strtok(clonedpath, ":"); - while (token != NULL) { - snprintf(trypath, sizeof(trypath) - 1, "%s/%s", token, args); - if (realpath(trypath, abspath) == abspath) { - /* Check the match is executable */ - if (access(abspath, X_OK) == 0) { - abspath_size = strlen(abspath); - - *size -= 1; - if (*size > abspath_size) - *size = abspath_size; - - memcpy(buffer, abspath, *size); - buffer[*size] = '\0'; - - uv__free(clonedpath); - return 0; - } - } - token = strtok(NULL, ":"); - } - uv__free(clonedpath); + } + uv_mutex_unlock(&process_title_mutex); + pi.pi_pid = getpid(); + res = getargs(&pi, sizeof(pi), args, sizeof(args)); - /* Out of tokens (path entries), and no match found */ + if (res < 0) return UV_EINVAL; - } + + return uv__search_path(args, buffer, size); } diff --git a/src/unix/aix.c b/src/unix/aix.c index 417ee55..6a013d4 100644 --- a/src/unix/aix.c +++ b/src/unix/aix.c @@ -65,14 +65,15 @@ #define RDWR_BUF_SIZE 4096 #define EQ(a,b) (strcmp(a,b) == 0) -static uv_mutex_t process_title_mutex; -static uv_once_t process_title_mutex_once = UV_ONCE_INIT; +char* original_exepath = NULL; +uv_mutex_t process_title_mutex; +uv_once_t process_title_mutex_once = UV_ONCE_INIT; static void* args_mem = NULL; static char** process_argv = NULL; static int process_argc = 0; static char* process_title_ptr = NULL; -static void init_process_title_mutex_once(void) { +void init_process_title_mutex_once(void) { uv_mutex_init(&process_title_mutex); } @@ -145,6 +146,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int i; int rc; int add_failed; + int user_timeout; + int reset_timeout; if (loop->nfds == 0) { assert(QUEUE_EMPTY(&loop->watcher_queue)); @@ -214,7 +217,21 @@ 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) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + } + 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); + nfds = pollset_poll(loop->backend_fd, events, ARRAY_SIZE(events), @@ -227,6 +244,15 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { 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; + } + assert(timeout != -1); return; } @@ -236,6 +262,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { abort(); } + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + if (timeout == -1) continue; @@ -280,16 +311,25 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { /* 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) + if (w == &loop->signal_io_watcher) { have_signals = 1; - else + } else { + uv__metrics_update_idle_time(loop); w->cb(loop, w, pe->revents); + } nevents++; } - if (have_signals != 0) + 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; @@ -830,6 +870,7 @@ void uv__fs_event_close(uv_fs_event_t* handle) { char** uv_setup_args(int argc, char** argv) { + char exepath[UV__PATH_MAX]; char** new_argv; size_t size; char* s; @@ -845,6 +886,15 @@ char** uv_setup_args(int argc, char** argv) { process_argv = argv; process_argc = argc; + /* Use argv[0] to determine value for uv_exepath(). */ + size = sizeof(exepath); + if (uv__search_path(argv[0], exepath, &size) == 0) { + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + original_exepath = uv__strdup(exepath); + uv_mutex_unlock(&process_title_mutex); + } + /* Calculate how much memory we need for the argv strings. */ size = 0; for (i = 0; i < argc; i++) @@ -875,6 +925,10 @@ char** uv_setup_args(int argc, char** argv) { int uv_set_process_title(const char* title) { char* new_title; + /* If uv_setup_args wasn't called or failed, we can't continue. */ + if (process_argv == NULL || args_mem == NULL) + return UV_ENOBUFS; + /* We cannot free this pointer when libuv shuts down, * the process may still be using it. */ @@ -908,6 +962,10 @@ int uv_get_process_title(char* buffer, size_t size) { if (buffer == NULL || size == 0) return UV_EINVAL; + /* If uv_setup_args wasn't called, we can't continue. */ + if (process_argv == NULL) + return UV_ENOBUFS; + uv_once(&process_title_mutex_once, init_process_title_mutex_once); uv_mutex_lock(&process_title_mutex); @@ -926,7 +984,7 @@ int uv_get_process_title(char* buffer, size_t size) { } -UV_DESTRUCTOR(static void free_args_mem(void)) { +void uv__process_title_cleanup(void) { uv__free(args_mem); /* Keep valgrind happy. */ args_mem = NULL; } diff --git a/src/unix/android-ifaddrs.c b/src/unix/android-ifaddrs.c deleted file mode 100644 index 7d48c6a..0000000 --- a/src/unix/android-ifaddrs.c +++ /dev/null @@ -1,712 +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: - 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 26d337e..e1805c3 100644 --- a/src/unix/async.c +++ b/src/unix/async.c @@ -32,6 +32,7 @@ #include #include #include +#include /* sched_yield() */ #ifdef __linux__ #include @@ -81,20 +82,32 @@ int uv_async_send(uv_async_t* handle) { /* Only call this from the event loop thread. */ static int uv__async_spin(uv_async_t* handle) { + int i; int rc; for (;;) { - /* 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. + /* 997 is not completely chosen at random. It's a prime number, acyclical + * by nature, and should therefore hopefully dampen sympathetic resonance. */ - rc = cmpxchgi(&handle->pending, 2, 0); - - if (rc != 1) - return rc; - - /* Other thread is busy with this handle, spin until it's done. */ - cpu_relax(); + 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; + + /* Other thread is busy with this handle, spin until it's done. */ + cpu_relax(); + } + + /* Yield the CPU. We may have preempted the other thread while it's + * inside the critical section and if it's running on the same CPU + * as us, we'll just burn CPU cycles until the end of our time slice. + */ + sched_yield(); } } @@ -201,7 +214,7 @@ static int uv__async_start(uv_loop_t* loop) { pipefd[0] = err; pipefd[1] = -1; #else - err = uv__make_pipe(pipefd, UV__F_NONBLOCK); + err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE); if (err < 0) return err; #endif diff --git a/src/unix/atomic-ops.h b/src/unix/atomic-ops.h index bc37c0d..c48d058 100644 --- a/src/unix/atomic-ops.h +++ b/src/unix/atomic-ops.h @@ -52,7 +52,11 @@ UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) { UV_UNUSED(static void cpu_relax(void)) { #if defined(__i386__) || defined(__x86_64__) - __asm__ __volatile__ ("rep; nop"); /* a.k.a. PAUSE */ + __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 } diff --git a/src/unix/bsd-ifaddrs.c b/src/unix/bsd-ifaddrs.c index a3385af..11ca955 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,10 +40,10 @@ 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`, just see whether `sa_family` - * equals to `AF_LINK` or not. Otherwise, the result depends on the operation + * If `exclude_type` is `UV__EXCLUDE_IFPHYS`, return whether `sa_family` + * equals `AF_LINK`. Otherwise, the result depends on the operating * system with `AF_LINK` or `PF_INET`. */ if (exclude_type == UV__EXCLUDE_IFPHYS) @@ -53,7 +53,7 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { defined(__HAIKU__) /* * On BSD getifaddrs returns information related to the raw underlying - * devices. We're not interested in this information. + * devices. We're not interested in this information. */ if (ent->ifa_addr->sa_family == AF_LINK) return 1; @@ -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 @@ -113,7 +113,9 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr); } - if (ent->ifa_netmask->sa_family == AF_INET6) { + if (ent->ifa_netmask == NULL) { + memset(&address->netmask, 0, sizeof(address->netmask)); + } else 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); @@ -124,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 0ce47c8..b0c01e2 100644 --- a/src/unix/bsd-proctitle.c +++ b/src/unix/bsd-proctitle.c @@ -37,6 +37,12 @@ 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); +} + + char** uv_setup_args(int argc, char** argv) { process_title = argc > 0 ? uv__strdup(argv[0]) : NULL; return argv; diff --git a/src/unix/core.c b/src/unix/core.c index 6d00635..def5b8f 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -71,7 +71,7 @@ extern char** environ; # include # include # include -# if defined(__FreeBSD__) || defined(__linux__) +# if defined(__FreeBSD__) # define uv__accept4 accept4 # endif # if defined(__NetBSD__) @@ -79,25 +79,27 @@ extern char** environ; # endif #endif -#if defined(__ANDROID_API__) && __ANDROID_API__ < 21 -# include /* for dlsym */ -#endif - #if defined(__MVS__) #include #endif #if defined(__linux__) -#include +# include +# include +# define uv__accept4 accept4 +#endif + +#if defined(__linux__) && defined(__SANITIZE_THREAD__) && defined(__clang__) +# include #endif static int 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)); @@ -219,15 +221,23 @@ int uv__getiovmax(void) { #if defined(IOV_MAX) return IOV_MAX; #elif defined(_SC_IOV_MAX) - static int iovmax = -1; - if (iovmax == -1) { - iovmax = sysconf(_SC_IOV_MAX); - /* On some embedded devices (arm-linux-uclibc based ip camera), - * sysconf(_SC_IOV_MAX) can not get the correct value. The return - * value is -1 and the errno is EINPROGRESS. Degrade the value to 1. - */ - if (iovmax == -1) iovmax = 1; - } + static int iovmax_cached = -1; + int iovmax; + + iovmax = uv__load_relaxed(&iovmax_cached); + if (iovmax != -1) + return iovmax; + + /* On some embedded devices (arm-linux-uclibc based ip camera), + * sysconf(_SC_IOV_MAX) can not get the correct value. The return + * value is -1 and the errno is EINPROGRESS. Degrade the value to 1. + */ + iovmax = sysconf(_SC_IOV_MAX); + if (iovmax == -1) + iovmax = 1; + + uv__store_relaxed(&iovmax_cached, iovmax); + return iovmax; #else return 1024; @@ -325,35 +335,36 @@ 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) || + !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)) && + QUEUE_EMPTY(&loop->pending_queue) && + QUEUE_EMPTY(&loop->idle_handles) && + 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 (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); } @@ -375,9 +386,17 @@ int uv_run(uv_loop_t* loop, uv_run_mode mode) { timeout = 0; if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT) - timeout = uv_backend_timeout(loop); + timeout = uv__backend_timeout(loop); uv__io_poll(loop, timeout); + + /* 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 + * the timeout == 0) or was already updated b/c an event was received. + */ + uv__metrics_update_idle_time(loop); + uv__run_check(loop); uv__run_closing_handles(loop); @@ -518,7 +537,7 @@ int uv__close_nocancel(int fd) { #if defined(__APPLE__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension" -#if defined(__LP64__) || defined(TARGET_OS_IPHONE) +#if defined(__LP64__) || TARGET_OS_IPHONE extern int close$NOCANCEL(int); return close$NOCANCEL(fd); #else @@ -526,7 +545,13 @@ int uv__close_nocancel(int fd) { return close$NOCANCEL$UNIX2003(fd); #endif #pragma GCC diagnostic pop -#elif defined(__linux__) +#elif defined(__linux__) && defined(__SANITIZE_THREAD__) && defined(__clang__) + long rc; + __sanitizer_syscall_pre_close(fd); + rc = syscall(SYS_close, fd); + __sanitizer_syscall_post_close(rc, fd); + return rc; +#elif defined(__linux__) && !defined(__SANITIZE_THREAD__) return syscall(SYS_close, fd); #else return close(fd); @@ -561,7 +586,7 @@ int uv__close(int fd) { return uv__close_nocheckstdio(fd); } - +#if UV__NONBLOCK_IS_IOCTL int uv__nonblock_ioctl(int fd, int set) { int r; @@ -574,21 +599,6 @@ int uv__nonblock_ioctl(int fd, int set) { return 0; } - - -#if !defined(__CYGWIN__) && !defined(__MSYS__) && !defined(__HAIKU__) -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 @@ -623,25 +633,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); @@ -661,7 +659,7 @@ ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) { int* end; #if defined(__linux__) static int no_msg_cmsg_cloexec; - if (no_msg_cmsg_cloexec == 0) { + if (0 == uv__load_relaxed(&no_msg_cmsg_cloexec)) { rc = recvmsg(fd, msg, flags | 0x40000000); /* MSG_CMSG_CLOEXEC */ if (rc != -1) return rc; @@ -670,7 +668,7 @@ ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) { rc = recvmsg(fd, msg, flags); if (rc == -1) return UV__ERR(errno); - no_msg_cmsg_cloexec = 1; + uv__store_relaxed(&no_msg_cmsg_cloexec, 1); } else { rc = recvmsg(fd, msg, flags); } @@ -834,8 +832,8 @@ static void maybe_resize(uv_loop_t* loop, unsigned int len) { } nwatchers = next_power_of_two(len + 2) - 2; - watchers = uv__realloc(loop->watchers, - (nwatchers + 2) * sizeof(loop->watchers[0])); + watchers = uv__reallocf(loop->watchers, + (nwatchers + 2) * sizeof(loop->watchers[0])); if (watchers == NULL) abort(); @@ -912,13 +910,12 @@ void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) { if (w->pevents == 0) { QUEUE_REMOVE(&w->watcher_queue); QUEUE_INIT(&w->watcher_queue); + w->events = 0; - if (loop->watchers[w->fd] != NULL) { - assert(loop->watchers[w->fd] == w); + if (w == loop->watchers[w->fd]) { assert(loop->nfds > 0); loop->watchers[w->fd] = NULL; loop->nfds--; - w->events = 0; } } else if (QUEUE_EMPTY(&w->watcher_queue)) @@ -1015,6 +1012,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; @@ -1141,13 +1164,6 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { size_t shell_size; long initsize; int r; -#if defined(__ANDROID_API__) && __ANDROID_API__ < 21 - int (*getpwuid_r)(uid_t, struct passwd*, char*, size_t, struct passwd**); - - getpwuid_r = dlsym(RTLD_DEFAULT, "getpwuid_r"); - if (getpwuid_r == NULL) - return UV_ENOSYS; -#endif if (pwd == NULL) return UV_EINVAL; @@ -1169,7 +1185,9 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { if (buf == NULL) return UV_ENOMEM; - r = getpwuid_r(uid, &pw, buf, bufsize, &result); + do + r = getpwuid_r(uid, &pw, buf, bufsize, &result); + while (r == EINTR); if (r != ERANGE) break; @@ -1179,7 +1197,7 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { if (r != 0) { uv__free(buf); - return -r; + return UV__ERR(r); } if (result == NULL) { @@ -1257,7 +1275,7 @@ int uv_os_environ(uv_env_item_t** envitems, int* count) { *envitems = uv__calloc(i, sizeof(**envitems)); - if (envitems == NULL) + if (*envitems == NULL) return UV_ENOMEM; for (j = 0, cnt = 0; j < i; j++) { @@ -1530,3 +1548,112 @@ void uv_sleep(unsigned int msec) { assert(rc == 0); } + +int uv__search_path(const char* prog, char* buf, size_t* buflen) { + char abspath[UV__PATH_MAX]; + size_t abspath_size; + char trypath[UV__PATH_MAX]; + char* cloned_path; + char* path_env; + char* token; + + if (buf == NULL || buflen == NULL || *buflen == 0) + return UV_EINVAL; + + /* + * Possibilities for prog: + * i) an absolute path such as: /home/user/myprojects/nodejs/node + * ii) a relative path such as: ./node or ../myprojects/nodejs/node + * iii) a bare filename such as "node", after exporting PATH variable + * to its location. + */ + + /* Case i) and ii) absolute or relative paths */ + if (strchr(prog, '/') != NULL) { + if (realpath(prog, abspath) != abspath) + return UV__ERR(errno); + + abspath_size = strlen(abspath); + + *buflen -= 1; + if (*buflen > abspath_size) + *buflen = abspath_size; + + memcpy(buf, abspath, *buflen); + buf[*buflen] = '\0'; + + return 0; + } + + /* Case iii). Search PATH environment variable */ + cloned_path = NULL; + token = NULL; + path_env = getenv("PATH"); + + if (path_env == NULL) + return UV_EINVAL; + + cloned_path = uv__strdup(path_env); + if (cloned_path == NULL) + return UV_ENOMEM; + + token = strtok(cloned_path, ":"); + while (token != NULL) { + snprintf(trypath, sizeof(trypath) - 1, "%s/%s", token, prog); + if (realpath(trypath, abspath) == abspath) { + /* Check the match is executable */ + if (access(abspath, X_OK) == 0) { + abspath_size = strlen(abspath); + + *buflen -= 1; + if (*buflen > abspath_size) + *buflen = abspath_size; + + memcpy(buf, abspath, *buflen); + buf[*buflen] = '\0'; + + uv__free(cloned_path); + return 0; + } + } + token = strtok(NULL, ":"); + } + uv__free(cloned_path); + + /* Out of tokens (path entries), and no match found */ + return UV_EINVAL; +} + + +unsigned int uv_available_parallelism(void) { +#ifdef __linux__ + cpu_set_t set; + long rc; + + 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 = CPU_COUNT(&set); + else + rc = sysconf(_SC_NPROCESSORS_ONLN); + + if (rc < 1) + rc = 1; + + return (unsigned) rc; +#elif defined(__MVS__) + return 1; /* TODO(bnoordhuis) Read from CSD_NUMBER_ONLINE_CPUS? */ +#else /* __linux__ */ + long rc; + + rc = sysconf(_SC_NPROCESSORS_ONLN); + if (rc < 1) + rc = 1; + + return (unsigned) rc; +#endif /* __linux__ */ +} diff --git a/src/unix/darwin-proctitle.c b/src/unix/darwin-proctitle.c index 2daf3e3..5288083 100644 --- a/src/unix/darwin-proctitle.c +++ b/src/unix/darwin-proctitle.c @@ -30,8 +30,7 @@ #include #if !TARGET_OS_IPHONE -# include -# include +#include "darwin-stub.h" #endif diff --git a/src/unix/darwin-stub.h b/src/unix/darwin-stub.h new file mode 100644 index 0000000..433e3ef --- /dev/null +++ b/src/unix/darwin-stub.h @@ -0,0 +1,113 @@ +/* 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_DARWIN_STUB_H_ +#define UV_DARWIN_STUB_H_ + +#include + +struct CFArrayCallBacks; +struct CFRunLoopSourceContext; +struct FSEventStreamContext; +struct CFRange; + +typedef double CFAbsoluteTime; +typedef double CFTimeInterval; +typedef int FSEventStreamEventFlags; +typedef int OSStatus; +typedef long CFIndex; +typedef struct CFArrayCallBacks CFArrayCallBacks; +typedef struct CFRunLoopSourceContext CFRunLoopSourceContext; +typedef struct FSEventStreamContext FSEventStreamContext; +typedef uint32_t FSEventStreamCreateFlags; +typedef uint64_t FSEventStreamEventId; +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, + void*, + const FSEventStreamEventFlags*, + const FSEventStreamEventId*); + +struct CFRunLoopSourceContext { + CFIndex version; + void* info; + void* pad[7]; + void (*perform)(void*); +}; + +struct FSEventStreamContext { + CFIndex version; + void* info; + void* pad[3]; +}; + +struct CFRange { + CFIndex location; + CFIndex length; +}; + +static const CFStringEncoding kCFStringEncodingUTF8 = 0x8000100; +static const OSStatus noErr = 0; + +static const FSEventStreamEventId kFSEventStreamEventIdSinceNow = -1; + +static const int kFSEventStreamCreateFlagNoDefer = 2; +static const int kFSEventStreamCreateFlagFileEvents = 16; + +static const int kFSEventStreamEventFlagEventIdsWrapped = 8; +static const int kFSEventStreamEventFlagHistoryDone = 16; +static const int kFSEventStreamEventFlagItemChangeOwner = 0x4000; +static const int kFSEventStreamEventFlagItemCreated = 0x100; +static const int kFSEventStreamEventFlagItemFinderInfoMod = 0x2000; +static const int kFSEventStreamEventFlagItemInodeMetaMod = 0x400; +static const int kFSEventStreamEventFlagItemIsDir = 0x20000; +static const int kFSEventStreamEventFlagItemModified = 0x1000; +static const int kFSEventStreamEventFlagItemRemoved = 0x200; +static const int kFSEventStreamEventFlagItemRenamed = 0x800; +static const int kFSEventStreamEventFlagItemXattrMod = 0x8000; +static const int kFSEventStreamEventFlagKernelDropped = 4; +static const int kFSEventStreamEventFlagMount = 64; +static const int kFSEventStreamEventFlagRootChanged = 32; +static const int kFSEventStreamEventFlagUnmount = 128; +static const int kFSEventStreamEventFlagUserDropped = 2; + +#endif /* UV_DARWIN_STUB_H_ */ diff --git a/src/unix/darwin.c b/src/unix/darwin.c index 654aba2..62f04d3 100644 --- a/src/unix/darwin.c +++ b/src/unix/darwin.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include /* _NSGetExecutablePath */ @@ -32,6 +33,13 @@ #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; @@ -48,15 +56,19 @@ void uv__platform_loop_delete(uv_loop_t* loop) { } -uint64_t uv__hrtime(uv_clocktype_t type) { - static mach_timebase_info_data_t info; - - if ((ACCESS_ONCE(uint32_t, info.numer) == 0 || - ACCESS_ONCE(uint32_t, info.denom) == 0) && - mach_timebase_info(&info) != KERN_SUCCESS) +static void uv__hrtime_init_once(void) { + if (KERN_SUCCESS != mach_timebase_info(&timebase)) abort(); - return mach_absolute_time() * info.numer / info.denom; + 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; } @@ -171,17 +183,159 @@ 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) && @@ -189,9 +343,9 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { return UV__ERR(errno); } - size = sizeof(cpuspeed); - if (sysctlbyname("hw.cpufrequency", &cpuspeed, &size, NULL, 0)) - return UV__ERR(errno); + err = uv__get_cpu_speed(&cpuspeed); + if (err < 0) + return err; if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numcpus, (processor_info_array_t*)&info, diff --git a/src/unix/dl.c b/src/unix/dl.c index fc1c052..80b3333 100644 --- a/src/unix/dl.c +++ b/src/unix/dl.c @@ -53,7 +53,7 @@ void uv_dlclose(uv_lib_t* lib) { int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr) { dlerror(); /* Reset error status. */ *ptr = dlsym(lib->handle, name); - return uv__dlerror(lib); + return *ptr ? 0 : uv__dlerror(lib); } diff --git a/src/unix/epoll.c b/src/unix/epoll.c new file mode 100644 index 0000000..97348e2 --- /dev/null +++ b/src/unix/epoll.c @@ -0,0 +1,422 @@ +/* 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 ef77e12..658ff26 100644 --- a/src/unix/freebsd.c +++ b/src/unix/freebsd.c @@ -56,31 +56,6 @@ int uv__platform_loop_init(uv_loop_t* loop) { void uv__platform_loop_delete(uv_loop_t* loop) { } - -#ifdef __DragonFly__ -int uv_exepath(char* buffer, size_t* size) { - char abspath[PATH_MAX * 2 + 1]; - ssize_t abspath_size; - - if (buffer == NULL || size == NULL || *size == 0) - return UV_EINVAL; - - abspath_size = readlink("/proc/curproc/file", abspath, sizeof(abspath)); - if (abspath_size < 0) - return UV__ERR(errno); - - assert(abspath_size > 0); - *size -= 1; - - if (*size > abspath_size) - *size = abspath_size; - - memcpy(buffer, abspath, *size); - buffer[*size] = '\0'; - - return 0; -} -#else int uv_exepath(char* buffer, size_t* size) { char abspath[PATH_MAX * 2 + 1]; int mib[4]; @@ -110,7 +85,6 @@ int uv_exepath(char* buffer, size_t* size) { return 0; } -#endif uint64_t uv_get_free_memory(void) { int freecount; @@ -290,26 +264,41 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { } -int uv__sendmmsg(int fd, - struct uv__mmsghdr* mmsg, - unsigned int vlen, - unsigned int flags) { -#if __FreeBSD__ >= 11 - return sendmmsg(fd, mmsg, vlen, flags); +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, - unsigned int flags, - struct timespec* timeout) { -#if __FreeBSD__ >= 11 - return recvmmsg(fd, mmsg, vlen, flags, timeout); +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 */); #else return errno = ENOSYS, -1; #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) +{ +#if __FreeBSD__ >= 13 && !defined(__DragonFly__) + return copy_file_range(fd_in, off_in, fd_out, off_out, len, flags); +#else + return errno = ENOSYS, -1; +#endif +} diff --git a/src/unix/fs.c b/src/unix/fs.c index d5017eb..b3211ec 100644 --- a/src/unix/fs.c +++ b/src/unix/fs.c @@ -56,8 +56,13 @@ # define HAVE_PREADV 0 #endif +#if defined(__linux__) +# include "sys/utsname.h" +#endif + #if defined(__linux__) || defined(__sun) # include +# include #endif #if defined(__APPLE__) @@ -79,7 +84,11 @@ defined(__NetBSD__) # include # include -#elif defined(__sun) || defined(__MVS__) || defined(__NetBSD__) || defined(__HAIKU__) +#elif defined(__sun) || \ + defined(__MVS__) || \ + defined(__NetBSD__) || \ + defined(__HAIKU__) || \ + defined(__QNX__) # include #else # include @@ -205,23 +214,45 @@ static ssize_t uv__fs_fdatasync(uv_fs_t* req) { } +UV_UNUSED(static struct timespec uv__fs_to_timespec(double time)) { + struct timespec ts; + ts.tv_sec = time; + ts.tv_nsec = (time - ts.tv_sec) * 1e9; + + /* TODO(bnoordhuis) Remove this. utimesat() has nanosecond resolution but we + * stick to microsecond resolution for the sake of consistency with other + * platforms. I'm the original author of this compatibility hack but I'm + * less convinced it's useful nowadays. + */ + ts.tv_nsec -= ts.tv_nsec % 1000; + + if (ts.tv_nsec < 0) { + ts.tv_nsec += 1e9; + ts.tv_sec -= 1; + } + return ts; +} + +UV_UNUSED(static struct timeval uv__fs_to_timeval(double time)) { + struct timeval tv; + tv.tv_sec = time; + tv.tv_usec = (time - tv.tv_sec) * 1e6; + if (tv.tv_usec < 0) { + tv.tv_usec += 1e6; + tv.tv_sec -= 1; + } + return tv; +} + static ssize_t uv__fs_futime(uv_fs_t* req) { #if defined(__linux__) \ || defined(_AIX71) \ - || defined(__HAIKU__) - /* utimesat() has nanosecond resolution but we stick to microseconds - * for the sake of consistency with other platforms. - */ + || defined(__HAIKU__) \ + || defined(__GNU__) struct timespec ts[2]; - ts[0].tv_sec = req->atime; - ts[0].tv_nsec = (uint64_t)(req->atime * 1000000) % 1000000 * 1000; - ts[1].tv_sec = req->mtime; - ts[1].tv_nsec = (uint64_t)(req->mtime * 1000000) % 1000000 * 1000; -#if defined(__ANDROID_API__) && __ANDROID_API__ < 21 - return utimensat(req->file, NULL, ts, 0); -#else + ts[0] = uv__fs_to_timespec(req->atime); + ts[1] = uv__fs_to_timespec(req->mtime); return futimens(req->file, ts); -#endif #elif defined(__APPLE__) \ || defined(__DragonFly__) \ || defined(__FreeBSD__) \ @@ -230,10 +261,8 @@ static ssize_t uv__fs_futime(uv_fs_t* req) { || defined(__OpenBSD__) \ || defined(__sun) struct timeval tv[2]; - tv[0].tv_sec = req->atime; - tv[0].tv_usec = (uint64_t)(req->atime * 1000000) % 1000000; - tv[1].tv_sec = req->mtime; - tv[1].tv_usec = (uint64_t)(req->mtime * 1000000) % 1000000; + tv[0] = uv__fs_to_timeval(req->atime); + tv[1] = uv__fs_to_timeval(req->mtime); # if defined(__sun) return futimesat(req->file, NULL, tv); # else @@ -300,13 +329,14 @@ static int uv__fs_mkstemp(uv_fs_t* req) { if (path_length < pattern_size || strcmp(path + path_length - pattern_size, pattern)) { errno = EINVAL; - return -1; + r = -1; + goto clobber; } uv_once(&once, uv__mkostemp_initonce); #ifdef O_CLOEXEC - if (no_cloexec_support == 0 && uv__mkostemp != NULL) { + if (uv__load_relaxed(&no_cloexec_support) == 0 && uv__mkostemp != NULL) { r = uv__mkostemp(path, O_CLOEXEC); if (r >= 0) @@ -315,11 +345,11 @@ static int uv__fs_mkstemp(uv_fs_t* req) { /* If mkostemp() returns EINVAL, it means the kernel doesn't support O_CLOEXEC, so we just fallback to mkstemp() below. */ if (errno != EINVAL) - return r; + goto clobber; /* We set the static variable so that next calls don't even try to use mkostemp. */ - no_cloexec_support = 1; + uv__store_relaxed(&no_cloexec_support, 1); } #endif /* O_CLOEXEC */ @@ -341,6 +371,9 @@ static int uv__fs_mkstemp(uv_fs_t* req) { if (req->cb != NULL) uv_rwlock_rdunlock(&req->loop->cloexec_lock); +clobber: + if (r < 0) + path[0] = '\0'; return r; } @@ -450,7 +483,7 @@ static ssize_t uv__fs_read(uv_fs_t* req) { result = preadv(req->file, (struct iovec*) req->bufs, req->nbufs, req->off); #else # if defined(__linux__) - if (no_preadv) retry: + if (uv__load_relaxed(&no_preadv)) retry: # endif { result = uv__fs_preadv(req->file, req->bufs, req->nbufs, req->off); @@ -462,7 +495,7 @@ static ssize_t uv__fs_read(uv_fs_t* req) { req->nbufs, req->off); if (result == -1 && errno == ENOSYS) { - no_preadv = 1; + uv__store_relaxed(&no_preadv, 1); goto retry; } } @@ -619,7 +652,11 @@ static int uv__fs_closedir(uv_fs_t* req) { static int uv__fs_statfs(uv_fs_t* req) { uv_statfs_t* stat_fs; -#if defined(__sun) || defined(__MVS__) || defined(__NetBSD__) || defined(__HAIKU__) +#if defined(__sun) || \ + defined(__MVS__) || \ + defined(__NetBSD__) || \ + defined(__HAIKU__) || \ + defined(__QNX__) struct statvfs buf; if (0 != statvfs(req->path, &buf)) @@ -636,7 +673,12 @@ static int uv__fs_statfs(uv_fs_t* req) { return -1; } -#if defined(__sun) || defined(__MVS__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__HAIKU__) +#if defined(__sun) || \ + defined(__MVS__) || \ + defined(__OpenBSD__) || \ + defined(__NetBSD__) || \ + defined(__HAIKU__) || \ + defined(__QNX__) stat_fs->f_type = 0; /* f_type is not supported. */ #else stat_fs->f_type = buf.f_type; @@ -666,7 +708,6 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) { ssize_t maxlen; ssize_t len; char* buf; - char* newbuf; #if defined(_POSIX_PATH_MAX) || defined(PATH_MAX) maxlen = uv__fs_pathmax_size(req->path); @@ -710,14 +751,10 @@ static ssize_t uv__fs_readlink(uv_fs_t* req) { /* Uncommon case: resize to make room for the trailing nul byte. */ if (len == maxlen) { - newbuf = uv__realloc(buf, len + 1); + buf = uv__reallocf(buf, len + 1); - if (newbuf == NULL) { - uv__free(buf); + if (buf == NULL) return -1; - } - - buf = newbuf; } buf[len] = '\0'; @@ -869,6 +906,115 @@ out: } +#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. + */ +static int uv__is_buggy_cephfs(int fd) { + struct statfs s; + + if (-1 == fstatfs(fd, &s)) + return 0; + + if (s.f_type != /* CephFS */ 0xC36400) + return 0; + + return uv__kernel_version() < /* 4.20.0 */ 0x041400; +} + + +static int uv__is_cifs_or_smb(int fd) { + struct statfs s; + + if (-1 == fstatfs(fd, &s)) + return 0; + + switch ((unsigned) s.f_type) { + case 0x0000517Bu: /* SMB */ + case 0xFE534D42u: /* SMB2 */ + case 0xFF534D42u: /* CIFS */ + return 1; + } + + return 0; +} + + +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; + ssize_t r; + + if (uv__load_relaxed(&no_copy_file_range_support)) { + errno = ENOSYS; + return -1; + } + + r = uv__fs_copy_file_range(in_fd, off, out_fd, NULL, len, 0); + + if (r != -1) + return r; + + switch (errno) { + case EACCES: + /* Pre-4.20 kernels have a bug where CephFS uses the RADOS + * copy-from command when it shouldn't. + */ + if (uv__is_buggy_cephfs(in_fd)) + errno = ENOSYS; /* Use fallback. */ + break; + case ENOSYS: + uv__store_relaxed(&no_copy_file_range_support, 1); + break; + case EPERM: + /* It's been reported that CIFS spuriously fails. + * Consider it a transient error. + */ + if (uv__is_cifs_or_smb(out_fd)) + errno = ENOSYS; /* Use fallback. */ + break; + case ENOTSUP: + case EXDEV: + /* ENOTSUP - it could work on another file system type. + * EXDEV - it will not work when in_fd and out_fd are not on the same + * mounted filesystem (pre Linux 5.3) + */ + errno = ENOSYS; /* Use fallback. */ + break; + } + + return -1; +} + +#endif /* __linux__ */ + + static ssize_t uv__fs_sendfile(uv_fs_t* req) { int in_fd; int out_fd; @@ -880,9 +1026,20 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) { { off_t off; ssize_t r; + size_t len; + int try_sendfile; off = req->off; - r = sendfile(out_fd, in_fd, &off, req->bufsml[0].len); + len = req->bufsml[0].len; + try_sendfile = 1; + +#ifdef __linux__ + r = uv__fs_try_copy_file_range(in_fd, &off, out_fd, len); + try_sendfile = (r == -1 && errno == ENOSYS); +#endif + + if (try_sendfile) + r = sendfile(out_fd, in_fd, &off, len); /* sendfile() on SunOS returns EINVAL if the target fd is not a socket but * it still writes out data. Fortunately, we can detect it by checking if @@ -918,6 +1075,17 @@ 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__) @@ -973,14 +1141,9 @@ static ssize_t uv__fs_utime(uv_fs_t* req) { || defined(_AIX71) \ || defined(__sun) \ || defined(__HAIKU__) - /* utimesat() has nanosecond resolution but we stick to microseconds - * for the sake of consistency with other platforms. - */ struct timespec ts[2]; - ts[0].tv_sec = req->atime; - ts[0].tv_nsec = (uint64_t)(req->atime * 1000000) % 1000000 * 1000; - ts[1].tv_sec = req->mtime; - ts[1].tv_nsec = (uint64_t)(req->mtime * 1000000) % 1000000 * 1000; + ts[0] = uv__fs_to_timespec(req->atime); + ts[1] = uv__fs_to_timespec(req->mtime); return utimensat(AT_FDCWD, req->path, ts, 0); #elif defined(__APPLE__) \ || defined(__DragonFly__) \ @@ -989,10 +1152,8 @@ static ssize_t uv__fs_utime(uv_fs_t* req) { || defined(__NetBSD__) \ || defined(__OpenBSD__) struct timeval tv[2]; - tv[0].tv_sec = req->atime; - tv[0].tv_usec = (uint64_t)(req->atime * 1000000) % 1000000; - tv[1].tv_sec = req->mtime; - tv[1].tv_usec = (uint64_t)(req->mtime * 1000000) % 1000000; + tv[0] = uv__fs_to_timeval(req->atime); + tv[1] = uv__fs_to_timeval(req->mtime); return utimes(req->path, tv); #elif defined(_AIX) \ && !defined(_AIX71) @@ -1015,6 +1176,32 @@ static ssize_t uv__fs_utime(uv_fs_t* req) { } +static ssize_t uv__fs_lutime(uv_fs_t* req) { +#if defined(__linux__) || \ + defined(_AIX71) || \ + defined(__sun) || \ + defined(__HAIKU__) || \ + defined(__GNU__) + struct timespec ts[2]; + ts[0] = uv__fs_to_timespec(req->atime); + ts[1] = uv__fs_to_timespec(req->mtime); + return utimensat(AT_FDCWD, req->path, ts, AT_SYMLINK_NOFOLLOW); +#elif defined(__APPLE__) || \ + defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || \ + defined(__NetBSD__) + struct timeval tv[2]; + tv[0] = uv__fs_to_timeval(req->atime); + tv[1] = uv__fs_to_timeval(req->mtime); + return lutimes(req->path, tv); +#else + errno = ENOSYS; + return -1; +#endif +} + + static ssize_t uv__fs_write(uv_fs_t* req) { #if defined(__linux__) static int no_pwritev; @@ -1084,9 +1271,10 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { int dst_flags; int result; int err; - size_t bytes_to_send; - int64_t in_offset; - ssize_t bytes_written; + off_t bytes_to_send; + off_t in_offset; + off_t bytes_written; + size_t bytes_chunk; dstfd = -1; err = 0; @@ -1104,7 +1292,7 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { goto out; } - dst_flags = O_WRONLY | O_CREAT | O_TRUNC; + dst_flags = O_WRONLY | O_CREAT; if (req->flags & UV_FS_COPYFILE_EXCL) dst_flags |= O_EXCL; @@ -1123,37 +1311,40 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { goto out; } - /* Get the destination file's mode. */ - if (fstat(dstfd, &dst_statsbuf)) { - err = UV__ERR(errno); - goto out; - } + /* If the file is not being opened exclusively, verify that the source and + 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)) { + err = UV__ERR(errno); + goto out; + } - /* Check if srcfd and dstfd refer to the same file */ - if (src_statsbuf.st_dev == dst_statsbuf.st_dev && - src_statsbuf.st_ino == dst_statsbuf.st_ino) { - goto out; + /* Check if srcfd and dstfd refer to the same file */ + if (src_statsbuf.st_dev == dst_statsbuf.st_dev && + src_statsbuf.st_ino == dst_statsbuf.st_ino) { + goto out; + } + + /* Truncate the file in case the destination already existed. */ + if (ftruncate(dstfd, 0) != 0) { + err = UV__ERR(errno); + goto out; + } } if (fchmod(dstfd, src_statsbuf.st_mode) == -1) { err = UV__ERR(errno); #ifdef __linux__ + /* fchmod() on CIFS shares always fails with EPERM unless the share is + * mounted with "noperm". As fchmod() is a meaningless operation on such + * shares anyway, detect that condition and squelch the error. + */ if (err != UV_EPERM) goto out; - { - struct statfs s; - - /* fchmod() on CIFS shares always fails with EPERM unless the share is - * mounted with "noperm". As fchmod() is a meaningless operation on such - * shares anyway, detect that condition and squelch the error. - */ - if (fstatfs(dstfd, &s) == -1) - goto out; - - if (s.f_type != /* CIFS */ 0xFF534D42u) - goto out; - } + if (!uv__is_cifs_or_smb(dstfd)) + goto out; err = 0; #else /* !__linux__ */ @@ -1185,7 +1376,10 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { bytes_to_send = src_statsbuf.st_size; in_offset = 0; while (bytes_to_send != 0) { - uv_fs_sendfile(NULL, &fs_req, dstfd, srcfd, in_offset, bytes_to_send, NULL); + bytes_chunk = SSIZE_MAX; + if (bytes_to_send < (off_t) bytes_chunk) + bytes_chunk = bytes_to_send; + uv_fs_sendfile(NULL, &fs_req, dstfd, srcfd, in_offset, bytes_chunk, NULL); bytes_written = fs_req.result; uv_fs_req_cleanup(&fs_req); @@ -1268,7 +1462,8 @@ static void uv__to_stat(struct stat* src, uv_stat_t* dst) { dst->st_birthtim.tv_nsec = src->st_ctimensec; dst->st_flags = 0; dst->st_gen = 0; -#elif !defined(_AIX) && ( \ +#elif !defined(_AIX) && \ + !defined(__MVS__) && ( \ defined(__DragonFly__) || \ defined(__FreeBSD__) || \ defined(__OpenBSD__) || \ @@ -1325,7 +1520,7 @@ static int uv__fs_statx(int fd, int mode; int rc; - if (no_statx) + if (uv__load_relaxed(&no_statx)) return UV_ENOSYS; dirfd = AT_FDCWD; @@ -1348,8 +1543,9 @@ static int uv__fs_statx(int fd, case -1: /* EPERM happens when a seccomp filter rejects the system call. * Has been observed with libseccomp < 2.3.3 and docker < 18.04. + * EOPNOTSUPP is used on DVS exported filesystems */ - if (errno != EINVAL && errno != EPERM && errno != ENOSYS) + if (errno != EINVAL && errno != EPERM && errno != ENOSYS && errno != EOPNOTSUPP) return -1; /* Fall through. */ default: @@ -1358,16 +1554,16 @@ 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. */ - no_statx = 1; + uv__store_relaxed(&no_statx, 1); return UV_ENOSYS; } - buf->st_dev = 256 * statxbuf.stx_dev_major + statxbuf.stx_dev_minor; + 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 = statxbuf.stx_rdev_major; + 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; @@ -1528,6 +1724,7 @@ static void uv__fs_work(struct uv__work* w) { X(FSYNC, uv__fs_fsync(req)); X(FTRUNCATE, ftruncate(req->file, req->off)); X(FUTIME, uv__fs_futime(req)); + X(LUTIME, uv__fs_lutime(req)); X(LSTAT, uv__fs_lstat(req->path, &req->statbuf)); X(LINK, link(req->path, req->new_path)); X(MKDIR, mkdir(req->path, req->mode)); @@ -1714,6 +1911,19 @@ int uv_fs_futime(uv_loop_t* loop, POST; } +int uv_fs_lutime(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + double atime, + double mtime, + uv_fs_cb cb) { + INIT(LUTIME); + PATH; + req->atime = atime; + req->mtime = mtime; + POST; +} + int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { INIT(LSTAT); @@ -1987,7 +2197,7 @@ void uv_fs_req_cleanup(uv_fs_t* req) { /* Only necessary for asychronous 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 + * 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. */ if (req->path != NULL && @@ -2042,3 +2252,7 @@ int uv_fs_statfs(uv_loop_t* loop, PATH; POST; } + +int uv_fs_get_system_error(const uv_fs_t* req) { + return -req->result; +} diff --git a/src/unix/fsevents.c b/src/unix/fsevents.c index 448921f..bf4f1f6 100644 --- a/src/unix/fsevents.c +++ b/src/unix/fsevents.c @@ -41,34 +41,33 @@ void uv__fsevents_loop_delete(uv_loop_t* loop) { #else /* TARGET_OS_IPHONE */ +#include "darwin-stub.h" + #include #include #include #include -#include -#include - -/* These are macros to avoid "initializer element is not constant" errors - * with old versions of gcc. - */ -#define kFSEventsModified (kFSEventStreamEventFlagItemFinderInfoMod | \ - kFSEventStreamEventFlagItemModified | \ - kFSEventStreamEventFlagItemInodeMetaMod | \ - kFSEventStreamEventFlagItemChangeOwner | \ - kFSEventStreamEventFlagItemXattrMod) - -#define kFSEventsRenamed (kFSEventStreamEventFlagItemCreated | \ - kFSEventStreamEventFlagItemRemoved | \ - kFSEventStreamEventFlagItemRenamed) - -#define kFSEventsSystem (kFSEventStreamEventFlagUserDropped | \ - kFSEventStreamEventFlagKernelDropped | \ - kFSEventStreamEventFlagEventIdsWrapped | \ - kFSEventStreamEventFlagHistoryDone | \ - kFSEventStreamEventFlagMount | \ - kFSEventStreamEventFlagUnmount | \ - kFSEventStreamEventFlagRootChanged) +static const int kFSEventsModified = + kFSEventStreamEventFlagItemChangeOwner | + kFSEventStreamEventFlagItemFinderInfoMod | + kFSEventStreamEventFlagItemInodeMetaMod | + kFSEventStreamEventFlagItemModified | + kFSEventStreamEventFlagItemXattrMod; + +static const int kFSEventsRenamed = + kFSEventStreamEventFlagItemCreated | + kFSEventStreamEventFlagItemRemoved | + kFSEventStreamEventFlagItemRenamed; + +static const int kFSEventsSystem = + kFSEventStreamEventFlagUserDropped | + kFSEventStreamEventFlagKernelDropped | + kFSEventStreamEventFlagEventIdsWrapped | + kFSEventStreamEventFlagHistoryDone | + kFSEventStreamEventFlagMount | + kFSEventStreamEventFlagUnmount | + kFSEventStreamEventFlagRootChanged; typedef struct uv__fsevents_event_s uv__fsevents_event_t; typedef struct uv__cf_loop_signal_s uv__cf_loop_signal_t; @@ -148,7 +147,7 @@ static void (*pFSEventStreamRelease)(FSEventStreamRef); static void (*pFSEventStreamScheduleWithRunLoop)(FSEventStreamRef, CFRunLoopRef, CFStringRef); -static Boolean (*pFSEventStreamStart)(FSEventStreamRef); +static int (*pFSEventStreamStart)(FSEventStreamRef); static void (*pFSEventStreamStop)(FSEventStreamRef); #define UV__FSEVENTS_PROCESS(handle, block) \ @@ -215,7 +214,7 @@ static void uv__fsevents_push_event(uv_fs_event_t* handle, /* Runs in CF thread, when there're events in FSEventStream */ -static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef, +static void uv__fsevents_event_cb(const FSEventStreamRef streamRef, void* info, size_t numEvents, void* eventPaths, @@ -340,11 +339,8 @@ static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) { FSEventStreamCreateFlags flags; /* Initialize context */ - ctx.version = 0; + memset(&ctx, 0, sizeof(ctx)); ctx.info = loop; - ctx.retain = NULL; - ctx.release = NULL; - ctx.copyDescription = NULL; latency = 0.05; @@ -599,8 +595,7 @@ out: static int uv__fsevents_loop_init(uv_loop_t* loop) { CFRunLoopSourceContext ctx; uv__cf_loop_state_t* state; - pthread_attr_t attr_storage; - pthread_attr_t* attr; + pthread_attr_t attr; int err; if (loop->cf_state != NULL) @@ -645,25 +640,19 @@ static int uv__fsevents_loop_init(uv_loop_t* loop) { goto fail_signal_source_create; } - /* In the unlikely event that pthread_attr_init() fails, create the thread - * with the default stack size. We'll use a little more address space but - * that in itself is not a fatal error. - */ - attr = &attr_storage; - if (pthread_attr_init(attr)) - attr = NULL; + if (pthread_attr_init(&attr)) + abort(); - if (attr != NULL) - if (pthread_attr_setstacksize(attr, 4 * PTHREAD_STACK_MIN)) - abort(); + if (pthread_attr_setstacksize(&attr, uv__thread_stack_size())) + abort(); loop->cf_state = state; /* uv_thread_t is an alias for pthread_t. */ - err = UV__ERR(pthread_create(&loop->cf_thread, attr, uv__cf_loop_runner, loop)); + err = UV__ERR(pthread_create(&loop->cf_thread, &attr, uv__cf_loop_runner, loop)); - if (attr != NULL) - pthread_attr_destroy(attr); + if (pthread_attr_destroy(&attr)) + abort(); if (err) goto fail_thread_create; diff --git a/src/unix/getaddrinfo.c b/src/unix/getaddrinfo.c index d7ca7d1..77337ac 100644 --- a/src/unix/getaddrinfo.c +++ b/src/unix/getaddrinfo.c @@ -21,9 +21,6 @@ /* Expose glibc-specific EAI_* error codes. Needs to be defined before we * include any headers. */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE -#endif #include "uv.h" #include "internal.h" diff --git a/src/unix/hurd.c b/src/unix/hurd.c new file mode 100644 index 0000000..d19ea63 --- /dev/null +++ b/src/unix/hurd.c @@ -0,0 +1,167 @@ +/* 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. */ +} diff --git a/src/unix/ibmi.c b/src/unix/ibmi.c index ff300ea..8c6ae63 100644 --- a/src/unix/ibmi.c +++ b/src/unix/ibmi.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -58,6 +57,9 @@ #include #include +char* original_exepath = NULL; +uv_mutex_t process_title_mutex; +uv_once_t process_title_mutex_once = UV_ONCE_INIT; typedef struct { int bytes_available; @@ -163,7 +165,7 @@ static void iconv_a2e(const char* src, unsigned char dst[], size_t length) { srclen = strlen(src); if (srclen > length) - abort(); + srclen = length; for (i = 0; i < srclen; i++) dst[i] = a2e[src[i]]; /* padding the remaining part with spaces */ @@ -171,6 +173,9 @@ static void iconv_a2e(const char* src, unsigned char dst[], size_t length) { dst[i] = a2e[' ']; } +void init_process_title_mutex_once(void) { + uv_mutex_init(&process_title_mutex); +} static int get_ibmi_system_status(SSTS0200* rcvr) { /* rcvrlen is input parameter 2 to QWCRSSTS */ @@ -354,6 +359,10 @@ static int get_ibmi_physical_address(const char* line, char (*phys_addr)[6]) { if (rc != 0) return rc; + if (err.bytes_available > 0) { + return -1; + } + /* convert ebcdic loca_adapter_address to ascii first */ iconv_e2a(rcvr.loca_adapter_address, mac_addr, sizeof(rcvr.loca_adapter_address)); @@ -437,9 +446,42 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { } address->is_internal = cur->ifa_flags & IFF_LOOPBACK ? 1 : 0; if (!address->is_internal) { - int rc = get_ibmi_physical_address(address->name, &address->phys_addr); - if (rc != 0) - r = rc; + int rc = -1; + size_t name_len = strlen(address->name); + /* To get the associated MAC address, we must convert the address to a + * line description. Normally, the name field contains the line + * description name, but for VLANs it has the VLAN appended with a + * period. Since object names can also contain periods and numbers, there + * is no way to know if a returned name is for a VLAN or not. eg. + * *LIND ETH1.1 and *LIND ETH1, VLAN 1 both have the same name: ETH1.1 + * + * Instead, we apply the same heuristic used by some of the XPF ioctls: + * - names > 10 *must* contain a VLAN + * - assume names <= 10 do not contain a VLAN and try directly + * - if >10 or QDCRLIND returned an error, try to strip off a VLAN + * and try again + * - if we still get an error or couldn't find a period, leave the MAC as + * 00:00:00:00:00:00 + */ + if (name_len <= 10) { + /* Assume name does not contain a VLAN ID */ + rc = get_ibmi_physical_address(address->name, &address->phys_addr); + } + + if (name_len > 10 || rc != 0) { + /* The interface name must contain a VLAN ID suffix. Attempt to strip + * it off so we can get the line description to pass to QDCRLIND. + */ + char* temp_name = uv__strdup(address->name); + char* dot = strrchr(temp_name, '.'); + if (dot != NULL) { + *dot = '\0'; + if (strlen(temp_name) <= 10) { + rc = get_ibmi_physical_address(temp_name, &address->phys_addr); + } + } + uv__free(temp_name); + } } address++; @@ -459,3 +501,37 @@ void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) { uv__free(addresses); } + +char** uv_setup_args(int argc, char** argv) { + char exepath[UV__PATH_MAX]; + char* s; + size_t size; + + if (argc > 0) { + /* Use argv[0] to determine value for uv_exepath(). */ + size = sizeof(exepath); + if (uv__search_path(argv[0], exepath, &size) == 0) { + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + original_exepath = uv__strdup(exepath); + uv_mutex_unlock(&process_title_mutex); + } + } + + return argv; +} + +int uv_set_process_title(const char* title) { + return 0; +} + +int uv_get_process_title(char* buffer, size_t size) { + if (buffer == NULL || size == 0) + return UV_EINVAL; + + buffer[0] = '\0'; + return 0; +} + +void uv__process_title_cleanup(void) { +} diff --git a/src/unix/internal.h b/src/unix/internal.h index 598554b..f418572 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -62,9 +62,18 @@ # include #endif -#if defined(_POSIX_PATH_MAX) -# define UV__PATH_MAX _POSIX_PATH_MAX -#elif defined(PATH_MAX) +/* + * Define common detection for active Thread Sanitizer + * - clang uses __has_feature(thread_sanitizer) + * - gcc-7+ uses __SANITIZE_THREAD__ + */ +#if defined(__has_feature) +# if __has_feature(thread_sanitizer) +# define __SANITIZE_THREAD__ 1 +# endif +#endif + +#if defined(PATH_MAX) # define UV__PATH_MAX PATH_MAX #else # define UV__PATH_MAX 8192 @@ -106,10 +115,8 @@ int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset); #if defined(__clang__) || \ defined(__GNUC__) || \ defined(__INTEL_COMPILER) -# define UV_DESTRUCTOR(declaration) __attribute__((destructor)) declaration # define UV_UNUSED(declaration) __attribute__((unused)) declaration #else -# define UV_DESTRUCTOR(declaration) declaration # define UV_UNUSED(declaration) declaration #endif @@ -138,7 +145,8 @@ 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 }; /* flags of excluding ifaddr */ @@ -167,11 +175,11 @@ struct uv__stream_queued_fds_s { 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 /* On Linux, uv__nonblock_fcntl() and uv__nonblock_ioctl() do not commute @@ -187,8 +195,7 @@ 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 */ @@ -232,14 +239,15 @@ 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); /* 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); @@ -250,6 +258,7 @@ 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); @@ -265,11 +274,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); +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); @@ -285,12 +297,6 @@ int uv___stream_fd(const uv_stream_t* handle); #define uv__stream_fd(handle) ((handle)->io_watcher.fd) #endif /* defined(__APPLE__) */ -#ifdef O_NONBLOCK -# define UV__F_NONBLOCK O_NONBLOCK -#else -# define UV__F_NONBLOCK 1 -#endif - int uv__make_pipe(int fds[2], int flags); #if defined(__APPLE__) @@ -330,25 +336,35 @@ int uv__getsockpeername(const uv_handle_t* handle, #if defined(__linux__) || \ defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) + defined(__FreeBSD_kernel__) || \ + defined(__DragonFly__) #define HAVE_MMSG 1 struct uv__mmsghdr { struct msghdr msg_hdr; unsigned int msg_len; }; -int uv__recvmmsg(int fd, - struct uv__mmsghdr* mmsg, - unsigned int vlen, - unsigned int flags, - struct timespec* timeout); -int uv__sendmmsg(int fd, - struct uv__mmsghdr* mmsg, - unsigned int vlen, - unsigned int flags); +int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen); +int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen); #else #define HAVE_MMSG 0 #endif +#if defined(__sun) +#if !defined(_POSIX_VERSION) || _POSIX_VERSION < 200809L +size_t strnlen(const char* s, size_t maxlen); +#endif +#endif + +#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 + #endif /* UV_UNIX_INTERNAL_H_ */ diff --git a/src/unix/kqueue.c b/src/unix/kqueue.c index ad09f40..0360551 100644 --- a/src/unix/kqueue.c +++ b/src/unix/kqueue.c @@ -82,7 +82,7 @@ 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__has_forked_with_cfrunloop = 1; + uv__store_relaxed(&uv__has_forked_with_cfrunloop, 1); uv__free(loop->cf_state); loop->cf_state = NULL; } @@ -117,6 +117,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { unsigned int revents; QUEUE* q; uv__io_t* w; + uv_process_t* process; sigset_t* pset; sigset_t set; uint64_t base; @@ -129,6 +130,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int fd; int op; int i; + int user_timeout; + int reset_timeout; if (loop->nfds == 0) { assert(QUEUE_EMPTY(&loop->watcher_queue)); @@ -202,7 +205,21 @@ 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) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + } + for (;; nevents = 0) { + /* 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); + if (timeout != -1) { spec.tv_sec = timeout / 1000; spec.tv_nsec = (timeout % 1000) * 1000000; @@ -228,6 +245,15 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { 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; + } + assert(timeout != -1); return; } @@ -236,6 +262,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (errno != EINTR) abort(); + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + if (timeout == 0) return; @@ -255,6 +286,21 @@ 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) { + QUEUE_FOREACH(q, &loop->process_handles) { + process = 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; @@ -276,6 +322,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (ev->filter == EVFILT_VNODE) { assert(w->events == POLLIN); assert(w->pevents == POLLIN); + uv__metrics_update_idle_time(loop); w->cb(loop, w, ev->fflags); /* XXX always uv__fs_event() */ nevents++; continue; @@ -295,6 +342,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (errno != ENOENT) abort(); } + if ((ev->flags & EV_EOF) && (w->pevents & UV__POLLRDHUP)) + revents |= UV__POLLRDHUP; } if (ev->filter == EV_OOBAND) { @@ -328,25 +377,36 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (ev->flags & EV_ERROR) revents |= POLLERR; - if ((ev->flags & EV_EOF) && (w->pevents & UV__POLLRDHUP)) - revents |= UV__POLLRDHUP; - if (revents == 0) continue; /* 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) + if (w == &loop->signal_io_watcher) { have_signals = 1; - else + } else { + uv__metrics_update_idle_time(loop); w->cb(loop, w, revents); + } nevents++; } - if (have_signals != 0) + if (loop->flags & UV_LOOP_REAP_CHILDREN) { + loop->flags &= ~UV_LOOP_REAP_CHILDREN; + uv__wait_children(loop); + } + + 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; @@ -487,7 +547,7 @@ int uv_fs_event_start(uv_fs_event_t* handle, if (!(statbuf.st_mode & S_IFDIR)) goto fallback; - if (!uv__has_forked_with_cfrunloop) { + if (0 == uv__load_relaxed(&uv__has_forked_with_cfrunloop)) { int r; /* The fallback fd is no longer needed */ uv__close_nocheckstdio(fd); @@ -522,8 +582,9 @@ 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 (!uv__has_forked_with_cfrunloop && handle->cf_cb != NULL) - r = uv__fsevents_close(handle); + if (0 == uv__load_relaxed(&uv__has_forked_with_cfrunloop)) + if (handle->cf_cb != NULL) + r = uv__fsevents_close(handle); #endif if (handle->event_watcher.fd != -1) { diff --git a/src/unix/linux-core.c b/src/unix/linux-core.c index f8d9ff5..23a7daf 100644 --- a/src/unix/linux-core.c +++ b/src/unix/linux-core.c @@ -45,6 +45,10 @@ #define HAVE_IFADDRS_H 1 +# if defined(__ANDROID_API__) && __ANDROID_API__ < 24 +# undef HAVE_IFADDRS_H +#endif + #ifdef __UCLIBC__ # if __UCLIBC_MAJOR__ < 0 && __UCLIBC_MINOR__ < 9 && __UCLIBC_SUBLEVEL__ < 32 # undef HAVE_IFADDRS_H @@ -52,11 +56,7 @@ #endif #ifdef HAVE_IFADDRS_H -# if defined(__ANDROID__) -# include "uv/android-ifaddrs.h" -# else -# include -# endif +# include # include # include # include @@ -82,39 +82,12 @@ static int read_times(FILE* statfile_fp, 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) { - int fd; - - /* It was reported that EPOLL_CLOEXEC is not defined on Android API < 21, - * a.k.a. Lollipop. Since EPOLL_CLOEXEC is an alias for O_CLOEXEC on all - * architectures, we just use that instead. - */ -#if defined(__ANDROID_API__) && __ANDROID_API__ < 21 - fd = -1; - errno = ENOSYS; -#else - fd = epoll_create1(O_CLOEXEC); -#endif - - /* 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; + loop->inotify_fd = -1; loop->inotify_watchers = NULL; - if (fd == -1) - return UV__ERR(errno); - - return 0; + return uv__epoll_init(loop); } @@ -144,331 +117,6 @@ void uv__platform_loop_delete(uv_loop_t* loop) { } -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; - static 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; - - 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; - - for (;;) { - /* 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)) { -#if defined(__ANDROID_API__) && __ANDROID_API__ < 21 - nfds = -1; - errno = ENOSYS; -#else - nfds = epoll_pwait(loop->backend_fd, - events, - ARRAY_SIZE(events), - timeout, - &sigset); -#endif - if (nfds == -1 && errno == ENOSYS) - no_epoll_pwait = 1; - } else { - nfds = epoll_wait(loop->backend_fd, - events, - ARRAY_SIZE(events), - timeout); - if (nfds == -1 && errno == ENOSYS) - 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 (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 (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 - w->cb(loop, w, pe->events); - - nevents++; - } - } - - if (have_signals != 0) - 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; - } -} - uint64_t uv__hrtime(uv_clocktype_t type) { static clock_t fast_clock_id = -1; @@ -483,18 +131,22 @@ uint64_t uv__hrtime(uv_clocktype_t type) { /* TODO(bnoordhuis) Use CLOCK_MONOTONIC_COARSE for UV_CLOCK_PRECISE * when it has microsecond granularity or better (unlikely). */ - if (type == UV_CLOCK_FAST && fast_clock_id == -1) { - if (clock_getres(CLOCK_MONOTONIC_COARSE, &t) == 0 && - t.tv_nsec <= 1 * 1000 * 1000) { - fast_clock_id = CLOCK_MONOTONIC_COARSE; - } else { - fast_clock_id = CLOCK_MONOTONIC; - } - } + 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 (type == UV_CLOCK_FAST) - clock_id = fast_clock_id; + 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. */ @@ -559,22 +211,28 @@ err: return UV_EINVAL; } - 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: r = clock_gettime(CLOCK_MONOTONIC, &now); + 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; + goto retry_clock_gettime; } if (r) @@ -666,35 +324,47 @@ static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci) { } -/* Also reads the CPU frequency on x86. The other architectures only have - * a BogoMIPS field, which may not be very accurate. +/* 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) @@ -713,11 +383,96 @@ static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) { continue; } } -#if defined(__arm__) || defined(__mips__) +#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 @@ -732,18 +487,18 @@ static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) { continue; } } -#else /* !__arm__ && !__mips__ */ +#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__ */ +#endif /* __arm__ || __mips__ || __aarch64__ */ } fclose(fp); -#endif /* __arm__ || __i386__ || __mips__ || __x86_64__ */ +#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 @@ -768,7 +523,8 @@ static int read_times(FILE* statfile_fp, unsigned int numcpus, uv_cpu_info_t* ci) { struct uv_cpu_times_s ts; - uint64_t clock_ticks; + unsigned int ticks; + unsigned int multiplier; uint64_t user; uint64_t nice; uint64_t sys; @@ -779,9 +535,10 @@ static int read_times(FILE* statfile_fp, uint64_t len; char buf[1024]; - clock_ticks = sysconf(_SC_CLK_TCK); - assert(clock_ticks != (uint64_t) -1); - assert(clock_ticks != 0); + ticks = (unsigned int)sysconf(_SC_CLK_TCK); + assert(ticks != (unsigned int) -1); + assert(ticks != 0); + multiplier = ((uint64_t)1000L / ticks); rewind(statfile_fp); @@ -823,11 +580,11 @@ static int read_times(FILE* statfile_fp, &irq)) abort(); - ts.user = clock_ticks * user; - ts.nice = clock_ticks * nice; - ts.sys = clock_ticks * sys; - ts.idle = clock_ticks * idle; - ts.irq = clock_ticks * irq; + 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); @@ -859,6 +616,7 @@ static uint64_t read_cpufreq(unsigned int cpunum) { } +#ifdef HAVE_IFADDRS_H static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING))) return 1; @@ -872,6 +630,7 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { return exclude_type; return !exclude_type; } +#endif int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { #ifndef HAVE_IFADDRS_H @@ -982,41 +741,23 @@ void uv__set_process_title(const char* title) { static uint64_t uv__read_proc_meminfo(const char* what) { uint64_t rc; - ssize_t n; char* p; - int fd; char buf[4096]; /* Large enough to hold all of /proc/meminfo. */ - rc = 0; - fd = uv__open_cloexec("/proc/meminfo", O_RDONLY); - - if (fd < 0) + if (uv__slurp("/proc/meminfo", buf, sizeof(buf))) return 0; - n = read(fd, buf, sizeof(buf) - 1); - - if (n <= 0) - goto out; - - buf[n] = '\0'; p = strstr(buf, what); if (p == NULL) - goto out; + return 0; p += strlen(what); - if (1 != sscanf(p, "%" PRIu64 " kB", &rc)) - goto out; - - rc *= 1024; - -out: - - if (uv__close_nocheckstdio(fd)) - abort(); + rc = 0; + sscanf(p, "%" PRIu64 " kB", &rc); - return rc; + return rc * 1024; } @@ -1024,7 +765,7 @@ uint64_t uv_get_free_memory(void) { struct sysinfo info; uint64_t rc; - rc = uv__read_proc_meminfo("MemFree:"); + rc = uv__read_proc_meminfo("MemAvailable:"); if (rc != 0) return rc; @@ -1054,28 +795,13 @@ uint64_t uv_get_total_memory(void) { static uint64_t uv__read_cgroups_uint64(const char* cgroup, const char* param) { char filename[256]; - uint64_t rc; - int fd; - ssize_t n; char buf[32]; /* Large enough to hold an encoded uint64_t. */ - - snprintf(filename, 256, "/sys/fs/cgroup/%s/%s", cgroup, param); + uint64_t rc; rc = 0; - fd = uv__open_cloexec(filename, O_RDONLY); - - if (fd < 0) - return 0; - - n = read(fd, buf, sizeof(buf) - 1); - - if (n > 0) { - buf[n] = '\0'; + snprintf(filename, sizeof(filename), "/sys/fs/cgroup/%s/%s", cgroup, param); + if (0 == uv__slurp(filename, buf, sizeof(buf))) sscanf(buf, "%" PRIu64, &rc); - } - - if (uv__close_nocheckstdio(fd)) - abort(); return rc; } @@ -1089,3 +815,20 @@ uint64_t uv_get_constrained_memory(void) { */ 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 index 42b601a..c1bd260 100644 --- a/src/unix/linux-inotify.c +++ b/src/unix/linux-inotify.c @@ -178,7 +178,7 @@ static void uv__inotify_read(uv_loop_t* loop, /* needs to be large enough for sizeof(inotify_event) + strlen(path) */ char buf[4096]; - while (1) { + for (;;) { do size = read(loop->inotify_fd, buf, sizeof(buf)); while (size == -1 && errno == EINTR); diff --git a/src/unix/linux-syscalls.c b/src/unix/linux-syscalls.c index 742f26a..5071cd5 100644 --- a/src/unix/linux-syscalls.c +++ b/src/unix/linux-syscalls.c @@ -37,8 +37,6 @@ #ifndef __NR_recvmmsg # if defined(__x86_64__) # define __NR_recvmmsg 299 -# elif defined(__i386__) -# define __NR_recvmmsg 337 # elif defined(__arm__) # define __NR_recvmmsg (UV_SYSCALL_BASE + 365) # endif @@ -47,8 +45,6 @@ #ifndef __NR_sendmmsg # if defined(__x86_64__) # define __NR_sendmmsg 307 -# elif defined(__i386__) -# define __NR_sendmmsg 345 # elif defined(__arm__) # define __NR_sendmmsg (UV_SYSCALL_BASE + 374) # endif @@ -94,6 +90,24 @@ # 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 @@ -128,25 +142,51 @@ struct uv__mmsghdr; -int uv__sendmmsg(int fd, - struct uv__mmsghdr* mmsg, - unsigned int vlen, - unsigned int flags) { -#if defined(__NR_sendmmsg) - return syscall(__NR_sendmmsg, fd, mmsg, vlen, flags); +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, - unsigned int flags, - struct timespec* timeout) { -#if defined(__NR_recvmmsg) - return syscall(__NR_recvmmsg, fd, mmsg, vlen, flags, timeout); +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 @@ -154,26 +194,48 @@ int uv__recvmmsg(int fd, ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset) { -#if defined(__NR_preadv) - return syscall(__NR_preadv, fd, iov, iovcnt, (long)offset, (long)(offset >> 32)); -#else +#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) - return syscall(__NR_pwritev, fd, iov, iovcnt, (long)offset, (long)(offset >> 32)); -#else +#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) +#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 @@ -185,21 +247,18 @@ int uv__statx(int dirfd, int flags, unsigned int mask, struct uv__statx* statxbuf) { - /* __NR_statx make Android box killed by SIGSYS. - * That looks like a seccomp2 sandbox filter rejecting the system call. - */ -#if defined(__NR_statx) && !defined(__ANDROID__) - return syscall(__NR_statx, dirfd, path, flags, mask, statxbuf); -#else +#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) - return syscall(__NR_getrandom, buf, buflen, flags); -#else +#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 index 2e8fa2a..b4d9082 100644 --- a/src/unix/linux-syscalls.h +++ b/src/unix/linux-syscalls.h @@ -22,9 +22,6 @@ #ifndef UV_LINUX_SYSCALL_H_ #define UV_LINUX_SYSCALL_H_ -#undef _GNU_SOURCE -#define _GNU_SOURCE - #include #include #include @@ -64,6 +61,13 @@ struct uv__statx { 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, diff --git a/src/unix/loop.c b/src/unix/loop.c index c2a03d7..a88e71c 100644 --- a/src/unix/loop.c +++ b/src/unix/loop.c @@ -28,6 +28,7 @@ #include int uv_loop_init(uv_loop_t* loop) { + uv__loop_internal_fields_t* lfields; void* saved_data; int err; @@ -36,6 +37,15 @@ int uv_loop_init(uv_loop_t* loop) { memset(loop, 0, sizeof(*loop)); loop->data = saved_data; + lfields = (uv__loop_internal_fields_t*) uv__calloc(1, sizeof(*lfields)); + if (lfields == NULL) + return UV_ENOMEM; + loop->internal_fields = lfields; + + err = uv_mutex_init(&lfields->loop_metrics.lock); + if (err) + goto fail_metrics_mutex_init; + heap_init((struct heap*) &loop->timer_heap); QUEUE_INIT(&loop->wq); QUEUE_INIT(&loop->idle_handles); @@ -66,7 +76,7 @@ int uv_loop_init(uv_loop_t* loop) { err = uv__platform_loop_init(loop); if (err) - return err; + goto fail_platform_init; uv__signal_global_once_init(); err = uv_signal_init(loop, &loop->child_watcher); @@ -106,6 +116,15 @@ fail_rwlock_init: fail_signal_init: uv__platform_loop_delete(loop); +fail_platform_init: + uv_mutex_destroy(&lfields->loop_metrics.lock); + +fail_metrics_mutex_init: + uv__free(lfields); + loop->internal_fields = NULL; + + uv__free(loop->watchers); + loop->nwatchers = 0; return err; } @@ -144,6 +163,8 @@ int uv_loop_fork(uv_loop_t* loop) { void uv__loop_close(uv_loop_t* loop) { + uv__loop_internal_fields_t* lfields; + uv__signal_loop_cleanup(loop); uv__platform_loop_delete(loop); uv__async_stop(loop); @@ -179,10 +200,23 @@ void uv__loop_close(uv_loop_t* loop) { uv__free(loop->watchers); loop->watchers = NULL; loop->nwatchers = 0; + + lfields = uv__get_internal_fields(loop); + uv_mutex_destroy(&lfields->loop_metrics.lock); + uv__free(lfields); + loop->internal_fields = NULL; } int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) { + uv__loop_internal_fields_t* lfields; + + lfields = uv__get_internal_fields(loop); + if (option == UV_METRICS_IDLE_TIME) { + lfields->flags |= UV_METRICS_IDLE_TIME; + return 0; + } + if (option != UV_LOOP_BLOCK_SIGNAL) return UV_ENOSYS; diff --git a/src/unix/no-proctitle.c b/src/unix/no-proctitle.c index 165740c..32aa0af 100644 --- a/src/unix/no-proctitle.c +++ b/src/unix/no-proctitle.c @@ -29,6 +29,9 @@ char** uv_setup_args(int argc, char** argv) { return argv; } +void uv__process_title_cleanup(void) { +} + int uv_set_process_title(const char* title) { return 0; } diff --git a/src/unix/openbsd.c b/src/unix/openbsd.c index 713b6c6..f32a94d 100644 --- a/src/unix/openbsd.c +++ b/src/unix/openbsd.c @@ -61,7 +61,6 @@ void uv_loadavg(double avg[3]) { int uv_exepath(char* buffer, size_t* size) { int mib[4]; char **argsbuf = NULL; - char **argsbuf_tmp; size_t argsbuf_size = 100U; size_t exepath_size; pid_t mypid; @@ -73,10 +72,9 @@ int uv_exepath(char* buffer, size_t* size) { mypid = getpid(); for (;;) { err = UV_ENOMEM; - argsbuf_tmp = uv__realloc(argsbuf, argsbuf_size); - if (argsbuf_tmp == NULL) + argsbuf = uv__reallocf(argsbuf, argsbuf_size); + if (argsbuf == NULL) goto out; - argsbuf = argsbuf_tmp; mib[0] = CTL_KERN; mib[1] = KERN_PROC_ARGS; mib[2] = mypid; diff --git a/src/unix/os390-proctitle.c b/src/unix/os390-proctitle.c new file mode 100644 index 0000000..ccda97c --- /dev/null +++ b/src/unix/os390-proctitle.c @@ -0,0 +1,136 @@ +/* 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 "internal.h" + +#include +#include + +static uv_mutex_t process_title_mutex; +static uv_once_t process_title_mutex_once = UV_ONCE_INIT; +static char* process_title = NULL; +static void* args_mem = NULL; + + +static void init_process_title_mutex_once(void) { + uv_mutex_init(&process_title_mutex); +} + + +char** uv_setup_args(int argc, char** argv) { + char** new_argv; + size_t size; + char* s; + int i; + + if (argc <= 0) + return argv; + + /* Calculate how much memory we need for the argv strings. */ + size = 0; + for (i = 0; i < argc; i++) + size += strlen(argv[i]) + 1; + + /* Add space for the argv pointers. */ + size += (argc + 1) * sizeof(char*); + + new_argv = uv__malloc(size); + if (new_argv == NULL) + return argv; + + /* Copy over the strings and set up the pointer table. */ + s = (char*) &new_argv[argc + 1]; + for (i = 0; i < argc; i++) { + size = strlen(argv[i]) + 1; + memcpy(s, argv[i], size); + new_argv[i] = s; + s += size; + } + new_argv[i] = NULL; + + args_mem = new_argv; + process_title = uv__strdup(argv[0]); + + return new_argv; +} + + +int uv_set_process_title(const char* title) { + char* new_title; + + /* If uv_setup_args wasn't called or failed, we can't continue. */ + if (args_mem == NULL) + return UV_ENOBUFS; + + /* We cannot free this pointer when libuv shuts down, + * the process may still be using it. + */ + new_title = uv__strdup(title); + if (new_title == NULL) + return UV_ENOMEM; + + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + + if (process_title != NULL) + uv__free(process_title); + + process_title = new_title; + + uv_mutex_unlock(&process_title_mutex); + + return 0; +} + + +int uv_get_process_title(char* buffer, size_t size) { + size_t len; + + if (buffer == NULL || size == 0) + return UV_EINVAL; + + /* If uv_setup_args wasn't called or failed, we can't continue. */ + if (args_mem == NULL || process_title == NULL) + return UV_ENOBUFS; + + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + + len = strlen(process_title); + + if (size <= len) { + uv_mutex_unlock(&process_title_mutex); + return UV_ENOBUFS; + } + + strcpy(buffer, process_title); + + uv_mutex_unlock(&process_title_mutex); + + return 0; +} + + +void uv__process_title_cleanup(void) { + uv__free(args_mem); /* Keep valgrind happy. */ + args_mem = NULL; +} diff --git a/src/unix/os390-syscalls.c b/src/unix/os390-syscalls.c index 4a926c7..a741127 100644 --- a/src/unix/os390-syscalls.c +++ b/src/unix/os390-syscalls.c @@ -27,13 +27,6 @@ #include #include -#define CW_INTRPT 1 -#define CW_CONDVAR 32 - -#pragma linkage(BPX4CTW, OS) -#pragma linkage(BPX1CTW, OS) - -static int number_of_epolls; static QUEUE global_epoll_queue; static uv_mutex_t global_epoll_lock; static uv_once_t once = UV_ONCE_INIT; @@ -56,7 +49,7 @@ int scandir(const char* maindir, struct dirent*** namelist, if (!mdir) return -1; - while (1) { + for (;;) { dirent = readdir(mdir); if (!dirent) break; @@ -128,7 +121,7 @@ static void maybe_resize(uv__os390_epoll* lst, unsigned int len) { } newsize = next_power_of_two(len); - newlst = uv__realloc(lst->items, newsize * sizeof(lst->items[0])); + newlst = uv__reallocf(lst->items, newsize * sizeof(lst->items[0])); if (newlst == NULL) abort(); @@ -143,6 +136,11 @@ static void maybe_resize(uv__os390_epoll* lst, unsigned int len) { } +void uv__os390_cleanup(void) { + msgctl(uv_backend_fd(uv_default_loop()), IPC_RMID, NULL); +} + + static void init_message_queue(uv__os390_epoll* lst) { struct { long int header; @@ -278,6 +276,8 @@ int epoll_ctl(uv__os390_epoll* lst, return 0; } +#define EP_MAX_PFDS (ULONG_MAX / sizeof(struct pollfd)) +#define EP_MAX_EVENTS (INT_MAX / sizeof(struct epoll_event)) int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events, int maxevents, int timeout) { @@ -286,18 +286,41 @@ int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events, int pollret; int reventcount; int nevents; + struct pollfd msg_fd; + int i; + + if (!lst || !lst->items || !events) { + errno = EFAULT; + return -1; + } + + if (lst->size > EP_MAX_PFDS) { + errno = EINVAL; + return -1; + } + + if (maxevents <= 0 || maxevents > EP_MAX_EVENTS) { + errno = EINVAL; + return -1; + } - _SET_FDS_MSGS(size, 1, lst->size - 1); + if (lst->size > 0) + _SET_FDS_MSGS(size, 1, lst->size - 1); + else + _SET_FDS_MSGS(size, 0, 0); pfds = lst->items; pollret = poll(pfds, size, timeout); if (pollret <= 0) return pollret; + assert(lst->size > 0); + pollret = _NFDS(pollret) + _NMSGS(pollret); reventcount = 0; nevents = 0; - for (int i = 0; + msg_fd = pfds[lst->size - 1]; + for (i = 0; i < lst->size && i < maxevents && reventcount < pollret; ++i) { struct epoll_event ev; struct pollfd* pfd; @@ -308,6 +331,7 @@ 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)) @@ -317,6 +341,10 @@ int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events, events[nevents++] = ev; } + if (msg_fd.revents != 0 && msg_fd.fd != -1) + if (i == lst->size) + events[nevents - 1].is_msg = 1; + return nevents; } @@ -352,44 +380,6 @@ void epoll_queue_close(uv__os390_epoll* lst) { } -int nanosleep(const struct timespec* req, struct timespec* rem) { - unsigned nano; - unsigned seconds; - unsigned events; - unsigned secrem; - unsigned nanorem; - int rv; - int err; - int rsn; - - nano = (int)req->tv_nsec; - seconds = req->tv_sec; - events = CW_CONDVAR | CW_INTRPT; - secrem = 0; - nanorem = 0; - -#if defined(_LP64) - BPX4CTW(&seconds, &nano, &events, &secrem, &nanorem, &rv, &err, &rsn); -#else - BPX1CTW(&seconds, &nano, &events, &secrem, &nanorem, &rv, &err, &rsn); -#endif - - /* Don't clobber errno unless BPX1CTW/BPX4CTW errored. - * Don't leak EAGAIN, that just means the timeout expired. - */ - if (rv == -1) - if (err != EAGAIN) - errno = err; - - if (rem != NULL && (rv == 0 || err == EINTR || err == EAGAIN)) { - rem->tv_nsec = nanorem; - rem->tv_sec = secrem; - } - - return rv; -} - - char* mkdtemp(char* path) { static const char* tempchars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; @@ -519,10 +509,26 @@ ssize_t os390_readlink(const char* path, char* buf, size_t len) { } -size_t strnlen(const char* str, size_t maxlen) { - char* p = memchr(str, 0, maxlen); - if (p == NULL) - return maxlen; - else - return p - str; +int sem_init(UV_PLATFORM_SEM_T* semid, int pshared, unsigned int value) { + UNREACHABLE(); +} + + +int sem_destroy(UV_PLATFORM_SEM_T* semid) { + UNREACHABLE(); +} + + +int sem_post(UV_PLATFORM_SEM_T* semid) { + UNREACHABLE(); +} + + +int sem_trywait(UV_PLATFORM_SEM_T* semid) { + UNREACHABLE(); +} + + +int sem_wait(UV_PLATFORM_SEM_T* semid) { + UNREACHABLE(); } diff --git a/src/unix/os390-syscalls.h b/src/unix/os390-syscalls.h index ea59910..9f50417 100644 --- a/src/unix/os390-syscalls.h +++ b/src/unix/os390-syscalls.h @@ -28,6 +28,7 @@ #include #include #include +#include "zos-base.h" #define EPOLL_CTL_ADD 1 #define EPOLL_CTL_DEL 2 @@ -40,6 +41,7 @@ struct epoll_event { int events; int fd; + int is_msg; }; typedef struct { @@ -56,7 +58,6 @@ int epoll_wait(uv__os390_epoll* ep, struct epoll_event *events, int maxevents, i int epoll_file_close(int fd); /* utility functions */ -int nanosleep(const struct timespec* req, struct timespec* rem); int scandir(const char* maindir, struct dirent*** namelist, int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, @@ -64,5 +65,11 @@ int scandir(const char* maindir, struct dirent*** namelist, char *mkdtemp(char* path); ssize_t os390_readlink(const char* path, char* buf, size_t len); size_t strnlen(const char* str, size_t maxlen); +int sem_init(UV_PLATFORM_SEM_T* semid, int pshared, unsigned int value); +int sem_destroy(UV_PLATFORM_SEM_T* semid); +int sem_post(UV_PLATFORM_SEM_T* semid); +int sem_trywait(UV_PLATFORM_SEM_T* semid); +int sem_wait(UV_PLATFORM_SEM_T* semid); +void uv__os390_cleanup(void); #endif /* UV_OS390_SYSCALL_H_ */ diff --git a/src/unix/os390.c b/src/unix/os390.c index a730500..bf0448b 100644 --- a/src/unix/os390.c +++ b/src/unix/os390.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include "zos-base.h" #if defined(__clang__) #include "csrsic.h" #else @@ -61,12 +63,6 @@ /* Address of the rsm control and enumeration area. */ #define CVTRCEP_OFFSET 0x490 -/* - Number of frames currently available to system. - Excluded are frames backing perm storage, frames offline, and bad frames. -*/ -#define RCEPOOL_OFFSET 0x004 - /* Total number of frames currently on all available frame queues. */ #define RCEAFC_OFFSET 0x088 @@ -144,102 +140,8 @@ uint64_t uv__hrtime(uv_clocktype_t type) { } -/* - Get the exe path using the thread entry information - in the address space. -*/ -static int getexe(const int pid, char* buf, size_t len) { - struct { - int pid; - int thid[2]; - char accesspid; - char accessthid; - char asid[2]; - char loginname[8]; - char flag; - char len; - } Input_data; - - union { - struct { - char gthb[4]; - int pid; - int thid[2]; - char accesspid; - char accessthid[3]; - int lenused; - int offsetProcess; - int offsetConTTY; - int offsetPath; - int offsetCommand; - int offsetFileData; - int offsetThread; - } Output_data; - char buf[2048]; - } Output_buf; - - struct Output_path_type { - char gthe[4]; - short int len; - char path[1024]; - }; - - int Input_length; - int Output_length; - void* Input_address; - void* Output_address; - struct Output_path_type* Output_path; - int rv; - int rc; - int rsn; - - Input_length = PGTH_LEN; - Output_length = sizeof(Output_buf); - Output_address = &Output_buf; - Input_address = &Input_data; - memset(&Input_data, 0, sizeof Input_data); - Input_data.flag |= PGTHAPATH; - Input_data.pid = pid; - Input_data.accesspid = PGTH_CURRENT; - -#ifdef _LP64 - BPX4GTH(&Input_length, - &Input_address, - &Output_length, - &Output_address, - &rv, - &rc, - &rsn); -#else - BPX1GTH(&Input_length, - &Input_address, - &Output_length, - &Output_address, - &rv, - &rc, - &rsn); -#endif - - if (rv == -1) { - errno = rc; - return -1; - } - - /* Check highest byte to ensure data availability */ - assert(((Output_buf.Output_data.offsetPath >>24) & 0xFF) == 'A'); - - /* Get the offset from the lowest 3 bytes */ - Output_path = (struct Output_path_type*) ((char*) (&Output_buf) + - (Output_buf.Output_data.offsetPath & 0x00FFFFFF)); - - if (Output_path->len >= len) { - errno = ENOBUFS; - return -1; - } - - uv__strscpy(buf, Output_path->path, len); - - return 0; +static int getexe(char* buf, size_t len) { + return uv__strscpy(buf, __getargv()[0], len); } @@ -254,81 +156,16 @@ static int getexe(const int pid, char* buf, size_t len) { int uv_exepath(char* buffer, size_t* size) { int res; char args[PATH_MAX]; - char abspath[PATH_MAX]; - size_t abspath_size; int pid; if (buffer == NULL || size == NULL || *size == 0) return UV_EINVAL; - pid = getpid(); - res = getexe(pid, args, sizeof(args)); + res = getexe(args, sizeof(args)); if (res < 0) return UV_EINVAL; - /* - * Possibilities for args: - * i) an absolute path such as: /home/user/myprojects/nodejs/node - * ii) a relative path such as: ./node or ../myprojects/nodejs/node - * iii) a bare filename such as "node", after exporting PATH variable - * to its location. - */ - - /* Case i) and ii) absolute or relative paths */ - if (strchr(args, '/') != NULL) { - if (realpath(args, abspath) != abspath) - return UV__ERR(errno); - - abspath_size = strlen(abspath); - - *size -= 1; - if (*size > abspath_size) - *size = abspath_size; - - memcpy(buffer, abspath, *size); - buffer[*size] = '\0'; - - return 0; - } else { - /* Case iii). Search PATH environment variable */ - char trypath[PATH_MAX]; - char* clonedpath = NULL; - char* token = NULL; - char* path = getenv("PATH"); - - if (path == NULL) - return UV_EINVAL; - - clonedpath = uv__strdup(path); - if (clonedpath == NULL) - return UV_ENOMEM; - - token = strtok(clonedpath, ":"); - while (token != NULL) { - snprintf(trypath, sizeof(trypath) - 1, "%s/%s", token, args); - if (realpath(trypath, abspath) == abspath) { - /* Check the match is executable */ - if (access(abspath, X_OK) == 0) { - abspath_size = strlen(abspath); - - *size -= 1; - if (*size > abspath_size) - *size = abspath_size; - - memcpy(buffer, abspath, *size); - buffer[*size] = '\0'; - - uv__free(clonedpath); - return 0; - } - } - token = strtok(NULL, ":"); - } - uv__free(clonedpath); - - /* Out of tokens (path entries), and no match found */ - return UV_EINVAL; - } + return uv__search_path(args, buffer, size); } @@ -339,25 +176,25 @@ uint64_t uv_get_free_memory(void) { data_area_ptr rcep = {0}; cvt.assign = *(data_area_ptr_assign_type*)(CVT_PTR); rcep.assign = *(data_area_ptr_assign_type*)(cvt.deref + CVTRCEP_OFFSET); - freeram = *((uint64_t*)(rcep.deref + RCEAFC_OFFSET)) * 4; + freeram = (uint64_t)*((uint32_t*)(rcep.deref + RCEAFC_OFFSET)) * 4096; return freeram; } uint64_t uv_get_total_memory(void) { - uint64_t totalram; - - data_area_ptr cvt = {0}; - data_area_ptr rcep = {0}; - cvt.assign = *(data_area_ptr_assign_type*)(CVT_PTR); - rcep.assign = *(data_area_ptr_assign_type*)(cvt.deref + CVTRCEP_OFFSET); - totalram = *((uint64_t*)(rcep.deref + RCEPOOL_OFFSET)) * 4; - return totalram; + /* Use CVTRLSTG to get the size of actual real storage online at IPL in K. */ + return (uint64_t)((int)((char *__ptr32 *__ptr32 *)0)[4][214]) * 1024; } uint64_t uv_get_constrained_memory(void) { - return 0; /* Memory constraints are unknown. */ + struct rlimit rl; + + /* RLIMIT_MEMLIMIT return value is in megabytes rather than bytes. */ + if (getrlimit(RLIMIT_MEMLIMIT, &rl) == 0) + return rl.rlim_cur * 1024 * 1024; + + return 0; /* There is no memory limit set. */ } @@ -797,6 +634,10 @@ static int os390_message_queue_handler(uv__os390_epoll* ep) { /* Some event that we are not interested in. */ return 0; + /* `__rfim_utok` is treated as text when it should be treated as binary while + * running in ASCII mode, resulting in an unwanted autoconversion. + */ + __a2e_l(msg.__rfim_utok, sizeof(msg.__rfim_utok)); handle = *(uv_fs_event_t**)(msg.__rfim_utok); handle->cb(handle, uv__basename_r(handle->path), events, 0); return 1; @@ -818,6 +659,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int fd; int op; int i; + int user_timeout; + int reset_timeout; if (loop->nfds == 0) { assert(QUEUE_EMPTY(&loop->watcher_queue)); @@ -870,8 +713,22 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { real_timeout = timeout; int nevents = 0; + if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + } + nfds = 0; 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); + if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout) timeout = max_safe_timeout; @@ -887,12 +744,21 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (nfds == 0) { assert(timeout != -1); - if (timeout > 0) { - timeout = real_timeout - timeout; - continue; + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; } - return; + 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) { @@ -900,6 +766,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (errno != EINTR) abort(); + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + if (timeout == -1) continue; @@ -923,7 +794,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { continue; ep = loop->ep; - if (fd == ep->msg_queue) { + if (pe->is_msg) { os390_message_queue_handler(ep); continue; } @@ -954,6 +825,7 @@ 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); nevents++; } @@ -961,6 +833,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { loop->watchers[loop->nwatchers] = NULL; loop->watchers[loop->nwatchers + 1] = NULL; + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + if (nevents != 0) { if (nfds == ARRAY_SIZE(events) && --count != 0) { /* Poll for more events but don't block this time. */ @@ -987,9 +864,6 @@ update_timeout: } } -void uv__set_process_title(const char* title) { - /* do nothing */ -} int uv__io_fork(uv_loop_t* loop) { /* diff --git a/src/unix/pipe.c b/src/unix/pipe.c index 040d578..fcc2cba 100644 --- a/src/unix/pipe.c +++ b/src/unix/pipe.c @@ -91,7 +91,7 @@ err_socket: } -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; @@ -379,3 +379,57 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) { return r != -1 ? 0 : UV__ERR(errno); } + + +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__) + int flags = O_CLOEXEC; + + if ((read_flags & UV_NONBLOCK_PIPE) && (write_flags & UV_NONBLOCK_PIPE)) + flags |= UV_FS_O_NONBLOCK; + + if (pipe2(temp, flags)) + return UV__ERR(errno); + + if (flags & UV_FS_O_NONBLOCK) { + fds[0] = temp[0]; + fds[1] = temp[1]; + return 0; + } +#else + if (pipe(temp)) + return UV__ERR(errno); + + if ((err = uv__cloexec(temp[0], 1))) + goto fail; + + if ((err = uv__cloexec(temp[1], 1))) + goto fail; +#endif + + if (read_flags & UV_NONBLOCK_PIPE) + if ((err = uv__nonblock(temp[0], 1))) + goto fail; + + if (write_flags & UV_NONBLOCK_PIPE) + if ((err = uv__nonblock(temp[1], 1))) + goto fail; + + fds[0] = temp[0]; + fds[1] = temp[1]; + return 0; + +fail: + uv__close(temp[0]); + uv__close(temp[1]); + return err; +} + + +int uv__make_pipe(int fds[2], int flags) { + return uv_pipe(fds, + flags & UV_NONBLOCK_PIPE, + flags & UV_NONBLOCK_PIPE); +} diff --git a/src/unix/poll.c b/src/unix/poll.c index 3d5022b..7a12e2d 100644 --- a/src/unix/poll.c +++ b/src/unix/poll.c @@ -79,9 +79,10 @@ int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) { * Workaround for e.g. kqueue fds not supporting ioctls. */ err = uv__nonblock(fd, 1); +#if UV__NONBLOCK_IS_IOCTL if (err == UV_ENOTTY) - if (uv__nonblock == uv__nonblock_ioctl) - err = uv__nonblock_fcntl(fd, 1); + err = uv__nonblock_fcntl(fd, 1); +#endif if (err) return err; @@ -116,12 +117,21 @@ int uv_poll_stop(uv_poll_t* handle) { int uv_poll_start(uv_poll_t* handle, int pevents, uv_poll_cb poll_cb) { + uv__io_t** watchers; + uv__io_t* w; int events; assert((pevents & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT | UV_PRIORITIZED)) == 0); assert(!uv__is_closing(handle)); + watchers = handle->loop->watchers; + w = &handle->io_watcher; + + if (uv__fd_exists(handle->loop, w->fd)) + if (watchers[w->fd] != w) + return UV_EEXIST; + uv__poll_stop(handle); if (pevents == 0) diff --git a/src/unix/posix-poll.c b/src/unix/posix-poll.c index a3b9f21..0f4bf93 100644 --- a/src/unix/posix-poll.c +++ b/src/unix/posix-poll.c @@ -61,7 +61,7 @@ static void uv__pollfds_maybe_resize(uv_loop_t* loop) { return; n = loop->poll_fds_size ? loop->poll_fds_size * 2 : 64; - p = uv__realloc(loop->poll_fds, n * sizeof(*loop->poll_fds)); + p = uv__reallocf(loop->poll_fds, n * sizeof(*loop->poll_fds)); if (p == NULL) abort(); @@ -144,6 +144,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int have_signals; struct pollfd* pe; int fd; + int user_timeout; + int reset_timeout; if (loop->nfds == 0) { assert(QUEUE_EMPTY(&loop->watcher_queue)); @@ -177,11 +179,25 @@ 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) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + } + /* Loop calls to poll() and processing of results. If we get some * results from poll() but they turn out not to be interesting to * our caller then we need to loop around and poll() again. */ 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); + if (pset != NULL) if (pthread_sigmask(SIG_BLOCK, pset, NULL)) abort(); @@ -197,6 +213,15 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { 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; + } + assert(timeout != -1); return; } @@ -205,6 +230,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (errno != EINTR) abort(); + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + if (timeout == -1) continue; @@ -254,6 +284,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (w == &loop->signal_io_watcher) { have_signals = 1; } else { + uv__metrics_update_idle_time(loop); w->cb(loop, w, pe->revents); } @@ -261,8 +292,15 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { } } - if (have_signals != 0) + 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->poll_fds_iterating = 0; diff --git a/src/unix/process.c b/src/unix/process.c index b021aae..8802e4f 100644 --- a/src/unix/process.c +++ b/src/unix/process.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #include @@ -34,8 +36,21 @@ #include #if defined(__APPLE__) && !TARGET_OS_IPHONE +# 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 @@ -44,22 +59,40 @@ extern char **environ; # include #endif +#if defined(__MVS__) +# include "zos-base.h" +#endif + +#if defined(__APPLE__) || \ + defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__NetBSD__) || \ + defined(__OpenBSD__) +#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); +} +#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; - assert(signum == SIGCHLD); - QUEUE_INIT(&pending); - loop = handle->loop; h = &loop->process_handles; q = QUEUE_HEAD(h); @@ -67,19 +100,33 @@ static void uv__chld(uv_signal_t* handle, int signum) { process = QUEUE_DATA(q, uv_process_t, queue); q = QUEUE_NEXT(q); +#ifndef UV_USE_SIGCHLD + if ((process->flags & UV_HANDLE_REAP) == 0) + continue; + options = 0; + process->flags &= ~UV_HANDLE_REAP; +#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); @@ -111,68 +158,6 @@ static void uv__chld(uv_signal_t* handle, int signum) { assert(QUEUE_EMPTY(&pending)); } - -static int uv__make_socketpair(int fds[2]) { -#if defined(__FreeBSD__) || defined(__linux__) - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds)) - return UV__ERR(errno); - - return 0; -#else - int err; - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) - return UV__ERR(errno); - - err = uv__cloexec(fds[0], 1); - if (err == 0) - err = uv__cloexec(fds[1], 1); - - if (err != 0) { - uv__close(fds[0]); - uv__close(fds[1]); - return UV__ERR(errno); - } - - return 0; -#endif -} - - -int uv__make_pipe(int fds[2], int flags) { -#if defined(__FreeBSD__) || defined(__linux__) - if (pipe2(fds, flags | O_CLOEXEC)) - return UV__ERR(errno); - - return 0; -#else - if (pipe(fds)) - return UV__ERR(errno); - - if (uv__cloexec(fds[0], 1)) - goto fail; - - if (uv__cloexec(fds[1], 1)) - goto fail; - - if (flags & UV__F_NONBLOCK) { - if (uv__nonblock(fds[0], 1)) - goto fail; - - if (uv__nonblock(fds[1], 1)) - goto fail; - } - - return 0; - -fail: - uv__close(fds[0]); - uv__close(fds[1]); - return UV__ERR(errno); -#endif -} - - /* * Used for initializing stdio streams like options.stdin_stream. Returns * zero on success. See also the cleanup section in uv_spawn(). @@ -192,7 +177,7 @@ static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) { if (container->data.stream->type != UV_NAMED_PIPE) return UV_EINVAL; else - return uv__make_socketpair(fds); + return uv_socketpair(SOCK_STREAM, 0, fds, 0, 0); case UV_INHERIT_FD: case UV_INHERIT_STREAM: @@ -252,10 +237,14 @@ 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 */ + /* 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); +} + - assert(n == sizeof(val)); +static void uv__write_errno(int error_fd) { + uv__write_int(error_fd, UV__ERR(errno)); } @@ -268,13 +257,32 @@ static void uv__process_child_init(const uv_process_options_t* options, int stdio_count, int (*pipes)[2], int error_fd) { - sigset_t set; + sigset_t signewset; int close_fd; int use_fd; - int err; int fd; int n; + /* Reset signal disposition first. Use a hard-coded limit because NSIG is not + * fixed on Linux: it's either 32, 34 or 64, depending on whether RT signals + * are enabled. We are not allowed to touch RT signal handlers, glibc uses + * them internally. + */ + for (n = 1; n < 32; n += 1) { + if (n == SIGKILL || n == SIGSTOP) + continue; /* Can't be changed. */ + +#if defined(__HAIKU__) + if (n == SIGKILLTHR) + continue; /* Can't be changed. */ +#endif + + if (SIG_ERR != signal(n, SIG_DFL)) + continue; + + uv__write_errno(error_fd); + } + if (options->flags & UV_PROCESS_DETACHED) setsid(); @@ -286,62 +294,62 @@ 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); - if (pipes[fd][1] == -1) { - uv__write_int(error_fd, UV__ERR(errno)); - _exit(127); - } +#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; - if (use_fd < 0) { - uv__write_int(error_fd, UV__ERR(errno)); - _exit(127); - } + if (use_fd < 0) + uv__write_errno(error_fd); } } - 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_int(error_fd, UV__ERR(errno)); - _exit(127); } - if (fd <= 2) + if (fd == -1) + uv__write_errno(error_fd); + + 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_int(error_fd, UV__ERR(errno)); - _exit(127); - } + if (options->cwd != NULL && chdir(options->cwd)) + uv__write_errno(error_fd); if (options->flags & (UV_PROCESS_SETUID | UV_PROCESS_SETGID)) { /* When dropping privileges from root, the `setgroups` call will @@ -354,56 +362,578 @@ static void uv__process_child_init(const uv_process_options_t* options, SAVE_ERRNO(setgroups(0, NULL)); } - if ((options->flags & UV_PROCESS_SETGID) && setgid(options->gid)) { - uv__write_int(error_fd, UV__ERR(errno)); - _exit(127); + if ((options->flags & UV_PROCESS_SETGID) && setgid(options->gid)) + uv__write_errno(error_fd); + + if ((options->flags & UV_PROCESS_SETUID) && setuid(options->uid)) + uv__write_errno(error_fd); + + if (options->env != NULL) + environ = options->env; + + /* Reset signal mask just before exec. */ + sigemptyset(&signewset); + if (sigprocmask(SIG_SETMASK, &signewset, NULL) != 0) + abort(); + +#ifdef __MVS__ + execvpe(options->file, options->args, environ); +#else + execvp(options->file, options->args); +#endif + + uv__write_errno(error_fd); +} +#endif + + +#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; } - if ((options->flags & UV_PROCESS_SETUID) && setuid(options->uid)) { - uv__write_int(error_fd, UV__ERR(errno)); - _exit(127); + /* 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; - if (options->env != NULL) { - environ = options->env; + /* 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; } - /* Reset signal disposition. Use a hard-coded limit because NSIG - * is not fixed on Linux: it's either 32, 34 or 64, depending on - * whether RT signals are enabled. We are not allowed to touch - * RT signal handlers, glibc uses them internally. - */ - for (n = 1; n < 32; n += 1) { - if (n == SIGKILL || n == SIGSTOP) - continue; /* Can't be changed. */ + /* Set the current working directory if requested */ + if (options->cwd != NULL) { + if (posix_spawn_fncs->file_actions.addchdir_np == NULL) { + err = ENOSYS; + goto error; + } -#if defined(__HAIKU__) - if (n == SIGKILLTHR) - continue; /* Can't be changed. */ -#endif + err = posix_spawn_fncs->file_actions.addchdir_np(actions, options->cwd); + if (err != 0) + goto error; + } - if (SIG_ERR != signal(n, SIG_DFL)) + /* 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; + } + } - uv__write_int(error_fd, UV__ERR(errno)); - _exit(127); + 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; + } } - /* Reset signal mask. */ - sigemptyset(&set); - err = pthread_sigmask(SIG_SETMASK, &set, NULL); + 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 overriden + * by options->env */ + char** env = environ; + if (options->env != NULL) + env = options->env; + + /* If options->file contains a slash, posix_spawn/posix_spawnp behave + * the same, and don't involve PATH resolution at all. Otherwise, if + * options->file does not include a slash, but no custom environment is + * to be used, the environment used for path resolution as well for the + * child process is that of the parent process, so posix_spawnp is the + * way to go. */ + if (strchr(options->file, '/') != NULL || options->env == NULL) { + do + err = posix_spawnp(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(options->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 options->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) { - uv__write_int(error_fd, UV__ERR(err)); - _exit(127); + (void) posix_spawnattr_destroy(&attrs); + goto error; } - execvp(options->file, options->args); - uv__write_int(error_fd, UV__ERR(errno)); - _exit(127); + /* 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 == -1) { + /* Failed to fork */ + return UV__ERR(errno); + } + + 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(); + + /* 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; +} int uv_spawn(uv_loop_t* loop, uv_process_t* process, @@ -412,16 +942,13 @@ int uv_spawn(uv_loop_t* loop, /* fork is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED. */ return UV_ENOSYS; #else - 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 | @@ -434,6 +961,7 @@ int uv_spawn(uv_loop_t* loop, uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS); QUEUE_INIT(&process->queue); + process->status = 0; stdio_count = options->stdio_count; if (stdio_count < 3) @@ -458,75 +986,42 @@ 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); - pid = fork(); - - if (pid == -1) { - err = UV__ERR(errno); - uv_rwlock_wrunlock(&loop->cloexec_lock); - uv__close(signal_pipe[0]); - uv__close(signal_pipe[1]); - goto error; - } - - if (pid == 0) { - uv__process_child_init(options, stdio_count, pipes, signal_pipe[1]); - abort(); - } - - /* Release lock in parent process */ - uv_rwlock_wrunlock(&loop->cloexec_lock); - uv__close(signal_pipe[1]); + /* Spawn the child */ + exec_errorno = uv__spawn_and_init_child(loop, options, stdio_count, pipes, &pid); - process->status = 0; - exec_errorno = 0; - do - r = read(signal_pipe[0], &exec_errorno, sizeof(exec_errorno)); - while (r == -1 && errno == EINTR); +#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 - 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(); + /* 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; + } +#endif - uv__close_nocheckstdio(signal_pipe[0]); + process->pid = pid; + process->exit_cb = options->exit_cb; + QUEUE_INSERT_TAIL(&loop->process_handles, &process->queue); + uv__handle_start(process); + } for (i = 0; i < options->stdio_count; i++) { err = uv__process_open_stream(options->stdio + i, pipes[i]); @@ -539,15 +1034,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); diff --git a/src/unix/proctitle.c b/src/unix/proctitle.c index d124d3c..9d1f00d 100644 --- a/src/unix/proctitle.c +++ b/src/unix/proctitle.c @@ -84,10 +84,7 @@ char** uv_setup_args(int argc, char** argv) { } new_argv[i] = NULL; - /* argv is not adjacent on z/os, we use just argv[0] on that platform. */ -#ifndef __MVS__ pt.cap = argv[i - 1] + size - argv[0]; -#endif args_mem = new_argv; process_title = pt; @@ -100,6 +97,10 @@ int uv_set_process_title(const char* title) { struct uv__process_title* pt; size_t len; + /* If uv_setup_args wasn't called or failed, we can't continue. */ + if (args_mem == NULL) + return UV_ENOBUFS; + pt = &process_title; len = strlen(title); @@ -115,6 +116,7 @@ int uv_set_process_title(const char* title) { memcpy(pt->str, title, len); memset(pt->str + len, '\0', pt->cap - len); pt->len = len; + uv__set_process_title(pt->str); uv_mutex_unlock(&process_title_mutex); @@ -126,6 +128,10 @@ int uv_get_process_title(char* buffer, size_t size) { if (buffer == NULL || size == 0) return UV_EINVAL; + /* If uv_setup_args wasn't called or failed, we can't continue. */ + if (args_mem == NULL) + return UV_ENOBUFS; + uv_once(&process_title_mutex_once, init_process_title_mutex_once); uv_mutex_lock(&process_title_mutex); @@ -145,7 +151,7 @@ int uv_get_process_title(char* buffer, size_t size) { } -UV_DESTRUCTOR(static void free_args_mem(void)) { +void uv__process_title_cleanup(void) { uv__free(args_mem); /* Keep valgrind happy. */ args_mem = NULL; } diff --git a/src/unix/pthread-fixes.c b/src/unix/pthread-fixes.c index fb17995..022d79c 100644 --- a/src/unix/pthread-fixes.c +++ b/src/unix/pthread-fixes.c @@ -30,6 +30,8 @@ */ /* Android versions < 4.1 have a broken pthread_sigmask. */ +#include "uv-common.h" + #include #include #include @@ -38,13 +40,13 @@ int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset) { static int workaround; int err; - if (workaround) { + 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) { - workaround = 1; + uv__store_relaxed(&workaround, 1); return 0; } else { return -1; diff --git a/src/unix/qnx.c b/src/unix/qnx.c new file mode 100644 index 0000000..ca148d3 --- /dev/null +++ b/src/unix/qnx.c @@ -0,0 +1,137 @@ +/* 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 +#include +#include +#include +#include + +static void +get_mem_info(uint64_t* totalmem, uint64_t* freemem) { + mem_info_t msg; + + memset(&msg, 0, sizeof(msg)); + msg.i.type = _MEM_INFO; + msg.i.fd = -1; + + if (MsgSend(MEMMGR_COID, &msg.i, sizeof(msg.i), &msg.o, sizeof(msg.o)) + != -1) { + *totalmem = msg.o.info.__posix_tmi_total; + *freemem = msg.o.info.posix_tmi_length; + } else { + *totalmem = 0; + *freemem = 0; + } +} + + +void uv_loadavg(double avg[3]) { + avg[0] = 0.0; + avg[1] = 0.0; + avg[2] = 0.0; +} + + +int uv_exepath(char* buffer, size_t* size) { + char path[PATH_MAX]; + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + realpath(_cmdname(NULL), path); + strlcpy(buffer, path, *size); + *size = strlen(buffer); + return 0; +} + + +uint64_t uv_get_free_memory(void) { + uint64_t totalmem; + uint64_t freemem; + get_mem_info(&totalmem, &freemem); + return freemem; +} + + +uint64_t uv_get_total_memory(void) { + uint64_t totalmem; + uint64_t freemem; + get_mem_info(&totalmem, &freemem); + return totalmem; +} + + +uint64_t uv_get_constrained_memory(void) { + return 0; +} + + +int uv_resident_set_memory(size_t* rss) { + int fd; + procfs_asinfo asinfo; + + fd = uv__open_cloexec("/proc/self/ctl", O_RDONLY); + if (fd == -1) + return UV__ERR(errno); + + if (devctl(fd, DCMD_PROC_ASINFO, &asinfo, sizeof(asinfo), 0) == -1) { + uv__close(fd); + return UV__ERR(errno); + } + + uv__close(fd); + *rss = asinfo.rss; + return 0; +} + + +int uv_uptime(double* uptime) { + struct qtime_entry* qtime = _SYSPAGE_ENTRY(_syspage_ptr, qtime); + *uptime = (qtime->nsec / 1000000000.0); + return 0; +} + + +int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + struct cpuinfo_entry* cpuinfo = + (struct cpuinfo_entry*)_SYSPAGE_ENTRY(_syspage_ptr, new_cpuinfo); + size_t cpuinfo_size = _SYSPAGE_ELEMENT_SIZE(_syspage_ptr, cpuinfo); + struct strings_entry* strings = _SYSPAGE_ENTRY(_syspage_ptr, strings); + int num_cpus = _syspage_ptr->num_cpu; + int i; + + *count = num_cpus; + *cpu_infos = uv__malloc(num_cpus * sizeof(**cpu_infos)); + if (*cpu_infos == NULL) + return UV_ENOMEM; + + for (i = 0; i < num_cpus; i++) { + (*cpu_infos)[i].model = strdup(&strings->data[cpuinfo->name]); + (*cpu_infos)[i].speed = cpuinfo->speed; + SYSPAGE_ARRAY_ADJ_OFFSET(cpuinfo, cpuinfo, cpuinfo_size); + } + + return 0; +} diff --git a/src/unix/signal.c b/src/unix/signal.c index 1e7e8ac..1133c73 100644 --- a/src/unix/signal.c +++ b/src/unix/signal.c @@ -77,7 +77,7 @@ static void uv__signal_global_init(void) { } -UV_DESTRUCTOR(static void uv__signal_global_fini(void)) { +void uv__signal_cleanup(void) { /* We can only use signal-safe functions here. * That includes read/write and close, fortunately. * We do all of this directly here instead of resetting @@ -98,7 +98,7 @@ UV_DESTRUCTOR(static void uv__signal_global_fini(void)) { static void uv__signal_global_reinit(void) { - uv__signal_global_fini(); + uv__signal_cleanup(); if (uv__make_pipe(uv__signal_lock_pipefd, 0)) abort(); @@ -143,6 +143,8 @@ static void uv__signal_block_and_lock(sigset_t* saved_sigmask) { if (sigfillset(&new_mask)) abort(); + /* to shut up valgrind */ + sigemptyset(saved_sigmask); if (pthread_sigmask(SIG_SETMASK, &new_mask, saved_sigmask)) abort(); @@ -263,7 +265,7 @@ static int uv__signal_loop_once_init(uv_loop_t* loop) { if (loop->signal_pipefd[0] != -1) return 0; - err = uv__make_pipe(loop->signal_pipefd, UV__F_NONBLOCK); + err = uv__make_pipe(loop->signal_pipefd, UV_NONBLOCK_PIPE); if (err) return err; diff --git a/src/unix/stream.c b/src/unix/stream.c index 8327f9c..619b625 100644 --- a/src/unix/stream.c +++ b/src/unix/stream.c @@ -58,20 +58,6 @@ struct uv__stream_select_s { fd_set* swrite; size_t swrite_sz; }; - -/* Due to a possible kernel bug at least in OS X 10.10 "Yosemite", - * EPROTOTYPE can be returned while trying to write to a socket that is - * shutting down. If we retry the write, we should get the expected EPIPE - * instead. - */ -# define RETRY_ON_WRITE_ERROR(errno) (errno == EINTR || errno == EPROTOTYPE) -# define IS_TRANSIENT_WRITE_ERROR(errno, send_handle) \ - (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS || \ - (errno == EMSGSIZE && send_handle != NULL)) -#else -# define RETRY_ON_WRITE_ERROR(errno) (errno == EINTR) -# define IS_TRANSIENT_WRITE_ERROR(errno, send_handle) \ - (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) #endif /* defined(__APPLE__) */ static void uv__stream_connect(uv_stream_t*); @@ -164,7 +150,7 @@ static void uv__stream_osx_select(void* arg) { else max_fd = s->int_fd; - while (1) { + for (;;) { /* Terminate on semaphore */ if (uv_sem_trywait(&s->close_sem) == 0) break; @@ -195,7 +181,7 @@ static void uv__stream_osx_select(void* arg) { /* Empty socketpair's buffer in case of interruption */ if (FD_ISSET(s->int_fd, s->sread)) - while (1) { + for (;;) { r = read(s->int_fd, buf, sizeof(buf)); if (r == sizeof(buf)) @@ -658,11 +644,11 @@ int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) { 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: @@ -799,33 +785,21 @@ static int uv__handle_fd(uv_handle_t* handle) { } } -static void uv__write(uv_stream_t* stream) { +static int uv__try_write(uv_stream_t* stream, + const uv_buf_t bufs[], + unsigned int nbufs, + uv_stream_t* send_handle) { struct iovec* iov; - QUEUE* q; - uv_write_t* req; int iovmax; int iovcnt; ssize_t n; - int err; - -start: - - assert(uv__stream_fd(stream) >= 0); - - if (QUEUE_EMPTY(&stream->write_queue)) - return; - - q = QUEUE_HEAD(&stream->write_queue); - req = QUEUE_DATA(q, uv_write_t, queue); - assert(req->handle == stream); /* * Cast to iovec. We had to have our own uv_buf_t instead of iovec * because Windows's WSABUF is not an iovec. */ - assert(sizeof(uv_buf_t) == sizeof(struct iovec)); - iov = (struct iovec*) &(req->bufs[req->write_index]); - iovcnt = req->nbufs - req->write_index; + iov = (struct iovec*) bufs; + iovcnt = nbufs; iovmax = uv__getiovmax(); @@ -837,8 +811,7 @@ start: * Now do the actual writev. Note that we've been updating the pointers * inside the iov each time we write. So there is no need to offset it. */ - - if (req->send_handle) { + if (send_handle != NULL) { int fd_to_send; struct msghdr msg; struct cmsghdr *cmsg; @@ -847,12 +820,10 @@ start: struct cmsghdr alias; } scratch; - if (uv__is_closing(req->send_handle)) { - err = UV_EBADF; - goto error; - } + if (uv__is_closing(send_handle)) + return UV_EBADF; - fd_to_send = uv__handle_fd((uv_handle_t*) req->send_handle); + fd_to_send = uv__handle_fd((uv_handle_t*) send_handle); memset(&scratch, 0, sizeof(scratch)); @@ -881,45 +852,83 @@ start: do n = sendmsg(uv__stream_fd(stream), &msg, 0); - while (n == -1 && RETRY_ON_WRITE_ERROR(errno)); - - /* Ensure the handle isn't sent again in case this is a partial write. */ - if (n >= 0) - req->send_handle = NULL; + while (n == -1 && errno == EINTR); } else { do n = uv__writev(uv__stream_fd(stream), iov, iovcnt); - while (n == -1 && RETRY_ON_WRITE_ERROR(errno)); + while (n == -1 && errno == EINTR); } - if (n == -1 && !IS_TRANSIENT_WRITE_ERROR(errno, req->send_handle)) { - err = UV__ERR(errno); - goto error; - } + if (n >= 0) + return n; - if (n >= 0 && uv__write_req_update(stream, req, n)) { - uv__write_req_finish(req); - return; /* TODO(bnoordhuis) Start trying to write the next request. */ - } + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) + return UV_EAGAIN; - /* If this is a blocking stream, try again. */ - if (stream->flags & UV_HANDLE_BLOCKING_WRITES) - goto start; +#ifdef __APPLE__ + /* macOS versions 10.10 and 10.15 - and presumbaly 10.11 to 10.14, too - + * have a bug where a race condition causes the kernel to return EPROTOTYPE + * because the socket isn't fully constructed. It's probably the result of + * the peer closing the connection and that is why libuv translates it to + * ECONNRESET. Previously, libuv retried until the EPROTOTYPE error went + * away but some VPN software causes the same behavior except the error is + * permanent, not transient, turning the retry mechanism into an infinite + * loop. See https://github.com/libuv/libuv/pull/482. + */ + if (errno == EPROTOTYPE) + return UV_ECONNRESET; +#endif /* __APPLE__ */ - /* We're not done. */ - uv__io_start(stream->loop, &stream->io_watcher, POLLOUT); + return UV__ERR(errno); +} - /* Notify select() thread about state change */ - uv__stream_osx_interrupt_select(stream); +static void uv__write(uv_stream_t* stream) { + QUEUE* q; + uv_write_t* req; + ssize_t n; + + assert(uv__stream_fd(stream) >= 0); + + for (;;) { + if (QUEUE_EMPTY(&stream->write_queue)) + return; - return; + q = QUEUE_HEAD(&stream->write_queue); + req = QUEUE_DATA(q, uv_write_t, queue); + assert(req->handle == stream); + + n = uv__try_write(stream, + &(req->bufs[req->write_index]), + req->nbufs - req->write_index, + req->send_handle); + + /* Ensure the handle isn't sent again in case this is a partial write. */ + if (n >= 0) { + 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. */ + } + } else if (n != UV_EAGAIN) + break; + + /* If this is a blocking stream, try again. */ + if (stream->flags & UV_HANDLE_BLOCKING_WRITES) + continue; + + /* We're not done. */ + uv__io_start(stream->loop, &stream->io_watcher, POLLOUT); -error: - req->error = err; + /* Notify select() thread about state change */ + uv__stream_osx_interrupt_select(stream); + + return; + } + + 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); - if (!uv__io_active(&stream->io_watcher, POLLIN)) - uv__handle_stop(stream); uv__stream_osx_interrupt_select(stream); } @@ -1002,8 +1011,7 @@ 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; uv__io_stop(stream->loop, &stream->io_watcher, POLLIN); - if (!uv__io_active(&stream->io_watcher, POLLOUT)) - uv__handle_stop(stream); + uv__handle_stop(stream); uv__stream_osx_interrupt_select(stream); stream->read_cb(stream, UV_EOF, buf); } @@ -1188,12 +1196,12 @@ static void uv__read(uv_stream_t* stream) { #endif } else { /* Error. User should call uv_close(). */ + stream->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); stream->read_cb(stream, UV__ERR(errno), &buf); if (stream->flags & UV_HANDLE_READING) { stream->flags &= ~UV_HANDLE_READING; uv__io_stop(stream->loop, &stream->io_watcher, POLLIN); - if (!uv__io_active(&stream->io_watcher, POLLOUT)) - uv__handle_stop(stream); + uv__handle_stop(stream); uv__stream_osx_interrupt_select(stream); } } @@ -1276,6 +1284,7 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) { 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); @@ -1390,14 +1399,9 @@ static void uv__stream_connect(uv_stream_t* stream) { } -int uv_write2(uv_write_t* req, - uv_stream_t* stream, - const uv_buf_t bufs[], - unsigned int nbufs, - uv_stream_t* send_handle, - uv_write_cb cb) { - int empty_queue; - +static int uv__check_before_write(uv_stream_t* stream, + unsigned int nbufs, + uv_stream_t* send_handle) { assert(nbufs > 0); assert((stream->type == UV_TCP || stream->type == UV_NAMED_PIPE || @@ -1410,7 +1414,7 @@ int uv_write2(uv_write_t* req, if (!(stream->flags & UV_HANDLE_WRITABLE)) return UV_EPIPE; - if (send_handle) { + if (send_handle != NULL) { if (stream->type != UV_NAMED_PIPE || !((uv_pipe_t*)stream)->ipc) return UV_EINVAL; @@ -1430,6 +1434,22 @@ int uv_write2(uv_write_t* req, #endif } + return 0; +} + +int uv_write2(uv_write_t* req, + uv_stream_t* stream, + const uv_buf_t bufs[], + unsigned int nbufs, + uv_stream_t* send_handle, + uv_write_cb cb) { + int empty_queue; + int err; + + err = uv__check_before_write(stream, nbufs, send_handle); + if (err < 0) + return err; + /* It's legal for write_queue_size > 0 even when the write_queue is empty; * it means there are error-state requests in the write_completed_queue that * will touch up write_queue_size later, see also uv__write_req_finish(). @@ -1498,81 +1518,43 @@ int uv_write(uv_write_t* req, } -void uv_try_write_cb(uv_write_t* req, int status) { - /* Should not be called */ - abort(); -} - - int uv_try_write(uv_stream_t* stream, const uv_buf_t bufs[], unsigned int nbufs) { - int r; - int has_pollout; - size_t written; - size_t req_size; - uv_write_t req; + return uv_try_write2(stream, bufs, nbufs, NULL); +} + + +int uv_try_write2(uv_stream_t* stream, + const uv_buf_t bufs[], + unsigned int nbufs, + uv_stream_t* send_handle) { + int err; /* Connecting or already writing some data */ if (stream->connect_req != NULL || stream->write_queue_size != 0) return UV_EAGAIN; - has_pollout = uv__io_active(&stream->io_watcher, POLLOUT); - - r = uv_write(&req, stream, bufs, nbufs, uv_try_write_cb); - if (r != 0) - return r; + err = uv__check_before_write(stream, nbufs, NULL); + if (err < 0) + return err; - /* Remove not written bytes from write queue size */ - written = uv__count_bufs(bufs, nbufs); - if (req.bufs != NULL) - req_size = uv__write_req_size(&req); - else - req_size = 0; - written -= req_size; - stream->write_queue_size -= req_size; - - /* Unqueue request, regardless of immediateness */ - QUEUE_REMOVE(&req.queue); - uv__req_unregister(stream->loop, &req); - if (req.bufs != req.bufsml) - uv__free(req.bufs); - req.bufs = NULL; - - /* Do not poll for writable, if we wasn't before calling this */ - if (!has_pollout) { - uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT); - uv__stream_osx_interrupt_select(stream); - } - - if (written == 0 && req_size != 0) - return req.error < 0 ? req.error : UV_EAGAIN; - else - return written; + return uv__try_write(stream, bufs, nbufs, send_handle); } -int uv_read_start(uv_stream_t* stream, - uv_alloc_cb alloc_cb, - uv_read_cb read_cb) { +int uv__read_start(uv_stream_t* stream, + uv_alloc_cb alloc_cb, + uv_read_cb read_cb) { assert(stream->type == UV_TCP || stream->type == UV_NAMED_PIPE || stream->type == UV_TTY); - if (stream->flags & UV_HANDLE_CLOSING) - return UV_EINVAL; - - if (!(stream->flags & UV_HANDLE_READABLE)) - return UV_ENOTCONN; - - /* The UV_HANDLE_READING flag is irrelevant of the state of the tcp - it just - * expresses the desired state of the user. - */ + /* The UV_HANDLE_READING flag is irrelevant of the state of the stream - it + * just expresses the desired state of the user. */ stream->flags |= UV_HANDLE_READING; + stream->flags &= ~UV_HANDLE_READ_EOF; /* TODO: try to do the read inline? */ - /* TODO: keep track of tcp state. If we've gotten a EOF then we should - * not start the IO watcher. - */ assert(uv__stream_fd(stream) >= 0); assert(alloc_cb); @@ -1593,8 +1575,7 @@ int uv_read_stop(uv_stream_t* stream) { stream->flags &= ~UV_HANDLE_READING; uv__io_stop(stream->loop, &stream->io_watcher, POLLIN); - if (!uv__io_active(&stream->io_watcher, POLLOUT)) - uv__handle_stop(stream); + uv__handle_stop(stream); uv__stream_osx_interrupt_select(stream); stream->read_cb = NULL; diff --git a/src/unix/sunos.c b/src/unix/sunos.c index 180cc84..2bf297e 100644 --- a/src/unix/sunos.c +++ b/src/unix/sunos.c @@ -154,6 +154,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { sigset_t set; uint64_t base; uint64_t diff; + uint64_t idle_poll; unsigned int nfds; unsigned int i; int saved_errno; @@ -162,6 +163,8 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { int count; int err; int fd; + int user_timeout; + int reset_timeout; if (loop->nfds == 0) { assert(QUEUE_EMPTY(&loop->watcher_queue)); @@ -199,7 +202,21 @@ 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) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + } + 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); + if (timeout != -1) { spec.tv_sec = timeout / 1000; spec.tv_nsec = (timeout % 1000) * 1000000; @@ -242,6 +259,11 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { SAVE_ERRNO(uv__update_time(loop)); if (events[0].portev_source == 0) { + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + if (timeout == 0) return; @@ -282,10 +304,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { /* 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) + if (w == &loop->signal_io_watcher) { have_signals = 1; - else + } else { + uv__metrics_update_idle_time(loop); w->cb(loop, w, pe->portev_events); + } nevents++; @@ -297,8 +321,15 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); } - if (have_signals != 0) + 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; @@ -834,3 +865,14 @@ void uv_free_interface_addresses(uv_interface_address_t* addresses, uv__free(addresses); } + + +#if !defined(_POSIX_VERSION) || _POSIX_VERSION < 200809L +size_t strnlen(const char* s, size_t maxlen) { + const char* end; + end = memchr(s, '\0', maxlen); + if (end == NULL) + return maxlen; + return end - s; +} +#endif diff --git a/src/unix/tcp.c b/src/unix/tcp.c index d47e943..789807f 100644 --- a/src/unix/tcp.c +++ b/src/unix/tcp.c @@ -214,14 +214,15 @@ int uv__tcp_connect(uv_connect_t* req, if (handle->connect_req != NULL) return UV_EALREADY; /* FIXME(bnoordhuis) UV_EINVAL or maybe UV_EBUSY. */ + if (handle->delayed_error != 0) + goto out; + err = maybe_new_socket(handle, addr->sa_family, UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); if (err) return err; - handle->delayed_error = 0; - do { errno = 0; r = connect(uv__stream_fd(handle), addr, addrlen); @@ -249,6 +250,8 @@ int uv__tcp_connect(uv_connect_t* req, return UV__ERR(errno); } +out: + uv__req_init(handle->loop, req, UV_CONNECT); req->cb = cb; req->handle = (uv_stream_t*) handle; @@ -325,17 +328,20 @@ int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb) { } -int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { - static int single_accept = -1; +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 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) @@ -456,3 +462,49 @@ int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) { void uv__tcp_close(uv_tcp_t* handle) { uv__stream_close((uv_stream_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__) + int flags; + + flags = type | SOCK_CLOEXEC; + if ((flags0 & UV_NONBLOCK_PIPE) && (flags1 & UV_NONBLOCK_PIPE)) + flags |= SOCK_NONBLOCK; + + if (socketpair(AF_UNIX, flags, protocol, temp)) + return UV__ERR(errno); + + if (flags & UV_FS_O_NONBLOCK) { + fds[0] = temp[0]; + fds[1] = temp[1]; + return 0; + } +#else + if (socketpair(AF_UNIX, type, protocol, temp)) + return UV__ERR(errno); + + if ((err = uv__cloexec(temp[0], 1))) + goto fail; + if ((err = uv__cloexec(temp[1], 1))) + goto fail; +#endif + + if (flags0 & UV_NONBLOCK_PIPE) + if ((err = uv__nonblock(temp[0], 1))) + goto fail; + if (flags1 & UV_NONBLOCK_PIPE) + if ((err = uv__nonblock(temp[1], 1))) + goto fail; + + fds[0] = temp[0]; + fds[1] = temp[1]; + return 0; + +fail: + uv__close(temp[0]); + uv__close(temp[1]); + return err; +} diff --git a/src/unix/thread.c b/src/unix/thread.c index f10c351..d89e5cd 100644 --- a/src/unix/thread.c +++ b/src/unix/thread.c @@ -107,8 +107,7 @@ int uv_barrier_wait(uv_barrier_t* barrier) { } last = (--b->out == 0); - if (!last) - uv_cond_signal(&b->cond); /* Not needed for last thread. */ + uv_cond_signal(&b->cond); uv_mutex_unlock(&b->mutex); return last; @@ -122,9 +121,10 @@ void uv_barrier_destroy(uv_barrier_t* barrier) { uv_mutex_lock(&b->mutex); assert(b->in == 0); - assert(b->out == 0); + while (b->out != 0) + uv_cond_wait(&b->cond, &b->mutex); - if (b->in != 0 || b->out != 0) + if (b->in != 0) abort(); uv_mutex_unlock(&b->mutex); @@ -162,40 +162,33 @@ void uv_barrier_destroy(uv_barrier_t* barrier) { #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. +/* Musl's PTHREAD_STACK_MIN is 2 KB on all architectures, which is + * too small to safely receive signals on. * - * 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. + * 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 thread_stack_size(void) { -#if defined(__APPLE__) || defined(__linux__) - struct rlimit lim; +static size_t uv__min_stack_size(void) { + static const size_t min = 8192; - if (getrlimit(RLIMIT_STACK, &lim)) - abort(); +#ifdef PTHREAD_STACK_MIN /* Not defined on NetBSD. */ + if (min < (size_t) PTHREAD_STACK_MIN) + return PTHREAD_STACK_MIN; +#endif /* PTHREAD_STACK_MIN */ + + return min; +} - if (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 +/* 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__) @@ -206,6 +199,34 @@ static size_t thread_stack_size(void) { } +/* 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. + */ +size_t uv__thread_stack_size(void) { +#if defined(__APPLE__) || defined(__linux__) + struct rlimit lim; + + /* getrlimit() can fail on some aarch64 systems due to a glibc bug where + * the system call wrapper invokes the wrong system call. Don't treat + * that as fatal, just use the default stack size instead. + */ + if (getrlimit(RLIMIT_STACK, &lim)) + return uv__default_stack_size(); + + 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(); +} + + int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { uv_thread_options_t params; params.flags = UV_THREAD_NO_FLAGS; @@ -221,6 +242,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 { @@ -233,15 +255,14 @@ int uv_thread_create_ex(uv_thread_t* tid, attr = NULL; if (stack_size == 0) { - stack_size = thread_stack_size(); + stack_size = uv__thread_stack_size(); } else { 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) { @@ -708,11 +729,9 @@ int uv_cond_init(uv_cond_t* cond) { if (err) return UV__ERR(err); -#if !(defined(__ANDROID_API__) && __ANDROID_API__ < 21) err = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); if (err) goto error2; -#endif err = pthread_cond_init(cond, &attr); if (err) @@ -804,16 +823,7 @@ int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { #endif ts.tv_sec = timeout / NANOSEC; ts.tv_nsec = timeout % NANOSEC; -#if defined(__ANDROID_API__) && __ANDROID_API__ < 21 - - /* - * The bionic pthread implementation doesn't support CLOCK_MONOTONIC, - * but has this alternative function instead. - */ - r = pthread_cond_timedwait_monotonic_np(cond, mutex, &ts); -#else r = pthread_cond_timedwait(cond, mutex, &ts); -#endif /* __ANDROID_API__ */ #endif diff --git a/src/unix/tty.c b/src/unix/tty.c index ff7d3b1..9442cf1 100644 --- a/src/unix/tty.c +++ b/src/unix/tty.c @@ -34,6 +34,34 @@ #define IMAXBEL 0 #endif +#if defined(__PASE__) +/* On IBM i PASE, for better compatibility with running interactive programs in + * a 5250 environment, isatty() will return true for the stdin/stdout/stderr + * streams created by QSH/QP2TERM. + * + * For more, see docs on PASE_STDIO_ISATTY in + * https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_74/apis/pase_environ.htm + * + * This behavior causes problems for Node as it expects that if isatty() returns + * true that TTY ioctls will be supported by that fd (which is not an + * unreasonable expectation) and when they don't it crashes with assertion + * errors. + * + * Here, we create our own version of isatty() that uses ioctl() to identify + * whether the fd is *really* a TTY or not. + */ +static int isreallyatty(int file) { + int rc; + + rc = !ioctl(file, TXISATTY + 0x81, NULL); + if (!rc && errno != EBADF) + errno = ENOTTY; + + return rc; +} +#define isatty(fd) isreallyatty(fd) +#endif + static int orig_termios_fd = -1; static struct termios orig_termios; static uv_spinlock_t termios_spinlock = UV_SPINLOCK_INITIALIZER; @@ -214,6 +242,24 @@ static void uv__tty_make_raw(struct termios* tio) { tio->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); tio->c_cflag &= ~(CSIZE | PARENB); tio->c_cflag |= CS8; + + /* + * By default, most software expects a pending read to block until at + * least one byte becomes available. As per termio(7I), this requires + * setting the MIN and TIME parameters appropriately. + * + * As a somewhat unfortunate artifact of history, the MIN and TIME slots + * in the control character array overlap with the EOF and EOL slots used + * for canonical mode processing. Because the EOF character needs to be + * the ASCII EOT value (aka Control-D), it has the byte value 4. When + * switching to raw mode, this is interpreted as a MIN value of 4; i.e., + * reads will block until at least four bytes have been input. + * + * Other platforms with a distinct MIN slot like Linux and FreeBSD appear + * to default to a MIN value of 1, so we'll force that value here: + */ + tio->c_cc[VMIN] = 1; + tio->c_cc[VTIME] = 0; #else cfmakeraw(tio); #endif /* #ifdef __sun */ @@ -293,14 +339,7 @@ uv_handle_type uv_guess_handle(uv_file file) { if (file < 0) return UV_UNKNOWN_HANDLE; -#if defined(__PASE__) - /* On IBMi PASE isatty() always returns true for stdin, stdout and stderr. - * Use ioctl() instead to identify whether it's actually a TTY. - */ - if (!ioctl(file, TXISATTY + 0x81, NULL) || errno != ENOTTY) -#else if (isatty(file)) -#endif return UV_TTY; if (fstat(file, &s)) diff --git a/src/unix/udp.c b/src/unix/udp.c index eb4b8f4..aad7a6d 100644 --- a/src/unix/udp.c +++ b/src/unix/udp.c @@ -32,8 +32,6 @@ #endif #include -#define UV__UDP_DGRAM_MAXSIZE (64 * 1024) - #if defined(IPV6_JOIN_GROUP) && !defined(IPV6_ADD_MEMBERSHIP) # define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP #endif @@ -42,6 +40,11 @@ # 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); @@ -68,12 +71,12 @@ static void uv__udp_mmsg_init(void) { s = uv__socket(AF_INET, SOCK_DGRAM, 0); if (s < 0) return; - ret = uv__sendmmsg(s, NULL, 0, 0); + 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, 0, NULL); + ret = uv__recvmmsg(s, NULL, 0); if (ret == 0 || errno != ENOSYS) uv__recvmmsg_avail = 1; } @@ -198,14 +201,18 @@ 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; msgs[k].msg_hdr.msg_namelen = sizeof(peers[0]); + msgs[k].msg_hdr.msg_control = NULL; + msgs[k].msg_hdr.msg_controllen = 0; + msgs[k].msg_hdr.msg_flags = 0; } do - nread = uv__recvmmsg(handle->io_watcher.fd, msgs, chunks, 0, NULL); + nread = uv__recvmmsg(handle->io_watcher.fd, msgs, chunks); while (nread == -1 && errno == EINTR); if (nread < 1) { @@ -214,14 +221,11 @@ static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) { else handle->recv_cb(handle, UV__ERR(errno), buf, NULL, 0); } else { - /* count to zero, so the buffer base comes last */ - for (k = nread; k > 0 && handle->recv_cb != NULL;) { - k--; - flags = 0; + /* pass each chunk to the application */ + for (k = 0; k < (size_t) nread && handle->recv_cb != NULL; k++) { + flags = UV_UDP_MMSG_CHUNK; if (msgs[k].msg_hdr.msg_flags & MSG_TRUNC) flags |= UV_UDP_PARTIAL; - if (k != 0) - flags |= UV_UDP_MMSG_CHUNK; chunk_buf = uv_buf_init(iov[k].iov_base, iov[k].iov_len); handle->recv_cb(handle, @@ -230,6 +234,10 @@ static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) { msgs[k].msg_hdr.msg_name, flags); } + + /* one last callback so the original buffer is freed */ + if (handle->recv_cb != NULL) + handle->recv_cb(handle, 0, buf, NULL, UV_UDP_MMSG_FREE); } return nread; } @@ -261,16 +269,11 @@ static void uv__udp_recvmsg(uv_udp_t* handle) { assert(buf.base != NULL); #if HAVE_MMSG - uv_once(&once, uv__udp_mmsg_init); - if (uv__recvmmsg_avail) { - /* Returned space for more than 1 datagram, use it to receive - * multiple datagrams. */ - if (buf.len >= 2 * UV__UDP_DGRAM_MAXSIZE) { - nread = uv__udp_recvmmsg(handle, &buf); - if (nread > 0) - count -= nread; - continue; - } + if (uv_udp_using_recvmmsg(handle)) { + nread = uv__udp_recvmmsg(handle, &buf); + if (nread > 0) + count -= nread; + continue; } #endif @@ -352,7 +355,7 @@ write_queue_drain: } do - npkts = uv__sendmmsg(handle->io_watcher.fd, h, pkts, 0); + npkts = uv__sendmmsg(handle->io_watcher.fd, h, pkts); while (npkts == -1 && errno == EINTR); if (npkts < 1) { @@ -360,7 +363,7 @@ write_queue_drain: return; for (i = 0, q = QUEUE_HEAD(&handle->write_queue); i < pkts && q != &handle->write_queue; - ++i, q = QUEUE_HEAD(q)) { + ++i, q = QUEUE_HEAD(&handle->write_queue)) { assert(q != NULL); req = QUEUE_DATA(q, uv_udp_send_t, queue); assert(req != NULL); @@ -373,8 +376,11 @@ write_queue_drain: 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 < pkts && q != &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); @@ -489,7 +495,7 @@ 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__) if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes))) return UV__ERR(errno); #else @@ -500,6 +506,28 @@ static int uv__set_reuse(int fd) { return 0; } +/* + * The Linux kernel suppresses some ICMP error messages by default for UDP + * sockets. Setting IP_RECVERR/IPV6_RECVERR on the socket enables full ICMP + * error reporting, hopefully resulting in faster failover to working name + * servers. + */ +static int uv__set_recverr(int fd, sa_family_t ss_family) { +#if defined(__linux__) + int yes; + + yes = 1; + if (ss_family == AF_INET) { + if (setsockopt(fd, IPPROTO_IP, IP_RECVERR, &yes, sizeof(yes))) + return UV__ERR(errno); + } else if (ss_family == AF_INET6) { + if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVERR, &yes, sizeof(yes))) + return UV__ERR(errno); + } +#endif + return 0; +} + int uv__udp_bind(uv_udp_t* handle, const struct sockaddr* addr, @@ -510,7 +538,7 @@ int uv__udp_bind(uv_udp_t* handle, int fd; /* Check for bad flags. */ - if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR)) + if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR | UV_UDP_LINUX_RECVERR)) return UV_EINVAL; /* Cannot set IPv6-only mode on non-IPv6 socket. */ @@ -526,6 +554,12 @@ int uv__udp_bind(uv_udp_t* handle, handle->io_watcher.fd = fd; } + if (flags & UV_UDP_LINUX_RECVERR) { + err = uv__set_recverr(fd, addr->sa_family); + if (err) + return err; + } + if (flags & UV_UDP_REUSEADDR) { err = uv__set_reuse(fd); if (err) @@ -565,11 +599,7 @@ int uv__udp_bind(uv_udp_t* handle, static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, int domain, unsigned int flags) { - union { - struct sockaddr_in6 in6; - struct sockaddr_in in; - struct sockaddr addr; - } taddr; + union uv__sockaddr taddr; socklen_t addrlen; if (handle->io_watcher.fd != -1) @@ -625,28 +655,71 @@ int uv__udp_connect(uv_udp_t* handle, return 0; } - +/* From https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html + * 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; + * if (addr->sa_family != so->addr->sa_family) return EAFNOSUPPORT; + * pru_connect(so); + * } + * else return EISCONN; + * + * z/OS (same with Windows): + * if(addr->sa_len < so->addr->sa_len) return EINVAL; + * if (addr->sa_family == AF_UNSPEC) sodisconnect(so); + * + * AIX: + * if(addr->sa_len != sizeof(struct sockaddr)) return EINVAL; // ignore ip proto version + * if (addr->sa_family == AF_UNSPEC) sodisconnect(so); + * + * Linux,Others: + * if(addr->sa_len < sizeof(struct sockaddr)) return EINVAL; + * if (addr->sa_family == AF_UNSPEC) sodisconnect(so); + */ int uv__udp_disconnect(uv_udp_t* handle) { int r; +#if defined(__MVS__) + struct sockaddr_storage addr; +#else struct sockaddr addr; +#endif memset(&addr, 0, sizeof(addr)); +#if defined(__MVS__) + addr.ss_family = AF_UNSPEC; +#else addr.sa_family = AF_UNSPEC; +#endif do { errno = 0; - r = connect(handle->io_watcher.fd, &addr, sizeof(addr)); + r = connect(handle->io_watcher.fd, (struct sockaddr*) &addr, sizeof(addr)); } while (r == -1 && errno == EINTR); - if (r == -1 && errno != EAFNOSUPPORT) + if (r == -1) { +#if defined(BSD) /* The macro BSD is from sys/param.h */ + if (errno != EAFNOSUPPORT && errno != EINVAL) + return UV__ERR(errno); +#else return UV__ERR(errno); +#endif + } handle->flags &= ~UV_HANDLE_UDP_CONNECTED; return 0; } - int uv__udp_send(uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], @@ -851,7 +924,12 @@ static int uv__udp_set_membership6(uv_udp_t* handle, } -#if !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__ANDROID__) +#if !defined(__OpenBSD__) && \ + !defined(__NetBSD__) && \ + !defined(__ANDROID__) && \ + !defined(__DragonFly__) && \ + !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, @@ -922,8 +1000,10 @@ static int uv__udp_set_source_membership6(uv_udp_t* handle, mreq.gsr_interface = 0; } - memcpy(&mreq.gsr_group, multicast_addr, sizeof(mreq.gsr_group)); - memcpy(&mreq.gsr_source, source_addr, sizeof(mreq.gsr_source)); + STATIC_ASSERT(sizeof(mreq.gsr_group) >= sizeof(*multicast_addr)); + STATIC_ASSERT(sizeof(mreq.gsr_source) >= sizeof(*source_addr)); + memcpy(&mreq.gsr_group, multicast_addr, sizeof(*multicast_addr)); + memcpy(&mreq.gsr_source, source_addr, sizeof(*source_addr)); if (membership == UV_JOIN_GROUP) optname = MCAST_JOIN_SOURCE_GROUP; @@ -945,26 +1025,17 @@ static int uv__udp_set_source_membership6(uv_udp_t* handle, #endif -int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) { - int domain; - int err; +int uv__udp_init_ex(uv_loop_t* loop, + uv_udp_t* handle, + unsigned flags, + int domain) { int fd; - /* Use the lower 8 bits for the domain */ - domain = flags & 0xFF; - if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC) - return UV_EINVAL; - - if (flags & ~0xFF) - return UV_EINVAL; - + fd = -1; if (domain != AF_UNSPEC) { - err = uv__socket(domain, SOCK_DGRAM, 0); - if (err < 0) - return err; - fd = err; - } else { - fd = -1; + fd = uv__socket(domain, SOCK_DGRAM, 0); + if (fd < 0) + return fd; } uv__handle_init(loop, (uv_handle_t*)handle, UV_UDP); @@ -980,8 +1051,14 @@ int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) { } -int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) { - return uv_udp_init_ex(loop, handle, AF_UNSPEC); +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; + } +#endif + return 0; } @@ -1040,42 +1117,38 @@ int uv_udp_set_source_membership(uv_udp_t* handle, const char* interface_addr, const char* source_addr, uv_membership membership) { -#if !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__ANDROID__) +#if !defined(__OpenBSD__) && \ + !defined(__NetBSD__) && \ + !defined(__ANDROID__) && \ + !defined(__DragonFly__) && \ + !defined(__QNX__) && \ + !defined(__GNU__) int err; - struct sockaddr_storage mcast_addr; - struct sockaddr_in* mcast_addr4; - struct sockaddr_in6* mcast_addr6; - struct sockaddr_storage src_addr; - struct sockaddr_in* src_addr4; - struct sockaddr_in6* src_addr6; - - mcast_addr4 = (struct sockaddr_in*)&mcast_addr; - mcast_addr6 = (struct sockaddr_in6*)&mcast_addr; - src_addr4 = (struct sockaddr_in*)&src_addr; - src_addr6 = (struct sockaddr_in6*)&src_addr; - - err = uv_ip4_addr(multicast_addr, 0, mcast_addr4); + union uv__sockaddr mcast_addr; + union uv__sockaddr src_addr; + + err = uv_ip4_addr(multicast_addr, 0, &mcast_addr.in); if (err) { - err = uv_ip6_addr(multicast_addr, 0, mcast_addr6); + err = uv_ip6_addr(multicast_addr, 0, &mcast_addr.in6); if (err) return err; - err = uv_ip6_addr(source_addr, 0, src_addr6); + err = uv_ip6_addr(source_addr, 0, &src_addr.in6); if (err) return err; return uv__udp_set_source_membership6(handle, - mcast_addr6, + &mcast_addr.in6, interface_addr, - src_addr6, + &src_addr.in6, membership); } - err = uv_ip4_addr(source_addr, 0, src_addr4); + err = uv_ip4_addr(source_addr, 0, &src_addr.in); if (err) return err; return uv__udp_set_source_membership4(handle, - mcast_addr4, + &mcast_addr.in, interface_addr, - src_addr4, + &src_addr.in, membership); #else return UV_ENOSYS; @@ -1156,7 +1229,7 @@ int uv_udp_set_ttl(uv_udp_t* handle, int ttl) { * and use the general uv__setsockopt_maybe_char call on other platforms. */ #if defined(__sun) || defined(_AIX) || defined(__OpenBSD__) || \ - defined(__MVS__) + defined(__MVS__) || defined(__QNX__) return uv__setsockopt(handle, IP_TTL, @@ -1165,7 +1238,7 @@ int uv_udp_set_ttl(uv_udp_t* handle, int ttl) { sizeof(ttl)); #else /* !(defined(__sun) || defined(_AIX) || defined (__OpenBSD__) || - defined(__MVS__)) */ + defined(__MVS__) || defined(__QNX__)) */ return uv__setsockopt_maybe_char(handle, IP_TTL, @@ -1173,7 +1246,7 @@ int uv_udp_set_ttl(uv_udp_t* handle, int ttl) { ttl); #endif /* defined(__sun) || defined(_AIX) || defined (__OpenBSD__) || - defined(__MVS__) */ + defined(__MVS__) || defined(__QNX__) */ } @@ -1185,7 +1258,7 @@ int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl) { * and use the general uv__setsockopt_maybe_char call otherwise. */ #if defined(__sun) || defined(_AIX) || defined(__OpenBSD__) || \ - defined(__MVS__) + defined(__MVS__) || defined(__QNX__) if (handle->flags & UV_HANDLE_IPV6) return uv__setsockopt(handle, IP_MULTICAST_TTL, @@ -1193,7 +1266,7 @@ int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl) { &ttl, sizeof(ttl)); #endif /* defined(__sun) || defined(_AIX) || defined(__OpenBSD__) || \ - defined(__MVS__) */ + defined(__MVS__) || defined(__QNX__) */ return uv__setsockopt_maybe_char(handle, IP_MULTICAST_TTL, @@ -1210,7 +1283,7 @@ int uv_udp_set_multicast_loop(uv_udp_t* handle, int on) { * and use the general uv__setsockopt_maybe_char call otherwise. */ #if defined(__sun) || defined(_AIX) || defined(__OpenBSD__) || \ - defined(__MVS__) + defined(__MVS__) || defined(__QNX__) if (handle->flags & UV_HANDLE_IPV6) return uv__setsockopt(handle, IP_MULTICAST_LOOP, @@ -1218,7 +1291,7 @@ int uv_udp_set_multicast_loop(uv_udp_t* handle, int on) { &on, sizeof(on)); #endif /* defined(__sun) || defined(_AIX) ||defined(__OpenBSD__) || - defined(__MVS__) */ + defined(__MVS__) || defined(__QNX__) */ return uv__setsockopt_maybe_char(handle, IP_MULTICAST_LOOP, diff --git a/src/uv-common.c b/src/uv-common.c index cec4ac6..f43dd3d 100644 --- a/src/uv-common.c +++ b/src/uv-common.c @@ -100,6 +100,17 @@ void* uv__realloc(void* ptr, size_t size) { return NULL; } +void* uv__reallocf(void* ptr, size_t size) { + void* newptr; + + newptr = uv__realloc(ptr, size); + if (newptr == NULL) + if (size > 0) + uv__free(ptr); + + return newptr; +} + int uv_replace_allocator(uv_malloc_func malloc_func, uv_realloc_func realloc_func, uv_calloc_func calloc_func, @@ -263,6 +274,20 @@ int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size) { } +int uv_ip_name(const struct sockaddr *src, char *dst, size_t size) { + switch (src->sa_family) { + case AF_INET: + return uv_inet_ntop(AF_INET, &((struct sockaddr_in *)src)->sin_addr, + dst, size); + case AF_INET6: + return uv_inet_ntop(AF_INET6, &((struct sockaddr_in6 *)src)->sin6_addr, + dst, size); + default: + return UV_EAFNOSUPPORT; + } +} + + int uv_tcp_bind(uv_tcp_t* handle, const struct sockaddr* addr, unsigned int flags) { @@ -282,6 +307,36 @@ int uv_tcp_bind(uv_tcp_t* handle, } +int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned flags) { + unsigned extra_flags; + int domain; + int rc; + + /* Use the lower 8 bits for the domain. */ + domain = flags & 0xFF; + if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC) + return UV_EINVAL; + + /* Use the higher bits for extra flags. */ + extra_flags = flags & ~0xFF; + if (extra_flags & ~UV_UDP_RECVMMSG) + return UV_EINVAL; + + rc = uv__udp_init_ex(loop, handle, flags, domain); + + if (rc == 0) + if (extra_flags & UV_UDP_RECVMMSG) + handle->flags |= UV_HANDLE_UDP_RECVMMSG; + + return rc; +} + + +int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) { + return uv_udp_init_ex(loop, handle, AF_UNSPEC); +} + + int uv_udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int flags) { @@ -791,6 +846,25 @@ void uv_loop_delete(uv_loop_t* loop) { } +int uv_read_start(uv_stream_t* stream, + uv_alloc_cb alloc_cb, + uv_read_cb read_cb) { + if (stream == NULL || alloc_cb == NULL || read_cb == NULL) + return UV_EINVAL; + + if (stream->flags & UV_HANDLE_CLOSING) + return UV_EINVAL; + + if (stream->flags & UV_HANDLE_READING) + return UV_EALREADY; + + if (!(stream->flags & UV_HANDLE_READABLE)) + return UV_ENOTCONN; + + return uv__read_start(stream, alloc_cb, read_cb); +} + + void uv_os_free_environ(uv_env_item_t* envitems, int count) { int i; @@ -810,3 +884,87 @@ void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { uv__free(cpu_infos); } + + +/* Also covers __clang__ and __INTEL_COMPILER. Disabled on Windows because + * threads have already been forcibly terminated by the operating system + * by the time destructors run, ergo, it's not safe to try to clean them up. + */ +#if defined(__GNUC__) && !defined(_WIN32) +__attribute__((destructor)) +#endif +void uv_library_shutdown(void) { + static int was_shutdown; + + if (uv__load_relaxed(&was_shutdown)) + return; + + uv__process_title_cleanup(); + uv__signal_cleanup(); +#ifdef __MVS__ + /* TODO(itodorov) - zos: revisit when Woz compiler is available. */ + uv__os390_cleanup(); +#else + uv__threadpool_cleanup(); +#endif + uv__store_relaxed(&was_shutdown, 1); +} + + +void uv__metrics_update_idle_time(uv_loop_t* loop) { + uv__loop_metrics_t* loop_metrics; + uint64_t entry_time; + uint64_t exit_time; + + if (!(uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME)) + return; + + loop_metrics = uv__get_loop_metrics(loop); + + /* The thread running uv__metrics_update_idle_time() is always the same + * thread that sets provider_entry_time. So it's unnecessary to lock before + * retrieving this value. + */ + if (loop_metrics->provider_entry_time == 0) + return; + + exit_time = uv_hrtime(); + + uv_mutex_lock(&loop_metrics->lock); + entry_time = loop_metrics->provider_entry_time; + loop_metrics->provider_entry_time = 0; + loop_metrics->provider_idle_time += exit_time - entry_time; + uv_mutex_unlock(&loop_metrics->lock); +} + + +void uv__metrics_set_provider_entry_time(uv_loop_t* loop) { + uv__loop_metrics_t* loop_metrics; + uint64_t now; + + if (!(uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME)) + return; + + now = uv_hrtime(); + loop_metrics = uv__get_loop_metrics(loop); + uv_mutex_lock(&loop_metrics->lock); + loop_metrics->provider_entry_time = now; + uv_mutex_unlock(&loop_metrics->lock); +} + + +uint64_t uv_metrics_idle_time(uv_loop_t* loop) { + uv__loop_metrics_t* loop_metrics; + uint64_t entry_time; + uint64_t idle_time; + + loop_metrics = uv__get_loop_metrics(loop); + uv_mutex_lock(&loop_metrics->lock); + idle_time = loop_metrics->provider_idle_time; + entry_time = loop_metrics->provider_entry_time; + uv_mutex_unlock(&loop_metrics->lock); + + if (entry_time > 0) + idle_time += uv_hrtime() - entry_time; + return idle_time; +} diff --git a/src/uv-common.h b/src/uv-common.h index f788161..6001b0c 100644 --- a/src/uv-common.h +++ b/src/uv-common.h @@ -60,6 +60,16 @@ extern int snprintf(char*, size_t, const char*, ...); #define STATIC_ASSERT(expr) \ void uv__static_assert(int static_assert_failed[1 - 2 * !(expr)]) +#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) +#else +#define uv__load_relaxed(p) (*p) +#define uv__store_relaxed(p, v) do *p = v; while (0) +#endif + +#define UV__UDP_DGRAM_MAXSIZE (64 * 1024) + /* Handle flags. Some flags are specific to Windows or UNIX. */ enum { /* Used by all handles. */ @@ -98,12 +108,12 @@ enum { UV_HANDLE_TCP_KEEPALIVE = 0x02000000, UV_HANDLE_TCP_SINGLE_ACCEPT = 0x04000000, UV_HANDLE_TCP_ACCEPT_STATE_CHANGING = 0x08000000, - UV_HANDLE_TCP_SOCKET_CLOSED = 0x10000000, - UV_HANDLE_SHARED_TCP_SOCKET = 0x20000000, + UV_HANDLE_SHARED_TCP_SOCKET = 0x10000000, /* Only used by uv_udp_t handles. */ UV_HANDLE_UDP_PROCESSING = 0x01000000, UV_HANDLE_UDP_CONNECTED = 0x02000000, + UV_HANDLE_UDP_RECVMMSG = 0x04000000, /* Only used by uv_pipe_t handles. */ UV_HANDLE_NON_OVERLAPPED_PIPE = 0x01000000, @@ -120,13 +130,20 @@ 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); void uv__loop_close(uv_loop_t* loop); +int uv__read_start(uv_stream_t* stream, + uv_alloc_cb alloc_cb, + uv_read_cb read_cb); + int uv__tcp_bind(uv_tcp_t* tcp, const struct sockaddr* addr, unsigned int addrlen, @@ -138,6 +155,11 @@ int uv__tcp_connect(uv_connect_t* req, unsigned int addrlen, uv_connect_cb cb); +int uv__udp_init_ex(uv_loop_t* loop, + uv_udp_t* handle, + unsigned flags, + int domain); + int uv__udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int addrlen, @@ -200,6 +222,10 @@ int uv__next_timeout(const uv_loop_t* loop); void uv__run_timers(uv_loop_t* loop); void uv__timer_close(uv_timer_t* handle); +void uv__process_title_cleanup(void); +void uv__signal_cleanup(void); +void uv__threadpool_cleanup(void); + #define uv__has_active_reqs(loop) \ ((loop)->active_reqs.count > 0) @@ -315,6 +341,12 @@ void uv__timer_close(uv_timer_t* handle); } \ while (0) +#define uv__get_internal_fields(loop) \ + ((uv__loop_internal_fields_t*) loop->internal_fields) + +#define uv__get_loop_metrics(loop) \ + (&uv__get_internal_fields(loop)->loop_metrics) + /* Allocator prototypes */ void *uv__calloc(size_t count, size_t size); char *uv__strdup(const char* s); @@ -322,5 +354,23 @@ char *uv__strndup(const char* s, size_t n); void* uv__malloc(size_t size); void uv__free(void* ptr); void* uv__realloc(void* ptr, size_t size); +void* uv__reallocf(void* ptr, size_t size); + +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 { + uint64_t provider_entry_time; + uint64_t provider_idle_time; + uv_mutex_t lock; +}; + +void uv__metrics_update_idle_time(uv_loop_t* loop); +void uv__metrics_set_provider_entry_time(uv_loop_t* loop); + +struct uv__loop_internal_fields_s { + unsigned int flags; + uv__loop_metrics_t loop_metrics; +}; #endif /* UV_COMMON_H_ */ diff --git a/src/uv-data-getter-setters.c b/src/uv-data-getter-setters.c index c302566..0bd0448 100644 --- a/src/uv-data-getter-setters.c +++ b/src/uv-data-getter-setters.c @@ -1,3 +1,24 @@ +/* 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" const char* uv_handle_type_name(uv_handle_type type) { diff --git a/src/win/async.c b/src/win/async.c index d787f66..b904676 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/atomicops-inl.h b/src/win/atomicops-inl.h index 52713cf..2f984c6 100644 --- a/src/win/atomicops-inl.h +++ b/src/win/atomicops-inl.h @@ -39,10 +39,11 @@ static char INLINE uv__atomic_exchange_set(char volatile* target) { return _InterlockedOr8(target, 1); } -#else /* GCC */ +#else /* GCC, Clang in mingw mode */ -/* Mingw-32 version, hopefully this works for 64-bit gcc as well. */ static inline char uv__atomic_exchange_set(char volatile* target) { +#if defined(__i386__) || defined(__x86_64__) + /* Mingw-32 version, hopefully this works for 64-bit gcc as well. */ const char one = 1; char old_value; __asm__ __volatile__ ("lock xchgb %0, %1\n\t" @@ -50,6 +51,9 @@ static inline char uv__atomic_exchange_set(char volatile* target) { : "0"(one), "m"(*target) : "memory"); return old_value; +#else + return __sync_fetch_and_or(target, 1); +#endif } #endif diff --git a/src/win/core.c b/src/win/core.c index 6ded90c..c08dedc 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 @@ failed_loops_realloc: 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(); @@ -222,6 +225,7 @@ static void uv_init(void) { int uv_loop_init(uv_loop_t* loop) { + uv__loop_internal_fields_t* lfields; struct heap* timer_heap; int err; @@ -233,6 +237,15 @@ int uv_loop_init(uv_loop_t* loop) { if (loop->iocp == NULL) return uv_translate_sys_error(GetLastError()); + lfields = (uv__loop_internal_fields_t*) uv__calloc(1, sizeof(*lfields)); + if (lfields == NULL) + return UV_ENOMEM; + loop->internal_fields = lfields; + + err = uv_mutex_init(&lfields->loop_metrics.lock); + if (err) + goto fail_metrics_mutex_init; + /* To prevent uninitialized memory access, loop->time must be initialized * to zero before calling uv_update_time for the first time. */ @@ -297,6 +310,11 @@ fail_mutex_init: loop->timer_heap = NULL; fail_timers_alloc: + uv_mutex_destroy(&lfields->loop_metrics.lock); + +fail_metrics_mutex_init: + uv__free(lfields); + loop->internal_fields = NULL; CloseHandle(loop->iocp); loop->iocp = INVALID_HANDLE_VALUE; @@ -312,11 +330,12 @@ 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); } void uv__loop_close(uv_loop_t* loop) { + uv__loop_internal_fields_t* lfields; size_t i; uv__loops_remove(loop); @@ -347,11 +366,24 @@ void uv__loop_close(uv_loop_t* loop) { uv__free(loop->timer_heap); loop->timer_heap = NULL; + lfields = uv__get_internal_fields(loop); + uv_mutex_destroy(&lfields->loop_metrics.lock); + uv__free(lfields); + loop->internal_fields = NULL; + CloseHandle(loop->iocp); } int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) { + uv__loop_internal_fields_t* lfields; + + lfields = uv__get_internal_fields(loop); + if (option == UV_METRICS_IDLE_TIME) { + lfields->flags |= UV_METRICS_IDLE_TIME; + return 0; + } + return UV_ENOSYS; } @@ -366,23 +398,28 @@ 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; +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; +} - if (loop->pending_reqs_tail) - return 0; - if (loop->endgame_handles) - return 0; +int uv_loop_alive(const uv_loop_t* loop) { + return uv__loop_alive(loop); +} - if (loop->idle_handles) - return 0; - return uv__next_timeout(loop); +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; } @@ -393,20 +430,48 @@ static void uv__poll_wine(uv_loop_t* loop, DWORD timeout) { 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); + if (overlapped) { /* Package was dequeued */ - req = uv_overlapped_to_req(overlapped); - uv_insert_pending_req(loop, req); + 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. @@ -445,16 +510,44 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) { ULONG i; 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++) { - success = GetQueuedCompletionStatusEx(loop->iocp, - overlappeds, - ARRAY_SIZE(overlappeds), - &count, - timeout, - FALSE); + /* 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); + + 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); if (success) { for (i = 0; i < count; i++) { @@ -462,8 +555,8 @@ 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); + req = uv__overlapped_to_req(overlappeds[i].lpOverlapped); + uv__insert_pending_req(loop, req); } } @@ -496,18 +589,6 @@ 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; @@ -521,9 +602,9 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) { uv_update_time(loop); uv__run_timers(loop); - ran_pending = uv_process_reqs(loop); - uv_idle_invoke(loop); - uv_prepare_invoke(loop); + ran_pending = 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) @@ -534,9 +615,15 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) { else uv__poll_wine(loop, timeout); + /* Run one final update on the provider_idle_time in case uv__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 + * the timeout == 0) or was already updated b/c an event was received. + */ + uv__metrics_update_idle_time(loop); - uv_check_invoke(loop); - uv_process_endgames(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 @@ -547,6 +634,7 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) { * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from * the check. */ + uv_update_time(loop); uv__run_timers(loop); } diff --git a/src/win/detect-wakeup.c b/src/win/detect-wakeup.c index 72dfb7a..ab19361 100644 --- a/src/win/detect-wakeup.c +++ b/src/win/detect-wakeup.c @@ -1,3 +1,24 @@ +/* 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 "internal.h" #include "winapi.h" diff --git a/src/win/error.c b/src/win/error.c index 32ac5e5..20ac19e 100644 --- a/src/win/error.c +++ b/src/win/error.c @@ -72,6 +72,8 @@ int uv_translate_sys_error(int 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; + case ERROR_ACCESS_DENIED: return UV_EACCES; case ERROR_ADDRESS_ALREADY_ASSOCIATED: return UV_EADDRINUSE; case WSAEADDRINUSE: return UV_EADDRINUSE; case WSAEADDRNOTAVAIL: return UV_EADDRNOTAVAIL; @@ -104,7 +106,6 @@ int uv_translate_sys_error(int sys_errno) { case ERROR_SYMLINK_NOT_SUPPORTED: return UV_EINVAL; case WSAEINVAL: return UV_EINVAL; case WSAEPFNOSUPPORT: return UV_EINVAL; - case WSAESOCKTNOSUPPORT: return UV_EINVAL; case ERROR_BEGINNING_OF_MEDIA: return UV_EIO; case ERROR_BUS_RESET: return UV_EIO; case ERROR_CRC: return UV_EIO; @@ -154,7 +155,6 @@ int uv_translate_sys_error(int sys_errno) { case WSAENOTSOCK: return UV_ENOTSOCK; case ERROR_NOT_SUPPORTED: return UV_ENOTSUP; case ERROR_BROKEN_PIPE: return UV_EOF; - 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; @@ -167,6 +167,7 @@ int uv_translate_sys_error(int sys_errno) { case ERROR_NOT_SAME_DEVICE: return UV_EXDEV; case ERROR_INVALID_FUNCTION: return UV_EISDIR; case ERROR_META_EXPANSION_TOO_LONG: return UV_E2BIG; + case WSAESOCKTNOSUPPORT: return UV_ESOCKTNOSUPPORT; default: return UV_UNKNOWN; } } diff --git a/src/win/fs-event.c b/src/win/fs-event.c index acf8e11..6758c7c 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,9 +80,10 @@ 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; if (filename == NULL) { if (dir != NULL) @@ -97,12 +98,16 @@ static int uv_split_path(const WCHAR* filename, WCHAR** dir, if (i == 0) { if (dir) { - *dir = (WCHAR*)uv__malloc((MAX_PATH + 1) * sizeof(WCHAR)); + dir_len = GetCurrentDirectoryW(0, NULL); + if (dir_len == 0) { + return -1; + } + *dir = (WCHAR*)uv__malloc(dir_len * sizeof(WCHAR)); if (!*dir) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } - if (!GetCurrentDirectoryW(MAX_PATH, *dir)) { + if (!GetCurrentDirectoryW(dir_len, *dir)) { uv__free(*dir); *dir = NULL; return -1; @@ -155,9 +160,11 @@ int uv_fs_event_start(uv_fs_event_t* handle, int name_size, is_path_dir, size; DWORD attr, last_error; WCHAR* dir = NULL, *dir_to_watch, *pathw = NULL; - WCHAR short_path_buffer[MAX_PATH]; + DWORD short_path_buffer_len; + WCHAR *short_path_buffer; WCHAR* short_path, *long_path; + short_path = NULL; if (uv__is_active(handle)) return UV_EINVAL; @@ -230,20 +237,30 @@ int uv_fs_event_start(uv_fs_event_t* handle, */ /* Convert to short path. */ + short_path_buffer = NULL; + short_path_buffer_len = GetShortPathNameW(pathw, NULL, 0); + if (short_path_buffer_len == 0) { + goto short_path_done; + } + short_path_buffer = uv__malloc(short_path_buffer_len * sizeof(WCHAR)); + if (short_path_buffer == NULL) { + goto short_path_done; + } if (GetShortPathNameW(pathw, short_path_buffer, - ARRAY_SIZE(short_path_buffer))) { - short_path = short_path_buffer; - } else { - short_path = NULL; + short_path_buffer_len) == 0) { + uv__free(short_path_buffer); + short_path_buffer = NULL; } +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; } @@ -346,6 +363,8 @@ error: if (uv__is_active(handle)) uv__handle_stop(handle); + uv__free(short_path); + return uv_translate_sys_error(last_error); } @@ -404,7 +423,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; @@ -423,7 +442,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; } @@ -496,9 +515,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; @@ -555,27 +574,27 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, handle->cb(handle, NULL, 0, uv_translate_sys_error(err)); } - if (!(handle->flags & UV_HANDLE_CLOSING)) { - uv_fs_event_queue_readdirchanges(loop, handle); - } else { - uv_want_endgame(loop, (uv_handle_t*)handle); + if (handle->flags & UV_HANDLE_CLOSING) { + uv__want_endgame(loop, (uv_handle_t*)handle); + } else if (uv__is_active(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-fd-hash-inl.h b/src/win/fs-fd-hash-inl.h index 7a203d2..0b532af 100644 --- a/src/win/fs-fd-hash-inl.h +++ b/src/win/fs-fd-hash-inl.h @@ -1,3 +1,24 @@ +/* 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_WIN_FS_FD_HASH_INL_H_ #define UV_WIN_FS_FD_HASH_INL_H_ @@ -53,7 +74,8 @@ static struct uv__fd_hash_bucket_s uv__fd_hash[UV__FD_HASH_SIZE]; INLINE static void uv__fd_hash_init(void) { - int i, err; + size_t i; + int err; err = uv_mutex_init(&uv__fd_hash_mutex); if (err) { diff --git a/src/win/fs.c b/src/win/fs.c index 8502b07..7923079 100644 --- a/src/win/fs.c +++ b/src/win/fs.c @@ -46,7 +46,7 @@ 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) @@ -70,10 +70,7 @@ #define SET_REQ_RESULT(req, result_value) \ do { \ req->result = (result_value); \ - if (req->result == -1) { \ - req->sys_errno_ = _doserrno; \ - req->result = uv_translate_sys_error(req->sys_errno_); \ - } \ + assert(req->result != -1); \ } while (0) #define SET_REQ_WIN32_ERROR(req, sys_errno) \ @@ -95,30 +92,24 @@ return; \ } -#define MILLIONu (1000U * 1000U) -#define BILLIONu (1000U * 1000U * 1000U) +#define MILLION ((int64_t) 1000 * 1000) +#define BILLION ((int64_t) 1000 * 1000 * 1000) -#define FILETIME_TO_UINT(filetime) \ - (*((uint64_t*) &(filetime)) - (uint64_t) 116444736 * BILLIONu) - -#define FILETIME_TO_TIME_T(filetime) \ - (FILETIME_TO_UINT(filetime) / (10u * MILLIONu)) - -#define FILETIME_TO_TIME_NS(filetime, secs) \ - ((FILETIME_TO_UINT(filetime) - (secs * (uint64_t) 10 * MILLIONu)) * 100U) - -#define FILETIME_TO_TIMESPEC(ts, filetime) \ - do { \ - (ts).tv_sec = (long) FILETIME_TO_TIME_T(filetime); \ - (ts).tv_nsec = (long) FILETIME_TO_TIME_NS(filetime, (ts).tv_sec); \ - } while(0) +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); + if (ts->tv_nsec < 0) { + ts->tv_sec -= 1; + ts->tv_nsec += 1e9; + } +} #define TIME_T_TO_FILETIME(time, filetime_ptr) \ do { \ - uint64_t bigtime = ((uint64_t) ((time) * (uint64_t) 10 * MILLIONu)) + \ - (uint64_t) 116444736 * BILLIONu; \ - (filetime_ptr)->dwLowDateTime = bigtime & 0xFFFFFFFF; \ - (filetime_ptr)->dwHighDateTime = bigtime >> 32; \ + int64_t bigtime = ((time) * 10 * MILLION + 116444736 * BILLION); \ + (filetime_ptr)->dwLowDateTime = (uint64_t) bigtime & 0xFFFFFFFF; \ + (filetime_ptr)->dwHighDateTime = (uint64_t) bigtime >> 32; \ } while(0) #define IS_SLASH(c) ((c) == L'\\' || (c) == L'/') @@ -141,7 +132,7 @@ static int uv__file_symlink_usermode_flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGE static DWORD uv__allocation_granularity; -void uv_fs_init(void) { +void uv__fs_init(void) { SYSTEM_INFO system_info; GetSystemInfo(&system_info); @@ -250,13 +241,14 @@ INLINE static int fs__capture_path(uv_fs_t* req, const char* path, -INLINE static void uv_fs_req_init(uv_loop_t* loop, uv_fs_t* req, +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; @@ -321,6 +313,8 @@ INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr, WCHAR* w_target; DWORD w_target_len; DWORD bytes; + size_t i; + size_t len; if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, @@ -405,6 +399,38 @@ INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr, w_target += 4; w_target_len -= 4; + } else if (reparse_data->ReparseTag == IO_REPARSE_TAG_APPEXECLINK) { + /* String #3 in the list has the target filename. */ + if (reparse_data->AppExecLinkReparseBuffer.StringCount < 3) { + SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); + return -1; + } + w_target = reparse_data->AppExecLinkReparseBuffer.StringList; + /* The StringList buffer contains a list of strings separated by "\0", */ + /* with "\0\0" terminating the list. Move to the 3rd string in the list: */ + for (i = 0; i < 2; ++i) { + len = wcslen(w_target); + if (len == 0) { + SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); + return -1; + } + w_target += len + 1; + } + w_target_len = wcslen(w_target); + if (w_target_len == 0) { + SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); + return -1; + } + /* Make sure it is an absolute path. */ + if (!(w_target_len >= 3 && + ((w_target[0] >= L'a' && w_target[0] <= L'z') || + (w_target[0] >= L'A' && w_target[0] <= L'Z')) && + w_target[1] == L':' && + w_target[2] == L'\\')) { + SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); + return -1; + } + } else { /* Reparse tag does not indicate a symlink. */ SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); @@ -695,14 +721,14 @@ void fs__close(uv_fs_t* req) { assert(errno == EBADF); SET_REQ_UV_ERROR(req, UV_EBADF, ERROR_INVALID_HANDLE); } else { - req->result = 0; + SET_REQ_RESULT(req, 0); } } LONG fs__filemap_ex_filter(LONG excode, PEXCEPTION_POINTERS pep, int* perror) { - if (excode != EXCEPTION_IN_PAGE_ERROR) { + if (excode != (LONG)EXCEPTION_IN_PAGE_ERROR) { return EXCEPTION_CONTINUE_SEARCH; } @@ -732,7 +758,7 @@ void fs__read_filemap(uv_fs_t* req, struct uv__fd_info_s* fd_info) { void* view; if (rw_flags == UV_FS_O_WRONLY) { - SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED); + SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FLAGS); return; } if (fd_info->is_directory) { @@ -781,10 +807,10 @@ void fs__read_filemap(uv_fs_t* req, struct uv__fd_info_s* fd_info) { for (index = 0; index < req->fs.info.nbufs && done_read < read_size; ++index) { - int err = 0; size_t this_read_size = MIN(req->fs.info.bufs[index].len, read_size - done_read); #ifdef _MSC_VER + int err = 0; __try { #endif memcpy(req->fs.info.bufs[index].base, @@ -886,7 +912,11 @@ void fs__read(uv_fs_t* req) { SET_REQ_RESULT(req, bytes); } else { error = GetLastError(); - if (error == ERROR_HANDLE_EOF) { + if (error == ERROR_ACCESS_DENIED) { + error = ERROR_INVALID_FLAGS; + } + + if (error == ERROR_HANDLE_EOF || error == ERROR_BROKEN_PIPE) { SET_REQ_RESULT(req, bytes); } else { SET_REQ_WIN32_ERROR(req, error); @@ -903,14 +933,14 @@ void fs__write_filemap(uv_fs_t* req, HANDLE file, (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); size_t write_size, done_write; unsigned int index; - LARGE_INTEGER zero, pos, end_pos; + LARGE_INTEGER pos, end_pos; size_t view_offset; LARGE_INTEGER view_base; void* view; FILETIME ft; if (rw_flags == UV_FS_O_RDONLY) { - SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED); + SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FLAGS); return; } if (fd_info->is_directory) { @@ -928,7 +958,6 @@ void fs__write_filemap(uv_fs_t* req, HANDLE file, return; } - zero.QuadPart = 0; if (force_append) { pos = fd_info->size; } else if (req->fs.info.offset == -1) { @@ -979,8 +1008,8 @@ void fs__write_filemap(uv_fs_t* req, HANDLE file, done_write = 0; for (index = 0; index < req->fs.info.nbufs; ++index) { - int err = 0; #ifdef _MSC_VER + int err = 0; __try { #endif memcpy((char*)view + view_offset + done_write, @@ -1027,6 +1056,7 @@ void fs__write(uv_fs_t* req) { OVERLAPPED overlapped, *overlapped_ptr; LARGE_INTEGER offset_; DWORD bytes; + DWORD error; int result; unsigned int index; LARGE_INTEGER original_position; @@ -1086,14 +1116,23 @@ void fs__write(uv_fs_t* req) { if (result || bytes > 0) { SET_REQ_RESULT(req, bytes); } else { - SET_REQ_WIN32_ERROR(req, GetLastError()); + error = GetLastError(); + + if (error == ERROR_ACCESS_DENIED) { + error = ERROR_INVALID_FLAGS; + } + + SET_REQ_WIN32_ERROR(req, error); } } void fs__rmdir(uv_fs_t* req) { int result = _wrmdir(req->file.pathw); - SET_REQ_RESULT(req, result); + if (result == -1) + SET_REQ_WIN32_ERROR(req, _doserrno); + else + SET_REQ_RESULT(req, 0); } @@ -1186,12 +1225,13 @@ void fs__unlink(uv_fs_t* req) { void fs__mkdir(uv_fs_t* req) { /* TODO: use req->mode. */ - req->result = _wmkdir(req->file.pathw); - if (req->result == -1) { - req->sys_errno_ = _doserrno; - req->result = req->sys_errno_ == ERROR_INVALID_NAME - ? UV_EINVAL - : uv_translate_sys_error(req->sys_errno_); + if (CreateDirectoryW(req->file.pathw, NULL)) { + SET_REQ_RESULT(req, 0); + } else { + SET_REQ_WIN32_ERROR(req, GetLastError()); + if (req->sys_errno_ == ERROR_INVALID_NAME || + req->sys_errno_ == ERROR_DIRECTORY) + req->result = UV_EINVAL; } } @@ -1207,19 +1247,21 @@ void fs__mktemp(uv_fs_t* req, uv__fs_mktemp_func func) { unsigned int tries, i; size_t len; uint64_t v; - + char* path; + + path = (char*)req->path; len = wcslen(req->file.pathw); ep = req->file.pathw + len; if (len < num_x || wcsncmp(ep - num_x, L"XXXXXX", num_x)) { SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); - return; + goto clobber; } tries = TMP_MAX; do { if (uv__random_rtlgenrandom((void *)&v, sizeof(v)) < 0) { SET_REQ_UV_ERROR(req, UV_EIO, ERROR_IO_DEVICE); - break; + goto clobber; } cp = ep - num_x; @@ -1230,25 +1272,29 @@ void fs__mktemp(uv_fs_t* req, uv__fs_mktemp_func func) { if (func(req)) { if (req->result >= 0) { - len = strlen(req->path); - wcstombs((char*) req->path + len - num_x, ep - num_x, num_x); + len = strlen(path); + wcstombs(path + len - num_x, ep - num_x, num_x); } - break; + return; } } while (--tries); - if (tries == 0) { - SET_REQ_RESULT(req, -1); - } + SET_REQ_WIN32_ERROR(req, GetLastError()); + +clobber: + path[0] = '\0'; } static int fs__mkdtemp_func(uv_fs_t* req) { - if (_wmkdir(req->file.pathw) == 0) { + DWORD error; + if (CreateDirectoryW(req->file.pathw, NULL)) { SET_REQ_RESULT(req, 0); return 1; - } else if (errno != EEXIST) { - SET_REQ_RESULT(req, -1); + } + error = GetLastError(); + if (error != ERROR_ALREADY_EXISTS) { + SET_REQ_WIN32_ERROR(req, error); return 1; } @@ -1369,7 +1415,7 @@ void fs__scandir(uv_fs_t* req) { /* If the handle is not a directory, we'll get STATUS_INVALID_PARAMETER. * This should be reported back as UV_ENOTDIR. */ - if (status == STATUS_INVALID_PARAMETER) + if (status == (NTSTATUS)STATUS_INVALID_PARAMETER) goto not_a_directory_error; while (NT_SUCCESS(status)) { @@ -1751,10 +1797,14 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf, statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) | ((_S_IREAD | _S_IWRITE) >> 6); - FILETIME_TO_TIMESPEC(statbuf->st_atim, file_info.BasicInformation.LastAccessTime); - FILETIME_TO_TIMESPEC(statbuf->st_ctim, file_info.BasicInformation.ChangeTime); - FILETIME_TO_TIMESPEC(statbuf->st_mtim, file_info.BasicInformation.LastWriteTime); - FILETIME_TO_TIMESPEC(statbuf->st_birthtim, file_info.BasicInformation.CreationTime); + uv__filetime_to_timespec(&statbuf->st_atim, + file_info.BasicInformation.LastAccessTime.QuadPart); + uv__filetime_to_timespec(&statbuf->st_ctim, + file_info.BasicInformation.ChangeTime.QuadPart); + uv__filetime_to_timespec(&statbuf->st_mtim, + file_info.BasicInformation.LastWriteTime.QuadPart); + uv__filetime_to_timespec(&statbuf->st_birthtim, + file_info.BasicInformation.CreationTime.QuadPart); statbuf->st_ino = file_info.InternalInformation.IndexNumber.QuadPart; @@ -1830,8 +1880,9 @@ INLINE static DWORD fs__stat_impl_from_path(WCHAR* path, NULL); if (handle == INVALID_HANDLE_VALUE) - ret = GetLastError(); - else if (fs__stat_handle(handle, statbuf, do_lstat) != 0) + return GetLastError(); + + if (fs__stat_handle(handle, statbuf, do_lstat) != 0) ret = GetLastError(); else ret = 0; @@ -1860,7 +1911,7 @@ INLINE static void fs__stat_impl(uv_fs_t* req, int do_lstat) { } req->ptr = &req->statbuf; - req->result = 0; + SET_REQ_RESULT(req, 0); } @@ -1895,7 +1946,7 @@ static void fs__fstat(uv_fs_t* req) { } req->ptr = &req->statbuf; - req->result = 0; + SET_REQ_RESULT(req, 0); } @@ -2122,7 +2173,10 @@ static void fs__access(uv_fs_t* req) { static void fs__chmod(uv_fs_t* req) { int result = _wchmod(req->file.pathw, req->fs.info.mode); - SET_REQ_RESULT(req, result); + if (result == -1) + SET_REQ_WIN32_ERROR(req, _doserrno); + else + SET_REQ_RESULT(req, 0); } @@ -2225,32 +2279,66 @@ INLINE static int fs__utime_handle(HANDLE handle, double atime, double mtime) { return 0; } - -static void fs__utime(uv_fs_t* req) { +INLINE static DWORD fs__utime_impl_from_path(WCHAR* path, + double atime, + double mtime, + int do_lutime) { HANDLE handle; + DWORD flags; + DWORD ret; - handle = CreateFileW(req->file.pathw, + flags = FILE_FLAG_BACKUP_SEMANTICS; + if (do_lutime) { + flags |= FILE_FLAG_OPEN_REPARSE_POINT; + } + + handle = CreateFileW(path, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, + flags, NULL); - if (handle == INVALID_HANDLE_VALUE) { - SET_REQ_WIN32_ERROR(req, GetLastError()); - return; - } + if (handle == INVALID_HANDLE_VALUE) + return GetLastError(); + + if (fs__utime_handle(handle, atime, mtime) != 0) + ret = GetLastError(); + else + ret = 0; + + CloseHandle(handle); + return ret; +} + +INLINE static void fs__utime_impl(uv_fs_t* req, int do_lutime) { + DWORD error; + + error = fs__utime_impl_from_path(req->file.pathw, + req->fs.time.atime, + req->fs.time.mtime, + do_lutime); + + if (error != 0) { + if (do_lutime && + (error == ERROR_SYMLINK_NOT_SUPPORTED || + error == ERROR_NOT_A_REPARSE_POINT)) { + /* Opened file is a reparse point but not a symlink. Try again. */ + fs__utime_impl(req, 0); + } else { + /* utime failed. */ + SET_REQ_WIN32_ERROR(req, error); + } - if (fs__utime_handle(handle, req->fs.time.atime, req->fs.time.mtime) != 0) { - SET_REQ_WIN32_ERROR(req, GetLastError()); - CloseHandle(handle); return; } - CloseHandle(handle); + SET_REQ_RESULT(req, 0); +} - req->result = 0; +static void fs__utime(uv_fs_t* req) { + fs__utime_impl(req, /* do_lutime */ 0); } @@ -2271,17 +2359,20 @@ static void fs__futime(uv_fs_t* req) { return; } - req->result = 0; + SET_REQ_RESULT(req, 0); +} + +static void fs__lutime(uv_fs_t* req) { + fs__utime_impl(req, /* do_lutime */ 1); } static void fs__link(uv_fs_t* req) { DWORD r = CreateHardLinkW(req->fs.info.new_pathw, req->file.pathw, NULL); - if (r == 0) { + if (r == 0) SET_REQ_WIN32_ERROR(req, GetLastError()); - } else { - req->result = 0; - } + else + SET_REQ_RESULT(req, 0); } @@ -2601,17 +2692,17 @@ static void fs__realpath(uv_fs_t* req) { static void fs__chown(uv_fs_t* req) { - req->result = 0; + SET_REQ_RESULT(req, 0); } static void fs__fchown(uv_fs_t* req) { - req->result = 0; + SET_REQ_RESULT(req, 0); } static void fs__lchown(uv_fs_t* req) { - req->result = 0; + SET_REQ_RESULT(req, 0); } @@ -2621,14 +2712,62 @@ static void fs__statfs(uv_fs_t* req) { DWORD bytes_per_sector; DWORD free_clusters; DWORD total_clusters; + WCHAR* pathw; - if (0 == GetDiskFreeSpaceW(req->file.pathw, + pathw = req->file.pathw; +retry_get_disk_free_space: + if (0 == GetDiskFreeSpaceW(pathw, §ors_per_cluster, &bytes_per_sector, &free_clusters, &total_clusters)) { - SET_REQ_WIN32_ERROR(req, GetLastError()); - return; + DWORD err; + WCHAR* fpart; + size_t len; + DWORD ret; + BOOL is_second; + + err = GetLastError(); + is_second = pathw != req->file.pathw; + if (err != ERROR_DIRECTORY || is_second) { + if (is_second) + uv__free(pathw); + + SET_REQ_WIN32_ERROR(req, err); + return; + } + + len = MAX_PATH + 1; + pathw = uv__malloc(len * sizeof(*pathw)); + if (pathw == NULL) { + SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); + return; + } +retry_get_full_path_name: + ret = GetFullPathNameW(req->file.pathw, + len, + pathw, + &fpart); + if (ret == 0) { + uv__free(pathw); + SET_REQ_WIN32_ERROR(req, err); + return; + } else if (ret > len) { + len = ret; + pathw = uv__reallocf(pathw, len * sizeof(*pathw)); + if (pathw == NULL) { + SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); + return; + } + goto retry_get_full_path_name; + } + if (fpart != 0) + *fpart = L'\0'; + + goto retry_get_disk_free_space; + } + if (pathw != req->file.pathw) { + uv__free(pathw); } stat_fs = uv__malloc(sizeof(*stat_fs)); @@ -2670,6 +2809,7 @@ static void uv__fs_work(struct uv__work* w) { XX(FTRUNCATE, ftruncate) XX(UTIME, utime) XX(FUTIME, futime) + XX(LUTIME, lutime) XX(ACCESS, access) XX(CHMOD, chmod) XX(FCHMOD, fchmod) @@ -2707,7 +2847,7 @@ static void uv__fs_done(struct uv__work* w, int status) { if (status == UV_ECANCELED) { assert(req->result == 0); - req->result = UV_ECANCELED; + SET_REQ_UV_ERROR(req, UV_ECANCELED, 0); } req->cb(req); @@ -2753,7 +2893,8 @@ int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, INIT(UV_FS_OPEN); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } req->fs.info.file_flags = flags; @@ -2778,8 +2919,10 @@ int uv_fs_read(uv_loop_t* loop, uv_fs_cb cb) { INIT(UV_FS_READ); - if (bufs == NULL || nbufs == 0) + if (bufs == NULL || nbufs == 0) { + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); return UV_EINVAL; + } req->file.fd = fd; @@ -2788,8 +2931,10 @@ int uv_fs_read(uv_loop_t* loop, if (nbufs > ARRAY_SIZE(req->fs.info.bufsml)) req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs)); - if (req->fs.info.bufs == NULL) + if (req->fs.info.bufs == NULL) { + SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); return UV_ENOMEM; + } memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs)); @@ -2807,8 +2952,10 @@ int uv_fs_write(uv_loop_t* loop, uv_fs_cb cb) { INIT(UV_FS_WRITE); - if (bufs == NULL || nbufs == 0) + if (bufs == NULL || nbufs == 0) { + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); return UV_EINVAL; + } req->file.fd = fd; @@ -2817,8 +2964,10 @@ int uv_fs_write(uv_loop_t* loop, if (nbufs > ARRAY_SIZE(req->fs.info.bufsml)) req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs)); - if (req->fs.info.bufs == NULL) + if (req->fs.info.bufs == NULL) { + SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); return UV_ENOMEM; + } memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs)); @@ -2834,7 +2983,8 @@ int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, INIT(UV_FS_UNLINK); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } POST; @@ -2848,7 +2998,8 @@ int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, INIT(UV_FS_MKDIR); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } req->fs.info.mode = mode; @@ -2864,8 +3015,10 @@ int uv_fs_mkdtemp(uv_loop_t* loop, INIT(UV_FS_MKDTEMP); err = fs__capture_path(req, tpl, NULL, TRUE); - if (err) - return uv_translate_sys_error(err); + if (err) { + SET_REQ_WIN32_ERROR(req, err); + return req->result; + } POST; } @@ -2879,8 +3032,10 @@ int uv_fs_mkstemp(uv_loop_t* loop, INIT(UV_FS_MKSTEMP); err = fs__capture_path(req, tpl, NULL, TRUE); - if (err) - return uv_translate_sys_error(err); + if (err) { + SET_REQ_WIN32_ERROR(req, err); + return req->result; + } POST; } @@ -2892,7 +3047,8 @@ int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { INIT(UV_FS_RMDIR); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } POST; @@ -2906,7 +3062,8 @@ int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, INIT(UV_FS_SCANDIR); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } req->fs.info.file_flags = flags; @@ -2921,8 +3078,10 @@ int uv_fs_opendir(uv_loop_t* loop, INIT(UV_FS_OPENDIR); err = fs__capture_path(req, path, NULL, cb != NULL); - if (err) - return uv_translate_sys_error(err); + if (err) { + SET_REQ_WIN32_ERROR(req, err); + return req->result; + } POST; } @@ -2935,6 +3094,7 @@ int uv_fs_readdir(uv_loop_t* loop, if (dir == NULL || dir->dirents == NULL || dir->dir_handle == INVALID_HANDLE_VALUE) { + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); return UV_EINVAL; } @@ -2947,8 +3107,10 @@ int uv_fs_closedir(uv_loop_t* loop, uv_dir_t* dir, uv_fs_cb cb) { INIT(UV_FS_CLOSEDIR); - if (dir == NULL) + if (dir == NULL) { + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); return UV_EINVAL; + } req->ptr = dir; POST; } @@ -2960,7 +3122,8 @@ int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, INIT(UV_FS_LINK); err = fs__capture_path(req, path, new_path, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } POST; @@ -2974,7 +3137,8 @@ int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, INIT(UV_FS_SYMLINK); err = fs__capture_path(req, path, new_path, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } req->fs.info.file_flags = flags; @@ -2989,7 +3153,8 @@ int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, INIT(UV_FS_READLINK); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } POST; @@ -3003,12 +3168,14 @@ int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path, INIT(UV_FS_REALPATH); if (!path) { + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); return UV_EINVAL; } err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } POST; @@ -3022,7 +3189,8 @@ int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, INIT(UV_FS_CHOWN); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } POST; @@ -3043,8 +3211,10 @@ int uv_fs_lchown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, INIT(UV_FS_LCHOWN); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } + POST; } @@ -3055,7 +3225,8 @@ int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { INIT(UV_FS_STAT); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } POST; @@ -3068,7 +3239,8 @@ int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { INIT(UV_FS_LSTAT); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } POST; @@ -3089,7 +3261,8 @@ int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, INIT(UV_FS_RENAME); err = fs__capture_path(req, path, new_path, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } POST; @@ -3132,13 +3305,15 @@ int uv_fs_copyfile(uv_loop_t* loop, if (flags & ~(UV_FS_COPYFILE_EXCL | UV_FS_COPYFILE_FICLONE | UV_FS_COPYFILE_FICLONE_FORCE)) { + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); return UV_EINVAL; } err = fs__capture_path(req, path, new_path, cb != NULL); - - if (err) - return uv_translate_sys_error(err); + if (err) { + SET_REQ_WIN32_ERROR(req, err); + return req->result; + } req->fs.info.file_flags = flags; POST; @@ -3165,8 +3340,10 @@ int uv_fs_access(uv_loop_t* loop, INIT(UV_FS_ACCESS); err = fs__capture_path(req, path, NULL, cb != NULL); - if (err) - return uv_translate_sys_error(err); + if (err) { + SET_REQ_WIN32_ERROR(req, err); + return req->result; + } req->fs.info.mode = flags; POST; @@ -3180,7 +3357,8 @@ int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, INIT(UV_FS_CHMOD); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } req->fs.info.mode = mode; @@ -3204,7 +3382,8 @@ int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, INIT(UV_FS_UTIME); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } req->fs.time.atime = atime; @@ -3222,6 +3401,22 @@ int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file fd, double atime, POST; } +int uv_fs_lutime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, + double mtime, uv_fs_cb cb) { + int err; + + INIT(UV_FS_LUTIME); + err = fs__capture_path(req, path, NULL, cb != NULL); + if (err) { + SET_REQ_WIN32_ERROR(req, err); + return req->result; + } + + req->fs.time.atime = atime; + req->fs.time.mtime = mtime; + POST; +} + int uv_fs_statfs(uv_loop_t* loop, uv_fs_t* req, @@ -3231,8 +3426,14 @@ int uv_fs_statfs(uv_loop_t* loop, INIT(UV_FS_STATFS); err = fs__capture_path(req, path, NULL, cb != NULL); - if (err) - return uv_translate_sys_error(err); + if (err) { + SET_REQ_WIN32_ERROR(req, err); + return req->result; + } POST; } + +int uv_fs_get_system_error(const uv_fs_t* req) { + return req->sys_errno_; +} diff --git a/src/win/handle-inl.h b/src/win/handle-inl.h index 82c657d..5c843c2 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) @@ -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 61e4df6..53a81fd 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 058ddb8..17e399b 100644 --- a/src/win/internal.h +++ b/src/win/internal.h @@ -72,25 +72,25 @@ 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_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,23 +104,23 @@ 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); /* * Pipes */ -int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, - char* name, size_t nameSize); +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, @@ -131,74 +131,74 @@ int uv__pipe_write(uv_loop_t* loop, uv_stream_t* send_handle, uv_write_cb cb); -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_cleanup(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 + * 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, +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 + * 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, +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); +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 +206,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); /* @@ -266,7 +260,7 @@ void uv__fs_poll_endgame(uv_loop_t* loop, uv_fs_poll_t* handle); */ void uv__util_init(void); -uint64_t uv__hrtime(double scale); +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); @@ -299,28 +293,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 */ diff --git a/src/win/loop-watcher.c b/src/win/loop-watcher.c index ad7fbea..fad9e8a 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 fc0112a..a4bab2c 100644 --- a/src/win/pipe.c +++ b/src/win/pipe.c @@ -98,13 +98,13 @@ 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) { +static void uv__unique_pipe_name(char* ptr, char* name, size_t size) { snprintf(name, size, "\\\\?\\pipe\\uv\\%p-%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; @@ -120,8 +120,8 @@ 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) { + uv__connection_init((uv_stream_t*) handle); handle->read_req.data = handle; handle->pipe.conn.eof_timer = NULL; assert(!(handle->flags & UV_HANDLE_PIPESERVER)); @@ -202,17 +202,17 @@ static void close_pipe(uv_pipe_t* pipe) { } -int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, - char* name, size_t nameSize) { +static int uv__pipe_server( + HANDLE* pipeHandle_ptr, DWORD access, + char* name, size_t nameSize, char* random) { HANDLE pipeHandle; int err; - char* ptr = (char*)handle; for (;;) { - uv_unique_pipe_name(ptr, name, nameSize); + uv__unique_pipe_name(random, name, nameSize); pipeHandle = CreateNamedPipeA(name, - access | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE | WRITE_DAC, + access | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 65536, 65536, 0, NULL); @@ -226,37 +226,235 @@ int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, goto error; } - /* Pipe name collision. Increment the pointer and try again. */ - ptr++; + /* Pipe name collision. Increment the random number and try again. */ + random++; } - if (CreateIoCompletionPort(pipeHandle, + *pipeHandle_ptr = pipeHandle; + + return 0; + + error: + if (pipeHandle != INVALID_HANDLE_VALUE) + CloseHandle(pipeHandle); + + return err; +} + + +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) { + /* allowed flags are: UV_READABLE_PIPE | UV_WRITABLE_PIPE | UV_NONBLOCK_PIPE */ + char pipe_name[64]; + SECURITY_ATTRIBUTES sa; + DWORD server_access; + DWORD client_access; + HANDLE server_pipe; + HANDLE client_pipe; + int err; + + server_pipe = INVALID_HANDLE_VALUE; + client_pipe = INVALID_HANDLE_VALUE; + + server_access = 0; + if (server_flags & UV_READABLE_PIPE) + server_access |= PIPE_ACCESS_INBOUND; + if (server_flags & UV_WRITABLE_PIPE) + server_access |= PIPE_ACCESS_OUTBOUND; + if (server_flags & UV_NONBLOCK_PIPE) + server_access |= FILE_FLAG_OVERLAPPED; + server_access |= WRITE_DAC; + + client_access = 0; + if (client_flags & UV_READABLE_PIPE) + client_access |= GENERIC_READ; + else + client_access |= FILE_READ_ATTRIBUTES; + if (client_flags & UV_WRITABLE_PIPE) + client_access |= GENERIC_WRITE; + else + client_access |= FILE_WRITE_ATTRIBUTES; + client_access |= WRITE_DAC; + + /* Create server pipe handle. */ + err = uv__pipe_server(&server_pipe, + server_access, + pipe_name, + sizeof(pipe_name), + random); + if (err) + goto error; + + /* Create client pipe handle. */ + sa.nLength = sizeof sa; + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = inherit_client; + + client_pipe = CreateFileA(pipe_name, + client_access, + 0, + &sa, + OPEN_EXISTING, + (client_flags & UV_NONBLOCK_PIPE) ? FILE_FLAG_OVERLAPPED : 0, + NULL); + if (client_pipe == INVALID_HANDLE_VALUE) { + err = GetLastError(); + goto error; + } + +#ifndef NDEBUG + /* Validate that the pipe was opened in the right mode. */ + { + DWORD mode; + BOOL r; + r = GetNamedPipeHandleState(client_pipe, &mode, NULL, NULL, NULL, NULL, 0); + if (r == TRUE) { + assert(mode == (PIPE_READMODE_BYTE | PIPE_WAIT)); + } else { + fprintf(stderr, "libuv assertion failure: GetNamedPipeHandleState failed\n"); + } + } +#endif + + /* Do a blocking ConnectNamedPipe. This should not block because we have + * both ends of the pipe created. */ + if (!ConnectNamedPipe(server_pipe, NULL)) { + if (GetLastError() != ERROR_PIPE_CONNECTED) { + err = GetLastError(); + goto error; + } + } + + *client_pipe_ptr = client_pipe; + *server_pipe_ptr = server_pipe; + return 0; + + error: + if (server_pipe != INVALID_HANDLE_VALUE) + CloseHandle(server_pipe); + + if (client_pipe != INVALID_HANDLE_VALUE) + CloseHandle(client_pipe); + + return err; +} + + +int uv_pipe(uv_file fds[2], int read_flags, int write_flags) { + uv_file temp[2]; + int err; + HANDLE readh; + HANDLE writeh; + + /* Make the server side the inbound (read) end, */ + /* so that both ends will have FILE_READ_ATTRIBUTES permission. */ + /* 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]); + if (err != 0) + return err; + temp[0] = _open_osfhandle((intptr_t) readh, 0); + if (temp[0] == -1) { + if (errno == UV_EMFILE) + err = UV_EMFILE; + else + err = UV_UNKNOWN; + CloseHandle(readh); + CloseHandle(writeh); + return err; + } + temp[1] = _open_osfhandle((intptr_t) writeh, 0); + if (temp[1] == -1) { + if (errno == UV_EMFILE) + err = UV_EMFILE; + else + err = UV_UNKNOWN; + _close(temp[0]); + CloseHandle(writeh); + return err; + } + fds[0] = temp[0]; + fds[1] = temp[1]; + return 0; +} + + +int uv__create_stdio_pipe_pair(uv_loop_t* loop, + uv_pipe_t* parent_pipe, HANDLE* child_pipe_ptr, unsigned int flags) { + /* The parent_pipe is always the server_pipe and kept by libuv. + * The child_pipe is always the client_pipe and is passed to the child. + * The flags are specified with respect to their usage in the child. */ + HANDLE server_pipe; + HANDLE client_pipe; + unsigned int server_flags; + unsigned int client_flags; + int err; + + server_pipe = INVALID_HANDLE_VALUE; + client_pipe = INVALID_HANDLE_VALUE; + + server_flags = 0; + client_flags = 0; + if (flags & UV_READABLE_PIPE) { + /* The server needs inbound (read) access too, otherwise CreateNamedPipe() + * won't give us the FILE_READ_ATTRIBUTES permission. We need that to probe + * the state of the write buffer when we're trying to shutdown the pipe. */ + server_flags |= UV_READABLE_PIPE | UV_WRITABLE_PIPE; + client_flags |= UV_READABLE_PIPE; + } + if (flags & UV_WRITABLE_PIPE) { + server_flags |= UV_READABLE_PIPE; + client_flags |= UV_WRITABLE_PIPE; + } + server_flags |= UV_NONBLOCK_PIPE; + if (flags & UV_NONBLOCK_PIPE || parent_pipe->ipc) { + client_flags |= UV_NONBLOCK_PIPE; + } + + err = uv__create_pipe_pair(&server_pipe, &client_pipe, + server_flags, client_flags, 1, (char*) server_pipe); + if (err) + goto error; + + if (CreateIoCompletionPort(server_pipe, loop->iocp, - (ULONG_PTR)handle, + (ULONG_PTR) parent_pipe, 0) == NULL) { err = GetLastError(); goto error; } - uv_pipe_connection_init(handle); - handle->handle = pipeHandle; + uv__pipe_connection_init(parent_pipe); + parent_pipe->handle = server_pipe; + *child_pipe_ptr = client_pipe; + + /* The server end is now readable and/or writable. */ + if (flags & UV_READABLE_PIPE) + parent_pipe->flags |= UV_HANDLE_WRITABLE; + if (flags & UV_WRITABLE_PIPE) + parent_pipe->flags |= UV_HANDLE_READABLE; return 0; error: - if (pipeHandle != INVALID_HANDLE_VALUE) { - CloseHandle(pipeHandle); - } + if (server_pipe != INVALID_HANDLE_VALUE) + CloseHandle(server_pipe); + + if (client_pipe != INVALID_HANDLE_VALUE) + CloseHandle(client_pipe); return err; } -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; @@ -380,7 +578,7 @@ static DWORD WINAPI pipe_shutdown_thread_proc(void* parameter) { } -void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { +void uv__pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { int err; DWORD result; uv_shutdown_t* req; @@ -432,7 +630,7 @@ void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { if (pipe_info.OutboundQuota == pipe_info.WriteQuotaAvailable) { /* Short-circuit, no need to call FlushFileBuffers. */ - uv_insert_pending_req(loop, (uv_req_t*) req); + uv__insert_pending_req(loop, (uv_req_t*) req); return; } @@ -554,7 +752,7 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { /* Convert name to UTF16. */ nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR); - handle->name = (WCHAR*)uv__malloc(nameSize); + handle->name = uv__malloc(nameSize); if (!handle->name) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } @@ -621,15 +819,14 @@ static DWORD WINAPI pipe_connect_thread_proc(void* parameter) { while (WaitNamedPipeW(handle->name, 30000)) { /* The pipe is now available, try to connect. */ pipeHandle = open_named_pipe(handle->name, &duplex_flags); - if (pipeHandle != INVALID_HANDLE_VALUE) { + if (pipeHandle != INVALID_HANDLE_VALUE) break; - } SwitchToThread(); } if (pipeHandle != INVALID_HANDLE_VALUE && - !uv_set_pipe_handle(loop, handle, pipeHandle, -1, duplex_flags)) { + !uv__set_pipe_handle(loop, handle, pipeHandle, -1, duplex_flags)) { SET_REQ_SUCCESS(req); } else { SET_REQ_ERROR(req, GetLastError()); @@ -655,7 +852,7 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, /* Convert name to UTF16. */ nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR); - handle->name = (WCHAR*)uv__malloc(nameSize); + handle->name = uv__malloc(nameSize); if (!handle->name) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } @@ -693,17 +890,17 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, assert(pipeHandle != INVALID_HANDLE_VALUE); - if (uv_set_pipe_handle(loop, - (uv_pipe_t*) req->handle, - pipeHandle, - -1, - duplex_flags)) { + if (uv__set_pipe_handle(loop, + (uv_pipe_t*) req->handle, + pipeHandle, + -1, + duplex_flags)) { err = GetLastError(); goto error; } SET_REQ_SUCCESS(req); - 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; @@ -714,13 +911,12 @@ error: handle->name = NULL; } - if (pipeHandle != INVALID_HANDLE_VALUE) { + if (pipeHandle != INVALID_HANDLE_VALUE) CloseHandle(pipeHandle); - } /* 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; @@ -784,7 +980,7 @@ void uv__pipe_read_stop(uv_pipe_t* 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_cleanup(uv_loop_t* loop, uv_pipe_t* handle) { int i; HANDLE pipeHandle; @@ -817,7 +1013,7 @@ void uv_pipe_cleanup(uv_loop_t* loop, uv_pipe_t* handle) { } -void uv_pipe_close(uv_loop_t* loop, uv_pipe_t* 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); @@ -828,10 +1024,10 @@ void uv_pipe_close(uv_loop_t* loop, uv_pipe_t* handle) { DECREASE_ACTIVE_COUNT(loop, handle); } - uv_pipe_cleanup(loop, handle); + uv__pipe_cleanup(loop, handle); if (handle->reqs_pending == 0) { - uv_want_endgame(loop, (uv_handle_t*) handle); + uv__want_endgame(loop, (uv_handle_t*) handle); } handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); @@ -839,13 +1035,13 @@ void uv_pipe_close(uv_loop_t* loop, uv_pipe_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; } @@ -865,7 +1061,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; } @@ -875,7 +1071,7 @@ 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; @@ -914,7 +1110,7 @@ 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); + uv__pipe_connection_init(pipe_client); pipe_client->handle = req->pipeHandle; pipe_client->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; @@ -925,7 +1121,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); } } @@ -934,7 +1130,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; @@ -966,7 +1162,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; @@ -1056,7 +1252,6 @@ static DWORD WINAPI uv_pipe_writefile_thread_proc(void* parameter) { assert(req != NULL); assert(req->type == UV_WRITE); assert(handle->type == UV_NAMED_PIPE); - assert(req->write_buffer.base); result = WriteFile(handle->handle, req->write_buffer.base, @@ -1111,7 +1306,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; @@ -1170,15 +1365,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; @@ -1196,14 +1391,14 @@ int uv_pipe_read_start(uv_pipe_t* handle, 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) { @@ -1239,7 +1434,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, @@ -1380,9 +1575,9 @@ static int uv__pipe_write_data(uv_loop_t* loop, 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. */ @@ -1595,20 +1790,19 @@ 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. */ eof_timer_destroy(handle); - handle->flags &= ~UV_HANDLE_READABLE; uv_read_stop((uv_stream_t*) handle); handle->read_cb((uv_stream_t*) handle, UV_EOF, &buf); } -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. */ @@ -1620,12 +1814,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, +static void uv__pipe_read_error_or_eof(uv_loop_t* loop, uv_pipe_t* handle, int 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); } } @@ -1696,7 +1890,7 @@ static DWORD uv__pipe_read_data(uv_loop_t* loop, /* 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); + uv__pipe_read_error_or_eof(loop, handle, GetLastError(), buf); return 0; /* Break out of read loop. */ } @@ -1783,14 +1977,14 @@ invalid: 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) { assert(handle->type == UV_NAMED_PIPE); handle->flags &= ~(UV_HANDLE_READ_PENDING | UV_HANDLE_CANCELLATION_PENDING); @@ -1811,7 +2005,7 @@ void uv_process_pipe_read_req(uv_loop_t* loop, * 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 @@ -1821,7 +2015,7 @@ void uv_process_pipe_read_req(uv_loop_t* loop, /* 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_); + 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. */ @@ -1850,12 +2044,12 @@ void uv_process_pipe_read_req(uv_loop_t* loop, /* 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; @@ -1897,26 +2091,26 @@ 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); + uv__want_endgame(loop, (uv_handle_t*)handle); } 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_cleanup(). */ assert(req->pipeHandle == INVALID_HANDLE_VALUE); DECREASE_PENDING_REQ_COUNT(handle); return; @@ -1936,7 +2130,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); } } @@ -1944,7 +2138,7 @@ 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) { int err; @@ -1955,7 +2149,7 @@ void uv_process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* handle, if (req->cb) { err = 0; if (REQ_SUCCESS(req)) { - uv_pipe_connection_init(handle); + uv__pipe_connection_init(handle); } else { err = GET_REQ_ERROR(req); } @@ -1966,7 +2160,7 @@ void uv_process_pipe_connect_req(uv_loop_t* loop, uv_pipe_t* 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) { assert(handle->type == UV_NAMED_PIPE); @@ -1977,7 +2171,7 @@ void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle, * is readable and we haven't seen EOF come in ourselves. */ eof_timer_init(handle); - /* If reading start the timer right now. Otherwise uv_pipe_queue_read will + /* 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); @@ -2037,9 +2231,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 @@ -2060,7 +2254,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_); } @@ -2134,20 +2328,20 @@ int uv_pipe_open(uv_pipe_t* pipe, uv_file file) { duplex_flags |= UV_HANDLE_READABLE; if (os_handle == INVALID_HANDLE_VALUE || - uv_set_pipe_handle(pipe->loop, - pipe, - os_handle, - file, - duplex_flags) == -1) { + uv__set_pipe_handle(pipe->loop, + pipe, + os_handle, + file, + duplex_flags) == -1) { return UV_EINVAL; } - uv_pipe_connection_init(pipe); + 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(); - assert(pipe->pipe.conn.ipc_remote_pid != (DWORD) -1); + assert(pipe->pipe.conn.ipc_remote_pid != (DWORD)(uv_pid_t) -1); } return 0; } diff --git a/src/win/poll.c b/src/win/poll.c index 3c66786..bd531b0 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,44 +124,18 @@ 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); } } -static int uv__fast_poll_cancel_poll_req(uv_loop_t* loop, uv_poll_t* handle) { - AFD_POLL_INFO afd_poll_info; - int result; - - afd_poll_info.Exclusive = TRUE; - afd_poll_info.NumberOfHandles = 1; - afd_poll_info.Timeout.QuadPart = INT64_MAX; - afd_poll_info.Handles[0].Handle = (HANDLE) handle->socket; - 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()); - - if (result == SOCKET_ERROR) { - DWORD error = WSAGetLastError(); - if (error != WSA_IO_PENDING) - return error; - } - - return 0; -} - - static void uv__fast_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req) { unsigned char mask_events; @@ -221,45 +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); - } -} - - -static int uv__fast_poll_set(uv_loop_t* loop, uv_poll_t* handle, int events) { - assert(handle->type == UV_POLL); - assert(!(handle->flags & UV_HANDLE_CLOSING)); - assert((events & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT)) == 0); - - handle->events = events; - - if (handle->events != 0) { - uv__handle_start(handle); - } else { - uv__handle_stop(handle); - } - - if ((handle->events & ~(handle->submitted_events_1 | - handle->submitted_events_2)) != 0) { - uv__fast_poll_submit_poll_req(handle->loop, handle); - } - - return 0; -} - - -static int uv__fast_poll_close(uv_loop_t* loop, uv_poll_t* handle) { - handle->events = 0; - uv__handle_closing(handle); - - if (handle->submitted_events_1 == 0 && - handle->submitted_events_2 == 0) { - uv_want_endgame(loop, (uv_handle_t*) handle); - return 0; - } else { - /* Cancel outstanding poll requests by executing another, unique poll - * request that forces the outstanding ones to return. */ - return uv__fast_poll_cancel_poll_req(loop, handle); + uv__want_endgame(loop, (uv_handle_t*) handle); } } @@ -421,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); } } @@ -464,43 +402,8 @@ 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); - } -} - - -static int uv__slow_poll_set(uv_loop_t* loop, uv_poll_t* handle, int events) { - assert(handle->type == UV_POLL); - assert(!(handle->flags & UV_HANDLE_CLOSING)); - assert((events & ~(UV_READABLE | UV_WRITABLE)) == 0); - - handle->events = events; - - if (handle->events != 0) { - uv__handle_start(handle); - } else { - uv__handle_stop(handle); - } - - if ((handle->events & - ~(handle->submitted_events_1 | handle->submitted_events_2)) != 0) { - uv__slow_poll_submit_poll_req(handle->loop, handle); - } - - return 0; -} - - -static int uv__slow_poll_close(uv_loop_t* loop, uv_poll_t* handle) { - handle->events = 0; - uv__handle_closing(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; } @@ -582,39 +485,48 @@ int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle, } -int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb) { - int err; +static int uv__poll_set(uv_poll_t* handle, int events, uv_poll_cb cb) { + int submitted_events; - if (!(handle->flags & UV_HANDLE_POLL_SLOW)) { - err = uv__fast_poll_set(handle->loop, handle, events); - } else { - err = uv__slow_poll_set(handle->loop, handle, events); - } + assert(handle->type == UV_POLL); + assert(!(handle->flags & UV_HANDLE_CLOSING)); + assert((events & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT | + UV_PRIORITIZED)) == 0); + + handle->events = events; + handle->poll_cb = cb; - if (err) { - return uv_translate_sys_error(err); + if (handle->events == 0) { + uv__handle_stop(handle); + return 0; } - handle->poll_cb = cb; + uv__handle_start(handle); + submitted_events = handle->submitted_events_1 | handle->submitted_events_2; + + if (handle->events & ~submitted_events) { + if (handle->flags & UV_HANDLE_POLL_SLOW) { + uv__slow_poll_submit_poll_req(handle->loop, handle); + } else { + uv__fast_poll_submit_poll_req(handle->loop, handle); + } + } return 0; } -int uv_poll_stop(uv_poll_t* handle) { - int err; +int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb) { + return uv__poll_set(handle, events, cb); +} - if (!(handle->flags & UV_HANDLE_POLL_SLOW)) { - err = uv__fast_poll_set(handle->loop, handle, 0); - } else { - err = uv__slow_poll_set(handle->loop, handle, 0); - } - return uv_translate_sys_error(err); +int uv_poll_stop(uv_poll_t* handle) { + return uv__poll_set(handle, 0, handle->poll_cb); } -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 { @@ -623,16 +535,48 @@ 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) { - if (!(handle->flags & UV_HANDLE_POLL_SLOW)) { - return uv__fast_poll_close(loop, handle); - } else { - return uv__slow_poll_close(loop, handle); +int uv__poll_close(uv_loop_t* loop, uv_poll_t* handle) { + AFD_POLL_INFO afd_poll_info; + DWORD error; + int result; + + handle->events = 0; + uv__handle_closing(handle); + + if (handle->submitted_events_1 == 0 && + handle->submitted_events_2 == 0) { + uv__want_endgame(loop, (uv_handle_t*) handle); + return 0; } + + if (handle->flags & UV_HANDLE_POLL_SLOW) + return 0; + + /* Cancel outstanding poll requests by executing another, unique poll + * request that forces the outstanding ones to return. */ + afd_poll_info.Exclusive = TRUE; + afd_poll_info.NumberOfHandles = 1; + afd_poll_info.Timeout.QuadPart = INT64_MAX; + afd_poll_info.Handles[0].Handle = (HANDLE) handle->socket; + 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()); + + if (result == SOCKET_ERROR) { + error = WSAGetLastError(); + if (error != WSA_IO_PENDING) + return uv_translate_sys_error(error); + } + + return 0; } -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 355d618..0db3572 100644 --- a/src/win/process-stdio.c +++ b/src/win/process-stdio.c @@ -95,102 +95,6 @@ void uv_disable_stdio_inheritance(void) { } -static int uv__create_stdio_pipe_pair(uv_loop_t* loop, - uv_pipe_t* server_pipe, HANDLE* child_pipe_ptr, unsigned int flags) { - char pipe_name[64]; - SECURITY_ATTRIBUTES sa; - DWORD server_access = 0; - DWORD client_access = 0; - HANDLE child_pipe = INVALID_HANDLE_VALUE; - int err; - int overlap; - - if (flags & UV_READABLE_PIPE) { - /* The server needs inbound access too, otherwise CreateNamedPipe() won't - * give us the FILE_READ_ATTRIBUTES permission. We need that to probe the - * state of the write buffer when we're trying to shutdown the pipe. */ - server_access |= PIPE_ACCESS_OUTBOUND | PIPE_ACCESS_INBOUND; - client_access |= GENERIC_READ | FILE_WRITE_ATTRIBUTES; - } - if (flags & UV_WRITABLE_PIPE) { - server_access |= PIPE_ACCESS_INBOUND; - client_access |= GENERIC_WRITE | FILE_READ_ATTRIBUTES; - } - - /* Create server pipe handle. */ - err = uv_stdio_pipe_server(loop, - server_pipe, - server_access, - pipe_name, - sizeof(pipe_name)); - if (err) - goto error; - - /* Create child pipe handle. */ - sa.nLength = sizeof sa; - sa.lpSecurityDescriptor = NULL; - sa.bInheritHandle = TRUE; - - overlap = server_pipe->ipc || (flags & UV_OVERLAPPED_PIPE); - child_pipe = CreateFileA(pipe_name, - client_access, - 0, - &sa, - OPEN_EXISTING, - overlap ? FILE_FLAG_OVERLAPPED : 0, - NULL); - if (child_pipe == INVALID_HANDLE_VALUE) { - err = GetLastError(); - goto error; - } - -#ifndef NDEBUG - /* Validate that the pipe was opened in the right mode. */ - { - DWORD mode; - BOOL r = GetNamedPipeHandleState(child_pipe, - &mode, - NULL, - NULL, - NULL, - NULL, - 0); - assert(r == TRUE); - assert(mode == (PIPE_READMODE_BYTE | PIPE_WAIT)); - } -#endif - - /* Do a blocking ConnectNamedPipe. This should not block because we have both - * ends of the pipe created. */ - if (!ConnectNamedPipe(server_pipe->handle, NULL)) { - if (GetLastError() != ERROR_PIPE_CONNECTED) { - err = GetLastError(); - goto error; - } - } - - /* The server end is now readable and/or writable. */ - if (flags & UV_READABLE_PIPE) - server_pipe->flags |= UV_HANDLE_WRITABLE; - if (flags & UV_WRITABLE_PIPE) - server_pipe->flags |= UV_HANDLE_READABLE; - - *child_pipe_ptr = child_pipe; - return 0; - - error: - if (server_pipe->handle != INVALID_HANDLE_VALUE) { - uv_pipe_cleanup(loop, server_pipe); - } - - if (child_pipe != INVALID_HANDLE_VALUE) { - CloseHandle(child_pipe); - } - - return err; -} - - static int uv__duplicate_handle(uv_loop_t* loop, HANDLE handle, HANDLE* dup) { HANDLE current_process; diff --git a/src/win/process.c b/src/win/process.c index 9b7fdc1..24c6333 100644 --- a/src/win/process.c +++ b/src/win/process.c @@ -58,7 +58,6 @@ static const env_var_t required_vars[] = { /* keep me sorted */ E_V("USERPROFILE"), E_V("WINDIR"), }; -static size_t n_required_vars = ARRAY_SIZE(required_vars); static HANDLE uv_global_job_handle_; @@ -106,7 +105,7 @@ static void uv__init_global_job_handle(void) { } -static int uv_utf8_to_utf16_alloc(const char* s, WCHAR** ws_ptr) { +static int uv__utf8_to_utf16_alloc(const char* s, WCHAR** ws_ptr) { int ws_len, r; WCHAR* ws; @@ -138,7 +137,7 @@ static int uv_utf8_to_utf16_alloc(const char* s, WCHAR** 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; @@ -170,7 +169,9 @@ static WCHAR* search_path_join_test(const WCHAR* dir, size_t cwd_len) { WCHAR *result, *result_pos; DWORD attrs; - if (dir_len > 2 && dir[0] == L'\\' && dir[1] == L'\\') { + if (dir_len > 2 && + ((dir[0] == L'\\' || dir[0] == L'/') && + (dir[1] == L'\\' || dir[1] == L'/'))) { /* It's a UNC path so ignore cwd */ cwd_len = 0; } else if (dir_len >= 1 && (dir[0] == L'/' || dir[0] == L'\\')) { @@ -643,7 +644,7 @@ int env_strncmp(const wchar_t* a, int na, const wchar_t* b) { assert(r==nb); B[nb] = L'\0'; - while (1) { + for (;;) { wchar_t AA = *A++; wchar_t BB = *B++; if (AA < BB) { @@ -692,7 +693,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) { WCHAR* dst_copy; WCHAR** ptr_copy; WCHAR** env_copy; - DWORD* required_vars_value_len = alloca(n_required_vars * sizeof(DWORD*)); + DWORD required_vars_value_len[ARRAY_SIZE(required_vars)]; /* first pass: determine size in UTF-16 */ for (env = env_block; *env; env++) { @@ -745,7 +746,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) { qsort(env_copy, env_block_count-1, sizeof(wchar_t*), qsort_wcscmp); /* third pass: check for required variables */ - for (ptr_copy = env_copy, i = 0; i < n_required_vars; ) { + for (ptr_copy = env_copy, i = 0; i < ARRAY_SIZE(required_vars); ) { int cmp; if (!*ptr_copy) { cmp = -1; @@ -778,10 +779,10 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) { } for (ptr = dst, ptr_copy = env_copy, i = 0; - *ptr_copy || i < n_required_vars; + *ptr_copy || i < ARRAY_SIZE(required_vars); ptr += len) { int cmp; - if (i >= n_required_vars) { + if (i >= ARRAY_SIZE(required_vars)) { cmp = 1; } else if (!*ptr_copy) { cmp = -1; @@ -863,7 +864,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; @@ -873,7 +874,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; } @@ -901,7 +902,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) { @@ -917,12 +918,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)); @@ -947,7 +948,7 @@ int uv_spawn(uv_loop_t* loop, PROCESS_INFORMATION info; DWORD process_flags; - uv_process_init(loop, process); + uv__process_init(loop, process); process->exit_cb = options->exit_cb; if (options->flags & (UV_PROCESS_SETGID | UV_PROCESS_SETUID)) { @@ -968,7 +969,7 @@ int uv_spawn(uv_loop_t* loop, 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; @@ -987,7 +988,7 @@ int uv_spawn(uv_loop_t* loop, 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; diff --git a/src/win/req-inl.h b/src/win/req-inl.h index f2513b7..b7a3456 100644 --- a/src/win/req-inl.h +++ b/src/win/req-inl.h @@ -50,7 +50,7 @@ (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) \ @@ -82,12 +82,12 @@ } -INLINE static uv_req_t* uv_overlapped_to_req(OVERLAPPED* overlapped) { +INLINE static uv_req_t* uv__overlapped_to_req(OVERLAPPED* overlapped) { return CONTAINING_RECORD(overlapped, uv_req_t, u.io.overlapped); } -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,7 +138,7 @@ 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 int uv__process_reqs(uv_loop_t* loop) { uv_req_t* req; uv_req_t* first; uv_req_t* next; @@ -174,40 +174,40 @@ INLINE static int uv_process_reqs(uv_loop_t* loop) { 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( + uv__process_pipe_shutdown_req( loop, (uv_pipe_t*) ((uv_shutdown_t*) req)->handle, (uv_shutdown_t*) req); 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: diff --git a/src/win/signal.c b/src/win/signal.c index 276dc60..8c79871 100644 --- a/src/win/signal.c +++ b/src/win/signal.c @@ -39,13 +39,18 @@ 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(); } +void uv__signal_cleanup(void) { + /* TODO(bnoordhuis) Undo effects of uv_signal_init()? */ +} + + static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2) { /* Compare signums first so all watchers with the same signnum end up * adjacent. */ @@ -226,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; @@ -249,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 40f5ddd..91b1e78 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 46a0709..b304d17 100644 --- a/src/win/stream.c +++ b/src/win/stream.c @@ -33,10 +33,10 @@ int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) { 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 +52,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); @@ -65,28 +65,21 @@ int uv_accept(uv_stream_t* server, uv_stream_t* client) { } -int uv_read_start(uv_stream_t* handle, uv_alloc_cb alloc_cb, - uv_read_cb read_cb) { +int uv__read_start(uv_stream_t* handle, + uv_alloc_cb alloc_cb, + uv_read_cb read_cb) { int err; - if (handle->flags & UV_HANDLE_READING) { - return UV_EALREADY; - } - - if (!(handle->flags & UV_HANDLE_READABLE)) { - return UV_ENOTCONN; - } - 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); @@ -104,7 +97,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 { @@ -131,14 +124,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; 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); @@ -195,6 +188,16 @@ int uv_try_write(uv_stream_t* stream, } +int uv_try_write2(uv_stream_t* stream, + const uv_buf_t bufs[], + unsigned int nbufs, + uv_stream_t* send_handle) { + if (send_handle != NULL) + return UV_EAGAIN; + return uv_try_write(stream, bufs, nbufs); +} + + int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) { uv_loop_t* loop = handle->loop; @@ -214,7 +217,7 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) { handle->reqs_pending++; REGISTER_HANDLE_REQ(loop, handle, req); - uv_want_endgame(loop, (uv_handle_t*)handle); + uv__want_endgame(loop, (uv_handle_t*)handle); return 0; } diff --git a/src/win/tcp.c b/src/win/tcp.c index 941c801..1d9085c 100644 --- a/src/win/tcp.c +++ b/src/win/tcp.c @@ -78,11 +78,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 +162,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 +173,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) { @@ -187,7 +187,7 @@ int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* handle, unsigned int flags) { 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); @@ -205,7 +205,7 @@ 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__tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { int err; unsigned int i; uv_tcp_accept_t* req; @@ -236,12 +236,7 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { if (handle->flags & UV_HANDLE_CLOSING && handle->reqs_pending == 0) { assert(!(handle->flags & UV_HANDLE_CLOSED)); - - if (!(handle->flags & UV_HANDLE_TCP_SOCKET_CLOSED)) { - closesocket(handle->socket); - handle->socket = INVALID_SOCKET; - handle->flags |= UV_HANDLE_TCP_SOCKET_CLOSED; - } + assert(handle->socket == INVALID_SOCKET); if (!(handle->flags & UV_HANDLE_CONNECTION) && handle->tcp.serv.accept_reqs) { if (handle->flags & UV_HANDLE_EMULATE_IOCP) { @@ -291,10 +286,10 @@ 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; @@ -310,7 +305,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; @@ -390,7 +385,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; @@ -411,7 +406,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; } @@ -419,7 +414,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; @@ -445,7 +440,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; @@ -456,12 +451,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); @@ -474,7 +469,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; @@ -523,29 +518,27 @@ static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { &req->u.io.overlapped, NULL); + handle->flags |= UV_HANDLE_READ_PENDING; + handle->reqs_pending++; + 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, (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. */ - handle->flags |= UV_HANDLE_READ_PENDING; - handle->reqs_pending++; if (handle->flags & UV_HANDLE_EMULATE_IOCP && req->wait_handle == INVALID_HANDLE_VALUE && !RegisterWaitForSingleObject(&req->wait_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); - handle->reqs_pending++; + uv__insert_pending_req(loop, (uv_req_t*)req); } } @@ -565,7 +558,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; @@ -585,10 +578,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) @@ -596,11 +589,12 @@ 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; } } + /* If this flag is set, we already made this listen call in xfer. */ if (!(handle->flags & UV_HANDLE_SHARED_TCP_SOCKET) && listen(handle->socket, backlog) == SOCKET_ERROR) { return WSAGetLastError(); @@ -636,7 +630,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 @@ -656,7 +650,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) { +int uv__tcp_accept(uv_tcp_t* server, uv_tcp_t* client) { uv_loop_t* loop = server->loop; int err = 0; int family; @@ -678,7 +672,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, @@ -686,7 +680,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; } @@ -699,7 +693,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); @@ -712,7 +706,7 @@ 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; } @@ -725,7 +719,7 @@ 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_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb) { uv_loop_t* loop = handle->loop; @@ -744,19 +738,54 @@ 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; } +static int uv__is_loopback(const struct sockaddr_storage* storage) { + const struct sockaddr_in* in4; + const struct sockaddr_in6* in6; + int i; + + if (storage->ss_family == AF_INET) { + in4 = (const struct sockaddr_in*) storage; + return in4->sin_addr.S_un.S_un_b.s_b1 == 127; + } + if (storage->ss_family == AF_INET6) { + in6 = (const struct sockaddr_in6*) storage; + for (i = 0; i < 7; ++i) { + if (in6->sin6_addr.u.Word[i] != 0) + return 0; + } + return in6->sin6_addr.u.Word[7] == htons(1); + } + return 0; +} + +// Check if Windows version is 10.0.16299 or later +static int uv__is_fast_loopback_fail_supported(void) { + OSVERSIONINFOW os_info; + if (!pRtlGetVersion) + return 0; + pRtlGetVersion(&os_info); + if (os_info.dwMajorVersion < 10) + return 0; + if (os_info.dwMajorVersion > 10) + return 1; + if (os_info.dwMinorVersion > 0) + return 1; + 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, uv_connect_cb cb) { uv_loop_t* loop = handle->loop; + TCP_INITIAL_RTO_PARAMETERS retransmit_ioctl; const struct sockaddr* bind_addr; struct sockaddr_storage converted; BOOL success; @@ -767,9 +796,8 @@ static int uv_tcp_try_connect(uv_connect_t* req, if (err) return err; - if (handle->delayed_error) { - return handle->delayed_error; - } + if (handle->delayed_error != 0) + goto out; if (!(handle->flags & UV_HANDLE_BOUND)) { if (addrlen == sizeof(uv_addr_ip4_any_)) { @@ -779,24 +807,53 @@ 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) - return handle->delayed_error; + if (handle->delayed_error != 0) + goto out; } 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; } } + /* This makes connect() fail instantly if the target port on the localhost + * is not reachable, instead of waiting for 2s. We do not care if this fails. + * This only works on Windows version 10.0.16299 and later. + */ + if (uv__is_fast_loopback_fail_supported() && uv__is_loopback(&converted)) { + memset(&retransmit_ioctl, 0, sizeof(retransmit_ioctl)); + retransmit_ioctl.Rtt = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS; + retransmit_ioctl.MaxSynRetransmissions = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS; + WSAIoctl(handle->socket, + SIO_TCP_INITIAL_RTO, + &retransmit_ioctl, + sizeof(retransmit_ioctl), + NULL, + 0, + &bytes, + NULL, + NULL); + } + +out: + UV_REQ_INIT(req, UV_CONNECT); req->handle = (uv_stream_t*) handle; req->cb = cb; memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); + 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); + return 0; + } + success = handle->tcp.conn.func_connectex(handle->socket, (const struct sockaddr*) &converted, addrlen, @@ -809,7 +866,7 @@ static int uv_tcp_try_connect(uv_connect_t* req, /* Process the req without IOCP. */ handle->reqs_pending++; REGISTER_HANDLE_REQ(loop, handle, req); - 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. */ handle->reqs_pending++; @@ -846,7 +903,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[], @@ -884,7 +941,7 @@ int uv_tcp_write(uv_loop_t* loop, handle->reqs_pending++; handle->stream.conn.write_reqs_pending++; REGISTER_HANDLE_REQ(loop, handle, req); - 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)) { /* Request queued by the kernel. */ req->u.io.queued_bytes = uv__count_bufs(bufs, nbufs); @@ -897,7 +954,7 @@ int uv_tcp_write(uv_loop_t* loop, 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 */ @@ -906,7 +963,7 @@ int uv_tcp_write(uv_loop_t* loop, handle->stream.conn.write_reqs_pending++; REGISTER_HANDLE_REQ(loop, handle, req); 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; @@ -937,7 +994,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; @@ -963,6 +1020,7 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, */ err = WSAECONNRESET; } + handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); handle->read_cb((uv_stream_t*)handle, uv_translate_sys_error(err), @@ -986,7 +1044,6 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, handle->flags &= ~UV_HANDLE_READING; DECREASE_ACTIVE_COUNT(loop, handle); } - handle->flags &= ~UV_HANDLE_READABLE; buf.base = 0; buf.len = 0; @@ -1023,7 +1080,7 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, } } else { /* Connection closed */ - handle->flags &= ~(UV_HANDLE_READING | UV_HANDLE_READABLE); + handle->flags &= ~UV_HANDLE_READING; DECREASE_ACTIVE_COUNT(loop, handle); handle->read_cb((uv_stream_t*)handle, UV_EOF, &buf); @@ -1044,6 +1101,7 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, * Unix. */ err = WSAECONNRESET; } + handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); handle->read_cb((uv_stream_t*)handle, uv_translate_sys_error(err), @@ -1057,7 +1115,7 @@ done: /* 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); } } @@ -1065,7 +1123,7 @@ done: } -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; @@ -1097,16 +1155,21 @@ void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle, } 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) { + if (handle->flags & UV_HANDLE_CLOSING) { + closesocket(handle->socket); + handle->socket = INVALID_SOCKET; + } + if (handle->stream.conn.shutdown_req != NULL) { + uv__want_endgame(loop, (uv_handle_t*)handle); + } } 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; @@ -1146,7 +1209,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); } } @@ -1154,7 +1217,7 @@ 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; @@ -1163,7 +1226,14 @@ void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, UNREGISTER_HANDLE_REQ(loop, handle, req); err = 0; - if (REQ_SUCCESS(req)) { + if (handle->delayed_error) { + /* To smooth over the differences between unixes errors that + * were reported synchronously on the first connect can be delayed + * until the next tick--which is now. + */ + err = handle->delayed_error; + handle->delayed_error = 0; + } else if (REQ_SUCCESS(req)) { if (handle->flags & UV_HANDLE_CLOSING) { /* use UV_ECANCELED for consistency with Unix */ err = ERROR_OPERATION_ABORTED; @@ -1172,7 +1242,7 @@ 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 { @@ -1242,7 +1312,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); @@ -1253,7 +1323,7 @@ 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; } @@ -1268,7 +1338,7 @@ int uv_tcp_nodelay(uv_tcp_t* handle, int enable) { if (handle->socket != INVALID_SOCKET) { err = uv__tcp_nodelay(handle, handle->socket, enable); if (err) - return err; + return uv_translate_sys_error(err); } if (enable) { @@ -1287,7 +1357,7 @@ int uv_tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay) { if (handle->socket != INVALID_SOCKET) { err = uv__tcp_keepalive(handle, handle->socket, enable, delay); if (err) - return err; + return uv_translate_sys_error(err); } if (enable) { @@ -1334,9 +1404,24 @@ int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) { } -static int uv_tcp_try_cancel_io(uv_tcp_t* tcp) { - SOCKET socket = tcp->socket; +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; + writing = tcp->stream.conn.write_reqs_pending > 0; + if (!reading && !writing) + return; + + /* TODO: in libuv v2, keep explicit track of write_reqs, so we can cancel + * them each explicitly with CancelIoEx (like unix). */ + if (reading) + CancelIoEx((HANDLE) socket, &tcp->read_req.u.io.overlapped); + if (writing) + CancelIo((HANDLE) socket); /* Check if we have any non-IFS LSPs stacked on top of TCP */ non_ifs_lsp = (tcp->flags & UV_HANDLE_IPV6) ? uv_tcp_non_ifs_lsp_ipv6 : @@ -1356,71 +1441,41 @@ static int uv_tcp_try_cancel_io(uv_tcp_t* tcp) { NULL, NULL) != 0) { /* Failed. We can't do CancelIo. */ - return -1; + return; } } assert(socket != 0 && socket != INVALID_SOCKET); - if (!CancelIo((HANDLE) socket)) { - return GetLastError(); + if (socket != tcp->socket) { + if (reading) + CancelIoEx((HANDLE) socket, &tcp->read_req.u.io.overlapped); + if (writing) + CancelIo((HANDLE) socket); } - - /* It worked. */ - return 0; } -void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) { - int close_socket = 1; - - if (tcp->flags & UV_HANDLE_READ_PENDING) { - /* In order for winsock to do a graceful close there must not be any any - * pending reads, or the socket must be shut down for writing */ - if (!(tcp->flags & UV_HANDLE_SHARED_TCP_SOCKET)) { - /* Just do shutdown on non-shared sockets, which ensures graceful close. */ - shutdown(tcp->socket, SD_SEND); - - } else if (uv_tcp_try_cancel_io(tcp) == 0) { - /* In case of a shared socket, we try to cancel all outstanding I/O,. If - * that works, don't close the socket yet - wait for the read req to - * return and close the socket in uv_tcp_endgame. */ - close_socket = 0; - - } else { - /* When cancelling isn't possible - which could happen when an LSP is - * present on an old Windows version, we will have to close the socket - * with a read pending. That is not nice because trailing sent bytes may - * not make it to the other side. */ +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); } - - } else if ((tcp->flags & UV_HANDLE_SHARED_TCP_SOCKET) && - tcp->tcp.serv.accept_reqs != NULL) { - /* Under normal circumstances closesocket() will ensure that all pending - * accept reqs are canceled. However, when the socket is shared the - * presence of another reference to the socket in another process will keep - * the accept reqs going, so we have to ensure that these are canceled. */ - if (uv_tcp_try_cancel_io(tcp) != 0) { - /* When cancellation is not possible, there is another option: we can - * close the incoming sockets, which will also cancel the accept - * operations. However this is not cool because we might inadvertently - * close a socket that just accepted a new connection, which will cause - * the connection to be aborted. */ + } else { + if (tcp->tcp.serv.accept_reqs != NULL) { + /* First close the incoming sockets to cancel the accept operations before + * we free their resources. */ unsigned int i; for (i = 0; i < uv_simultaneous_server_accepts; i++) { uv_tcp_accept_t* req = &tcp->tcp.serv.accept_reqs[i]; - if (req->accept_socket != INVALID_SOCKET && - !HasOverlappedIoCompleted(&req->u.io.overlapped)) { + if (req->accept_socket != INVALID_SOCKET) { closesocket(req->accept_socket); req->accept_socket = INVALID_SOCKET; } } } - } - - if (tcp->flags & UV_HANDLE_READING) { - tcp->flags &= ~UV_HANDLE_READING; - DECREASE_ACTIVE_COUNT(loop, tcp); + assert(!(tcp->flags & UV_HANDLE_READING)); } if (tcp->flags & UV_HANDLE_LISTENING) { @@ -1428,17 +1483,22 @@ void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) { DECREASE_ACTIVE_COUNT(loop, tcp); } - if (close_socket) { + /* 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 + * first (which typically should be cancellations). There's not much we can + * do about canceled reads, which also will generate an RST packet. */ + if (!(tcp->flags & UV_HANDLE_CONNECTION) || + tcp->stream.conn.write_reqs_pending == 0) { closesocket(tcp->socket); tcp->socket = INVALID_SOCKET; - tcp->flags |= UV_HANDLE_TCP_SOCKET_CLOSED; } 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); + uv__want_endgame(tcp->loop, (uv_handle_t*)tcp); } } @@ -1460,7 +1520,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, @@ -1477,7 +1537,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; } } @@ -1495,7 +1555,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); @@ -1513,9 +1573,124 @@ 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; + SOCKET client0 = INVALID_SOCKET; + SOCKET client1 = INVALID_SOCKET; + SOCKADDR_IN name; + LPFN_ACCEPTEX func_acceptex; + WSAOVERLAPPED overlap; + char accept_buffer[sizeof(struct sockaddr_storage) * 2 + 32]; + int namelen; + int err; + DWORD bytes; + DWORD flags; + DWORD client0_flags = WSA_FLAG_NO_HANDLE_INHERIT; + DWORD client1_flags = WSA_FLAG_NO_HANDLE_INHERIT; + + if (flags0 & UV_NONBLOCK_PIPE) + client0_flags |= WSA_FLAG_OVERLAPPED; + if (flags1 & UV_NONBLOCK_PIPE) + client1_flags |= WSA_FLAG_OVERLAPPED; + + server = WSASocketW(AF_INET, type, protocol, NULL, 0, + WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT); + if (server == INVALID_SOCKET) + goto wsaerror; + if (!SetHandleInformation((HANDLE) server, HANDLE_FLAG_INHERIT, 0)) + goto error; + name.sin_family = AF_INET; + name.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + name.sin_port = 0; + if (bind(server, (SOCKADDR*) &name, sizeof(name)) != 0) + goto wsaerror; + if (listen(server, 1) != 0) + goto wsaerror; + namelen = sizeof(name); + if (getsockname(server, (SOCKADDR*) &name, &namelen) != 0) + goto wsaerror; + client0 = WSASocketW(AF_INET, type, protocol, NULL, 0, client0_flags); + if (client0 == INVALID_SOCKET) + goto wsaerror; + if (!SetHandleInformation((HANDLE) client0, HANDLE_FLAG_INHERIT, 0)) + goto error; + if (connect(client0, (SOCKADDR*) &name, sizeof(name)) != 0) + goto wsaerror; + client1 = WSASocketW(AF_INET, type, protocol, NULL, 0, client1_flags); + if (client1 == INVALID_SOCKET) + goto wsaerror; + if (!SetHandleInformation((HANDLE) client1, HANDLE_FLAG_INHERIT, 0)) + goto error; + if (!uv__get_acceptex_function(server, &func_acceptex)) { + err = WSAEAFNOSUPPORT; + goto cleanup; + } + memset(&overlap, 0, sizeof(overlap)); + if (!func_acceptex(server, + client1, + accept_buffer, + 0, + sizeof(struct sockaddr_storage), + sizeof(struct sockaddr_storage), + &bytes, + &overlap)) { + err = WSAGetLastError(); + if (err == ERROR_IO_PENDING) { + /* Result should complete immediately, since we already called connect, + * but empirically, we sometimes have to poll the kernel a couple times + * until it notices that. */ + while (!WSAGetOverlappedResult(client1, &overlap, &bytes, FALSE, &flags)) { + err = WSAGetLastError(); + if (err != WSA_IO_INCOMPLETE) + goto cleanup; + SwitchToThread(); + } + } + else { + goto cleanup; + } + } + if (setsockopt(client1, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, + (char*) &server, sizeof(server)) != 0) { + goto wsaerror; + } + + closesocket(server); + + fds[0] = client0; + fds[1] = client1; + + return 0; + + wsaerror: + err = WSAGetLastError(); + goto cleanup; + + error: + err = GetLastError(); + goto cleanup; + + cleanup: + if (server != INVALID_SOCKET) + closesocket(server); + if (client0 != INVALID_SOCKET) + closesocket(client0); + if (client1 != INVALID_SOCKET) + closesocket(client1); + + assert(err); + return uv_translate_sys_error(err); +} diff --git a/src/win/thread.c b/src/win/thread.c index 89c53ad..d3b1c96 100644 --- a/src/win/thread.c +++ b/src/win/thread.c @@ -103,7 +103,7 @@ static UINT __stdcall uv__thread_start(void* arg) { uv__free(ctx_p); uv_once(&uv__current_thread_init_guard, uv__init_current_thread_key); - uv_key_set(&uv__current_thread_key, (void*) ctx.self); + uv_key_set(&uv__current_thread_key, ctx.self); ctx.entry(ctx.arg); @@ -182,8 +182,20 @@ int uv_thread_create_ex(uv_thread_t* tid, uv_thread_t uv_thread_self(void) { + uv_thread_t key; uv_once(&uv__current_thread_init_guard, uv__init_current_thread_key); - return (uv_thread_t) 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. */ + if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), + GetCurrentProcess(), &key, 0, + FALSE, DUPLICATE_SAME_ACCESS)) { + uv_fatal_error(GetLastError(), "DuplicateHandle"); + } + uv_key_set(&uv__current_thread_key, key); + } + return key; } @@ -237,113 +249,60 @@ void uv_mutex_unlock(uv_mutex_t* mutex) { LeaveCriticalSection(mutex); } +/* Ensure that the ABI for this type remains stable in v1.x */ +#ifdef _WIN64 +STATIC_ASSERT(sizeof(uv_rwlock_t) == 80); +#else +STATIC_ASSERT(sizeof(uv_rwlock_t) == 48); +#endif int uv_rwlock_init(uv_rwlock_t* rwlock) { - /* Initialize the semaphore that acts as the write lock. */ - HANDLE handle = CreateSemaphoreW(NULL, 1, 1, NULL); - if (handle == NULL) - return uv_translate_sys_error(GetLastError()); - rwlock->state_.write_semaphore_ = handle; - - /* Initialize the critical section protecting the reader count. */ - InitializeCriticalSection(&rwlock->state_.num_readers_lock_); - - /* Initialize the reader count. */ - rwlock->state_.num_readers_ = 0; + memset(rwlock, 0, sizeof(*rwlock)); + InitializeSRWLock(&rwlock->read_write_lock_); return 0; } void uv_rwlock_destroy(uv_rwlock_t* rwlock) { - DeleteCriticalSection(&rwlock->state_.num_readers_lock_); - CloseHandle(rwlock->state_.write_semaphore_); + /* SRWLock does not need explicit destruction so long as there are no waiting threads + See: https://docs.microsoft.com/windows/win32/api/synchapi/nf-synchapi-initializesrwlock#remarks */ } void uv_rwlock_rdlock(uv_rwlock_t* rwlock) { - /* Acquire the lock that protects the reader count. */ - EnterCriticalSection(&rwlock->state_.num_readers_lock_); - - /* Increase the reader count, and lock for write if this is the first - * reader. - */ - if (++rwlock->state_.num_readers_ == 1) { - DWORD r = WaitForSingleObject(rwlock->state_.write_semaphore_, INFINITE); - if (r != WAIT_OBJECT_0) - uv_fatal_error(GetLastError(), "WaitForSingleObject"); - } - - /* Release the lock that protects the reader count. */ - LeaveCriticalSection(&rwlock->state_.num_readers_lock_); + AcquireSRWLockShared(&rwlock->read_write_lock_); } int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) { - int err; - - if (!TryEnterCriticalSection(&rwlock->state_.num_readers_lock_)) + if (!TryAcquireSRWLockShared(&rwlock->read_write_lock_)) return UV_EBUSY; - err = 0; - - if (rwlock->state_.num_readers_ == 0) { - /* Currently there are no other readers, which means that the write lock - * needs to be acquired. - */ - DWORD r = WaitForSingleObject(rwlock->state_.write_semaphore_, 0); - if (r == WAIT_OBJECT_0) - rwlock->state_.num_readers_++; - else if (r == WAIT_TIMEOUT) - err = UV_EBUSY; - else if (r == WAIT_FAILED) - uv_fatal_error(GetLastError(), "WaitForSingleObject"); - - } else { - /* The write lock has already been acquired because there are other - * active readers. - */ - rwlock->state_.num_readers_++; - } - - LeaveCriticalSection(&rwlock->state_.num_readers_lock_); - return err; + return 0; } void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) { - EnterCriticalSection(&rwlock->state_.num_readers_lock_); - - if (--rwlock->state_.num_readers_ == 0) { - if (!ReleaseSemaphore(rwlock->state_.write_semaphore_, 1, NULL)) - uv_fatal_error(GetLastError(), "ReleaseSemaphore"); - } - - LeaveCriticalSection(&rwlock->state_.num_readers_lock_); + ReleaseSRWLockShared(&rwlock->read_write_lock_); } void uv_rwlock_wrlock(uv_rwlock_t* rwlock) { - DWORD r = WaitForSingleObject(rwlock->state_.write_semaphore_, INFINITE); - if (r != WAIT_OBJECT_0) - uv_fatal_error(GetLastError(), "WaitForSingleObject"); + AcquireSRWLockExclusive(&rwlock->read_write_lock_); } int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) { - DWORD r = WaitForSingleObject(rwlock->state_.write_semaphore_, 0); - if (r == WAIT_OBJECT_0) - return 0; - else if (r == WAIT_TIMEOUT) + if (!TryAcquireSRWLockExclusive(&rwlock->read_write_lock_)) return UV_EBUSY; - else - uv_fatal_error(GetLastError(), "WaitForSingleObject"); + + return 0; } void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) { - if (!ReleaseSemaphore(rwlock->state_.write_semaphore_, 1, NULL)) - uv_fatal_error(GetLastError(), "ReleaseSemaphore"); + ReleaseSRWLockExclusive(&rwlock->read_write_lock_); } diff --git a/src/win/tty.c b/src/win/tty.c index 488d9b2..98c5888 100644 --- a/src/win/tty.c +++ b/src/win/tty.c @@ -67,10 +67,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 +163,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$", @@ -238,16 +238,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 +289,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 +380,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 +404,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 +422,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 +452,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 +475,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; @@ -517,6 +517,7 @@ static DWORD CALLBACK uv_tty_line_read_thread(void* data) { status = InterlockedExchange(&uv__read_console_status, IN_PROGRESS); if (status == TRAP_REQUESTED) { SET_REQ_SUCCESS(req); + InterlockedExchange(&uv__read_console_status, COMPLETED); req->u.io.overlapped.InternalHigh = 0; POST_COMPLETION_FOR_REQ(loop, req); return 0; @@ -578,7 +579,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; @@ -610,7 +611,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; @@ -618,11 +619,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); } } @@ -946,7 +947,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); @@ -991,14 +992,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); @@ -1014,7 +1015,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; @@ -1037,20 +1038,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; @@ -1136,7 +1137,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; @@ -1159,12 +1160,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) { @@ -1196,7 +1197,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; @@ -1217,7 +1218,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; @@ -1231,7 +1232,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) { @@ -1247,7 +1248,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; @@ -1299,7 +1300,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)) { @@ -1311,7 +1312,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; @@ -1340,7 +1341,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; } @@ -1363,8 +1364,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; @@ -1399,7 +1400,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; @@ -1555,7 +1556,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; @@ -1568,10 +1569,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) { @@ -1584,7 +1586,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; @@ -1594,7 +1596,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, @@ -1624,7 +1626,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; @@ -1644,7 +1646,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)) { @@ -1669,7 +1671,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) { @@ -1682,7 +1684,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) @@ -1801,21 +1803,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; @@ -1848,7 +1850,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); } } @@ -1946,7 +1948,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; @@ -1955,7 +1957,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; } @@ -1969,7 +1971,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': @@ -1977,7 +1979,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': @@ -1985,7 +1987,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': @@ -1993,7 +1995,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': @@ -2001,7 +2003,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': @@ -2009,7 +2011,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': @@ -2018,7 +2020,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': @@ -2031,7 +2033,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': @@ -2040,7 +2042,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; @@ -2050,26 +2052,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; } } @@ -2121,13 +2123,6 @@ static int uv_tty_write_bufs(uv_tty_t* handle, abort(); } - /* We wouldn't mind emitting utf-16 surrogate pairs. Too bad, the windows - * console doesn't really support UTF-16, so just emit the replacement - * character. */ - if (utf8_codepoint > 0xffff) { - utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER; - } - if (utf8_codepoint == 0x0a || utf8_codepoint == 0x0d) { /* EOL conversion - emit \r\n when we see \n. */ @@ -2154,6 +2149,12 @@ static int uv_tty_write_bufs(uv_tty_t* handle, ENSURE_BUFFER_SPACE(1); utf16_buf[utf16_buf_used++] = (WCHAR) utf8_codepoint; previous_eol = 0; + } else { + ENSURE_BUFFER_SPACE(2); + utf8_codepoint -= 0x10000; + utf16_buf[utf16_buf_used++] = (WCHAR) (utf8_codepoint / 0x400 + 0xD800); + utf16_buf[utf16_buf_used++] = (WCHAR) (utf8_codepoint % 0x400 + 0xDC00); + previous_eol = 0; } } } @@ -2179,7 +2180,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[], @@ -2197,13 +2198,13 @@ int uv_tty_write(uv_loop_t* loop, 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,14 +2218,14 @@ 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; @@ -2239,17 +2240,17 @@ void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle, 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); + uv__want_endgame(loop, (uv_handle_t*)handle); } 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); @@ -2262,12 +2263,12 @@ void uv_tty_close(uv_tty_t* handle) { uv__handle_closing(handle); if (handle->reqs_pending == 0) { - uv_want_endgame(handle->loop, (uv_handle_t*) handle); + uv__want_endgame(handle->loop, (uv_handle_t*) handle); } } -void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle) { +void uv__tty_endgame(uv_loop_t* loop, uv_tty_t* handle) { if (!(handle->flags & UV_HANDLE_TTY_READABLE) && handle->stream.conn.shutdown_req != NULL && handle->stream.conn.write_reqs_pending == 0) { @@ -2302,20 +2303,20 @@ void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle) { /* - * uv_process_tty_accept_req() is a stub to keep DELEGATE_STREAM_REQ working + * 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, +void uv__process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle, uv_req_t* raw_req) { abort(); } /* - * uv_process_tty_connect_req() is a stub to keep DELEGATE_STREAM_REQ working + * 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, +void uv__process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle, uv_connect_t* req) { abort(); } @@ -2412,6 +2413,7 @@ static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param) { uv__tty_console_signal_resize(); ResetEvent(uv__tty_console_resized); } + return 0; } static void uv__tty_console_signal_resize(void) { diff --git a/src/win/udp.c b/src/win/udp.c index 3daa55f..407aa6c 100644 --- a/src/win/udp.c +++ b/src/win/udp.c @@ -60,7 +60,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 +106,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(); } @@ -125,17 +125,10 @@ static int uv_udp_set_socket(uv_loop_t* loop, uv_udp_t* handle, SOCKET socket, } -int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) { - int domain; - - /* Use the lower 8 bits for the domain */ - domain = flags & 0xFF; - if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC) - return UV_EINVAL; - - if (flags & ~0xFF) - return UV_EINVAL; - +int uv__udp_init_ex(uv_loop_t* loop, + uv_udp_t* handle, + unsigned flags, + int domain) { uv__handle_init(loop, (uv_handle_t*) handle, UV_UDP); handle->socket = INVALID_SOCKET; handle->reqs_pending = 0; @@ -162,7 +155,7 @@ int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) { 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); @@ -174,12 +167,7 @@ int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) { } -int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) { - return uv_udp_init_ex(loop, handle, AF_UNSPEC); -} - - -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; @@ -187,12 +175,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)); @@ -201,10 +189,15 @@ void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle) { } -static int uv_udp_maybe_bind(uv_udp_t* handle, - const struct sockaddr* addr, - unsigned int addrlen, - unsigned int flags) { +int uv_udp_using_recvmmsg(const uv_udp_t* handle) { + return 0; +} + + +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; @@ -223,7 +216,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; @@ -271,7 +264,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; @@ -291,7 +284,7 @@ static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) { handle->flags &= ~UV_HANDLE_ZERO_READ; handle->recv_buffer = uv_buf_init(NULL, 0); - handle->alloc_cb((uv_handle_t*) handle, 65536, &handle->recv_buffer); + 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; @@ -318,7 +311,7 @@ static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) { handle->flags |= UV_HANDLE_READ_PENDING; req->u.io.overlapped.InternalHigh = bytes; handle->reqs_pending++; - uv_insert_pending_req(loop, req); + 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; @@ -326,7 +319,7 @@ static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) { } else { /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, WSAGetLastError()); - uv_insert_pending_req(loop, req); + uv__insert_pending_req(loop, req); handle->reqs_pending++; } @@ -350,7 +343,7 @@ static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) { handle->flags |= UV_HANDLE_READ_PENDING; req->u.io.overlapped.InternalHigh = bytes; handle->reqs_pending++; - uv_insert_pending_req(loop, req); + 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; @@ -358,7 +351,7 @@ static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) { } else { /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, WSAGetLastError()); - uv_insert_pending_req(loop, req); + uv__insert_pending_req(loop, req); handle->reqs_pending++; } } @@ -374,10 +367,10 @@ 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); @@ -391,7 +384,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; } @@ -440,7 +433,7 @@ static int uv__send(uv_udp_send_t* req, 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); + 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); @@ -457,7 +450,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; @@ -508,7 +501,7 @@ void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle, /* 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, 65536, &buf); + 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; @@ -561,14 +554,14 @@ 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; @@ -605,10 +598,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); @@ -659,10 +652,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); @@ -715,10 +708,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); @@ -764,13 +757,16 @@ int uv__udp_set_source_membership6(uv_udp_t* handle, int optname; int err; + STATIC_ASSERT(sizeof(mreq.gsr_group) >= sizeof(*multicast_addr)); + STATIC_ASSERT(sizeof(mreq.gsr_source) >= sizeof(*source_addr)); + 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); @@ -786,8 +782,8 @@ int uv__udp_set_source_membership6(uv_udp_t* handle, mreq.gsr_interface = 0; } - memcpy(&mreq.gsr_group, multicast_addr, sizeof(mreq.gsr_group)); - memcpy(&mreq.gsr_source, source_addr, sizeof(mreq.gsr_source)); + memcpy(&mreq.gsr_group, multicast_addr, sizeof(*multicast_addr)); + memcpy(&mreq.gsr_source, source_addr, sizeof(*source_addr)); if (membership == UV_JOIN_GROUP) optname = MCAST_JOIN_SOURCE_GROUP; @@ -966,10 +962,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); @@ -1048,7 +1044,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); @@ -1070,14 +1066,14 @@ 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); } err = connect(handle->socket, addr, addrlen); if (err) - return uv_translate_sys_error(err); + return uv_translate_sys_error(WSAGetLastError()); handle->flags |= UV_HANDLE_UDP_CONNECTED; @@ -1087,13 +1083,13 @@ int uv__udp_connect(uv_udp_t* handle, int uv__udp_disconnect(uv_udp_t* handle) { int err; - struct sockaddr addr; + struct sockaddr_storage addr; memset(&addr, 0, sizeof(addr)); err = connect(handle->socket, &addr, sizeof(addr)); if (err) - return uv_translate_sys_error(err); + return uv_translate_sys_error(WSAGetLastError()); handle->flags &= ~UV_HANDLE_UDP_CONNECTED; return 0; @@ -1121,7 +1117,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); } @@ -1163,7 +1159,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); } diff --git a/src/win/util.c b/src/win/util.c index 4de638f..9943205 100644 --- a/src/win/util.c +++ b/src/win/util.c @@ -30,12 +30,14 @@ #include "uv.h" #include "internal.h" +/* clang-format off */ #include #include #include #include #include #include +/* clang-format on */ #include #include @@ -60,15 +62,15 @@ #endif -/* Maximum environment variable size, including the terminating null */ -#define MAX_ENV_VAR_LENGTH 32767 +/* A RtlGenRandom() by any other name... */ +extern BOOLEAN NTAPI SystemFunction036(PVOID Buffer, ULONG BufferLength); /* Cached copy of the process title, plus a mutex guarding it. */ static char *process_title; static CRITICAL_SECTION process_title_lock; -/* Interval (in seconds) of the high-resolution clock. */ -static double hrtime_interval_ = 0; +/* Frequency of the high-resolution clock. */ +static uint64_t hrtime_frequency_ = 0; /* @@ -84,9 +86,9 @@ void uv__util_init(void) { * and precompute its reciprocal. */ if (QueryPerformanceFrequency(&perf_frequency)) { - hrtime_interval_ = 1.0 / perf_frequency.QuadPart; + hrtime_frequency_ = perf_frequency.QuadPart; } else { - hrtime_interval_= 0; + uv_fatal_error(GetLastError(), "QueryPerformanceFrequency"); } } @@ -151,20 +153,26 @@ int uv_exepath(char* buffer, size_t* size_ptr) { int uv_cwd(char* buffer, size_t* size) { DWORD utf16_len; - WCHAR utf16_buffer[MAX_PATH]; + WCHAR *utf16_buffer; int r; if (buffer == NULL || size == NULL) { return UV_EINVAL; } - utf16_len = GetCurrentDirectoryW(MAX_PATH, utf16_buffer); + utf16_len = GetCurrentDirectoryW(0, NULL); if (utf16_len == 0) { return uv_translate_sys_error(GetLastError()); - } else if (utf16_len > MAX_PATH) { - /* This should be impossible; however the CRT has a code path to deal with - * this scenario, so I added a check anyway. */ - return UV_EIO; + } + utf16_buffer = uv__malloc(utf16_len * sizeof(WCHAR)); + if (utf16_buffer == 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()); } /* utf16_len contains the length, *not* including the terminating null. */ @@ -188,8 +196,10 @@ int uv_cwd(char* buffer, size_t* size) { 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; } @@ -203,6 +213,8 @@ int uv_cwd(char* buffer, size_t* size) { *size > INT_MAX ? INT_MAX : (int) *size, NULL, NULL); + uv__free(utf16_buffer); + if (r == 0) { return uv_translate_sys_error(GetLastError()); } @@ -213,43 +225,61 @@ int uv_cwd(char* buffer, size_t* size) { int uv_chdir(const char* dir) { - WCHAR utf16_buffer[MAX_PATH]; - size_t utf16_len; + WCHAR *utf16_buffer; + size_t utf16_len, new_utf16_len; WCHAR drive_letter, env_var[4]; if (dir == NULL) { 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; + } + if (MultiByteToWideChar(CP_UTF8, 0, dir, -1, utf16_buffer, - MAX_PATH) == 0) { - DWORD error = GetLastError(); - /* The maximum length of the current working directory is 260 chars, - * including terminating null. If it doesn't fit, the path name must be too - * long. */ - if (error == ERROR_INSUFFICIENT_BUFFER) { - return UV_ENAMETOOLONG; - } else { - return uv_translate_sys_error(error); - } + utf16_len) == 0) { + uv__free(utf16_buffer); + return uv_translate_sys_error(GetLastError()); } if (!SetCurrentDirectoryW(utf16_buffer)) { + uv__free(utf16_buffer); return uv_translate_sys_error(GetLastError()); } /* 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. */ - utf16_len = GetCurrentDirectoryW(MAX_PATH, utf16_buffer); + 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) { - return uv_translate_sys_error(GetLastError()); - } else if (utf16_len > MAX_PATH) { - return UV_EIO; + uv__free(utf16_buffer); + return 0; } /* The returned directory should not have a trailing slash, unless it points @@ -281,11 +311,10 @@ int uv_chdir(const char* dir) { env_var[2] = L':'; env_var[3] = L'\0'; - if (!SetEnvironmentVariableW(env_var, utf16_buffer)) { - return uv_translate_sys_error(GetLastError()); - } + SetEnvironmentVariableW(env_var, utf16_buffer); } + uv__free(utf16_buffer); return 0; } @@ -358,6 +387,10 @@ char** uv_setup_args(int argc, char** argv) { } +void uv__process_title_cleanup(void) { +} + + int uv_set_process_title(const char* title) { int err; int length; @@ -459,23 +492,25 @@ uint64_t uv_hrtime(void) { return uv__hrtime(UV__NANOSEC); } -uint64_t uv__hrtime(double scale) { +uint64_t uv__hrtime(unsigned int scale) { LARGE_INTEGER counter; + double scaled_freq; + double result; - /* If the performance interval is zero, there's no support. */ - if (hrtime_interval_ == 0) { - return 0; - } - + assert(hrtime_frequency_ != 0); + assert(scale != 0); if (!QueryPerformanceCounter(&counter)) { - return 0; + uv_fatal_error(GetLastError(), "QueryPerformanceCounter"); } + assert(counter.QuadPart != 0); /* Because we have no guarantee about the order of magnitude of the * performance counter interval, integer math could cause this computation * to overflow. Therefore we resort to floating point math. */ - return (uint64_t) ((double) counter.QuadPart * hrtime_interval_ * scale); + scaled_freq = (double) hrtime_frequency_ / scale; + result = (double) counter.QuadPart / scaled_freq; + return (uint64_t) result; } @@ -496,103 +531,25 @@ 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); + *uptime = GetTickCount64() / 1000.0; + return 0; +} - if (object_type->NumInstances != PERF_NO_INSTANCES) - goto internalError; - 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) { + SYSTEM_INFO info; + unsigned rc; - 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 + */ + GetSystemInfo(&info); - /* If we get here, the uptime value was not found. */ - uv__free(malloced_buffer); - *uptime = 0; - return UV_ENOSYS; + rc = info.dwNumberOfProcessors; + if (rc < 1) + rc = 1; - internalError: - uv__free(malloced_buffer); - *uptime = 0; - return UV_EIO; + return rc; } @@ -1160,20 +1117,29 @@ int uv_os_homedir(char* buffer, size_t* size) { int uv_os_tmpdir(char* buffer, size_t* size) { - wchar_t path[MAX_PATH + 2]; + wchar_t *path; DWORD bufsize; size_t len; if (buffer == NULL || size == NULL || *size == 0) return UV_EINVAL; - len = GetTempPathW(ARRAY_SIZE(path), path); + len = 0; + len = GetTempPathW(0, NULL); + if (len == 0) { + return uv_translate_sys_error(GetLastError()); + } + /* 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); if (len == 0) { + uv__free(path); return uv_translate_sys_error(GetLastError()); - } else if (len > ARRAY_SIZE(path)) { - /* This should not be possible */ - return UV_EIO; } /* The returned directory should not have a trailing slash, unless it points @@ -1188,8 +1154,10 @@ int uv_os_tmpdir(char* buffer, size_t* size) { 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; } @@ -1203,6 +1171,7 @@ int uv_os_tmpdir(char* buffer, size_t* size) { *size, NULL, NULL); + uv__free(path); if (bufsize == 0) return uv_translate_sys_error(GetLastError()); @@ -1322,7 +1291,7 @@ int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16) { int uv__getpwuid_r(uv_passwd_t* pwd) { HANDLE token; wchar_t username[UNLEN + 1]; - wchar_t path[MAX_PATH]; + wchar_t *path; DWORD bufsize; int r; @@ -1333,15 +1302,24 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token) == 0) return uv_translate_sys_error(GetLastError()); - bufsize = ARRAY_SIZE(path); - if (!GetUserProfileDirectoryW(token, path, &bufsize)) { + bufsize = 0; + GetUserProfileDirectoryW(token, NULL, &bufsize); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { r = GetLastError(); CloseHandle(token); + return uv_translate_sys_error(r); + } - /* This should not be possible */ - if (r == ERROR_INSUFFICIENT_BUFFER) - return UV_ENOMEM; + path = uv__malloc(bufsize * sizeof(wchar_t)); + if (path == NULL) { + CloseHandle(token); + return UV_ENOMEM; + } + if (!GetUserProfileDirectoryW(token, path, &bufsize)) { + r = GetLastError(); + CloseHandle(token); + uv__free(path); return uv_translate_sys_error(r); } @@ -1351,6 +1329,7 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { bufsize = ARRAY_SIZE(username); if (!GetUserNameW(username, &bufsize)) { r = GetLastError(); + uv__free(path); /* This should not be possible */ if (r == ERROR_INSUFFICIENT_BUFFER) @@ -1361,6 +1340,7 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { pwd->homedir = NULL; r = uv__convert_utf16_to_utf8(path, -1, &pwd->homedir); + uv__free(path); if (r != 0) return r; @@ -1402,7 +1382,7 @@ int uv_os_environ(uv_env_item_t** envitems, int* count) { for (penv = env, i = 0; *penv != L'\0'; penv += wcslen(penv) + 1, i++); *envitems = uv__calloc(i, sizeof(**envitems)); - if (envitems == NULL) { + if (*envitems == NULL) { FreeEnvironmentStringsW(env); return UV_ENOMEM; } @@ -1458,7 +1438,9 @@ fail: int uv_os_getenv(const char* name, char* buffer, size_t* size) { - wchar_t var[MAX_ENV_VAR_LENGTH]; + wchar_t fastvar[512]; + wchar_t* var; + DWORD varlen; wchar_t* name_w; DWORD bufsize; size_t len; @@ -1472,25 +1454,52 @@ int uv_os_getenv(const char* name, char* buffer, size_t* size) { if (r != 0) return r; - SetLastError(ERROR_SUCCESS); - len = GetEnvironmentVariableW(name_w, var, MAX_ENV_VAR_LENGTH); + var = fastvar; + varlen = ARRAY_SIZE(fastvar); + + for (;;) { + SetLastError(ERROR_SUCCESS); + len = GetEnvironmentVariableW(name_w, var, varlen); + + if (len < varlen) + break; + + /* Try repeatedly because we might have been preempted by another thread + * modifying the environment variable just as we're trying to read it. + */ + if (var != fastvar) + uv__free(var); + + varlen = 1 + len; + var = uv__malloc(varlen * sizeof(*var)); + + if (var == NULL) { + r = UV_ENOMEM; + goto fail; + } + } + uv__free(name_w); - assert(len < MAX_ENV_VAR_LENGTH); /* len does not include the null */ + name_w = NULL; if (len == 0) { r = GetLastError(); - if (r != ERROR_SUCCESS) - return uv_translate_sys_error(r); + 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) { - return uv_translate_sys_error(GetLastError()); + r = uv_translate_sys_error(GetLastError()); + goto fail; } else if (bufsize > *size) { *size = bufsize; - return UV_ENOBUFS; + r = UV_ENOBUFS; + goto fail; } /* Convert to UTF-8 */ @@ -1503,11 +1512,23 @@ int uv_os_getenv(const char* name, char* buffer, size_t* size) { NULL, NULL); - if (bufsize == 0) - return uv_translate_sys_error(GetLastError()); + if (bufsize == 0) { + r = uv_translate_sys_error(GetLastError()); + goto fail; + } *size = bufsize - 1; - return 0; + r = 0; + +fail: + + if (name_w != NULL) + uv__free(name_w); + + if (var != fastvar) + uv__free(var); + + return r; } @@ -1565,26 +1586,36 @@ int uv_os_unsetenv(const char* name) { int uv_os_gethostname(char* buffer, size_t* size) { - char buf[UV_MAXHOSTNAMESIZE]; + WCHAR buf[UV_MAXHOSTNAMESIZE]; size_t len; + char* utf8_str; + int convert_result; if (buffer == NULL || size == NULL || *size == 0) return UV_EINVAL; uv__once_init(); /* Initialize winsock */ - if (gethostname(buf, sizeof(buf)) != 0) + if (pGetHostNameW == NULL) + return UV_ENOSYS; + + if (pGetHostNameW(buf, UV_MAXHOSTNAMESIZE) != 0) return uv_translate_sys_error(WSAGetLastError()); - buf[sizeof(buf) - 1] = '\0'; /* Null terminate, just to be safe. */ - len = strlen(buf); + 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, buf, len + 1); + memcpy(buffer, utf8_str, len + 1); + uv__free(utf8_str); *size = len; return 0; } @@ -1709,7 +1740,9 @@ int uv_os_uname(uv_utsname_t* buffer) { 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; @@ -1776,7 +1809,7 @@ int uv_os_uname(uv_utsname_t* buffer) { "MINGW32_NT-%u.%u", (unsigned int) os_info.dwMajorVersion, (unsigned int) os_info.dwMinorVersion); - assert(r < sizeof(buffer->sysname)); + assert((size_t)r < sizeof(buffer->sysname)); #else uv__strscpy(buffer->sysname, "Windows_NT", sizeof(buffer->sysname)); #endif @@ -1788,7 +1821,7 @@ int uv_os_uname(uv_utsname_t* buffer) { (unsigned int) os_info.dwMajorVersion, (unsigned int) os_info.dwMinorVersion, (unsigned int) os_info.dwBuildNumber); - assert(r < sizeof(buffer->release)); + assert((size_t)r < sizeof(buffer->release)); /* Populate the machine field. */ GetSystemInfo(&system_info); @@ -1862,13 +1895,10 @@ int uv_gettimeofday(uv_timeval64_t* tv) { } int uv__random_rtlgenrandom(void* buf, size_t buflen) { - if (pRtlGenRandom == NULL) - return UV_ENOSYS; - if (buflen == 0) return 0; - if (pRtlGenRandom(buf, buflen) == FALSE) + if (SystemFunction036(buf, buflen) == FALSE) return UV_EIO; return 0; diff --git a/src/win/winapi.c b/src/win/winapi.c index 85a9de8..53147b8 100644 --- a/src/win/winapi.c +++ b/src/win/winapi.c @@ -36,9 +36,6 @@ sNtQueryDirectoryFile pNtQueryDirectoryFile; sNtQuerySystemInformation pNtQuerySystemInformation; sNtQueryInformationProcess pNtQueryInformationProcess; -/* Advapi32 function pointers */ -sRtlGenRandom pRtlGenRandom; - /* Kernel32 function pointers */ sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx; @@ -48,13 +45,15 @@ sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification; /* User32.dll function pointer */ sSetWinEventHook pSetWinEventHook; +/* ws2_32.dll function pointer */ +uv_sGetHostNameW pGetHostNameW; -void uv_winapi_init(void) { +void uv__winapi_init(void) { HMODULE ntdll_module; HMODULE powrprof_module; HMODULE user32_module; HMODULE kernel32_module; - HMODULE advapi32_module; + HMODULE ws2_32_module; ntdll_module = GetModuleHandleA("ntdll.dll"); if (ntdll_module == NULL) { @@ -127,23 +126,22 @@ void uv_winapi_init(void) { 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"); } - advapi32_module = GetModuleHandleA("advapi32.dll"); - if (advapi32_module == NULL) { - uv_fatal_error(GetLastError(), "GetModuleHandleA"); + ws2_32_module = GetModuleHandleA("ws2_32.dll"); + if (ws2_32_module != NULL) { + pGetHostNameW = (uv_sGetHostNameW) GetProcAddress( + ws2_32_module, + "GetHostNameW"); } - - pRtlGenRandom = - (sRtlGenRandom) GetProcAddress(advapi32_module, "SystemFunction036"); } diff --git a/src/win/winapi.h b/src/win/winapi.h index fcc7065..d380bda 100644 --- a/src/win/winapi.h +++ b/src/win/winapi.h @@ -4152,6 +4152,10 @@ typedef const UNICODE_STRING *PCUNICODE_STRING; struct { UCHAR DataBuffer[1]; } GenericReparseBuffer; + struct { + ULONG StringCount; + WCHAR StringList[1]; + } AppExecLinkReparseBuffer; }; } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; #endif @@ -4517,6 +4521,9 @@ typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { #ifndef IO_REPARSE_TAG_SYMLINK # define IO_REPARSE_TAG_SYMLINK (0xA000000CL) #endif +#ifndef IO_REPARSE_TAG_APPEXECLINK +# define IO_REPARSE_TAG_APPEXECLINK (0x8000001BL) +#endif typedef VOID (NTAPI *PIO_APC_ROUTINE) (PVOID ApcContext, @@ -4589,11 +4596,6 @@ typedef NTSTATUS (NTAPI *sNtQueryInformationProcess) ULONG Length, PULONG ReturnLength); -/* - * Advapi32 headers - */ -typedef BOOLEAN (WINAPI *sRtlGenRandom)(PVOID Buffer, ULONG BufferLength); - /* * Kernel32 headers */ @@ -4724,6 +4726,18 @@ typedef HWINEVENTHOOK (WINAPI *sSetWinEventHook) DWORD idThread, UINT dwflags); +/* From mstcpip.h */ +typedef struct _TCP_INITIAL_RTO_PARAMETERS { + USHORT Rtt; + UCHAR MaxSynRetransmissions; +} TCP_INITIAL_RTO_PARAMETERS, *PTCP_INITIAL_RTO_PARAMETERS; + +#ifndef TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS +# define TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS ((UCHAR) -2) +#endif +#ifndef SIO_TCP_INITIAL_RTO +# define SIO_TCP_INITIAL_RTO _WSAIOW(IOC_VENDOR,17) +#endif /* Ntdll function pointers */ extern sRtlGetVersion pRtlGetVersion; @@ -4736,9 +4750,6 @@ extern sNtQueryDirectoryFile pNtQueryDirectoryFile; extern sNtQuerySystemInformation pNtQuerySystemInformation; extern sNtQueryInformationProcess pNtQueryInformationProcess; -/* Advapi32 function pointers */ -extern sRtlGenRandom pRtlGenRandom; - /* Kernel32 function pointers */ extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx; @@ -4748,4 +4759,11 @@ extern sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotifi /* User32.dll function pointer */ extern sSetWinEventHook pSetWinEventHook; +/* ws2_32.dll function pointer */ +/* mingw doesn't have this definition, so let's declare it here locally */ +typedef int (WINAPI *uv_sGetHostNameW) + (PWSTR, + int); +extern uv_sGetHostNameW pGetHostNameW; + #endif /* UV_WIN_WINAPI_H_ */ diff --git a/src/win/winsock.c b/src/win/winsock.c index 4cf6e6b..a68b095 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/test/benchmark-async-pummel.c b/test/benchmark-async-pummel.c index 119ae5e..49660a6 100644 --- a/test/benchmark-async-pummel.c +++ b/test/benchmark-async-pummel.c @@ -68,7 +68,7 @@ static int test_async_pummel(int nthreads) { int i; tids = calloc(nthreads, sizeof(tids[0])); - ASSERT(tids != NULL); + ASSERT_NOT_NULL(tids); ASSERT(0 == uv_async_init(uv_default_loop(), &handle, async_cb)); ACCESS_ONCE(const char*, handle.data) = running; diff --git a/test/benchmark-async.c b/test/benchmark-async.c index e44165f..5167ecb 100644 --- a/test/benchmark-async.c +++ b/test/benchmark-async.c @@ -79,7 +79,7 @@ static int test_async(int nthreads) { int i; threads = calloc(nthreads, sizeof(threads[0])); - ASSERT(threads != NULL); + ASSERT_NOT_NULL(threads); for (i = 0; i < nthreads; i++) { ctx = threads + i; diff --git a/test/benchmark-list.h b/test/benchmark-list.h index 29e44c3..1ab8f25 100644 --- a/test/benchmark-list.h +++ b/test/benchmark-list.h @@ -23,7 +23,9 @@ BENCHMARK_DECLARE (sizes) BENCHMARK_DECLARE (loop_count) BENCHMARK_DECLARE (loop_count_timed) 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 +74,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) @@ -81,7 +84,6 @@ HELPER_DECLARE (tcp_pump_server) HELPER_DECLARE (pipe_pump_server) HELPER_DECLARE (tcp4_echo_server) HELPER_DECLARE (pipe_echo_server) -HELPER_DECLARE (dns_server) TASK_LIST_START BENCHMARK_ENTRY (sizes) @@ -91,6 +93,10 @@ TASK_LIST_START 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) @@ -156,6 +162,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-million-async.c b/test/benchmark-million-async.c index 5395ed5..937a12f 100644 --- a/test/benchmark-million-async.c +++ b/test/benchmark-million-async.c @@ -86,7 +86,7 @@ BENCHMARK_IMPL(million_async) { timeout = 5000; container = malloc(sizeof(*container)); - ASSERT(container != NULL); + ASSERT_NOT_NULL(container); container->async_events = 0; container->handles_seen = 0; diff --git a/test/benchmark-million-timers.c b/test/benchmark-million-timers.c index 60a308b..ef25c20 100644 --- a/test/benchmark-million-timers.c +++ b/test/benchmark-million-timers.c @@ -49,7 +49,7 @@ BENCHMARK_IMPL(million_timers) { int i; timers = malloc(NUM_TIMERS * sizeof(timers[0])); - ASSERT(timers != NULL); + ASSERT_NOT_NULL(timers); loop = uv_default_loop(); timeout = 0; diff --git a/test/benchmark-multi-accept.c b/test/benchmark-multi-accept.c index 5a18623..86b7da5 100644 --- a/test/benchmark-multi-accept.c +++ b/test/benchmark-multi-accept.c @@ -114,7 +114,7 @@ static void ipc_connection_cb(uv_stream_t* ipc_pipe, int status) { buf = uv_buf_init("PING", 4); sc = container_of(ipc_pipe, struct ipc_server_ctx, ipc_pipe); pc = calloc(1, sizeof(*pc)); - ASSERT(pc != NULL); + ASSERT_NOT_NULL(pc); if (ipc_pipe->type == UV_TCP) ASSERT(0 == uv_tcp_init(loop, (uv_tcp_t*) &pc->peer_handle)); @@ -295,7 +295,7 @@ static void sv_connection_cb(uv_stream_t* server_handle, int status) { ASSERT(status == 0); storage = malloc(sizeof(*storage)); - ASSERT(storage != NULL); + ASSERT_NOT_NULL(storage); if (server_handle->type == UV_TCP) ASSERT(0 == uv_tcp_init(server_handle->loop, (uv_tcp_t*) storage)); @@ -372,8 +372,8 @@ static int test_tcp(unsigned int num_servers, unsigned int num_clients) { servers = calloc(num_servers, sizeof(servers[0])); clients = calloc(num_clients, sizeof(clients[0])); - ASSERT(servers != NULL); - ASSERT(clients != NULL); + ASSERT_NOT_NULL(servers); + ASSERT_NOT_NULL(clients); /* We're making the assumption here that from the perspective of the * OS scheduler, threads are functionally equivalent to and interchangeable diff --git a/test/benchmark-ping-udp.c b/test/benchmark-ping-udp.c index a29502a..cf9ca98 100644 --- a/test/benchmark-ping-udp.c +++ b/test/benchmark-ping-udp.c @@ -94,6 +94,9 @@ 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(nread > 0); + /* Now we count the pings */ for (i = 0; i < nread; i++) { ASSERT(buf->base[i] == PING[pinger->state]); @@ -108,7 +111,8 @@ 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) { @@ -122,6 +126,8 @@ static void udp_pinger_new(void) { /* Try to do NUM_PINGS ping-pongs (connection-less). */ r = uv_udp_init(loop, &pinger->udp); ASSERT(r == 0); + r = uv_udp_bind(&pinger->udp, (const struct sockaddr*) &pinger->server_addr, 0); + ASSERT(r == 0); pinger->udp.data = pinger; diff --git a/test/benchmark-pound.c b/test/benchmark-pound.c index 79f3634..830bc55 100644 --- a/test/benchmark-pound.c +++ b/test/benchmark-pound.c @@ -114,11 +114,11 @@ static void connect_cb(uv_connect_t* req, int status) { return; } - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status == 0); conn = (conn_rec*)req->data; - ASSERT(conn != NULL); + ASSERT_NOT_NULL(conn); #if DEBUG printf("connect_cb %d\n", conn->i); @@ -137,7 +137,7 @@ static void connect_cb(uv_connect_t* req, int status) { static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { - ASSERT(stream != NULL); + ASSERT_NOT_NULL(stream); #if DEBUG printf("read_cb %d\n", p->i); @@ -161,7 +161,7 @@ static void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { static void close_cb(uv_handle_t* handle) { conn_rec* p = (conn_rec*)handle->data; - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); closed_streams++; #if DEBUG diff --git a/test/benchmark-pump.c b/test/benchmark-pump.c index 8685258..7d3977d 100644 --- a/test/benchmark-pump.c +++ b/test/benchmark-pump.c @@ -390,6 +390,7 @@ HELPER_IMPL(tcp_pump_server) { r = uv_listen((uv_stream_t*)&tcpServer, MAX_WRITE_HANDLES, connection_cb); ASSERT(r == 0); + notify_parent_process(); uv_run(loop, UV_RUN_DEFAULT); return 0; @@ -411,6 +412,7 @@ HELPER_IMPL(pipe_pump_server) { r = uv_listen((uv_stream_t*)&pipeServer, MAX_WRITE_HANDLES, connection_cb); ASSERT(r == 0); + notify_parent_process(); uv_run(loop, UV_RUN_DEFAULT); MAKE_VALGRIND_HAPPY(); diff --git a/test/benchmark-queue-work.c b/test/benchmark-queue-work.c new file mode 100644 index 0000000..2dd5cb6 --- /dev/null +++ b/test/benchmark-queue-work.c @@ -0,0 +1,68 @@ +/* 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 "task.h" +#include "uv.h" + +static int done = 0; +static unsigned events = 0; +static unsigned result; + +static unsigned fastrand(void) { + static unsigned g = 0; + g = g * 214013 + 2531011; + return g; +} + +static void work_cb(uv_work_t* req) { + req->data = &result; + *(unsigned*)req->data = fastrand(); +} + +static void after_work_cb(uv_work_t* req, int status) { + events++; + if (!done) + ASSERT_EQ(0, uv_queue_work(req->loop, req, work_cb, after_work_cb)); +} + +static void timer_cb(uv_timer_t* handle) { done = 1; } + +BENCHMARK_IMPL(queue_work) { + uv_timer_t timer_handle; + uv_work_t work; + uv_loop_t* loop; + int timeout; + + loop = uv_default_loop(); + timeout = 5000; + + ASSERT_EQ(0, uv_timer_init(loop, &timer_handle)); + ASSERT_EQ(0, uv_timer_start(&timer_handle, timer_cb, timeout, 0)); + + ASSERT_EQ(0, uv_queue_work(loop, &work, work_cb, after_work_cb)); + ASSERT_EQ(0, uv_run(loop, UV_RUN_DEFAULT)); + + printf("%s async jobs in %.1f seconds (%s/s)\n", fmt(events), timeout / 1000., + fmt(events / (timeout / 1000.))); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/test/benchmark-tcp-write-batch.c b/test/benchmark-tcp-write-batch.c index 96921b7..16aa72f 100644 --- a/test/benchmark-tcp-write-batch.c +++ b/test/benchmark-tcp-write-batch.c @@ -71,7 +71,7 @@ static void connect_cb(uv_connect_t* req, int status) { static void write_cb(uv_write_t* req, int status) { - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status == 0); write_cb_called++; } @@ -103,7 +103,7 @@ BENCHMARK_IMPL(tcp_write_batch) { int r; write_reqs = malloc(sizeof(*write_reqs) * NUM_WRITE_REQS); - ASSERT(write_reqs != NULL); + ASSERT_NOT_NULL(write_reqs); /* Prepare the data to write out. */ for (i = 0; i < NUM_WRITE_REQS; i++) { diff --git a/test/benchmark-udp-pummel.c b/test/benchmark-udp-pummel.c index 68a2373..1a22057 100644 --- a/test/benchmark-udp-pummel.c +++ b/test/benchmark-udp-pummel.c @@ -72,7 +72,7 @@ static void alloc_cb(uv_handle_t* handle, static void send_cb(uv_udp_send_t* req, int status) { struct sender_state* s; - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); if (status != 0) { ASSERT(status == UV_ECANCELED); @@ -127,7 +127,7 @@ static void recv_cb(uv_udp_t* handle, static void close_cb(uv_handle_t* handle) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); close_cb_called++; } @@ -179,11 +179,11 @@ static int pummel(unsigned int n_senders, uv_unref((uv_handle_t*)&s->udp_handle); } - bufs[0] = uv_buf_init(EXPECTED + 0, 10); - bufs[1] = uv_buf_init(EXPECTED + 10, 10); - bufs[2] = uv_buf_init(EXPECTED + 20, 10); - bufs[3] = uv_buf_init(EXPECTED + 30, 10); - bufs[4] = uv_buf_init(EXPECTED + 40, 5); + bufs[0] = uv_buf_init(&EXPECTED[0], 10); + bufs[1] = uv_buf_init(&EXPECTED[10], 10); + bufs[2] = uv_buf_init(&EXPECTED[20], 10); + bufs[3] = uv_buf_init(&EXPECTED[30], 10); + bufs[4] = uv_buf_init(&EXPECTED[40], 5); for (i = 0; i < n_senders; i++) { struct sender_state* s = senders + i; diff --git a/test/blackhole-server.c b/test/blackhole-server.c index ad878b3..0a8758e 100644 --- a/test/blackhole-server.c +++ b/test/blackhole-server.c @@ -47,7 +47,7 @@ static void connection_cb(uv_stream_t* stream, int status) { ASSERT(stream == (uv_stream_t*)&tcp_server); conn = malloc(sizeof *conn); - ASSERT(conn != NULL); + ASSERT_NOT_NULL(conn); r = uv_tcp_init(stream->loop, &conn->handle); ASSERT(r == 0); @@ -114,6 +114,7 @@ HELPER_IMPL(tcp4_blackhole_server) { r = uv_listen((uv_stream_t*)&tcp_server, 128, connection_cb); ASSERT(r == 0); + notify_parent_process(); r = uv_run(loop, UV_RUN_DEFAULT); ASSERT(0 && "Blackhole server dropped out of event loop."); diff --git a/test/dns-server.c b/test/dns-server.c deleted file mode 100644 index 80052c7..0000000 --- a/test/dns-server.c +++ /dev/null @@ -1,340 +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 "task.h" -#include -#include -#include - - -typedef struct { - uv_write_t req; - uv_buf_t buf; -} write_req_t; - - -/* used to track multiple DNS requests received */ -typedef struct { - char* prevbuf_ptr; - int prevbuf_pos; - int prevbuf_rem; -} dnsstate; - - -/* modify handle to append dnsstate */ -typedef struct { - uv_tcp_t handle; - dnsstate state; -} dnshandle; - - -static uv_loop_t* loop; - - -static uv_tcp_t server; - - -static void after_write(uv_write_t* req, int status); -static void after_read(uv_stream_t*, ssize_t nread, const uv_buf_t* buf); -static void on_close(uv_handle_t* peer); -static void on_connection(uv_stream_t*, int status); - -#define WRITE_BUF_LEN (64*1024) -#define DNSREC_LEN (4) - -#define LEN_OFFSET 0 -#define QUERYID_OFFSET 2 - -static unsigned char DNSRsp[] = { - 0, 43, 0, 0, 0x81, 0x80, 0, 1, 0, 1, 0, 0, 0, 0 -}; - -static unsigned char qrecord[] = { - 5, 'e', 'c', 'h', 'o', 's', 3, 's', 'r', 'v', 0, 0, 1, 0, 1 -}; - -static unsigned char arecord[] = { - 0xc0, 0x0c, 0, 1, 0, 1, 0, 0, 5, 0xbd, 0, 4, 10, 0, 1, 1 -}; - - -static void after_write(uv_write_t* req, int status) { - write_req_t* wr; - - if (status) { - fprintf(stderr, "uv_write error: %s\n", uv_strerror(status)); - ASSERT(0); - } - - wr = (write_req_t*) req; - - /* Free the read/write buffer and the request */ - free(wr->buf.base); - free(wr); -} - - -static void after_shutdown(uv_shutdown_t* req, int status) { - uv_close((uv_handle_t*) req->handle, on_close); - free(req); -} - - -static void addrsp(write_req_t* wr, char* hdr) { - char * dnsrsp; - short int rsplen; - short int* reclen; - - rsplen = sizeof(DNSRsp) + sizeof(qrecord) + sizeof(arecord); - - ASSERT (rsplen + wr->buf.len < WRITE_BUF_LEN); - - dnsrsp = wr->buf.base + wr->buf.len; - - /* copy stock response */ - memcpy(dnsrsp, DNSRsp, sizeof(DNSRsp)); - memcpy(dnsrsp + sizeof(DNSRsp), qrecord, sizeof(qrecord)); - memcpy(dnsrsp + sizeof(DNSRsp) + sizeof(qrecord), arecord, sizeof(arecord)); - - /* overwrite with network order length and id from request header */ - reclen = (short int*)dnsrsp; - *reclen = htons(rsplen-2); - dnsrsp[QUERYID_OFFSET] = hdr[QUERYID_OFFSET]; - dnsrsp[QUERYID_OFFSET+1] = hdr[QUERYID_OFFSET+1]; - - wr->buf.len += rsplen; -} - -static void process_req(uv_stream_t* handle, - ssize_t nread, - const uv_buf_t* buf) { - write_req_t* wr; - dnshandle* dns = (dnshandle*)handle; - char hdrbuf[DNSREC_LEN]; - int hdrbuf_remaining = DNSREC_LEN; - int rec_remaining = 0; - int readbuf_remaining; - char* dnsreq; - char* hdrstart; - int usingprev = 0; - - wr = (write_req_t*) malloc(sizeof *wr); - wr->buf.base = (char*)malloc(WRITE_BUF_LEN); - wr->buf.len = 0; - - if (dns->state.prevbuf_ptr != NULL) { - dnsreq = dns->state.prevbuf_ptr + dns->state.prevbuf_pos; - readbuf_remaining = dns->state.prevbuf_rem; - usingprev = 1; - } else { - dnsreq = buf->base; - readbuf_remaining = nread; - } - hdrstart = dnsreq; - - while (dnsreq != NULL) { - /* something to process */ - while (readbuf_remaining > 0) { - /* something to process in current buffer */ - if (hdrbuf_remaining > 0) { - /* process len and id */ - if (readbuf_remaining < hdrbuf_remaining) { - /* too little to get request header. save for next buffer */ - memcpy(&hdrbuf[DNSREC_LEN - hdrbuf_remaining], - dnsreq, - readbuf_remaining); - hdrbuf_remaining = DNSREC_LEN - readbuf_remaining; - break; - } else { - /* save header */ - memcpy(&hdrbuf[DNSREC_LEN - hdrbuf_remaining], - dnsreq, - hdrbuf_remaining); - dnsreq += hdrbuf_remaining; - readbuf_remaining -= hdrbuf_remaining; - hdrbuf_remaining = 0; - - /* get record length */ - rec_remaining = (unsigned) hdrbuf[0] * 256 + (unsigned) hdrbuf[1]; - rec_remaining -= (DNSREC_LEN - 2); - } - } - - if (rec_remaining <= readbuf_remaining) { - /* prepare reply */ - addrsp(wr, hdrbuf); - - /* move to next record */ - dnsreq += rec_remaining; - hdrstart = dnsreq; - readbuf_remaining -= rec_remaining; - rec_remaining = 0; - hdrbuf_remaining = DNSREC_LEN; - } else { - /* otherwise this buffer is done. */ - rec_remaining -= readbuf_remaining; - break; - } - } - - /* If we had to use bytes from prev buffer, start processing the current - * one. - */ - if (usingprev == 1) { - /* free previous buffer */ - free(dns->state.prevbuf_ptr); - dnsreq = buf->base; - readbuf_remaining = nread; - usingprev = 0; - } else { - dnsreq = NULL; - } - } - - /* send write buffer */ - if (wr->buf.len > 0) { - if (uv_write((uv_write_t*) &wr->req, handle, &wr->buf, 1, after_write)) { - FATAL("uv_write failed"); - } - } - - if (readbuf_remaining > 0) { - /* save start of record position, so we can continue on next read */ - dns->state.prevbuf_ptr = buf->base; - dns->state.prevbuf_pos = hdrstart - buf->base; - dns->state.prevbuf_rem = nread - dns->state.prevbuf_pos; - } else { - /* nothing left in this buffer */ - dns->state.prevbuf_ptr = NULL; - dns->state.prevbuf_pos = 0; - dns->state.prevbuf_rem = 0; - free(buf->base); - } -} - -static void after_read(uv_stream_t* handle, - ssize_t nread, - const uv_buf_t* buf) { - uv_shutdown_t* req; - - if (nread < 0) { - /* Error or EOF */ - ASSERT(nread == UV_EOF); - - if (buf->base) { - free(buf->base); - } - - req = malloc(sizeof *req); - uv_shutdown(req, handle, after_shutdown); - - return; - } - - if (nread == 0) { - /* Everything OK, but nothing read. */ - free(buf->base); - return; - } - /* process requests and send responses */ - process_req(handle, nread, buf); -} - - -static void on_close(uv_handle_t* peer) { - free(peer); -} - - -static void buf_alloc(uv_handle_t* handle, - size_t suggested_size, - uv_buf_t* buf) { - buf->base = malloc(suggested_size); - buf->len = suggested_size; -} - - -static void on_connection(uv_stream_t* server, int status) { - dnshandle* handle; - int r; - - ASSERT(status == 0); - - handle = (dnshandle*) malloc(sizeof *handle); - ASSERT(handle != NULL); - - /* initialize read buffer state */ - handle->state.prevbuf_ptr = 0; - handle->state.prevbuf_pos = 0; - handle->state.prevbuf_rem = 0; - - r = uv_tcp_init(loop, (uv_tcp_t*)handle); - ASSERT(r == 0); - - r = uv_accept(server, (uv_stream_t*)handle); - ASSERT(r == 0); - - r = uv_read_start((uv_stream_t*)handle, buf_alloc, after_read); - ASSERT(r == 0); -} - - -static int dns_start(int port) { - struct sockaddr_in addr; - int r; - - ASSERT(0 == uv_ip4_addr("0.0.0.0", port, &addr)); - - r = uv_tcp_init(loop, &server); - if (r) { - /* TODO: Error codes */ - fprintf(stderr, "Socket creation error\n"); - return 1; - } - - r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); - if (r) { - /* TODO: Error codes */ - fprintf(stderr, "Bind error\n"); - return 1; - } - - r = uv_listen((uv_stream_t*)&server, 128, on_connection); - if (r) { - /* TODO: Error codes */ - fprintf(stderr, "Listen error\n"); - return 1; - } - - return 0; -} - - -HELPER_IMPL(dns_server) { - loop = uv_default_loop(); - - if (dns_start(TEST_PORT_2)) - return 1; - - uv_run(loop, UV_RUN_DEFAULT); - return 0; -} diff --git a/test/echo-server.c b/test/echo-server.c index c65142f..058c992 100644 --- a/test/echo-server.c +++ b/test/echo-server.c @@ -65,25 +65,35 @@ static void after_write(uv_write_t* req, int status) { static void after_shutdown(uv_shutdown_t* req, int status) { + ASSERT_EQ(status, 0); 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); + free(req); +} + + static void after_read(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { int i; write_req_t *wr; uv_shutdown_t* sreq; + int shutdown = 0; if (nread < 0) { /* Error or EOF */ - ASSERT(nread == UV_EOF); + ASSERT_EQ(nread, UV_EOF); free(buf->base); sreq = malloc(sizeof* sreq); - ASSERT(0 == uv_shutdown(sreq, handle, after_shutdown)); + if (uv_is_writable(handle)) { + ASSERT_EQ(0, uv_shutdown(sreq, handle, after_shutdown)); + } return; } @@ -96,29 +106,42 @@ static void after_read(uv_stream_t* handle, /* * Scan for the letter Q which signals that we should quit the server. * If we get QS it means close the stream. + * If we get QSS it means shutdown the stream. + * If we get QSH it means disable linger before close the socket. */ - if (!server_closed) { - for (i = 0; i < nread; i++) { - if (buf->base[i] == 'Q') { - if (i + 1 < nread && buf->base[i + 1] == 'S') { - free(buf->base); - uv_close((uv_handle_t*)handle, on_close); - return; - } else { - uv_close(server, on_server_close); - server_closed = 1; - } + for (i = 0; i < nread; i++) { + if (buf->base[i] == 'Q') { + if (i + 1 < nread && buf->base[i + 1] == 'S') { + int reset = 0; + if (i + 2 < nread && buf->base[i + 2] == 'S') + shutdown = 1; + 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)); + else if (shutdown) + break; + else + uv_close((uv_handle_t*) handle, on_close); + free(buf->base); + return; + } else if (!server_closed) { + uv_close(server, on_server_close); + server_closed = 1; } } } wr = (write_req_t*) malloc(sizeof *wr); - ASSERT(wr != NULL); + ASSERT_NOT_NULL(wr); wr->buf = uv_buf_init(buf->base, nread); if (uv_write(&wr->req, handle, &wr->buf, 1, after_write)) { FATAL("uv_write failed"); } + + if (shutdown) + ASSERT_EQ(0, uv_shutdown(malloc(sizeof* sreq), handle, on_shutdown)); } @@ -155,14 +178,14 @@ static void on_connection(uv_stream_t* server, int status) { switch (serverType) { case TCP: stream = malloc(sizeof(uv_tcp_t)); - ASSERT(stream != NULL); + ASSERT_NOT_NULL(stream); r = uv_tcp_init(loop, (uv_tcp_t*)stream); ASSERT(r == 0); break; case PIPE: stream = malloc(sizeof(uv_pipe_t)); - ASSERT(stream != NULL); + ASSERT_NOT_NULL(stream); r = uv_pipe_init(loop, (uv_pipe_t*)stream, 0); ASSERT(r == 0); break; @@ -197,7 +220,7 @@ static uv_udp_send_t* send_alloc(void) { } static void on_send(uv_udp_send_t* req, int status) { - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status == 0); req->data = send_freelist; send_freelist = req; @@ -209,6 +232,7 @@ static void on_recv(uv_udp_t* handle, const struct sockaddr* addr, unsigned flags) { uv_buf_t sndbuf; + uv_udp_send_t* req; if (nread == 0) { /* Everything OK, but nothing read. */ @@ -218,8 +242,8 @@ static void on_recv(uv_udp_t* handle, ASSERT(nread > 0); ASSERT(addr->sa_family == AF_INET); - uv_udp_send_t* req = send_alloc(); - ASSERT(req != NULL); + 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)); } @@ -228,7 +252,7 @@ static int tcp4_echo_start(int port) { struct sockaddr_in addr; int r; - ASSERT(0 == uv_ip4_addr("0.0.0.0", port, &addr)); + ASSERT(0 == uv_ip4_addr("127.0.0.1", port, &addr)); server = (uv_handle_t*)&tcpServer; serverType = TCP; diff --git a/test/run-benchmarks.c b/test/run-benchmarks.c index 6e42623..2b343da 100644 --- a/test/run-benchmarks.c +++ b/test/run-benchmarks.c @@ -28,13 +28,22 @@ /* Actual benchmarks and helpers are defined in benchmark-list.h */ #include "benchmark-list.h" +#ifdef __MVS__ +#include "zos-base.h" +/* Initialize environment and zoslib */ +__attribute__((constructor)) void init() { + zoslib_config_t config; + init_zoslib_config(&config); + init_zoslib(config); +} +#endif + static int maybe_run_test(int argc, char **argv); int main(int argc, char **argv) { - if (platform_init(argc, argv)) - return EXIT_FAILURE; + platform_init(argc, argv); switch (argc) { case 1: return run_tests(1); @@ -45,8 +54,6 @@ int main(int argc, char **argv) { fflush(stderr); return EXIT_FAILURE; } - - return EXIT_SUCCESS; } diff --git a/test/run-tests.c b/test/run-tests.c index 3a6452f..86b0359 100644 --- a/test/run-tests.c +++ b/test/run-tests.c @@ -36,10 +36,19 @@ /* Actual tests and helpers are defined in test-list.h */ #include "test-list.h" +#ifdef __MVS__ +#include "zos-base.h" +/* Initialize environment and zoslib */ +__attribute__((constructor)) void init() { + zoslib_config_t config; + init_zoslib_config(&config); + init_zoslib(config); +} +#endif + 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); @@ -50,6 +59,10 @@ int spawn_tcp_server_helper(void); static int maybe_run_test(int argc, char **argv); +#ifdef _WIN32 +typedef BOOL (WINAPI *sCompareObjectHandles)(_In_ HANDLE, _In_ HANDLE); +#endif + int main(int argc, char **argv) { #ifndef _WIN32 @@ -59,9 +72,7 @@ int main(int argc, char **argv) { } #endif - if (platform_init(argc, argv)) - return EXIT_FAILURE; - + platform_init(argc, argv); argv = uv_setup_args(argc, argv); switch (argc) { @@ -107,10 +118,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(); } @@ -151,7 +158,7 @@ static int maybe_run_test(int argc, char **argv) { if (strcmp(argv[1], "spawn_helper4") == 0) { notify_parent_process(); /* Never surrender, never return! */ - while (1) uv_sleep(10000); + for (;;) uv_sleep(10000); } if (strcmp(argv[1], "spawn_helper5") == 0) { @@ -196,7 +203,7 @@ static int maybe_run_test(int argc, char **argv) { /* Test if the test value from the parent is still set */ test = getenv("ENV_TEST"); - ASSERT(test != NULL); + ASSERT_NOT_NULL(test); r = fprintf(stdout, "%s", test); ASSERT(r > 0); @@ -204,22 +211,36 @@ static int maybe_run_test(int argc, char **argv) { return 1; } -#ifndef _WIN32 if (strcmp(argv[1], "spawn_helper8") == 0) { - int fd; - + uv_os_fd_t closed_fd; + uv_os_fd_t open_fd; +#ifdef _WIN32 + DWORD flags; + HMODULE kernelbase_module; + sCompareObjectHandles pCompareObjectHandles; /* function introduced in Windows 10 */ +#endif notify_parent_process(); - ASSERT(sizeof(fd) == read(0, &fd, sizeof(fd))); - ASSERT(fd > 2); + ASSERT(sizeof(closed_fd) == read(0, &closed_fd, sizeof(closed_fd))); + ASSERT(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)); + kernelbase_module = GetModuleHandleA("kernelbase.dll"); + pCompareObjectHandles = (sCompareObjectHandles) + GetProcAddress(kernelbase_module, "CompareObjectHandles"); + ASSERT(pCompareObjectHandles == NULL || !pCompareObjectHandles(open_fd, closed_fd)); +#else + ASSERT(open_fd > 2); + ASSERT(closed_fd > 2); # if defined(__PASE__) /* On IBMi PASE, write() returns 1 */ - ASSERT(1 == write(fd, "x", 1)); + ASSERT(1 == write(closed_fd, "x", 1)); # else - ASSERT(-1 == write(fd, "x", 1)); + ASSERT(-1 == write(closed_fd, "x", 1)); # endif /* !__PASE__ */ - +#endif return 1; } -#endif /* !_WIN32 */ if (strcmp(argv[1], "spawn_helper9") == 0) { notify_parent_process(); diff --git a/test/runner-unix.c b/test/runner-unix.c index dbb33bf..c165aab 100644 --- a/test/runner-unix.c +++ b/test/runner-unix.c @@ -26,7 +26,7 @@ #include /* uintptr_t */ #include -#include /* readlink, usleep */ +#include /* usleep */ #include /* strdup */ #include #include @@ -67,18 +67,12 @@ void notify_parent_process(void) { /* Do platform-specific initialization. */ -int platform_init(int argc, char **argv) { +void platform_init(int argc, char **argv) { /* Disable stdio output buffering. */ setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); signal(SIGPIPE, SIG_IGN); - - if (realpath(argv[0], executable_path) == NULL) { - perror("realpath"); - return -1; - } - - return 0; + snprintf(executable_path, sizeof(executable_path), "%s", argv[0]); } @@ -203,7 +197,7 @@ static void* dowait(void* data) { process_info_t* p; for (i = 0; i < args->n; i++) { - p = (process_info_t*)(args->vec + i * sizeof(process_info_t)); + p = &args->vec[i]; if (p->terminated) continue; r = waitpid(p->pid, &p->status, 0); if (r < 0) { @@ -329,7 +323,7 @@ int process_wait(process_info_t* vec, int n, int timeout) { } else { /* Timeout. Kill all the children. */ for (i = 0; i < n; i++) { - p = (process_info_t*)(vec + i * sizeof(process_info_t)); + p = &vec[i]; kill(p->pid, SIGTERM); } retval = -2; @@ -339,8 +333,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; } diff --git a/test/runner-win.c b/test/runner-win.c index e69b744..8c2a00b 100644 --- a/test/runner-win.c +++ b/test/runner-win.c @@ -43,7 +43,7 @@ /* Do platform-specific initialization. */ -int platform_init(int argc, char **argv) { +void platform_init(int argc, char **argv) { /* Disable the "application crashed" popup. */ SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); @@ -67,8 +67,6 @@ int platform_init(int argc, char **argv) { setvbuf(stderr, NULL, _IONBF, 0); strcpy(executable_path, argv[0]); - - return 0; } diff --git a/test/runner.c b/test/runner.c index bb50b43..7891082 100644 --- a/test/runner.c +++ b/test/runner.c @@ -98,8 +98,8 @@ int run_tests(int benchmark_output) { skip = (actual > 0 && 0 == strcmp(TASKS[0].task_name, "platform_output")); qsort(TASKS + skip, actual - skip, sizeof(TASKS[0]), compare_task); - fprintf(stderr, "1..%d\n", total); - fflush(stderr); + fprintf(stdout, "1..%d\n", total); + fflush(stdout); /* Run all tests. */ passed = 0; @@ -156,8 +156,8 @@ void log_tap_result(int test_count, reason[0] = '\0'; } - fprintf(stderr, "%s %d - %s%s%s\n", result, test_count, test, directive, reason); - fflush(stderr); + fprintf(stdout, "%s %d - %s%s%s\n", result, test_count, test, directive, reason); + fflush(stdout); } @@ -307,28 +307,28 @@ out: /* Show error and output from processes if the test failed. */ if ((status != TEST_OK && status != TEST_SKIP) || task->show_output) { if (strlen(errmsg) > 0) - fprintf(stderr, "# %s\n", errmsg); - fprintf(stderr, "# "); - fflush(stderr); + fprintf(stdout, "# %s\n", errmsg); + fprintf(stdout, "# "); + fflush(stdout); for (i = 0; i < process_count; i++) { switch (process_output_size(&processes[i])) { case -1: - fprintf(stderr, "Output from process `%s`: (unavailable)\n", + fprintf(stdout, "Output from process `%s`: (unavailable)\n", process_get_name(&processes[i])); - fflush(stderr); + fflush(stdout); break; case 0: - fprintf(stderr, "Output from process `%s`: (no output)\n", + fprintf(stdout, "Output from process `%s`: (no output)\n", process_get_name(&processes[i])); - fflush(stderr); + fflush(stdout); break; default: - fprintf(stderr, "Output from process `%s`:\n", process_get_name(&processes[i])); - fflush(stderr); - process_copy_output(&processes[i], stderr); + fprintf(stdout, "Output from process `%s`:\n", process_get_name(&processes[i])); + fflush(stdout); + process_copy_output(&processes[i], stdout); break; } } @@ -337,18 +337,18 @@ out: } else if (benchmark_output) { switch (process_output_size(main_proc)) { case -1: - fprintf(stderr, "%s: (unavailable)\n", test); - fflush(stderr); + fprintf(stdout, "%s: (unavailable)\n", test); + fflush(stdout); break; case 0: - fprintf(stderr, "%s: (no output)\n", test); - fflush(stderr); + fprintf(stdout, "%s: (no output)\n", test); + fflush(stdout); break; default: for (i = 0; i < process_count; i++) { - process_copy_output(&processes[i], stderr); + process_copy_output(&processes[i], stdout); } break; } @@ -378,8 +378,8 @@ int run_test_part(const char* test, const char* part) { } } - fprintf(stderr, "No test part with that name: %s:%s\n", test, part); - fflush(stderr); + fprintf(stdout, "No test part with that name: %s:%s\n", test, part); + fflush(stdout); return 255; } diff --git a/test/runner.h b/test/runner.h index 1a33950..6801564 100644 --- a/test/runner.h +++ b/test/runner.h @@ -84,11 +84,7 @@ typedef struct { #define TEST_HELPER HELPER_ENTRY #define BENCHMARK_HELPER HELPER_ENTRY -#ifdef PATH_MAX -extern char executable_path[PATH_MAX]; -#else extern char executable_path[4096]; -#endif /* * Include platform-dependent definitions @@ -136,7 +132,7 @@ void print_lines(const char* buffer, size_t size, FILE* stream); */ /* Do platform-specific initialization. */ -int platform_init(int argc, char** argv); +void platform_init(int argc, char** argv); /* Invoke "argv[0] test-name [test-part]". Store process info in *p. Make sure * that all stdio output of the processes is buffered up. */ diff --git a/test/task.h b/test/task.h index 13105d0..925f1b1 100644 --- a/test/task.h +++ b/test/task.h @@ -27,6 +27,8 @@ #include #include #include +#include +#include #if defined(_MSC_VER) && _MSC_VER < 1600 # include "uv/stdint-msvc2008.h" @@ -50,6 +52,7 @@ #define TEST_PORT 9123 #define TEST_PORT_2 9124 +#define TEST_PORT_3 9125 #ifdef _WIN32 # define TEST_PIPENAME "\\\\?\\pipe\\uv-test" @@ -109,6 +112,142 @@ typedef enum { } \ } while (0) +#define ASSERT_BASE(a, operator, b, type, conv) \ + do { \ + volatile type eval_a = (type) (a); \ + volatile type eval_b = (type) (b); \ + if (!(eval_a operator eval_b)) { \ + fprintf(stderr, \ + "Assertion failed in %s on line %d: `%s %s %s` " \ + "(%"conv" %s %"conv")\n", \ + __FILE__, \ + __LINE__, \ + #a, \ + #operator, \ + #b, \ + eval_a, \ + #operator, \ + eval_b); \ + abort(); \ + } \ + } while (0) + +#define ASSERT_BASE_STR(expr, a, operator, b, type, conv) \ + do { \ + if (!(expr)) { \ + fprintf(stderr, \ + "Assertion failed in %s on line %d: `%s %s %s` " \ + "(%"conv" %s %"conv")\n", \ + __FILE__, \ + __LINE__, \ + #a, \ + #operator, \ + #b, \ + (type)a, \ + #operator, \ + (type)b); \ + abort(); \ + } \ + } while (0) + +#define ASSERT_BASE_LEN(expr, a, operator, b, conv, len) \ + do { \ + if (!(expr)) { \ + fprintf(stderr, \ + "Assertion failed in %s on line %d: `%s %s %s` " \ + "(%.*"#conv" %s %.*"#conv")\n", \ + __FILE__, \ + __LINE__, \ + #a, \ + #operator, \ + #b, \ + (int)len, \ + a, \ + #operator, \ + (int)len, \ + b); \ + abort(); \ + } \ + } while (0) + +#define ASSERT_BASE_HEX(expr, a, operator, b, size) \ + do { \ + if (!(expr)) { \ + int i; \ + unsigned char* a_ = (unsigned char*)a; \ + unsigned char* b_ = (unsigned char*)b; \ + fprintf(stderr, \ + "Assertion failed in %s on line %d: `%s %s %s` (", \ + __FILE__, \ + __LINE__, \ + #a, \ + #operator, \ + #b); \ + for (i = 0; i < size; ++i) { \ + if (i > 0) fprintf(stderr, ":"); \ + fprintf(stderr, "%02X", a_[i]); \ + } \ + fprintf(stderr, " %s ", #operator); \ + for (i = 0; i < size; ++i) { \ + if (i > 0) fprintf(stderr, ":"); \ + fprintf(stderr, "%02X", b_[i]); \ + } \ + fprintf(stderr, ")\n"); \ + abort(); \ + } \ + } while (0) + +#define ASSERT_EQ(a, b) ASSERT_BASE(a, ==, b, int64_t, PRId64) +#define ASSERT_GE(a, b) ASSERT_BASE(a, >=, b, int64_t, PRId64) +#define ASSERT_GT(a, b) ASSERT_BASE(a, >, b, int64_t, PRId64) +#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_UINT64_EQ(a, b) ASSERT_BASE(a, ==, b, uint64_t, PRIu64) +#define ASSERT_UINT64_GE(a, b) ASSERT_BASE(a, >=, b, uint64_t, PRIu64) +#define ASSERT_UINT64_GT(a, b) ASSERT_BASE(a, >, b, uint64_t, PRIu64) +#define ASSERT_UINT64_LE(a, b) ASSERT_BASE(a, <=, b, uint64_t, PRIu64) +#define ASSERT_UINT64_LT(a, b) ASSERT_BASE(a, <, b, uint64_t, PRIu64) +#define ASSERT_UINT64_NE(a, b) ASSERT_BASE(a, !=, b, uint64_t, PRIu64) + +#define ASSERT_DOUBLE_EQ(a, b) ASSERT_BASE(a, ==, b, double, "f") +#define ASSERT_DOUBLE_GE(a, b) ASSERT_BASE(a, >=, b, double, "f") +#define ASSERT_DOUBLE_GT(a, b) ASSERT_BASE(a, >, b, double, "f") +#define ASSERT_DOUBLE_LE(a, b) ASSERT_BASE(a, <=, b, double, "f") +#define ASSERT_DOUBLE_LT(a, b) ASSERT_BASE(a, <, b, double, "f") +#define ASSERT_DOUBLE_NE(a, b) ASSERT_BASE(a, !=, b, double, "f") + +#define ASSERT_STR_EQ(a, b) \ + ASSERT_BASE_STR(strcmp(a, b) == 0, a, == , b, char*, "s") + +#define ASSERT_STR_NE(a, b) \ + ASSERT_BASE_STR(strcmp(a, b) != 0, a, !=, b, char*, "s") + +#define ASSERT_MEM_EQ(a, b, size) \ + ASSERT_BASE_LEN(memcmp(a, b, size) == 0, a, ==, b, s, size) + +#define ASSERT_MEM_NE(a, b, size) \ + ASSERT_BASE_LEN(memcmp(a, b, size) != 0, a, !=, b, s, size) + +#define ASSERT_MEM_HEX_EQ(a, b, size) \ + ASSERT_BASE_HEX(memcmp(a, b, size) == 0, a, ==, b, size) + +#define ASSERT_MEM_HEX_NE(a, b, size) \ + ASSERT_BASE_HEX(memcmp(a, b, size) != 0, a, !=, b, size) + +#define ASSERT_NULL(a) \ + ASSERT_BASE(a, ==, NULL, void*, "p") + +#define ASSERT_NOT_NULL(a) \ + ASSERT_BASE(a, !=, NULL, void*, "p") + +#define ASSERT_PTR_EQ(a, b) \ + ASSERT_BASE(a, ==, b, void*, "p") + +#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. */ @@ -116,6 +255,7 @@ typedef enum { do { \ close_loop(uv_default_loop()); \ ASSERT(0 == uv_loop_close(uv_default_loop())); \ + uv_library_shutdown(); \ } while (0) /* Just sugar for wrapping the main() for a task or helper. */ @@ -137,7 +277,7 @@ const char* fmt(double d); /* Reserved test exit codes. */ enum test_status { TEST_OK = 0, - TEST_SKIP + TEST_SKIP = 7 }; #define RETURN_OK() \ diff --git a/test/test-active.c b/test/test-active.c index b17bd17..3843895 100644 --- a/test/test-active.c +++ b/test/test-active.c @@ -30,7 +30,7 @@ static int close_cb_called = 0; static void close_cb(uv_handle_t* handle) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); close_cb_called++; } diff --git a/test/test-async.c b/test/test-async.c index 6f5351b..619be62 100644 --- a/test/test-async.c +++ b/test/test-async.c @@ -70,7 +70,7 @@ static void thread_cb(void *arg) { static void close_cb(uv_handle_t* handle) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); close_cb_called++; } diff --git a/test/test-callback-stack.c b/test/test-callback-stack.c index 1871e7e..a5195c7 100644 --- a/test/test-callback-stack.c +++ b/test/test-callback-stack.c @@ -48,7 +48,7 @@ static int shutdown_cb_called = 0; static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { buf->len = size; buf->base = malloc(size); - ASSERT(buf->base != NULL); + ASSERT_NOT_NULL(buf->base); } diff --git a/test/test-close-fd.c b/test/test-close-fd.c index 2ed9a10..0d3927f 100644 --- a/test/test-close-fd.c +++ b/test/test-close-fd.c @@ -19,22 +19,21 @@ * IN THE SOFTWARE. */ -#if !defined(_WIN32) - #include "uv.h" #include "task.h" -#include +#ifndef _WIN32 #include +#endif static unsigned int read_cb_called; -static void alloc_cb(uv_handle_t *handle, size_t size, uv_buf_t *buf) { +static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { static char slab[1]; buf->base = slab; buf->len = sizeof(slab); } -static void read_cb(uv_stream_t *handle, ssize_t nread, const 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); @@ -51,14 +50,25 @@ static void read_cb(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) { TEST_IMPL(close_fd) { uv_pipe_t pipe_handle; - int fd[2]; + uv_fs_t req; + uv_buf_t bufs[1]; + uv_file fd[2]; + bufs[0] = uv_buf_init("", 1); - ASSERT(0 == pipe(fd)); + 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])); - fd[0] = -1; /* uv_pipe_open() takes ownership of the file descriptor. */ - ASSERT(1 == write(fd[1], "", 1)); + /* 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); + uv_fs_req_cleanup(&req); +#ifdef _WIN32 + ASSERT(0 == _close(fd[1])); +#else ASSERT(0 == 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)); @@ -72,9 +82,3 @@ TEST_IMPL(close_fd) { MAKE_VALGRIND_HAPPY(); return 0; } - -#else - -typedef int file_has_no_tests; /* ISO C forbids an empty translation unit. */ - -#endif /* !_WIN32 */ diff --git a/test/test-close-order.c b/test/test-close-order.c index 2b24f6d..c2fd6c3 100644 --- a/test/test-close-order.c +++ b/test/test-close-order.c @@ -32,7 +32,7 @@ static uv_timer_t timer_handle2; static void close_cb(uv_handle_t* handle) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); close_cb_called++; } diff --git a/test/test-connection-fail.c b/test/test-connection-fail.c index 8338cac..5904810 100644 --- a/test/test-connection-fail.c +++ b/test/test-connection-fail.c @@ -120,6 +120,11 @@ static void connection_fail(uv_connect_cb connect_cb) { * expect an error. */ TEST_IMPL(connection_fail) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + connection_fail(on_connect_with_close); ASSERT(timer_close_cb_calls == 0); @@ -136,6 +141,11 @@ TEST_IMPL(connection_fail) { * attempt. */ TEST_IMPL(connection_fail_doesnt_auto_close) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + int r; r = uv_timer_init(uv_default_loop(), &timer); diff --git a/test/test-cwd-and-chdir.c b/test/test-cwd-and-chdir.c index 5d43524..faeed02 100644 --- a/test/test-cwd-and-chdir.c +++ b/test/test-cwd-and-chdir.c @@ -23,8 +23,7 @@ #include "task.h" #include -#define PATHMAX 1024 -extern char executable_path[]; +#define PATHMAX 4096 TEST_IMPL(cwd_and_chdir) { char buffer_orig[PATHMAX]; diff --git a/test/test-default-loop-close.c b/test/test-default-loop-close.c index fd11cfa..51e1e7d 100644 --- a/test/test-default-loop-close.c +++ b/test/test-default-loop-close.c @@ -37,7 +37,7 @@ TEST_IMPL(default_loop_close) { uv_timer_t timer_handle; loop = uv_default_loop(); - ASSERT(loop != NULL); + ASSERT_NOT_NULL(loop); ASSERT(0 == uv_timer_init(loop, &timer_handle)); ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 1, 0)); @@ -46,7 +46,7 @@ TEST_IMPL(default_loop_close) { ASSERT(0 == uv_loop_close(loop)); loop = uv_default_loop(); - ASSERT(loop != NULL); + ASSERT_NOT_NULL(loop); ASSERT(0 == uv_timer_init(loop, &timer_handle)); ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 1, 0)); diff --git a/test/test-delayed-accept.c b/test/test-delayed-accept.c index 513e69b..88b31e2 100644 --- a/test/test-delayed-accept.c +++ b/test/test-delayed-accept.c @@ -37,7 +37,7 @@ static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { static void close_cb(uv_handle_t* handle) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); free(handle); @@ -50,8 +50,8 @@ static void do_accept(uv_timer_t* timer_handle) { uv_tcp_t* accepted_handle = (uv_tcp_t*)malloc(sizeof *accepted_handle); int r; - ASSERT(timer_handle != NULL); - ASSERT(accepted_handle != NULL); + ASSERT_NOT_NULL(timer_handle); + ASSERT_NOT_NULL(accepted_handle); r = uv_tcp_init(uv_default_loop(), accepted_handle); ASSERT(r == 0); @@ -82,7 +82,7 @@ static void connection_cb(uv_stream_t* tcp, int status) { ASSERT(status == 0); timer_handle = (uv_timer_t*)malloc(sizeof *timer_handle); - ASSERT(timer_handle != NULL); + ASSERT_NOT_NULL(timer_handle); /* Accept the client after 1 second */ r = uv_timer_init(uv_default_loop(), timer_handle); @@ -103,7 +103,7 @@ static void start_server(void) { int r; ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); - ASSERT(server != NULL); + ASSERT_NOT_NULL(server); r = uv_tcp_init(uv_default_loop(), server); ASSERT(r == 0); @@ -125,7 +125,7 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { if (nread >= 0) { ASSERT(nread == 0); } else { - ASSERT(tcp != NULL); + ASSERT_NOT_NULL(tcp); ASSERT(nread == UV_EOF); uv_close((uv_handle_t*)tcp, close_cb); } @@ -135,7 +135,7 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { static void connect_cb(uv_connect_t* req, int status) { int r; - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status == 0); /* Not that the server will send anything, but otherwise we'll never know @@ -156,8 +156,8 @@ static void client_connect(void) { int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); - ASSERT(client != NULL); - ASSERT(connect_req != NULL); + ASSERT_NOT_NULL(client); + ASSERT_NOT_NULL(connect_req); r = uv_tcp_init(uv_default_loop(), client); ASSERT(r == 0); diff --git a/test/test-dlerror.c b/test/test-dlerror.c index 70cc9bf..a436ec0 100644 --- a/test/test-dlerror.c +++ b/test/test-dlerror.c @@ -34,26 +34,26 @@ TEST_IMPL(dlerror) { lib.errmsg = NULL; lib.handle = NULL; msg = uv_dlerror(&lib); - ASSERT(msg != NULL); - ASSERT(strstr(msg, dlerror_no_error) != NULL); + ASSERT_NOT_NULL(msg); + ASSERT_NOT_NULL(strstr(msg, dlerror_no_error)); r = uv_dlopen(path, &lib); ASSERT(r == -1); msg = uv_dlerror(&lib); - ASSERT(msg != NULL); -#ifndef __OpenBSD__ - ASSERT(strstr(msg, path) != NULL); + ASSERT_NOT_NULL(msg); +#if !defined(__OpenBSD__) && !defined(__QNX__) + ASSERT_NOT_NULL(strstr(msg, path)); #endif - ASSERT(strstr(msg, dlerror_no_error) == NULL); + ASSERT_NULL(strstr(msg, dlerror_no_error)); /* Should return the same error twice in a row. */ msg = uv_dlerror(&lib); - ASSERT(msg != NULL); -#ifndef __OpenBSD__ - ASSERT(strstr(msg, path) != NULL); + ASSERT_NOT_NULL(msg); +#if !defined(__OpenBSD__) && !defined(__QNX__) + ASSERT_NOT_NULL(strstr(msg, path)); #endif - ASSERT(strstr(msg, dlerror_no_error) == NULL); + ASSERT_NULL(strstr(msg, dlerror_no_error)); uv_dlclose(&lib); diff --git a/test/test-env-vars.c b/test/test-env-vars.c index f61c1c6..ecaba33 100644 --- a/test/test-env-vars.c +++ b/test/test-env-vars.c @@ -142,5 +142,29 @@ TEST_IMPL(env_vars) { r = uv_os_unsetenv(name2); ASSERT(r == 0); + for (i = 1; i <= 4; i++) { + size_t n; + char* p; + + n = i * 32768; + size = n + 1; + + p = malloc(size); + ASSERT_NOT_NULL(p); + + 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_EQ(n, size); + + for (n = 0; n < size; n++) + ASSERT_EQ('x', p[n]); + + ASSERT_EQ(0, uv_os_unsetenv(name)); + free(p); + } + return 0; } diff --git a/test/test-error.c b/test/test-error.c index 7f44f4a..f0fb864 100644 --- a/test/test-error.c +++ b/test/test-error.c @@ -37,6 +37,9 @@ * See https://github.com/joyent/libuv/issues/210 */ TEST_IMPL(error_message) { +#if defined(__ASAN__) + RETURN_SKIP("Test does not currently work in ASAN"); +#endif char buf[32]; /* Cop out. Can't do proper checks on systems with @@ -47,13 +50,13 @@ TEST_IMPL(error_message) { return 0; } - ASSERT(strstr(uv_strerror(UV_EINVAL), "Success") == NULL); + 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(strstr(uv_strerror_r(UV_EINVAL, buf, sizeof(buf)), "Success") == NULL); - ASSERT(strstr(uv_strerror_r(1337, buf, sizeof(buf)), "1337") != NULL); - ASSERT(strstr(uv_strerror_r(-1337, buf, sizeof(buf)), "-1337") != NULL); + ASSERT_NULL(strstr(uv_strerror_r(UV_EINVAL, buf, sizeof(buf)), "Success")); + ASSERT_NOT_NULL(strstr(uv_strerror_r(1337, buf, sizeof(buf)), "1337")); + ASSERT_NOT_NULL(strstr(uv_strerror_r(-1337, buf, sizeof(buf)), "-1337")); return 0; } diff --git a/test/test-fs-copyfile.c b/test/test-fs-copyfile.c index 3335c88..c785a4b 100644 --- a/test/test-fs-copyfile.c +++ b/test/test-fs-copyfile.c @@ -25,7 +25,7 @@ #if defined(__unix__) || defined(__POSIX__) || \ defined(__APPLE__) || defined(__sun) || \ defined(_AIX) || defined(__MVS__) || \ - defined(__HAIKU__) + defined(__HAIKU__) || defined(__QNX__) #include /* unlink, etc. */ #else # include @@ -125,6 +125,11 @@ TEST_IMPL(fs_copyfile) { r = uv_fs_copyfile(NULL, &req, src, src, 0, NULL); ASSERT(r == 0); 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); + uv_fs_req_cleanup(&req); unlink(src); /* Copies file synchronously. Creates new file. */ diff --git a/test/test-fs-event.c b/test/test-fs-event.c index 28a6a1e..cbe6319 100644 --- a/test/test-fs-event.c +++ b/test/test-fs-event.c @@ -118,7 +118,7 @@ static void touch_file(const char* name) { } static void close_cb(uv_handle_t* handle) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); close_cb_called++; } @@ -337,7 +337,7 @@ static void fs_event_cb_file(uv_fs_event_t* handle, const char* filename, static void timer_cb_close_handle(uv_timer_t* timer) { uv_handle_t* handle; - ASSERT(timer != NULL); + ASSERT_NOT_NULL(timer); handle = timer->data; uv_close((uv_handle_t*)timer, NULL); @@ -380,7 +380,7 @@ static void timer_cb_file(uv_timer_t* handle) { static void timer_cb_touch(uv_timer_t* timer) { uv_close((uv_handle_t*)timer, NULL); - touch_file("watch_file"); + touch_file((char*) timer->data); timer_cb_touch_called++; } @@ -727,6 +727,7 @@ TEST_IMPL(fs_event_watch_file_current_dir) { r = uv_timer_init(loop, &timer); ASSERT(r == 0); + timer.data = "watch_file"; r = uv_timer_start(&timer, timer_cb_touch, 1100, 0); ASSERT(r == 0); @@ -755,7 +756,7 @@ TEST_IMPL(fs_event_watch_file_root_dir) { const char* sys_drive = getenv("SystemDrive"); char path[] = "\\\\?\\X:\\bootsect.bak"; - ASSERT(sys_drive != NULL); + ASSERT_NOT_NULL(sys_drive); strncpy(path + sizeof("\\\\?\\") - 1, sys_drive, 1); loop = uv_default_loop(); @@ -1069,7 +1070,7 @@ static void timer_cb_nop(uv_timer_t* handle) { } static void fs_event_error_report_close_cb(uv_handle_t* handle) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); close_cb_called++; /* handle is allocated on-stack, no need to free it */ @@ -1171,3 +1172,49 @@ TEST_IMPL(fs_event_watch_invalid_path) { MAKE_VALGRIND_HAPPY(); return 0; } + +static int fs_event_cb_stop_calls; + +static void fs_event_cb_stop(uv_fs_event_t* handle, const char* path, + int events, int status) { + uv_fs_event_stop(handle); + fs_event_cb_stop_calls++; +} + +TEST_IMPL(fs_event_stop_in_cb) { + uv_fs_event_t fs; + uv_timer_t timer; + char path[] = "fs_event_stop_in_cb.txt"; + +#if defined(NO_FS_EVENTS) + RETURN_SKIP(NO_FS_EVENTS); +#endif + + remove(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)); + + /* 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_EQ(0, fs_event_cb_stop_calls); + ASSERT_EQ(0, timer_cb_touch_called); + + ASSERT_EQ(0, 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_EQ(1, fs_event_cb_stop_calls); + + remove(path); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/test/test-fs-open-flags.c b/test/test-fs-open-flags.c index fcef2fb..372afe1 100644 --- a/test/test-fs-open-flags.c +++ b/test/test-fs-open-flags.c @@ -58,7 +58,7 @@ static char empty_file[FILE_NAME_SIZE]; static char dummy_file[FILE_NAME_SIZE]; static char empty_dir[] = "empty_dir"; -static void setup() { +static void setup(void) { int r; /* empty_dir */ @@ -73,7 +73,7 @@ static void setup() { uv_fs_req_cleanup(&mkdir_req); } -static void refresh() { +static void refresh(void) { int r; /* absent_file */ @@ -119,7 +119,7 @@ static void refresh() { uv_fs_req_cleanup(&close_req); } -static void cleanup() { +static void cleanup(void) { unlink(absent_file); unlink(empty_file); unlink(dummy_file); @@ -276,21 +276,21 @@ static void fs_open_flags(int add_flags) { /* r */ flags = add_flags | UV_FS_O_RDONLY; openFail(absent_file, UV_ENOENT); - writeFail(empty_file, UV_EPERM); + writeFail(empty_file, UV_EBADF); readExpect(empty_file, "", 0); - writeFail(dummy_file, UV_EPERM); + writeFail(dummy_file, UV_EBADF); readExpect(dummy_file, "a", 1); - writeFail(empty_dir, UV_EPERM); + writeFail(empty_dir, UV_EBADF); readFail(empty_dir, UV_EISDIR); /* rs */ flags = add_flags | UV_FS_O_RDONLY | UV_FS_O_SYNC; openFail(absent_file, UV_ENOENT); - writeFail(empty_file, UV_EPERM); + writeFail(empty_file, UV_EBADF); readExpect(empty_file, "", 0); - writeFail(dummy_file, UV_EPERM); + writeFail(dummy_file, UV_EBADF); readExpect(dummy_file, "a", 1); - writeFail(empty_dir, UV_EPERM); + writeFail(empty_dir, UV_EBADF); readFail(empty_dir, UV_EISDIR); /* r+ */ @@ -316,18 +316,18 @@ static void fs_open_flags(int add_flags) { /* w */ flags = add_flags | UV_FS_O_TRUNC | UV_FS_O_CREAT | UV_FS_O_WRONLY; writeExpect(absent_file, "bc", 2); - readFail(absent_file, UV_EPERM); + readFail(absent_file, UV_EBADF); writeExpect(empty_file, "bc", 2); - readFail(empty_file, UV_EPERM); + readFail(empty_file, UV_EBADF); writeExpect(dummy_file, "bc", 2); - readFail(dummy_file, UV_EPERM); + readFail(dummy_file, UV_EBADF); openFail(empty_dir, UV_EISDIR); /* wx */ flags = add_flags | UV_FS_O_TRUNC | UV_FS_O_CREAT | UV_FS_O_WRONLY | UV_FS_O_EXCL; writeExpect(absent_file, "bc", 2); - readFail(absent_file, UV_EPERM); + readFail(absent_file, UV_EBADF); openFail(empty_file, UV_EEXIST); openFail(dummy_file, UV_EEXIST); openFail(empty_dir, UV_EEXIST); @@ -354,19 +354,19 @@ static void fs_open_flags(int add_flags) { /* a */ flags = add_flags | UV_FS_O_APPEND | UV_FS_O_CREAT | UV_FS_O_WRONLY; writeExpect(absent_file, "bc", 2); - readFail(absent_file, UV_EPERM); + readFail(absent_file, UV_EBADF); writeExpect(empty_file, "bc", 2); - readFail(empty_file, UV_EPERM); + readFail(empty_file, UV_EBADF); writeExpect(dummy_file, "abc", 3); - readFail(dummy_file, UV_EPERM); + readFail(dummy_file, UV_EBADF); writeFail(empty_dir, UV_EISDIR); - readFail(empty_dir, UV_EPERM); + readFail(empty_dir, UV_EBADF); /* ax */ flags = add_flags | UV_FS_O_APPEND | UV_FS_O_CREAT | UV_FS_O_WRONLY | UV_FS_O_EXCL; writeExpect(absent_file, "bc", 2); - readFail(absent_file, UV_EPERM); + readFail(absent_file, UV_EBADF); openFail(empty_file, UV_EEXIST); openFail(dummy_file, UV_EEXIST); openFail(empty_dir, UV_EEXIST); @@ -375,13 +375,13 @@ static void fs_open_flags(int add_flags) { flags = add_flags | UV_FS_O_APPEND | UV_FS_O_CREAT | UV_FS_O_WRONLY | UV_FS_O_SYNC; writeExpect(absent_file, "bc", 2); - readFail(absent_file, UV_EPERM); + readFail(absent_file, UV_EBADF); writeExpect(empty_file, "bc", 2); - readFail(empty_file, UV_EPERM); + readFail(empty_file, UV_EBADF); writeExpect(dummy_file, "abc", 3); - readFail(dummy_file, UV_EPERM); + readFail(dummy_file, UV_EBADF); writeFail(empty_dir, UV_EISDIR); - readFail(empty_dir, UV_EPERM); + readFail(empty_dir, UV_EBADF); /* a+ */ flags = add_flags | UV_FS_O_APPEND | UV_FS_O_CREAT | UV_FS_O_RDWR; diff --git a/test/test-fs-poll.c b/test/test-fs-poll.c index 9dfd5fd..76fe6fc 100644 --- a/test/test-fs-poll.c +++ b/test/test-fs-poll.c @@ -105,8 +105,8 @@ static void poll_cb(uv_fs_poll_t* handle, ASSERT(handle == &poll_handle); ASSERT(1 == uv_is_active((uv_handle_t*) handle)); - ASSERT(prev != NULL); - ASSERT(curr != NULL); + ASSERT_NOT_NULL(prev); + ASSERT_NOT_NULL(curr); switch (poll_cb_called++) { case 0: diff --git a/test/test-fs-readdir.c b/test/test-fs-readdir.c index 5efc853..6bb6917 100644 --- a/test/test-fs-readdir.c +++ b/test/test-fs-readdir.c @@ -77,7 +77,7 @@ static void empty_opendir_cb(uv_fs_t* req) { ASSERT(req == &opendir_req); ASSERT(req->fs_type == UV_FS_OPENDIR); ASSERT(req->result == 0); - ASSERT(req->ptr != NULL); + ASSERT_NOT_NULL(req->ptr); dir = req->ptr; dir->dirents = dirents; dir->nentries = ARRAY_SIZE(dirents); @@ -118,7 +118,7 @@ TEST_IMPL(fs_readdir_empty_dir) { ASSERT(r == 0); ASSERT(opendir_req.fs_type == UV_FS_OPENDIR); ASSERT(opendir_req.result == 0); - ASSERT(opendir_req.ptr != NULL); + ASSERT_NOT_NULL(opendir_req.ptr); dir = opendir_req.ptr; uv_fs_req_cleanup(&opendir_req); @@ -171,7 +171,7 @@ 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(req->ptr == NULL); + ASSERT_NULL(req->ptr); uv_fs_req_cleanup(req); ++non_existing_opendir_cb_count; @@ -191,7 +191,7 @@ TEST_IMPL(fs_readdir_non_existing_dir) { ASSERT(r == UV_ENOENT); ASSERT(opendir_req.fs_type == UV_FS_OPENDIR); ASSERT(opendir_req.result == UV_ENOENT); - ASSERT(opendir_req.ptr == NULL); + ASSERT_NULL(opendir_req.ptr); uv_fs_req_cleanup(&opendir_req); /* Fill the req to ensure that required fields are cleaned up. */ @@ -223,7 +223,7 @@ 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(req->ptr == NULL); + ASSERT_NULL(req->ptr); uv_fs_req_cleanup(req); ++file_opendir_cb_count; @@ -244,7 +244,7 @@ TEST_IMPL(fs_readdir_file) { ASSERT(r == UV_ENOTDIR); ASSERT(opendir_req.fs_type == UV_FS_OPENDIR); ASSERT(opendir_req.result == UV_ENOTDIR); - ASSERT(opendir_req.ptr == NULL); + ASSERT_NULL(opendir_req.ptr); uv_fs_req_cleanup(&opendir_req); @@ -326,7 +326,7 @@ static void non_empty_opendir_cb(uv_fs_t* req) { ASSERT(req == &opendir_req); ASSERT(req->fs_type == UV_FS_OPENDIR); ASSERT(req->result == 0); - ASSERT(req->ptr != NULL); + ASSERT_NOT_NULL(req->ptr); dir = req->ptr; dir->dirents = dirents; @@ -400,7 +400,7 @@ TEST_IMPL(fs_readdir_non_empty_dir) { ASSERT(r == 0); ASSERT(opendir_req.fs_type == UV_FS_OPENDIR); ASSERT(opendir_req.result == 0); - ASSERT(opendir_req.ptr != NULL); + ASSERT_NOT_NULL(opendir_req.ptr); entries_count = 0; dir = opendir_req.ptr; diff --git a/test/test-fs.c b/test/test-fs.c index b6b2b19..c879f62 100644 --- a/test/test-fs.c +++ b/test/test-fs.c @@ -28,12 +28,8 @@ #include #include /* INT_MAX, PATH_MAX, IOV_MAX */ -/* FIXME we shouldn't need to branch in this file */ -#if defined(__unix__) || defined(__POSIX__) || \ - defined(__APPLE__) || defined(__sun) || \ - defined(_AIX) || defined(__MVS__) || \ - defined(__HAIKU__) -#include /* unlink, rmdir, etc. */ +#ifndef _WIN32 +# include /* unlink, rmdir, etc. */ #else # include # include @@ -55,7 +51,7 @@ #endif #define TOO_LONG_NAME_LENGTH 65536 -#define PATHMAX 1024 +#define PATHMAX 4096 typedef struct { const char* path; @@ -95,6 +91,7 @@ static int readlink_cb_count; static int realpath_cb_count; static int utime_cb_count; static int futime_cb_count; +static int lutime_cb_count; static int statfs_cb_count; static uv_loop_t* loop; @@ -314,7 +311,7 @@ static void chown_root_cb(uv_fs_t* req) { * User may grant qsecofr's privileges, including changing * the file's ownership to uid 0. */ - ASSERT(req->result == 0); + ASSERT(req->result == 0 || req->result == UV_EPERM); # else ASSERT(req->result == UV_EPERM); # endif @@ -346,7 +343,7 @@ static void statfs_cb(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_STATFS); ASSERT(req->result == 0); - ASSERT(req->ptr != NULL); + ASSERT_NOT_NULL(req->ptr); stats = req->ptr; #if defined(_WIN32) || defined(__sun) || defined(_AIX) || defined(__MVS__) || \ @@ -369,7 +366,7 @@ static void statfs_cb(uv_fs_t* req) { ASSERT(stats->f_ffree <= stats->f_files); #endif uv_fs_req_cleanup(req); - ASSERT(req->ptr == NULL); + ASSERT_NULL(req->ptr); statfs_cb_count++; } @@ -633,7 +630,7 @@ static void empty_scandir_cb(uv_fs_t* req) { ASSERT(req == &scandir_req); ASSERT(req->fs_type == UV_FS_SCANDIR); ASSERT(req->result == 0); - ASSERT(req->ptr == NULL); + ASSERT_NULL(req->ptr); ASSERT(UV_EOF == uv_fs_scandir_next(req, &dent)); uv_fs_req_cleanup(req); scandir_cb_count++; @@ -645,7 +642,7 @@ static void non_existent_scandir_cb(uv_fs_t* req) { ASSERT(req == &scandir_req); ASSERT(req->fs_type == UV_FS_SCANDIR); ASSERT(req->result == UV_ENOENT); - ASSERT(req->ptr == NULL); + ASSERT_NULL(req->ptr); ASSERT(UV_ENOENT == uv_fs_scandir_next(req, &dent)); uv_fs_req_cleanup(req); scandir_cb_count++; @@ -656,7 +653,7 @@ 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(req->ptr == NULL); + ASSERT_NULL(req->ptr); uv_fs_req_cleanup(req); scandir_cb_count++; } @@ -676,7 +673,7 @@ 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 == 65546); + ASSERT(req->result == 65545); sendfile_cb_count++; uv_fs_req_cleanup(req); } @@ -806,19 +803,62 @@ TEST_IMPL(fs_file_loop) { return 0; } -static void check_utime(const char* path, double atime, double mtime) { +static void check_utime(const char* path, + double atime, + double mtime, + int test_lutime) { uv_stat_t* s; uv_fs_t req; int r; - r = uv_fs_stat(loop, &req, path, NULL); - ASSERT(r == 0); + if (test_lutime) + r = uv_fs_lstat(loop, &req, path, NULL); + else + r = uv_fs_stat(loop, &req, path, NULL); - ASSERT(req.result == 0); + ASSERT_EQ(r, 0); + + ASSERT_EQ(req.result, 0); s = &req.statbuf; - ASSERT(s->st_atim.tv_sec + (s->st_atim.tv_nsec / 1000000000.0) == atime); - ASSERT(s->st_mtim.tv_sec + (s->st_mtim.tv_nsec / 1000000000.0) == mtime); + if (s->st_atim.tv_nsec == 0 && s->st_mtim.tv_nsec == 0) { + /* + * Test sub-second timestamps only when supported (such as Windows with + * NTFS). Some other platforms support sub-second timestamps, but that + * support is filesystem-dependent. Notably OS X (HFS Plus) does NOT + * support sub-second timestamps. But kernels may round or truncate in + * either direction, so we may accept either possible answer. + */ +#ifdef _WIN32 + ASSERT_DOUBLE_EQ(atime, (long) atime); + ASSERT_DOUBLE_EQ(mtime, (long) atime); +#endif + if (atime > 0 || (long) atime == atime) + ASSERT_EQ(s->st_atim.tv_sec, (long) atime); + if (mtime > 0 || (long) mtime == mtime) + ASSERT_EQ(s->st_mtim.tv_sec, (long) mtime); + ASSERT_GE(s->st_atim.tv_sec, (long) atime - 1); + ASSERT_GE(s->st_mtim.tv_sec, (long) mtime - 1); + ASSERT_LE(s->st_atim.tv_sec, (long) atime); + ASSERT_LE(s->st_mtim.tv_sec, (long) mtime); + } else { + double st_atim; + double st_mtim; +#if !defined(__APPLE__) && !defined(__SUNPRO_C) + /* TODO(vtjnash): would it be better to normalize this? */ + ASSERT_DOUBLE_GE(s->st_atim.tv_nsec, 0); + ASSERT_DOUBLE_GE(s->st_mtim.tv_nsec, 0); +#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; + /* + * 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); + } uv_fs_req_cleanup(&req); } @@ -832,7 +872,7 @@ static void utime_cb(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_UTIME); c = req->data; - check_utime(c->path, c->atime, c->mtime); + check_utime(c->path, c->atime, c->mtime, /* test_lutime */ 0); uv_fs_req_cleanup(req); utime_cb_count++; @@ -847,13 +887,27 @@ static void futime_cb(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_FUTIME); c = req->data; - check_utime(c->path, c->atime, c->mtime); + check_utime(c->path, c->atime, c->mtime, /* test_lutime */ 0); uv_fs_req_cleanup(req); futime_cb_count++; } +static void lutime_cb(uv_fs_t* req) { + utime_check_t* c; + + ASSERT(req->result == 0); + ASSERT(req->fs_type == UV_FS_LUTIME); + + c = req->data; + check_utime(c->path, c->atime, c->mtime, /* test_lutime */ 1); + + uv_fs_req_cleanup(req); + lutime_cb_count++; +} + + TEST_IMPL(fs_file_async) { int r; @@ -1141,6 +1195,8 @@ TEST_IMPL(fs_async_dir) { static int test_sendfile(void (*setup)(int), uv_fs_cb cb, off_t expected_size) { int f, r; struct stat s1, s2; + uv_fs_t req; + char buf1[1]; loop = uv_default_loop(); @@ -1170,7 +1226,7 @@ static int test_sendfile(void (*setup)(int), uv_fs_cb cb, off_t expected_size) { uv_fs_req_cleanup(&open_req2); r = uv_fs_sendfile(loop, &sendfile_req, open_req2.result, open_req1.result, - 0, 131072, cb); + 1, 131072, cb); ASSERT(r == 0); uv_run(loop, UV_RUN_DEFAULT); @@ -1185,9 +1241,26 @@ static int test_sendfile(void (*setup)(int), uv_fs_cb cb, off_t expected_size) { ASSERT(0 == stat("test_file", &s1)); ASSERT(0 == stat("test_file2", &s2)); - ASSERT(s1.st_size == s2.st_size); ASSERT(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); + 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_EQ(buf1[0], 'e'); /* 'e' from begin */ + uv_fs_req_cleanup(&req); + } else { + ASSERT_UINT64_EQ(s1.st_size, s2.st_size); + } + /* Cleanup. */ unlink("test_file"); unlink("test_file2"); @@ -1205,7 +1278,7 @@ static void sendfile_setup(int f) { TEST_IMPL(fs_async_sendfile) { - return test_sendfile(sendfile_setup, sendfile_cb, 65546); + return test_sendfile(sendfile_setup, sendfile_cb, 65545); } @@ -1268,7 +1341,10 @@ TEST_IMPL(fs_mkstemp) { ASSERT(strcmp(mkstemp_req1.path, mkstemp_req2.path) != 0); /* invalid template returns EINVAL */ - ASSERT(uv_fs_mkstemp(NULL, &mkstemp_req3, "test_file", NULL) == UV_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)); /* We can write to the opened file */ iov = uv_buf_init(test_buf, sizeof(test_buf)); @@ -1382,7 +1458,8 @@ TEST_IMPL(fs_fstat) { 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); -#elif defined(_AIX) +#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); @@ -1940,12 +2017,12 @@ TEST_IMPL(fs_readlink) { 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(req.ptr == NULL); + ASSERT_NULL(req.ptr); ASSERT(req.result == UV_ENOENT); uv_fs_req_cleanup(&req); ASSERT(UV_ENOENT == uv_fs_readlink(NULL, &req, "no_such_file", NULL)); - ASSERT(req.ptr == NULL); + ASSERT_NULL(req.ptr); ASSERT(req.result == UV_ENOENT); uv_fs_req_cleanup(&req); @@ -1961,7 +2038,7 @@ TEST_IMPL(fs_realpath) { 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(req.ptr == NULL); + ASSERT_NULL(req.ptr); #ifdef _WIN32 /* * Windows XP and Server 2003 don't support GetFinalPathNameByHandleW() @@ -1975,7 +2052,7 @@ TEST_IMPL(fs_realpath) { uv_fs_req_cleanup(&req); ASSERT(UV_ENOENT == uv_fs_realpath(NULL, &req, "no_such_file", NULL)); - ASSERT(req.ptr == NULL); + ASSERT_NULL(req.ptr); ASSERT(req.result == UV_ENOENT); uv_fs_req_cleanup(&req); @@ -2431,6 +2508,57 @@ TEST_IMPL(fs_non_symlink_reparse_point) { MAKE_VALGRIND_HAPPY(); return 0; } + +TEST_IMPL(fs_lstat_windows_store_apps) { + uv_loop_t* loop; + char localappdata[MAX_PATH]; + char windowsapps_path[MAX_PATH]; + char file_path[MAX_PATH]; + size_t len; + int r; + uv_fs_t req; + uv_fs_t stat_req; + uv_dirent_t dirent; + + loop = uv_default_loop(); + ASSERT_NOT_NULL(loop); + len = sizeof(localappdata); + r = uv_os_getenv("LOCALAPPDATA", localappdata, &len); + if (r == UV_ENOENT) { + MAKE_VALGRIND_HAPPY(); + return TEST_SKIP; + } + ASSERT_EQ(r, 0); + r = snprintf(windowsapps_path, + sizeof(localappdata), + "%s\\Microsoft\\WindowsApps", + localappdata); + 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(); + return TEST_SKIP; + } + if (uv_fs_scandir(loop, &req, windowsapps_path, 0, NULL) <= 0) { + MAKE_VALGRIND_HAPPY(); + return TEST_SKIP; + } + while (uv_fs_scandir_next(&req, &dirent) != UV_EOF) { + if (dirent.type != UV_DIRENT_LINK) { + continue; + } + if (snprintf(file_path, + sizeof(file_path), + "%s\\%s", + windowsapps_path, + dirent.name) < 0) { + continue; + } + ASSERT_EQ(uv_fs_lstat(loop, &stat_req, file_path, NULL), 0); + } + MAKE_VALGRIND_HAPPY(); + return 0; +} #endif @@ -2451,29 +2579,16 @@ TEST_IMPL(fs_utime) { uv_fs_req_cleanup(&req); uv_fs_close(loop, &req, r, NULL); - atime = mtime = 400497753; /* 1982-09-10 11:22:33 */ - - /* - * Test sub-second timestamps only on Windows (assuming NTFS). Some other - * platforms support sub-second timestamps, but that support is filesystem- - * dependent. Notably OS X (HFS Plus) does NOT support sub-second timestamps. - */ -#ifdef _WIN32 - mtime += 0.444; /* 1982-09-10 11:22:33.444 */ -#endif + 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); uv_fs_req_cleanup(&req); - r = uv_fs_stat(NULL, &req, path, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); - check_utime(path, atime, mtime); - uv_fs_req_cleanup(&req); + check_utime(path, atime, mtime, /* test_lutime */ 0); - atime = mtime = 1291404900; /* 2010-12-03 20:35:00 - mees <3 */ + atime = mtime = 1291404900.25; /* 2010-12-03 20:35:00.25 - mees <3 */ checkme.path = path; checkme.atime = atime; checkme.mtime = mtime; @@ -2493,6 +2608,45 @@ TEST_IMPL(fs_utime) { } +TEST_IMPL(fs_utime_round) { + const char path[] = "test_file"; + double atime; + double mtime; + uv_fs_t req; + int r; + + loop = uv_default_loop(); + unlink(path); + r = uv_fs_open(NULL, &req, path, O_RDWR | 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)); + + atime = mtime = -14245440.25; /* 1969-07-20T02:56:00.25Z */ + + r = uv_fs_utime(NULL, &req, path, atime, mtime, NULL); +#if !defined(__linux__) && \ + !defined(_WIN32) && \ + !defined(__APPLE__) && \ + !defined(__FreeBSD__) && \ + !defined(__sun) + if (r != 0) { + ASSERT_EQ(r, UV_EINVAL); + 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); + uv_fs_req_cleanup(&req); + check_utime(path, atime, mtime, /* test_lutime */ 0); + unlink(path); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + #ifdef _WIN32 TEST_IMPL(fs_stat_root) { int r; @@ -2546,16 +2700,7 @@ TEST_IMPL(fs_futime) { uv_fs_req_cleanup(&req); uv_fs_close(loop, &req, r, NULL); - atime = mtime = 400497753; /* 1982-09-10 11:22:33 */ - - /* - * Test sub-second timestamps only on Windows (assuming NTFS). Some other - * platforms support sub-second timestamps, but that support is filesystem- - * dependent. Notably OS X (HFS Plus) does NOT support sub-second timestamps. - */ -#ifdef _WIN32 - mtime += 0.444; /* 1982-09-10 11:22:33.444 */ -#endif + 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); @@ -2573,11 +2718,7 @@ TEST_IMPL(fs_futime) { #endif uv_fs_req_cleanup(&req); - r = uv_fs_stat(NULL, &req, path, NULL); - ASSERT(r == 0); - ASSERT(req.result == 0); - check_utime(path, atime, mtime); - uv_fs_req_cleanup(&req); + check_utime(path, atime, mtime, /* test_lutime */ 0); atime = mtime = 1291404900; /* 2010-12-03 20:35:00 - mees <3 */ @@ -2600,6 +2741,80 @@ TEST_IMPL(fs_futime) { } +TEST_IMPL(fs_lutime) { + utime_check_t checkme; + const char* path = "test_file"; + const char* symlink_path = "test_file_symlink"; + double atime; + double mtime; + uv_fs_t req; + int r, s; + + + /* 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); + uv_fs_req_cleanup(&req); + uv_fs_close(loop, &req, r, NULL); + + unlink(symlink_path); + s = uv_fs_symlink(NULL, &req, path, symlink_path, 0, NULL); +#ifdef _WIN32 + if (s == UV_EPERM) { + /* + * Creating a symlink before Windows 10 Creators Update was only allowed + * when running elevated console (with admin rights) + */ + RETURN_SKIP( + "Symlink creation requires elevated console (with admin rights)"); + } +#endif + ASSERT(s == 0); + ASSERT(req.result == 0); + uv_fs_req_cleanup(&req); + + /* Test the synchronous version. */ + atime = mtime = 400497753.25; /* 1982-09-10 11:22:33.25 */ + + checkme.atime = atime; + checkme.mtime = mtime; + checkme.path = symlink_path; + req.data = &checkme; + + r = uv_fs_lutime(NULL, &req, symlink_path, atime, mtime, NULL); +#if (defined(_AIX) && !defined(_AIX71)) || \ + defined(__MVS__) + ASSERT(r == UV_ENOSYS); + RETURN_SKIP("lutime is not implemented for z/OS and AIX versions below 7.1"); +#endif + ASSERT(r == 0); + lutime_cb(&req); + ASSERT(lutime_cb_count == 1); + + /* Test the asynchronous version. */ + atime = mtime = 1291404900; /* 2010-12-03 20:35:00 */ + + checkme.atime = atime; + checkme.mtime = mtime; + checkme.path = symlink_path; + + r = uv_fs_lutime(loop, &req, symlink_path, atime, mtime, lutime_cb); + ASSERT(r == 0); + uv_run(loop, UV_RUN_DEFAULT); + ASSERT(lutime_cb_count == 2); + + /* Cleanup. */ + unlink(path); + unlink(symlink_path); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + TEST_IMPL(fs_stat_missing_path) { uv_fs_t req; int r; @@ -2634,7 +2849,7 @@ TEST_IMPL(fs_scandir_empty_dir) { r = uv_fs_scandir(NULL, &req, path, 0, NULL); ASSERT(r == 0); ASSERT(req.result == 0); - ASSERT(req.ptr == NULL); + ASSERT_NULL(req.ptr); ASSERT(UV_EOF == uv_fs_scandir_next(&req, &dent)); uv_fs_req_cleanup(&req); @@ -2671,7 +2886,7 @@ TEST_IMPL(fs_scandir_non_existent_dir) { r = uv_fs_scandir(NULL, &req, path, 0, NULL); ASSERT(r == UV_ENOENT); ASSERT(req.result == UV_ENOENT); - ASSERT(req.ptr == NULL); + ASSERT_NULL(req.ptr); ASSERT(UV_ENOENT == uv_fs_scandir_next(&req, &dent)); uv_fs_req_cleanup(&req); @@ -2720,7 +2935,7 @@ TEST_IMPL(fs_open_dir) { r = uv_fs_open(NULL, &req, path, O_RDONLY, 0, NULL); ASSERT(r >= 0); ASSERT(req.result >= 0); - ASSERT(req.ptr == NULL); + ASSERT_NULL(req.ptr); file = r; uv_fs_req_cleanup(&req); @@ -3117,7 +3332,7 @@ static void fs_write_alotof_bufs(int add_flags) { loop = uv_default_loop(); iovs = malloc(sizeof(*iovs) * iovcount); - ASSERT(iovs != NULL); + ASSERT_NOT_NULL(iovs); iovmax = uv_test_getiovmax(); r = uv_fs_open(NULL, @@ -3146,7 +3361,7 @@ static void fs_write_alotof_bufs(int add_flags) { /* Read the strings back to separate buffers. */ buffer = malloc(sizeof(test_buf) * iovcount); - ASSERT(buffer != NULL); + ASSERT_NOT_NULL(buffer); for (index = 0; index < iovcount; ++index) iovs[index] = uv_buf_init(buffer + index * sizeof(test_buf), @@ -3229,7 +3444,7 @@ static void fs_write_alotof_bufs_with_offset(int add_flags) { loop = uv_default_loop(); iovs = malloc(sizeof(*iovs) * iovcount); - ASSERT(iovs != NULL); + ASSERT_NOT_NULL(iovs); iovmax = uv_test_getiovmax(); r = uv_fs_open(NULL, @@ -3265,7 +3480,7 @@ static void fs_write_alotof_bufs_with_offset(int add_flags) { /* Read the strings back to separate buffers. */ buffer = malloc(sizeof(test_buf) * iovcount); - ASSERT(buffer != NULL); + ASSERT_NOT_NULL(buffer); for (index = 0; index < iovcount; ++index) iovs[index] = uv_buf_init(buffer + index * sizeof(test_buf), @@ -3461,16 +3676,16 @@ static void test_fs_partial(int doread) { iovcount = 54321; iovs = malloc(sizeof(*iovs) * iovcount); - ASSERT(iovs != NULL); + ASSERT_NOT_NULL(iovs); ctx.pid = pthread_self(); ctx.doread = doread; ctx.interval = 1000; ctx.size = sizeof(test_buf) * iovcount; ctx.data = malloc(ctx.size); - ASSERT(ctx.data != NULL); + ASSERT_NOT_NULL(ctx.data); buffer = malloc(ctx.size); - ASSERT(buffer != NULL); + ASSERT_NOT_NULL(buffer); for (index = 0; index < iovcount; ++index) iovs[index] = uv_buf_init(buffer + index * sizeof(test_buf), sizeof(test_buf)); @@ -3558,15 +3773,15 @@ TEST_IMPL(fs_read_write_null_arguments) { /* Validate some memory management on failed input validation before sending fs work to the thread pool. */ ASSERT(r == UV_EINVAL); - ASSERT(write_req.path == NULL); - ASSERT(write_req.ptr == NULL); + ASSERT_NULL(write_req.path); + ASSERT_NULL(write_req.ptr); #ifdef _WIN32 - ASSERT(write_req.file.pathw == NULL); - ASSERT(write_req.fs.info.new_pathw == NULL); - ASSERT(write_req.fs.info.bufs == NULL); + ASSERT_NULL(write_req.file.pathw); + ASSERT_NULL(write_req.fs.info.new_pathw); + ASSERT_NULL(write_req.fs.info.bufs); #else - ASSERT(write_req.new_path == NULL); - ASSERT(write_req.bufs == NULL); + ASSERT_NULL(write_req.new_path); + ASSERT_NULL(write_req.bufs); #endif uv_fs_req_cleanup(&write_req); @@ -3734,7 +3949,7 @@ TEST_IMPL(fs_file_pos_after_op_with_offset) { } #ifdef _WIN32 -static void fs_file_pos_common() { +static void fs_file_pos_common(void) { int r; iov = uv_buf_init("abc", 3); @@ -4222,6 +4437,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(UV_EINVAL, uv_fs_mkdir(loop, &req, "test:lol", 0, NULL)); return 0; } @@ -4247,3 +4463,21 @@ TEST_IMPL(fs_statfs) { return 0; } + +TEST_IMPL(fs_get_system_error) { + uv_fs_t req; + int r; + int system_error; + + r = uv_fs_statfs(NULL, &req, "non_existing_file", NULL); + ASSERT(r != 0); + + system_error = uv_fs_get_system_error(&req); +#ifdef _WIN32 + ASSERT(system_error == ERROR_FILE_NOT_FOUND); +#else + ASSERT(system_error == ENOENT); +#endif + + return 0; +} diff --git a/test/test-get-currentexe.c b/test/test-get-currentexe.c index 0e9d696..dc239cc 100644 --- a/test/test-get-currentexe.c +++ b/test/test-get-currentexe.c @@ -23,28 +23,34 @@ #include "task.h" #include -#define PATHMAX 1024 +#ifndef _WIN32 +#include +#endif + +#define PATHMAX 4096 extern char executable_path[]; TEST_IMPL(get_currentexe) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + char buffer[PATHMAX]; + char path[PATHMAX]; size_t size; char* match; - char* path; int r; size = sizeof(buffer) / sizeof(buffer[0]); r = uv_exepath(buffer, &size); ASSERT(!r); - /* uv_exepath can return an absolute path on darwin, so if the test runner - * was run with a relative prefix of "./", we need to strip that prefix off - * executable_path or we'll fail. */ - if (executable_path[0] == '.' && executable_path[1] == '/') { - path = executable_path + 2; - } else { - path = executable_path; - } +#ifdef _WIN32 + snprintf(path, sizeof(path), "%s", executable_path); +#else + ASSERT_NOT_NULL(realpath(executable_path, path)); +#endif match = strstr(buffer, path); /* Verify that the path returned from uv_exepath is a subdirectory of @@ -82,5 +88,19 @@ TEST_IMPL(get_currentexe) { ASSERT(buffer[0] != '\0'); ASSERT(buffer[1] == '\0'); + /* Verify uv_exepath is not affected by uv_set_process_title(). */ + r = uv_set_process_title("foobar"); + ASSERT_EQ(r, 0); + size = sizeof(buffer); + r = uv_exepath(buffer, &size); + ASSERT_EQ(r, 0); + + match = strstr(buffer, path); + /* Verify that the path returned from uv_exepath is a subdirectory of + * executable_path. + */ + ASSERT_NOT_NULL(match); + ASSERT_STR_EQ(match, path); + ASSERT_EQ(size, strlen(buffer)); return 0; } diff --git a/test/test-get-passwd.c b/test/test-get-passwd.c index 9b5273b..d2c7431 100644 --- a/test/test-get-passwd.c +++ b/test/test-get-passwd.c @@ -22,8 +22,17 @@ #include "uv.h" #include "task.h" #include +#ifndef _WIN32 +#include +#include +#endif TEST_IMPL(get_passwd) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + uv_passwd_t pwd; size_t len; int r; @@ -35,7 +44,7 @@ TEST_IMPL(get_passwd) { ASSERT(len > 0); #ifdef _WIN32 - ASSERT(pwd.shell == NULL); + ASSERT_NULL(pwd.shell); #else len = strlen(pwd.shell); # ifndef __PASE__ @@ -59,26 +68,30 @@ TEST_IMPL(get_passwd) { #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() */ uv_os_free_passwd(&pwd); - ASSERT(pwd.username == NULL); - ASSERT(pwd.shell == NULL); - ASSERT(pwd.homedir == NULL); + ASSERT_NULL(pwd.username); + ASSERT_NULL(pwd.shell); + ASSERT_NULL(pwd.homedir); /* Test a double free */ uv_os_free_passwd(&pwd); - ASSERT(pwd.username == NULL); - ASSERT(pwd.shell == NULL); - ASSERT(pwd.homedir == NULL); + ASSERT_NULL(pwd.username); + ASSERT_NULL(pwd.shell); + ASSERT_NULL(pwd.homedir); /* Test invalid input */ r = uv_os_get_passwd(NULL); diff --git a/test/test-getaddrinfo.c b/test/test-getaddrinfo.c index 03dc126..d0b6a50 100644 --- a/test/test-getaddrinfo.c +++ b/test/test-getaddrinfo.c @@ -39,9 +39,10 @@ static int fail_cb_called; static void getaddrinfo_fail_cb(uv_getaddrinfo_t* req, int status, struct addrinfo* res) { + ASSERT(fail_cb_called == 0); ASSERT(status < 0); - ASSERT(res == NULL); + ASSERT_NULL(res); uv_freeaddrinfo(res); /* Should not crash. */ fail_cb_called++; } @@ -81,6 +82,11 @@ static void getaddrinfo_cuncurrent_cb(uv_getaddrinfo_t* handle, TEST_IMPL(getaddrinfo_fail) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + uv_getaddrinfo_t req; ASSERT(UV_EINVAL == uv_getaddrinfo(uv_default_loop(), @@ -94,7 +100,7 @@ TEST_IMPL(getaddrinfo_fail) { ASSERT(0 == uv_getaddrinfo(uv_default_loop(), &req, getaddrinfo_fail_cb, - "xyzzy.xyzzy.xyzzy.", + "example.invalid.", NULL, NULL)); ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); @@ -106,13 +112,17 @@ TEST_IMPL(getaddrinfo_fail) { TEST_IMPL(getaddrinfo_fail_sync) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif uv_getaddrinfo_t req; /* Use a FQDN by ending in a period */ ASSERT(0 > uv_getaddrinfo(uv_default_loop(), &req, NULL, - "xyzzy.xyzzy.xyzzy.", + "example.invalid.", NULL, NULL)); uv_freeaddrinfo(req.addrinfo); @@ -123,6 +133,11 @@ TEST_IMPL(getaddrinfo_fail_sync) { TEST_IMPL(getaddrinfo_basic) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + int r; getaddrinfo_handle = (uv_getaddrinfo_t*)malloc(sizeof(uv_getaddrinfo_t)); @@ -144,6 +159,10 @@ TEST_IMPL(getaddrinfo_basic) { TEST_IMPL(getaddrinfo_basic_sync) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif uv_getaddrinfo_t req; ASSERT(0 == uv_getaddrinfo(uv_default_loop(), @@ -160,6 +179,11 @@ TEST_IMPL(getaddrinfo_basic_sync) { TEST_IMPL(getaddrinfo_concurrent) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + int i, r; int* data; @@ -167,7 +191,7 @@ TEST_IMPL(getaddrinfo_concurrent) { callback_counts[i] = 0; data = (int*)malloc(sizeof(int)); - ASSERT(data != NULL); + ASSERT_NOT_NULL(data); *data = i; getaddrinfo_handles[i].data = data; diff --git a/test/test-gethostname.c b/test/test-gethostname.c index ac636f0..1a9816d 100644 --- a/test/test-gethostname.c +++ b/test/test-gethostname.c @@ -51,7 +51,7 @@ TEST_IMPL(gethostname) { size = UV_MAXHOSTNAMESIZE; r = uv_os_gethostname(buf, &size); ASSERT(r == 0); - ASSERT(size > 1 && size == strlen(buf)); + ASSERT(size > 0 && size == strlen(buf)); ASSERT(size + 1 == enobufs_size); return 0; diff --git a/test/test-getnameinfo.c b/test/test-getnameinfo.c index b139161..2bfedd3 100644 --- a/test/test-getnameinfo.c +++ b/test/test-getnameinfo.c @@ -38,14 +38,19 @@ static void getnameinfo_req(uv_getnameinfo_t* handle, int status, const char* hostname, const char* service) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); ASSERT(status == 0); - ASSERT(hostname != NULL); - ASSERT(service != NULL); + ASSERT_NOT_NULL(hostname); + ASSERT_NOT_NULL(service); } TEST_IMPL(getnameinfo_basic_ip4) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + int r; r = uv_ip4_addr(address_ip4, port, &addr4); @@ -66,6 +71,11 @@ TEST_IMPL(getnameinfo_basic_ip4) { TEST_IMPL(getnameinfo_basic_ip4_sync) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + ASSERT(0 == uv_ip4_addr(address_ip4, port, &addr4)); ASSERT(0 == uv_getnameinfo(uv_default_loop(), @@ -82,6 +92,11 @@ TEST_IMPL(getnameinfo_basic_ip4_sync) { TEST_IMPL(getnameinfo_basic_ip6) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + int r; r = uv_ip6_addr(address_ip6, port, &addr6); diff --git a/test/test-getsockname.c b/test/test-getsockname.c index 565c17f..7c77fcb 100644 --- a/test/test-getsockname.c +++ b/test/test-getsockname.c @@ -116,7 +116,7 @@ static void on_connection(uv_stream_t* server, int status) { ASSERT(status == 0); handle = malloc(sizeof(*handle)); - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); r = uv_tcp_init(loop, handle); ASSERT(r == 0); diff --git a/test/test-getters-setters.c b/test/test-getters-setters.c index 60a1b92..2a37122 100644 --- a/test/test-getters-setters.c +++ b/test/test-getters-setters.c @@ -1,3 +1,24 @@ +/* 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 @@ -12,9 +33,9 @@ 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(uv_handle_type_name(UV_HANDLE_TYPE_MAX) == NULL); - ASSERT(uv_handle_type_name(UV_HANDLE_TYPE_MAX + 1) == NULL); - ASSERT(uv_handle_type_name(UV_UNKNOWN_HANDLE) == NULL); + 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)); return 0; } @@ -23,9 +44,9 @@ 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(uv_req_type_name(UV_REQ_TYPE_MAX) == NULL); - ASSERT(uv_req_type_name(UV_REQ_TYPE_MAX + 1) == NULL); - ASSERT(uv_req_type_name(UV_UNKNOWN_REQ) == NULL); + 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)); return 0; } @@ -37,7 +58,7 @@ TEST_IMPL(getters_setters) { int r; loop = malloc(uv_loop_size()); - ASSERT(loop != NULL); + ASSERT_NOT_NULL(loop); r = uv_loop_init(loop); ASSERT(r == 0); diff --git a/test/test-homedir.c b/test/test-homedir.c index 856534a..508351f 100644 --- a/test/test-homedir.c +++ b/test/test-homedir.c @@ -23,7 +23,7 @@ #include "task.h" #include -#define PATHMAX 1024 +#define PATHMAX 4096 #define SMALLPATH 1 TEST_IMPL(homedir) { diff --git a/test/test-idna.c b/test/test-idna.c index b76853c..f4fad96 100644 --- a/test/test-idna.c +++ b/test/test-idna.c @@ -96,6 +96,25 @@ TEST_IMPL(utf8_decode1) { return 0; } +TEST_IMPL(utf8_decode1_overrun) { + const char* p; + char b[1]; + + /* Single byte. */ + p = b; + b[0] = 0x7F; + ASSERT_EQ(0x7F, uv__utf8_decode1(&p, b + 1)); + ASSERT_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); + + return 0; +} + /* Doesn't work on z/OS because that platform uses EBCDIC, not ASCII. */ #ifndef __MVS__ diff --git a/test/test-ip-name.c b/test/test-ip-name.c new file mode 100644 index 0000000..1cb1b60 --- /dev/null +++ b/test/test-ip-name.c @@ -0,0 +1,65 @@ +/* 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 + +union TestAddr { + struct sockaddr addr; + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; +}; + + +TEST_IMPL(ip_name) { + char dst[INET6_ADDRSTRLEN]; + union TestAddr test_addr; + struct sockaddr* addr = &test_addr.addr; + struct sockaddr_in* addr4 = &test_addr.addr4; + 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_EQ(0, uv_ip_name(addr, dst, INET_ADDRSTRLEN)); + ASSERT_EQ(0, 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)); + + /* 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(); + return 0; +} diff --git a/test/test-ip6-addr.c b/test/test-ip6-addr.c index 39d5706..8036c4b 100644 --- a/test/test-ip6-addr.c +++ b/test/test-ip6-addr.c @@ -161,11 +161,11 @@ TEST_IMPL(ip6_pton) { #undef GOOD_ADDR_LIST #undef BAD_ADDR_LIST -#ifdef SIN6_LEN TEST_IMPL(ip6_sin6_len) { struct sockaddr_in6 s; - ASSERT(uv_ip6_addr("::", 0, &s) < 0); + ASSERT_EQ(0, uv_ip6_addr("::", 0, &s)); +#ifdef SIN6_LEN ASSERT(s.sin6_len == sizeof(s)); +#endif return 0; } -#endif diff --git a/test/test-ipc-heavy-traffic-deadlock-bug.c b/test/test-ipc-heavy-traffic-deadlock-bug.c index 753fb7b..89b977d 100644 --- a/test/test-ipc-heavy-traffic-deadlock-bug.c +++ b/test/test-ipc-heavy-traffic-deadlock-bug.c @@ -66,7 +66,7 @@ static void do_write(uv_stream_t* handle) { int r; write_info = malloc(sizeof *write_info); - ASSERT(write_info != NULL); + ASSERT_NOT_NULL(write_info); for (i = 0; i < BUFFERS_PER_WRITE; i++) { memset(&write_info->buffers[i], BUFFER_CONTENT, BUFFER_SIZE); diff --git a/test/test-ipc-send-recv.c b/test/test-ipc-send-recv.c index 12d4e33..8a0e970 100644 --- a/test/test-ipc-send-recv.c +++ b/test/test-ipc-send-recv.c @@ -308,8 +308,12 @@ static void read_cb(uv_stream_t* handle, return; } + ASSERT_GE(nread, 0); + pipe = (uv_pipe_t*) handle; - do { + ASSERT_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 +322,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); @@ -344,7 +344,7 @@ static void read_cb(uv_stream_t* handle, &recv->stream, write2_cb); ASSERT(r == 0); - } while (uv_pipe_pending_count(pipe) > 0); + } } static void send_recv_start(void) { diff --git a/test/test-ipc.c b/test/test-ipc.c index 8579a47..cf131ff 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(status == 0); - ASSERT((uv_stream_t*)&tcp_server == server); + ASSERT_EQ(status, 0); + ASSERT_PTR_EQ(&tcp_server, server); conn = malloc(sizeof(*conn)); - ASSERT(conn); + ASSERT_NOT_NULL(conn); r = uv_tcp_init(server->loop, conn); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_accept(server, (uv_stream_t*)conn); - ASSERT(r == 0); + ASSERT_EQ(r, 0); 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(exit_status == 0); - ASSERT(term_signal == 0); + ASSERT_EQ(exit_status, 0); + ASSERT_EQ(term_signal, 0); uv_close((uv_handle_t*)process, NULL); } @@ -134,18 +123,17 @@ static void make_many_connections(void) { for (i = 0; i < CONN_COUNT; i++) { conn = malloc(sizeof(*conn)); - ASSERT(conn); + ASSERT_NOT_NULL(conn); r = uv_tcp_init(uv_default_loop(), &conn->conn); - ASSERT(r == 0); - - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_EQ(r, 0); + ASSERT_EQ(0, 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(r == 0); + ASSERT_EQ(r, 0); conn->conn.data = conn; } @@ -182,36 +170,38 @@ static void on_read(uv_stream_t* handle, pending = uv_pipe_pending_type(pipe); if (!tcp_server_listening) { - ASSERT(1 == uv_pipe_pending_count(pipe)); - ASSERT(nread > 0 && buf->base && pending != UV_UNKNOWN_HANDLE); + ASSERT_EQ(1, uv_pipe_pending_count(pipe)); + ASSERT_GT(nread, 0); + ASSERT_NOT_NULL(buf->base); + ASSERT_NE(pending, UV_UNKNOWN_HANDLE); read_cb_called++; /* Accept the pending TCP server, and start listening on it. */ - ASSERT(pending == UV_TCP); + ASSERT_EQ(pending, UV_TCP); r = uv_tcp_init(uv_default_loop(), &tcp_server); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_accept((uv_stream_t*)pipe, (uv_stream_t*)&tcp_server); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, on_connection); - ASSERT(r == 0); + ASSERT_EQ(r, 0); tcp_server_listening = 1; /* Make sure that the expected data is correctly multiplexed. */ - ASSERT(memcmp("hello\n", buf->base, nread) == 0); + ASSERT_MEM_EQ("hello\n", buf->base, nread); - outbuf = uv_buf_init("world\n", 6); + outbuf = uv_buf_init("foobar\n", 7); r = uv_write(&write_req, (uv_stream_t*)pipe, &outbuf, 1, NULL); - ASSERT(r == 0); + ASSERT_EQ(r, 0); /* 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(0 == uv_pipe_pending_count(pipe)); - ASSERT(pending == UV_UNKNOWN_HANDLE); + ASSERT_EQ(0, uv_pipe_pending_count(pipe)); + ASSERT_EQ(pending, UV_UNKNOWN_HANDLE); remote_conn_accepted = 1; uv_close((uv_handle_t*)&channel, NULL); } @@ -247,37 +237,39 @@ static void on_read_listen_after_bound_twice(uv_stream_t* handle, fprintf(stderr, "got %d bytes\n", (int)nread); - ASSERT(uv_pipe_pending_count(pipe) > 0); + ASSERT_GT(uv_pipe_pending_count(pipe), 0); pending = uv_pipe_pending_type(pipe); - ASSERT(nread > 0 && buf->base && pending != UV_UNKNOWN_HANDLE); + ASSERT_GT(nread, 0); + ASSERT_NOT_NULL(buf->base); + ASSERT_NE(pending, UV_UNKNOWN_HANDLE); read_cb_called++; if (read_cb_called == 1) { /* Accept the first TCP server, and start listening on it. */ - ASSERT(pending == UV_TCP); + ASSERT_EQ(pending, UV_TCP); r = uv_tcp_init(uv_default_loop(), &tcp_server); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_accept((uv_stream_t*)pipe, (uv_stream_t*)&tcp_server); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, on_connection); - ASSERT(r == 0); + ASSERT_EQ(r, 0); } else if (read_cb_called == 2) { /* Accept the second TCP server, and start listening on it. */ - ASSERT(pending == UV_TCP); + ASSERT_EQ(pending, UV_TCP); r = uv_tcp_init(uv_default_loop(), &tcp_server2); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_accept((uv_stream_t*)pipe, (uv_stream_t*)&tcp_server2); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_listen((uv_stream_t*)&tcp_server2, BACKLOG, on_connection); - ASSERT(r == UV_EADDRINUSE); + ASSERT_EQ(r, UV_EADDRINUSE); uv_close((uv_handle_t*)&tcp_server, NULL); uv_close((uv_handle_t*)&tcp_server2, NULL); - ASSERT(0 == uv_pipe_pending_count(pipe)); + ASSERT_EQ(0, uv_pipe_pending_count(pipe)); uv_close((uv_handle_t*)&channel, NULL); } @@ -296,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(r == 0); - ASSERT(channel->ipc); + ASSERT_EQ(r, 0); + ASSERT_NE(channel->ipc, 0); exepath_size = sizeof(exepath); r = uv_exepath(exepath, &exepath_size); - ASSERT(r == 0); + ASSERT_EQ(r, 0); exepath[exepath_size] = '\0'; args[0] = exepath; @@ -323,13 +315,13 @@ void spawn_helper(uv_pipe_t* channel, stdio[2].data.fd = 2; r = uv_spawn(uv_default_loop(), process, &options); - ASSERT(r == 0); + ASSERT_EQ(r, 0); } static void on_tcp_write(uv_write_t* req, int status) { - ASSERT(status == 0); - ASSERT(req->handle == (uv_stream_t*)&tcp_connection); + ASSERT_EQ(status, 0); + ASSERT_PTR_EQ(req->handle, &tcp_connection); tcp_write_cb_called++; } @@ -343,9 +335,9 @@ static void on_read_alloc(uv_handle_t* handle, static void on_tcp_read(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { - ASSERT(nread > 0); - ASSERT(memcmp("hello again\n", buf->base, nread) == 0); - ASSERT(tcp == (uv_stream_t*)&tcp_connection); + ASSERT_GT(nread, 0); + ASSERT_MEM_EQ("hello again\n", buf->base, nread); + ASSERT_PTR_EQ(tcp, &tcp_connection); free(buf->base); tcp_read_cb_called++; @@ -382,56 +374,38 @@ static void on_read_connection(uv_stream_t* handle, fprintf(stderr, "got %d bytes\n", (int)nread); - ASSERT(1 == uv_pipe_pending_count(pipe)); + ASSERT_EQ(1, uv_pipe_pending_count(pipe)); pending = uv_pipe_pending_type(pipe); - ASSERT(nread > 0 && buf->base && pending != UV_UNKNOWN_HANDLE); + ASSERT_GT(nread, 0); + ASSERT_NOT_NULL(buf->base); + ASSERT_NE(pending, UV_UNKNOWN_HANDLE); read_cb_called++; /* Accept the pending TCP connection */ - ASSERT(pending == UV_TCP); + ASSERT_EQ(pending, UV_TCP); r = uv_tcp_init(uv_default_loop(), &tcp_connection); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_accept(handle, (uv_stream_t*)&tcp_connection); - ASSERT(r == 0); + ASSERT_EQ(r, 0); /* Make sure that the expected data is correctly multiplexed. */ - ASSERT(memcmp("hello\n", buf->base, nread) == 0); + ASSERT_MEM_EQ("hello\n", buf->base, nread); /* Write/read to/from the connection */ outbuf = uv_buf_init("world\n", 6); r = uv_write(&write_req, (uv_stream_t*)&tcp_connection, &outbuf, 1, on_tcp_write); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_read_start((uv_stream_t*)&tcp_connection, on_read_alloc, on_tcp_read); - ASSERT(r == 0); + 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(); - } - - closed_handle_data_read += nread; - free(buf->base); -} -#endif - - static void on_read_send_zero(uv_stream_t* handle, ssize_t nread, const uv_buf_t* buf) { @@ -448,7 +422,7 @@ 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(r == 0); + ASSERT_EQ(r, 0); MAKE_VALGRIND_HAPPY(); return 0; @@ -460,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(local_conn_accepted == 1); - ASSERT(remote_conn_accepted == 1); - ASSERT(read_cb_called == 1); - ASSERT(exit_cb_called == 1); + ASSERT_EQ(local_conn_accepted, 1); + ASSERT_EQ(remote_conn_accepted, 1); + ASSERT_EQ(read_cb_called, 1); + ASSERT_EQ(exit_cb_called, 1); return r; } @@ -473,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(local_conn_accepted == 1); - ASSERT(remote_conn_accepted == 1); - ASSERT(read_cb_called == 1); - ASSERT(exit_cb_called == 1); + ASSERT_EQ(local_conn_accepted, 1); + ASSERT_EQ(remote_conn_accepted, 1); + ASSERT_EQ(read_cb_called, 1); + ASSERT_EQ(exit_cb_called, 1); return r; } @@ -486,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(read_cb_called == 1); - ASSERT(tcp_write_cb_called == 1); - ASSERT(tcp_read_cb_called == 1); - ASSERT(exit_cb_called == 1); + 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); return r; } -#ifndef _WIN32 -TEST_IMPL(ipc_closed_handle) { - int r; - r = run_ipc_test("ipc_helper_closed_handle", on_read_closed_handle); - ASSERT(r == 0); - return 0; -} -#endif - #ifdef _WIN32 TEST_IMPL(listen_with_simultaneous_accepts) { @@ -509,20 +474,20 @@ TEST_IMPL(listen_with_simultaneous_accepts) { int r; struct sockaddr_in addr; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_EQ(0, uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_tcp_simultaneous_accepts(&server, 1); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_listen((uv_stream_t*)&server, SOMAXCONN, NULL); - ASSERT(r == 0); - ASSERT(server.reqs_pending == 32); + ASSERT_EQ(r, 0); + ASSERT_EQ(server.reqs_pending, 32); MAKE_VALGRIND_HAPPY(); return 0; @@ -534,20 +499,20 @@ TEST_IMPL(listen_no_simultaneous_accepts) { int r; struct sockaddr_in addr; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_EQ(0, uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_tcp_init(uv_default_loop(), &server); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_tcp_simultaneous_accepts(&server, 0); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_listen((uv_stream_t*)&server, SOMAXCONN, NULL); - ASSERT(r == 0); - ASSERT(server.reqs_pending == 1); + ASSERT_EQ(r, 0); + ASSERT_EQ(server.reqs_pending, 1); MAKE_VALGRIND_HAPPY(); return 0; @@ -558,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(read_cb_called == 2); - ASSERT(exit_cb_called == 1); + ASSERT_EQ(read_cb_called, 2); + ASSERT_EQ(exit_cb_called, 1); return r; } #endif @@ -567,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(r == 0); + ASSERT_EQ(r, 0); return 0; } @@ -589,7 +554,7 @@ static void conn_notify_write_cb(uv_write_t* req, int status) { static void tcp_connection_write_cb(uv_write_t* req, int status) { - ASSERT((uv_handle_t*)&conn.conn == (uv_handle_t*)req->handle); + ASSERT_PTR_EQ(&conn.conn, req->handle); uv_close((uv_handle_t*)req->handle, close_cb); uv_close((uv_handle_t*)&channel, close_cb); uv_close((uv_handle_t*)&tcp_server, close_cb); @@ -597,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(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(status == UV_EBADF); - closed_handle_write = 1; -} - - static void send_zero_write_cb(uv_write_t* req, int status) { - ASSERT(status == 0); + ASSERT_EQ(status, 0); send_zero_write++; } @@ -635,15 +583,15 @@ static void on_tcp_child_process_read(uv_stream_t* tcp, abort(); } - ASSERT(nread > 0); - ASSERT(memcmp("world\n", buf->base, nread) == 0); + ASSERT_GT(nread, 0); + ASSERT_MEM_EQ("world\n", buf->base, nread); on_pipe_read_called++; free(buf->base); /* 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(r == 0); + ASSERT_EQ(r, 0); tcp_conn_read_cb_called++; } @@ -652,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(status == 0); + ASSERT_EQ(status, 0); r = uv_read_start(req->handle, on_read_alloc, on_tcp_child_process_read); - ASSERT(r == 0); + ASSERT_EQ(r, 0); } @@ -667,56 +615,61 @@ static void ipc_on_connection(uv_stream_t* server, int status) { * Accept the connection and close it. Also let the other * side know. */ - ASSERT(status == 0); - ASSERT((uv_stream_t*)&tcp_server == server); + ASSERT_EQ(status, 0); + ASSERT_PTR_EQ(&tcp_server, server); r = uv_tcp_init(server->loop, &conn.conn); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_accept(server, (uv_stream_t*)&conn.conn); - ASSERT(r == 0); + ASSERT_EQ(r, 0); 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(r == 0); + ASSERT_EQ(r, 0); connection_accepted = 1; } } +static void close_and_free_cb(uv_handle_t* handle) { + close_cb_called++; + free(handle); +} + static void ipc_on_connection_tcp_conn(uv_stream_t* server, int status) { int r; uv_buf_t buf; uv_tcp_t* conn; - ASSERT(status == 0); - ASSERT((uv_stream_t*)&tcp_server == server); + ASSERT_EQ(status, 0); + ASSERT_PTR_EQ(&tcp_server, server); conn = malloc(sizeof(*conn)); - ASSERT(conn); + ASSERT_NOT_NULL(conn); r = uv_tcp_init(server->loop, conn); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_accept(server, (uv_stream_t*)conn); - ASSERT(r == 0); + ASSERT_EQ(r, 0); /* 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(r == 0); + ASSERT_EQ(r, 0); r = uv_read_start((uv_stream_t*) conn, on_read_alloc, on_tcp_child_process_read); - ASSERT(r == 0); + ASSERT_EQ(r, 0); - uv_close((uv_handle_t*)conn, close_cb); + uv_close((uv_handle_t*)conn, close_and_free_cb); } @@ -729,44 +682,44 @@ int ipc_helper(int listen_after_write) { int r; uv_buf_t buf; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_EQ(0, uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_pipe_init(uv_default_loop(), &channel, 1); - ASSERT(r == 0); + ASSERT_EQ(r, 0); uv_pipe_open(&channel, 0); - ASSERT(1 == uv_is_readable((uv_stream_t*) &channel)); - ASSERT(1 == uv_is_writable((uv_stream_t*) &channel)); - ASSERT(0 == uv_is_closing((uv_handle_t*) &channel)); + 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)); r = uv_tcp_init(uv_default_loop(), &tcp_server); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_EQ(r, 0); if (!listen_after_write) { r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, ipc_on_connection); - ASSERT(r == 0); + ASSERT_EQ(r, 0); } 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(r == 0); + ASSERT_EQ(r, 0); if (listen_after_write) { r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, ipc_on_connection); - ASSERT(r == 0); + ASSERT_EQ(r, 0); } notify_parent_process(); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_EQ(r, 0); - ASSERT(connection_accepted == 1); - ASSERT(close_cb_called == 3); + ASSERT_EQ(connection_accepted, 1); + ASSERT_EQ(close_cb_called, 3); MAKE_VALGRIND_HAPPY(); return 0; @@ -783,118 +736,48 @@ int ipc_helper_tcp_connection(void) { struct sockaddr_in addr; r = uv_pipe_init(uv_default_loop(), &channel, 1); - ASSERT(r == 0); + ASSERT_EQ(r, 0); uv_pipe_open(&channel, 0); - ASSERT(1 == uv_is_readable((uv_stream_t*) &channel)); - ASSERT(1 == uv_is_writable((uv_stream_t*) &channel)); - ASSERT(0 == uv_is_closing((uv_handle_t*) &channel)); + 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)); r = uv_tcp_init(uv_default_loop(), &tcp_server); - ASSERT(r == 0); + ASSERT_EQ(r, 0); - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + 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(r == 0); + ASSERT_EQ(r, 0); r = uv_listen((uv_stream_t*)&tcp_server, BACKLOG, ipc_on_connection_tcp_conn); - ASSERT(r == 0); + ASSERT_EQ(r, 0); /* Make a connection to the server */ r = uv_tcp_init(uv_default_loop(), &conn.conn); - ASSERT(r == 0); + ASSERT_EQ(r, 0); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + ASSERT_EQ(0, 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(r == 0); + ASSERT_EQ(r, 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_EQ(r, 0); - ASSERT(tcp_conn_read_cb_called == 1); - ASSERT(tcp_conn_write_cb_called == 1); - ASSERT(close_cb_called == 4); + 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(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(r == 0); - - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); - - r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); - - r = uv_write2(&write_req, - (uv_stream_t*)&channel, - &large_buf, - 1, - (uv_stream_t*)&tcp_server, - closed_handle_write_cb); - ASSERT(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(r == 0); - - uv_pipe_open(&channel, 0); - - ASSERT(1 == uv_is_readable((uv_stream_t*) &channel)); - ASSERT(1 == uv_is_writable((uv_stream_t*) &channel)); - ASSERT(0 == uv_is_closing((uv_handle_t*) &channel)); - - if (write_until_data_queued() > 0) - send_handle_and_close(); - - r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); - - ASSERT(closed_handle_write == 1); - - MAKE_VALGRIND_HAPPY(); - return 0; -} - - int ipc_helper_bind_twice(void) { /* * This is launched from test-ipc.c. stdin is a duplex channel @@ -904,38 +787,38 @@ int ipc_helper_bind_twice(void) { int r; uv_buf_t buf; - ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + ASSERT_EQ(0, uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); r = uv_pipe_init(uv_default_loop(), &channel, 1); - ASSERT(r == 0); + ASSERT_EQ(r, 0); uv_pipe_open(&channel, 0); - ASSERT(1 == uv_is_readable((uv_stream_t*) &channel)); - ASSERT(1 == uv_is_writable((uv_stream_t*) &channel)); - ASSERT(0 == uv_is_closing((uv_handle_t*) &channel)); + 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)); buf = uv_buf_init("hello\n", 6); r = uv_tcp_init(uv_default_loop(), &tcp_server); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_tcp_init(uv_default_loop(), &tcp_server2); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_tcp_bind(&tcp_server2, (const struct sockaddr*) &addr, 0); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_write2(&write_req, (uv_stream_t*)&channel, &buf, 1, (uv_stream_t*)&tcp_server, NULL); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_write2(&write_req2, (uv_stream_t*)&channel, &buf, 1, (uv_stream_t*)&tcp_server2, NULL); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_EQ(r, 0); MAKE_VALGRIND_HAPPY(); return 0; @@ -948,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(r == 0); + ASSERT_EQ(r, 0); uv_pipe_open(&channel, 0); - ASSERT(1 == uv_is_readable((uv_stream_t*) &channel)); - ASSERT(1 == uv_is_writable((uv_stream_t*) &channel)); - ASSERT(0 == uv_is_closing((uv_handle_t*) &channel)); + 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)); r = uv_write(&write_req, (uv_stream_t*)&channel, @@ -962,12 +845,12 @@ int ipc_helper_send_zero(void) { 1, send_zero_write_cb); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(r == 0); + ASSERT_EQ(r, 0); - ASSERT(send_zero_write == 1); + ASSERT_EQ(send_zero_write, 1); MAKE_VALGRIND_HAPPY(); return 0; diff --git a/test/test-list.h b/test/test-list.h index 003b8d2..15a3bad 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -78,6 +78,7 @@ TEST_DECLARE (tty_pty) TEST_DECLARE (stdio_over_pipes) TEST_DECLARE (stdio_emulate_iocp) TEST_DECLARE (ip6_pton) +TEST_DECLARE (ip6_sin6_len) TEST_DECLARE (connect_unspecified) TEST_DECLARE (ipc_heavy_traffic_deadlock_bug) TEST_DECLARE (ipc_listen_before_write) @@ -90,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) @@ -116,7 +114,8 @@ TEST_DECLARE (tcp_open_bound) TEST_DECLARE (tcp_open_connected) TEST_DECLARE (tcp_connect_error_after_write) TEST_DECLARE (tcp_shutdown_after_write) -TEST_DECLARE (tcp_bind_error_addrinuse) +TEST_DECLARE (tcp_bind_error_addrinuse_connect) +TEST_DECLARE (tcp_bind_error_addrinuse_listen) TEST_DECLARE (tcp_bind_error_addrnotavail_1) TEST_DECLARE (tcp_bind_error_addrnotavail_2) TEST_DECLARE (tcp_bind_error_fault) @@ -127,6 +126,8 @@ TEST_DECLARE (tcp_bind_writable_flags) TEST_DECLARE (tcp_listen_without_bind) TEST_DECLARE (tcp_connect_error_fault) 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) TEST_DECLARE (tcp_close_reset_accepted) @@ -145,6 +146,7 @@ TEST_DECLARE (tcp_flags) 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_bind6_error_addrinuse) TEST_DECLARE (tcp_bind6_error_addrnotavail) TEST_DECLARE (tcp_bind6_error_fault) @@ -155,6 +157,7 @@ TEST_DECLARE (udp_alloc_cb_fail) TEST_DECLARE (udp_bind) TEST_DECLARE (udp_bind_reuseaddr) TEST_DECLARE (udp_connect) +TEST_DECLARE (udp_connect6) TEST_DECLARE (udp_create_early) TEST_DECLARE (udp_create_early_bad_bind) TEST_DECLARE (udp_create_early_bad_domain) @@ -162,6 +165,7 @@ TEST_DECLARE (udp_send_and_recv) TEST_DECLARE (udp_send_hang_loop) TEST_DECLARE (udp_send_immediate) TEST_DECLARE (udp_send_unreachable) +TEST_DECLARE (udp_mmsg) TEST_DECLARE (udp_multicast_join) TEST_DECLARE (udp_multicast_join6) TEST_DECLARE (udp_multicast_ttl) @@ -180,6 +184,7 @@ TEST_DECLARE (udp_open_connect) #ifndef _WIN32 TEST_DECLARE (udp_send_unix) #endif +TEST_DECLARE (udp_sendmmsg_error) TEST_DECLARE (udp_try_send) TEST_DECLARE (pipe_bind_error_addrinuse) TEST_DECLARE (pipe_bind_error_addrnotavail) @@ -200,6 +205,7 @@ TEST_DECLARE (connection_fail_doesnt_auto_close) TEST_DECLARE (shutdown_close_tcp) TEST_DECLARE (shutdown_close_pipe) TEST_DECLARE (shutdown_eof) +TEST_DECLARE (shutdown_simultaneous) TEST_DECLARE (shutdown_twice) TEST_DECLARE (callback_stack) TEST_DECLARE (env_vars) @@ -280,6 +286,7 @@ TEST_DECLARE (getnameinfo_basic_ip6) TEST_DECLARE (getsockname_tcp) TEST_DECLARE (getsockname_udp) TEST_DECLARE (gettimeofday) +TEST_DECLARE (test_macros) TEST_DECLARE (fail_always) TEST_DECLARE (pass_always) TEST_DECLARE (socket_buffer_size) @@ -310,6 +317,7 @@ 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 (fs_poll) TEST_DECLARE (fs_poll_getpath) TEST_DECLARE (fs_poll_close_request) @@ -346,13 +354,16 @@ TEST_DECLARE (fs_symlink_dir) #ifdef _WIN32 TEST_DECLARE (fs_symlink_junction) TEST_DECLARE (fs_non_symlink_reparse_point) +TEST_DECLARE (fs_lstat_windows_store_apps) TEST_DECLARE (fs_open_flags) #endif #if defined(_WIN32) && !defined(USING_UV_SHARED) TEST_DECLARE (fs_fd_hash) #endif TEST_DECLARE (fs_utime) +TEST_DECLARE (fs_utime_round) TEST_DECLARE (fs_futime) +TEST_DECLARE (fs_lutime) TEST_DECLARE (fs_file_open_append) TEST_DECLARE (fs_statfs) TEST_DECLARE (fs_stat_missing_path) @@ -379,6 +390,7 @@ 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) @@ -408,6 +420,7 @@ TEST_DECLARE (fs_open_readonly_acl) TEST_DECLARE (fs_fchmod_archive_readonly) TEST_DECLARE (fs_invalid_mkdir_name) #endif +TEST_DECLARE (fs_get_system_error) TEST_DECLARE (strscpy) TEST_DECLARE (threadpool_queue_work_simple) TEST_DECLARE (threadpool_queue_work_einval) @@ -442,12 +455,17 @@ TEST_DECLARE (poll_nested_epoll) #ifdef UV_HAVE_KQUEUE TEST_DECLARE (poll_nested_kqueue) #endif +TEST_DECLARE (poll_multiple_handles) TEST_DECLARE (ip4_addr) TEST_DECLARE (ip6_addr_link_local) +TEST_DECLARE (ip_name) TEST_DECLARE (poll_close_doesnt_corrupt_stack) TEST_DECLARE (poll_closesocket) +TEST_DECLARE (close_fd) +TEST_DECLARE (closed_fd_events) +TEST_DECLARE (spawn_fs_open) #ifdef _WIN32 TEST_DECLARE (spawn_detect_pipe_name_collisions_on_windows) #if !defined(USING_UV_SHARED) @@ -462,8 +480,6 @@ TEST_DECLARE (ipc_listen_after_bind_twice) TEST_DECLARE (win32_signum_number) #else TEST_DECLARE (emfile) -TEST_DECLARE (close_fd) -TEST_DECLARE (spawn_fs_open) TEST_DECLARE (spawn_setuid_setgid) TEST_DECLARE (we_get_signal) TEST_DECLARE (we_get_signals) @@ -472,7 +488,6 @@ TEST_DECLARE (we_get_signals_mixed) TEST_DECLARE (signal_multiple_loops) TEST_DECLARE (signal_pending_on_close) TEST_DECLARE (signal_close_loop_alive) -TEST_DECLARE (closed_fd_events) #endif #ifdef __APPLE__ TEST_DECLARE (osx_select) @@ -492,6 +507,10 @@ TEST_DECLARE (handle_type_name) TEST_DECLARE (req_type_name) TEST_DECLARE (getters_setters) +TEST_DECLARE (not_writable_after_shutdown) +TEST_DECLARE (not_readable_nor_writable_on_read_error) +TEST_DECLARE (readable_on_eof) + #ifndef _WIN32 TEST_DECLARE (fork_timer) TEST_DECLARE (fork_socketpair) @@ -512,14 +531,20 @@ TEST_DECLARE (fork_threadpool_queue_work_simple) TEST_DECLARE (idna_toascii) TEST_DECLARE (utf8_decode1) +TEST_DECLARE (utf8_decode1_overrun) TEST_DECLARE (uname) +TEST_DECLARE (metrics_idle_time) +TEST_DECLARE (metrics_idle_time_thread) +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) TEST_ENTRY (run_nowait) @@ -553,7 +578,8 @@ TASK_LIST_START #ifndef _WIN32 TEST_ENTRY (pipe_close_stdout_read_stdin) #endif - TEST_ENTRY (pipe_set_non_blocking) + /* Seems to be either about 0.5s or 5s, depending on the OS. */ + TEST_ENTRY_CUSTOM (pipe_set_non_blocking, 0, 0, 20000) TEST_ENTRY (pipe_set_chmod) TEST_ENTRY (tty) #ifdef _WIN32 @@ -586,6 +612,7 @@ TASK_LIST_START TEST_ENTRY (stdio_over_pipes) TEST_ENTRY (stdio_emulate_iocp) TEST_ENTRY (ip6_pton) + TEST_ENTRY (ip6_sin6_len) TEST_ENTRY (connect_unspecified) TEST_ENTRY (ipc_heavy_traffic_deadlock_bug) TEST_ENTRY (ipc_listen_before_write) @@ -598,9 +625,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) @@ -657,7 +681,13 @@ TASK_LIST_START TEST_HELPER (tcp_shutdown_after_write, tcp4_echo_server) TEST_ENTRY (tcp_connect_error_after_write) - TEST_ENTRY (tcp_bind_error_addrinuse) + TEST_ENTRY (tcp_bind_error_addrinuse_connect) + /* tcp4_echo_server steals the port. It needs to be a separate process + * because libuv sets setsockopt(SO_REUSEADDR) that lets you steal an + * existing bind if it originates from the same process. + */ + TEST_HELPER (tcp_bind_error_addrinuse_connect, tcp4_echo_server) + TEST_ENTRY (tcp_bind_error_addrinuse_listen) TEST_ENTRY (tcp_bind_error_addrnotavail_1) TEST_ENTRY (tcp_bind_error_addrnotavail_2) TEST_ENTRY (tcp_bind_error_fault) @@ -668,6 +698,8 @@ TASK_LIST_START TEST_ENTRY (tcp_listen_without_bind) TEST_ENTRY (tcp_connect_error_fault) 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) TEST_ENTRY (tcp_close_reset_accepted) @@ -689,6 +721,8 @@ TASK_LIST_START TEST_ENTRY (tcp_read_stop) TEST_HELPER (tcp_read_stop, tcp4_echo_server) + TEST_ENTRY (tcp_read_stop_start) + TEST_ENTRY (tcp_bind6_error_addrinuse) TEST_ENTRY (tcp_bind6_error_addrnotavail) TEST_ENTRY (tcp_bind6_error_fault) @@ -699,6 +733,7 @@ TASK_LIST_START TEST_ENTRY (udp_bind) TEST_ENTRY (udp_bind_reuseaddr) TEST_ENTRY (udp_connect) + TEST_ENTRY (udp_connect6) TEST_ENTRY (udp_create_early) TEST_ENTRY (udp_create_early_bad_bind) TEST_ENTRY (udp_create_early_bad_domain) @@ -712,11 +747,13 @@ TASK_LIST_START TEST_ENTRY (udp_options) TEST_ENTRY (udp_options6) TEST_ENTRY (udp_no_autobind) + TEST_ENTRY (udp_mmsg) TEST_ENTRY (udp_multicast_interface) TEST_ENTRY (udp_multicast_interface6) TEST_ENTRY (udp_multicast_join) TEST_ENTRY (udp_multicast_join6) TEST_ENTRY (udp_multicast_ttl) + TEST_ENTRY (udp_sendmmsg_error) TEST_ENTRY (udp_try_send) TEST_ENTRY (udp_open) @@ -749,6 +786,9 @@ TASK_LIST_START TEST_ENTRY (shutdown_eof) TEST_HELPER (shutdown_eof, tcp4_echo_server) + TEST_ENTRY (shutdown_simultaneous) + TEST_HELPER (shutdown_simultaneous, tcp4_echo_server) + TEST_ENTRY (shutdown_twice) TEST_HELPER (shutdown_twice, tcp4_echo_server) @@ -839,7 +879,7 @@ TASK_LIST_START TEST_ENTRY (tmpdir) - TEST_ENTRY_CUSTOM (hrtime, 0, 0, 10000) + TEST_ENTRY_CUSTOM (hrtime, 0, 0, 20000) TEST_ENTRY_CUSTOM (getaddrinfo_fail, 0, 0, 10000) TEST_ENTRY_CUSTOM (getaddrinfo_fail_sync, 0, 0, 10000) @@ -874,6 +914,7 @@ TASK_LIST_START #ifdef UV_HAVE_KQUEUE TEST_ENTRY (poll_nested_kqueue) #endif + TEST_ENTRY (poll_multiple_handles) TEST_ENTRY (socket_buffer_size) @@ -904,6 +945,7 @@ 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 (fs_poll) TEST_ENTRY (fs_poll_getpath) TEST_ENTRY (fs_poll_close_request) @@ -915,6 +957,9 @@ TASK_LIST_START TEST_ENTRY (poll_close_doesnt_corrupt_stack) TEST_ENTRY (poll_closesocket) + TEST_ENTRY (close_fd) + TEST_ENTRY (closed_fd_events) + TEST_ENTRY (spawn_fs_open) #ifdef _WIN32 TEST_ENTRY (spawn_detect_pipe_name_collisions_on_windows) #if !defined(USING_UV_SHARED) @@ -929,8 +974,6 @@ TASK_LIST_START TEST_ENTRY (win32_signum_number) #else TEST_ENTRY (emfile) - TEST_ENTRY (close_fd) - TEST_ENTRY (spawn_fs_open) TEST_ENTRY (spawn_setuid_setgid) TEST_ENTRY (we_get_signal) TEST_ENTRY (we_get_signals) @@ -939,7 +982,6 @@ TASK_LIST_START TEST_ENTRY (signal_multiple_loops) TEST_ENTRY (signal_pending_on_close) TEST_ENTRY (signal_close_loop_alive) - TEST_ENTRY (closed_fd_events) #endif #ifdef __APPLE__ @@ -968,7 +1010,9 @@ TASK_LIST_START #endif TEST_ENTRY (fs_chown) TEST_ENTRY (fs_utime) + TEST_ENTRY (fs_utime_round) TEST_ENTRY (fs_futime) + TEST_ENTRY (fs_lutime) TEST_ENTRY (fs_readlink) TEST_ENTRY (fs_realpath) TEST_ENTRY (fs_symlink) @@ -976,6 +1020,7 @@ TASK_LIST_START #ifdef _WIN32 TEST_ENTRY (fs_symlink_junction) TEST_ENTRY (fs_non_symlink_reparse_point) + TEST_ENTRY (fs_lstat_windows_store_apps) TEST_ENTRY (fs_open_flags) #endif #if defined(_WIN32) && !defined(USING_UV_SHARED) @@ -1005,8 +1050,9 @@ TASK_LIST_START TEST_ENTRY (fs_event_close_with_pending_event) TEST_ENTRY (fs_event_close_in_callback) TEST_ENTRY (fs_event_start_and_close) - TEST_ENTRY (fs_event_error_reporting) + 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) @@ -1034,6 +1080,7 @@ TASK_LIST_START TEST_ENTRY (fs_fchmod_archive_readonly) TEST_ENTRY (fs_invalid_mkdir_name) #endif + TEST_ENTRY (fs_get_system_error) TEST_ENTRY (get_osfhandle_valid_handle) TEST_ENTRY (open_osfhandle_valid_handle) TEST_ENTRY (strscpy) @@ -1058,6 +1105,7 @@ TASK_LIST_START TEST_ENTRY (dlerror) TEST_ENTRY (ip4_addr) TEST_ENTRY (ip6_addr_link_local) + TEST_ENTRY (ip_name) TEST_ENTRY (queue_foreach_delete) @@ -1085,6 +1133,7 @@ TASK_LIST_START #endif TEST_ENTRY (utf8_decode1) + TEST_ENTRY (utf8_decode1_overrun) TEST_ENTRY (uname) /* Doesn't work on z/OS because that platform uses EBCDIC, not ASCII. */ @@ -1092,6 +1141,17 @@ TASK_LIST_START TEST_ENTRY (idna_toascii) #endif + TEST_ENTRY (not_writable_after_shutdown) + TEST_HELPER (not_writable_after_shutdown, tcp4_echo_server) + TEST_ENTRY (not_readable_nor_writable_on_read_error) + TEST_HELPER (not_readable_nor_writable_on_read_error, tcp4_echo_server) + TEST_ENTRY (readable_on_eof) + TEST_HELPER (readable_on_eof, tcp4_echo_server) + + TEST_ENTRY (metrics_idle_time) + TEST_ENTRY (metrics_idle_time_thread) + TEST_ENTRY (metrics_idle_time_zero) + #if 0 /* These are for testing the test runner. */ TEST_ENTRY (fail_always) diff --git a/test/test-loop-handles.c b/test/test-loop-handles.c index 1310988..05cb846 100644 --- a/test/test-loop-handles.c +++ b/test/test-loop-handles.c @@ -143,7 +143,7 @@ static void idle_1_cb(uv_idle_t* handle) { fprintf(stderr, "%s", "IDLE_1_CB\n"); fflush(stderr); - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); ASSERT(idles_1_active > 0); /* Init idle_2 and make it active */ @@ -170,7 +170,7 @@ static void idle_1_close_cb(uv_handle_t* handle) { fprintf(stderr, "%s", "IDLE_1_CLOSE_CB\n"); fflush(stderr); - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); idle_1_close_cb_called++; } diff --git a/test/test-loop-time.c b/test/test-loop-time.c index a2db42c..087720b 100644 --- a/test/test-loop-time.c +++ b/test/test-loop-time.c @@ -28,7 +28,7 @@ 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_EQ(0, uv_run(uv_default_loop(), UV_RUN_NOWAIT)); MAKE_VALGRIND_HAPPY(); return 0; @@ -43,20 +43,26 @@ 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(r, 1); + loop->active_handles--; + ASSERT_EQ(uv_loop_alive(loop), 0); + r = uv_timer_init(loop, &timer); - ASSERT(r == 0); + ASSERT_EQ(r, 0); - ASSERT(!uv_loop_alive(loop)); - ASSERT(uv_backend_timeout(loop) == 0); + ASSERT_EQ(uv_loop_alive(loop), 0); + ASSERT_EQ(uv_backend_timeout(loop), 0); 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_EQ(r, 0); + ASSERT_EQ(uv_backend_timeout(loop), 1000); r = uv_run(loop, UV_RUN_DEFAULT); - ASSERT(r == 0); - ASSERT(uv_backend_timeout(loop) == 0); + ASSERT_EQ(r, 0); + ASSERT_EQ(uv_backend_timeout(loop), 0); MAKE_VALGRIND_HAPPY(); return 0; diff --git a/test/test-metrics.c b/test/test-metrics.c new file mode 100644 index 0000000..f527494 --- /dev/null +++ b/test/test-metrics.c @@ -0,0 +1,135 @@ +/* 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 /* memset */ + +#define UV_NS_TO_MS 1000000 + + +static void timer_spin_cb(uv_timer_t* handle) { + uint64_t t; + + (*(int*) handle->data)++; + t = uv_hrtime(); + /* Spin for 500 ms to spin loop time out of the delta check. */ + while (uv_hrtime() - t < 600 * UV_NS_TO_MS) { } +} + + +TEST_IMPL(metrics_idle_time) { + const uint64_t timeout = 1000; + uv_timer_t timer; + uint64_t idle_time; + 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_spin_cb, timeout, 0)); + + ASSERT_EQ(0, 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)); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +static void metrics_routine_cb(void* arg) { + const uint64_t timeout = 1000; + uv_loop_t loop; + uv_timer_t timer; + uint64_t idle_time; + int cntr; + + 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_EQ(0, uv_run(&loop, UV_RUN_DEFAULT)); + ASSERT_GT(cntr, 0); + + idle_time = uv_metrics_idle_time(&loop); + + /* Only checking that idle time is greater than the lower bound since there + * may have been thread contention, causing the event loop to be delayed in + * the idle phase longer than expected. + */ + ASSERT_GE(idle_time, (timeout - 500) * UV_NS_TO_MS); + + close_loop(&loop); + ASSERT_EQ(0, uv_loop_close(&loop)); +} + + +TEST_IMPL(metrics_idle_time_thread) { + uv_thread_t threads[5]; + int i; + + for (i = 0; i < 5; i++) { + ASSERT_EQ(0, uv_thread_create(&threads[i], metrics_routine_cb, NULL)); + } + + for (i = 0; i < 5; i++) { + uv_thread_join(&threads[i]); + } + + return 0; +} + + +static void timer_noop_cb(uv_timer_t* handle) { + (*(int*) handle->data)++; +} + + +TEST_IMPL(metrics_idle_time_zero) { + 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_EQ(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + ASSERT_GT(cntr, 0); + ASSERT_EQ(0, uv_metrics_idle_time(uv_default_loop())); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/test/test-multiple-listen.c b/test/test-multiple-listen.c index 4ae5fa6..0b28514 100644 --- a/test/test-multiple-listen.c +++ b/test/test-multiple-listen.c @@ -32,7 +32,7 @@ static uv_tcp_t client; static void close_cb(uv_handle_t* handle) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); close_cb_called++; } @@ -65,7 +65,7 @@ static void start_server(void) { static void connect_cb(uv_connect_t* req, int status) { - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status == 0); free(req); uv_close((uv_handle_t*)&client, close_cb); @@ -79,7 +79,7 @@ static void client_connect(void) { int r; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); - ASSERT(connect_req != NULL); + ASSERT_NOT_NULL(connect_req); r = uv_tcp_init(uv_default_loop(), &client); ASSERT(r == 0); diff --git a/test/test-not-readable-nor-writable-on-read-error.c b/test/test-not-readable-nor-writable-on-read-error.c new file mode 100644 index 0000000..ae951e3 --- /dev/null +++ b/test/test-not-readable-nor-writable-on-read-error.c @@ -0,0 +1,104 @@ +/* Copyright the 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" + +static uv_loop_t loop; +static uv_tcp_t tcp_client; +static uv_connect_t connect_req; +static uv_write_t write_req; +static char reset_me_cmd[] = {'Q', 'S', 'H'}; + +static int connect_cb_called; +static int read_cb_called; +static int write_cb_called; +static int close_cb_called; + +static void write_cb(uv_write_t* req, int status) { + write_cb_called++; + ASSERT(status == 0); +} + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[64]; + buf->base = slab; + buf->len = sizeof(slab); +} + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + +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)); + + uv_close((uv_handle_t*) handle, close_cb); +} + +static void connect_cb(uv_connect_t* req, int status) { + int r; + uv_buf_t reset_me; + + connect_cb_called++; + ASSERT(status == 0); + + r = uv_read_start((uv_stream_t*) &tcp_client, alloc_cb, read_cb); + ASSERT(r == 0); + + reset_me = uv_buf_init(reset_me_cmd, sizeof(reset_me_cmd)); + + r = uv_write(&write_req, + (uv_stream_t*) &tcp_client, + &reset_me, + 1, + write_cb); + + ASSERT(r == 0); +} + +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(0 == uv_tcp_connect(&connect_req, + &tcp_client, + (const struct sockaddr*) &sa, + connect_cb)); + + ASSERT(0 == 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); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/test/test-not-writable-after-shutdown.c b/test/test-not-writable-after-shutdown.c new file mode 100644 index 0000000..9cd9370 --- /dev/null +++ b/test/test-not-writable-after-shutdown.c @@ -0,0 +1,69 @@ +/* Copyright the 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" + +static uv_shutdown_t shutdown_req; + +static void close_cb(uv_handle_t* handle) { + +} + +static void shutdown_cb(uv_shutdown_t* req, int status) { + uv_close((uv_handle_t*) req->handle, close_cb); +} + +static void connect_cb(uv_connect_t* req, int status) { + int r; + ASSERT(status == 0); + + r = uv_shutdown(&shutdown_req, req->handle, shutdown_cb); + ASSERT(r == 0); + + ASSERT(0 == uv_is_writable(req->handle)); +} + +TEST_IMPL(not_writable_after_shutdown) { + int r; + struct sockaddr_in addr; + uv_loop_t* loop; + uv_tcp_t socket; + uv_connect_t connect_req; + + ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + loop = uv_default_loop(); + + r = uv_tcp_init(loop, &socket); + ASSERT(r == 0); + + r = uv_tcp_connect(&connect_req, + &socket, + (const struct sockaddr*) &addr, + connect_cb); + ASSERT(r == 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/test/test-ping-pong.c b/test/test-ping-pong.c index c86a3f4..c598587 100644 --- a/test/test-ping-pong.c +++ b/test/test-ping-pong.c @@ -24,6 +24,7 @@ #include #include +#include /* strlen */ static int completed_pingers = 0; @@ -33,23 +34,21 @@ static int completed_pingers = 0; #define NUM_PINGS 1000 #endif -/* 64 bytes is enough for a pinger */ -#define BUFSIZE 10240 - static char PING[] = "PING\n"; +static char PONG[] = "PONG\n"; static int pinger_on_connect_count; typedef struct { int vectored_writes; - int pongs; - int state; + unsigned pongs; + unsigned state; union { uv_tcp_t tcp; uv_pipe_t pipe; } stream; uv_connect_t connect_req; - char read_buffer[BUFSIZE]; + char* pong; } pinger_t; @@ -59,28 +58,45 @@ static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { } +static void ponger_on_close(uv_handle_t* handle) { + if (handle->data) + free(handle->data); + else + free(handle); +} + + static void pinger_on_close(uv_handle_t* handle) { - pinger_t* pinger = (pinger_t*)handle->data; + pinger_t* pinger = (pinger_t*) handle->data; - ASSERT(NUM_PINGS == pinger->pongs); + ASSERT_EQ(NUM_PINGS, pinger->pongs); - free(pinger); + if (handle == (uv_handle_t*) &pinger->stream.tcp) { + free(pinger); /* also frees handle */ + } else { + uv_close((uv_handle_t*) &pinger->stream.tcp, ponger_on_close); + free(handle); + } completed_pingers++; } -static void pinger_after_write(uv_write_t *req, int status) { - ASSERT(status == 0); +static void pinger_after_write(uv_write_t* req, int status) { + ASSERT_EQ(status, 0); + free(req->data); free(req); } static void pinger_write_ping(pinger_t* pinger) { - uv_write_t *req; + uv_stream_t* stream; + uv_write_t* req; uv_buf_t bufs[sizeof PING - 1]; int i, nbufs; + stream = (uv_stream_t*) &pinger->stream.tcp; + if (!pinger->vectored_writes) { /* Write a single buffer. */ nbufs = 1; @@ -94,13 +110,9 @@ static void pinger_write_ping(pinger_t* pinger) { } req = malloc(sizeof(*req)); - if (uv_write(req, - (uv_stream_t*) &pinger->stream.tcp, - bufs, - nbufs, - pinger_after_write)) { - FATAL("uv_write failed"); - } + ASSERT_NOT_NULL(req); + req->data = NULL; + ASSERT_EQ(0, uv_write(req, stream, bufs, nbufs, pinger_after_write)); puts("PING"); } @@ -112,23 +124,23 @@ static void pinger_read_cb(uv_stream_t* stream, ssize_t i; pinger_t* pinger; - pinger = (pinger_t*)stream->data; + pinger = (pinger_t*) stream->data; if (nread < 0) { - ASSERT(nread == UV_EOF); + ASSERT_EQ(nread, UV_EOF); puts("got EOF"); free(buf->base); - uv_close((uv_handle_t*)(&pinger->stream.tcp), pinger_on_close); + uv_close((uv_handle_t*) stream, pinger_on_close); return; } - /* Now we count the pings */ + /* Now we count the pongs */ for (i = 0; i < nread; i++) { - ASSERT(buf->base[i] == PING[pinger->state]); - pinger->state = (pinger->state + 1) % (sizeof(PING) - 1); + ASSERT_EQ(buf->base[i], pinger->pong[pinger->state]); + pinger->state = (pinger->state + 1) % strlen(pinger->pong); if (pinger->state != 0) continue; @@ -139,7 +151,7 @@ static void pinger_read_cb(uv_stream_t* stream, if (pinger->pongs < NUM_PINGS) { pinger_write_ping(pinger); } else { - uv_close((uv_handle_t*)(&pinger->stream.tcp), pinger_on_close); + uv_close((uv_handle_t*) stream, pinger_on_close); break; } } @@ -148,20 +160,54 @@ static void pinger_read_cb(uv_stream_t* stream, } -static void pinger_on_connect(uv_connect_t *req, int status) { - pinger_t *pinger = (pinger_t*)req->handle->data; +static void ponger_read_cb(uv_stream_t* stream, + ssize_t nread, + const uv_buf_t* buf) { + uv_buf_t writebuf; + uv_write_t* req; + int i; + + if (nread < 0) { + ASSERT_EQ(nread, UV_EOF); + + puts("got EOF"); + free(buf->base); + + uv_close((uv_handle_t*) stream, ponger_on_close); + + return; + } + + /* Echo back */ + for (i = 0; i < nread; i++) { + if (buf->base[i] == 'I') + buf->base[i] = 'O'; + } + + writebuf = uv_buf_init(buf->base, nread); + req = malloc(sizeof(*req)); + ASSERT_NOT_NULL(req); + req->data = buf->base; + ASSERT_EQ(0, uv_write(req, stream, &writebuf, 1, pinger_after_write)); +} + + +static void pinger_on_connect(uv_connect_t* req, int status) { + pinger_t* pinger = (pinger_t*) req->handle->data; pinger_on_connect_count++; - ASSERT(status == 0); + ASSERT_EQ(status, 0); - ASSERT(1 == uv_is_readable(req->handle)); - ASSERT(1 == uv_is_writable(req->handle)); - ASSERT(0 == uv_is_closing((uv_handle_t *) req->handle)); + 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)); pinger_write_ping(pinger); - uv_read_start((uv_stream_t*)(req->handle), alloc_cb, pinger_read_cb); + ASSERT_EQ(0, uv_read_start((uv_stream_t*) req->handle, + alloc_cb, + pinger_read_cb)); } @@ -169,20 +215,21 @@ static void pinger_on_connect(uv_connect_t *req, int status) { static void tcp_pinger_v6_new(int vectored_writes) { int r; struct sockaddr_in6 server_addr; - pinger_t *pinger; + pinger_t* pinger; - ASSERT(0 ==uv_ip6_addr("::1", TEST_PORT, &server_addr)); + ASSERT_EQ(0, uv_ip6_addr("::1", TEST_PORT, &server_addr)); pinger = malloc(sizeof(*pinger)); - ASSERT(pinger != NULL); + ASSERT_NOT_NULL(pinger); pinger->vectored_writes = vectored_writes; pinger->state = 0; pinger->pongs = 0; + pinger->pong = PING; /* 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(!r); + ASSERT_EQ(0, r); /* We are never doing multiple reads/connects at a time anyway, so these * handles can be pre-initialized. */ @@ -190,29 +237,30 @@ static void tcp_pinger_v6_new(int vectored_writes) { &pinger->stream.tcp, (const struct sockaddr*) &server_addr, pinger_on_connect); - ASSERT(!r); + ASSERT_EQ(0, r); /* Synchronous connect callbacks are not allowed. */ - ASSERT(pinger_on_connect_count == 0); + ASSERT_EQ(pinger_on_connect_count, 0); } static void tcp_pinger_new(int vectored_writes) { int r; struct sockaddr_in server_addr; - pinger_t *pinger; + pinger_t* pinger; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); + ASSERT_EQ(0, uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); pinger = malloc(sizeof(*pinger)); - ASSERT(pinger != NULL); + ASSERT_NOT_NULL(pinger); pinger->vectored_writes = vectored_writes; pinger->state = 0; pinger->pongs = 0; + pinger->pong = PING; /* 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(!r); + ASSERT_EQ(0, r); /* We are never doing multiple reads/connects at a time anyway, so these * handles can be pre-initialized. */ @@ -220,27 +268,28 @@ static void tcp_pinger_new(int vectored_writes) { &pinger->stream.tcp, (const struct sockaddr*) &server_addr, pinger_on_connect); - ASSERT(!r); + ASSERT_EQ(0, r); /* Synchronous connect callbacks are not allowed. */ - ASSERT(pinger_on_connect_count == 0); + ASSERT_EQ(pinger_on_connect_count, 0); } static void pipe_pinger_new(int vectored_writes) { int r; - pinger_t *pinger; + pinger_t* pinger; - pinger = (pinger_t*)malloc(sizeof(*pinger)); - ASSERT(pinger != NULL); + pinger = malloc(sizeof(*pinger)); + ASSERT_NOT_NULL(pinger); pinger->vectored_writes = vectored_writes; pinger->state = 0; pinger->pongs = 0; + pinger->pong = PING; /* 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(!r); + ASSERT_EQ(0, r); /* We are never doing multiple reads/connects at a time anyway, so these * handles can be pre-initialized. */ @@ -248,13 +297,86 @@ static void pipe_pinger_new(int vectored_writes) { pinger_on_connect); /* Synchronous connect callbacks are not allowed. */ - ASSERT(pinger_on_connect_count == 0); + ASSERT_EQ(pinger_on_connect_count, 0); +} + + +static void socketpair_pinger_new(int vectored_writes) { + pinger_t* pinger; + uv_os_sock_t fds[2]; + uv_tcp_t* ponger; + + pinger = malloc(sizeof(*pinger)); + ASSERT_NOT_NULL(pinger); + pinger->vectored_writes = vectored_writes; + pinger->state = 0; + pinger->pongs = 0; + pinger->pong = PONG; + + /* 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)); +#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)); + pinger->stream.pipe.data = pinger; + ASSERT_EQ(0, 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])); + + 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)); } +static void pipe2_pinger_new(int vectored_writes) { + uv_file fds[2]; + pinger_t* pinger; + 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_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])); + + pinger = malloc(sizeof(*pinger)); + ASSERT_NOT_NULL(pinger); + pinger->vectored_writes = 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])); + 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)); +} + static int run_ping_pong_test(void) { uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(completed_pingers == 1); + ASSERT_EQ(completed_pingers, 1); MAKE_VALGRIND_HAPPY(); return 0; @@ -263,12 +385,20 @@ static int run_ping_pong_test(void) { TEST_IMPL(tcp_ping_pong) { tcp_pinger_new(0); + run_ping_pong_test(); + + completed_pingers = 0; + socketpair_pinger_new(0); return run_ping_pong_test(); } TEST_IMPL(tcp_ping_pong_vec) { tcp_pinger_new(1); + run_ping_pong_test(); + + completed_pingers = 0; + socketpair_pinger_new(1); return run_ping_pong_test(); } @@ -291,11 +421,19 @@ TEST_IMPL(tcp6_ping_pong_vec) { TEST_IMPL(pipe_ping_pong) { pipe_pinger_new(0); + run_ping_pong_test(); + + completed_pingers = 0; + pipe2_pinger_new(0); return run_ping_pong_test(); } TEST_IMPL(pipe_ping_pong_vec) { pipe_pinger_new(1); + run_ping_pong_test(); + + completed_pingers = 0; + pipe2_pinger_new(1); return run_ping_pong_test(); } diff --git a/test/test-pipe-bind-error.c b/test/test-pipe-bind-error.c index 9cf9316..ce35052 100644 --- a/test/test-pipe-bind-error.c +++ b/test/test-pipe-bind-error.c @@ -36,7 +36,7 @@ static int close_cb_called = 0; static void close_cb(uv_handle_t* handle) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); close_cb_called++; } diff --git a/test/test-pipe-connect-error.c b/test/test-pipe-connect-error.c index ebb2a6c..0f1e2b1 100644 --- a/test/test-pipe-connect-error.c +++ b/test/test-pipe-connect-error.c @@ -37,7 +37,7 @@ static int connect_cb_called = 0; static void close_cb(uv_handle_t* handle) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); close_cb_called++; } diff --git a/test/test-pipe-connect-prepare.c b/test/test-pipe-connect-prepare.c index a86e728..08b57cb 100644 --- a/test/test-pipe-connect-prepare.c +++ b/test/test-pipe-connect-prepare.c @@ -42,7 +42,7 @@ static uv_connect_t conn_req; static void close_cb(uv_handle_t* handle) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); close_cb_called++; } diff --git a/test/test-pipe-getsockname.c b/test/test-pipe-getsockname.c index 48ee400..79db8eb 100644 --- a/test/test-pipe-getsockname.c +++ b/test/test-pipe-getsockname.c @@ -96,7 +96,7 @@ TEST_IMPL(pipe_getsockname) { int r; loop = uv_default_loop(); - ASSERT(loop != NULL); + ASSERT_NOT_NULL(loop); r = uv_pipe_init(loop, &pipe_server, 0); ASSERT(r == 0); @@ -225,7 +225,9 @@ TEST_IMPL(pipe_getsockname_blocking) { ASSERT(r != -1); r = uv_pipe_open(&pipe_client, readfd); ASSERT(r == 0); - r = uv_read_start((uv_stream_t*)&pipe_client, NULL, NULL); + r = uv_read_start((uv_stream_t*) &pipe_client, + (uv_alloc_cb) abort, + (uv_read_cb) abort); ASSERT(r == 0); Sleep(100); r = uv_read_stop((uv_stream_t*)&pipe_client); @@ -236,7 +238,9 @@ TEST_IMPL(pipe_getsockname_blocking) { ASSERT(r == 0); ASSERT(len1 == 0); /* It's an annonymous pipe. */ - r = uv_read_start((uv_stream_t*)&pipe_client, NULL, NULL); + r = uv_read_start((uv_stream_t*)&pipe_client, + (uv_alloc_cb) abort, + (uv_read_cb) abort); ASSERT(r == 0); Sleep(100); diff --git a/test/test-pipe-server-close.c b/test/test-pipe-server-close.c index ea9977d..25305b3 100644 --- a/test/test-pipe-server-close.c +++ b/test/test-pipe-server-close.c @@ -68,7 +68,7 @@ TEST_IMPL(pipe_server_close) { int r; loop = uv_default_loop(); - ASSERT(loop != NULL); + ASSERT_NOT_NULL(loop); r = uv_pipe_init(loop, &pipe_server, 0); ASSERT(r == 0); diff --git a/test/test-pipe-set-non-blocking.c b/test/test-pipe-set-non-blocking.c index fcc9fc0..827e726 100644 --- a/test/test-pipe-set-non-blocking.c +++ b/test/test-pipe-set-non-blocking.c @@ -16,41 +16,54 @@ #include "uv.h" #include "task.h" -#ifdef _WIN32 - -TEST_IMPL(pipe_set_non_blocking) { - RETURN_SKIP("Test not implemented on Windows."); -} - -#else /* !_WIN32 */ - -#include -#include -#include -#include -#include -#include +#include /* memset */ +#ifndef _WIN32 +#include /* close */ +#endif struct thread_ctx { uv_barrier_t barrier; - int fd; + uv_file fd; }; static void thread_main(void* arg) { struct thread_ctx* ctx; + uv_fs_t req; + uv_buf_t bufs[1]; char buf[4096]; ssize_t n; + int uv_errno; + + bufs[0] = uv_buf_init(buf, sizeof(buf)); ctx = arg; uv_barrier_wait(&ctx->barrier); - do - n = read(ctx->fd, buf, sizeof(buf)); - while (n > 0 || (n == -1 && errno == EINTR)); + uv_sleep(100); /* make sure we are forcing the writer to block a bit */ + do { + uv_errno = uv_fs_read(NULL, &req, ctx->fd, bufs, 1, -1, NULL); + n = req.result; + uv_fs_req_cleanup(&req); + } while (n > 0 || (n == -1 && uv_errno == UV_EINTR)); ASSERT(n == 0); } + +#ifdef _WIN32 +static void write_cb(uv_write_t* req, int status) { + ASSERT(status == 0); + req->handle = NULL; /* signal completion of write_cb */ +} +#endif + +#ifdef _WIN32 +#define NWRITES (10 << 16) +#else +#define NWRITES (10 << 20) +#endif + + TEST_IMPL(pipe_set_non_blocking) { struct thread_ctx ctx; uv_pipe_t pipe_handle; @@ -58,15 +71,19 @@ TEST_IMPL(pipe_set_non_blocking) { size_t nwritten; char data[4096]; uv_buf_t buf; - int fd[2]; + uv_file fd[2]; int n; +#ifdef _WIN32 + uv_write_t write_req; +#endif ASSERT(0 == uv_pipe_init(uv_default_loop(), &pipe_handle, 0)); - ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, fd)); - ASSERT(0 == uv_pipe_open(&pipe_handle, fd[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)); + fd[1] = -1; /* fd[1] is owned by pipe_handle now. */ - ctx.fd = fd[1]; + ctx.fd = fd[0]; ASSERT(0 == uv_barrier_init(&ctx.barrier, 2)); ASSERT(0 == uv_thread_create(&thread, thread_main, &ctx)); uv_barrier_wait(&ctx.barrier); @@ -76,11 +93,20 @@ TEST_IMPL(pipe_set_non_blocking) { memset(data, '.', sizeof(data)); nwritten = 0; - while (nwritten < 10 << 20) { + while (nwritten < NWRITES) { /* The stream is in blocking mode so uv_try_write() should always succeed * with the exact number of bytes that we wanted written. */ 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_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_NULL(write_req.handle); /* check for signaled completion of write_cb */ + n = buf.len; +#endif ASSERT(n == sizeof(data)); nwritten += n; } @@ -89,11 +115,14 @@ TEST_IMPL(pipe_set_non_blocking) { ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); ASSERT(0 == uv_thread_join(&thread)); - ASSERT(0 == close(fd[1])); /* fd[0] is closed by uv_close(). */ +#ifdef _WIN32 + ASSERT(0 == _close(fd[0])); /* fd[1] is closed by uv_close(). */ +#else + ASSERT(0 == close(fd[0])); /* fd[1] is closed by uv_close(). */ +#endif + fd[0] = -1; uv_barrier_destroy(&ctx.barrier); MAKE_VALGRIND_HAPPY(); return 0; } - -#endif /* !_WIN32 */ diff --git a/test/test-platform-output.c b/test/test-platform-output.c index 65cfa1b..5827dca 100644 --- a/test/test-platform-output.c +++ b/test/test-platform-output.c @@ -25,6 +25,11 @@ 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; @@ -36,6 +41,7 @@ TEST_IMPL(platform_output) { uv_interface_address_t* interfaces; uv_passwd_t pwd; uv_utsname_t uname; + unsigned par; int count; int i; int err; @@ -83,6 +89,10 @@ 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); + err = uv_cpu_info(&cpus, &count); #if defined(__CYGWIN__) || defined(__MSYS__) ASSERT(err == UV_ENOSYS); @@ -150,6 +160,7 @@ TEST_IMPL(platform_output) { printf(" username: %s\n", pwd.username); printf(" shell: %s\n", pwd.shell); printf(" home directory: %s\n", pwd.homedir); + uv_os_free_passwd(&pwd); pid = uv_os_getpid(); ASSERT(pid > 0); diff --git a/test/test-poll-multiple-handles.c b/test/test-poll-multiple-handles.c new file mode 100644 index 0000000..fc2205d --- /dev/null +++ b/test/test-poll-multiple-handles.c @@ -0,0 +1,99 @@ +/* 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 + +#ifndef _WIN32 +# include +# include +# include +#endif + +#include "uv.h" +#include "task.h" + + +static int close_cb_called = 0; + + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + +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); +} + +TEST_IMPL(poll_multiple_handles) { + uv_os_sock_t sock; + uv_poll_t first_poll_handle, second_poll_handle; + +#ifdef _WIN32 + { + struct WSAData wsa_data; + int r = WSAStartup(MAKEWORD(2, 2), &wsa_data); + ASSERT(r == 0); + } +#endif + + sock = socket(AF_INET, SOCK_STREAM, 0); +#ifdef _WIN32 + ASSERT(sock != INVALID_SOCKET); +#else + ASSERT(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(0 == uv_poll_start(&first_poll_handle, UV_READABLE, poll_cb)); + + /* We may not start polling while another polling handle is active + * on that fd. + */ +#ifndef _WIN32 + /* 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); +#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)); + + /* 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); + 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); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/test/test-poll.c b/test/test-poll.c index b330844..3bc422d 100644 --- a/test/test-poll.c +++ b/test/test-poll.c @@ -147,7 +147,7 @@ static connection_context_t* create_connection_context( connection_context_t* context; context = (connection_context_t*) malloc(sizeof *context); - ASSERT(context != NULL); + ASSERT_NOT_NULL(context); context->sock = sock; context->is_server_connection = is_server_connection; @@ -464,7 +464,7 @@ static server_context_t* create_server_context( server_context_t* context; context = (server_context_t*) malloc(sizeof *context); - ASSERT(context != NULL); + ASSERT_NOT_NULL(context); context->sock = sock; context->connections = 0; @@ -592,10 +592,19 @@ static void start_poll_test(void) { MAKE_VALGRIND_HAPPY(); } - + +/* Issuing a shutdown() on IBM i PASE with parameter SHUT_WR + * also sends a normal close sequence to the partner program. + * This leads to timing issues and ECONNRESET failures in the + * test 'poll_duplex' and 'poll_unidirectional'. + * + * https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_74/apis/shutdn.htm + */ TEST_IMPL(poll_duplex) { #if defined(NO_SELF_CONNECT) RETURN_SKIP(NO_SELF_CONNECT); +#elif defined(__PASE__) + RETURN_SKIP("API shutdown() may lead to timing issue on IBM i PASE"); #endif test_mode = DUPLEX; start_poll_test(); @@ -606,6 +615,8 @@ TEST_IMPL(poll_duplex) { TEST_IMPL(poll_unidirectional) { #if defined(NO_SELF_CONNECT) RETURN_SKIP(NO_SELF_CONNECT); +#elif defined(__PASE__) + RETURN_SKIP("API shutdown() may lead to timing issue on IBM i PASE"); #endif test_mode = UNIDIRECTIONAL; start_poll_test(); diff --git a/test/test-process-title-threadsafe.c b/test/test-process-title-threadsafe.c index 2f12b74..927643c 100644 --- a/test/test-process-title-threadsafe.c +++ b/test/test-process-title-threadsafe.c @@ -39,15 +39,27 @@ static const char* titles[] = { }; static void getter_thread_body(void* arg) { + uv_sem_t* getter_sem; char buffer[512]; + size_t len; - for (;;) { + getter_sem = arg; + + while (UV_EAGAIN == uv_sem_trywait(getter_sem)) { ASSERT(0 == 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 + * back a title that's shorter than what we submitted. + */ + len = strlen(buffer); + ASSERT_GT(len, 0); + ASSERT( - 0 == strcmp(buffer, titles[0]) || - 0 == strcmp(buffer, titles[1]) || - 0 == strcmp(buffer, titles[2]) || - 0 == strcmp(buffer, titles[3])); + 0 == strncmp(buffer, titles[0], len) || + 0 == strncmp(buffer, titles[1], len) || + 0 == strncmp(buffer, titles[2], len) || + 0 == strncmp(buffer, titles[3], len)); uv_sleep(0); } @@ -69,6 +81,7 @@ static void setter_thread_body(void* arg) { TEST_IMPL(process_title_threadsafe) { uv_thread_t setter_threads[4]; uv_thread_t getter_thread; + uv_sem_t getter_sem; int i; #if defined(__sun) || defined(__CYGWIN__) || defined(__MSYS__) || \ @@ -77,7 +90,10 @@ TEST_IMPL(process_title_threadsafe) { #endif ASSERT(0 == uv_set_process_title(titles[0])); - ASSERT(0 == uv_thread_create(&getter_thread, getter_thread_body, NULL)); + + ASSERT_EQ(0, uv_sem_init(&getter_sem, 0)); + ASSERT_EQ(0, + 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)); @@ -85,5 +101,9 @@ TEST_IMPL(process_title_threadsafe) { for (i = 0; i < (int) ARRAY_SIZE(setter_threads); i++) ASSERT(0 == uv_thread_join(&setter_threads[i])); + uv_sem_post(&getter_sem); + ASSERT_EQ(0, uv_thread_join(&getter_thread)); + uv_sem_destroy(&getter_sem); + return 0; } diff --git a/test/test-readable-on-eof.c b/test/test-readable-on-eof.c new file mode 100644 index 0000000..68e8454 --- /dev/null +++ b/test/test-readable-on-eof.c @@ -0,0 +1,111 @@ +/* Copyright the 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" + +static uv_loop_t loop; +static uv_tcp_t tcp_client; +static uv_connect_t connect_req; +static uv_write_t write_req; +static char close_me_cmd[] = {'Q', 'S'}; + +static int connect_cb_called; +static int read_cb_called; +static int write_cb_called; +static int close_cb_called; + +static void write_cb(uv_write_t* req, int status) { + write_cb_called++; + ASSERT_EQ(status, 0); +} + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[64]; + buf->base = slab; + buf->len = sizeof(slab); +} + +static void close_cb(uv_handle_t* handle) { + close_cb_called++; +} + +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); + + 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); + } else { + r = uv_read_start((uv_stream_t*) &tcp_client, alloc_cb, read_cb); + ASSERT_EQ(r, 0); + } +} + +static void connect_cb(uv_connect_t* req, int status) { + int r; + uv_buf_t close_me; + + connect_cb_called++; + ASSERT_EQ(status, 0); + + read_cb((uv_stream_t*) &tcp_client, UV_EOF, NULL); + + close_me = uv_buf_init(close_me_cmd, sizeof(close_me_cmd)); + + r = uv_write(&write_req, + (uv_stream_t*) &tcp_client, + &close_me, + 1, + write_cb); + + ASSERT_EQ(r, 0); +} + +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_EQ(uv_tcp_connect(&connect_req, + &tcp_client, + (const struct sockaddr*) &sa, + connect_cb), + 0); + + ASSERT_EQ(uv_run(&loop, UV_RUN_DEFAULT), 0); + + ASSERT_EQ(connect_cb_called, 1); + ASSERT_EQ(read_cb_called, 3); + ASSERT_EQ(write_cb_called, 1); + ASSERT_EQ(close_cb_called, 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/test/test-ref.c b/test/test-ref.c index 05728c8..d24ea4a 100644 --- a/test/test-ref.c +++ b/test/test-ref.c @@ -154,7 +154,7 @@ TEST_IMPL(check_ref) { static void prepare_cb(uv_prepare_t* h) { - ASSERT(h != NULL); + ASSERT_NOT_NULL(h); uv_unref((uv_handle_t*)h); } diff --git a/test/test-shutdown-eof.c b/test/test-shutdown-eof.c index 9f95e75..0abab91 100644 --- a/test/test-shutdown-eof.c +++ b/test/test-shutdown-eof.c @@ -89,7 +89,13 @@ static void connect_cb(uv_connect_t *req, int status) { ASSERT(req == &connect_req); /* Start reading from our connection so we can receive the EOF. */ - uv_read_start((uv_stream_t*)&tcp, alloc_cb, read_cb); + ASSERT_EQ(0, 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)); + ASSERT_EQ(UV_EINVAL, uv_read_start(NULL, alloc_cb, read_cb)); + ASSERT_EQ(UV_EINVAL, uv_read_start((uv_stream_t*)&tcp, NULL, read_cb)); + ASSERT_EQ(UV_EINVAL, uv_read_start((uv_stream_t*)&tcp, alloc_cb, NULL)); /* * Write the letter 'Q' to gracefully kill the echo-server. This will not diff --git a/test/test-shutdown-simultaneous.c b/test/test-shutdown-simultaneous.c new file mode 100644 index 0000000..7de3bd4 --- /dev/null +++ b/test/test-shutdown-simultaneous.c @@ -0,0 +1,135 @@ +/* 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" +#include +#include + +static uv_tcp_t tcp; +static uv_connect_t connect_req; +static uv_shutdown_t shutdown_req; +static uv_buf_t qbuf; +static int got_q; +static int got_eof; +static int called_connect_cb; +static int called_shutdown_cb; +static int called_tcp_close_cb; + + +static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { + buf->base = malloc(size); + buf->len = size; +} + + +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); +} + + +static void read_cb(uv_stream_t* t, ssize_t nread, const uv_buf_t* buf) { + ASSERT_PTR_EQ((uv_tcp_t*)t, &tcp); + + if (nread == 0) { + free(buf->base); + return; + } + + if (!got_q) { + ASSERT_EQ(nread, 4); + ASSERT_EQ(got_eof, 0); + ASSERT_MEM_EQ(buf->base, "QQSS", 4); + free(buf->base); + got_q = 1; + puts("got QQSS"); + /* Shutdown our end of the connection simultaneously */ + uv_shutdown(&shutdown_req, (uv_stream_t*) &tcp, shutdown_cb); + called_shutdown_cb++; + } else { + ASSERT_EQ(nread, UV_EOF); + if (buf->base) { + free(buf->base); + } + got_eof = 1; + puts("got EOF"); + } +} + + +static void connect_cb(uv_connect_t *req, int status) { + ASSERT_EQ(status, 0); + 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)); + + /* Check error handling. */ + ASSERT_EQ(UV_EALREADY, uv_read_start((uv_stream_t*)&tcp, alloc_cb, read_cb)); + ASSERT_EQ(UV_EINVAL, uv_read_start(NULL, alloc_cb, read_cb)); + ASSERT_EQ(UV_EINVAL, uv_read_start((uv_stream_t*)&tcp, NULL, read_cb)); + ASSERT_EQ(UV_EINVAL, uv_read_start((uv_stream_t*)&tcp, alloc_cb, NULL)); + + /* + * Write the letter 'Q' and 'QSS` to gracefully kill the echo-server. This + * will not effect our connection. + */ + ASSERT_EQ(qbuf.len, uv_try_write((uv_stream_t*) &tcp, &qbuf, 1)); + + called_connect_cb++; + ASSERT_EQ(called_shutdown_cb, 0); +} + + +/* + * This test has a client which connects to the echo_server and immediately + * issues a shutdown. We check that this does not cause libuv to hang. + */ +TEST_IMPL(shutdown_simultaneous) { + struct sockaddr_in server_addr; + int r; + + qbuf.base = "QQSS"; + qbuf.len = 4; + + ASSERT_EQ(0, uv_ip4_addr("127.0.0.1", TEST_PORT, &server_addr)); + r = uv_tcp_init(uv_default_loop(), &tcp); + ASSERT_EQ(r, 0); + + r = uv_tcp_connect(&connect_req, + &tcp, + (const struct sockaddr*) &server_addr, + connect_cb); + ASSERT_EQ(r, 0); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + ASSERT_EQ(called_connect_cb, 1); + ASSERT_EQ(called_shutdown_cb, 1); + ASSERT_EQ(got_eof, 1); + ASSERT_EQ(got_q, 1); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/test/test-signal-multiple-loops.c b/test/test-signal-multiple-loops.c index 4281d23..7d61ff6 100644 --- a/test/test-signal-multiple-loops.c +++ b/test/test-signal-multiple-loops.c @@ -170,7 +170,7 @@ static void loop_creating_worker(void* context) { int r; loop = malloc(sizeof(*loop)); - ASSERT(loop != NULL); + ASSERT_NOT_NULL(loop); ASSERT(0 == uv_loop_init(loop)); r = uv_signal_init(loop, &signal); @@ -199,6 +199,11 @@ TEST_IMPL(signal_multiple_loops) { though it is supposed to be blocking. Also the test hangs during thread setup occasionally. */ RETURN_SKIP("FIXME: This test needs more investigation on Cygwin"); +#endif +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + // See https://github.com/libuv/libuv/issues/2859 + RETURN_SKIP("QEMU's signal emulation code is notoriously tricky"); #endif uv_thread_t loop_creating_threads[NUM_LOOP_CREATING_THREADS]; uv_thread_t signal_handling_threads[NUM_SIGNAL_HANDLING_THREADS]; diff --git a/test/test-signal-pending-on-close.c b/test/test-signal-pending-on-close.c index 23b9ec8..428a97e 100644 --- a/test/test-signal-pending-on-close.c +++ b/test/test-signal-pending-on-close.c @@ -49,7 +49,7 @@ static void close_cb(uv_handle_t *handle) { static void write_cb(uv_write_t* req, int status) { - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status == UV_EPIPE); free(buf); uv_close((uv_handle_t *) &pipe_hdl, close_cb); @@ -76,7 +76,7 @@ TEST_IMPL(signal_pending_on_close) { /* Write data large enough so it needs loop iteration */ buf = malloc(1<<24); - ASSERT(buf != NULL); + ASSERT_NOT_NULL(buf); memset(buf, '.', 1<<24); buffer = uv_buf_init(buf, 1<<24); diff --git a/test/test-spawn.c b/test/test-spawn.c index 2b7f5d5..dfd5458 100644 --- a/test/test-spawn.c +++ b/test/test-spawn.c @@ -29,11 +29,9 @@ #include #ifdef _WIN32 -# if defined(__MINGW32__) -# include -# endif # include # include + typedef BOOL (WINAPI *sCompareObjectHandles)(_In_ HANDLE, _In_ HANDLE); #else # include # include @@ -49,9 +47,7 @@ static char exepath[1024]; static size_t exepath_size = 1024; static char* args[5]; static int no_term_signal; -#ifndef _WIN32 static int timer_counter; -#endif static uv_tcp_t tcp_server; #define OUTPUT_SIZE 1024 @@ -71,7 +67,7 @@ static void exit_cb(uv_process_t* process, exit_cb_called++; ASSERT(exit_status == 1); ASSERT(term_signal == 0); - uv_close((uv_handle_t*)process, close_cb); + uv_close((uv_handle_t*) process, close_cb); } @@ -104,7 +100,7 @@ static void kill_cb(uv_process_t* process, #else ASSERT(no_term_signal || term_signal == SIGTERM); #endif - uv_close((uv_handle_t*)process, close_cb); + uv_close((uv_handle_t*) process, close_cb); /* * Sending signum == 0 should check if the @@ -135,22 +131,25 @@ static void on_read(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { output_used += nread; } else if (nread < 0) { ASSERT(nread == UV_EOF); - uv_close((uv_handle_t*)tcp, close_cb); + uv_close((uv_handle_t*) tcp, close_cb); } } -#ifndef _WIN32 static void on_read_once(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { uv_read_stop(tcp); on_read(tcp, nread, buf); } -#endif static void write_cb(uv_write_t* req, int status) { ASSERT(status == 0); - uv_close((uv_handle_t*)req->handle, close_cb); + uv_close((uv_handle_t*) req->handle, close_cb); +} + + +static void write_null_cb(uv_write_t* req, int status) { + ASSERT(status == 0); } @@ -172,16 +171,14 @@ static void init_process_options(char* test, uv_exit_cb exit_cb) { static void timer_cb(uv_timer_t* handle) { - uv_process_kill(&process, /* SIGTERM */ 15); - uv_close((uv_handle_t*)handle, close_cb); + uv_process_kill(&process, SIGTERM); + uv_close((uv_handle_t*) handle, close_cb); } -#ifndef _WIN32 static void timer_counter_cb(uv_timer_t* handle) { ++timer_counter; } -#endif TEST_IMPL(spawn_fails) { @@ -236,13 +233,14 @@ TEST_IMPL(spawn_empty_env) { char* env[1]; /* The autotools dynamic library build requires the presence of - * DYLD_LIBARY_PATH (macOS) or LD_LIBRARY_PATH (other Unices) + * DYLD_LIBARY_PATH (macOS) or LD_LIBRARY_PATH/LIBPATH (other Unices) * in the environment, but of course that doesn't work with * the empty environment that we're testing here. */ if (NULL != getenv("DYLD_LIBRARY_PATH") || - NULL != getenv("LD_LIBRARY_PATH")) { - RETURN_SKIP("doesn't work with DYLD_LIBRARY_PATH/LD_LIBRARY_PATH"); + NULL != getenv("LD_LIBRARY_PATH") || + NULL != getenv("LIBPATH")) { + RETURN_SKIP("doesn't work with DYLD_LIBRARY_PATH/LD_LIBRARY_PATH/LIBPATH"); } init_process_options("spawn_helper1", exit_cb); @@ -290,7 +288,7 @@ TEST_IMPL(spawn_stdout) { options.stdio = stdio; options.stdio[0].flags = UV_IGNORE; options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio[1].data.stream = (uv_stream_t*) &out; options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, &options); @@ -594,9 +592,9 @@ TEST_IMPL(spawn_stdin) { uv_pipe_init(uv_default_loop(), &in, 0); options.stdio = stdio; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; - options.stdio[0].data.stream = (uv_stream_t*)∈ + options.stdio[0].data.stream = (uv_stream_t*) ∈ options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio[1].data.stream = (uv_stream_t*) &out; options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, &options); @@ -604,7 +602,7 @@ TEST_IMPL(spawn_stdin) { buf.base = buffer; buf.len = sizeof(buffer); - r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb); + r = uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_cb); ASSERT(r == 0); r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); @@ -635,7 +633,7 @@ TEST_IMPL(spawn_stdio_greater_than_3) { options.stdio[1].flags = UV_IGNORE; options.stdio[2].flags = UV_IGNORE; options.stdio[3].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - options.stdio[3].data.stream = (uv_stream_t*)&pipe; + options.stdio[3].data.stream = (uv_stream_t*) &pipe; options.stdio_count = 4; r = uv_spawn(uv_default_loop(), &process, &options); @@ -676,7 +674,7 @@ int spawn_tcp_server_helper(void) { /* 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); + r = uv_listen((uv_stream_t*) &tcp, SOMAXCONN, NULL); ASSERT(r == 0); return 1; @@ -702,10 +700,10 @@ TEST_IMPL(spawn_tcp_server) { r = uv_tcp_bind(&tcp_server, (const struct sockaddr*) &addr, 0); ASSERT(r == 0); #ifdef _WIN32 - r = uv_fileno((uv_handle_t*)&tcp_server, &handle); + 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); + r = uv_fileno((uv_handle_t*) &tcp_server, &fd); #endif ASSERT(r == 0); ASSERT(fd > 0); @@ -832,7 +830,7 @@ TEST_IMPL(spawn_detached) { r = uv_spawn(uv_default_loop(), &process, &options); ASSERT(r == 0); - uv_unref((uv_handle_t*)&process); + uv_unref((uv_handle_t*) &process); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); @@ -844,7 +842,7 @@ TEST_IMPL(spawn_detached) { r = uv_kill(process.pid, 0); ASSERT(r == 0); - r = uv_kill(process.pid, 15); + r = uv_kill(process.pid, SIGTERM); ASSERT(r == 0); MAKE_VALGRIND_HAPPY(); @@ -873,11 +871,11 @@ TEST_IMPL(spawn_and_kill_with_std) { options.stdio = stdio; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; - options.stdio[0].data.stream = (uv_stream_t*)∈ + options.stdio[0].data.stream = (uv_stream_t*) ∈ options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio[1].data.stream = (uv_stream_t*) &out; options.stdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - options.stdio[2].data.stream = (uv_stream_t*)&err; + options.stdio[2].data.stream = (uv_stream_t*) &err; options.stdio_count = 3; r = uv_spawn(uv_default_loop(), &process, &options); @@ -924,9 +922,9 @@ TEST_IMPL(spawn_and_ping) { uv_pipe_init(uv_default_loop(), &in, 0); options.stdio = stdio; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; - options.stdio[0].data.stream = (uv_stream_t*)∈ + options.stdio[0].data.stream = (uv_stream_t*) ∈ options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio[1].data.stream = (uv_stream_t*) &out; options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, &options); @@ -938,10 +936,10 @@ TEST_IMPL(spawn_and_ping) { r = uv_process_kill(&process, 0); ASSERT(r == 0); - r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb); + r = uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_cb); ASSERT(r == 0); - r = uv_read_start((uv_stream_t*)&out, on_alloc, on_read); + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); ASSERT(r == 0); ASSERT(exit_cb_called == 0); @@ -971,9 +969,9 @@ TEST_IMPL(spawn_same_stdout_stderr) { uv_pipe_init(uv_default_loop(), &in, 0); options.stdio = stdio; options.stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; - options.stdio[0].data.stream = (uv_stream_t*)∈ + options.stdio[0].data.stream = (uv_stream_t*) ∈ options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio[1].data.stream = (uv_stream_t*) &out; options.stdio_count = 2; r = uv_spawn(uv_default_loop(), &process, &options); @@ -985,10 +983,10 @@ TEST_IMPL(spawn_same_stdout_stderr) { r = uv_process_kill(&process, 0); ASSERT(r == 0); - r = uv_write(&write_req, (uv_stream_t*)&in, &buf, 1, write_cb); + r = uv_write(&write_req, (uv_stream_t*) &in, &buf, 1, write_cb); ASSERT(r == 0); - r = uv_read_start((uv_stream_t*)&out, on_alloc, on_read); + r = uv_read_start((uv_stream_t*) &out, on_alloc, on_read); ASSERT(r == 0); ASSERT(exit_cb_called == 0); @@ -1076,7 +1074,7 @@ TEST_IMPL(kill) { ASSERT(r == 0); /* Kill the process. */ - r = uv_kill(process.pid, /* SIGTERM */ 15); + r = uv_kill(process.pid, SIGTERM); ASSERT(r == 0); r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); @@ -1104,7 +1102,7 @@ TEST_IMPL(spawn_detect_pipe_name_collisions_on_windows) { options.stdio = stdio; options.stdio[0].flags = UV_IGNORE; options.stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - options.stdio[1].data.stream = (uv_stream_t*)&out; + options.stdio[1].data.stream = (uv_stream_t*) &out; options.stdio_count = 2; /* Create a pipe that'll cause a collision. */ @@ -1178,7 +1176,7 @@ TEST_IMPL(argument_escaping) { WCHAR* non_verbatim_output; test_output = calloc(count, sizeof(WCHAR*)); - ASSERT(test_output != NULL); + ASSERT_NOT_NULL(test_output); for (i = 0; i < count; ++i) { test_output[i] = calloc(2 * (wcslen(test_str[i]) + 2), sizeof(WCHAR)); quote_cmd_arg(test_str[i], test_output[i]); @@ -1187,7 +1185,7 @@ TEST_IMPL(argument_escaping) { total_size += wcslen(test_output[i]) + 1; } command_line = calloc(total_size + 1, sizeof(WCHAR)); - ASSERT(command_line != NULL); + ASSERT_NOT_NULL(command_line); for (i = 0; i < count; ++i) { wcscat(command_line, test_output[i]); wcscat(command_line, L" "); @@ -1327,9 +1325,8 @@ TEST_IMPL(environment_creation) { found = 1; } } - if (prev) { /* verify sort order -- requires Vista */ -#if _WIN32_WINNT >= 0x0600 && \ - (!defined(__MINGW32__) || defined(__MINGW64_VERSION_MAJOR)) + if (prev) { /* verify sort order */ +#if !defined(__MINGW32__) || defined(__MINGW64_VERSION_MAJOR) ASSERT(CompareStringOrdinal(prev, -1, str, -1, TRUE) == 1); #endif } @@ -1354,7 +1351,7 @@ TEST_IMPL(spawn_with_an_odd_path) { char newpath[2048]; char *path = getenv("PATH"); - ASSERT(path != NULL); + ASSERT_NOT_NULL(path); snprintf(newpath, 2048, ";.;%s", path); SetEnvironmentVariable("PATH", newpath); @@ -1388,7 +1385,7 @@ TEST_IMPL(spawn_setuid_setgid) { /* become the "nobody" user. */ pw = getpwnam("nobody"); - ASSERT(pw != NULL); + ASSERT_NOT_NULL(pw); options.uid = pw->pw_uid; options.gid = pw->pw_gid; snprintf(uidstr, sizeof(uidstr), "%d", pw->pw_uid); @@ -1426,7 +1423,7 @@ TEST_IMPL(spawn_setuid_fails) { if (uid == 0) { struct passwd* pw; pw = getpwnam("nobody"); - ASSERT(pw != NULL); + ASSERT_NOT_NULL(pw); ASSERT(0 == setgid(pw->pw_gid)); ASSERT(0 == setuid(pw->pw_uid)); } @@ -1451,7 +1448,7 @@ TEST_IMPL(spawn_setuid_fails) { options.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS; r = uv_spawn(uv_default_loop(), &process, &options); -#if defined(__CYGWIN__) || defined(__PASE__) +#if defined(__CYGWIN__) ASSERT(r == UV_EINVAL); #else ASSERT(r == UV_EPERM); @@ -1477,7 +1474,7 @@ TEST_IMPL(spawn_setgid_fails) { if (uid == 0) { struct passwd* pw; pw = getpwnam("nobody"); - ASSERT(pw != NULL); + ASSERT_NOT_NULL(pw); ASSERT(0 == setgid(pw->pw_gid)); ASSERT(0 == setuid(pw->pw_uid)); } @@ -1496,7 +1493,7 @@ TEST_IMPL(spawn_setgid_fails) { #endif r = uv_spawn(uv_default_loop(), &process, &options); -#if defined(__CYGWIN__) || defined(__MVS__) || defined(__PASE__) +#if defined(__CYGWIN__) || defined(__MVS__) ASSERT(r == UV_EINVAL); #else ASSERT(r == UV_EPERM); @@ -1578,17 +1575,27 @@ TEST_IMPL(spawn_auto_unref) { } -#ifndef _WIN32 TEST_IMPL(spawn_fs_open) { - int fd; + int r; + uv_os_fd_t fd; + uv_os_fd_t dup_fd; uv_fs_t fs_req; uv_pipe_t in; uv_write_t write_req; + uv_write_t write_req2; uv_buf_t buf; uv_stdio_container_t stdio[1]; +#ifdef _WIN32 + const char dev_null[] = "NUL"; + HMODULE kernelbase_module; + sCompareObjectHandles pCompareObjectHandles; /* function introduced in Windows 10 */ +#else + const char dev_null[] = "/dev/null"; +#endif - fd = uv_fs_open(NULL, &fs_req, "/dev/null", O_RDWR, 0, NULL); - ASSERT(fd >= 0); + r = uv_fs_open(NULL, &fs_req, dev_null, O_RDWR, 0, NULL); + ASSERT(r != -1); + fd = uv_get_osfhandle((uv_file) fs_req.result); uv_fs_req_cleanup(&fs_req); init_process_options("spawn_helper8", exit_cb); @@ -1600,13 +1607,28 @@ TEST_IMPL(spawn_fs_open) { options.stdio[0].data.stream = (uv_stream_t*) ∈ options.stdio_count = 1; + /* make an inheritable copy */ +#ifdef _WIN32 + ASSERT(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)); +#else + dup_fd = dup(fd); +#endif + ASSERT(0 == 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_cb)); + ASSERT(0 == 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(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(0 == uv_fs_close(NULL, &fs_req, fd, NULL)); + ASSERT(0 == uv_fs_close(NULL, &fs_req, r, NULL)); ASSERT(exit_cb_called == 1); ASSERT(close_cb_called == 2); /* One for `in`, one for process */ @@ -1614,17 +1636,20 @@ TEST_IMPL(spawn_fs_open) { MAKE_VALGRIND_HAPPY(); return 0; } -#endif /* !_WIN32 */ -#ifndef _WIN32 TEST_IMPL(closed_fd_events) { uv_stdio_container_t stdio[3]; uv_pipe_t pipe_handle; - int fd[2]; + uv_fs_t req; + uv_buf_t bufs[1]; + uv_file fd[2]; + bufs[0] = uv_buf_init("", 1); /* create a pipe and share it with a child process */ - ASSERT(0 == pipe(fd)); + ASSERT(0 == uv_pipe(fd, 0, 0)); + ASSERT(fd[0] > 2); + ASSERT(fd[1] > 2); /* spawn_helper4 blocks indefinitely. */ init_process_options("spawn_helper4", exit_cb); @@ -1641,12 +1666,18 @@ TEST_IMPL(closed_fd_events) { /* 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])); + /* 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(1 == write(fd[1], "", 1)); + ASSERT(1 == uv_fs_write(NULL, &req, fd[1], bufs, 1, -1, NULL)); + ASSERT(req.result == 1); + 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)); /* should have received just one byte */ @@ -1655,7 +1686,9 @@ TEST_IMPL(closed_fd_events) { /* close the pipe and see if we still get events */ uv_close((uv_handle_t*) &pipe_handle, close_cb); - ASSERT(1 == write(fd[1], "", 1)); + ASSERT(1 == uv_fs_write(NULL, &req, fd[1], bufs, 1, -1, NULL)); + ASSERT(req.result == 1); + 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)); @@ -1668,13 +1701,17 @@ TEST_IMPL(closed_fd_events) { ASSERT(timer_counter == 1); /* cleanup */ - ASSERT(0 == uv_process_kill(&process, /* SIGTERM */ 15)); + ASSERT(0 == uv_process_kill(&process, SIGTERM)); +#ifdef _WIN32 + ASSERT(0 == _close(fd[1])); +#else ASSERT(0 == close(fd[1])); +#endif MAKE_VALGRIND_HAPPY(); return 0; } -#endif /* !_WIN32 */ + TEST_IMPL(spawn_reads_child_path) { int r; @@ -1688,7 +1725,7 @@ TEST_IMPL(spawn_reads_child_path) { */ #if defined(__APPLE__) static const char dyld_path_var[] = "DYLD_LIBRARY_PATH"; -#elif defined __MVS__ +#elif defined(__MVS__) || defined(__PASE__) static const char dyld_path_var[] = "LIBPATH"; #else static const char dyld_path_var[] = "LD_LIBRARY_PATH"; @@ -1745,38 +1782,6 @@ TEST_IMPL(spawn_reads_child_path) { return 0; } -#ifndef _WIN32 -static int mpipe(int *fds) { - if (pipe(fds) == -1) - return -1; - if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 || - fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) { - close(fds[0]); - close(fds[1]); - return -1; - } - return 0; -} -#else -static int mpipe(int *fds) { - SECURITY_ATTRIBUTES attr; - HANDLE readh, writeh; - attr.nLength = sizeof(attr); - attr.lpSecurityDescriptor = NULL; - attr.bInheritHandle = FALSE; - if (!CreatePipe(&readh, &writeh, &attr, 0)) - return -1; - fds[0] = _open_osfhandle((intptr_t)readh, 0); - fds[1] = _open_osfhandle((intptr_t)writeh, 0); - if (fds[0] == -1 || fds[1] == -1) { - CloseHandle(readh); - CloseHandle(writeh); - return -1; - } - return 0; -} -#endif /* !_WIN32 */ - TEST_IMPL(spawn_inherit_streams) { uv_process_t child_req; uv_stdio_container_t child_stdio[2]; @@ -1802,8 +1807,8 @@ TEST_IMPL(spawn_inherit_streams) { ASSERT(uv_pipe_init(loop, &pipe_stdin_parent, 0) == 0); ASSERT(uv_pipe_init(loop, &pipe_stdout_parent, 0) == 0); - ASSERT(mpipe(fds_stdin) != -1); - ASSERT(mpipe(fds_stdout) != -1); + 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); @@ -1820,32 +1825,32 @@ TEST_IMPL(spawn_inherit_streams) { ASSERT(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; + child_stdio[0].data.stream = (uv_stream_t *) &pipe_stdin_child; child_stdio[1].flags = UV_INHERIT_STREAM; - child_stdio[1].data.stream = (uv_stream_t *)&pipe_stdout_child; + child_stdio[1].data.stream = (uv_stream_t *) &pipe_stdout_child; options.stdio = child_stdio; options.stdio_count = 2; ASSERT(uv_spawn(loop, &child_req, &options) == 0); - uv_close((uv_handle_t*)&pipe_stdin_child, NULL); - uv_close((uv_handle_t*)&pipe_stdout_child, NULL); + uv_close((uv_handle_t*) &pipe_stdin_child, NULL); + uv_close((uv_handle_t*) &pipe_stdout_child, NULL); - buf = uv_buf_init((char*)ubuf, sizeof ubuf); + buf = uv_buf_init((char*) ubuf, sizeof ubuf); for (i = 0; i < sizeof ubuf; ++i) ubuf[i] = i & 255u; memset(output, 0, sizeof ubuf); r = uv_write(&write_req, - (uv_stream_t*)&pipe_stdin_parent, + (uv_stream_t*) &pipe_stdin_parent, &buf, 1, write_cb); ASSERT(r == 0); - r = uv_read_start((uv_stream_t*)&pipe_stdout_parent, on_alloc, on_read); + r = uv_read_start((uv_stream_t*) &pipe_stdout_parent, on_alloc, on_read); ASSERT(r == 0); r = uv_run(loop, UV_RUN_DEFAULT); @@ -1886,6 +1891,44 @@ TEST_IMPL(spawn_quoted_path) { #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_EQ(r, 0); + + // 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_EQ(r, 0); + + ASSERT_EQ(exit_cb_called, 1); + ASSERT_EQ(close_cb_called, 101); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + /* Helper for child process of spawn_inherit_streams */ #ifndef _WIN32 void spawn_stdin_stdout(void) { diff --git a/test/test-tcp-alloc-cb-fail.c b/test/test-tcp-alloc-cb-fail.c index 61ca667..b6f4ca3 100644 --- a/test/test-tcp-alloc-cb-fail.c +++ b/test/test-tcp-alloc-cb-fail.c @@ -53,7 +53,7 @@ static void conn_read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { ASSERT(nread == UV_ENOBUFS); - ASSERT(buf->base == NULL); + ASSERT_NULL(buf->base); ASSERT(buf->len == 0); uv_close((uv_handle_t*) &incoming, close_cb); diff --git a/test/test-tcp-bind-error.c b/test/test-tcp-bind-error.c index f95efd9..fdd1fe0 100644 --- a/test/test-tcp-bind-error.c +++ b/test/test-tcp-bind-error.c @@ -25,16 +25,59 @@ #include +static int connect_cb_called = 0; static int close_cb_called = 0; static void close_cb(uv_handle_t* handle) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); close_cb_called++; } -TEST_IMPL(tcp_bind_error_addrinuse) { +static void connect_cb(uv_connect_t* req, int status) { + ASSERT(status == UV_EADDRINUSE); + uv_close((uv_handle_t*) req->handle, close_cb); + connect_cb_called++; +} + + +TEST_IMPL(tcp_bind_error_addrinuse_connect) { + struct sockaddr_in addr; + int addrlen; + uv_connect_t req; + uv_tcp_t conn; + + /* 127.0.0.1: is already taken by tcp4_echo_server running in + * another process. uv_tcp_bind() and uv_tcp_connect() should still succeed + * (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(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)); + + addrlen = sizeof(addr); + ASSERT(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); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + + +TEST_IMPL(tcp_bind_error_addrinuse_listen) { struct sockaddr_in addr; uv_tcp_t server1, server2; int r; @@ -240,7 +283,9 @@ TEST_IMPL(tcp_bind_writable_flags) { ASSERT(r == UV_EPIPE); r = uv_shutdown(&shutdown_req, (uv_stream_t*) &server, NULL); ASSERT(r == UV_ENOTCONN); - r = uv_read_start((uv_stream_t*) &server, NULL, NULL); + r = uv_read_start((uv_stream_t*) &server, + (uv_alloc_cb) abort, + (uv_read_cb) abort); ASSERT(r == UV_ENOTCONN); uv_close((uv_handle_t*)&server, close_cb); diff --git a/test/test-tcp-bind6-error.c b/test/test-tcp-bind6-error.c index b762bcb..86181b7 100644 --- a/test/test-tcp-bind6-error.c +++ b/test/test-tcp-bind6-error.c @@ -29,7 +29,7 @@ static int close_cb_called = 0; static void close_cb(uv_handle_t* handle) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); close_cb_called++; } diff --git a/test/test-tcp-close.c b/test/test-tcp-close.c index e65885a..5a7bd68 100644 --- a/test/test-tcp-close.c +++ b/test/test-tcp-close.c @@ -46,7 +46,7 @@ static void connect_cb(uv_connect_t* conn_req, int status) { buf = uv_buf_init("PING", 4); for (i = 0; i < NUM_WRITE_REQS; i++) { req = malloc(sizeof *req); - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); r = uv_write(req, (uv_stream_t*)&tcp_handle, &buf, 1, write_cb); ASSERT(r == 0); diff --git a/test/test-tcp-connect-error.c b/test/test-tcp-connect-error.c index eab1eeb..dda30a5 100644 --- a/test/test-tcp-connect-error.c +++ b/test/test-tcp-connect-error.c @@ -31,14 +31,14 @@ static int close_cb_called = 0; static void connect_cb(uv_connect_t* handle, int status) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); connect_cb_called++; } static void close_cb(uv_handle_t* handle) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); close_cb_called++; } diff --git a/test/test-tcp-connect-timeout.c b/test/test-tcp-connect-timeout.c index 081424b..0f96815 100644 --- a/test/test-tcp-connect-timeout.c +++ b/test/test-tcp-connect-timeout.c @@ -89,3 +89,108 @@ TEST_IMPL(tcp_connect_timeout) { MAKE_VALGRIND_HAPPY(); return 0; } + +/* Make sure connect fails instantly if the target is nonexisting + * local port. + */ + +static void connect_local_cb(uv_connect_t* req, int status) { + ASSERT_PTR_EQ(req, &connect_req); + ASSERT_NE(status, UV_ECANCELED); + connect_cb_called++; +} + +static int is_supported_system(void) { + int semver[3]; + int min_semver[3] = {10, 0, 16299}; + int cnt; + uv_utsname_t uname; + ASSERT_EQ(uv_os_uname(&uname), 0); + if (strcmp(uname.sysname, "Windows_NT") == 0) { + cnt = sscanf(uname.release, "%d.%d.%d", &semver[0], &semver[1], &semver[2]); + if (cnt != 3) { + return 0; + } + /* release >= 10.0.16299 */ + for (cnt = 0; cnt < 3; ++cnt) { + if (semver[cnt] > min_semver[cnt]) + return 1; + if (semver[cnt] < min_semver[cnt]) + return 0; + } + return 1; + } + return 1; +} + +TEST_IMPL(tcp_local_connect_timeout) { + struct sockaddr_in addr; + int r; + + if (!is_supported_system()) { + RETURN_SKIP("Unsupported system"); + } + ASSERT_EQ(0, uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + r = uv_timer_init(uv_default_loop(), &timer); + ASSERT_EQ(r, 0); + + /* Give it 1s to timeout. */ + r = uv_timer_start(&timer, timer_cb, 1000, 0); + ASSERT_EQ(r, 0); + + r = uv_tcp_init(uv_default_loop(), &conn); + ASSERT_EQ(r, 0); + + r = uv_tcp_connect(&connect_req, + &conn, + (const struct sockaddr*) &addr, + connect_local_cb); + if (r == UV_ENETUNREACH) + RETURN_SKIP("Network unreachable."); + ASSERT_EQ(r, 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT(r == 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} + +TEST_IMPL(tcp6_local_connect_timeout) { + struct sockaddr_in6 addr; + int r; + + if (!is_supported_system()) { + RETURN_SKIP("Unsupported system"); + } + if (!can_ipv6()) { + RETURN_SKIP("IPv6 not supported"); + } + + ASSERT_EQ(0, uv_ip6_addr("::1", 9999, &addr)); + + r = uv_timer_init(uv_default_loop(), &timer); + ASSERT_EQ(r, 0); + + /* Give it 1s to timeout. */ + r = uv_timer_start(&timer, timer_cb, 1000, 0); + ASSERT_EQ(r, 0); + + r = uv_tcp_init(uv_default_loop(), &conn); + ASSERT_EQ(r, 0); + + r = uv_tcp_connect(&connect_req, + &conn, + (const struct sockaddr*) &addr, + connect_local_cb); + if (r == UV_ENETUNREACH) + RETURN_SKIP("Network unreachable."); + ASSERT_EQ(r, 0); + + r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); + ASSERT_EQ(r, 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/test/test-tcp-connect6-error.c b/test/test-tcp-connect6-error.c index 91ac0a3..2f6e9cb 100644 --- a/test/test-tcp-connect6-error.c +++ b/test/test-tcp-connect6-error.c @@ -30,13 +30,13 @@ static int close_cb_called = 0; static void connect_cb(uv_connect_t* handle, int status) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); connect_cb_called++; } static void close_cb(uv_handle_t* handle) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); close_cb_called++; } diff --git a/test/test-tcp-create-socket-early.c b/test/test-tcp-create-socket-early.c index b87e732..f2bc60d 100644 --- a/test/test-tcp-create-socket-early.c +++ b/test/test-tcp-create-socket-early.c @@ -44,7 +44,7 @@ static void on_connection(uv_stream_t* server, int status) { ASSERT(status == 0); handle = malloc(sizeof(*handle)); - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); r = uv_tcp_init_ex(server->loop, handle, AF_INET); ASSERT(r == 0); diff --git a/test/test-tcp-open.c b/test/test-tcp-open.c index 0d92886..7e49139 100644 --- a/test/test-tcp-open.c +++ b/test/test-tcp-open.c @@ -96,7 +96,7 @@ static void alloc_cb(uv_handle_t* handle, static void close_cb(uv_handle_t* handle) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); close_cb_called++; } @@ -111,7 +111,7 @@ static void shutdown_cb(uv_shutdown_t* req, int status) { static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { - ASSERT(tcp != NULL); + ASSERT_NOT_NULL(tcp); if (nread >= 0) { ASSERT(nread == 4); @@ -126,7 +126,7 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { static void read1_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { int i; - ASSERT(tcp != NULL); + ASSERT_NOT_NULL(tcp); if (nread >= 0) { for (i = 0; i < nread; ++i) @@ -140,7 +140,7 @@ static void read1_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { static void write_cb(uv_write_t* req, int status) { - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); if (status) { fprintf(stderr, "uv_write error: %s\n", uv_strerror(status)); @@ -155,7 +155,7 @@ static void write1_cb(uv_write_t* req, int status) { uv_buf_t buf; int r; - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); if (status) { ASSERT(shutdown_cb_called); return; @@ -237,6 +237,7 @@ TEST_IMPL(tcp_open) { struct sockaddr_in addr; uv_os_sock_t sock; int r; + uv_tcp_t client2; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); @@ -257,8 +258,6 @@ TEST_IMPL(tcp_open) { #ifndef _WIN32 { - uv_tcp_t client2; - r = uv_tcp_init(uv_default_loop(), &client2); ASSERT(r == 0); @@ -267,7 +266,9 @@ TEST_IMPL(tcp_open) { uv_close((uv_handle_t*) &client2, NULL); } -#endif /* !_WIN32 */ +#else /* _WIN32 */ + (void)client2; +#endif uv_run(uv_default_loop(), UV_RUN_DEFAULT); diff --git a/test/test-tcp-read-stop-start.c b/test/test-tcp-read-stop-start.c new file mode 100644 index 0000000..9bccbc1 --- /dev/null +++ b/test/test-tcp-read-stop-start.c @@ -0,0 +1,136 @@ +/* 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" + +static uv_tcp_t server; +static uv_tcp_t connection; +static int read_cb_called = 0; + +static uv_tcp_t client; +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); + + 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); + + free(req); +} + +static void do_write(uv_stream_t* stream, uv_write_cb cb) { + uv_write_t* req = malloc(sizeof(*req)); + uv_buf_t buf; + buf.base = "1234578"; + buf.len = 8; + ASSERT(0 == uv_write(req, stream, &buf, 1, cb)); +} + +static void on_alloc(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[65536]; + buf->base = slab; + buf->len = sizeof(slab); +} + +static void on_read1(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { + ASSERT(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(0 == 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); + + uv_close((uv_handle_t*)stream, NULL); + uv_close((uv_handle_t*)&server, NULL); + + read_cb_called++; +} + +static void on_connection(uv_stream_t* server, int status) { + ASSERT(0 == status); + + ASSERT(0 == uv_tcp_init(server->loop, &connection)); + + ASSERT(0 == uv_accept(server, (uv_stream_t* )&connection)); + + ASSERT(0 == uv_read_start((uv_stream_t*)&connection, on_alloc, on_read1)); +} + +static void on_connect(uv_connect_t* req, int status) { + ASSERT(0 == status); + + do_write((uv_stream_t*)&client, on_write_close_immediately); +} + +TEST_IMPL(tcp_read_stop_start) { + uv_loop_t* loop = uv_default_loop(); + + { /* Server */ + struct sockaddr_in addr; + + ASSERT(0 == uv_ip4_addr("0.0.0.0", TEST_PORT, &addr)); + + ASSERT(0 == uv_tcp_init(loop, &server)); + + ASSERT(0 == uv_tcp_bind(&server, (struct sockaddr*) & addr, 0)); + + ASSERT(0 == 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(0 == uv_tcp_init(loop, &client)); + + ASSERT(0 == uv_tcp_connect(&connect_req, &client, + (const struct sockaddr*) & addr, on_connect)); + } + + ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); + + ASSERT(read_cb_called >= 2); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/test/test-tcp-write-after-connect.c b/test/test-tcp-write-after-connect.c index 8198e7e..8a698f4 100644 --- a/test/test-tcp-write-after-connect.c +++ b/test/test-tcp-write-after-connect.c @@ -43,6 +43,11 @@ static void connect_cb(uv_connect_t *req, int status) { TEST_IMPL(tcp_write_after_connect) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + struct sockaddr_in sa; ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &sa)); ASSERT(0 == uv_loop_init(&loop)); diff --git a/test/test-tcp-write-fail.c b/test/test-tcp-write-fail.c index 5256a9f..58ee00f 100644 --- a/test/test-tcp-write-fail.c +++ b/test/test-tcp-write-fail.c @@ -52,13 +52,13 @@ static void close_socket(uv_tcp_t* sock) { static void close_cb(uv_handle_t* handle) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); close_cb_called++; } static void write_cb(uv_write_t* req, int status) { - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status != 0); fprintf(stderr, "uv_write error: %s\n", uv_strerror(status)); diff --git a/test/test-tcp-write-to-half-open-connection.c b/test/test-tcp-write-to-half-open-connection.c index 2fa2ae7..ae42513 100644 --- a/test/test-tcp-write-to-half-open-connection.c +++ b/test/test-tcp-write-to-half-open-connection.c @@ -110,7 +110,7 @@ TEST_IMPL(tcp_write_to_half_open_connection) { ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); loop = uv_default_loop(); - ASSERT(loop != NULL); + ASSERT_NOT_NULL(loop); r = uv_tcp_init(loop, &tcp_server); ASSERT(r == 0); diff --git a/test/test-tcp-writealot.c b/test/test-tcp-writealot.c index 7206fdc..40dce96 100644 --- a/test/test-tcp-writealot.c +++ b/test/test-tcp-writealot.c @@ -57,7 +57,7 @@ static void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { static void close_cb(uv_handle_t* handle) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); close_cb_called++; } @@ -82,7 +82,7 @@ static void shutdown_cb(uv_shutdown_t* req, int status) { static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { - ASSERT(tcp != NULL); + ASSERT_NOT_NULL(tcp); if (nread >= 0) { bytes_received_done += nread; @@ -98,7 +98,7 @@ static void read_cb(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { static void write_cb(uv_write_t* req, int status) { - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); if (status) { fprintf(stderr, "uv_write error: %s\n", uv_strerror(status)); @@ -152,7 +152,7 @@ TEST_IMPL(tcp_writealot) { ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); send_buffer = calloc(1, TOTAL_BYTES); - ASSERT(send_buffer != NULL); + ASSERT_NOT_NULL(send_buffer); r = uv_tcp_init(uv_default_loop(), &client); ASSERT(r == 0); diff --git a/test/test-test-macros.c b/test/test-test-macros.c new file mode 100644 index 0000000..72a3992 --- /dev/null +++ b/test/test-test-macros.c @@ -0,0 +1,42 @@ +/* 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 "task.h" + +int test_macros_evil(void) { + static int x; + return x++; +} + + +TEST_IMPL(test_macros) { + char* a = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + char* b = "ABCDEFGHIJKLMNOPQRSTUVWXYz"; + char* c = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + int i; + + i = test_macros_evil(); + ASSERT_STR_NE(a, b); + ASSERT_STR_EQ(a, c); + ASSERT_EQ(i + 1, test_macros_evil()); + ASSERT_EQ(i + 2, test_macros_evil()); + return 0; +} diff --git a/test/test-thread-equal.c b/test/test-thread-equal.c index 27c07ee..f7bde71 100644 --- a/test/test-thread-equal.c +++ b/test/test-thread-equal.c @@ -28,6 +28,9 @@ uv_thread_t subthreads[2]; static void check_thread(void* arg) { uv_thread_t *thread_id = arg; uv_thread_t self_id = uv_thread_self(); +#ifdef _WIN32 + ASSERT_NOT_NULL(self_id); +#endif ASSERT(uv_thread_equal(&main_thread_id, &self_id) == 0); *thread_id = uv_thread_self(); } @@ -35,6 +38,9 @@ static void check_thread(void* arg) { TEST_IMPL(thread_equal) { uv_thread_t threads[2]; main_thread_id = uv_thread_self(); +#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)); diff --git a/test/test-thread.c b/test/test-thread.c index ffb392c..c01991b 100644 --- a/test/test-thread.c +++ b/test/test-thread.c @@ -78,6 +78,10 @@ static void getaddrinfo_do(struct getaddrinfo_req* req) { 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); @@ -163,6 +167,11 @@ TEST_IMPL(thread_create) { * that each "finished" callback is run in its originating thread. */ TEST_IMPL(threadpool_multiple_event_loops) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + struct test_thread threads[8]; size_t i; int r; @@ -185,11 +194,11 @@ TEST_IMPL(threadpool_multiple_event_loops) { static void tls_thread(void* arg) { - ASSERT(NULL == uv_key_get(&tls_key)); + ASSERT_NULL(uv_key_get(&tls_key)); uv_key_set(&tls_key, arg); ASSERT(arg == uv_key_get(&tls_key)); uv_key_set(&tls_key, NULL); - ASSERT(NULL == uv_key_get(&tls_key)); + ASSERT_NULL(uv_key_get(&tls_key)); } @@ -197,7 +206,7 @@ TEST_IMPL(thread_local_storage) { char name[] = "main"; uv_thread_t threads[2]; ASSERT(0 == uv_key_create(&tls_key)); - ASSERT(NULL == uv_key_get(&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)); @@ -264,6 +273,11 @@ TEST_IMPL(thread_stack_size_explicit) { thread_check_stack, &options)); ASSERT(0 == uv_thread_join(&thread)); + options.stack_size = 42; + ASSERT(0 == uv_thread_create_ex(&thread, &options, + thread_check_stack, &options)); + ASSERT(0 == 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, diff --git a/test/test-threadpool-cancel.c b/test/test-threadpool-cancel.c index be252c6..1e867c5 100644 --- a/test/test-threadpool-cancel.c +++ b/test/test-threadpool-cancel.c @@ -98,7 +98,7 @@ static void getaddrinfo_cb(uv_getaddrinfo_t* req, int status, struct addrinfo* res) { ASSERT(status == UV_EAI_CANCELED); - ASSERT(res == NULL); + ASSERT_NULL(res); uv_freeaddrinfo(res); /* Should not crash. */ } @@ -108,8 +108,8 @@ static void getnameinfo_cb(uv_getnameinfo_t* handle, const char* hostname, const char* service) { ASSERT(status == UV_EAI_CANCELED); - ASSERT(hostname == NULL); - ASSERT(service == NULL); + ASSERT_NULL(hostname); + ASSERT_NULL(service); } diff --git a/test/test-timer-again.c b/test/test-timer-again.c index e87d2ed..834b59d 100644 --- a/test/test-timer-again.c +++ b/test/test-timer-again.c @@ -35,7 +35,7 @@ static uint64_t start_time; static void close_cb(uv_handle_t* handle) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); close_cb_called++; } diff --git a/test/test-timer.c b/test/test-timer.c index c667da0..d0921a9 100644 --- a/test/test-timer.c +++ b/test/test-timer.c @@ -37,7 +37,7 @@ static uv_timer_t huge_timer2; static void once_close_cb(uv_handle_t* handle) { printf("ONCE_CLOSE_CB\n"); - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); ASSERT(0 == uv_is_active(handle)); once_close_cb_called++; @@ -47,7 +47,7 @@ static void once_close_cb(uv_handle_t* handle) { static void once_cb(uv_timer_t* handle) { printf("ONCE_CB %d\n", once_cb_called); - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); ASSERT(0 == uv_is_active((uv_handle_t*) handle)); once_cb_called++; @@ -62,7 +62,7 @@ static void once_cb(uv_timer_t* handle) { static void repeat_close_cb(uv_handle_t* handle) { printf("REPEAT_CLOSE_CB\n"); - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); repeat_close_cb_called++; } @@ -71,7 +71,7 @@ static void repeat_close_cb(uv_handle_t* handle) { static void repeat_cb(uv_timer_t* handle) { printf("REPEAT_CB\n"); - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); ASSERT(1 == uv_is_active((uv_handle_t*) handle)); repeat_cb_called++; @@ -161,6 +161,7 @@ TEST_IMPL(timer_init) { ASSERT(0 == uv_timer_init(uv_default_loop(), &handle)); ASSERT(0 == uv_timer_get_repeat(&handle)); + ASSERT_UINT64_LE(0, uv_timer_get_due_in(&handle)); ASSERT(0 == uv_is_active((uv_handle_t*) &handle)); MAKE_VALGRIND_HAPPY(); @@ -232,6 +233,9 @@ TEST_IMPL(timer_huge_timeout) { 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_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(); return 0; diff --git a/test/test-tmpdir.c b/test/test-tmpdir.c index dac488d..86f72e2 100644 --- a/test/test-tmpdir.c +++ b/test/test-tmpdir.c @@ -23,7 +23,7 @@ #include "task.h" #include -#define PATHMAX 1024 +#define PATHMAX 4096 #define SMALLPATH 1 TEST_IMPL(tmpdir) { diff --git a/test/test-tty-duplicate-key.c b/test/test-tty-duplicate-key.c index 1ec2f36..efd79e1 100644 --- a/test/test-tty-duplicate-key.c +++ b/test/test-tty-duplicate-key.c @@ -55,7 +55,7 @@ static void print_err_msg(const char* expect, ssize_t expect_len, static void tty_alloc(uv_handle_t* handle, size_t size, uv_buf_t* buf) { buf->base = malloc(size); - ASSERT(buf->base != NULL); + ASSERT_NOT_NULL(buf->base); buf->len = size; } diff --git a/test/test-tty-escape-sequence-processing.c b/test/test-tty-escape-sequence-processing.c index c4461e9..5f04291 100644 --- a/test/test-tty-escape-sequence-processing.c +++ b/test/test-tty-escape-sequence-processing.c @@ -212,9 +212,9 @@ static void capture_screen(uv_tty_t* tty_out, struct captured_screen* cs) { origin.X = 0; origin.Y = cs->si.csbi.srWindow.Top; cs->text = malloc(cs->si.length * sizeof(*cs->text)); - ASSERT(cs->text != NULL); + ASSERT_NOT_NULL(cs->text); cs->attributes = (WORD*) malloc(cs->si.length * sizeof(*cs->attributes)); - ASSERT(cs->attributes != NULL); + ASSERT_NOT_NULL(cs->attributes); ASSERT(ReadConsoleOutputCharacter( tty_out->handle, cs->text, cs->si.length, origin, &length)); ASSERT((unsigned int) cs->si.length == length); @@ -271,7 +271,7 @@ static void make_expect_screen_erase(struct captured_screen* cs, static void make_expect_screen_write(struct captured_screen* cs, COORD cursor_position, const char* text) { - /* postion of cursor */ + /* position of cursor */ char* start; start = cs->text + cs->si.width * (cursor_position.Y - 1) + cursor_position.X - 1; @@ -1070,6 +1070,11 @@ 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); @@ -1261,7 +1266,7 @@ TEST_IMPL(tty_save_restore_cursor_position) { cursor_pos.Y = si.height / 4; set_cursor_position(&tty_out, cursor_pos); - /* restore the cursor postion */ + /* restore the cursor position */ snprintf(buffer, sizeof(buffer), "%su", CSI); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); @@ -1280,7 +1285,7 @@ TEST_IMPL(tty_save_restore_cursor_position) { cursor_pos.Y = si.height / 4; set_cursor_position(&tty_out, cursor_pos); - /* restore the cursor postion */ + /* restore the cursor position */ snprintf(buffer, sizeof(buffer), "%s8", ESC); write_console(&tty_out, buffer); get_cursor_position(&tty_out, &cursor_pos); @@ -1348,6 +1353,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); diff --git a/test/test-tty.c b/test/test-tty.c index 688711e..ff7d388 100644 --- a/test/test-tty.c +++ b/test/test-tty.c @@ -360,6 +360,8 @@ TEST_IMPL(tty_file) { if (fd != -1) { ASSERT(UV_EINVAL == uv_tty_init(&loop, &tty, fd, 1)); ASSERT(0 == close(fd)); + /* test EBADF handling */ + ASSERT(UV_EINVAL == uv_tty_init(&loop, &tty, fd, 1)); } /* Bug on AIX where '/dev/random' returns 1 from isatty() */ @@ -420,6 +422,14 @@ TEST_IMPL(tty_file) { } TEST_IMPL(tty_pty) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif +#if defined(__ASAN__) + RETURN_SKIP("Test does not currently work in ASAN"); +#endif + #if defined(__APPLE__) || \ defined(__DragonFly__) || \ defined(__FreeBSD__) || \ diff --git a/test/test-udp-alloc-cb-fail.c b/test/test-udp-alloc-cb-fail.c index 0cee09c..6b09801 100644 --- a/test/test-udp-alloc-cb-fail.c +++ b/test/test-udp-alloc-cb-fail.c @@ -83,7 +83,7 @@ static void cl_recv_cb(uv_udp_t* handle, static void cl_send_cb(uv_udp_send_t* req, int status) { int r; - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status == 0); CHECK_HANDLE(req->handle); @@ -95,7 +95,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(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status == 0); CHECK_HANDLE(req->handle); @@ -121,14 +121,14 @@ static void sv_recv_cb(uv_udp_t* handle, if (nread == 0) { /* Returning unused buffer. Don't count towards sv_recv_cb_called */ - ASSERT(addr == NULL); + ASSERT_NULL(addr); return; } CHECK_HANDLE(handle); ASSERT(flags == 0); - ASSERT(addr != NULL); + ASSERT_NOT_NULL(addr); ASSERT(nread == 4); ASSERT(!memcmp("PING", rcvbuf->base, nread)); @@ -136,7 +136,7 @@ static void sv_recv_cb(uv_udp_t* handle, ASSERT(r == 0); req = malloc(sizeof *req); - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); sndbuf = uv_buf_init("PONG", 4); r = uv_udp_send(req, handle, &sndbuf, 1, addr, sv_send_cb); diff --git a/test/test-udp-connect.c b/test/test-udp-connect.c index 58cf947..cd159b6 100644 --- a/test/test-udp-connect.c +++ b/test/test-udp-connect.c @@ -61,7 +61,7 @@ static void close_cb(uv_handle_t* handle) { static void cl_send_cb(uv_udp_send_t* req, int status) { int r; - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status == 0); CHECK_HANDLE(req->handle); if (++cl_send_cb_called == 1) { @@ -87,7 +87,7 @@ static void sv_recv_cb(uv_udp_t* handle, unsigned flags) { if (nread > 0) { ASSERT(nread == 4); - ASSERT(addr != NULL); + ASSERT_NOT_NULL(addr); ASSERT(memcmp("EXIT", rcvbuf->base, nread) == 0); if (++sv_recv_cb_called == 4) { uv_close((uv_handle_t*) &server, close_cb); @@ -124,6 +124,17 @@ TEST_IMPL(udp_connect) { 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)); + r = uv_udp_connect(&client, (const struct sockaddr*) &tmp_addr); +#ifdef _WIN32 + ASSERT_EQ(r, UV_EADDRNOTAVAIL); +#else + ASSERT_EQ(r, 0); + r = uv_udp_connect(&client, NULL); + ASSERT_EQ(r, 0); +#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)); diff --git a/test/test-udp-connect6.c b/test/test-udp-connect6.c new file mode 100644 index 0000000..8e385af --- /dev/null +++ b/test/test-udp-connect6.c @@ -0,0 +1,200 @@ +/* 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" + +#include +#include +#include + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &server || (uv_udp_t*)(handle) == &client) + +static uv_udp_t server; +static uv_udp_t client; +static uv_buf_t buf; +static struct sockaddr_in6 lo_addr; + +static int cl_send_cb_called; +static int sv_recv_cb_called; + +static int close_cb_called; + + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + static char slab[65536]; + CHECK_HANDLE(handle); + ASSERT_LE(suggested_size, sizeof(slab)); + buf->base = slab; + buf->len = sizeof(slab); +} + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + ASSERT(uv_is_closing(handle)); + close_cb_called++; +} + + +static void cl_send_cb(uv_udp_send_t* req, int status) { + int r; + + ASSERT_NOT_NULL(req); + ASSERT_EQ(status, 0); + 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_EQ(r, UV_EDESTADDRREQ); + r = uv_udp_send(req, + &client, + &buf, + 1, + (const struct sockaddr*) &lo_addr, + cl_send_cb); + ASSERT_EQ(r, 0); + } + +} + + +static void sv_recv_cb(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* rcvbuf, + const struct sockaddr* addr, + unsigned flags) { + if (nread > 0) { + ASSERT_EQ(nread, 4); + ASSERT_NOT_NULL(addr); + ASSERT_EQ(memcmp("EXIT", rcvbuf->base, nread), 0); + if (++sv_recv_cb_called == 4) { + uv_close((uv_handle_t*) &server, close_cb); + uv_close((uv_handle_t*) &client, close_cb); + } + } +} + + +TEST_IMPL(udp_connect6) { +#if defined(__PASE__) + RETURN_SKIP( + "IBMi PASE's UDP connection can not be disconnected with AF_UNSPEC."); +#endif + uv_udp_send_t req; + struct sockaddr_in6 ext_addr; + struct sockaddr_in6 tmp_addr; + int r; + int addrlen; + + if (!can_ipv6()) + RETURN_SKIP("IPv6 not supported"); + + ASSERT_EQ(0, uv_ip6_addr("::", TEST_PORT, &lo_addr)); + + r = uv_udp_init(uv_default_loop(), &server); + ASSERT_EQ(r, 0); + + r = uv_udp_bind(&server, (const struct sockaddr*) &lo_addr, 0); + ASSERT_EQ(r, 0); + + r = uv_udp_recv_start(&server, alloc_cb, sv_recv_cb); + ASSERT_EQ(r, 0); + + r = uv_udp_init(uv_default_loop(), &client); + ASSERT_EQ(r, 0); + + 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)); + r = uv_udp_connect(&client, (const struct sockaddr*) &tmp_addr); +#ifdef _WIN32 + ASSERT_EQ(r, UV_EADDRNOTAVAIL); +#else + ASSERT_EQ(r, 0); + r = uv_udp_connect(&client, NULL); + ASSERT_EQ(r, 0); +#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)); + + r = uv_udp_connect(&client, (const struct sockaddr*) &lo_addr); + ASSERT_EQ(r, 0); + 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); + + /* 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); + 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); + r = uv_udp_connect(&client, NULL); + ASSERT_EQ(r, UV_ENOTCONN); + + addrlen = sizeof(tmp_addr); + r = uv_udp_getpeername(&client, (struct sockaddr*) &tmp_addr, &addrlen); + 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_EQ(r, 4); + 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); + r = uv_udp_send(&req, + &client, + &buf, + 1, + (const struct sockaddr*) &lo_addr, + cl_send_cb); + ASSERT_EQ(r, UV_EISCONN); + r = uv_udp_send(&req, &client, &buf, 1, NULL, cl_send_cb); + ASSERT_EQ(r, 0); + + 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(client.send_queue_size, 0); + ASSERT_EQ(server.send_queue_size, 0); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/test/test-udp-mmsg.c b/test/test-udp-mmsg.c new file mode 100644 index 0000000..f722608 --- /dev/null +++ b/test/test-udp-mmsg.c @@ -0,0 +1,149 @@ +/* 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 + +#define CHECK_HANDLE(handle) \ + ASSERT((uv_udp_t*)(handle) == &recver || (uv_udp_t*)(handle) == &sender) + +#define BUFFER_MULTIPLIER 20 +#define MAX_DGRAM_SIZE (64 * 1024) +#define NUM_SENDS 40 +#define EXPECTED_MMSG_ALLOCS (NUM_SENDS / BUFFER_MULTIPLIER) + +static uv_udp_t recver; +static uv_udp_t sender; +static int recv_cb_called; +static int received_datagrams; +static int close_cb_called; +static int alloc_cb_called; + + +static void alloc_cb(uv_handle_t* handle, + size_t suggested_size, + uv_buf_t* buf) { + size_t buffer_size; + CHECK_HANDLE(handle); + + /* Only allocate enough room for multiple dgrams if we can actually recv them */ + buffer_size = MAX_DGRAM_SIZE; + if (uv_udp_using_recvmmsg((uv_udp_t*)handle)) + buffer_size *= BUFFER_MULTIPLIER; + + /* Actually malloc to exercise free'ing the buffer later */ + buf->base = malloc(buffer_size); + ASSERT_NOT_NULL(buf->base); + buf->len = buffer_size; + alloc_cb_called++; +} + + +static void close_cb(uv_handle_t* handle) { + CHECK_HANDLE(handle); + ASSERT(uv_is_closing(handle)); + close_cb_called++; +} + + +static void recv_cb(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* rcvbuf, + const struct sockaddr* addr, + unsigned flags) { + ASSERT_GE(nread, 0); + + /* free and return if this is a mmsg free-only callback invocation */ + if (flags & UV_UDP_MMSG_FREE) { + ASSERT_EQ(nread, 0); + ASSERT_NULL(addr); + free(rcvbuf->base); + return; + } + + if (nread == 0) { + /* There can be no more available data for the time being. */ + ASSERT_NULL(addr); + } else { + ASSERT_EQ(nread, 4); + ASSERT_NOT_NULL(addr); + ASSERT_MEM_EQ("PING", rcvbuf->base, nread); + received_datagrams++; + } + + recv_cb_called++; + 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 */ + if (rcvbuf && !(flags & UV_UDP_MMSG_CHUNK)) + free(rcvbuf->base); +} + + +TEST_IMPL(udp_mmsg) { + struct sockaddr_in addr; + uv_buf_t buf; + int i; + + ASSERT_EQ(0, 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_EQ(0, uv_udp_bind(&recver, (const struct sockaddr*) &addr, 0)); + + ASSERT_EQ(0, uv_udp_recv_start(&recver, alloc_cb, recv_cb)); + + ASSERT_EQ(0, uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); + + ASSERT_EQ(0, 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_EQ(close_cb_called, 2); + ASSERT_EQ(received_datagrams, NUM_SENDS); + + ASSERT_EQ(sender.send_queue_size, 0); + ASSERT_EQ(recver.send_queue_size, 0); + + 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); + else + ASSERT_EQ(alloc_cb_called, recv_cb_called); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/test/test-udp-multicast-interface.c b/test/test-udp-multicast-interface.c index 0b3c0e6..bd9a61c 100644 --- a/test/test-udp-multicast-interface.c +++ b/test/test-udp-multicast-interface.c @@ -43,7 +43,7 @@ static void close_cb(uv_handle_t* handle) { static void sv_send_cb(uv_udp_send_t* req, int status) { - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status == 0 || status == UV_ENETUNREACH || status == UV_EPERM); CHECK_HANDLE(req->handle); @@ -54,6 +54,11 @@ static void sv_send_cb(uv_udp_send_t* req, int status) { TEST_IMPL(udp_multicast_interface) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + int r; uv_udp_send_t req; uv_buf_t buf; diff --git a/test/test-udp-multicast-interface6.c b/test/test-udp-multicast-interface6.c index 40b0553..be11514 100644 --- a/test/test-udp-multicast-interface6.c +++ b/test/test-udp-multicast-interface6.c @@ -43,7 +43,7 @@ static void close_cb(uv_handle_t* handle) { static void sv_send_cb(uv_udp_send_t* req, int status) { - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status == 0); CHECK_HANDLE(req->handle); @@ -54,6 +54,11 @@ static void sv_send_cb(uv_udp_send_t* req, int status) { TEST_IMPL(udp_multicast_interface6) { +/* TODO(gengjiawen): Fix test on QEMU. */ +#if defined(__QEMU__) + RETURN_SKIP("Test does not currently work in QEMU"); +#endif + int r; uv_udp_send_t req; uv_buf_t buf; diff --git a/test/test-udp-multicast-join.c b/test/test-udp-multicast-join.c index cb3ff87..9e603a8 100644 --- a/test/test-udp-multicast-join.c +++ b/test/test-udp-multicast-join.c @@ -60,7 +60,7 @@ static void close_cb(uv_handle_t* handle) { static void sv_send_cb(uv_udp_send_t* req, int status) { - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status == 0); CHECK_HANDLE(req->handle); @@ -103,11 +103,11 @@ static void cl_recv_cb(uv_udp_t* handle, if (nread == 0) { /* Returning unused buffer. Don't count towards cl_recv_cb_called */ - ASSERT(addr == NULL); + ASSERT_NULL(addr); return; } - ASSERT(addr != NULL); + ASSERT_NOT_NULL(addr); ASSERT(nread == 4); ASSERT(!memcmp("PING", buf->base, nread)); diff --git a/test/test-udp-multicast-join6.c b/test/test-udp-multicast-join6.c index 5de27a7..e67c5ee 100644 --- a/test/test-udp-multicast-join6.c +++ b/test/test-udp-multicast-join6.c @@ -72,7 +72,7 @@ static void close_cb(uv_handle_t* handle) { static void sv_send_cb(uv_udp_send_t* req, int status) { - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status == 0); CHECK_HANDLE(req->handle); @@ -115,11 +115,11 @@ static void cl_recv_cb(uv_udp_t* handle, if (nread == 0) { /* Returning unused buffer. Don't count towards cl_recv_cb_called */ - ASSERT(addr == NULL); + ASSERT_NULL(addr); return; } - ASSERT(addr != NULL); + ASSERT_NOT_NULL(addr); ASSERT(nread == 4); ASSERT(!memcmp("PING", buf->base, nread)); @@ -193,6 +193,10 @@ TEST_IMPL(udp_multicast_join6) { ASSERT(r == 0); +/* 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); diff --git a/test/test-udp-multicast-ttl.c b/test/test-udp-multicast-ttl.c index e92608b..fbddd90 100644 --- a/test/test-udp-multicast-ttl.c +++ b/test/test-udp-multicast-ttl.c @@ -43,7 +43,7 @@ static void close_cb(uv_handle_t* handle) { static void sv_send_cb(uv_udp_send_t* req, int status) { - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status == 0 || status == UV_ENETUNREACH || status == UV_EPERM); CHECK_HANDLE(req->handle); diff --git a/test/test-udp-open.c b/test/test-udp-open.c index dee408b..f5136b6 100644 --- a/test/test-udp-open.c +++ b/test/test-udp-open.c @@ -91,7 +91,7 @@ static void alloc_cb(uv_handle_t* handle, static void close_cb(uv_handle_t* handle) { - ASSERT(handle != NULL); + ASSERT_NOT_NULL(handle); close_cb_called++; } @@ -109,13 +109,13 @@ static void recv_cb(uv_udp_t* handle, if (nread == 0) { /* Returning unused buffer. Don't count towards sv_recv_cb_called */ - ASSERT(addr == NULL); + ASSERT_NULL(addr); return; } ASSERT(flags == 0); - ASSERT(addr != NULL); + ASSERT_NOT_NULL(addr); ASSERT(nread == 4); ASSERT(memcmp("PING", buf->base, nread) == 0); @@ -127,7 +127,7 @@ static void recv_cb(uv_udp_t* handle, static void send_cb(uv_udp_send_t* req, int status) { - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status == 0); send_cb_called++; @@ -138,7 +138,7 @@ static void send_cb(uv_udp_send_t* req, int status) { TEST_IMPL(udp_open) { struct sockaddr_in addr; uv_buf_t buf = uv_buf_init("PING", 4); - uv_udp_t client; + uv_udp_t client, client2; uv_os_sock_t sock; int r; @@ -169,8 +169,6 @@ TEST_IMPL(udp_open) { #ifndef _WIN32 { - uv_udp_t client2; - r = uv_udp_init(uv_default_loop(), &client2); ASSERT(r == 0); @@ -179,7 +177,9 @@ TEST_IMPL(udp_open) { uv_close((uv_handle_t*) &client2, NULL); } -#endif /* !_WIN32 */ +#else /* _WIN32 */ + (void)client2; +#endif uv_run(uv_default_loop(), UV_RUN_DEFAULT); diff --git a/test/test-udp-options.c b/test/test-udp-options.c index d8c9d68..3ea51ba 100644 --- a/test/test-udp-options.c +++ b/test/test-udp-options.c @@ -128,12 +128,16 @@ TEST_IMPL(udp_no_autobind) { ASSERT(UV_EBADF == uv_udp_set_ttl(&h, 1)); #endif ASSERT(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")); uv_close((uv_handle_t*) &h, NULL); /* Test a non-lazily initialized socket. */ - ASSERT(0 == uv_udp_init_ex(loop, &h2, AF_INET)); + 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)); diff --git a/test/test-udp-send-and-recv.c b/test/test-udp-send-and-recv.c index 1f01188..d602090 100644 --- a/test/test-udp-send-and-recv.c +++ b/test/test-udp-send-and-recv.c @@ -73,11 +73,11 @@ static void cl_recv_cb(uv_udp_t* handle, if (nread == 0) { /* Returning unused buffer. Don't count towards cl_recv_cb_called */ - ASSERT(addr == NULL); + ASSERT_NULL(addr); return; } - ASSERT(addr != NULL); + ASSERT_NOT_NULL(addr); ASSERT(nread == 4); ASSERT(!memcmp("PONG", buf->base, nread)); @@ -90,7 +90,7 @@ static void cl_recv_cb(uv_udp_t* handle, static void cl_send_cb(uv_udp_send_t* req, int status) { int r; - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status == 0); CHECK_HANDLE(req->handle); @@ -102,7 +102,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(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status == 0); CHECK_HANDLE(req->handle); @@ -128,14 +128,14 @@ static void sv_recv_cb(uv_udp_t* handle, if (nread == 0) { /* Returning unused buffer. Don't count towards sv_recv_cb_called */ - ASSERT(addr == NULL); + ASSERT_NULL(addr); return; } CHECK_HANDLE(handle); ASSERT(flags == 0); - ASSERT(addr != NULL); + ASSERT_NOT_NULL(addr); ASSERT(nread == 4); ASSERT(!memcmp("PING", rcvbuf->base, nread)); @@ -147,7 +147,7 @@ static void sv_recv_cb(uv_udp_t* handle, ASSERT(r == 0); req = malloc(sizeof *req); - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); sndbuf = uv_buf_init("PONG", 4); r = uv_udp_send(req, handle, &sndbuf, 1, addr, sv_send_cb); diff --git a/test/test-udp-send-hang-loop.c b/test/test-udp-send-hang-loop.c index bf4dfeb..072070b 100644 --- a/test/test-udp-send-hang-loop.c +++ b/test/test-udp-send-hang-loop.c @@ -44,7 +44,7 @@ static void send_cb(uv_udp_send_t* req, int status); static void idle_cb(uv_idle_t* handle) { int r; - ASSERT(send_req.handle == NULL); + ASSERT_NULL(send_req.handle); CHECK_OBJECT(handle, uv_idle_t, idle_handle); ASSERT(0 == uv_idle_stop(handle)); @@ -66,7 +66,7 @@ static void idle_cb(uv_idle_t* handle) { static void send_cb(uv_udp_send_t* req, int status) { - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status == 0 || status == UV_ENETUNREACH); CHECK_OBJECT(req->handle, uv_udp_t, client); CHECK_OBJECT(req, uv_udp_send_t, send_req); diff --git a/test/test-udp-send-immediate.c b/test/test-udp-send-immediate.c index 1011aa4..a1c95d3 100644 --- a/test/test-udp-send-immediate.c +++ b/test/test-udp-send-immediate.c @@ -56,7 +56,7 @@ static void close_cb(uv_handle_t* handle) { static void cl_send_cb(uv_udp_send_t* req, int status) { - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status == 0); CHECK_HANDLE(req->handle); @@ -75,14 +75,14 @@ static void sv_recv_cb(uv_udp_t* handle, if (nread == 0) { /* Returning unused buffer. Don't count towards sv_recv_cb_called */ - ASSERT(addr == NULL); + ASSERT_NULL(addr); return; } CHECK_HANDLE(handle); ASSERT(flags == 0); - ASSERT(addr != NULL); + ASSERT_NOT_NULL(addr); ASSERT(nread == 4); ASSERT(memcmp("PING", rcvbuf->base, nread) == 0 || memcmp("PANG", rcvbuf->base, nread) == 0); diff --git a/test/test-udp-send-unreachable.c b/test/test-udp-send-unreachable.c index c650032..c67a23b 100644 --- a/test/test-udp-send-unreachable.c +++ b/test/test-udp-send-unreachable.c @@ -27,9 +27,10 @@ #include #define CHECK_HANDLE(handle) \ - ASSERT((uv_udp_t*)(handle) == &client) + ASSERT((uv_udp_t*)(handle) == &client || (uv_udp_t*)(handle) == &client2) static uv_udp_t client; +static uv_udp_t client2; static uv_timer_t timer; static int send_cb_called; @@ -37,6 +38,7 @@ static int recv_cb_called; static int close_cb_called; static int alloc_cb_called; static int timer_cb_called; +static int can_recverr; static void alloc_cb(uv_handle_t* handle, @@ -44,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); alloc_cb_called++; @@ -52,18 +54,25 @@ static void alloc_cb(uv_handle_t* handle, static void close_cb(uv_handle_t* handle) { - ASSERT(1 == uv_is_closing(handle)); + ASSERT_EQ(1, uv_is_closing(handle)); close_cb_called++; } static void send_cb(uv_udp_send_t* req, int status) { - ASSERT(req != NULL); + ASSERT_NOT_NULL(req); ASSERT(status == 0); + ASSERT_EQ(status, 0); CHECK_HANDLE(req->handle); send_cb_called++; } +static void send_cb_recverr(uv_udp_send_t* req, int status) { + ASSERT_PTR_NE(req, NULL); + ASSERT(status == 0 || status == UV_ECONNREFUSED); + CHECK_HANDLE(req->handle); + send_cb_called++; +} static void recv_cb(uv_udp_t* handle, ssize_t nread, @@ -77,17 +86,19 @@ static void recv_cb(uv_udp_t* handle, ASSERT(0 && "unexpected error"); } else if (nread == 0) { /* Returning unused buffer */ - ASSERT(addr == NULL); + ASSERT_NULL(addr); } else { - ASSERT(addr != NULL); + ASSERT_NOT_NULL(addr); } } static void timer_cb(uv_timer_t* h) { - ASSERT(h == &timer); + ASSERT_PTR_EQ(h, &timer); timer_cb_called++; uv_close((uv_handle_t*) &client, close_cb); + if (can_recverr) + uv_close((uv_handle_t*) &client2, close_cb); uv_close((uv_handle_t*) h, close_cb); } @@ -95,27 +106,33 @@ static void timer_cb(uv_timer_t* h) { TEST_IMPL(udp_send_unreachable) { struct sockaddr_in addr; struct sockaddr_in addr2; - uv_udp_send_t req1, req2; + struct sockaddr_in addr3; + uv_udp_send_t req1, req2, req3, req4; uv_buf_t buf; int r; - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT, &addr)); - ASSERT(0 == uv_ip4_addr("127.0.0.1", TEST_PORT_2, &addr2)); +#ifdef __linux__ + 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)); r = uv_timer_init( uv_default_loop(), &timer ); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_timer_start( &timer, timer_cb, 1000, 0 ); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_udp_init(uv_default_loop(), &client); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_udp_bind(&client, (const struct sockaddr*) &addr2, 0); - ASSERT(r == 0); + ASSERT_EQ(r, 0); r = uv_udp_recv_start(&client, alloc_cb, recv_cb); - ASSERT(r == 0); + ASSERT_EQ(r, 0); /* client sends "PING", then "PANG" */ buf = uv_buf_init("PING", 4); @@ -126,7 +143,7 @@ TEST_IMPL(udp_send_unreachable) { 1, (const struct sockaddr*) &addr, send_cb); - ASSERT(r == 0); + ASSERT_EQ(r, 0); buf = uv_buf_init("PANG", 4); @@ -136,14 +153,48 @@ TEST_IMPL(udp_send_unreachable) { 1, (const struct sockaddr*) &addr, send_cb); - ASSERT(r == 0); + ASSERT_EQ(r, 0); + + if (can_recverr) { + r = uv_udp_init(uv_default_loop(), &client2); + ASSERT_EQ(r, 0); + + r = uv_udp_bind(&client2, + (const struct sockaddr*) &addr3, + UV_UDP_LINUX_RECVERR); + ASSERT_EQ(r, 0); + + r = uv_udp_recv_start(&client2, alloc_cb, recv_cb); + ASSERT_EQ(r, 0); + + /* client sends "PING", then "PANG" */ + buf = uv_buf_init("PING", 4); + + r = uv_udp_send(&req3, + &client2, + &buf, + 1, + (const struct sockaddr*) &addr, + send_cb_recverr); + ASSERT_EQ(r, 0); + + buf = uv_buf_init("PANG", 4); + + r = uv_udp_send(&req4, + &client2, + &buf, + 1, + (const struct sockaddr*) &addr, + send_cb_recverr); + ASSERT_EQ(r, 0); + } uv_run(uv_default_loop(), UV_RUN_DEFAULT); - ASSERT(send_cb_called == 2); - ASSERT(recv_cb_called == alloc_cb_called); - ASSERT(timer_cb_called == 1); - ASSERT(close_cb_called == 2); + 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(close_cb_called, (long)(can_recverr ? 3 : 2)); MAKE_VALGRIND_HAPPY(); return 0; diff --git a/test/test-udp-sendmmsg-error.c b/test/test-udp-sendmmsg-error.c new file mode 100644 index 0000000..c8a411b --- /dev/null +++ b/test/test-udp-sendmmsg-error.c @@ -0,0 +1,75 @@ +/* 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" + +#include +#include + +#define DATAGRAMS 6 + +static uv_udp_t client; +static uv_udp_send_t req[DATAGRAMS]; + +static int send_cb_called; +static int close_cb_called; + + +static void close_cb(uv_handle_t* handle) { + ASSERT_PTR_EQ(handle, &client); + ASSERT(uv_is_closing(handle)); + close_cb_called++; +} + + +static void send_cb(uv_udp_send_t* req, int status) { + if (status != 0) + ASSERT_EQ(status, UV_ECONNREFUSED); + + if (++send_cb_called == DATAGRAMS) + uv_close((uv_handle_t*)&client, close_cb); +} + + +TEST_IMPL(udp_sendmmsg_error) { + struct sockaddr_in addr; + 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)); + + 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)); + + 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); + + MAKE_VALGRIND_HAPPY(); + return 0; +} diff --git a/test/test-udp-try-send.c b/test/test-udp-try-send.c index a31d382..85caaac 100644 --- a/test/test-udp-try-send.c +++ b/test/test-udp-try-send.c @@ -63,12 +63,12 @@ static void sv_recv_cb(uv_udp_t* handle, ASSERT(nread > 0); if (nread == 0) { - ASSERT(addr == NULL); + ASSERT_NULL(addr); return; } ASSERT(nread == 4); - ASSERT(addr != NULL); + ASSERT_NOT_NULL(addr); ASSERT(memcmp("EXIT", rcvbuf->base, nread) == 0); uv_close((uv_handle_t*) handle, close_cb); diff --git a/test/test.gyp b/test/test.gyp deleted file mode 100644 index 48fc5ea..0000000 --- a/test/test.gyp +++ /dev/null @@ -1,295 +0,0 @@ -{ - 'targets': [ - { - 'target_name': 'run-tests', - 'type': 'executable', - 'dependencies': [ '../uv.gyp:libuv' ], - 'sources': [ - 'blackhole-server.c', - 'echo-server.c', - 'run-tests.c', - 'runner.c', - 'runner.h', - 'test-get-loadavg.c', - 'task.h', - 'test-active.c', - 'test-async.c', - 'test-async-null-cb.c', - 'test-callback-stack.c', - 'test-callback-order.c', - 'test-close-fd.c', - 'test-close-order.c', - 'test-connect-unspecified.c', - 'test-connection-fail.c', - 'test-cwd-and-chdir.c', - 'test-default-loop-close.c', - 'test-delayed-accept.c', - 'test-eintr-handling.c', - 'test-error.c', - 'test-embed.c', - 'test-emfile.c', - 'test-env-vars.c', - 'test-fail-always.c', - 'test-fork.c', - 'test-fs.c', - 'test-fs-readdir.c', - 'test-fs-copyfile.c', - 'test-fs-event.c', - 'test-fs-fd-hash.c', - 'test-fs-open-flags.c', - 'test-fs-poll.c', - 'test-getters-setters.c', - 'test-get-currentexe.c', - 'test-get-memory.c', - 'test-get-passwd.c', - 'test-getaddrinfo.c', - 'test-gethostname.c', - 'test-getnameinfo.c', - 'test-getsockname.c', - 'test-gettimeofday.c', - 'test-handle-fileno.c', - 'test-homedir.c', - 'test-hrtime.c', - 'test-idle.c', - 'test-idna.c', - 'test-ip4-addr.c', - 'test-ip6-addr.c', - 'test-ipc-heavy-traffic-deadlock-bug.c', - 'test-ipc-send-recv.c', - 'test-ipc.c', - 'test-list.h', - 'test-loop-handles.c', - 'test-loop-alive.c', - 'test-loop-close.c', - 'test-loop-stop.c', - 'test-loop-time.c', - 'test-loop-configure.c', - 'test-walk-handles.c', - 'test-watcher-cross-stop.c', - 'test-multiple-listen.c', - 'test-osx-select.c', - 'test-pass-always.c', - 'test-ping-pong.c', - 'test-pipe-bind-error.c', - 'test-pipe-connect-error.c', - 'test-pipe-connect-multiple.c', - 'test-pipe-connect-prepare.c', - 'test-pipe-getsockname.c', - 'test-pipe-pending-instances.c', - 'test-pipe-sendmsg.c', - 'test-pipe-server-close.c', - 'test-pipe-close-stdout-read-stdin.c', - 'test-pipe-set-non-blocking.c', - 'test-pipe-set-fchmod.c', - 'test-platform-output.c', - 'test-poll.c', - 'test-poll-close.c', - 'test-poll-close-doesnt-corrupt-stack.c', - 'test-poll-closesocket.c', - 'test-poll-oob.c', - 'test-process-priority.c', - 'test-process-title.c', - 'test-process-title-threadsafe.c', - 'test-queue-foreach-delete.c', - 'test-random.c', - 'test-ref.c', - 'test-run-nowait.c', - 'test-run-once.c', - 'test-semaphore.c', - 'test-shutdown-close.c', - 'test-shutdown-eof.c', - 'test-shutdown-twice.c', - 'test-signal.c', - 'test-signal-multiple-loops.c', - 'test-signal-pending-on-close.c', - 'test-socket-buffer-size.c', - 'test-spawn.c', - 'test-strscpy.c', - 'test-stdio-over-pipes.c', - 'test-tcp-alloc-cb-fail.c', - 'test-tcp-bind-error.c', - 'test-tcp-bind6-error.c', - 'test-tcp-close.c', - 'test-tcp-close-accept.c', - 'test-tcp-close-reset.c', - 'test-tcp-close-while-connecting.c', - 'test-tcp-create-socket-early.c', - 'test-tcp-connect-error-after-write.c', - 'test-tcp-shutdown-after-write.c', - 'test-tcp-flags.c', - 'test-tcp-connect-error.c', - 'test-tcp-connect-timeout.c', - 'test-tcp-connect6-error.c', - 'test-tcp-open.c', - 'test-tcp-write-to-half-open-connection.c', - 'test-tcp-write-after-connect.c', - 'test-tcp-writealot.c', - 'test-tcp-write-fail.c', - 'test-tcp-try-write.c', - 'test-tcp-try-write-error.c', - 'test-tcp-unexpected-read.c', - 'test-tcp-oob.c', - 'test-tcp-read-stop.c', - 'test-tcp-write-queue-order.c', - 'test-threadpool.c', - 'test-threadpool-cancel.c', - 'test-thread-equal.c', - 'test-tmpdir.c', - 'test-mutexes.c', - 'test-thread.c', - 'test-barrier.c', - 'test-condvar.c', - 'test-timer-again.c', - 'test-timer-from-check.c', - 'test-timer.c', - 'test-tty-duplicate-key.c', - 'test-tty-escape-sequence-processing.c', - 'test-tty.c', - 'test-udp-alloc-cb-fail.c', - 'test-udp-bind.c', - 'test-udp-connect.c', - 'test-udp-create-socket-early.c', - 'test-udp-dgram-too-big.c', - 'test-udp-ipv6.c', - 'test-udp-open.c', - 'test-udp-options.c', - 'test-udp-send-and-recv.c', - 'test-udp-send-hang-loop.c', - 'test-udp-send-immediate.c', - 'test-udp-send-unreachable.c', - 'test-udp-multicast-join.c', - 'test-udp-multicast-join6.c', - 'test-dlerror.c', - 'test-udp-multicast-ttl.c', - 'test-udp-multicast-interface.c', - 'test-udp-multicast-interface6.c', - 'test-udp-try-send.c', - 'test-uname.c', - ], - 'conditions': [ - [ 'OS=="win"', { - 'sources': [ - 'runner-win.c', - 'runner-win.h', - '../src/win/snprintf.c', - ], - 'libraries': [ '-lws2_32' ] - }, { # POSIX - 'sources': [ - 'runner-unix.c', - 'runner-unix.h', - ], - 'conditions': [ - [ 'OS != "zos"', { - 'defines': [ '_GNU_SOURCE' ], - 'cflags': [ '-Wno-long-long' ], - 'xcode_settings': { - 'WARNING_CFLAGS': [ '-Wno-long-long' ] - } - }], - ]}, - ], - [ 'OS in "mac dragonflybsd freebsd linux netbsd openbsd".split()', { - 'link_settings': { - 'libraries': [ '-lutil' ], - }, - }], - [ 'OS=="solaris"', { # make test-fs.c compile, needs _POSIX_C_SOURCE - 'defines': [ - '__EXTENSIONS__', - '_XOPEN_SOURCE=500', - ], - }], - [ 'OS=="aix"', { # make test-fs.c compile, needs _POSIX_C_SOURCE - 'defines': [ - '_ALL_SOURCE', - '_XOPEN_SOURCE=500', - ], - }], - [ 'OS == "zos"', { - 'cflags': [ '-qxplink' ], - 'ldflags': [ '-qxplink' ], - }], - ['uv_library=="shared_library"', { - 'defines': [ 'USING_UV_SHARED=1' ], - 'conditions': [ - [ 'OS == "zos"', { - 'cflags': [ '-Wc,DLL' ], - }], - ], - }], - ], - 'msvs-settings': { - 'VCLinkerTool': { - 'SubSystem': 1, # /subsystem:console - }, - }, - }, - - { - 'target_name': 'run-benchmarks', - 'type': 'executable', - 'dependencies': [ '../uv.gyp:libuv' ], - 'sources': [ - 'benchmark-async.c', - 'benchmark-async-pummel.c', - 'benchmark-fs-stat.c', - 'benchmark-getaddrinfo.c', - 'benchmark-list.h', - 'benchmark-loop-count.c', - 'benchmark-million-async.c', - 'benchmark-million-timers.c', - 'benchmark-multi-accept.c', - 'benchmark-ping-pongs.c', - 'benchmark-ping-udp.c', - 'benchmark-pound.c', - 'benchmark-pump.c', - 'benchmark-sizes.c', - 'benchmark-spawn.c', - 'benchmark-thread.c', - 'benchmark-tcp-write-batch.c', - 'benchmark-udp-pummel.c', - 'dns-server.c', - 'echo-server.c', - 'blackhole-server.c', - 'run-benchmarks.c', - 'runner.c', - 'runner.h', - 'task.h', - ], - 'conditions': [ - [ 'OS=="win"', { - 'sources': [ - 'runner-win.c', - 'runner-win.h', - '../src/win/snprintf.c', - ], - 'libraries': [ '-lws2_32' ] - }, { # POSIX - 'defines': [ '_GNU_SOURCE' ], - 'sources': [ - 'runner-unix.c', - 'runner-unix.h', - ] - }], - [ 'OS == "zos"', { - 'cflags': [ '-qxplink' ], - 'ldflags': [ '-qxplink' ], - }], - ['uv_library=="shared_library"', { - 'defines': [ 'USING_UV_SHARED=1' ], - 'conditions': [ - [ 'OS == "zos"', { - 'cflags': [ '-Wc,DLL' ], - }], - ], - }], - ], - 'msvs-settings': { - 'VCLinkerTool': { - 'SubSystem': 1, # /subsystem:console - }, - }, - }, - ], -} diff --git a/tools/make_dist_html.py b/tools/make_dist_html.py index 7a19d3e..4833b1b 100644 --- a/tools/make_dist_html.py +++ b/tools/make_dist_html.py @@ -1,6 +1,4 @@ -#!/usr/bin/python - -from __future__ import print_function +#!/usr/bin/python3 import itertools import os @@ -84,7 +82,7 @@ EXE = r''' ''' def version(tag): - return map(int, re.match('^v(\d+)\.(\d+)\.(\d+)', tag).groups()) + return list(map(int, re.match('^v(\d+)\.(\d+)\.(\d+)', tag).groups())) def major_minor(tag): return version(tag)[:2] @@ -114,7 +112,7 @@ def groups_for(groups, n=4): if __name__ == '__main__': os.chdir(os.path.dirname(__file__)) - tags = subprocess.check_output(['git', 'tag']) + tags = subprocess.check_output(['git', 'tag'], text=True) tags = [tag for tag in tags.split('\n') if tag.startswith('v')] tags.sort(key=version, reverse=True) groups = [group_for(list(g)) for _, g in itertools.groupby(tags, major_minor)] diff --git a/uv.gyp b/uv.gyp deleted file mode 100644 index 116b753..0000000 --- a/uv.gyp +++ /dev/null @@ -1,368 +0,0 @@ -{ - 'variables': { - 'conditions': [ - ['OS=="win"', { - 'shared_unix_defines': [ ], - }, { - 'shared_unix_defines': [ - '_LARGEFILE_SOURCE', - '_FILE_OFFSET_BITS=64', - ], - }], - ['OS in "mac ios"', { - 'shared_mac_defines': [ '_DARWIN_USE_64_BIT_INODE=1' ], - }, { - 'shared_mac_defines': [ ], - }], - ['OS=="zos"', { - 'shared_zos_defines': [ - '_UNIX03_THREADS', - '_UNIX03_SOURCE', - '_UNIX03_WITHDRAWN', - '_OPEN_SYS_IF_EXT', - '_OPEN_SYS_SOCK_EXT3', - '_OPEN_SYS_SOCK_IPV6', - '_OPEN_MSGQ_EXT', - '_XOPEN_SOURCE_EXTENDED', - '_ALL_SOURCE', - '_LARGE_TIME_API', - '_OPEN_SYS_FILE_EXT', - '_AE_BIMODAL', - 'PATH_MAX=255' - ], - }, { - 'shared_zos_defines': [ ], - }], - ], - }, - - 'targets': [ - { - 'target_name': 'libuv', - 'type': '<(uv_library)', - 'include_dirs': [ - 'include', - 'src/', - ], - 'defines': [ - '<@(shared_mac_defines)', - '<@(shared_unix_defines)', - '<@(shared_zos_defines)', - ], - 'direct_dependent_settings': { - 'defines': [ - '<@(shared_mac_defines)', - '<@(shared_unix_defines)', - '<@(shared_zos_defines)', - ], - 'include_dirs': [ 'include' ], - 'conditions': [ - ['OS == "linux"', { - 'defines': [ '_POSIX_C_SOURCE=200112' ], - }], - ], - }, - 'sources': [ - 'common.gypi', - 'include/uv.h', - 'include/uv/tree.h', - 'include/uv/errno.h', - 'include/uv/threadpool.h', - 'include/uv/version.h', - 'src/fs-poll.c', - 'src/heap-inl.h', - 'src/idna.c', - 'src/idna.h', - 'src/inet.c', - 'src/queue.h', - 'src/random.c', - 'src/strscpy.c', - 'src/strscpy.h', - 'src/threadpool.c', - 'src/timer.c', - 'src/uv-data-getter-setters.c', - 'src/uv-common.c', - 'src/uv-common.h', - 'src/version.c' - ], - 'xcode_settings': { - 'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES', # -fvisibility=hidden - 'WARNING_CFLAGS': [ - '-Wall', - '-Wextra', - '-Wno-unused-parameter', - '-Wstrict-prototypes', - ], - 'OTHER_CFLAGS': [ '-g', '--std=gnu89' ], - }, - 'conditions': [ - [ 'OS=="win"', { - 'defines': [ - '_WIN32_WINNT=0x0600', - '_GNU_SOURCE', - ], - 'sources': [ - 'include/uv/win.h', - 'src/win/async.c', - 'src/win/atomicops-inl.h', - 'src/win/core.c', - 'src/win/detect-wakeup.c', - 'src/win/dl.c', - 'src/win/error.c', - 'src/win/fs.c', - 'src/win/fs-event.c', - 'src/win/getaddrinfo.c', - 'src/win/getnameinfo.c', - 'src/win/handle.c', - 'src/win/handle-inl.h', - 'src/win/internal.h', - 'src/win/loop-watcher.c', - 'src/win/pipe.c', - 'src/win/thread.c', - 'src/win/poll.c', - 'src/win/process.c', - 'src/win/process-stdio.c', - 'src/win/req-inl.h', - 'src/win/signal.c', - 'src/win/snprintf.c', - 'src/win/stream.c', - 'src/win/stream-inl.h', - 'src/win/tcp.c', - 'src/win/tty.c', - 'src/win/udp.c', - 'src/win/util.c', - 'src/win/winapi.c', - 'src/win/winapi.h', - 'src/win/winsock.c', - 'src/win/winsock.h', - ], - 'link_settings': { - 'libraries': [ - '-ladvapi32', - '-liphlpapi', - '-lpsapi', - '-lshell32', - '-luser32', - '-luserenv', - '-lws2_32' - ], - }, - }, { # Not Windows i.e. POSIX - 'sources': [ - 'include/uv/unix.h', - 'include/uv/linux.h', - 'include/uv/sunos.h', - 'include/uv/darwin.h', - 'include/uv/bsd.h', - 'include/uv/aix.h', - 'src/unix/async.c', - 'src/unix/atomic-ops.h', - 'src/unix/core.c', - 'src/unix/dl.c', - 'src/unix/fs.c', - 'src/unix/getaddrinfo.c', - 'src/unix/getnameinfo.c', - 'src/unix/internal.h', - 'src/unix/loop.c', - 'src/unix/loop-watcher.c', - 'src/unix/pipe.c', - 'src/unix/poll.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', - 'src/unix/tty.c', - 'src/unix/udp.c', - ], - 'link_settings': { - 'libraries': [ '-lm' ], - 'conditions': [ - ['OS=="solaris"', { - 'ldflags': [ '-pthreads' ], - }], - [ 'OS=="zos" and uv_library=="shared_library"', { - 'ldflags': [ '-Wl,DLL' ], - }], - ['OS != "solaris" and OS != "android" and OS != "zos"', { - 'ldflags': [ '-pthread' ], - }], - ], - }, - 'conditions': [ - ['uv_library=="shared_library"', { - 'conditions': [ - ['OS=="zos"', { - 'cflags': [ '-qexportall' ], - }, { - 'cflags': [ '-fPIC' ], - }], - ], - }], - ['uv_library=="shared_library" and OS!="mac" and OS!="zos"', { - # This will cause gyp to set soname - # Must correspond with UV_VERSION_MAJOR - # in include/uv/version.h - 'product_extension': 'so.1', - }], - ], - }], - [ 'OS in "linux mac ios android zos"', { - 'sources': [ 'src/unix/proctitle.c' ], - }], - [ 'OS != "zos"', { - 'cflags': [ - '-fvisibility=hidden', - '-g', - '--std=gnu89', - '-Wall', - '-Wextra', - '-Wno-unused-parameter', - '-Wstrict-prototypes', - ], - }], - [ 'OS in "mac ios"', { - 'sources': [ - 'src/unix/darwin.c', - 'src/unix/fsevents.c', - 'src/unix/darwin-proctitle.c', - 'src/unix/random-getentropy.c', - ], - 'defines': [ - '_DARWIN_USE_64_BIT_INODE=1', - '_DARWIN_UNLIMITED_SELECT=1', - ] - }], - [ 'OS=="linux"', { - 'defines': [ '_GNU_SOURCE' ], - 'sources': [ - 'src/unix/linux-core.c', - 'src/unix/linux-inotify.c', - 'src/unix/linux-syscalls.c', - 'src/unix/linux-syscalls.h', - 'src/unix/procfs-exepath.c', - 'src/unix/random-getrandom.c', - 'src/unix/random-sysctl-linux.c', - 'src/unix/sysinfo-loadavg.c', - ], - 'link_settings': { - 'libraries': [ '-ldl', '-lrt' ], - }, - }], - [ 'OS=="android"', { - 'sources': [ - 'src/unix/linux-core.c', - 'src/unix/linux-inotify.c', - 'src/unix/linux-syscalls.c', - 'src/unix/linux-syscalls.h', - 'src/unix/pthread-fixes.c', - 'src/unix/android-ifaddrs.c', - 'src/unix/procfs-exepath.c', - 'src/unix/random-getrandom.c', - 'src/unix/random-sysctl-linux.c', - 'src/unix/sysinfo-loadavg.c', - ], - 'link_settings': { - 'libraries': [ '-ldl' ], - }, - }], - [ 'OS=="solaris"', { - 'sources': [ - 'src/unix/no-proctitle.c', - 'src/unix/sunos.c', - ], - 'defines': [ - '__EXTENSIONS__', - '_XOPEN_SOURCE=500', - ], - 'link_settings': { - 'libraries': [ - '-lkstat', - '-lnsl', - '-lsendfile', - '-lsocket', - ], - }, - }], - [ 'OS=="aix"', { - 'variables': { - 'os_name': ' + + + + true + + + diff --git a/vcbuild.bat b/vcbuild.bat deleted file mode 100644 index c195394..0000000 --- a/vcbuild.bat +++ /dev/null @@ -1,184 +0,0 @@ -@echo off - -cd %~dp0 - -if /i "%1"=="help" goto help -if /i "%1"=="--help" goto help -if /i "%1"=="-help" goto help -if /i "%1"=="/help" goto help -if /i "%1"=="?" goto help -if /i "%1"=="-?" goto help -if /i "%1"=="--?" goto help -if /i "%1"=="/?" goto help - -@rem Process arguments. -set config= -set target=Build -set target_arch=ia32 -set target_env= -set noprojgen= -set nobuild= -set run= -set vs_toolset=x86 -set msbuild_platform=WIN32 -set library=static_library - -:next-arg -if "%1"=="" goto args-done -if /i "%1"=="debug" set config=Debug&goto arg-ok -if /i "%1"=="release" set config=Release&goto arg-ok -if /i "%1"=="test" set run=run-tests.exe&goto arg-ok -if /i "%1"=="bench" set run=run-benchmarks.exe&goto arg-ok -if /i "%1"=="clean" set target=Clean&goto arg-ok -if /i "%1"=="vs2017" set target_env=vs2017&goto arg-ok -if /i "%1"=="noprojgen" set noprojgen=1&goto arg-ok -if /i "%1"=="nobuild" set nobuild=1&goto arg-ok -if /i "%1"=="x86" set target_arch=ia32&set msbuild_platform=WIN32&set vs_toolset=x86&goto arg-ok -if /i "%1"=="ia32" set target_arch=ia32&set msbuild_platform=WIN32&set vs_toolset=x86&goto arg-ok -if /i "%1"=="x64" set target_arch=x64&set msbuild_platform=x64&set vs_toolset=x64&goto arg-ok -if /i "%1"=="shared" set library=shared_library&goto arg-ok -if /i "%1"=="static" set library=static_library&goto arg-ok -:arg-ok -shift -goto next-arg -:args-done - -if defined WindowsSDKDir goto select-target -if defined VCINSTALLDIR goto select-target - -@rem Look for Visual Studio 2017 only if explicitly requested. -if "%target_env%" NEQ "vs2017" goto vs-set-2015 -echo Looking for Visual Studio 2017 -@rem Check if VS2017 is already setup, and for the requested arch. -if "_%VisualStudioVersion%_" == "_15.0_" if "_%VSCMD_ARG_TGT_ARCH%_"=="_%vs_toolset%_" goto found_vs2017 -set "VSINSTALLDIR=" -call tools\vswhere_usability_wrapper.cmd -if "_%VCINSTALLDIR%_" == "__" goto vs-set-2015 -@rem Need to clear VSINSTALLDIR for vcvarsall to work as expected. -@rem Keep current working directory after call to vcvarsall -set "VSCMD_START_DIR=%CD%" -set vcvars_call="%VCINSTALLDIR%\Auxiliary\Build\vcvarsall.bat" %vs_toolset% -echo calling: %vcvars_call% -call %vcvars_call% - -:found_vs2017 -echo Found MSVS version %VisualStudioVersion% -if %VSCMD_ARG_TGT_ARCH%==x64 set target_arch=x64&set msbuild_platform=x64&set vs_toolset=x64 -set GYP_MSVS_VERSION=2017 -goto select-target - - -@rem Look for Visual Studio 2015 -:vs-set-2015 -if not defined VS140COMNTOOLS goto vc-set-2013 -if not exist "%VS140COMNTOOLS%\..\..\vc\vcvarsall.bat" goto vc-set-2013 -call "%VS140COMNTOOLS%\..\..\vc\vcvarsall.bat" %vs_toolset% -set GYP_MSVS_VERSION=2015 -echo Using Visual Studio 2015 -goto select-target - -:vc-set-2013 -@rem Look for Visual Studio 2013 -if not defined VS120COMNTOOLS goto vc-set-2012 -if not exist "%VS120COMNTOOLS%\..\..\vc\vcvarsall.bat" goto vc-set-2012 -call "%VS120COMNTOOLS%\..\..\vc\vcvarsall.bat" %vs_toolset% -set GYP_MSVS_VERSION=2013 -echo Using Visual Studio 2013 -goto select-target - -:vc-set-2012 -@rem Look for Visual Studio 2012 -if not defined VS110COMNTOOLS goto vc-set-2010 -if not exist "%VS110COMNTOOLS%\..\..\vc\vcvarsall.bat" goto vc-set-2010 -call "%VS110COMNTOOLS%\..\..\vc\vcvarsall.bat" %vs_toolset% -set GYP_MSVS_VERSION=2012 -echo Using Visual Studio 2012 -goto select-target - -:vc-set-2010 -@rem Look for Visual Studio 2010 -if not defined VS100COMNTOOLS goto vc-set-2008 -if not exist "%VS100COMNTOOLS%\..\..\vc\vcvarsall.bat" goto vc-set-2008 -call "%VS100COMNTOOLS%\..\..\vc\vcvarsall.bat" %vs_toolset% -set GYP_MSVS_VERSION=2010 -echo Using Visual Studio 2010 -goto select-target - -:vc-set-2008 -@rem Look for Visual Studio 2008 -if not defined VS90COMNTOOLS goto vc-set-notfound -if not exist "%VS90COMNTOOLS%\..\..\vc\vcvarsall.bat" goto vc-set-notfound -call "%VS90COMNTOOLS%\..\..\vc\vcvarsall.bat" %vs_toolset% -set GYP_MSVS_VERSION=2008 -echo Using Visual Studio 2008 -goto select-target - -:vc-set-notfound -echo Warning: Visual Studio not found - -:select-target -if not "%config%"=="" goto project-gen -if "%run%"=="run-tests.exe" set config=Debug& goto project-gen -if "%run%"=="run-benchmarks.exe" set config=Release& goto project-gen -set config=Debug - -:project-gen -@rem Skip project generation if requested. -if defined noprojgen goto msbuild - -@rem Generate the VS project. -if exist build\gyp goto have_gyp -echo git clone https://chromium.googlesource.com/external/gyp build/gyp -git clone https://chromium.googlesource.com/external/gyp build/gyp -if errorlevel 1 goto gyp_install_failed -goto have_gyp - -:gyp_install_failed -echo Failed to download gyp. Make sure you have git installed, or -echo manually install gyp into %~dp0build\gyp. -exit /b 1 - -:have_gyp -if not defined PYTHON set PYTHON=python -"%PYTHON%" gyp_uv.py -Dtarget_arch=%target_arch% -Duv_library=%library% -if errorlevel 1 goto create-msvs-files-failed -if not exist uv.sln goto create-msvs-files-failed -echo Project files generated. - -:msbuild -@rem Skip project generation if requested. -if defined nobuild goto run - -@rem Check if VS build env is available -if defined VCINSTALLDIR goto msbuild-found -if defined WindowsSDKDir goto msbuild-found -echo Build skipped. To build, this file needs to run from VS cmd prompt. -goto run - -@rem Build the sln with msbuild. -:msbuild-found -msbuild uv.sln /t:%target% /p:Configuration=%config% /p:Platform="%msbuild_platform%" /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo -if errorlevel 1 exit /b 1 -msbuild test\test.sln /t:%target% /p:Configuration=%config% /p:Platform="%msbuild_platform%" /clp:NoSummary;NoItemAndPropertyList;Verbosity=minimal /nologo -if errorlevel 1 exit /b 1 - -:run -@rem Run tests if requested. -if "%run%"=="" goto exit -echo running 'test\%config%\%run%' -test\%config%\%run% -goto exit - -:create-msvs-files-failed -echo Failed to create vc project files. -exit /b 1 - -:help -echo vcbuild.bat [debug/release] [test/bench] [clean] [noprojgen] [nobuild] [vs2017] [x86/x64] [static/shared] -echo Examples: -echo vcbuild.bat : builds debug build -echo vcbuild.bat test : builds debug build and runs tests -echo vcbuild.bat release bench: builds release build and runs benchmarks -goto exit - -:exit -- Gitee