From e2ebeeeff6a7ecf4527989e76705977645b5619c Mon Sep 17 00:00:00 2001 From: Packit Service Date: Jan 27 2021 00:20:29 +0000 Subject: libuv-1.40.0 base --- diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 008de82..c384123 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -3,6 +3,20 @@ name: CI on: [push, pull_request] jobs: + build-android: + runs-on: ubuntu-latest + container: reactnativecommunity/react-native-android:2020-5-20 + steps: + - uses: actions/checkout@v2 + - name: Envinfo + run: npx envinfo + - name: Build android arm64 + # see build options you can use in https://developer.android.com/ndk/guides/cmake + run: | + mkdir build && cd build + $ANDROID_HOME/cmake/3.10.2.4988404/bin/cmake -DCMAKE_TOOLCHAIN_FILE=$ANDROID_HOME/ndk/20.0.5594570/build/cmake/android.toolchain.cmake -DCMAKE_BUILD_TYPE=Release -DANDROID_ABI="arm64-v8a" -DANDROID_PLATFORM=android-21 .. + $ANDROID_HOME/cmake/3.10.2.4988404/bin/cmake --build . + build-cross-qemu: runs-on: ubuntu-latest name: build-cross-qemu-${{ matrix.config.target }} @@ -37,11 +51,12 @@ jobs: 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` + 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: | diff --git a/.mailmap b/.mailmap index 2ae2968..56a80f5 100644 --- a/.mailmap +++ b/.mailmap @@ -27,6 +27,7 @@ Maciej Małecki Marc Schlaich Michael Michael Neumann +Michael Penick Nicholas Vavilov Nick Logan Rasmus Christian Pedersen @@ -41,10 +42,12 @@ Santiago Gimeno Saúl Ibarra Corretgé Saúl Ibarra Corretgé Shigeki Ohtsu +TK-one Timothy J. Fontaine Yasuhiro Matsumoto Yazhong Liu Yuki Okumura +gengjiawen jBarz jBarz ptlomholt diff --git a/AUTHORS b/AUTHORS index 222367e..e7c789c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -432,3 +432,19 @@ 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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 0496d36..e648b00 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,8 @@ 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) +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>) @@ -76,6 +78,7 @@ set(lint-no-unsafe-msvc $<$:/wd4996>) 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}) @@ -90,6 +93,7 @@ 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} ) set(uv_sources src/fs-poll.c @@ -107,6 +111,8 @@ if(WIN32) list(APPEND uv_defines WIN32_LEAN_AND_MEAN _WIN32_WINNT=0x0600) list(APPEND uv_libraries psapi + user32 + advapi32 iphlpapi userenv ws2_32) @@ -140,7 +146,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 MATCHES "Android|OS390") + 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. @@ -182,6 +188,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "AIX") endif() if(CMAKE_SYSTEM_NAME STREQUAL "Android") + list(APPEND uv_defines _GNU_SOURCE) list(APPEND uv_libraries dl) list(APPEND uv_sources src/unix/android-ifaddrs.c @@ -192,8 +199,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Android") src/unix/pthread-fixes.c src/unix/random-getentropy.c src/unix/random-getrandom.c - src/unix/random-sysctl-linux.c - src/unix/sysinfo-loadavg.c) + src/unix/random-sysctl-linux.c) endif() if(APPLE OR CMAKE_SYSTEM_NAME MATCHES "Android|Linux|OS390") @@ -206,7 +212,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") @@ -238,12 +243,12 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") src/unix/linux-syscalls.c src/unix/procfs-exepath.c src/unix/random-getrandom.c - src/unix/random-sysctl-linux.c - src/unix/sysinfo-loadavg.c) + src/unix/random-sysctl-linux.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") @@ -284,7 +289,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL "OS400") src/unix/aix-common.c src/unix/ibmi.c src/unix/no-fsevents.c - src/unix/no-proctitle.c src/unix/posix-poll.c) endif() @@ -294,6 +298,30 @@ if(CMAKE_SYSTEM_NAME STREQUAL "SunOS") 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_cflags -fno-strict-aliasing) + list(APPEND uv_libraries socket) +endif() + if(APPLE OR CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD|Linux|NetBSD|OpenBSD") list(APPEND uv_test_libraries util) endif() @@ -417,6 +445,7 @@ 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-osx-select.c @@ -474,6 +503,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 @@ -483,6 +513,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 @@ -500,6 +531,7 @@ if(LIBUV_BUILD_TESTS) 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 @@ -510,6 +542,7 @@ 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 @@ -541,7 +574,7 @@ if(LIBUV_BUILD_TESTS) WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) endif() -if(UNIX) +if(UNIX OR MINGW) # Now for some gibbering horrors from beyond the stars... foreach(lib IN LISTS uv_libraries) list(APPEND LIBS "-l${lib}") @@ -559,16 +592,17 @@ if(UNIX) 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 + install(FILES ${PROJECT_BINARY_DIR}/libuv.pc ${PROJECT_BINARY_DIR}/libuv-static.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) install(TARGETS uv LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(TARGETS uv_a ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() -if(WIN32) +if(MSVC) install(DIRECTORY include/ DESTINATION include) install(FILES LICENSE DESTINATION .) install(TARGETS uv uv_a diff --git a/ChangeLog b/ChangeLog index b5e8684..8788d94 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,154 @@ -2020.05.18, Version 1.38.0 (Stable) +2020.09.26, Version 1.40.0 (Stable) + +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: diff --git a/Makefile.am b/Makefile.am index d9d2f3d..46308ea 100644 --- a/Makefile.am +++ b/Makefile.am @@ -203,6 +203,7 @@ 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-osx-select.c \ @@ -259,6 +260,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 \ @@ -269,6 +271,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 \ @@ -286,6 +289,7 @@ test_run_tests_SOURCES = test/blackhole-server.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 \ @@ -296,6 +300,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 \ @@ -373,12 +378,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_CFLAGS += -D_GNU_SOURCE libuv_la_SOURCES += src/unix/android-ifaddrs.c \ src/unix/linux-core.c \ src/unix/linux-inotify.c \ @@ -386,8 +391,7 @@ libuv_la_SOURCES += src/unix/android-ifaddrs.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 + src/unix/random-sysctl-linux.c endif if CYGWIN @@ -468,8 +472,7 @@ libuv_la_SOURCES += src/unix/linux-core.c \ src/unix/procfs-exepath.c \ src/unix/proctitle.c \ src/unix/random-getrandom.c \ - src/unix/random-sysctl-linux.c \ - src/unix/sysinfo-loadavg.c + src/unix/random-sysctl-linux.c test_run_tests_LDFLAGS += -lutil endif diff --git a/configure.ac b/configure.ac index 6616972..1a66b74 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.38.0], [https://github.com/libuv/libuv/issues]) +AC_INIT([libuv], [1.40.0], [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]) diff --git a/docs/make.bat b/docs/make.bat index aa7089a..10eb94b 100644 --- a/docs/make.bat +++ b/docs/make.bat @@ -1,243 +1,243 @@ -@ECHO OFF - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set BUILDDIR=build -set SRCDIR=src -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% %SRCDIR% -set I18NSPHINXOPTS=%SPHINXOPTS% %SRCDIR% -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% - set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% -) - -if "%1" == "" goto help - -if "%1" == "help" ( - :help - echo.Please use `make ^` where ^ is one of - echo. html to make standalone HTML files - echo. dirhtml to make HTML files named index.html in directories - echo. singlehtml to make a single large HTML file - echo. pickle to make pickle files - echo. json to make JSON files - echo. htmlhelp to make HTML files and a HTML help project - echo. qthelp to make HTML files and a qthelp project - echo. devhelp to make HTML files and a Devhelp project - echo. epub to make an epub - echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter - echo. text to make text files - echo. man to make manual pages - echo. texinfo to make Texinfo files - echo. gettext to make PO message catalogs - echo. changes to make an overview over all changed/added/deprecated items - echo. xml to make Docutils-native XML files - echo. pseudoxml to make pseudoxml-XML files for display purposes - echo. linkcheck to check all external links for integrity - echo. doctest to run all doctests embedded in the documentation if enabled - goto end -) - -if "%1" == "clean" ( - for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i - del /q /s %BUILDDIR%\* - goto end -) - - -%SPHINXBUILD% 2> nul -if errorlevel 9009 ( - echo. - echo.The 'sphinx-build' command was not found. Make sure you have Sphinx - echo.installed, then set the SPHINXBUILD environment variable to point - echo.to the full path of the 'sphinx-build' executable. Alternatively you - echo.may add the Sphinx directory to PATH. - echo. - echo.If you don't have Sphinx installed, grab it from - echo.http://sphinx-doc.org/ - exit /b 1 -) - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "htmlhelp" ( - %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run HTML Help Workshop with the ^ -.hhp project file in %BUILDDIR%/htmlhelp. - goto end -) - -if "%1" == "qthelp" ( - %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; now you can run "qcollectiongenerator" with the ^ -.qhcp project file in %BUILDDIR%/qthelp, like this: - echo.^> qcollectiongenerator %BUILDDIR%\qthelp\libuv.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\libuv.ghc - goto end -) - -if "%1" == "devhelp" ( - %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - if errorlevel 1 exit /b 1 - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "latexpdf" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - cd %BUILDDIR%/latex - make all-pdf - cd %BUILDDIR%/.. - echo. - echo.Build finished; the PDF files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "latexpdfja" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - cd %BUILDDIR%/latex - make all-pdf-ja - cd %BUILDDIR%/.. - echo. - echo.Build finished; the PDF files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "text" ( - %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The text files are in %BUILDDIR%/text. - goto end -) - -if "%1" == "man" ( - %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The manual pages are in %BUILDDIR%/man. - goto end -) - -if "%1" == "texinfo" ( - %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. - goto end -) - -if "%1" == "gettext" ( - %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The message catalogs are in %BUILDDIR%/locale. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - if errorlevel 1 exit /b 1 - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "linkcheck" ( - %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck - if errorlevel 1 exit /b 1 - echo. - echo.Link check complete; look for any errors in the above output ^ -or in %BUILDDIR%/linkcheck/output.txt. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - if errorlevel 1 exit /b 1 - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -if "%1" == "xml" ( - %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The XML files are in %BUILDDIR%/xml. - goto end -) - -if "%1" == "pseudoxml" ( - %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml - if errorlevel 1 exit /b 1 - echo. - echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. - goto end -) - -:end +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=build +set SRCDIR=src +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% %SRCDIR% +set I18NSPHINXOPTS=%SPHINXOPTS% %SRCDIR% +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\libuv.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\libuv.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end 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/errors.rst b/docs/src/errors.rst index b8f971f..c2daa85 100644 --- a/docs/src/errors.rst +++ b/docs/src/errors.rst @@ -319,11 +319,23 @@ 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 + 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 73666f3..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. @@ -122,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`. @@ -535,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/filesystem.rst b/docs/src/guide/filesystem.rst index 63dbbe4..e89d4cd 100644 --- a/docs/src/guide/filesystem.rst +++ b/docs/src/guide/filesystem.rst @@ -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 diff --git a/docs/src/guide/networking.rst b/docs/src/guide/networking.rst index e61f899..b2f3fc7 100644 --- a/docs/src/guide/networking.rst +++ b/docs/src/guide/networking.rst @@ -200,7 +200,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 @@ -235,7 +235,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..cbd8bce 100644 --- a/docs/src/guide/processes.rst +++ b/docs/src/guide/processes.rst @@ -266,10 +266,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 diff --git a/docs/src/guide/utilities.rst b/docs/src/guide/utilities.rst index a9fe56c..a863c5e 100644 --- a/docs/src/guide/utilities.rst +++ b/docs/src/guide/utilities.rst @@ -243,7 +243,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 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/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 906ca8f..b2725c3 100644 --- a/docs/src/misc.rst +++ b/docs/src/misc.rst @@ -261,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: @@ -275,22 +275,37 @@ 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. @@ -425,7 +440,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) @@ -502,11 +518,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) diff --git a/docs/src/process.rst b/docs/src/process.rst index f2b3be2..8ff19ad 100644 --- a/docs/src/process.rst +++ b/docs/src/process.rst @@ -102,7 +102,7 @@ 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. @@ -131,43 +131,43 @@ Data types 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 +178,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 +188,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 5807ccb..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 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/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 6be2034..30aa459 100644 --- a/docs/src/udp.rst +++ b/docs/src/udp.rst @@ -48,6 +48,12 @@ Data types */ 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 that recvmmsg should be used, if available. */ UV_UDP_RECVMMSG = 256 @@ -80,15 +86,17 @@ Data types When using :man:`recvmmsg(2)`, chunks will have the `UV_UDP_MMSG_CHUNK` flag set, those must not be freed. 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. This is a good chance for the callee to - free the provided buffer. + the `UV_UDP_MMSG_CHUNK` flag cleared and the `UV_UDP_MMSG_FREE` flag set. + 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. @@ -391,6 +399,16 @@ API .. 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) diff --git a/include/uv.h b/include/uv.h index fec6631..2557961 100644 --- a/include/uv.h +++ b/include/uv.h @@ -247,7 +247,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 { @@ -613,6 +614,12 @@ enum uv_udp_flags { * must not be freed by the recv_cb callback. */ 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 that recvmmsg should be used, if available. @@ -693,6 +700,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); @@ -863,6 +871,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); /* @@ -1191,12 +1200,12 @@ UV_EXTERN uv_pid_t uv_os_getppid(void); #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) +# 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 @@ -1243,6 +1252,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, @@ -1774,9 +1784,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/errno.h b/include/uv/errno.h index 165fd11..aadce9c 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) diff --git a/include/uv/unix.h b/include/uv/unix.h index 3a13163..e3cf7bd 100644 --- a/include/uv/unix.h +++ b/include/uv/unix.h @@ -69,6 +69,8 @@ # include "uv/posix.h" #elif defined(__HAIKU__) # include "uv/posix.h" +#elif defined(__QNX__) +# include "uv/posix.h" #endif #ifndef NI_MAXHOST diff --git a/include/uv/version.h b/include/uv/version.h index 486658c..5272008 100644 --- a/include/uv/version.h +++ b/include/uv/version.h @@ -31,7 +31,7 @@ */ #define UV_VERSION_MAJOR 1 -#define UV_VERSION_MINOR 38 +#define UV_VERSION_MINOR 40 #define UV_VERSION_PATCH 0 #define UV_VERSION_IS_RELEASE 1 #define UV_VERSION_SUFFIX "" 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 index c44e4c2..bb91e50 100644 --- a/m4/.gitignore +++ b/m4/.gitignore @@ -1,4 +1,5 @@ # Ignore libtoolize-generated files. *.m4 !as_case.m4 +!ax_pthread.m4 !libuv-check-flags.m4 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..cc78149 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_ diff --git a/src/timer.c b/src/timer.c index 4cf4ed4..1bea2a8 100644 --- a/src/timer.c +++ b/src/timer.c @@ -130,6 +130,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 6b4594b..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); diff --git a/src/unix/bsd-ifaddrs.c b/src/unix/bsd-ifaddrs.c index a3385af..5223ab4 100644 --- a/src/unix/bsd-ifaddrs.c +++ b/src/unix/bsd-ifaddrs.c @@ -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); diff --git a/src/unix/core.c b/src/unix/core.c index 949eefa..1597828 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -79,10 +79,6 @@ extern char** environ; # endif #endif -#if defined(__ANDROID_API__) && __ANDROID_API__ < 21 -# include /* for dlsym */ -#endif - #if defined(__MVS__) #include #endif @@ -220,15 +216,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; @@ -379,6 +383,14 @@ int uv_run(uv_loop_t* loop, uv_run_mode mode) { 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); @@ -662,7 +674,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; @@ -671,7 +683,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); } @@ -1142,13 +1154,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; @@ -1531,3 +1536,78 @@ 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; +} diff --git a/src/unix/darwin-stub.h b/src/unix/darwin-stub.h index b93cf67..433e3ef 100644 --- a/src/unix/darwin-stub.h +++ b/src/unix/darwin-stub.h @@ -27,6 +27,7 @@ struct CFArrayCallBacks; struct CFRunLoopSourceContext; struct FSEventStreamContext; +struct CFRange; typedef double CFAbsoluteTime; typedef double CFTimeInterval; @@ -42,13 +43,23 @@ 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, @@ -69,6 +80,11 @@ struct FSEventStreamContext { void* pad[3]; }; +struct CFRange { + CFIndex location; + CFIndex length; +}; + static const CFStringEncoding kCFStringEncodingUTF8 = 0x8000100; static const OSStatus noErr = 0; diff --git a/src/unix/darwin.c b/src/unix/darwin.c index 654aba2..d0ecd45 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,15 @@ #include #include /* sysconf */ +#if !TARGET_OS_IPHONE +#include "darwin-stub.h" +#endif + +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 +58,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 +185,149 @@ 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/" + "Versions/A/CoreFoundation", + RTLD_LAZY | RTLD_LOCAL); + iokit_handle = dlopen("/System/Library/Frameworks/IOKit.framework/" + "Versions/A/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) { + uint32_t freq; + CFIndex len = pCFDataGetLength(freq_ref); + CFRange range; + range.location = 0; + range.length = len; + + pCFDataGetBytes(freq_ref, range, (UInt8*)&freq); + *speed = freq; + pCFRelease(freq_ref); + pCFRelease(data); + break; + } + } + pCFRelease(data); + } + + service = pIOIteratorNext(it); + } + + pIOObjectRelease(it); + + err = 0; +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 +335,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/freebsd.c b/src/unix/freebsd.c index ef77e12..fe795a0 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,25 +264,18 @@ 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) { +int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { #if __FreeBSD__ >= 11 - return sendmmsg(fd, mmsg, vlen, flags); + return 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) { +int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { #if __FreeBSD__ >= 11 - return recvmmsg(fd, mmsg, vlen, flags, timeout); + return recvmmsg(fd, mmsg, vlen, 0 /* flags */, NULL /* timeout */); #else return errno = ENOSYS, -1; #endif diff --git a/src/unix/fs.c b/src/unix/fs.c index f5b2b94..556fd10 100644 --- a/src/unix/fs.c +++ b/src/unix/fs.c @@ -79,7 +79,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 @@ -229,11 +233,7 @@ static ssize_t uv__fs_futime(uv_fs_t* req) { struct timespec ts[2]; ts[0] = uv__fs_to_timespec(req->atime); ts[1] = uv__fs_to_timespec(req->mtime); -#if defined(__ANDROID_API__) && __ANDROID_API__ < 21 - return utimensat(req->file, NULL, ts, 0); -#else return futimens(req->file, ts); -#endif #elif defined(__APPLE__) \ || defined(__DragonFly__) \ || defined(__FreeBSD__) \ @@ -310,13 +310,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) @@ -325,11 +326,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 */ @@ -351,6 +352,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; } @@ -460,7 +464,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); @@ -472,7 +476,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; } } @@ -629,7 +633,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)) @@ -646,7 +654,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; @@ -887,8 +900,27 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) { ssize_t r; off = req->off; + +#ifdef __linux__ + { + static int copy_file_range_support = 1; + + if (copy_file_range_support) { + r = uv__fs_copy_file_range(in_fd, NULL, out_fd, &off, req->bufsml[0].len, 0); + + if (r == -1 && errno == ENOSYS) { + errno = 0; + copy_file_range_support = 0; + } else { + goto ok; + } + } + } +#endif + r = sendfile(out_fd, in_fd, &off, req->bufsml[0].len); +ok: /* 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 * the offset has been updated. @@ -1131,7 +1163,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; @@ -1150,16 +1182,26 @@ 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) { @@ -1355,7 +1397,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; @@ -1388,7 +1430,7 @@ 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; } @@ -2031,7 +2073,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 && diff --git a/src/unix/ibmi.c b/src/unix/ibmi.c index ff300ea..96efc02 100644 --- a/src/unix/ibmi.c +++ b/src/unix/ibmi.c @@ -58,6 +58,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; @@ -171,6 +174,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 */ @@ -459,3 +465,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) { +} \ No newline at end of file diff --git a/src/unix/internal.h b/src/unix/internal.h index 402ee87..570274e 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -62,9 +62,7 @@ # include #endif -#if defined(_POSIX_PATH_MAX) -# define UV__PATH_MAX _POSIX_PATH_MAX -#elif defined(PATH_MAX) +#if defined(PATH_MAX) # define UV__PATH_MAX PATH_MAX #else # define UV__PATH_MAX 8192 @@ -268,6 +266,7 @@ 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); /* random */ int uv__random_devurandom(void* buf, size_t buflen); @@ -335,15 +334,8 @@ struct uv__mmsghdr { 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 diff --git a/src/unix/kqueue.c b/src/unix/kqueue.c index ad09f40..bf183d5 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; } @@ -129,6 +129,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 +204,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 +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 +261,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; @@ -276,6 +306,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; @@ -337,16 +368,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, 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; @@ -487,7 +527,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 +562,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 99cbb1c..4db2f05 100644 --- a/src/unix/linux-core.c +++ b/src/unix/linux-core.c @@ -85,17 +85,7 @@ 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. @@ -208,8 +198,10 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { * 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; + 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; @@ -226,6 +218,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)); @@ -281,7 +275,31 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { 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. */ @@ -293,25 +311,24 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { 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) + 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) + if (nfds == -1 && errno == ENOSYS) { + uv__store_relaxed(&no_epoll_wait_cached, 1); no_epoll_wait = 1; + } } if (sigmask != 0 && no_epoll_pwait != 0) @@ -327,6 +344,14 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { if (nfds == 0) { assert(timeout != -1); + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (timeout == -1) + continue; + if (timeout == 0) return; @@ -346,6 +371,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; @@ -425,17 +455,26 @@ 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->events); + } 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; @@ -483,18 +522,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. */ @@ -982,43 +1025,51 @@ void uv__set_process_title(const char* title) { } -static uint64_t uv__read_proc_meminfo(const char* what) { - uint64_t rc; +static int uv__slurp(const char* filename, char* buf, size_t len) { 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); + assert(len > 0); + fd = uv__open_cloexec(filename, O_RDONLY); if (fd < 0) - return 0; + return fd; - n = read(fd, buf, sizeof(buf) - 1); + do + n = read(fd, buf, len - 1); + while (n == -1 && errno == EINTR); - if (n <= 0) - goto out; + if (uv__close_nocheckstdio(fd)) + abort(); + + if (n < 0) + return UV__ERR(errno); 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; +static uint64_t uv__read_proc_meminfo(const char* what) { + uint64_t rc; + char* p; + char buf[4096]; /* Large enough to hold all of /proc/meminfo. */ + + if (uv__slurp("/proc/meminfo", buf, sizeof(buf))) + return 0; - rc *= 1024; + p = strstr(buf, what); -out: + if (p == NULL) + return 0; - if (uv__close_nocheckstdio(fd)) - abort(); + p += strlen(what); - return rc; + rc = 0; + sscanf(p, "%" PRIu64 " kB", &rc); + + return rc * 1024; } @@ -1056,28 +1107,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; } @@ -1091,3 +1127,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-syscalls.c b/src/unix/linux-syscalls.c index 742f26a..44daaf1 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 @@ -180,6 +220,28 @@ int uv__dup3(int oldfd, int newfd, int flags) { } +ssize_t +uv__fs_copy_file_range(int fd_in, + ssize_t* off_in, + int fd_out, + ssize_t* off_out, + size_t len, + unsigned int flags) +{ +#ifdef __NR_copy_file_range + return syscall(__NR_copy_file_range, + fd_in, + off_in, + fd_out, + off_out, + len, + flags); +#else + return errno = ENOSYS, -1; +#endif +} + + int uv__statx(int dirfd, const char* path, int flags, diff --git a/src/unix/linux-syscalls.h b/src/unix/linux-syscalls.h index 2e8fa2a..761ff32 100644 --- a/src/unix/linux-syscalls.h +++ b/src/unix/linux-syscalls.h @@ -64,6 +64,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, + ssize_t* off_in, + int fd_out, + ssize_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 e5b2889..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,13 @@ 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; @@ -146,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); @@ -181,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/os390-syscalls.c b/src/unix/os390-syscalls.c index 424cc48..491e950 100644 --- a/src/unix/os390-syscalls.c +++ b/src/unix/os390-syscalls.c @@ -33,7 +33,6 @@ #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; diff --git a/src/unix/os390.c b/src/unix/os390.c index dce169b..3bb4426 100644 --- a/src/unix/os390.c +++ b/src/unix/os390.c @@ -254,8 +254,6 @@ 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) @@ -266,69 +264,7 @@ int uv_exepath(char* buffer, size_t* size) { 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); } @@ -818,6 +754,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 +808,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 +839,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 +861,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; @@ -954,6 +920,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 +928,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. */ diff --git a/src/unix/posix-poll.c b/src/unix/posix-poll.c index 766e832..0f4bf93 100644 --- a/src/unix/posix-poll.c +++ b/src/unix/posix-poll.c @@ -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/proctitle.c b/src/unix/proctitle.c index 4ee991f..9ffe5b6 100644 --- a/src/unix/proctitle.c +++ b/src/unix/proctitle.c @@ -100,6 +100,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); @@ -126,6 +130,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); 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 1c83e09..f40a3e5 100644 --- a/src/unix/signal.c +++ b/src/unix/signal.c @@ -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(); diff --git a/src/unix/sunos.c b/src/unix/sunos.c index 180cc84..d511c18 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; diff --git a/src/unix/tcp.c b/src/unix/tcp.c index d47e943..18acd20 100644 --- a/src/unix/tcp.c +++ b/src/unix/tcp.c @@ -326,16 +326,19 @@ 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; + 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) diff --git a/src/unix/thread.c b/src/unix/thread.c index c9a18d1..1a85d1d 100644 --- a/src/unix/thread.c +++ b/src/unix/thread.c @@ -709,11 +709,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) @@ -805,16 +803,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/udp.c b/src/unix/udp.c index 7cf80ef..7d699a1 100644 --- a/src/unix/udp.c +++ b/src/unix/udp.c @@ -73,12 +73,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; } @@ -213,7 +213,7 @@ static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) { } 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) { @@ -238,7 +238,7 @@ static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) { /* one last callback so the original buffer is freed */ if (handle->recv_cb != NULL) - handle->recv_cb(handle, 0, buf, NULL, 0); + handle->recv_cb(handle, 0, buf, NULL, UV_UDP_MMSG_FREE); } return nread; } @@ -270,14 +270,11 @@ static void uv__udp_recvmsg(uv_udp_t* handle) { assert(buf.base != NULL); #if HAVE_MMSG - if (handle->flags & UV_HANDLE_UDP_RECVMMSG) { - uv_once(&once, uv__udp_mmsg_init); - if (uv__recvmmsg_avail) { - 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 @@ -359,7 +356,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) { @@ -367,7 +364,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); @@ -854,7 +851,11 @@ 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__) static int uv__udp_set_source_membership4(uv_udp_t* handle, const struct sockaddr_in* multicast_addr, const char* interface_addr, @@ -976,6 +977,17 @@ int uv__udp_init_ex(uv_loop_t* loop, } +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; +} + + int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { int err; @@ -1031,7 +1043,11 @@ 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__) int err; union uv__sockaddr mcast_addr; union uv__sockaddr src_addr; @@ -1138,7 +1154,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, @@ -1147,7 +1163,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, @@ -1155,7 +1171,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__) */ } @@ -1167,7 +1183,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, @@ -1175,7 +1191,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, @@ -1192,7 +1208,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, @@ -1200,7 +1216,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 a25d6aa..602e5f4 100644 --- a/src/uv-common.c +++ b/src/uv-common.c @@ -859,11 +859,70 @@ __attribute__((destructor)) void uv_library_shutdown(void) { static int was_shutdown; - if (was_shutdown) + if (uv__load_relaxed(&was_shutdown)) return; uv__process_title_cleanup(); uv__signal_cleanup(); uv__threadpool_cleanup(); - was_shutdown = 1; + 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 0b0f5f8..e851291 100644 --- a/src/uv-common.h +++ b/src/uv-common.h @@ -60,6 +60,14 @@ 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 + /* Handle flags. Some flags are specific to Windows or UNIX. */ enum { /* Used by all handles. */ @@ -325,6 +333,12 @@ void uv__threadpool_cleanup(void); } \ 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); @@ -334,4 +348,21 @@ 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/core.c b/src/win/core.c index 9974a11..e53a0f8 100644 --- a/src/win/core.c +++ b/src/win/core.c @@ -222,6 +222,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 +234,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 +307,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; @@ -317,6 +332,7 @@ void uv__once_init(void) { void uv__loop_close(uv_loop_t* loop) { + uv__loop_internal_fields_t* lfields; size_t i; uv__loops_remove(loop); @@ -347,11 +363,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; } @@ -393,16 +422,44 @@ 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); @@ -445,10 +502,26 @@ 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++) { + /* 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), @@ -456,6 +529,18 @@ static void uv__poll(uv_loop_t* loop, DWORD timeout) { 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++) { /* Package was dequeued, but see if it is not a empty package @@ -534,6 +619,12 @@ 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); 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/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 9577bc0..8a80174 100644 --- a/src/win/fs.c +++ b/src/win/fs.c @@ -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) \ @@ -730,14 +727,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; } @@ -816,10 +813,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, @@ -938,7 +935,7 @@ 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; @@ -963,7 +960,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) { @@ -1014,8 +1010,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, @@ -1128,7 +1124,10 @@ void fs__write(uv_fs_t* req) { 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); } @@ -1221,12 +1220,12 @@ 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->result = UV_EINVAL; } } @@ -1242,19 +1241,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 = 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; @@ -1265,25 +1266,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; } @@ -1404,7 +1409,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)) { @@ -1895,7 +1900,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); } @@ -1930,7 +1935,7 @@ static void fs__fstat(uv_fs_t* req) { } req->ptr = &req->statbuf; - req->result = 0; + SET_REQ_RESULT(req, 0); } @@ -2157,7 +2162,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); } @@ -2315,7 +2323,7 @@ INLINE static void fs__utime_impl(uv_fs_t* req, int do_lutime) { return; } - req->result = 0; + SET_REQ_RESULT(req, 0); } static void fs__utime(uv_fs_t* req) { @@ -2340,7 +2348,7 @@ 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) { @@ -2350,11 +2358,10 @@ static void fs__lutime(uv_fs_t* req) { 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); } @@ -2674,17 +2681,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); } @@ -2829,7 +2836,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); diff --git a/src/win/internal.h b/src/win/internal.h index 058ddb8..b096255 100644 --- a/src/win/internal.h +++ b/src/win/internal.h @@ -266,7 +266,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); diff --git a/src/win/pipe.c b/src/win/pipe.c index fc0112a..f81245e 100644 --- a/src/win/pipe.c +++ b/src/win/pipe.c @@ -244,9 +244,8 @@ int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, return 0; error: - if (pipeHandle != INVALID_HANDLE_VALUE) { + if (pipeHandle != INVALID_HANDLE_VALUE) CloseHandle(pipeHandle); - } return err; } @@ -554,7 +553,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,9 +620,8 @@ 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(); } @@ -655,7 +653,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"); } @@ -2147,7 +2145,7 @@ int uv_pipe_open(uv_pipe_t* pipe, uv_file file) { 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/tcp.c b/src/win/tcp.c index 941c801..0dcaa97 100644 --- a/src/win/tcp.c +++ b/src/win/tcp.c @@ -523,16 +523,15 @@ 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); } 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, @@ -545,7 +544,6 @@ static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, WSAGetLastError()); uv_insert_pending_req(loop, (uv_req_t*)req); - handle->reqs_pending++; } } @@ -750,6 +748,40 @@ int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb, 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() { + 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, uv_tcp_t* handle, @@ -757,6 +789,7 @@ static int uv_tcp_try_connect(uv_connect_t* req, 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; @@ -792,6 +825,25 @@ static int uv_tcp_try_connect(uv_connect_t* req, } } + /* 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); + } + UV_REQ_INIT(req, UV_CONNECT); req->handle = (uv_stream_t*) handle; req->cb = cb; diff --git a/src/win/tty.c b/src/win/tty.c index 488d9b2..1b9d4f8 100644 --- a/src/win/tty.c +++ b/src/win/tty.c @@ -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; @@ -2121,13 +2122,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 +2148,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; } } } @@ -2412,6 +2412,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 1c4977a..68ca728 100644 --- a/src/win/udp.c +++ b/src/win/udp.c @@ -189,6 +189,11 @@ void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle) { } +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, @@ -752,6 +757,9 @@ 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; @@ -774,8 +782,6 @@ int uv__udp_set_source_membership6(uv_udp_t* handle, mreq.gsr_interface = 0; } - 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)); @@ -1067,7 +1073,7 @@ int uv__udp_connect(uv_udp_t* handle, 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; @@ -1083,7 +1089,7 @@ int uv__udp_disconnect(uv_udp_t* handle) { 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; diff --git a/src/win/util.c b/src/win/util.c index 9e1e7f7..aad8f1a 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 @@ -67,8 +69,8 @@ extern BOOLEAN NTAPI SystemFunction036(PVOID Buffer, ULONG BufferLength); 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"); } } @@ -490,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; } @@ -1804,7 +1808,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; @@ -1871,7 +1877,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 @@ -1883,7 +1889,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); diff --git a/src/win/winapi.h b/src/win/winapi.h index cbe1437..0b66b56 100644 --- a/src/win/winapi.h +++ b/src/win/winapi.h @@ -4726,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; diff --git a/test/task.h b/test/task.h index e95e3bd..8250f94 100644 --- a/test/task.h +++ b/test/task.h @@ -111,24 +111,44 @@ typedef enum { } \ } while (0) -#define ASSERT_BASE(expr, a, operator, b, type, conv) \ +#define ASSERT_BASE(a, operator, b, type, conv) \ do { \ - if (!(expr)) { \ + type eval_a = (type) (a); \ + 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", \ + "(%"conv" %s %"conv")\n", \ __FILE__, \ __LINE__, \ #a, \ #operator, \ #b, \ - (type)a, \ + eval_a, \ #operator, \ - (type)b); \ + 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)) { \ @@ -177,7 +197,7 @@ typedef enum { } while (0) #define ASSERT_INT_BASE(a, operator, b, type, conv) \ - ASSERT_BASE(a operator b, a, operator, b, type, conv) + ASSERT_BASE(a, operator, b, type, conv) #define ASSERT_EQ(a, b) ASSERT_INT_BASE(a, ==, b, int64_t, PRId64) #define ASSERT_GE(a, b) ASSERT_INT_BASE(a, >=, b, int64_t, PRId64) @@ -194,10 +214,10 @@ typedef enum { #define ASSERT_UINT64_NE(a, b) ASSERT_INT_BASE(a, !=, b, uint64_t, PRIu64) #define ASSERT_STR_EQ(a, b) \ - ASSERT_BASE(strcmp(a, b) == 0, a, ==, b, char*, "s") + ASSERT_BASE_STR(strcmp(a, b) == 0, a, == , b, char*, "s") #define ASSERT_STR_NE(a, b) \ - ASSERT_BASE(strcmp(a, b) != 0, a, !=, b, char*, "s") + 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) @@ -212,16 +232,16 @@ typedef enum { ASSERT_BASE_HEX(memcmp(a, b, size) != 0, a, !=, b, size) #define ASSERT_NULL(a) \ - ASSERT_BASE(a == NULL, a, ==, NULL, void*, "p") + ASSERT_BASE(a, ==, NULL, void*, "p") #define ASSERT_NOT_NULL(a) \ - ASSERT_BASE(a != NULL, a, !=, NULL, void*, "p") + ASSERT_BASE(a, !=, NULL, void*, "p") #define ASSERT_PTR_EQ(a, b) \ - ASSERT_BASE((void*)a == (void*)b, a, ==, b, void*, "p") + ASSERT_BASE(a, ==, b, void*, "p") #define ASSERT_PTR_NE(a, b) \ - ASSERT_BASE((void*)a != (void*)b, a, !=, b, void*, "p") + 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. diff --git a/test/test-close-fd.c b/test/test-close-fd.c index 2ed9a10..cea4a1b 100644 --- a/test/test-close-fd.c +++ b/test/test-close-fd.c @@ -28,13 +28,13 @@ 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); diff --git a/test/test-dlerror.c b/test/test-dlerror.c index 70cc9bf..42ad688 100644 --- a/test/test-dlerror.c +++ b/test/test-dlerror.c @@ -42,7 +42,7 @@ TEST_IMPL(dlerror) { msg = uv_dlerror(&lib); ASSERT(msg != NULL); -#ifndef __OpenBSD__ +#if !defined(__OpenBSD__) && !defined(__QNX__) ASSERT(strstr(msg, path) != NULL); #endif ASSERT(strstr(msg, dlerror_no_error) == NULL); @@ -50,7 +50,7 @@ TEST_IMPL(dlerror) { /* Should return the same error twice in a row. */ msg = uv_dlerror(&lib); ASSERT(msg != NULL); -#ifndef __OpenBSD__ +#if !defined(__OpenBSD__) && !defined(__QNX__) ASSERT(strstr(msg, path) != NULL); #endif ASSERT(strstr(msg, dlerror_no_error) == NULL); 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-open-flags.c b/test/test-fs-open-flags.c index fcef2fb..5f61007 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); diff --git a/test/test-fs.c b/test/test-fs.c index ae9923a..63189d0 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 @@ -1290,7 +1286,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)); @@ -3885,7 +3884,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); diff --git a/test/test-get-currentexe.c b/test/test-get-currentexe.c index 5e4a083..8eba7b7 100644 --- a/test/test-get-currentexe.c +++ b/test/test-get-currentexe.c @@ -88,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-getaddrinfo.c b/test/test-getaddrinfo.c index f2b4e03..628e4d1 100644 --- a/test/test-getaddrinfo.c +++ b/test/test-getaddrinfo.c @@ -39,6 +39,7 @@ 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); @@ -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(), @@ -127,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)); @@ -168,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; diff --git a/test/test-getnameinfo.c b/test/test-getnameinfo.c index 3767ffd..eb32645 100644 --- a/test/test-getnameinfo.c +++ b/test/test-getnameinfo.c @@ -46,6 +46,11 @@ static void getnameinfo_req(uv_getnameinfo_t* handle, 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); @@ -87,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-getters-setters.c b/test/test-getters-setters.c index 60a1b92..42c9dca 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 diff --git a/test/test-list.h b/test/test-list.h index 24a8a65..52b17a6 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -127,6 +127,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 +147,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) @@ -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) @@ -280,6 +285,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) @@ -517,12 +523,17 @@ TEST_DECLARE (idna_toascii) TEST_DECLARE (utf8_decode1) 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) @@ -671,6 +682,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) @@ -692,6 +705,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) @@ -715,11 +730,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) @@ -842,7 +859,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) @@ -1010,7 +1027,7 @@ 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_scandir_empty_dir) TEST_ENTRY (fs_scandir_non_existent_dir) @@ -1098,6 +1115,10 @@ TASK_LIST_START TEST_ENTRY (idna_toascii) #endif + 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-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-ping-pong.c b/test/test-ping-pong.c index c86a3f4..7f7758b 100644 --- a/test/test-ping-pong.c +++ b/test/test-ping-pong.c @@ -70,14 +70,14 @@ static void pinger_on_close(uv_handle_t* handle) { } -static void pinger_after_write(uv_write_t *req, int status) { +static void pinger_after_write(uv_write_t* req, int status) { ASSERT(status == 0); free(req); } static void pinger_write_ping(pinger_t* pinger) { - uv_write_t *req; + uv_write_t* req; uv_buf_t bufs[sizeof PING - 1]; int i, nbufs; @@ -112,7 +112,7 @@ 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); @@ -148,8 +148,8 @@ 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 pinger_on_connect(uv_connect_t* req, int status) { + pinger_t* pinger = (pinger_t*)req->handle->data; pinger_on_connect_count++; @@ -169,10 +169,10 @@ 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(0 == uv_ip6_addr("::1", TEST_PORT, &server_addr)); pinger = malloc(sizeof(*pinger)); ASSERT(pinger != NULL); pinger->vectored_writes = vectored_writes; @@ -200,7 +200,7 @@ static void tcp_pinger_v6_new(int vectored_writes) { 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)); pinger = malloc(sizeof(*pinger)); @@ -229,9 +229,9 @@ static void tcp_pinger_new(int vectored_writes) { static void pipe_pinger_new(int vectored_writes) { int r; - pinger_t *pinger; + pinger_t* pinger; - pinger = (pinger_t*)malloc(sizeof(*pinger)); + pinger = malloc(sizeof(*pinger)); ASSERT(pinger != NULL); pinger->vectored_writes = vectored_writes; pinger->state = 0; diff --git a/test/test-pipe-set-non-blocking.c b/test/test-pipe-set-non-blocking.c index fcc9fc0..626b53f 100644 --- a/test/test-pipe-set-non-blocking.c +++ b/test/test-pipe-set-non-blocking.c @@ -24,29 +24,35 @@ TEST_IMPL(pipe_set_non_blocking) { #else /* !_WIN32 */ -#include -#include -#include +#include /* memset */ +#include /* close */ #include -#include -#include +#include 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); } @@ -58,15 +64,16 @@ 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; 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_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); @@ -89,7 +96,8 @@ 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(). */ + ASSERT(0 == close(fd[0])); /* fd[1] is closed by uv_close(). */ + fd[0] = -1; uv_barrier_destroy(&ctx.barrier); MAKE_VALGRIND_HAPPY(); diff --git a/test/test-process-title-threadsafe.c b/test/test-process-title-threadsafe.c index 3b4168b..927643c 100644 --- a/test/test-process-title-threadsafe.c +++ b/test/test-process-title-threadsafe.c @@ -39,10 +39,13 @@ 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 @@ -78,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__) || \ @@ -86,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)); @@ -94,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-signal-multiple-loops.c b/test/test-signal-multiple-loops.c index 4281d23..09d9cac 100644 --- a/test/test-signal-multiple-loops.c +++ b/test/test-signal-multiple-loops.c @@ -200,6 +200,11 @@ TEST_IMPL(signal_multiple_loops) { 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]; enum signal_action action; diff --git a/test/test-spawn.c b/test/test-spawn.c index 5e1c6d6..d175733 100644 --- a/test/test-spawn.c +++ b/test/test-spawn.c @@ -71,7 +71,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 +104,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,7 +135,7 @@ 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); } } @@ -150,7 +150,7 @@ static void on_read_once(uv_stream_t* tcp, ssize_t nread, const uv_buf_t* buf) { static void write_cb(uv_write_t* req, int status) { ASSERT(status == 0); - uv_close((uv_handle_t*)req->handle, close_cb); + uv_close((uv_handle_t*) req->handle, close_cb); } @@ -172,8 +172,8 @@ 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); } @@ -291,7 +291,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); @@ -595,9 +595,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); @@ -605,7 +605,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); @@ -636,7 +636,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); @@ -677,7 +677,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; @@ -703,10 +703,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); @@ -833,7 +833,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); @@ -845,7 +845,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(); @@ -874,11 +874,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); @@ -925,9 +925,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); @@ -939,10 +939,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); @@ -972,9 +972,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); @@ -986,10 +986,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); @@ -1077,7 +1077,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); @@ -1105,7 +1105,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. */ @@ -1821,32 +1821,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); diff --git a/test/test-tcp-connect-timeout.c b/test/test-tcp-connect-timeout.c index 081424b..a67d325 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; + } + // relase >= 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-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-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.c b/test/test-thread.c index f53bce0..432c243 100644 --- a/test/test-thread.c +++ b/test/test-thread.c @@ -167,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; diff --git a/test/test-timer.c b/test/test-timer.c index c667da0..ee8331c 100644 --- a/test/test-timer.c +++ b/test/test-timer.c @@ -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-tty.c b/test/test-tty.c index 0c6548f..a9d38f2 100644 --- a/test/test-tty.c +++ b/test/test-tty.c @@ -422,6 +422,11 @@ 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(__APPLE__) || \ defined(__DragonFly__) || \ defined(__FreeBSD__) || \ diff --git a/test/test-udp-connect.c b/test/test-udp-connect.c index 58cf947..41ace11 100644 --- a/test/test-udp-connect.c +++ b/test/test-udp-connect.c @@ -124,6 +124,17 @@ TEST_IMPL(udp_connect) { buf = uv_buf_init("EXIT", 4); + // connect() to INADDR_ANY fails on Windows wih 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-mmsg.c b/test/test-udp-mmsg.c new file mode 100644 index 0000000..08628a5 --- /dev/null +++ b/test/test-udp-mmsg.c @@ -0,0 +1,142 @@ +/* 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 4 +#define MAX_DGRAM_SIZE (64 * 1024) +#define NUM_SENDS 8 +#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 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(buf->base != NULL); + 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(addr == NULL); + free(rcvbuf->base); + return; + } + + ASSERT_EQ(nread, 4); + ASSERT(addr != NULL); + ASSERT_MEM_EQ("PING", rcvbuf->base, nread); + + recv_cb_called++; + if (recv_cb_called == NUM_SENDS) { + uv_close((uv_handle_t*)handle, close_cb); + uv_close((uv_handle_t*)&sender, close_cb); + } + + /* 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(recv_cb_called, 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-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; +}