From b4069b04ff63b327ff4881282200c3c73fded34d Mon Sep 17 00:00:00 2001
From: libuv upstream <libuv@googlegroups.com>
Date: Mon, 10 Jun 2019 15:17:21 +0200
Subject: libuv 2019-06-10 (ee24ce90)

Code extracted from:

    https://github.com/libuv/libuv.git

at commit ee24ce900e5714c950b248da2bdd311b01c983be (v1.x).
---
 include/uv.h                 |  66 +++++++++-
 include/uv/unix.h            |  19 ++-
 include/uv/version.h         |   2 +-
 include/uv/win.h             |   5 +
 src/fs-poll.c                |  63 +++++++---
 src/threadpool.c             |   2 +-
 src/unix/aix.c               |   6 +
 src/unix/async.c             |  37 +++++-
 src/unix/atomic-ops.h        |  39 +-----
 src/unix/bsd-ifaddrs.c       |  18 +--
 src/unix/core.c              |  90 +++++++++++---
 src/unix/darwin.c            |   5 +
 src/unix/freebsd.c           |   5 +
 src/unix/fs.c                | 289 +++++++++++++++++++++++++++++++++++--------
 src/unix/fsevents.c          |   3 +-
 src/unix/getaddrinfo.c       |   2 +
 src/unix/haiku.c             | 176 ++++++++++++++++++++++++++
 src/unix/ibmi.c              | 141 ++++++++++++++++++++-
 src/unix/internal.h          |  11 +-
 src/unix/kqueue.c            |  18 +--
 src/unix/linux-core.c        | 162 ++++++++++++++++++++----
 src/unix/linux-syscalls.c    |  31 +++++
 src/unix/linux-syscalls.h    |  35 ++++++
 src/unix/netbsd.c            |   5 +
 src/unix/openbsd.c           |   5 +
 src/unix/os390.c             |   6 +
 src/unix/pipe.c              |  12 +-
 src/unix/posix-poll.c        |   2 +
 src/unix/process.c           |   9 +-
 src/unix/stream.c            |   8 +-
 src/unix/sunos.c             |  24 +++-
 src/unix/tcp.c               |  48 +++----
 src/unix/thread.c            |  52 +++++++-
 src/unix/udp.c               | 144 +++++++++++++++++----
 src/uv-common.c              | 146 ++++++++++++++++++----
 src/uv-common.h              |  11 ++
 src/uv-data-getter-setters.c |   2 +-
 src/win/core.c               |  27 ++++
 src/win/fs.c                 | 175 ++++++++++++++++++++++++++
 src/win/handle.c             |   1 -
 src/win/internal.h           |   8 ++
 src/win/tcp.c                |  40 ++----
 src/win/thread.c             |  27 +++-
 src/win/tty.c                |   4 +-
 src/win/udp.c                | 103 ++++++++++++---
 src/win/util.c               | 110 ++++++++++++----
 src/win/winsock.c            |  15 ++-
 47 files changed, 1852 insertions(+), 357 deletions(-)
 create mode 100644 src/unix/haiku.c

diff --git a/include/uv.h b/include/uv.h
index a46b229..f97801c 100644
--- a/include/uv.h
+++ b/include/uv.h
@@ -202,6 +202,7 @@ typedef enum {
 /* Handle types. */
 typedef struct uv_loop_s uv_loop_t;
 typedef struct uv_handle_s uv_handle_t;
+typedef struct uv_dir_s uv_dir_t;
 typedef struct uv_stream_s uv_stream_t;
 typedef struct uv_tcp_s uv_tcp_t;
 typedef struct uv_udp_s uv_udp_t;
@@ -630,7 +631,11 @@ UV_EXTERN int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock);
 UV_EXTERN int uv_udp_bind(uv_udp_t* handle,
                           const struct sockaddr* addr,
                           unsigned int flags);
+UV_EXTERN int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr);
 
+UV_EXTERN int uv_udp_getpeername(const uv_udp_t* handle,
+                                 struct sockaddr* name,
+                                 int* namelen);
 UV_EXTERN int uv_udp_getsockname(const uv_udp_t* handle,
                                  struct sockaddr* name,
                                  int* namelen);
@@ -1095,6 +1100,11 @@ typedef struct {
 } uv_timeval_t;
 
 typedef struct {
+  int64_t tv_sec;
+  int32_t tv_usec;
+} uv_timeval64_t;
+
+typedef struct {
    uv_timeval_t ru_utime; /* user CPU time used */
    uv_timeval_t ru_stime; /* system CPU time used */
    uint64_t ru_maxrss;    /* maximum resident set size */
@@ -1144,6 +1154,17 @@ UV_EXTERN int uv_os_getenv(const char* name, char* buffer, size_t* size);
 UV_EXTERN int uv_os_setenv(const char* name, const char* value);
 UV_EXTERN int uv_os_unsetenv(const char* name);
 
+#ifdef MAXHOSTNAMELEN
+# define UV_MAXHOSTNAMESIZE (MAXHOSTNAMELEN + 1)
+#else
+  /*
+    Fallback for the maximum hostname size, including the null terminator. The
+    Windows gethostname() documentation states that 256 bytes will always be
+    large enough to hold the null-terminated hostname.
+  */
+# define UV_MAXHOSTNAMESIZE 256
+#endif
+
 UV_EXTERN int uv_os_gethostname(char* buffer, size_t* size);
 
 UV_EXTERN int uv_os_uname(uv_utsname_t* buffer);
@@ -1181,9 +1202,19 @@ typedef enum {
   UV_FS_FCHOWN,
   UV_FS_REALPATH,
   UV_FS_COPYFILE,
-  UV_FS_LCHOWN
+  UV_FS_LCHOWN,
+  UV_FS_OPENDIR,
+  UV_FS_READDIR,
+  UV_FS_CLOSEDIR
 } uv_fs_type;
 
+struct uv_dir_s {
+  uv_dirent_t* dirents;
+  size_t nentries;
+  void* reserved[4];
+  UV_DIR_PRIVATE_FIELDS
+};
+
 /* uv_fs_t is a subclass of uv_req_t. */
 struct uv_fs_s {
   UV_REQ_FIELDS
@@ -1276,6 +1307,18 @@ UV_EXTERN int uv_fs_scandir(uv_loop_t* loop,
                             uv_fs_cb cb);
 UV_EXTERN int uv_fs_scandir_next(uv_fs_t* req,
                                  uv_dirent_t* ent);
+UV_EXTERN int uv_fs_opendir(uv_loop_t* loop,
+                            uv_fs_t* req,
+                            const char* path,
+                            uv_fs_cb cb);
+UV_EXTERN int uv_fs_readdir(uv_loop_t* loop,
+                            uv_fs_t* req,
+                            uv_dir_t* dir,
+                            uv_fs_cb cb);
+UV_EXTERN int uv_fs_closedir(uv_loop_t* loop,
+                             uv_fs_t* req,
+                             uv_dir_t* dir,
+                             uv_fs_cb cb);
 UV_EXTERN int uv_fs_stat(uv_loop_t* loop,
                          uv_fs_t* req,
                          const char* path,
@@ -1518,6 +1561,7 @@ UV_EXTERN int uv_chdir(const char* dir);
 
 UV_EXTERN uint64_t uv_get_free_memory(void);
 UV_EXTERN uint64_t uv_get_total_memory(void);
+UV_EXTERN uint64_t uv_get_constrained_memory(void);
 
 UV_EXTERN uint64_t uv_hrtime(void);
 
@@ -1571,9 +1615,29 @@ UV_EXTERN void uv_key_delete(uv_key_t* key);
 UV_EXTERN void* uv_key_get(uv_key_t* key);
 UV_EXTERN void uv_key_set(uv_key_t* key, void* value);
 
+UV_EXTERN int uv_gettimeofday(uv_timeval64_t* tv);
+
 typedef void (*uv_thread_cb)(void* arg);
 
 UV_EXTERN int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg);
+
+typedef enum {
+  UV_THREAD_NO_FLAGS = 0x00,
+  UV_THREAD_HAS_STACK_SIZE = 0x01
+} uv_thread_create_flags;
+
+struct uv_thread_options_s {
+  unsigned int flags;
+  size_t stack_size;
+  /* More fields may be added at any time. */
+};
+
+typedef struct uv_thread_options_s uv_thread_options_t;
+
+UV_EXTERN int uv_thread_create_ex(uv_thread_t* tid,
+                                  const uv_thread_options_t* params,
+                                  uv_thread_cb entry,
+                                  void* arg);
 UV_EXTERN uv_thread_t uv_thread_self(void);
 UV_EXTERN int uv_thread_join(uv_thread_t *tid);
 UV_EXTERN int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2);
diff --git a/include/uv/unix.h b/include/uv/unix.h
index 9de9efe..6c93ee9 100644
--- a/include/uv/unix.h
+++ b/include/uv/unix.h
@@ -31,13 +31,14 @@
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <arpa/inet.h>
-#include <netdb.h>
+#include <netdb.h>  /* MAXHOSTNAMELEN on Solaris */
 
 #include <termios.h>
 #include <pwd.h>
 
 #if !defined(__MVS__)
 #include <semaphore.h>
+#include <sys/param.h> /* MAXHOSTNAMELEN on Linux and the BSDs */
 #endif
 #include <pthread.h>
 #include <signal.h>
@@ -48,8 +49,6 @@
 # include "uv/linux.h"
 #elif defined (__MVS__)
 # include "uv/os390.h"
-#elif defined(__PASE__)
-# include "uv/posix.h"
 #elif defined(_AIX)
 # include "uv/aix.h"
 #elif defined(__sun)
@@ -62,9 +61,12 @@
       defined(__OpenBSD__)         || \
       defined(__NetBSD__)
 # include "uv/bsd.h"
-#elif defined(__CYGWIN__) || defined(__MSYS__)
+#elif defined(__PASE__)   || \
+      defined(__CYGWIN__) || \
+      defined(__MSYS__)   || \
+      defined(__GNU__)
 # include "uv/posix.h"
-#elif defined(__GNU__)
+#elif defined(__HAIKU__)
 # include "uv/posix.h"
 #endif
 
@@ -136,7 +138,9 @@ typedef pthread_cond_t uv_cond_t;
 typedef pthread_key_t uv_key_t;
 
 /* Note: guard clauses should match uv_barrier_init's in src/unix/thread.c. */
-#if defined(_AIX) || !defined(PTHREAD_BARRIER_SERIAL_THREAD)
+#if defined(_AIX) || \
+    defined(__OpenBSD__) || \
+    !defined(PTHREAD_BARRIER_SERIAL_THREAD)
 /* TODO(bnoordhuis) Merge into uv_barrier_t in v2. */
 struct _uv_barrier {
   uv_mutex_t mutex;
@@ -163,6 +167,9 @@ typedef uid_t uv_uid_t;
 
 typedef struct dirent uv__dirent_t;
 
+#define UV_DIR_PRIVATE_FIELDS \
+  DIR* dir;
+
 #if defined(DT_UNKNOWN)
 # define HAVE_DIRENT_TYPES
 # if defined(DT_REG)
diff --git a/include/uv/version.h b/include/uv/version.h
index abc140a..97f0bc2 100644
--- a/include/uv/version.h
+++ b/include/uv/version.h
@@ -31,7 +31,7 @@
  */
 
 #define UV_VERSION_MAJOR 1
-#define UV_VERSION_MINOR 24
+#define UV_VERSION_MINOR 29
 #define UV_VERSION_PATCH 2
 #define UV_VERSION_IS_RELEASE 0
 #define UV_VERSION_SUFFIX "dev"
diff --git a/include/uv/win.h b/include/uv/win.h
index edd2cc6..acbd958 100644
--- a/include/uv/win.h
+++ b/include/uv/win.h
@@ -301,6 +301,11 @@ typedef struct uv__dirent_s {
   char d_name[1];
 } uv__dirent_t;
 
+#define UV_DIR_PRIVATE_FIELDS \
+  HANDLE dir_handle;          \
+  WIN32_FIND_DATAW find_data; \
+  BOOL need_find_call;
+
 #define HAVE_DIRENT_TYPES
 #define UV__DT_DIR     UV_DIRENT_DIR
 #define UV__DT_FILE    UV_DIRENT_FILE
diff --git a/src/fs-poll.c b/src/fs-poll.c
index 6c82dfc..89864e2 100644
--- a/src/fs-poll.c
+++ b/src/fs-poll.c
@@ -22,12 +22,20 @@
 #include "uv.h"
 #include "uv-common.h"
 
+#ifdef _WIN32
+#include "win/internal.h"
+#include "win/handle-inl.h"
+#define uv__make_close_pending(h) uv_want_endgame((h)->loop, (h))
+#else
+#include "unix/internal.h"
+#endif
+
 #include <assert.h>
 #include <stdlib.h>
 #include <string.h>
 
 struct poll_ctx {
-  uv_fs_poll_t* parent_handle; /* NULL if parent has been stopped or closed */
+  uv_fs_poll_t* parent_handle;
   int busy_polling;
   unsigned int interval;
   uint64_t start_time;
@@ -36,6 +44,7 @@ struct poll_ctx {
   uv_timer_t timer_handle;
   uv_fs_t fs_req; /* TODO(bnoordhuis) mark fs_req internal */
   uv_stat_t statbuf;
+  struct poll_ctx* previous; /* context from previous start()..stop() period */
   char path[1]; /* variable length */
 };
 
@@ -49,6 +58,7 @@ static uv_stat_t zero_statbuf;
 
 int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle) {
   uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_POLL);
+  handle->poll_ctx = NULL;
   return 0;
 }
 
@@ -62,7 +72,7 @@ int uv_fs_poll_start(uv_fs_poll_t* handle,
   size_t len;
   int err;
 
-  if (uv__is_active(handle))
+  if (uv_is_active((uv_handle_t*)handle))
     return 0;
 
   loop = handle->loop;
@@ -90,6 +100,8 @@ int uv_fs_poll_start(uv_fs_poll_t* handle,
   if (err < 0)
     goto error;
 
+  if (handle->poll_ctx != NULL)
+    ctx->previous = handle->poll_ctx;
   handle->poll_ctx = ctx;
   uv__handle_start(handle);
 
@@ -104,19 +116,17 @@ error:
 int uv_fs_poll_stop(uv_fs_poll_t* handle) {
   struct poll_ctx* ctx;
 
-  if (!uv__is_active(handle))
+  if (!uv_is_active((uv_handle_t*)handle))
     return 0;
 
   ctx = handle->poll_ctx;
   assert(ctx != NULL);
-  assert(ctx->parent_handle != NULL);
-  ctx->parent_handle = NULL;
-  handle->poll_ctx = NULL;
+  assert(ctx->parent_handle == handle);
 
   /* Close the timer if it's active. If it's inactive, there's a stat request
    * in progress and poll_cb will take care of the cleanup.
    */
-  if (uv__is_active(&ctx->timer_handle))
+  if (uv_is_active((uv_handle_t*)&ctx->timer_handle))
     uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb);
 
   uv__handle_stop(handle);
@@ -129,7 +139,7 @@ int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buffer, size_t* size) {
   struct poll_ctx* ctx;
   size_t required_len;
 
-  if (!uv__is_active(handle)) {
+  if (!uv_is_active((uv_handle_t*)handle)) {
     *size = 0;
     return UV_EINVAL;
   }
@@ -153,6 +163,9 @@ int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buffer, size_t* size) {
 
 void uv__fs_poll_close(uv_fs_poll_t* handle) {
   uv_fs_poll_stop(handle);
+
+  if (handle->poll_ctx == NULL)
+    uv__make_close_pending((uv_handle_t*)handle);
 }
 
 
@@ -173,14 +186,13 @@ static void poll_cb(uv_fs_t* req) {
   uv_stat_t* statbuf;
   struct poll_ctx* ctx;
   uint64_t interval;
+  uv_fs_poll_t* handle;
 
   ctx = container_of(req, struct poll_ctx, fs_req);
+  handle = ctx->parent_handle;
 
-  if (ctx->parent_handle == NULL) { /* handle has been stopped or closed */
-    uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb);
-    uv_fs_req_cleanup(req);
-    return;
-  }
+  if (!uv_is_active((uv_handle_t*)handle) || uv__is_closing(handle))
+    goto out;
 
   if (req->result != 0) {
     if (ctx->busy_polling != req->result) {
@@ -205,7 +217,7 @@ static void poll_cb(uv_fs_t* req) {
 out:
   uv_fs_req_cleanup(req);
 
-  if (ctx->parent_handle == NULL) { /* handle has been stopped by callback */
+  if (!uv_is_active((uv_handle_t*)handle) || uv__is_closing(handle)) {
     uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb);
     return;
   }
@@ -219,8 +231,27 @@ out:
 }
 
 
-static void timer_close_cb(uv_handle_t* handle) {
-  uv__free(container_of(handle, struct poll_ctx, timer_handle));
+static void timer_close_cb(uv_handle_t* timer) {
+  struct poll_ctx* ctx;
+  struct poll_ctx* it;
+  struct poll_ctx* last;
+  uv_fs_poll_t* handle;
+
+  ctx = container_of(timer, struct poll_ctx, timer_handle);
+  handle = ctx->parent_handle;
+  if (ctx == handle->poll_ctx) {
+    handle->poll_ctx = ctx->previous;
+    if (handle->poll_ctx == NULL && uv__is_closing(handle))
+      uv__make_close_pending((uv_handle_t*)handle);
+  } else {
+    for (last = handle->poll_ctx, it = last->previous;
+         it != ctx;
+         last = it, it = it->previous) {
+      assert(last->previous != NULL);
+    }
+    last->previous = ctx->previous;
+  }
+  uv__free(ctx);
 }
 
 
diff --git a/src/threadpool.c b/src/threadpool.c
index 4258933..7aa5755 100644
--- a/src/threadpool.c
+++ b/src/threadpool.c
@@ -27,7 +27,7 @@
 
 #include <stdlib.h>
 
-#define MAX_THREADPOOL_SIZE 128
+#define MAX_THREADPOOL_SIZE 1024
 
 static uv_once_t once = UV_ONCE_INIT;
 static uv_cond_t cond;
diff --git a/src/unix/aix.c b/src/unix/aix.c
index 337e58e..1f36926 100644
--- a/src/unix/aix.c
+++ b/src/unix/aix.c
@@ -344,6 +344,11 @@ uint64_t uv_get_total_memory(void) {
 }
 
 
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
 void uv_loadavg(double avg[3]) {
   perfstat_cpu_total_t ps_total;
   int result = perfstat_cpu_total(NULL, &ps_total, sizeof(ps_total), 1);
@@ -1041,6 +1046,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
   struct poll_ctl pc;
 
   assert(loop->watchers != NULL);
+  assert(fd >= 0);
 
   events = (struct pollfd*) loop->watchers[loop->nwatchers];
   nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
diff --git a/src/unix/async.c b/src/unix/async.c
index 0b450ae..a5c47bc 100644
--- a/src/unix/async.c
+++ b/src/unix/async.c
@@ -61,14 +61,43 @@ int uv_async_send(uv_async_t* handle) {
   if (ACCESS_ONCE(int, handle->pending) != 0)
     return 0;
 
-  if (cmpxchgi(&handle->pending, 0, 1) == 0)
-    uv__async_send(handle->loop);
+  /* Tell the other thread we're busy with the handle. */
+  if (cmpxchgi(&handle->pending, 0, 1) != 0)
+    return 0;
+
+  /* Wake up the other thread's event loop. */
+  uv__async_send(handle->loop);
+
+  /* Tell the other thread we're done. */
+  if (cmpxchgi(&handle->pending, 1, 2) != 1)
+    abort();
 
   return 0;
 }
 
 
+/* Only call this from the event loop thread. */
+static int uv__async_spin(uv_async_t* handle) {
+  int rc;
+
+  for (;;) {
+    /* rc=0 -- handle is not pending.
+     * rc=1 -- handle is pending, other thread is still working with it.
+     * rc=2 -- handle is pending, other thread is done.
+     */
+    rc = cmpxchgi(&handle->pending, 2, 0);
+
+    if (rc != 1)
+      return rc;
+
+    /* Other thread is busy with this handle, spin until it's done. */
+    cpu_relax();
+  }
+}
+
+
 void uv__async_close(uv_async_t* handle) {
+  uv__async_spin(handle);
   QUEUE_REMOVE(&handle->queue);
   uv__handle_stop(handle);
 }
@@ -109,8 +138,8 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
     QUEUE_REMOVE(q);
     QUEUE_INSERT_TAIL(&loop->async_handles, q);
 
-    if (cmpxchgi(&h->pending, 1, 0) == 0)
-      continue;
+    if (0 == uv__async_spin(h))
+      continue;  /* Not pending. */
 
     if (h->async_cb == NULL)
       continue;
diff --git a/src/unix/atomic-ops.h b/src/unix/atomic-ops.h
index 7cac1f9..541a6c8 100644
--- a/src/unix/atomic-ops.h
+++ b/src/unix/atomic-ops.h
@@ -23,7 +23,6 @@
 #endif
 
 UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval));
-UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval));
 UV_UNUSED(static void cpu_relax(void));
 
 /* Prefer hand-rolled assembly over the gcc builtins because the latter also
@@ -49,43 +48,7 @@ UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) {
   else
     return op4;
 #elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
-  return atomic_cas_uint(ptr, oldval, newval);
-#else
-  return __sync_val_compare_and_swap(ptr, oldval, newval);
-#endif
-}
-
-UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval)) {
-#if defined(__i386__) || defined(__x86_64__)
-  long out;
-  __asm__ __volatile__ ("lock; cmpxchg %2, %1;"
-                        : "=a" (out), "+m" (*(volatile long*) ptr)
-                        : "r" (newval), "0" (oldval)
-                        : "memory");
-  return out;
-#elif defined(_AIX) && defined(__xlC__)
-  const long out = (*(volatile int*) ptr);
-# if defined(__64BIT__)
-  __compare_and_swaplp(ptr, &oldval, newval);
-# else
-  __compare_and_swap(ptr, &oldval, newval);
-# endif /* if defined(__64BIT__) */
-  return out;
-#elif defined (__MVS__)
-#ifdef _LP64
-  unsigned long long op4;
-  if (__plo_CSSTGR(ptr, (unsigned long long*) &oldval, newval,
-                  (unsigned long long*) ptr, *ptr, &op4))
-#else
-  unsigned long op4;
-  if (__plo_CSST(ptr, (unsigned int*) &oldval, newval,
-                (unsigned int*) ptr, *ptr, &op4))
-#endif
-    return oldval;
-  else
-    return op4;
-#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
-  return atomic_cas_ulong(ptr, oldval, newval);
+  return atomic_cas_uint((uint_t *)ptr, (uint_t)oldval, (uint_t)newval);
 #else
   return __sync_val_compare_and_swap(ptr, oldval, newval);
 #endif
diff --git a/src/unix/bsd-ifaddrs.c b/src/unix/bsd-ifaddrs.c
index 3c2253f..0d7bbe6 100644
--- a/src/unix/bsd-ifaddrs.c
+++ b/src/unix/bsd-ifaddrs.c
@@ -31,6 +31,10 @@
 #include <net/if_dl.h>
 #endif
 
+#if defined(__HAIKU__)
+#define IFF_RUNNING IFF_LINK
+#endif
+
 static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
   if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING)))
     return 1;
@@ -45,7 +49,8 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) {
   if (exclude_type == UV__EXCLUDE_IFPHYS)
     return (ent->ifa_addr->sa_family != AF_LINK);
 #endif
-#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || \
+    defined(__HAIKU__)
   /*
    * On BSD getifaddrs returns information related to the raw underlying
    * devices.  We're not interested in this information.
@@ -84,7 +89,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
     return 0;
   }
 
-  *addresses = uv__malloc(*count * sizeof(**addresses));
+  /* Make sure the memory is initiallized to zero using calloc() */
+  *addresses = uv__calloc(*count, sizeof(**addresses));
 
   if (*addresses == NULL) {
     freeifaddrs(addrs);
@@ -116,6 +122,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
     address++;
   }
 
+#if !(defined(__CYGWIN__) || defined(__MSYS__))
   /* Fill in physical addresses for each interface */
   for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
     if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS))
@@ -124,20 +131,15 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
     address = *addresses;
 
     for (i = 0; i < *count; i++) {
-#if defined(__CYGWIN__) || defined(__MSYS__)
-      memset(address->phys_addr, 0, sizeof(address->phys_addr));
-#else
       if (strcmp(address->name, ent->ifa_name) == 0) {
         struct sockaddr_dl* sa_addr;
         sa_addr = (struct sockaddr_dl*)(ent->ifa_addr);
         memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr));
-      } else {
-        memset(address->phys_addr, 0, sizeof(address->phys_addr));
       }
-#endif
       address++;
     }
   }
+#endif
 
   freeifaddrs(addrs);
 
diff --git a/src/unix/core.c b/src/unix/core.c
index cd57ce2..52896f9 100644
--- a/src/unix/core.c
+++ b/src/unix/core.c
@@ -41,9 +41,9 @@
 #include <sys/resource.h> /* getrusage */
 #include <pwd.h>
 #include <sys/utsname.h>
+#include <sys/time.h>
 
 #ifdef __sun
-# include <netdb.h> /* MAXHOSTNAMELEN on Solaris */
 # include <sys/filio.h>
 # include <sys/types.h>
 # include <sys/wait.h>
@@ -88,13 +88,8 @@
 #include <sys/ioctl.h>
 #endif
 
-#if !defined(__MVS__)
-#include <sys/param.h> /* MAXHOSTNAMELEN on Linux and the BSDs */
-#endif
-
-/* Fallback for the maximum hostname length */
-#ifndef MAXHOSTNAMELEN
-# define MAXHOSTNAMELEN 256
+#if defined(__linux__)
+#include <sys/syscall.h>
 #endif
 
 static int uv__run_pending(uv_loop_t* loop);
@@ -171,7 +166,9 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) {
 
   case UV_FS_POLL:
     uv__fs_poll_close((uv_fs_poll_t*)handle);
-    break;
+    /* Poll handles use file system requests, and one of them may still be
+     * running. The poll code will call uv__make_close_pending() for us. */
+    return;
 
   case UV_SIGNAL:
     uv__signal_close((uv_signal_t*) handle);
@@ -517,6 +514,34 @@ skip:
 }
 
 
+/* close() on macos has the "interesting" quirk that it fails with EINTR
+ * without closing the file descriptor when a thread is in the cancel state.
+ * That's why libuv calls close$NOCANCEL() instead.
+ *
+ * glibc on linux has a similar issue: close() is a cancellation point and
+ * will unwind the thread when it's in the cancel state. Work around that
+ * by making the system call directly. Musl libc is unaffected.
+ */
+int uv__close_nocancel(int fd) {
+#if defined(__APPLE__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension"
+#if defined(__LP64__)
+  extern int close$NOCANCEL(int);
+  return close$NOCANCEL(fd);
+#else
+  extern int close$NOCANCEL$UNIX2003(int);
+  return close$NOCANCEL$UNIX2003(fd);
+#endif
+#pragma GCC diagnostic pop
+#elif defined(__linux__)
+  return syscall(SYS_close, fd);
+#else
+  return close(fd);
+#endif
+}
+
+
 int uv__close_nocheckstdio(int fd) {
   int saved_errno;
   int rc;
@@ -524,7 +549,7 @@ int uv__close_nocheckstdio(int fd) {
   assert(fd > -1);  /* Catch uninitialized io_watcher.fd bugs. */
 
   saved_errno = errno;
-  rc = close(fd);
+  rc = uv__close_nocancel(fd);
   if (rc == -1) {
     rc = UV__ERR(errno);
     if (rc == UV_EINTR || rc == UV__ERR(EINPROGRESS))
@@ -559,7 +584,7 @@ int uv__nonblock_ioctl(int fd, int set) {
 }
 
 
-#if !defined(__CYGWIN__) && !defined(__MSYS__)
+#if !defined(__CYGWIN__) && !defined(__MSYS__) && !defined(__HAIKU__)
 int uv__cloexec_ioctl(int fd, int set) {
   int r;
 
@@ -892,7 +917,8 @@ void uv__io_close(uv_loop_t* loop, uv__io_t* w) {
   QUEUE_REMOVE(&w->pending_queue);
 
   /* Remove stale events for this file descriptor */
-  uv__platform_invalidate_fd(loop, w->fd);
+  if (w->fd != -1)
+    uv__platform_invalidate_fd(loop, w->fd);
 }
 
 
@@ -926,7 +952,7 @@ int uv_getrusage(uv_rusage_t* rusage) {
   rusage->ru_stime.tv_sec = usage.ru_stime.tv_sec;
   rusage->ru_stime.tv_usec = usage.ru_stime.tv_usec;
 
-#if !defined(__MVS__)
+#if !defined(__MVS__) && !defined(__HAIKU__)
   rusage->ru_maxrss = usage.ru_maxrss;
   rusage->ru_ixrss = usage.ru_ixrss;
   rusage->ru_idrss = usage.ru_idrss;
@@ -1291,7 +1317,7 @@ int uv_os_gethostname(char* buffer, size_t* size) {
     instead by creating a large enough buffer and comparing the hostname length
     to the size input.
   */
-  char buf[MAXHOSTNAMELEN + 1];
+  char buf[UV_MAXHOSTNAMESIZE];
   size_t len;
 
   if (buffer == NULL || size == NULL || *size == 0)
@@ -1414,3 +1440,39 @@ error:
   buffer->machine[0] = '\0';
   return r;
 }
+
+int uv__getsockpeername(const uv_handle_t* handle,
+                        uv__peersockfunc func,
+                        struct sockaddr* name,
+                        int* namelen) {
+  socklen_t socklen;
+  uv_os_fd_t fd;
+  int r;
+
+  r = uv_fileno(handle, &fd);
+  if (r < 0)
+    return r;
+
+  /* sizeof(socklen_t) != sizeof(int) on some systems. */
+  socklen = (socklen_t) *namelen;
+
+  if (func(fd, name, &socklen))
+    return UV__ERR(errno);
+
+  *namelen = (int) socklen;
+  return 0;
+}
+
+int uv_gettimeofday(uv_timeval64_t* tv) {
+  struct timeval time;
+
+  if (tv == NULL)
+    return UV_EINVAL;
+
+  if (gettimeofday(&time, NULL) != 0)
+    return UV__ERR(errno);
+
+  tv->tv_sec = (int64_t) time.tv_sec;
+  tv->tv_usec = (int32_t) time.tv_usec;
+  return 0;
+}
diff --git a/src/unix/darwin.c b/src/unix/darwin.c
index 31ad8a9..e4cd8ff 100644
--- a/src/unix/darwin.c
+++ b/src/unix/darwin.c
@@ -117,6 +117,11 @@ uint64_t uv_get_total_memory(void) {
 }
 
 
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
 void uv_loadavg(double avg[3]) {
   struct loadavg info;
   size_t size = sizeof(info);
diff --git a/src/unix/freebsd.c b/src/unix/freebsd.c
index 0f729cf..7de88d6 100644
--- a/src/unix/freebsd.c
+++ b/src/unix/freebsd.c
@@ -137,6 +137,11 @@ uint64_t uv_get_total_memory(void) {
 }
 
 
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
 void uv_loadavg(double avg[3]) {
   struct loadavg info;
   size_t size = sizeof(info);
diff --git a/src/unix/fs.c b/src/unix/fs.c
index a0bd70d..0d6bfc6 100644
--- a/src/unix/fs.c
+++ b/src/unix/fs.c
@@ -47,7 +47,7 @@
 
 #if defined(__DragonFly__)        ||                                      \
     defined(__FreeBSD__)          ||                                      \
-    defined(__FreeBSD_kernel_)    ||                                      \
+    defined(__FreeBSD_kernel__)   ||                                      \
     defined(__OpenBSD__)          ||                                      \
     defined(__NetBSD__)
 # define HAVE_PREADV 1
@@ -60,7 +60,6 @@
 #endif
 
 #if defined(__APPLE__)
-# include <copyfile.h>
 # include <sys/sysctl.h>
 #elif defined(__linux__) && !defined(FICLONE)
 # include <sys/ioctl.h>
@@ -143,19 +142,34 @@ extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */
   while (0)
 
 
+static int uv__fs_close(int fd) {
+  int rc;
+
+  rc = uv__close_nocancel(fd);
+  if (rc == -1)
+    if (errno == EINTR || errno == EINPROGRESS)
+      rc = 0;  /* The close is in progress, not an error. */
+
+  return rc;
+}
+
+
 static ssize_t uv__fs_fsync(uv_fs_t* req) {
 #if defined(__APPLE__)
   /* Apple's fdatasync and fsync explicitly do NOT flush the drive write cache
    * to the drive platters. This is in contrast to Linux's fdatasync and fsync
    * which do, according to recent man pages. F_FULLFSYNC is Apple's equivalent
    * for flushing buffered data to permanent storage. If F_FULLFSYNC is not
-   * supported by the file system we should fall back to fsync(). This is the
-   * same approach taken by sqlite.
+   * supported by the file system we fall back to F_BARRIERFSYNC or fsync().
+   * This is the same approach taken by sqlite, except sqlite does not issue
+   * an F_BARRIERFSYNC call.
    */
   int r;
 
   r = fcntl(req->file, F_FULLFSYNC);
   if (r != 0)
+    r = fcntl(req->file, F_BARRIERFSYNC);  /* fsync + barrier */
+  if (r != 0)
     r = fsync(req->file);
   return r;
 #else
@@ -178,7 +192,8 @@ static ssize_t uv__fs_fdatasync(uv_fs_t* req) {
 
 static ssize_t uv__fs_futime(uv_fs_t* req) {
 #if defined(__linux__)                                                        \
-    || defined(_AIX71)
+    || defined(_AIX71)                                                        \
+    || defined(__HAIKU__)
   /* utimesat() has nanosecond resolution but we stick to microseconds
    * for the sake of consistency with other platforms.
    */
@@ -317,6 +332,18 @@ done:
   req->bufs = NULL;
   req->nbufs = 0;
 
+#ifdef __PASE__
+  /* PASE returns EOPNOTSUPP when reading a directory, convert to EISDIR */
+  if (result == -1 && errno == EOPNOTSUPP) {
+    struct stat buf;
+    ssize_t rc;
+    rc = fstat(req->file, &buf);
+    if (rc == 0 && S_ISDIR(buf.st_mode)) {
+      errno = EISDIR;
+    }
+  }
+#endif
+
   return result;
 }
 
@@ -339,7 +366,7 @@ static int uv__fs_scandir_sort(UV_CONST_DIRENT** a, UV_CONST_DIRENT** b) {
 
 
 static ssize_t uv__fs_scandir(uv_fs_t* req) {
-  uv__dirent_t **dents;
+  uv__dirent_t** dents;
   int n;
 
   dents = NULL;
@@ -363,6 +390,87 @@ static ssize_t uv__fs_scandir(uv_fs_t* req) {
   return n;
 }
 
+static int uv__fs_opendir(uv_fs_t* req) {
+  uv_dir_t* dir;
+
+  dir = uv__malloc(sizeof(*dir));
+  if (dir == NULL)
+    goto error;
+
+  dir->dir = opendir(req->path);
+  if (dir->dir == NULL)
+    goto error;
+
+  req->ptr = dir;
+  return 0;
+
+error:
+  uv__free(dir);
+  req->ptr = NULL;
+  return -1;
+}
+
+static int uv__fs_readdir(uv_fs_t* req) {
+  uv_dir_t* dir;
+  uv_dirent_t* dirent;
+  struct dirent* res;
+  unsigned int dirent_idx;
+  unsigned int i;
+
+  dir = req->ptr;
+  dirent_idx = 0;
+
+  while (dirent_idx < dir->nentries) {
+    /* readdir() returns NULL on end of directory, as well as on error. errno
+       is used to differentiate between the two conditions. */
+    errno = 0;
+    res = readdir(dir->dir);
+
+    if (res == NULL) {
+      if (errno != 0)
+        goto error;
+      break;
+    }
+
+    if (strcmp(res->d_name, ".") == 0 || strcmp(res->d_name, "..") == 0)
+      continue;
+
+    dirent = &dir->dirents[dirent_idx];
+    dirent->name = uv__strdup(res->d_name);
+
+    if (dirent->name == NULL)
+      goto error;
+
+    dirent->type = uv__fs_get_dirent_type(res);
+    ++dirent_idx;
+  }
+
+  return dirent_idx;
+
+error:
+  for (i = 0; i < dirent_idx; ++i) {
+    uv__free((char*) dir->dirents[i].name);
+    dir->dirents[i].name = NULL;
+  }
+
+  return -1;
+}
+
+static int uv__fs_closedir(uv_fs_t* req) {
+  uv_dir_t* dir;
+
+  dir = req->ptr;
+
+  if (dir->dir != NULL) {
+    closedir(dir->dir);
+    dir->dir = NULL;
+  }
+
+  uv__free(req->ptr);
+  req->ptr = NULL;
+  return 0;
+}
+
 #if defined(_POSIX_PATH_MAX)
 # define UV__FS_PATH_MAX _POSIX_PATH_MAX
 #elif defined(PATH_MAX)
@@ -692,7 +800,8 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) {
 static ssize_t uv__fs_utime(uv_fs_t* req) {
 #if defined(__linux__)                                                         \
     || defined(_AIX71)                                                         \
-    || defined(__sun)
+    || defined(__sun)                                                          \
+    || defined(__HAIKU__)
   /* utimesat() has nanosecond resolution but we stick to microseconds
    * for the sake of consistency with other platforms.
    */
@@ -796,45 +905,6 @@ done:
 }
 
 static ssize_t uv__fs_copyfile(uv_fs_t* req) {
-#if defined(__APPLE__) && !TARGET_OS_IPHONE
-  /* On macOS, use the native copyfile(3). */
-  static int can_clone;
-  copyfile_flags_t flags;
-  char buf[64];
-  size_t len;
-  int major;
-
-  flags = COPYFILE_ALL;
-
-  if (req->flags & UV_FS_COPYFILE_EXCL)
-    flags |= COPYFILE_EXCL;
-
-  /* Check OS version. Cloning is only supported on macOS >= 10.12. */
-  if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) {
-    if (can_clone == 0) {
-      len = sizeof(buf);
-      if (sysctlbyname("kern.osrelease", buf, &len, NULL, 0))
-        return UV__ERR(errno);
-
-      if (1 != sscanf(buf, "%d", &major))
-        abort();
-
-      can_clone = -1 + 2 * (major >= 16);  /* macOS >= 10.12 */
-    }
-
-    if (can_clone < 0)
-      return UV_ENOSYS;
-  }
-
-  /* copyfile() simply ignores COPYFILE_CLONE if it's not supported. */
-  if (req->flags & UV_FS_COPYFILE_FICLONE)
-    flags |= 1 << 24;  /* COPYFILE_CLONE */
-
-  if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE)
-    flags |= 1 << 25;  /* COPYFILE_CLONE_FORCE */
-
-  return copyfile(req->path, req->new_path, NULL, flags);
-#else
   uv_fs_t fs_req;
   uv_file srcfd;
   uv_file dstfd;
@@ -961,7 +1031,6 @@ out:
 
   errno = UV__ERR(result);
   return -1;
-#endif
 }
 
 static void uv__to_stat(struct stat* src, uv_stat_t* dst) {
@@ -1041,10 +1110,84 @@ static void uv__to_stat(struct stat* src, uv_stat_t* dst) {
 }
 
 
+static int uv__fs_statx(int fd,
+                        const char* path,
+                        int is_fstat,
+                        int is_lstat,
+                        uv_stat_t* buf) {
+  STATIC_ASSERT(UV_ENOSYS != -1);
+#ifdef __linux__
+  static int no_statx;
+  struct uv__statx statxbuf;
+  int dirfd;
+  int flags;
+  int mode;
+  int rc;
+
+  if (no_statx)
+    return UV_ENOSYS;
+
+  dirfd = AT_FDCWD;
+  flags = 0; /* AT_STATX_SYNC_AS_STAT */
+  mode = 0xFFF; /* STATX_BASIC_STATS + STATX_BTIME */
+
+  if (is_fstat) {
+    dirfd = fd;
+    flags |= 0x1000; /* AT_EMPTY_PATH */
+  }
+
+  if (is_lstat)
+    flags |= AT_SYMLINK_NOFOLLOW;
+
+  rc = uv__statx(dirfd, path, flags, mode, &statxbuf);
+
+  if (rc == -1) {
+    /* EPERM happens when a seccomp filter rejects the system call.
+     * Has been observed with libseccomp < 2.3.3 and docker < 18.04.
+     */
+    if (errno != EINVAL && errno != EPERM && errno != ENOSYS)
+      return -1;
+
+    no_statx = 1;
+    return UV_ENOSYS;
+  }
+
+  buf->st_dev = 256 * statxbuf.stx_dev_major + statxbuf.stx_dev_minor;
+  buf->st_mode = statxbuf.stx_mode;
+  buf->st_nlink = statxbuf.stx_nlink;
+  buf->st_uid = statxbuf.stx_uid;
+  buf->st_gid = statxbuf.stx_gid;
+  buf->st_rdev = statxbuf.stx_rdev_major;
+  buf->st_ino = statxbuf.stx_ino;
+  buf->st_size = statxbuf.stx_size;
+  buf->st_blksize = statxbuf.stx_blksize;
+  buf->st_blocks = statxbuf.stx_blocks;
+  buf->st_atim.tv_sec = statxbuf.stx_atime.tv_sec;
+  buf->st_atim.tv_nsec = statxbuf.stx_atime.tv_nsec;
+  buf->st_mtim.tv_sec = statxbuf.stx_mtime.tv_sec;
+  buf->st_mtim.tv_nsec = statxbuf.stx_mtime.tv_nsec;
+  buf->st_ctim.tv_sec = statxbuf.stx_ctime.tv_sec;
+  buf->st_ctim.tv_nsec = statxbuf.stx_ctime.tv_nsec;
+  buf->st_birthtim.tv_sec = statxbuf.stx_btime.tv_sec;
+  buf->st_birthtim.tv_nsec = statxbuf.stx_btime.tv_nsec;
+  buf->st_flags = 0;
+  buf->st_gen = 0;
+
+  return 0;
+#else
+  return UV_ENOSYS;
+#endif /* __linux__ */
+}
+
+
 static int uv__fs_stat(const char *path, uv_stat_t *buf) {
   struct stat pbuf;
   int ret;
 
+  ret = uv__fs_statx(-1, path, /* is_fstat */ 0, /* is_lstat */ 0, buf);
+  if (ret != UV_ENOSYS)
+    return ret;
+
   ret = stat(path, &pbuf);
   if (ret == 0)
     uv__to_stat(&pbuf, buf);
@@ -1057,6 +1200,10 @@ static int uv__fs_lstat(const char *path, uv_stat_t *buf) {
   struct stat pbuf;
   int ret;
 
+  ret = uv__fs_statx(-1, path, /* is_fstat */ 0, /* is_lstat */ 1, buf);
+  if (ret != UV_ENOSYS)
+    return ret;
+
   ret = lstat(path, &pbuf);
   if (ret == 0)
     uv__to_stat(&pbuf, buf);
@@ -1069,6 +1216,10 @@ static int uv__fs_fstat(int fd, uv_stat_t *buf) {
   struct stat pbuf;
   int ret;
 
+  ret = uv__fs_statx(fd, "", /* is_fstat */ 1, /* is_lstat */ 0, buf);
+  if (ret != UV_ENOSYS)
+    return ret;
+
   ret = fstat(fd, &pbuf);
   if (ret == 0)
     uv__to_stat(&pbuf, buf);
@@ -1157,7 +1308,7 @@ static void uv__fs_work(struct uv__work* w) {
     X(ACCESS, access(req->path, req->flags));
     X(CHMOD, chmod(req->path, req->mode));
     X(CHOWN, chown(req->path, req->uid, req->gid));
-    X(CLOSE, close(req->file));
+    X(CLOSE, uv__fs_close(req->file));
     X(COPYFILE, uv__fs_copyfile(req));
     X(FCHMOD, fchmod(req->file, req->mode));
     X(FCHOWN, fchown(req->file, req->uid, req->gid));
@@ -1174,6 +1325,9 @@ static void uv__fs_work(struct uv__work* w) {
     X(OPEN, uv__fs_open(req));
     X(READ, uv__fs_read(req));
     X(SCANDIR, uv__fs_scandir(req));
+    X(OPENDIR, uv__fs_opendir(req));
+    X(READDIR, uv__fs_readdir(req));
+    X(CLOSEDIR, uv__fs_closedir(req));
     X(READLINK, uv__fs_readlink(req));
     X(REALPATH, uv__fs_realpath(req));
     X(RENAME, rename(req->path, req->new_path));
@@ -1444,6 +1598,40 @@ int uv_fs_scandir(uv_loop_t* loop,
   POST;
 }
 
+int uv_fs_opendir(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  const char* path,
+                  uv_fs_cb cb) {
+  INIT(OPENDIR);
+  PATH;
+  POST;
+}
+
+int uv_fs_readdir(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  uv_dir_t* dir,
+                  uv_fs_cb cb) {
+  INIT(READDIR);
+
+  if (dir == NULL || dir->dir == NULL || dir->dirents == NULL)
+    return UV_EINVAL;
+
+  req->ptr = dir;
+  POST;
+}
+
+int uv_fs_closedir(uv_loop_t* loop,
+                   uv_fs_t* req,
+                   uv_dir_t* dir,
+                   uv_fs_cb cb) {
+  INIT(CLOSEDIR);
+
+  if (dir == NULL)
+    return UV_EINVAL;
+
+  req->ptr = dir;
+  POST;
+}
 
 int uv_fs_readlink(uv_loop_t* loop,
                    uv_fs_t* req,
@@ -1584,6 +1772,9 @@ void uv_fs_req_cleanup(uv_fs_t* req) {
   req->path = NULL;
   req->new_path = NULL;
 
+  if (req->fs_type == UV_FS_READDIR && req->ptr != NULL)
+    uv__fs_readdir_cleanup(req);
+
   if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL)
     uv__fs_scandir_cleanup(req);
 
@@ -1591,7 +1782,7 @@ void uv_fs_req_cleanup(uv_fs_t* req) {
     uv__free(req->bufs);
   req->bufs = NULL;
 
-  if (req->ptr != &req->statbuf)
+  if (req->fs_type != UV_FS_OPENDIR && req->ptr != &req->statbuf)
     uv__free(req->ptr);
   req->ptr = NULL;
 }
diff --git a/src/unix/fsevents.c b/src/unix/fsevents.c
index c430562..ddacda3 100644
--- a/src/unix/fsevents.c
+++ b/src/unix/fsevents.c
@@ -21,9 +21,10 @@
 #include "uv.h"
 #include "internal.h"
 
-#if TARGET_OS_IPHONE
+#if TARGET_OS_IPHONE || MAC_OS_X_VERSION_MAX_ALLOWED < 1070
 
 /* iOS (currently) doesn't provide the FSEvents-API (nor CoreServices) */
+/* macOS prior to 10.7 doesn't provide the full FSEvents API so use kqueue */
 
 int uv__fsevents_init(uv_fs_event_t* handle) {
   return 0;
diff --git a/src/unix/getaddrinfo.c b/src/unix/getaddrinfo.c
index 6d23fbe..d7ca7d1 100644
--- a/src/unix/getaddrinfo.c
+++ b/src/unix/getaddrinfo.c
@@ -92,7 +92,9 @@ int uv__getaddrinfo_translate_error(int sys_err) {
   }
   assert(!"unknown EAI_* error code");
   abort();
+#ifndef __SUNPRO_C
   return 0;  /* Pacify compiler. */
+#endif
 }
 
 
diff --git a/src/unix/haiku.c b/src/unix/haiku.c
new file mode 100644
index 0000000..7708851
--- /dev/null
+++ b/src/unix/haiku.c
@@ -0,0 +1,176 @@
+/* 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 <FindDirectory.h> /* find_path() */
+#include <OS.h>
+
+
+void uv_loadavg(double avg[3]) {
+  avg[0] = 0;
+  avg[1] = 0;
+  avg[2] = 0;
+}
+
+
+int uv_exepath(char* buffer, size_t* size) {
+  char abspath[B_PATH_NAME_LENGTH];
+  status_t status;
+  ssize_t abspath_len;
+
+  if (buffer == NULL || size == NULL || *size == 0)
+    return UV_EINVAL;
+
+  status = find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, abspath,
+                     sizeof(abspath));
+  if (status != B_OK)
+    return UV__ERR(status);
+
+  abspath_len = uv__strscpy(buffer, abspath, *size);
+  *size -= 1;
+  if (abspath_len >= 0 && *size > (size_t)abspath_len)
+    *size = (size_t)abspath_len;
+
+  return 0;
+}
+
+
+uint64_t uv_get_free_memory(void) {
+  status_t status;
+  system_info sinfo;
+
+  status = get_system_info(&sinfo);
+  if (status != B_OK)
+    return 0;
+
+  return (sinfo.max_pages - sinfo.used_pages) * B_PAGE_SIZE;
+}
+
+
+uint64_t uv_get_total_memory(void) {
+  status_t status;
+  system_info sinfo;
+
+  status = get_system_info(&sinfo);
+  if (status != B_OK)
+    return 0;
+
+  return sinfo.max_pages * B_PAGE_SIZE;
+}
+
+
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
+int uv_resident_set_memory(size_t* rss) {
+  area_info area;
+  ssize_t cookie;
+  status_t status;
+  thread_info thread;
+
+  status = get_thread_info(find_thread(NULL), &thread);
+  if (status != B_OK)
+    return UV__ERR(status);
+
+  cookie = 0;
+  *rss = 0;
+  while (get_next_area_info(thread.team, &cookie, &area) == B_OK)
+    *rss += area.ram_size;
+
+  return 0;
+}
+
+
+int uv_uptime(double* uptime) {
+  /* system_time() returns time since booting in microseconds */
+  *uptime = (double)system_time() / 1000000;
+  return 0;
+}
+
+
+int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+  cpu_topology_node_info* topology_infos;
+  int i;
+  status_t status;
+  system_info system;
+  uint32_t topology_count;
+  uint64_t cpuspeed;
+  uv_cpu_info_t* cpu_info;
+
+  if (cpu_infos == NULL || count == NULL)
+    return UV_EINVAL;
+
+  status = get_cpu_topology_info(NULL, &topology_count);
+  if (status != B_OK)
+    return UV__ERR(status);
+
+  topology_infos = uv__malloc(topology_count * sizeof(*topology_infos));
+  if (topology_infos == NULL)
+    return UV_ENOMEM;
+
+  status = get_cpu_topology_info(topology_infos, &topology_count);
+  if (status != B_OK) {
+    uv__free(topology_infos);
+    return UV__ERR(status);
+  }
+
+  cpuspeed = 0;
+  for (i = 0; i < (int)topology_count; i++) {
+    if (topology_infos[i].type == B_TOPOLOGY_CORE) {
+      cpuspeed = topology_infos[i].data.core.default_frequency;
+      break;
+    }
+  }
+
+  uv__free(topology_infos);
+
+  status = get_system_info(&system);
+  if (status != B_OK)
+    return UV__ERR(status);
+
+  *cpu_infos = uv__calloc(system.cpu_count, sizeof(**cpu_infos));
+  if (*cpu_infos == NULL)
+    return UV_ENOMEM;
+
+  /* CPU time and model are not exposed by Haiku. */
+  cpu_info = *cpu_infos;
+  for (i = 0; i < (int)system.cpu_count; i++) {
+    cpu_info->model = uv__strdup("unknown");
+    cpu_info->speed = (int)(cpuspeed / 1000000);
+    cpu_info++;
+  }
+  *count = system.cpu_count;
+
+  return 0;
+}
+
+void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
+  int i;
+
+  for (i = 0; i < count; i++)
+    uv__free(cpu_infos[i].model);
+
+  uv__free(cpu_infos);
+}
diff --git a/src/unix/ibmi.c b/src/unix/ibmi.c
index 13fed6c..c7e1051 100644
--- a/src/unix/ibmi.c
+++ b/src/unix/ibmi.c
@@ -55,19 +55,155 @@
 #include <strings.h>
 #include <sys/vnode.h>
 
+#include <as400_protos.h>
+
+
+typedef struct {
+  int bytes_available;
+  int bytes_returned;
+  char current_date_and_time[8];
+  char system_name[8];
+  char elapsed_time[6];
+  char restricted_state_flag;
+  char reserved;
+  int percent_processing_unit_used;
+  int jobs_in_system;
+  int percent_permanent_addresses;
+  int percent_temporary_addresses;
+  int system_asp;
+  int percent_system_asp_used;
+  int total_auxiliary_storage;
+  int current_unprotected_storage_used;
+  int maximum_unprotected_storage_used;
+  int percent_db_capability;
+  int main_storage_size;
+  int number_of_partitions;
+  int partition_identifier;
+  int reserved1;
+  int current_processing_capacity;
+  char processor_sharing_attribute;
+  char reserved2[3];
+  int number_of_processors;
+  int active_jobs_in_system;
+  int active_threads_in_system;
+  int maximum_jobs_in_system;
+  int percent_temporary_256mb_segments_used;
+  int percent_temporary_4gb_segments_used;
+  int percent_permanent_256mb_segments_used;
+  int percent_permanent_4gb_segments_used;
+  int percent_current_interactive_performance;
+  int percent_uncapped_cpu_capacity_used;
+  int percent_shared_processor_pool_used;
+  long main_storage_size_long;
+} SSTS0200;
+
+
+static int get_ibmi_system_status(SSTS0200* rcvr) {
+  /* rcvrlen is input parameter 2 to QWCRSSTS */
+  unsigned int rcvrlen = sizeof(*rcvr);
+
+  /* format is input parameter 3 to QWCRSSTS ("SSTS0200" in EBCDIC) */
+  unsigned char format[] = {0xE2, 0xE2, 0xE3, 0xE2, 0xF0, 0xF2, 0xF0, 0xF0};
+
+  /* reset_status is input parameter 4 to QWCRSSTS ("*NO       " in EBCDIC) */
+  unsigned char reset_status[] = {
+    0x5C, 0xD5, 0xD6, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40
+  };
+
+  /* errcode is input parameter 5 to QWCRSSTS */
+  struct _errcode {
+    int bytes_provided;
+    int bytes_available;
+    char msgid[7];
+  } errcode;
+
+  /* qwcrssts_pointer is the 16-byte tagged system pointer to QWCRSSTS */
+  ILEpointer __attribute__((aligned(16))) qwcrssts_pointer;
+
+  /* qwcrssts_argv is the array of argument pointers to QWCRSSTS */
+  void* qwcrssts_argv[6];
+
+  /* Set the IBM i pointer to the QSYS/QWCRSSTS *PGM object */
+  int rc = _RSLOBJ2(&qwcrssts_pointer, RSLOBJ_TS_PGM, "QWCRSSTS", "QSYS");
+
+  if (rc != 0)
+    return rc;
+
+  /* initialize the QWCRSSTS returned info structure */
+  memset(rcvr, 0, sizeof(*rcvr));
+
+  /* initialize the QWCRSSTS error code structure */
+  memset(&errcode, 0, sizeof(errcode));
+  errcode.bytes_provided = sizeof(errcode);
+
+  /* initialize the array of argument pointers for the QWCRSSTS API */
+  qwcrssts_argv[0] = rcvr;
+  qwcrssts_argv[1] = &rcvrlen;
+  qwcrssts_argv[2] = &format;
+  qwcrssts_argv[3] = &reset_status;
+  qwcrssts_argv[4] = &errcode;
+  qwcrssts_argv[5] = NULL;
+
+  /* Call the IBM i QWCRSSTS API from PASE */
+  rc = _PGMCALL(&qwcrssts_pointer, (void**)&qwcrssts_argv, 0);
+
+  return rc;
+}
+
+
 uint64_t uv_get_free_memory(void) {
-  return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_AVPHYS_PAGES);
+  SSTS0200 rcvr;
+
+  if (get_ibmi_system_status(&rcvr))
+    return 0;
+
+  /* The amount of main storage, in kilobytes, in the system. */
+  uint64_t main_storage_size = rcvr.main_storage_size;
+
+  /* The current amount of storage in use for temporary objects.
+   * in millions (M) of bytes.
+   */
+  uint64_t current_unprotected_storage_used =
+    rcvr.current_unprotected_storage_used * 1024ULL;
+
+  uint64_t free_storage_size =
+    (main_storage_size - current_unprotected_storage_used) * 1024ULL;
+
+  return free_storage_size < 0 ? 0 : free_storage_size;
 }
 
 
 uint64_t uv_get_total_memory(void) {
-  return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_PHYS_PAGES);
+  SSTS0200 rcvr;
+
+  if (get_ibmi_system_status(&rcvr))
+    return 0;
+
+  return (uint64_t)rcvr.main_storage_size * 1024ULL;
+}
+
+
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
 }
 
 
 void uv_loadavg(double avg[3]) {
+  SSTS0200 rcvr;
+
+  if (get_ibmi_system_status(&rcvr)) {
     avg[0] = avg[1] = avg[2] = 0;
     return;
+  }
+
+  /* The average (in tenths) of the elapsed time during which the processing
+   * units were in use. For example, a value of 411 in binary would be 41.1%.
+   * This percentage could be greater than 100% for an uncapped partition.
+   */
+  double processing_unit_used_percent =
+    rcvr.percent_processing_unit_used / 1000.0;
+
+  avg[0] = avg[1] = avg[2] = processing_unit_used_percent;
 }
 
 
@@ -111,3 +247,4 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
 
   return 0;
 }
+
diff --git a/src/unix/internal.h b/src/unix/internal.h
index c059893..8c8ddc8 100644
--- a/src/unix/internal.h
+++ b/src/unix/internal.h
@@ -95,8 +95,7 @@ int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset);
  */
 #if defined(__clang__) ||                                                     \
     defined(__GNUC__) ||                                                      \
-    defined(__INTEL_COMPILER) ||                                              \
-    defined(__SUNPRO_C)
+    defined(__INTEL_COMPILER)
 # define UV_DESTRUCTOR(declaration) __attribute__((destructor)) declaration
 # define UV_UNUSED(declaration)     __attribute__((unused)) declaration
 #else
@@ -184,6 +183,7 @@ int uv__nonblock_ioctl(int fd, int set);
 int uv__nonblock_fcntl(int fd, int set);
 int uv__close(int fd); /* preserves errno */
 int uv__close_nocheckstdio(int fd);
+int uv__close_nocancel(int fd);
 int uv__socket(int domain, int type, int protocol);
 ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags);
 void uv__make_close_pending(uv_handle_t* handle);
@@ -306,4 +306,11 @@ UV_UNUSED(static char* uv__basename_r(const char* path)) {
 int uv__inotify_fork(uv_loop_t* loop, void* old_watchers);
 #endif
 
+typedef int (*uv__peersockfunc)(int, struct sockaddr*, socklen_t*);
+
+int uv__getsockpeername(const uv_handle_t* handle,
+                        uv__peersockfunc func,
+                        struct sockaddr* name,
+                        int* namelen);
+
 #endif /* UV_UNIX_INTERNAL_H_ */
diff --git a/src/unix/kqueue.c b/src/unix/kqueue.c
index c24f96e..c04e7a4 100644
--- a/src/unix/kqueue.c
+++ b/src/unix/kqueue.c
@@ -59,7 +59,7 @@ int uv__kqueue_init(uv_loop_t* loop) {
 }
 
 
-#if defined(__APPLE__)
+#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
 static int uv__has_forked_with_cfrunloop;
 #endif
 
@@ -70,7 +70,7 @@ int uv__io_fork(uv_loop_t* loop) {
   if (err)
     return err;
 
-#if defined(__APPLE__)
+#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
   if (loop->cf_state != NULL) {
     /* We cannot start another CFRunloop and/or thread in the child
        process; CF aborts if you try or if you try to touch the thread
@@ -86,7 +86,7 @@ int uv__io_fork(uv_loop_t* loop) {
     uv__free(loop->cf_state);
     loop->cf_state = NULL;
   }
-#endif
+#endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
   return err;
 }
 
@@ -387,6 +387,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
   uintptr_t nfds;
 
   assert(loop->watchers != NULL);
+  assert(fd >= 0);
 
   events = (struct kevent*) loop->watchers[loop->nwatchers];
   nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
@@ -457,7 +458,7 @@ int uv_fs_event_start(uv_fs_event_t* handle,
   if (uv__is_active(handle))
     return UV_EINVAL;
 
-#if defined(__APPLE__)
+#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
   /* Nullify field to perform checks later */
   handle->cf_cb = NULL;
   handle->realpath = NULL;
@@ -481,7 +482,7 @@ int uv_fs_event_start(uv_fs_event_t* handle,
     }
     return r;
   }
-#endif /* defined(__APPLE__) */
+#endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */
 
   /* TODO open asynchronously - but how do we report back errors? */
   fd = open(path, O_RDONLY);
@@ -489,8 +490,11 @@ int uv_fs_event_start(uv_fs_event_t* handle,
     return UV__ERR(errno);
 
   handle->path = uv__strdup(path);
-  if (handle->path == NULL)
+  if (handle->path == NULL) {
+    uv__close_nocheckstdio(fd);
     return UV_ENOMEM;
+  }
+
   handle->cb = cb;
   uv__handle_start(handle);
   uv__io_init(&handle->event_watcher, uv__fs_event, fd);
@@ -509,7 +513,7 @@ int uv_fs_event_stop(uv_fs_event_t* handle) {
 
   uv__handle_stop(handle);
 
-#if defined(__APPLE__)
+#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
   if (!uv__has_forked_with_cfrunloop)
     r = uv__fsevents_close(handle);
 #endif
diff --git a/src/unix/linux-core.c b/src/unix/linux-core.c
index 3341b94..b539beb 100644
--- a/src/unix/linux-core.c
+++ b/src/unix/linux-core.c
@@ -26,6 +26,7 @@
 #include "uv.h"
 #include "internal.h"
 
+#include <inttypes.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -79,16 +80,20 @@ static int read_times(FILE* statfile_fp,
                       unsigned int numcpus,
                       uv_cpu_info_t* ci);
 static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci);
-static unsigned long read_cpufreq(unsigned int cpunum);
+static uint64_t read_cpufreq(unsigned int cpunum);
 
 
 int uv__platform_loop_init(uv_loop_t* loop) {
   int fd;
 
-  fd = epoll_create1(EPOLL_CLOEXEC);
+  /* 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.
+   */
+  fd = epoll_create1(O_CLOEXEC);
 
   /* epoll_create1() can fail either because it's not implemented (old kernel)
-   * or because it doesn't understand the EPOLL_CLOEXEC flag.
+   * or because it doesn't understand the O_CLOEXEC flag.
    */
   if (fd == -1 && (errno == ENOSYS || errno == EINVAL)) {
     fd = epoll_create(256);
@@ -141,6 +146,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
   uintptr_t nfds;
 
   assert(loop->watchers != NULL);
+  assert(fd >= 0);
 
   events = (struct epoll_event*) loop->watchers[loop->nwatchers];
   nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
@@ -714,20 +720,20 @@ static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) {
 static int read_times(FILE* statfile_fp,
                       unsigned int numcpus,
                       uv_cpu_info_t* ci) {
-  unsigned long clock_ticks;
   struct uv_cpu_times_s ts;
-  unsigned long user;
-  unsigned long nice;
-  unsigned long sys;
-  unsigned long idle;
-  unsigned long dummy;
-  unsigned long irq;
-  unsigned int num;
-  unsigned int len;
+  uint64_t clock_ticks;
+  uint64_t user;
+  uint64_t nice;
+  uint64_t sys;
+  uint64_t idle;
+  uint64_t dummy;
+  uint64_t irq;
+  uint64_t num;
+  uint64_t len;
   char buf[1024];
 
   clock_ticks = sysconf(_SC_CLK_TCK);
-  assert(clock_ticks != (unsigned long) -1);
+  assert(clock_ticks != (uint64_t) -1);
   assert(clock_ticks != 0);
 
   rewind(statfile_fp);
@@ -760,7 +766,8 @@ static int read_times(FILE* statfile_fp,
      * fields, they're not allowed in C89 mode.
      */
     if (6 != sscanf(buf + len,
-                    "%lu %lu %lu %lu %lu %lu",
+                    "%" PRIu64 " %" PRIu64 " %" PRIu64
+                    "%" PRIu64 " %" PRIu64 " %" PRIu64,
                     &user,
                     &nice,
                     &sys,
@@ -782,8 +789,8 @@ static int read_times(FILE* statfile_fp,
 }
 
 
-static unsigned long read_cpufreq(unsigned int cpunum) {
-  unsigned long val;
+static uint64_t read_cpufreq(unsigned int cpunum) {
+  uint64_t val;
   char buf[1024];
   FILE* fp;
 
@@ -796,7 +803,7 @@ static unsigned long read_cpufreq(unsigned int cpunum) {
   if (fp == NULL)
     return 0;
 
-  if (fscanf(fp, "%lu", &val) != 1)
+  if (fscanf(fp, "%" PRIu64, &val) != 1)
     val = 0;
 
   fclose(fp);
@@ -859,7 +866,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
     return 0;
   }
 
-  *addresses = uv__malloc(*count * sizeof(**addresses));
+  /* Make sure the memory is initiallized to zero using calloc() */
+  *addresses = uv__calloc(*count, sizeof(**addresses));
   if (!(*addresses)) {
     freeifaddrs(addrs);
     return UV_ENOMEM;
@@ -898,11 +906,12 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
     address = *addresses;
 
     for (i = 0; i < (*count); i++) {
-      if (strcmp(address->name, ent->ifa_name) == 0) {
+      size_t namelen = strlen(ent->ifa_name);
+      /* Alias interface share the same physical address */
+      if (strncmp(address->name, ent->ifa_name, namelen) == 0 &&
+          (address->name[namelen] == 0 || address->name[namelen] == ':')) {
         sll = (struct sockaddr_ll*)ent->ifa_addr;
         memcpy(address->phys_addr, sll->sll_addr, sizeof(address->phys_addr));
-      } else {
-        memset(address->phys_addr, 0, sizeof(address->phys_addr));
       }
       address++;
     }
@@ -932,3 +941,114 @@ void uv__set_process_title(const char* title) {
   prctl(PR_SET_NAME, title);  /* Only copies first 16 characters. */
 #endif
 }
+
+
+static uint64_t uv__read_proc_meminfo(const char* what) {
+  uint64_t rc;
+  ssize_t n;
+  char* p;
+  int fd;
+  char buf[4096];  /* Large enough to hold all of /proc/meminfo. */
+
+  rc = 0;
+  fd = uv__open_cloexec("/proc/meminfo", O_RDONLY);
+
+  if (fd == -1)
+    return 0;
+
+  n = read(fd, buf, sizeof(buf) - 1);
+
+  if (n <= 0)
+    goto out;
+
+  buf[n] = '\0';
+  p = strstr(buf, what);
+
+  if (p == NULL)
+    goto out;
+
+  p += strlen(what);
+
+  if (1 != sscanf(p, "%" PRIu64 " kB", &rc))
+    goto out;
+
+  rc *= 1024;
+
+out:
+
+  if (uv__close_nocheckstdio(fd))
+    abort();
+
+  return rc;
+}
+
+
+uint64_t uv_get_free_memory(void) {
+  struct sysinfo info;
+  uint64_t rc;
+
+  rc = uv__read_proc_meminfo("MemFree:");
+
+  if (rc != 0)
+    return rc;
+
+  if (0 == sysinfo(&info))
+    return (uint64_t) info.freeram * info.mem_unit;
+
+  return 0;
+}
+
+
+uint64_t uv_get_total_memory(void) {
+  struct sysinfo info;
+  uint64_t rc;
+
+  rc = uv__read_proc_meminfo("MemTotal:");
+
+  if (rc != 0)
+    return rc;
+
+  if (0 == sysinfo(&info))
+    return (uint64_t) info.totalram * info.mem_unit;
+
+  return 0;
+}
+
+
+static uint64_t uv__read_cgroups_uint64(const char* cgroup, const char* param) {
+  char filename[256];
+  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);
+
+  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';
+    sscanf(buf, "%" PRIu64, &rc);
+  }
+
+  if (uv__close_nocheckstdio(fd))
+    abort();
+
+  return rc;
+}
+
+
+uint64_t uv_get_constrained_memory(void) {
+  /*
+   * This might return 0 if there was a problem getting the memory limit from
+   * cgroups. This is OK because a return value of 0 signifies that the memory
+   * limit is unknown.
+   */
+  return uv__read_cgroups_uint64("memory", "memory.limit_in_bytes");
+}
diff --git a/src/unix/linux-syscalls.c b/src/unix/linux-syscalls.c
index bfd7544..5637cf9 100644
--- a/src/unix/linux-syscalls.c
+++ b/src/unix/linux-syscalls.c
@@ -187,6 +187,21 @@
 # endif
 #endif /* __NR_pwritev */
 
+#ifndef __NR_statx
+# if defined(__x86_64__)
+#  define __NR_statx 332
+# elif defined(__i386__)
+#  define __NR_statx 383
+# elif defined(__aarch64__)
+#  define __NR_statx 397
+# elif defined(__arm__)
+#  define __NR_statx (UV_SYSCALL_BASE + 397)
+# elif defined(__ppc__)
+#  define __NR_statx 383
+# elif defined(__s390__)
+#  define __NR_statx 379
+# endif
+#endif /* __NR_statx */
 
 int uv__accept4(int fd, struct sockaddr* addr, socklen_t* addrlen, int flags) {
 #if defined(__i386__)
@@ -336,3 +351,19 @@ int uv__dup3(int oldfd, int newfd, int flags) {
   return errno = ENOSYS, -1;
 #endif
 }
+
+
+int uv__statx(int dirfd,
+              const char* path,
+              int flags,
+              unsigned int mask,
+              struct uv__statx* statxbuf) {
+  /* __NR_statx make Android box killed by SIGSYS.
+   * That looks like a seccomp2 sandbox filter rejecting the system call.
+   */
+#if defined(__NR_statx) && !defined(__ANDROID__)
+  return syscall(__NR_statx, dirfd, path, flags, mask, statxbuf);
+#else
+  return errno = ENOSYS, -1;
+#endif
+}
diff --git a/src/unix/linux-syscalls.h b/src/unix/linux-syscalls.h
index 3dfd329..7e58bfa 100644
--- a/src/unix/linux-syscalls.h
+++ b/src/unix/linux-syscalls.h
@@ -80,6 +80,36 @@
 #define UV__IN_DELETE_SELF    0x400
 #define UV__IN_MOVE_SELF      0x800
 
+struct uv__statx_timestamp {
+  int64_t tv_sec;
+  uint32_t tv_nsec;
+  int32_t unused0;
+};
+
+struct uv__statx {
+  uint32_t stx_mask;
+  uint32_t stx_blksize;
+  uint64_t stx_attributes;
+  uint32_t stx_nlink;
+  uint32_t stx_uid;
+  uint32_t stx_gid;
+  uint16_t stx_mode;
+  uint16_t unused0;
+  uint64_t stx_ino;
+  uint64_t stx_size;
+  uint64_t stx_blocks;
+  uint64_t stx_attributes_mask;
+  struct uv__statx_timestamp stx_atime;
+  struct uv__statx_timestamp stx_btime;
+  struct uv__statx_timestamp stx_ctime;
+  struct uv__statx_timestamp stx_mtime;
+  uint32_t stx_rdev_major;
+  uint32_t stx_rdev_minor;
+  uint32_t stx_dev_major;
+  uint32_t stx_dev_minor;
+  uint64_t unused1[14];
+};
+
 struct uv__inotify_event {
   int32_t wd;
   uint32_t mask;
@@ -113,5 +143,10 @@ int uv__sendmmsg(int fd,
 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);
+int uv__statx(int dirfd,
+              const char* path,
+              int flags,
+              unsigned int mask,
+              struct uv__statx* statxbuf);
 
 #endif /* UV_LINUX_SYSCALL_H_ */
diff --git a/src/unix/netbsd.c b/src/unix/netbsd.c
index a2a4e52..c649bb3 100644
--- a/src/unix/netbsd.c
+++ b/src/unix/netbsd.c
@@ -126,6 +126,11 @@ uint64_t uv_get_total_memory(void) {
 }
 
 
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
 int uv_resident_set_memory(size_t* rss) {
   kvm_t *kd = NULL;
   struct kinfo_proc2 *kinfo = NULL;
diff --git a/src/unix/openbsd.c b/src/unix/openbsd.c
index bffb58b..ffae768 100644
--- a/src/unix/openbsd.c
+++ b/src/unix/openbsd.c
@@ -136,6 +136,11 @@ uint64_t uv_get_total_memory(void) {
 }
 
 
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
 int uv_resident_set_memory(size_t* rss) {
   struct kinfo_proc kinfo;
   size_t page_size = getpagesize();
diff --git a/src/unix/os390.c b/src/unix/os390.c
index dc146e3..273ded7 100644
--- a/src/unix/os390.c
+++ b/src/unix/os390.c
@@ -356,6 +356,11 @@ uint64_t uv_get_total_memory(void) {
 }
 
 
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
 int uv_resident_set_memory(size_t* rss) {
   char* ascb;
   char* rax;
@@ -657,6 +662,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
   uintptr_t nfds;
 
   assert(loop->watchers != NULL);
+  assert(fd >= 0);
 
   events = (struct epoll_event*) loop->watchers[loop->nwatchers];
   nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
diff --git a/src/unix/pipe.c b/src/unix/pipe.c
index d3b554c..8347668 100644
--- a/src/unix/pipe.c
+++ b/src/unix/pipe.c
@@ -215,7 +215,7 @@ void uv_pipe_connect(uv_connect_t* req,
   }
 
   if (err == 0)
-    uv__io_start(handle->loop, &handle->io_watcher, POLLIN | POLLOUT);
+    uv__io_start(handle->loop, &handle->io_watcher, POLLOUT);
 
 out:
   handle->delayed_error = err;
@@ -233,9 +233,6 @@ out:
 }
 
 
-typedef int (*uv__peersockfunc)(int, struct sockaddr*, socklen_t*);
-
-
 static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
                                     uv__peersockfunc func,
                                     char* buffer,
@@ -246,10 +243,13 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
 
   addrlen = sizeof(sa);
   memset(&sa, 0, addrlen);
-  err = func(uv__stream_fd(handle), (struct sockaddr*) &sa, &addrlen);
+  err = uv__getsockpeername((const uv_handle_t*) handle,
+                            func,
+                            (struct sockaddr*) &sa,
+                            (int*) &addrlen);
   if (err < 0) {
     *size = 0;
-    return UV__ERR(errno);
+    return err;
   }
 
 #if defined(__linux__)
diff --git a/src/unix/posix-poll.c b/src/unix/posix-poll.c
index f3181f9..a3b9f21 100644
--- a/src/unix/posix-poll.c
+++ b/src/unix/posix-poll.c
@@ -298,6 +298,8 @@ update_timeout:
 void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
   size_t i;
 
+  assert(fd >= 0);
+
   if (loop->poll_fds_iterating) {
     /* uv__io_poll is currently iterating.  Just invalidate fd.  */
     for (i = 0; i < loop->poll_fds_used; i++)
diff --git a/src/unix/process.c b/src/unix/process.c
index 101c9c5..e29bf15 100644
--- a/src/unix/process.c
+++ b/src/unix/process.c
@@ -315,7 +315,7 @@ static void uv__process_child_init(const uv_process_options_t* options,
         use_fd = open("/dev/null", fd == 0 ? O_RDONLY : O_RDWR);
         close_fd = use_fd;
 
-        if (use_fd == -1) {
+        if (use_fd < 0) {
           uv__write_int(error_fd, UV__ERR(errno));
           _exit(127);
         }
@@ -385,6 +385,11 @@ static void uv__process_child_init(const uv_process_options_t* options,
     if (n == SIGKILL || n == SIGSTOP)
       continue;  /* Can't be changed. */
 
+#if defined(__HAIKU__)
+    if (n == SIGKILLTHR)
+      continue;  /* Can't be changed. */
+#endif
+
     if (SIG_ERR != signal(n, SIG_DFL))
       continue;
 
@@ -431,6 +436,8 @@ int uv_spawn(uv_loop_t* loop,
                               UV_PROCESS_SETGID |
                               UV_PROCESS_SETUID |
                               UV_PROCESS_WINDOWS_HIDE |
+                              UV_PROCESS_WINDOWS_HIDE_CONSOLE |
+                              UV_PROCESS_WINDOWS_HIDE_GUI |
                               UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS)));
 
   uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS);
diff --git a/src/unix/stream.c b/src/unix/stream.c
index a75ba15..17b06a3 100644
--- a/src/unix/stream.c
+++ b/src/unix/stream.c
@@ -745,13 +745,13 @@ static int uv__write_req_update(uv_stream_t* stream,
 
   buf = req->bufs + req->write_index;
 
-  while (n > 0) {
+  do {
     len = n < buf->len ? n : buf->len;
     buf->base += len;
     buf->len -= len;
     buf += (buf->len == 0);  /* Advance to next buffer if this one is empty. */
     n -= len;
-  }
+  } while (n > 0);
 
   req->write_index = buf - req->bufs;
 
@@ -897,7 +897,7 @@ start:
     goto error;
   }
 
-  if (n > 0 && uv__write_req_update(stream, req, n)) {
+  if (n >= 0 && uv__write_req_update(stream, req, n)) {
     uv__write_req_finish(req);
     return;  /* TODO(bnoordhuis) Start trying to write the next request. */
   }
@@ -1541,7 +1541,7 @@ int uv_try_write(uv_stream_t* stream,
   }
 
   if (written == 0 && req_size != 0)
-    return UV_EAGAIN;
+    return req.error < 0 ? req.error : UV_EAGAIN;
   else
     return written;
 }
diff --git a/src/unix/sunos.c b/src/unix/sunos.c
index 2552a01..f323d1d 100644
--- a/src/unix/sunos.c
+++ b/src/unix/sunos.c
@@ -117,6 +117,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) {
   uintptr_t nfds;
 
   assert(loop->watchers != NULL);
+  assert(fd >= 0);
 
   events = (struct port_event*) loop->watchers[loop->nwatchers];
   nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1];
@@ -134,8 +135,10 @@ int uv__io_check_fd(uv_loop_t* loop, int fd) {
   if (port_associate(loop->backend_fd, PORT_SOURCE_FD, fd, POLLIN, 0))
     return UV__ERR(errno);
 
-  if (port_dissociate(loop->backend_fd, PORT_SOURCE_FD, fd))
+  if (port_dissociate(loop->backend_fd, PORT_SOURCE_FD, fd)) {
+    perror("(libuv) port_dissociate()");
     abort();
+  }
 
   return 0;
 }
@@ -173,8 +176,14 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
     w = QUEUE_DATA(q, uv__io_t, watcher_queue);
     assert(w->pevents != 0);
 
-    if (port_associate(loop->backend_fd, PORT_SOURCE_FD, w->fd, w->pevents, 0))
+    if (port_associate(loop->backend_fd,
+                       PORT_SOURCE_FD,
+                       w->fd,
+                       w->pevents,
+                       0)) {
+      perror("(libuv) port_associate()");
       abort();
+    }
 
     w->events = w->pevents;
   }
@@ -218,10 +227,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
       /* Work around another kernel bug: port_getn() may return events even
        * on error.
        */
-      if (errno == EINTR || errno == ETIME)
+      if (errno == EINTR || errno == ETIME) {
         saved_errno = errno;
-      else
+      } else {
+        perror("(libuv) port_getn()");
         abort();
+      }
     }
 
     /* Update loop->time unconditionally. It's tempting to skip the update when
@@ -369,6 +380,11 @@ uint64_t uv_get_total_memory(void) {
 }
 
 
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
 void uv_loadavg(double avg[3]) {
   (void) getloadavg(avg, 3);
 }
diff --git a/src/unix/tcp.c b/src/unix/tcp.c
index 2982851..8cedcd6 100644
--- a/src/unix/tcp.c
+++ b/src/unix/tcp.c
@@ -82,7 +82,7 @@ static int maybe_new_socket(uv_tcp_t* handle, int domain, unsigned long flags) {
         handle->flags |= flags;
         return 0;
       }
-      
+
       /* Query to see if tcp socket is bound. */
       slen = sizeof(saddr);
       memset(&saddr, 0, sizeof(saddr));
@@ -235,12 +235,16 @@ int uv__tcp_connect(uv_connect_t* req,
   if (r == -1 && errno != 0) {
     if (errno == EINPROGRESS)
       ; /* not an error */
-    else if (errno == ECONNREFUSED)
-    /* If we get a ECONNREFUSED wait until the next tick to report the
-     * error. Solaris wants to report immediately--other unixes want to
-     * wait.
+    else if (errno == ECONNREFUSED
+#if defined(__OpenBSD__)
+      || errno == EINVAL
+#endif
+      )
+    /* If we get ECONNREFUSED (Solaris) or EINVAL (OpenBSD) wait until the
+     * next tick to report the error. Solaris and OpenBSD wants to report
+     * immediately -- other unixes want to wait.
      */
-      handle->delayed_error = UV__ERR(errno);
+      handle->delayed_error = UV__ERR(ECONNREFUSED);
     else
       return UV__ERR(errno);
   }
@@ -279,44 +283,28 @@ int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) {
 int uv_tcp_getsockname(const uv_tcp_t* handle,
                        struct sockaddr* name,
                        int* namelen) {
-  socklen_t socklen;
 
   if (handle->delayed_error)
     return handle->delayed_error;
 
-  if (uv__stream_fd(handle) < 0)
-    return UV_EINVAL;  /* FIXME(bnoordhuis) UV_EBADF */
-
-  /* sizeof(socklen_t) != sizeof(int) on some systems. */
-  socklen = (socklen_t) *namelen;
-
-  if (getsockname(uv__stream_fd(handle), name, &socklen))
-    return UV__ERR(errno);
-
-  *namelen = (int) socklen;
-  return 0;
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getsockname,
+                             name,
+                             namelen);
 }
 
 
 int uv_tcp_getpeername(const uv_tcp_t* handle,
                        struct sockaddr* name,
                        int* namelen) {
-  socklen_t socklen;
 
   if (handle->delayed_error)
     return handle->delayed_error;
 
-  if (uv__stream_fd(handle) < 0)
-    return UV_EINVAL;  /* FIXME(bnoordhuis) UV_EBADF */
-
-  /* sizeof(socklen_t) != sizeof(int) on some systems. */
-  socklen = (socklen_t) *namelen;
-
-  if (getpeername(uv__stream_fd(handle), name, &socklen))
-    return UV__ERR(errno);
-
-  *namelen = (int) socklen;
-  return 0;
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getpeername,
+                             name,
+                             namelen);
 }
 
 
diff --git a/src/unix/thread.c b/src/unix/thread.c
index 2900470..cd0b7aa 100644
--- a/src/unix/thread.c
+++ b/src/unix/thread.c
@@ -48,8 +48,10 @@
 STATIC_ASSERT(sizeof(uv_barrier_t) == sizeof(pthread_barrier_t));
 #endif
 
-/* Note: guard clauses should match uv_barrier_t's in include/uv/uv-unix.h. */
-#if defined(_AIX) || !defined(PTHREAD_BARRIER_SERIAL_THREAD)
+/* Note: guard clauses should match uv_barrier_t's in include/uv/unix.h. */
+#if defined(_AIX) || \
+    defined(__OpenBSD__) || \
+    !defined(PTHREAD_BARRIER_SERIAL_THREAD)
 int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) {
   struct _uv_barrier* b;
   int rc;
@@ -176,8 +178,21 @@ static size_t thread_stack_size(void) {
   if (lim.rlim_cur != RLIM_INFINITY) {
     /* pthread_attr_setstacksize() expects page-aligned values. */
     lim.rlim_cur -= lim.rlim_cur % (rlim_t) getpagesize();
-    if (lim.rlim_cur >= PTHREAD_STACK_MIN)
-      return lim.rlim_cur;
+
+    /* Musl's PTHREAD_STACK_MIN is 2 KB on all architectures, which is
+     * too small to safely receive signals on.
+     *
+     * Musl's PTHREAD_STACK_MIN + MINSIGSTKSZ == 8192 on arm64 (which has
+     * the largest MINSIGSTKSZ of the architectures that musl supports) so
+     * let's use that as a lower bound.
+     *
+     * We use a hardcoded value because PTHREAD_STACK_MIN + MINSIGSTKSZ
+     * is between 28 and 133 KB when compiling against glibc, depending
+     * on the architecture.
+     */
+    if (lim.rlim_cur >= 8192)
+      if (lim.rlim_cur >= PTHREAD_STACK_MIN)
+        return lim.rlim_cur;
   }
 #endif
 
@@ -192,13 +207,36 @@ static size_t thread_stack_size(void) {
 
 
 int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
+  uv_thread_options_t params;
+  params.flags = UV_THREAD_NO_FLAGS;
+  return uv_thread_create_ex(tid, &params, entry, arg);
+}
+
+int uv_thread_create_ex(uv_thread_t* tid,
+                        const uv_thread_options_t* params,
+                        void (*entry)(void *arg),
+                        void *arg) {
   int err;
-  size_t stack_size;
   pthread_attr_t* attr;
   pthread_attr_t attr_storage;
+  size_t pagesize;
+  size_t stack_size;
+
+  stack_size =
+      params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0;
 
   attr = NULL;
-  stack_size = thread_stack_size();
+  if (stack_size == 0) {
+    stack_size = thread_stack_size();
+  } else {
+    pagesize = (size_t)getpagesize();
+    /* Round up to the nearest page boundary. */
+    stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1);
+#ifdef PTHREAD_STACK_MIN
+    if (stack_size < PTHREAD_STACK_MIN)
+      stack_size = PTHREAD_STACK_MIN;
+#endif
+  }
 
   if (stack_size > 0) {
     attr = &attr_storage;
@@ -778,7 +816,9 @@ int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) {
     return UV_ETIMEDOUT;
 
   abort();
+#ifndef __SUNPRO_C
   return UV_EINVAL;  /* Satisfy the compiler. */
+#endif
 }
 
 
diff --git a/src/unix/udp.c b/src/unix/udp.c
index ec337ec..b578e7b 100644
--- a/src/unix/udp.c
+++ b/src/unix/udp.c
@@ -30,6 +30,7 @@
 #if defined(__MVS__)
 #include <xti.h>
 #endif
+#include <sys/un.h>
 
 #if defined(IPV6_JOIN_GROUP) && !defined(IPV6_ADD_MEMBERSHIP)
 # define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
@@ -227,9 +228,22 @@ static void uv__udp_sendmsg(uv_udp_t* handle) {
     assert(req != NULL);
 
     memset(&h, 0, sizeof h);
-    h.msg_name = &req->addr;
-    h.msg_namelen = (req->addr.ss_family == AF_INET6 ?
-      sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
+    if (req->addr.ss_family == AF_UNSPEC) {
+      h.msg_name = NULL;
+      h.msg_namelen = 0;
+    } else {
+      h.msg_name = &req->addr;
+      if (req->addr.ss_family == AF_INET6)
+        h.msg_namelen = sizeof(struct sockaddr_in6);
+      else if (req->addr.ss_family == AF_INET)
+        h.msg_namelen = sizeof(struct sockaddr_in);
+      else if (req->addr.ss_family == AF_UNIX)
+        h.msg_namelen = sizeof(struct sockaddr_un);
+      else {
+        assert(0 && "unsupported address family");
+        abort();
+      }
+    }
     h.msg_iov = (struct iovec*) req->bufs;
     h.msg_iovlen = req->nbufs;
 
@@ -263,16 +277,30 @@ static void uv__udp_sendmsg(uv_udp_t* handle) {
  * are different from the BSDs: it _shares_ the port rather than steal it
  * from the current listener.  While useful, it's not something we can emulate
  * on other platforms so we don't enable it.
+ *
+ * zOS does not support getsockname with SO_REUSEPORT option when using
+ * AF_UNIX.
  */
 static int uv__set_reuse(int fd) {
   int yes;
-
-#if defined(SO_REUSEPORT) && !defined(__linux__)
   yes = 1;
+
+#if defined(SO_REUSEPORT) && defined(__MVS__)
+  struct sockaddr_in sockfd;
+  unsigned int sockfd_len = sizeof(sockfd);
+  if (getsockname(fd, (struct sockaddr*) &sockfd, &sockfd_len) == -1)
+      return UV__ERR(errno);
+  if (sockfd.sin_family == AF_UNIX) {
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)))
+      return UV__ERR(errno);
+  } else {
+    if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
+       return UV__ERR(errno);
+  }
+#elif defined(SO_REUSEPORT) && !defined(__linux__)
   if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)))
     return UV__ERR(errno);
 #else
-  yes = 1;
   if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)))
     return UV__ERR(errno);
 #endif
@@ -383,6 +411,50 @@ static int uv__udp_maybe_deferred_bind(uv_udp_t* handle,
 }
 
 
+int uv__udp_connect(uv_udp_t* handle,
+                    const struct sockaddr* addr,
+                    unsigned int addrlen) {
+  int err;
+
+  err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
+  if (err)
+    return err;
+
+  do {
+    errno = 0;
+    err = connect(handle->io_watcher.fd, addr, addrlen);
+  } while (err == -1 && errno == EINTR);
+
+  if (err)
+    return UV__ERR(errno);
+
+  handle->flags |= UV_HANDLE_UDP_CONNECTED;
+
+  return 0;
+}
+
+
+int uv__udp_disconnect(uv_udp_t* handle) {
+    int r;
+    struct sockaddr addr;
+
+    memset(&addr, 0, sizeof(addr));
+
+    addr.sa_family = AF_UNSPEC;
+
+    do {
+      errno = 0;
+      r = connect(handle->io_watcher.fd, &addr, sizeof(addr));
+    } while (r == -1 && errno == EINTR);
+
+    if (r == -1 && errno != EAFNOSUPPORT)
+      return UV__ERR(errno);
+
+    handle->flags &= ~UV_HANDLE_UDP_CONNECTED;
+    return 0;
+}
+
+
 int uv__udp_send(uv_udp_send_t* req,
                  uv_udp_t* handle,
                  const uv_buf_t bufs[],
@@ -395,9 +467,11 @@ int uv__udp_send(uv_udp_send_t* req,
 
   assert(nbufs > 0);
 
-  err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
-  if (err)
-    return err;
+  if (addr) {
+    err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
+    if (err)
+      return err;
+  }
 
   /* It's legal for send_queue_count > 0 even when the write_queue is empty;
    * it means there are error-state requests in the write_completed_queue that
@@ -407,7 +481,10 @@ int uv__udp_send(uv_udp_send_t* req,
 
   uv__req_init(handle->loop, req, UV_UDP_SEND);
   assert(addrlen <= sizeof(req->addr));
-  memcpy(&req->addr, addr, addrlen);
+  if (addr == NULL)
+    req->addr.ss_family = AF_UNSPEC;
+  else
+    memcpy(&req->addr, addr, addrlen);
   req->send_cb = send_cb;
   req->handle = handle;
   req->nbufs = nbufs;
@@ -459,9 +536,13 @@ int uv__udp_try_send(uv_udp_t* handle,
   if (handle->send_queue_count != 0)
     return UV_EAGAIN;
 
-  err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
-  if (err)
-    return err;
+  if (addr) {
+    err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0);
+    if (err)
+      return err;
+  } else {
+    assert(handle->flags & UV_HANDLE_UDP_CONNECTED);
+  }
 
   memset(&h, 0, sizeof h);
   h.msg_name = (struct sockaddr*) addr;
@@ -608,6 +689,7 @@ int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) {
   uv__io_init(&handle->io_watcher, uv__udp_io, fd);
   QUEUE_INIT(&handle->write_queue);
   QUEUE_INIT(&handle->write_completed_queue);
+
   return 0;
 }
 
@@ -636,6 +718,9 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
     return err;
 
   handle->io_watcher.fd = sock;
+  if (uv__udp_is_connected(handle))
+    handle->flags |= UV_HANDLE_UDP_CONNECTED;
+
   return 0;
 }
 
@@ -743,13 +828,17 @@ int uv_udp_set_ttl(uv_udp_t* handle, int ttl) {
                         IPV6_UNICAST_HOPS,
                         &ttl,
                         sizeof(ttl));
-#endif /* defined(__sun) || defined(_AIX) || defined (__OpenBSD__) ||
-          defined(__MVS__) */
+
+#else /* !(defined(__sun) || defined(_AIX) || defined (__OpenBSD__) ||
+           defined(__MVS__)) */
 
   return uv__setsockopt_maybe_char(handle,
                                    IP_TTL,
                                    IPV6_UNICAST_HOPS,
                                    ttl);
+
+#endif /* defined(__sun) || defined(_AIX) || defined (__OpenBSD__) ||
+          defined(__MVS__) */
 }
 
 
@@ -851,23 +940,24 @@ int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr)
   return 0;
 }
 
-
-int uv_udp_getsockname(const uv_udp_t* handle,
+int uv_udp_getpeername(const uv_udp_t* handle,
                        struct sockaddr* name,
                        int* namelen) {
-  socklen_t socklen;
-
-  if (handle->io_watcher.fd == -1)
-    return UV_EINVAL;  /* FIXME(bnoordhuis) UV_EBADF */
 
-  /* sizeof(socklen_t) != sizeof(int) on some systems. */
-  socklen = (socklen_t) *namelen;
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getpeername,
+                             name,
+                             namelen);
+}
 
-  if (getsockname(handle->io_watcher.fd, name, &socklen))
-    return UV__ERR(errno);
+int uv_udp_getsockname(const uv_udp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
 
-  *namelen = (int) socklen;
-  return 0;
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getsockname,
+                             name,
+                             namelen);
 }
 
 
diff --git a/src/uv-common.c b/src/uv-common.c
index 952bde0..066eb31 100644
--- a/src/uv-common.c
+++ b/src/uv-common.c
@@ -34,6 +34,7 @@
 # include <malloc.h> /* malloc */
 #else
 # include <net/if.h> /* if_nametoindex */
+# include <sys/un.h> /* AF_UNIX, sockaddr_un */
 #endif
 
 
@@ -222,6 +223,9 @@ int uv_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr) {
   memset(addr, 0, sizeof(*addr));
   addr->sin6_family = AF_INET6;
   addr->sin6_port = htons(port);
+#ifdef SIN6_LEN
+  addr->sin6_len = sizeof(*addr);
+#endif
 
   zone_index = strchr(ip, '%');
   if (zone_index != NULL) {
@@ -314,17 +318,20 @@ int uv_tcp_connect(uv_connect_t* req,
 }
 
 
-int uv_udp_send(uv_udp_send_t* req,
-                uv_udp_t* handle,
-                const uv_buf_t bufs[],
-                unsigned int nbufs,
-                const struct sockaddr* addr,
-                uv_udp_send_cb send_cb) {
+int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr) {
   unsigned int addrlen;
 
   if (handle->type != UV_UDP)
     return UV_EINVAL;
 
+  /* Disconnect the handle */
+  if (addr == NULL) {
+    if (!(handle->flags & UV_HANDLE_UDP_CONNECTED))
+      return UV_ENOTCONN;
+
+    return uv__udp_disconnect(handle);
+  }
+
   if (addr->sa_family == AF_INET)
     addrlen = sizeof(struct sockaddr_in);
   else if (addr->sa_family == AF_INET6)
@@ -332,6 +339,70 @@ int uv_udp_send(uv_udp_send_t* req,
   else
     return UV_EINVAL;
 
+  if (handle->flags & UV_HANDLE_UDP_CONNECTED)
+    return UV_EISCONN;
+
+  return uv__udp_connect(handle, addr, addrlen);
+}
+
+
+int uv__udp_is_connected(uv_udp_t* handle) {
+  struct sockaddr_storage addr;
+  int addrlen;
+  if (handle->type != UV_UDP)
+    return 0;
+
+  addrlen = sizeof(addr);
+  if (uv_udp_getpeername(handle, (struct sockaddr*) &addr, &addrlen) != 0)
+    return 0;
+
+  return addrlen > 0;
+}
+
+
+int uv__udp_check_before_send(uv_udp_t* handle, const struct sockaddr* addr) {
+  unsigned int addrlen;
+
+  if (handle->type != UV_UDP)
+    return UV_EINVAL;
+
+  if (addr != NULL && (handle->flags & UV_HANDLE_UDP_CONNECTED))
+    return UV_EISCONN;
+
+  if (addr == NULL && !(handle->flags & UV_HANDLE_UDP_CONNECTED))
+    return UV_EDESTADDRREQ;
+
+  if (addr != NULL) {
+    if (addr->sa_family == AF_INET)
+      addrlen = sizeof(struct sockaddr_in);
+    else if (addr->sa_family == AF_INET6)
+      addrlen = sizeof(struct sockaddr_in6);
+#if defined(AF_UNIX) && !defined(_WIN32)
+    else if (addr->sa_family == AF_UNIX)
+      addrlen = sizeof(struct sockaddr_un);
+#endif
+    else
+      return UV_EINVAL;
+  } else {
+    addrlen = 0;
+  }
+
+  return addrlen;
+}
+
+
+int uv_udp_send(uv_udp_send_t* req,
+                uv_udp_t* handle,
+                const uv_buf_t bufs[],
+                unsigned int nbufs,
+                const struct sockaddr* addr,
+                uv_udp_send_cb send_cb) {
+  int addrlen;
+
+  addrlen = uv__udp_check_before_send(handle, addr);
+  if (addrlen < 0)
+    return addrlen;
+
   return uv__udp_send(req, handle, bufs, nbufs, addr, addrlen, send_cb);
 }
 
@@ -340,17 +411,11 @@ int uv_udp_try_send(uv_udp_t* handle,
                     const uv_buf_t bufs[],
                     unsigned int nbufs,
                     const struct sockaddr* addr) {
-  unsigned int addrlen;
-
-  if (handle->type != UV_UDP)
-    return UV_EINVAL;
+  int addrlen;
 
-  if (addr->sa_family == AF_INET)
-    addrlen = sizeof(struct sockaddr_in);
-  else if (addr->sa_family == AF_INET6)
-    addrlen = sizeof(struct sockaddr_in6);
-  else
-    return UV_EINVAL;
+  addrlen = uv__udp_check_before_send(handle, addr);
+  if (addrlen < 0)
+    return addrlen;
 
   return uv__udp_try_send(handle, bufs, nbufs, addr, addrlen);
 }
@@ -571,37 +636,66 @@ int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent) {
   dent = dents[(*nbufs)++];
 
   ent->name = dent->d_name;
+  ent->type = uv__fs_get_dirent_type(dent);
+
+  return 0;
+}
+
+uv_dirent_type_t uv__fs_get_dirent_type(uv__dirent_t* dent) {
+  uv_dirent_type_t type;
+
 #ifdef HAVE_DIRENT_TYPES
   switch (dent->d_type) {
     case UV__DT_DIR:
-      ent->type = UV_DIRENT_DIR;
+      type = UV_DIRENT_DIR;
       break;
     case UV__DT_FILE:
-      ent->type = UV_DIRENT_FILE;
+      type = UV_DIRENT_FILE;
       break;
     case UV__DT_LINK:
-      ent->type = UV_DIRENT_LINK;
+      type = UV_DIRENT_LINK;
       break;
     case UV__DT_FIFO:
-      ent->type = UV_DIRENT_FIFO;
+      type = UV_DIRENT_FIFO;
       break;
     case UV__DT_SOCKET:
-      ent->type = UV_DIRENT_SOCKET;
+      type = UV_DIRENT_SOCKET;
       break;
     case UV__DT_CHAR:
-      ent->type = UV_DIRENT_CHAR;
+      type = UV_DIRENT_CHAR;
       break;
     case UV__DT_BLOCK:
-      ent->type = UV_DIRENT_BLOCK;
+      type = UV_DIRENT_BLOCK;
       break;
     default:
-      ent->type = UV_DIRENT_UNKNOWN;
+      type = UV_DIRENT_UNKNOWN;
   }
 #else
-  ent->type = UV_DIRENT_UNKNOWN;
+  type = UV_DIRENT_UNKNOWN;
 #endif
 
-  return 0;
+  return type;
+}
+
+void uv__fs_readdir_cleanup(uv_fs_t* req) {
+  uv_dir_t* dir;
+  uv_dirent_t* dirents;
+  int i;
+
+  if (req->ptr == NULL)
+    return;
+
+  dir = req->ptr;
+  dirents = dir->dirents;
+  req->ptr = NULL;
+
+  if (dirents == NULL)
+    return;
+
+  for (i = 0; i < req->result; ++i) {
+    uv__free((char*) dirents[i].name);
+    dirents[i].name = NULL;
+  }
 }
 
 
diff --git a/src/uv-common.h b/src/uv-common.h
index 15ac4d0..f788161 100644
--- a/src/uv-common.h
+++ b/src/uv-common.h
@@ -103,6 +103,7 @@ enum {
 
   /* Only used by uv_udp_t handles. */
   UV_HANDLE_UDP_PROCESSING              = 0x01000000,
+  UV_HANDLE_UDP_CONNECTED               = 0x02000000,
 
   /* Only used by uv_pipe_t handles. */
   UV_HANDLE_NON_OVERLAPPED_PIPE         = 0x01000000,
@@ -142,6 +143,14 @@ int uv__udp_bind(uv_udp_t* handle,
                  unsigned int  addrlen,
                  unsigned int flags);
 
+int uv__udp_connect(uv_udp_t* handle,
+                    const struct sockaddr* addr,
+                    unsigned int addrlen);
+
+int uv__udp_disconnect(uv_udp_t* handle);
+
+int uv__udp_is_connected(uv_udp_t* handle);
+
 int uv__udp_send(uv_udp_send_t* req,
                  uv_udp_t* handle,
                  const uv_buf_t bufs[],
@@ -184,6 +193,8 @@ size_t uv__count_bufs(const uv_buf_t bufs[], unsigned int nbufs);
 int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value);
 
 void uv__fs_scandir_cleanup(uv_fs_t* req);
+void uv__fs_readdir_cleanup(uv_fs_t* req);
+uv_dirent_type_t uv__fs_get_dirent_type(uv__dirent_t* dent);
 
 int uv__next_timeout(const uv_loop_t* loop);
 void uv__run_timers(uv_loop_t* loop);
diff --git a/src/uv-data-getter-setters.c b/src/uv-data-getter-setters.c
index b7fcd4a..c302566 100644
--- a/src/uv-data-getter-setters.c
+++ b/src/uv-data-getter-setters.c
@@ -36,7 +36,7 @@ const char* uv_req_type_name(uv_req_type type) {
   case UV_REQ_TYPE_MAX:
   case UV_UNKNOWN_REQ:
   default: /* UV_REQ_TYPE_PRIVATE */
-     return NULL;
+    break;
   }
   return NULL;
 }
diff --git a/src/win/core.c b/src/win/core.c
index bf80d77..e9d0a58 100644
--- a/src/win/core.c
+++ b/src/win/core.c
@@ -623,3 +623,30 @@ int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value) {
 
   return 0;
 }
+
+int uv_cpumask_size(void) {
+  return (int)(sizeof(DWORD_PTR) * 8);
+}
+
+int uv__getsockpeername(const uv_handle_t* handle,
+                        uv__peersockfunc func,
+                        struct sockaddr* name,
+                        int* namelen,
+                        int delayed_error) {
+
+  int result;
+  uv_os_fd_t fd;
+
+  result = uv_fileno(handle, &fd);
+  if (result != 0)
+    return result;
+
+  if (delayed_error)
+    return uv_translate_sys_error(delayed_error);
+
+  result = func((SOCKET) fd, name, namelen);
+  if (result != 0)
+    return uv_translate_sys_error(WSAGetLastError());
+
+  return 0;
+}
diff --git a/src/win/fs.c b/src/win/fs.c
index 65d936b..9e2f084 100644
--- a/src/win/fs.c
+++ b/src/win/fs.c
@@ -1125,6 +1125,137 @@ cleanup:
     uv__free(dirents);
 }
 
+void fs__opendir(uv_fs_t* req) {
+  WCHAR* pathw;
+  size_t len;
+  const WCHAR* fmt;
+  WCHAR* find_path;
+  uv_dir_t* dir;
+
+  pathw = req->file.pathw;
+  dir = NULL;
+  find_path = NULL;
+
+  /* Figure out whether path is a file or a directory. */
+  if (!(GetFileAttributesW(pathw) & FILE_ATTRIBUTE_DIRECTORY)) {
+    SET_REQ_UV_ERROR(req, UV_ENOTDIR, ERROR_DIRECTORY);
+    goto error;
+  }
+
+  dir = uv__malloc(sizeof(*dir));
+  if (dir == NULL) {
+    SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+    goto error;
+  }
+
+  len = wcslen(pathw);
+
+  if (len == 0)
+    fmt = L"./*";
+  else if (IS_SLASH(pathw[len - 1]))
+    fmt = L"%s*";
+  else
+    fmt = L"%s\\*";
+
+  find_path = uv__malloc(sizeof(WCHAR) * (len + 4));
+  if (find_path == NULL) {
+    SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY);
+    goto error;
+  }
+
+  _snwprintf(find_path, len + 3, fmt, pathw);
+  dir->dir_handle = FindFirstFileW(find_path, &dir->find_data);
+  uv__free(find_path);
+  find_path = NULL;
+  if (dir->dir_handle == INVALID_HANDLE_VALUE &&
+      GetLastError() != ERROR_FILE_NOT_FOUND) {
+    SET_REQ_WIN32_ERROR(req, GetLastError());
+    goto error;
+  }
+
+  dir->need_find_call = FALSE;
+  req->ptr = dir;
+  SET_REQ_RESULT(req, 0);
+  return;
+
+error:
+  uv__free(dir);
+  uv__free(find_path);
+  req->ptr = NULL;
+}
+
+void fs__readdir(uv_fs_t* req) {
+  uv_dir_t* dir;
+  uv_dirent_t* dirents;
+  uv__dirent_t dent;
+  unsigned int dirent_idx;
+  PWIN32_FIND_DATAW find_data;
+  unsigned int i;
+  int r;
+
+  req->flags |= UV_FS_FREE_PTR;
+  dir = req->ptr;
+  dirents = dir->dirents;
+  memset(dirents, 0, dir->nentries * sizeof(*dir->dirents));
+  find_data = &dir->find_data;
+  dirent_idx = 0;
+
+  while (dirent_idx < dir->nentries) {
+    if (dir->need_find_call && FindNextFileW(dir->dir_handle, find_data) == 0) {
+      if (GetLastError() == ERROR_NO_MORE_FILES)
+        break;
+      goto error;
+    }
+
+    /* Skip "." and ".." entries. */
+    if (find_data->cFileName[0] == L'.' &&
+        (find_data->cFileName[1] == L'\0' ||
+        (find_data->cFileName[1] == L'.' &&
+        find_data->cFileName[2] == L'\0'))) {
+      dir->need_find_call = TRUE;
+      continue;
+    }
+
+    r = uv__convert_utf16_to_utf8((const WCHAR*) &find_data->cFileName,
+                                  -1,
+                                  (char**) &dirents[dirent_idx].name);
+    if (r != 0)
+      goto error;
+
+    /* Copy file type. */
+    if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
+      dent.d_type = UV__DT_DIR;
+    else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0)
+      dent.d_type = UV__DT_LINK;
+    else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0)
+      dent.d_type = UV__DT_CHAR;
+    else
+      dent.d_type = UV__DT_FILE;
+
+    dirents[dirent_idx].type = uv__fs_get_dirent_type(&dent);
+    dir->need_find_call = TRUE;
+    ++dirent_idx;
+  }
+
+  SET_REQ_RESULT(req, dirent_idx);
+  return;
+
+error:
+  SET_REQ_WIN32_ERROR(req, GetLastError());
+  for (i = 0; i < dirent_idx; ++i) {
+    uv__free((char*) dirents[i].name);
+    dirents[i].name = NULL;
+  }
+}
+
+void fs__closedir(uv_fs_t* req) {
+  uv_dir_t* dir;
+
+  dir = req->ptr;
+  FindClose(dir->dir_handle);
+  uv__free(req->ptr);
+  SET_REQ_RESULT(req, 0);
+}
 
 INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
     int do_lstat) {
@@ -2039,6 +2170,9 @@ static void uv__fs_work(struct uv__work* w) {
     XX(MKDTEMP, mkdtemp)
     XX(RENAME, rename)
     XX(SCANDIR, scandir)
+    XX(READDIR, readdir)
+    XX(OPENDIR, opendir)
+    XX(CLOSEDIR, closedir)
     XX(LINK, link)
     XX(SYMLINK, symlink)
     XX(READLINK, readlink)
@@ -2080,6 +2214,8 @@ void uv_fs_req_cleanup(uv_fs_t* req) {
   if (req->flags & UV_FS_FREE_PTR) {
     if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL)
       uv__fs_scandir_cleanup(req);
+    else if (req->fs_type == UV_FS_READDIR)
+      uv__fs_readdir_cleanup(req);
     else
       uv__free(req->ptr);
   }
@@ -2247,6 +2383,45 @@ int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags,
   POST;
 }
 
+int uv_fs_opendir(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  const char* path,
+                  uv_fs_cb cb) {
+  int err;
+
+  INIT(UV_FS_OPENDIR);
+  err = fs__capture_path(req, path, NULL, cb != NULL);
+  if (err)
+    return uv_translate_sys_error(err);
+  POST;
+}
+
+int uv_fs_readdir(uv_loop_t* loop,
+                  uv_fs_t* req,
+                  uv_dir_t* dir,
+                  uv_fs_cb cb) {
+  INIT(UV_FS_READDIR);
+
+  if (dir == NULL ||
+      dir->dirents == NULL ||
+      dir->dir_handle == INVALID_HANDLE_VALUE) {
+    return UV_EINVAL;
+  }
+
+  req->ptr = dir;
+  POST;
+}
+
+int uv_fs_closedir(uv_loop_t* loop,
+                   uv_fs_t* req,
+                   uv_dir_t* dir,
+                   uv_fs_cb cb) {
+  INIT(UV_FS_CLOSEDIR);
+  if (dir == NULL)
+    return UV_EINVAL;
+  req->ptr = dir;
+  POST;
+}
 
 int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path,
     const char* new_path, uv_fs_cb cb) {
diff --git a/src/win/handle.c b/src/win/handle.c
index 9d76c3f..61e4df6 100644
--- a/src/win/handle.c
+++ b/src/win/handle.c
@@ -139,7 +139,6 @@ void uv_close(uv_handle_t* handle, uv_close_cb cb) {
     case UV_FS_POLL:
       uv__fs_poll_close((uv_fs_poll_t*) handle);
       uv__handle_closing(handle);
-      uv_want_endgame(loop, handle);
       return;
 
     default:
diff --git a/src/win/internal.h b/src/win/internal.h
index 634b9f7..70ddaa5 100644
--- a/src/win/internal.h
+++ b/src/win/internal.h
@@ -272,6 +272,14 @@ int uv__getpwuid_r(uv_passwd_t* pwd);
 int uv__convert_utf16_to_utf8(const WCHAR* utf16, int utf16len, char** utf8);
 int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16);
 
+typedef int (WINAPI *uv__peersockfunc)(SOCKET, struct sockaddr*, int*);
+
+int uv__getsockpeername(const uv_handle_t* handle,
+                        uv__peersockfunc func,
+                        struct sockaddr* name,
+                        int* namelen,
+                        int delayed_error);
+
 
 /*
  * Process stdio handles.
diff --git a/src/win/tcp.c b/src/win/tcp.c
index 3ce5548..f2cb527 100644
--- a/src/win/tcp.c
+++ b/src/win/tcp.c
@@ -809,44 +809,24 @@ static int uv_tcp_try_connect(uv_connect_t* req,
 int uv_tcp_getsockname(const uv_tcp_t* handle,
                        struct sockaddr* name,
                        int* namelen) {
-  int result;
-
-  if (handle->socket == INVALID_SOCKET) {
-    return UV_EINVAL;
-  }
-
-  if (handle->delayed_error) {
-    return uv_translate_sys_error(handle->delayed_error);
-  }
-
-  result = getsockname(handle->socket, name, namelen);
-  if (result != 0) {
-    return uv_translate_sys_error(WSAGetLastError());
-  }
 
-  return 0;
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getsockname,
+                             name,
+                             namelen,
+                             handle->delayed_error);
 }
 
 
 int uv_tcp_getpeername(const uv_tcp_t* handle,
                        struct sockaddr* name,
                        int* namelen) {
-  int result;
-
-  if (handle->socket == INVALID_SOCKET) {
-    return UV_EINVAL;
-  }
-
-  if (handle->delayed_error) {
-    return uv_translate_sys_error(handle->delayed_error);
-  }
-
-  result = getpeername(handle->socket, name, namelen);
-  if (result != 0) {
-    return uv_translate_sys_error(WSAGetLastError());
-  }
 
-  return 0;
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getpeername,
+                             name,
+                             namelen,
+                             handle->delayed_error);
 }
 
 
diff --git a/src/win/thread.c b/src/win/thread.c
index fd4b7c9..89c53ad 100644
--- a/src/win/thread.c
+++ b/src/win/thread.c
@@ -112,9 +112,34 @@ static UINT __stdcall uv__thread_start(void* arg) {
 
 
 int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
+  uv_thread_options_t params;
+  params.flags = UV_THREAD_NO_FLAGS;
+  return uv_thread_create_ex(tid, &params, entry, arg);
+}
+
+int uv_thread_create_ex(uv_thread_t* tid,
+                        const uv_thread_options_t* params,
+                        void (*entry)(void *arg),
+                        void *arg) {
   struct thread_ctx* ctx;
   int err;
   HANDLE thread;
+  SYSTEM_INFO sysinfo;
+  size_t stack_size;
+  size_t pagesize;
+
+  stack_size =
+      params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0;
+
+  if (stack_size != 0) {
+    GetNativeSystemInfo(&sysinfo);
+    pagesize = (size_t)sysinfo.dwPageSize;
+    /* Round up to the nearest page boundary. */
+    stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1);
+
+    if ((unsigned)stack_size != stack_size)
+      return UV_EINVAL;
+  }
 
   ctx = uv__malloc(sizeof(*ctx));
   if (ctx == NULL)
@@ -126,7 +151,7 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
   /* Create the thread in suspended state so we have a chance to pass
    * its own creation handle to it */
   thread = (HANDLE) _beginthreadex(NULL,
-                                   0,
+                                   (unsigned)stack_size,
                                    uv__thread_start,
                                    ctx,
                                    CREATE_SUSPENDED,
diff --git a/src/win/tty.c b/src/win/tty.c
index f38e9a8..a98fe26 100644
--- a/src/win/tty.c
+++ b/src/win/tty.c
@@ -736,8 +736,8 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle,
       /* Ignore keyup events, unless the left alt key was held and a valid
        * unicode character was emitted. */
       if (!KEV.bKeyDown &&
-          KEV.wVirtualKeyCode != VK_MENU &&
-          KEV.uChar.UnicodeChar != 0) {
+          (KEV.wVirtualKeyCode != VK_MENU ||
+           KEV.uChar.UnicodeChar == 0)) {
         continue;
       }
 
diff --git a/src/win/udp.c b/src/win/udp.c
index 37df849..8aeeab3 100644
--- a/src/win/udp.c
+++ b/src/win/udp.c
@@ -36,22 +36,27 @@ const unsigned int uv_active_udp_streams_threshold = 0;
 
 /* A zero-size buffer for use by uv_udp_read */
 static char uv_zero_[] = "";
-
-int uv_udp_getsockname(const uv_udp_t* handle,
+int uv_udp_getpeername(const uv_udp_t* handle,
                        struct sockaddr* name,
                        int* namelen) {
-  int result;
 
-  if (handle->socket == INVALID_SOCKET) {
-    return UV_EINVAL;
-  }
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getpeername,
+                             name,
+                             namelen,
+                             0);
+}
 
-  result = getsockname(handle->socket, name, namelen);
-  if (result != 0) {
-    return uv_translate_sys_error(WSAGetLastError());
-  }
 
-  return 0;
+int uv_udp_getsockname(const uv_udp_t* handle,
+                       struct sockaddr* name,
+                       int* namelen) {
+
+  return uv__getsockpeername((const uv_handle_t*) handle,
+                             getsockname,
+                             name,
+                             namelen,
+                             0);
 }
 
 
@@ -784,6 +789,18 @@ int uv_udp_set_broadcast(uv_udp_t* handle, int value) {
 }
 
 
+int uv__udp_is_bound(uv_udp_t* handle) {
+  struct sockaddr_storage addr;
+  int addrlen;
+
+  addrlen = sizeof(addr);
+  if (uv_udp_getsockname(handle, (struct sockaddr*) &addr, &addrlen) != 0)
+    return 0;
+
+  return addrlen > 0;
+}
+
+
 int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
   WSAPROTOCOL_INFOW protocol_info;
   int opt_len;
@@ -803,7 +820,16 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) {
                           handle,
                           sock,
                           protocol_info.iAddressFamily);
-  return uv_translate_sys_error(err);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  if (uv__udp_is_bound(handle))
+    handle->flags |= UV_HANDLE_BOUND;
+
+  if (uv__udp_is_connected(handle))
+    handle->flags |= UV_HANDLE_UDP_CONNECTED;
+
+  return 0;
 }
 
 
@@ -880,6 +906,50 @@ int uv__udp_bind(uv_udp_t* handle,
 }
 
 
+int uv__udp_connect(uv_udp_t* handle,
+                    const struct sockaddr* addr,
+                    unsigned int addrlen) {
+  const struct sockaddr* bind_addr;
+  int err;
+
+  if (!(handle->flags & UV_HANDLE_BOUND)) {
+    if (addrlen == sizeof(uv_addr_ip4_any_))
+      bind_addr = (const struct sockaddr*) &uv_addr_ip4_any_;
+    else if (addrlen == sizeof(uv_addr_ip6_any_))
+      bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_;
+    else
+      return UV_EINVAL;
+
+    err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0);
+    if (err)
+      return uv_translate_sys_error(err);
+  }
+
+  err = connect(handle->socket, addr, addrlen);
+  if (err)
+    return uv_translate_sys_error(err);
+
+  handle->flags |= UV_HANDLE_UDP_CONNECTED;
+
+  return 0;
+}
+
+
+int uv__udp_disconnect(uv_udp_t* handle) {
+    int err;
+    struct sockaddr addr;
+
+    memset(&addr, 0, sizeof(addr));
+
+    err = connect(handle->socket, &addr, sizeof(addr));
+    if (err)
+      return uv_translate_sys_error(err);
+
+    handle->flags &= ~UV_HANDLE_UDP_CONNECTED;
+    return 0;
+}
+
+
 /* This function is an egress point, i.e. it returns libuv errors rather than
  * system errors.
  */
@@ -900,6 +970,7 @@ int uv__udp_send(uv_udp_send_t* req,
       bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_;
     else
       return UV_EINVAL;
+
     err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0);
     if (err)
       return uv_translate_sys_error(err);
@@ -925,9 +996,11 @@ int uv__udp_try_send(uv_udp_t* handle,
 
   assert(nbufs > 0);
 
-  err = uv__convert_to_localhost_if_unspecified(addr, &converted);
-  if (err)
-    return err;
+  if (addr != NULL) {
+    err = uv__convert_to_localhost_if_unspecified(addr, &converted);
+    if (err)
+      return err;
+  }
 
   /* Already sending a message.*/
   if (handle->send_queue_count != 0)
diff --git a/src/win/util.c b/src/win/util.c
index 9237891..7ca8321 100644
--- a/src/win/util.c
+++ b/src/win/util.c
@@ -59,13 +59,6 @@
 # define UNLEN 256
 #endif
 
-/*
-  Max hostname length. The Windows gethostname() documentation states that 256
-  bytes will always be large enough to hold the null-terminated hostname.
-*/
-#ifndef MAXHOSTNAMELEN
-# define MAXHOSTNAMELEN 256
-#endif
 
 /* Maximum environment variable size, including the terminating null */
 #define MAX_ENV_VAR_LENGTH 32767
@@ -327,6 +320,11 @@ uint64_t uv_get_total_memory(void) {
 }
 
 
+uint64_t uv_get_constrained_memory(void) {
+  return 0;  /* Memory constraints are unknown. */
+}
+
+
 uv_pid_t uv_os_getpid(void) {
   return GetCurrentProcessId();
 }
@@ -684,12 +682,9 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) {
                            NULL,
                            (BYTE*)&cpu_brand,
                            &cpu_brand_size);
-    if (err != ERROR_SUCCESS) {
-      RegCloseKey(processor_key);
-      goto error;
-    }
-
     RegCloseKey(processor_key);
+    if (err != ERROR_SUCCESS)
+      goto error;
 
     cpu_info = &cpu_infos[i];
     cpu_info->speed = cpu_speed;
@@ -713,9 +708,11 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) {
   return 0;
 
  error:
-  /* This is safe because the cpu_infos array is zeroed on allocation. */
-  for (i = 0; i < cpu_count; i++)
-    uv__free(cpu_infos[i].model);
+  if (cpu_infos != NULL) {
+    /* This is safe because the cpu_infos array is zeroed on allocation. */
+    for (i = 0; i < cpu_count; i++)
+      uv__free(cpu_infos[i].model);
+  }
 
   uv__free(cpu_infos);
   uv__free(sppi);
@@ -1510,7 +1507,7 @@ int uv_os_unsetenv(const char* name) {
 
 
 int uv_os_gethostname(char* buffer, size_t* size) {
-  char buf[MAXHOSTNAMELEN + 1];
+  char buf[UV_MAXHOSTNAMESIZE];
   size_t len;
 
   if (buffer == NULL || size == NULL || *size == 0)
@@ -1634,6 +1631,10 @@ int uv_os_uname(uv_utsname_t* buffer) {
      https://github.com/gagern/gnulib/blob/master/lib/uname.c */
   OSVERSIONINFOW os_info;
   SYSTEM_INFO system_info;
+  HKEY registry_key;
+  WCHAR product_name_w[256];
+  DWORD product_name_w_size;
+  int version_size;
   int processor_level;
   int r;
 
@@ -1658,16 +1659,56 @@ int uv_os_uname(uv_utsname_t* buffer) {
   }
 
   /* Populate the version field. */
-  if (WideCharToMultiByte(CP_UTF8,
-                          0,
-                          os_info.szCSDVersion,
-                          -1,
-                          buffer->version,
-                          sizeof(buffer->version),
-                          NULL,
-                          NULL) == 0) {
-    r = uv_translate_sys_error(GetLastError());
-    goto error;
+  version_size = 0;
+  r = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+                    L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+                    0,
+                    KEY_QUERY_VALUE,
+                    &registry_key);
+
+  if (r == ERROR_SUCCESS) {
+    product_name_w_size = sizeof(product_name_w);
+    r = RegGetValueW(registry_key,
+                     NULL,
+                     L"ProductName",
+                     RRF_RT_REG_SZ,
+                     NULL,
+                     (PVOID) product_name_w,
+                     &product_name_w_size);
+    RegCloseKey(registry_key);
+
+    if (r == ERROR_SUCCESS) {
+      version_size = WideCharToMultiByte(CP_UTF8,
+                                         0,
+                                         product_name_w,
+                                         -1,
+                                         buffer->version,
+                                         sizeof(buffer->version),
+                                         NULL,
+                                         NULL);
+      if (version_size == 0) {
+        r = uv_translate_sys_error(GetLastError());
+        goto error;
+      }
+    }
+  }
+
+  /* Append service pack information to the version if present. */
+  if (os_info.szCSDVersion[0] != L'\0') {
+    if (version_size > 0)
+      buffer->version[version_size - 1] = ' ';
+
+    if (WideCharToMultiByte(CP_UTF8,
+                            0,
+                            os_info.szCSDVersion,
+                            -1,
+                            buffer->version + version_size,
+                            sizeof(buffer->version) - version_size,
+                            NULL,
+                            NULL) == 0) {
+      r = uv_translate_sys_error(GetLastError());
+      goto error;
+    }
   }
 
   /* Populate the sysname field. */
@@ -1744,3 +1785,20 @@ error:
   buffer->machine[0] = '\0';
   return r;
 }
+
+int uv_gettimeofday(uv_timeval64_t* tv) {
+  /* Based on https://doxygen.postgresql.org/gettimeofday_8c_source.html */
+  const uint64_t epoch = (uint64_t) 116444736000000000ULL;
+  FILETIME file_time;
+  ULARGE_INTEGER ularge;
+
+  if (tv == NULL)
+    return UV_EINVAL;
+
+  GetSystemTimeAsFileTime(&file_time);
+  ularge.LowPart = file_time.dwLowDateTime;
+  ularge.HighPart = file_time.dwHighDateTime;
+  tv->tv_sec = (int64_t) ((ularge.QuadPart - epoch) / 10000000L);
+  tv->tv_usec = (int32_t) (((ularge.QuadPart - epoch) % 10000000L) / 10);
+  return 0;
+}
diff --git a/src/win/winsock.c b/src/win/winsock.c
index 5e7da2a..5820ba9 100644
--- a/src/win/winsock.c
+++ b/src/win/winsock.c
@@ -87,12 +87,6 @@ void uv_winsock_init(void) {
   WSAPROTOCOL_INFOW protocol_info;
   int opt_len;
 
-  /* Initialize winsock */
-  errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data);
-  if (errorno != 0) {
-    uv_fatal_error(errorno, "WSAStartup");
-  }
-
   /* Set implicit binding address used by connectEx */
   if (uv_ip4_addr("0.0.0.0", 0, &uv_addr_ip4_any_)) {
     abort();
@@ -102,6 +96,15 @@ void uv_winsock_init(void) {
     abort();
   }
 
+  /* Skip initialization in safe mode without network support */
+  if (1 == GetSystemMetrics(SM_CLEANBOOT)) return;
+
+  /* Initialize winsock */
+  errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data);
+  if (errorno != 0) {
+    uv_fatal_error(errorno, "WSAStartup");
+  }
+
   /* Detect non-IFS LSPs */
   dummy = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
 
-- 
cgit v0.12


From cee57246ef03ba5c2a6e67b472d2d1f8946c4f75 Mon Sep 17 00:00:00 2001
From: Kyle Edwards <kyle.edwards@kitware.com>
Date: Mon, 17 Jun 2019 15:48:48 -0400
Subject: libuv: Add uv__statx() stub to cmake-bootstrap.c

---
 Utilities/cmlibuv/src/unix/cmake-bootstrap.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/Utilities/cmlibuv/src/unix/cmake-bootstrap.c b/Utilities/cmlibuv/src/unix/cmake-bootstrap.c
index 309ec79..d42ff05 100644
--- a/Utilities/cmlibuv/src/unix/cmake-bootstrap.c
+++ b/Utilities/cmlibuv/src/unix/cmake-bootstrap.c
@@ -137,4 +137,13 @@ int uv__utimesat(int dirfd, const char* path, const struct timespec times[2],
   errno = ENOSYS;
   return -1;
 }
+
+int uv__statx(int dirfd,
+              const char* path,
+              int flags,
+              unsigned int mask,
+              struct uv__statx* statxbuf) {
+  errno = ENOSYS;
+  return -1;
+}
 #endif
-- 
cgit v0.12


From db4667cc727fd7e52f6731642501e680922f72e7 Mon Sep 17 00:00:00 2001
From: Brad King <brad.king@kitware.com>
Date: Wed, 19 Jun 2019 06:52:19 -0400
Subject: libuv: fix compilation with macOS 10.10 SDK

The `F_BARRIERFSYNC` constant was not added until the macOS 10.11 SDK.
Hard-code its value instead.  This approach is backported from upstream
libuv PR 2334.
---
 Utilities/cmlibuv/src/unix/fs.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Utilities/cmlibuv/src/unix/fs.c b/Utilities/cmlibuv/src/unix/fs.c
index 48500ff..3023b1e 100644
--- a/Utilities/cmlibuv/src/unix/fs.c
+++ b/Utilities/cmlibuv/src/unix/fs.c
@@ -168,7 +168,7 @@ static ssize_t uv__fs_fsync(uv_fs_t* req) {
 
   r = fcntl(req->file, F_FULLFSYNC);
   if (r != 0)
-    r = fcntl(req->file, F_BARRIERFSYNC);  /* fsync + barrier */
+    r = fcntl(req->file, 85 /* F_BARRIERFSYNC */);  /* fsync + barrier */
   if (r != 0)
     r = fsync(req->file);
   return r;
-- 
cgit v0.12