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 <maciej.malecki@notimplemented.org> <me@mmalecki.com>
 Marc Schlaich <marc.schlaich@googlemail.com> <marc.schlaich@gmail.com>
 Michael <michael_dawson@ca.ibm.com>
 Michael Neumann <mneumann@think.localnet> <mneumann@ntecs.de>
+Michael Penick <michael.penick@datastax.com> <mpenick@users.noreply.github.com>
 Nicholas Vavilov <vvnicholas@gmail.com>
 Nick Logan <ugexe@cpan.org> <nlogan@gmail.com>
 Rasmus Christian Pedersen <zerhacken@yahoo.com>
@@ -41,10 +42,12 @@ Santiago Gimeno <santiago.gimeno@quantion.es> <santiago.gimeno@gmail.com>
 Saúl Ibarra Corretgé <saghul@gmail.com>
 Saúl Ibarra Corretgé <saghul@gmail.com> <s@saghul.net>
 Shigeki Ohtsu <ohtsu@iij.ad.jp> <ohtsu@ohtsu.org>
+TK-one <tk5641@naver.com>
 Timothy J. Fontaine <tjfontaine@gmail.com>
 Yasuhiro Matsumoto <mattn.jp@gmail.com>
 Yazhong Liu <yorkiefixer@gmail.com>
 Yuki Okumura <mjt@cltn.org>
+gengjiawen <technicalcute@gmail.com>
 jBarz <jBarz@users.noreply.github.com> <jbarboza@ca.ibm.com>
 jBarz <jBarz@users.noreply.github.com> <jbarz@users.noreply.github.com>
 ptlomholt <pt@lomholt.com>
diff --git a/AUTHORS b/AUTHORS
index 222367e..e7c789c 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -432,3 +432,19 @@ Philip Chimento <philip.chimento@gmail.com>
 Michal Artazov <michal@artazov.cz>
 Jeroen Roovers <jer@gentoo.org>
 MasterDuke17 <MasterDuke17@users.noreply.github.com>
+Alexander Tokmakov <avtokmakov@yandex-team.ru>
+Arenoros <arenoros@gmail.com>
+lander0s <dh.landeros08@gmail.com>
+Turbinya <wownucleos@gmail.com>
+OleksandrKvl <oleksandrdvl@gmail.com>
+Carter Li <carter.li@eoitek.com>
+Juan Sebastian velez Posada <jvelezpo@users.noreply.github.com>
+escherstair <ernestviga@gmail.com>
+Evan Lucas <evanlucas@me.com>
+tjarlama <59913901+tjarlama@users.noreply.github.com>
+司徒玟琅 <sanjusss@qq.com>
+YuMeiJie <yumeijie@huawei.com>
+Aleksej Lebedev <root@zta.lk>
+Nikolay Mitev <github@hmel.org>
+Ulrik Strid <ulrik.strid@outlook.com>
+Elad Lahav <elahav@qnx.com>
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 $<$<BOOL:${UV_LINT_NO_UNUSED_PARAMETER}>:-Wno-unused-parameter>)
 set(lint-strict-prototypes $<$<BOOL:${UV_LINT_STRICT_PROTOTYPES}>:-Wstrict-prototypes>)
 set(lint-extra $<$<BOOL:${UV_LINT_EXTRA}>:-Wextra>)
@@ -76,6 +78,7 @@ set(lint-no-unsafe-msvc $<$<BOOL:${UV_LINT_NO_UNSAFE_MSVC}>:/wd4996>)
 string(CONCAT lint-default $<
   $<AND:$<BOOL:${UV_LINT_WALL}>,$<NOT:${is-msvc}>>:-Wall
 >)
+set(lint-utf8-msvc $<$<BOOL:${UV_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 ^<target^>` where ^<target^> 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 ^<target^>` where ^<target^> 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 <http://man7.org/linux/man-pages/man7/signal-safety.7.html>`_.
+        :c:func:`uv_async_send` is `async-signal-safe <https://man7.org/linux/man-pages/man7/signal-safety.7.html>`_.
         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 <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/open-osfhandle?view=vs-2019>`_.
-   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 <http://man7.org/linux/man-pages/man2/open.2.html>`_.
+`Unix flags <https://man7.org/linux/man-pages/man2/open.2.html>`_.
 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 <getaddrinfo>`_. The
+structures are documented in `the getaddrinfo man page <getaddrinfo_>`_. 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 <Timers>`_ 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 <limits.h>  /* 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 <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
-#include <assert.h>
-#include <errno.h>
 
 #include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <net/if.h>
-#include <netinet/in.h>
-#include <netinet/in6_var.h>
-#include <arpa/inet.h>
 
 #include <sys/time.h>
 #include <unistd.h>
-#include <fcntl.h>
-#include <utmp.h>
-#include <libgen.h>
 
-#include <sys/protosw.h>
 #include <procinfo.h>
-#include <sys/proc.h>
-#include <sys/procfs.h>
 
-#include <sys/poll.h>
-
-#include <sys/pollset.h>
 #include <ctype.h>
 
-#include <sys/mntctl.h>
-#include <sys/vmount.h>
-#include <limits.h>
-#include <strings.h>
-#include <sys/vnode.h>
+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 <dlfcn.h>  /* for dlsym */
-#endif
-
 #if defined(__MVS__)
 #include <sys/ioctl.h>
 #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 <stdint.h>
 #include <errno.h>
 
+#include <dlfcn.h>
 #include <mach/mach.h>
 #include <mach/mach_time.h>
 #include <mach-o/dyld.h> /* _NSGetExecutablePath */
@@ -32,6 +33,15 @@
 #include <sys/sysctl.h>
 #include <unistd.h>  /* 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 <sys/param.h>
 # include <sys/mount.h>
-#elif defined(__sun) || defined(__MVS__) || defined(__NetBSD__) || defined(__HAIKU__)
+#elif defined(__sun)      || \
+      defined(__MVS__)    || \
+      defined(__NetBSD__) || \
+      defined(__HAIKU__)  || \
+      defined(__QNX__)
 # include <sys/statvfs.h>
 #else
 # include <sys/statfs.h>
@@ -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 <as400_protos.h>
 #include <as400_types.h>
 
+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 <AvailabilityMacros.h>
 #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 <unistd.h>
 
 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 <errno.h>
 #include <pthread.h>
 #include <signal.h>
@@ -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 <string.h>
+#include <sys/process.h>
+#include <sys/neutrino.h>
+#include <sys/memmsg.h>
+#include <sys/syspage.h>
+#include <sys/procfs.h>
+
+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 <winsock2.h>
 #include <winperf.h>
 #include <iphlpapi.h>
 #include <psapi.h>
 #include <tlhelp32.h>
 #include <windows.h>
+/* clang-format on */
 #include <userenv.h>
 #include <math.h>
 
@@ -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 <unistd.h> /* unlink, etc. */
 #else
 # include <direct.h>
@@ -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 <sys/stat.h>
 #include <limits.h> /* 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 <unistd.h> /* unlink, rmdir, etc. */
+#ifndef _WIN32
+# include <unistd.h> /* unlink, rmdir, etc. */
 #else
 # include <winioctl.h>
 # include <direct.h>
@@ -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 <string.h>
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 <string.h> /* 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 <errno.h>
-#include <string.h>
-#include <sys/socket.h>
+#include <string.h> /* memset */
+#include <unistd.h> /* close */
 #include <sys/types.h>
-#include <sys/un.h>
-#include <unistd.h>
+#include <sys/socket.h>
 
 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*)&in;
+  options.stdio[0].data.stream = (uv_stream_t*) &in;
   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*)&in;
+  options.stdio[0].data.stream = (uv_stream_t*) &in;
   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*)&in;
+  options.stdio[0].data.stream = (uv_stream_t*) &in;
   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*)&in;
+  options.stdio[0].data.stream = (uv_stream_t*) &in;
   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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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 <stdio.h>
+#include <stdlib.h>
+
+#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;
+}