deps: upgrade libuv to 1f9bd99
authorBen Noordhuis <info@bnoordhuis.nl>
Thu, 13 Sep 2012 14:18:54 +0000 (16:18 +0200)
committerBen Noordhuis <info@bnoordhuis.nl>
Thu, 13 Sep 2012 14:18:54 +0000 (16:18 +0200)
27 files changed:
deps/uv/README.md
deps/uv/config-unix.mk
deps/uv/gyp_uv
deps/uv/include/uv-private/uv-darwin.h
deps/uv/include/uv-private/uv-unix.h
deps/uv/include/uv.h
deps/uv/src/unix/async.c
deps/uv/src/unix/core.c
deps/uv/src/unix/darwin.c
deps/uv/src/unix/fsevents.c [new file with mode: 0644]
deps/uv/src/unix/internal.h
deps/uv/src/unix/kqueue.c
deps/uv/src/unix/loop.c
deps/uv/src/unix/netbsd.c
deps/uv/src/unix/process.c
deps/uv/src/unix/stream.c
deps/uv/src/unix/tcp.c
deps/uv/src/uv-common.c
deps/uv/src/win/error.c
deps/uv/src/win/internal.h
deps/uv/src/win/pipe.c
deps/uv/src/win/process-stdio.c
deps/uv/src/win/stream.c
deps/uv/src/win/tcp.c
deps/uv/src/win/tty.c
deps/uv/test/test-fs-event.c
deps/uv/uv.gyp

index ca8fc2e..6789ffc 100644 (file)
@@ -77,6 +77,9 @@ Macintosh users run
     ./gyp_uv -f xcode
     xcodebuild -project uv.xcodeproj -configuration Release -target All
 
+Note for Linux users: compile your project with `-D_GNU_SOURCE` when you
+include `uv.h`. GYP builds take care of that automatically. If you use
+autotools, add a `AC_GNU_SOURCE` declaration to your `configure.ac`.
 
 ## Supported Platforms
 
index 53c636d..c0be7e4 100644 (file)
@@ -60,6 +60,7 @@ CPPFLAGS += -D_DARWIN_USE_64_BIT_INODE=1
 LINKFLAGS+=-framework CoreServices
 OBJS += src/unix/darwin.o
 OBJS += src/unix/kqueue.o
+OBJS += src/unix/fsevents.o
 endif
 
 ifeq (Linux,$(uname_S))
@@ -91,7 +92,7 @@ endif
 ifeq (NetBSD,$(uname_S))
 EV_CONFIG=config_netbsd.h
 EIO_CONFIG=config_netbsd.h
-LINKFLAGS+=
+LINKFLAGS+=-lkvm
 OBJS += src/unix/netbsd.o
 OBJS += src/unix/kqueue.o
 endif
index a8528fa..9c719fd 100755 (executable)
@@ -60,9 +60,11 @@ if __name__ == '__main__':
 
   # There's a bug with windows which doesn't allow this feature.
   if sys.platform != 'win32':
-    args.extend(['--generator-output', output_dir])
-    args.extend(['-Goutput_dir=' + output_dir])
-    args.extend('-f make'.split())
+    if '-f' not in args:
+      args.extend('-f make'.split())
+    if 'ninja' not in args:
+      args.extend(['-Goutput_dir=' + output_dir])
+      args.extend(['--generator-output', output_dir])
     (major, minor), is_clang = compiler_version()
     args.append('-Dgcc_version=%d' % (10 * major + minor))
     args.append('-Dclang=%d' % int(is_clang))
index 7f1b928..397c6a9 100644 (file)
 # define UV_PLATFORM_SEM_T semaphore_t
 #endif
 
+#define UV_PLATFORM_LOOP_FIELDS                                               \
+  uv_thread_t cf_thread;                                                      \
+  void* cf_cb;                                                                \
+  void* cf_loop;                                                              \
+  uv_mutex_t cf_mutex;                                                        \
+  uv_sem_t cf_sem;                                                            \
+  ngx_queue_t cf_signals;                                                     \
+
 #define UV_PLATFORM_FS_EVENT_FIELDS                                           \
   ev_io event_watcher;                                                        \
   int fflags;                                                                 \
   int fd;                                                                     \
+  void* cf_eventstream;                                                       \
+  uv_async_t* cf_cb;                                                          \
+  ngx_queue_t cf_events;                                                      \
+  uv_sem_t cf_sem;                                                            \
+  uv_mutex_t cf_mutex;                                                        \
 
 #define UV_STREAM_PRIVATE_PLATFORM_FIELDS                                     \
   void* select;                                                               \
index 6cdea67..91ffbe4 100644 (file)
@@ -137,6 +137,7 @@ typedef struct {
   uint64_t time;                                                              \
   void* signal_ctx;                                                           \
   uv_signal_t child_watcher;                                                  \
+  int emfile_fd;                                                              \
   UV_PLATFORM_LOOP_FIELDS                                                     \
 
 #define UV_REQ_TYPE_PRIVATE /* empty */
index 4078bcd..6a76383 100644 (file)
@@ -1789,6 +1789,7 @@ UV_EXTERN void uv_once(uv_once_t* guard, void (*callback)(void));
 
 UV_EXTERN int uv_thread_create(uv_thread_t *tid,
     void (*entry)(void *arg), void *arg);
+UV_EXTERN unsigned long uv_thread_self(void);
 UV_EXTERN int uv_thread_join(uv_thread_t *tid);
 
 /* the presence of these unions force similar struct layout */
index 78c1863..63bedf5 100644 (file)
@@ -31,7 +31,7 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* handle, int events);
 
 
 __attribute__((always_inline))
-inline static int uv__async_make_pending(volatile sig_atomic_t* ptr) {
+static int uv__async_make_pending(volatile sig_atomic_t* ptr) {
   /* Do a cheap read first. */
   if (*ptr)
     return 1;
index 85d5f16..3ad63f1 100644 (file)
@@ -463,7 +463,7 @@ int uv__accept(int sockfd) {
 
   while (1) {
 #if __linux__
-    static __read_mostly int no_accept4;
+    static int no_accept4;
 
     if (no_accept4)
       goto skip;
index b1586a3..675a4f6 100644 (file)
 
 static char *process_title;
 
+/* Forward declarations */
+void uv__cf_loop_runner(void* arg);
+void uv__cf_loop_cb(void* arg);
+
+typedef struct uv__cf_loop_signal_s uv__cf_loop_signal_t;
+struct uv__cf_loop_signal_s {
+  void* arg;
+  cf_loop_signal_cb cb;
+  ngx_queue_t member;
+};
+
 
 int uv__platform_loop_init(uv_loop_t* loop, int default_loop) {
+  CFRunLoopSourceContext ctx;
+  int r;
+
+  loop->cf_loop = NULL;
+  if ((r = uv_mutex_init(&loop->cf_mutex)))
+    return r;
+  if ((r = uv_sem_init(&loop->cf_sem, 0)))
+    return r;
+  ngx_queue_init(&loop->cf_signals);
+
+  memset(&ctx, 0, sizeof(ctx));
+  ctx.info = loop;
+  ctx.perform = uv__cf_loop_cb;
+  loop->cf_cb = CFRunLoopSourceCreate(NULL, 0, &ctx);
+
+  if ((r = uv_thread_create(&loop->cf_thread, uv__cf_loop_runner, loop)))
+    return r;
+
+  /* Synchronize threads */
+  uv_sem_wait(&loop->cf_sem);
+  assert(((volatile CFRunLoopRef) loop->cf_loop) != NULL);
+
   return 0;
 }
 
 
 void uv__platform_loop_delete(uv_loop_t* loop) {
+  ngx_queue_t* item;
+  uv__cf_loop_signal_t* s;
+
+  assert(loop->cf_loop != NULL);
+  CFRunLoopStop(loop->cf_loop);
+  uv_thread_join(&loop->cf_thread);
+  loop->cf_loop = NULL;
+
+  uv_sem_destroy(&loop->cf_sem);
+  uv_mutex_destroy(&loop->cf_mutex);
+
+  /* Free any remaining data */
+  while (!ngx_queue_empty(&loop->cf_signals)) {
+    item = ngx_queue_head(&loop->cf_signals);
+
+    s = ngx_queue_data(item, uv__cf_loop_signal_t, member);
+
+    ngx_queue_remove(item);
+    free(s);
+  }
+}
+
+
+void uv__cf_loop_runner(void* arg) {
+  uv_loop_t* loop;
+
+  loop = arg;
+
+  /* Get thread's loop */
+  *((volatile CFRunLoopRef*)&loop->cf_loop) = CFRunLoopGetCurrent();
+
+  CFRunLoopAddSource(loop->cf_loop,
+                     loop->cf_cb,
+                     kCFRunLoopDefaultMode);
+
+  uv_sem_post(&loop->cf_sem);
+
+  CFRunLoopRun();
+
+  CFRunLoopRemoveSource(loop->cf_loop,
+                        loop->cf_cb,
+                        kCFRunLoopDefaultMode);
+}
+
+
+void uv__cf_loop_cb(void* arg) {
+  uv_loop_t* loop;
+  ngx_queue_t* item;
+  ngx_queue_t split_head;
+  uv__cf_loop_signal_t* s;
+
+  loop = arg;
+
+  uv_mutex_lock(&loop->cf_mutex);
+  ngx_queue_init(&split_head);
+  if (!ngx_queue_empty(&loop->cf_signals)) {
+    ngx_queue_t* split_pos = ngx_queue_next(&loop->cf_signals);
+    ngx_queue_split(&loop->cf_signals, split_pos, &split_head);
+  }
+  uv_mutex_unlock(&loop->cf_mutex);
+
+  while (!ngx_queue_empty(&split_head)) {
+    item = ngx_queue_head(&split_head);
+
+    s = ngx_queue_data(item, uv__cf_loop_signal_t, member);
+    s->cb(s->arg);
+
+    ngx_queue_remove(item);
+    free(s);
+  }
+}
+
+
+void uv__cf_loop_signal(uv_loop_t* loop, cf_loop_signal_cb cb, void* arg) {
+  uv__cf_loop_signal_t* item;
+
+  item = malloc(sizeof(*item));
+  /* XXX: Fail */
+  if (item == NULL)
+    abort();
+
+  item->arg = arg;
+  item->cb = cb;
+
+  uv_mutex_lock(&loop->cf_mutex);
+  ngx_queue_insert_tail(&loop->cf_signals, &item->member);
+  uv_mutex_unlock(&loop->cf_mutex);
+
+  assert(loop->cf_loop != NULL);
+  CFRunLoopSourceSignal(loop->cf_cb);
+  CFRunLoopWakeUp(loop->cf_loop);
 }
 
 
diff --git a/deps/uv/src/unix/fsevents.c b/deps/uv/src/unix/fsevents.c
new file mode 100644 (file)
index 0000000..1a7e06e
--- /dev/null
@@ -0,0 +1,225 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "uv.h"
+#include "internal.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <CoreServices/CoreServices.h>
+
+typedef struct uv__fsevents_event_s uv__fsevents_event_t;
+
+struct uv__fsevents_event_s {
+  int events;
+  ngx_queue_t member;
+  char path[1];
+};
+
+
+#define UV__FSEVENTS_WALK(handle, block)                                      \
+    {                                                                         \
+      ngx_queue_t* curr;                                                      \
+      ngx_queue_t split_head;                                                 \
+      uv__fsevents_event_t* event;                                            \
+      uv_mutex_lock(&(handle)->cf_mutex);                                     \
+      ngx_queue_init(&split_head);                                            \
+      if (!ngx_queue_empty(&(handle)->cf_events)) {                           \
+        ngx_queue_t* split_pos = ngx_queue_next(&(handle)->cf_events);        \
+        ngx_queue_split(&(handle)->cf_events, split_pos, &split_head);        \
+      }                                                                       \
+      uv_mutex_unlock(&(handle)->cf_mutex);                                   \
+      while (!ngx_queue_empty(&split_head)) {                                 \
+        curr = ngx_queue_head(&split_head);                                   \
+        /* Invoke callback */                                                 \
+        event = ngx_queue_data(curr, uv__fsevents_event_t, member);           \
+        ngx_queue_remove(curr);                                               \
+        /* Invoke block code, but only if handle wasn't closed */             \
+        if (((handle)->flags & (UV_CLOSING | UV_CLOSED)) == 0)                \
+          block                                                               \
+        /* Free allocated data */                                             \
+        free(event);                                                          \
+      }                                                                       \
+    }
+
+
+void uv__fsevents_cb(uv_async_t* cb, int status) {
+  uv_fs_event_t* handle;
+
+  handle = cb->data;
+
+  UV__FSEVENTS_WALK(handle, {
+    if (handle->fd != -1)
+      handle->cb(handle, event->path, event->events, 0);
+  })
+
+  if ((handle->flags & (UV_CLOSING | UV_CLOSED)) == 0 && handle->fd == -1)
+    uv__fsevents_close(handle);
+}
+
+
+void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef,
+                           void* info,
+                           size_t numEvents,
+                           void* eventPaths,
+                           const FSEventStreamEventFlags eventFlags[],
+                           const FSEventStreamEventId eventIds[]) {
+  size_t i;
+  int len;
+  char** paths;
+  uv_fs_event_t* handle;
+  uv__fsevents_event_t* event;
+  ngx_queue_t add_list;
+
+  handle = info;
+  paths = eventPaths;
+  ngx_queue_init(&add_list);
+
+  for (i = 0; i < numEvents; i++) {
+    /* Ignore system events */
+    if (eventFlags[i] & (kFSEventStreamEventFlagUserDropped |
+                         kFSEventStreamEventFlagKernelDropped |
+                         kFSEventStreamEventFlagEventIdsWrapped |
+                         kFSEventStreamEventFlagHistoryDone |
+                         kFSEventStreamEventFlagMount |
+                         kFSEventStreamEventFlagUnmount)) {
+      continue;
+    }
+
+    /* TODO: Report errors */
+    len = strlen(paths[i]);
+    event = malloc(sizeof(*event) + len);
+    if (event == NULL)
+      break;
+
+    memcpy(event->path, paths[i], len + 1);
+
+    if (eventFlags[i] & kFSEventStreamEventFlagItemModified)
+      event->events = UV_CHANGE;
+    else
+      event->events = UV_RENAME;
+
+    ngx_queue_insert_tail(&add_list, &event->member);
+  }
+  uv_mutex_lock(&handle->cf_mutex);
+  ngx_queue_add(&handle->cf_events, &add_list);
+  uv_mutex_unlock(&handle->cf_mutex);
+
+  uv_async_send(handle->cf_cb);
+}
+
+
+void uv__fsevents_schedule(void* arg) {
+  uv_fs_event_t* handle;
+
+  handle = arg;
+  FSEventStreamScheduleWithRunLoop(handle->cf_eventstream,
+                                   handle->loop->cf_loop,
+                                   kCFRunLoopDefaultMode);
+  FSEventStreamStart(handle->cf_eventstream);
+  uv_sem_post(&handle->cf_sem);
+}
+
+
+int uv__fsevents_init(uv_fs_event_t* handle) {
+  FSEventStreamContext ctx;
+  FSEventStreamRef ref;
+  CFStringRef path;
+  CFArrayRef paths;
+  CFAbsoluteTime latency;
+  FSEventStreamCreateFlags flags;
+
+  /* Initialize context */
+  ctx.version = 0;
+  ctx.info = handle;
+  ctx.retain = NULL;
+  ctx.release = NULL;
+  ctx.copyDescription = NULL;
+
+  /* Initialize paths array */
+  path = CFStringCreateWithCString(NULL,
+                                   handle->filename,
+                                   CFStringGetSystemEncoding());
+  paths = CFArrayCreate(NULL, (const void**)&path, 1, NULL);
+
+  latency = 0.15;
+
+  /* Set appropriate flags */
+  flags = kFSEventStreamCreateFlagFileEvents;
+
+  ref = FSEventStreamCreate(NULL,
+                            &uv__fsevents_event_cb,
+                            &ctx,
+                            paths,
+                            kFSEventStreamEventIdSinceNow,
+                            latency,
+                            flags);
+  handle->cf_eventstream = ref;
+
+  /*
+   * Events will occur in other thread.
+   * Initialize callback for getting them back into event loop's thread
+   */
+  handle->cf_cb = malloc(sizeof(*handle->cf_cb));
+  if (handle->cf_cb == NULL)
+    return uv__set_sys_error(handle->loop, ENOMEM);
+
+  handle->cf_cb->data = handle;
+  uv_async_init(handle->loop, handle->cf_cb, uv__fsevents_cb);
+  handle->cf_cb->flags |= UV__HANDLE_INTERNAL;
+  uv_unref((uv_handle_t*) handle->cf_cb);
+
+  uv_mutex_init(&handle->cf_mutex);
+  uv_sem_init(&handle->cf_sem, 0);
+  ngx_queue_init(&handle->cf_events);
+
+  uv__cf_loop_signal(handle->loop, uv__fsevents_schedule, handle);
+
+  return 0;
+}
+
+
+int uv__fsevents_close(uv_fs_event_t* handle) {
+  if (handle->cf_eventstream == NULL)
+    return -1;
+
+  /* Ensure that event stream was scheduled */
+  uv_sem_wait(&handle->cf_sem);
+
+  /* Stop emitting events */
+  FSEventStreamStop(handle->cf_eventstream);
+
+  /* Release stream */
+  FSEventStreamInvalidate(handle->cf_eventstream);
+  FSEventStreamRelease(handle->cf_eventstream);
+  handle->cf_eventstream = NULL;
+
+  uv_close((uv_handle_t*) handle->cf_cb, (uv_close_cb) free);
+
+  /* Free data in queue */
+  UV__FSEVENTS_WALK(handle, {
+    /* NOP */
+  })
+
+  uv_mutex_destroy(&handle->cf_mutex);
+  uv_sem_destroy(&handle->cf_sem);
+
+  return 0;
+}
index e266810..4abfedd 100644 (file)
 #include <assert.h>
 #include <stdlib.h> /* abort */
 
-#if defined(__GNUC__)
-# define __read_mostly __attribute__((__section__(".data.read_mostly")))
-#else
-# define __read_mostly
-#endif
-
 #if defined(__STRICT_ANSI__)
 # define inline __inline
 #endif
@@ -108,9 +102,9 @@ enum {
   UV_LOOP_EIO_INITIALIZED = 1
 };
 
-inline static void uv__req_init(uv_loop_t* loop,
-                                uv_req_t* req,
-                                uv_req_type type) {
+__attribute__((unused))
+__attribute__((always_inline))
+static void uv__req_init(uv_loop_t* loop, uv_req_t* req, uv_req_type type) {
   req->type = type;
   uv__req_register(loop, req);
 }
@@ -195,4 +189,32 @@ void uv__udp_finish_close(uv_udp_t* handle);
 int uv__make_socketpair(int fds[2], int flags);
 int uv__make_pipe(int fds[2], int flags);
 
+#if defined(__APPLE__)
+typedef void (*cf_loop_signal_cb)(void*);
+
+void uv__cf_loop_signal(uv_loop_t* loop, cf_loop_signal_cb cb, void* arg);
+
+int uv__fsevents_init(uv_fs_event_t* handle);
+int uv__fsevents_close(uv_fs_event_t* handle);
+
+/* OSX < 10.7 has no file events, polyfill them */
+#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070
+
+static const int kFSEventStreamCreateFlagFileEvents = 0x00000010;
+static const int kFSEventStreamEventFlagItemCreated = 0x00000100;
+static const int kFSEventStreamEventFlagItemRemoved = 0x00000200;
+static const int kFSEventStreamEventFlagItemInodeMetaMod = 0x00000400;
+static const int kFSEventStreamEventFlagItemRenamed = 0x00000800;
+static const int kFSEventStreamEventFlagItemModified = 0x00001000;
+static const int kFSEventStreamEventFlagItemFinderInfoMod = 0x00002000;
+static const int kFSEventStreamEventFlagItemChangeOwner = 0x00004000;
+static const int kFSEventStreamEventFlagItemXattrMod = 0x00008000;
+static const int kFSEventStreamEventFlagItemIsFile = 0x00010000;
+static const int kFSEventStreamEventFlagItemIsDir = 0x00020000;
+static const int kFSEventStreamEventFlagItemIsSymlink = 0x00040000;
+
+#endif /* __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070 */
+
+#endif /* defined(__APPLE__) */
+
 #endif /* UV_UNIX_INTERNAL_H_ */
index be68b58..b79dce3 100644 (file)
@@ -89,6 +89,9 @@ int uv_fs_event_init(uv_loop_t* loop,
                      uv_fs_event_cb cb,
                      int flags) {
   int fd;
+#if defined(__APPLE__)
+  struct stat statbuf;
+#endif /* defined(__APPLE__) */
 
   /* We don't support any flags yet. */
   assert(!flags);
@@ -105,6 +108,22 @@ int uv_fs_event_init(uv_loop_t* loop,
   handle->fflags = 0;
   handle->cb = cb;
   handle->fd = fd;
+
+#if defined(__APPLE__)
+  /* Nullify field to perform checks later */
+  handle->cf_eventstream = NULL;
+
+  if (fstat(fd, &statbuf))
+    goto fallback;
+  /* FSEvents works only with directories */
+  if (!(statbuf.st_mode & S_IFDIR))
+    goto fallback;
+
+  return uv__fsevents_init(handle);
+
+fallback:
+#endif /* defined(__APPLE__) */
+
   uv__fs_event_start(handle);
 
   return 0;
@@ -112,7 +131,13 @@ int uv_fs_event_init(uv_loop_t* loop,
 
 
 void uv__fs_event_close(uv_fs_event_t* handle) {
+#if defined(__APPLE__)
+  if (uv__fsevents_close(handle))
+    uv__fs_event_stop(handle);
+#else
   uv__fs_event_stop(handle);
+#endif /* defined(__APPLE__) */
+
   uv__handle_stop(handle);
   free(handle->filename);
   close(handle->fd);
index 70cdc82..5cd3bd0 100644 (file)
@@ -51,6 +51,7 @@ int uv__loop_init(uv_loop_t* loop, int default_loop) {
   loop->time = uv_hrtime() / 1000000;
   loop->async_pipefd[0] = -1;
   loop->async_pipefd[1] = -1;
+  loop->emfile_fd = -1;
   loop->ev = (default_loop ? ev_default_loop : ev_loop_new)(flags);
   ev_set_userdata(loop->ev, loop);
   eio_channel_init(&loop->uv_eio_channel, loop);
@@ -73,4 +74,9 @@ void uv__loop_delete(uv_loop_t* loop) {
   uv__platform_loop_delete(loop);
   uv__signal_unregister(loop);
   ev_loop_destroy(loop->ev);
+
+  if (loop->emfile_fd != -1) {
+    close(loop->emfile_fd);
+    loop->emfile_fd = -1;
+  }
 }
index b1c437b..dab1b69 100644 (file)
  */
 
 #include "uv.h"
+#include "internal.h"
 
 #include <assert.h>
 #include <string.h>
 #include <errno.h>
 
+#include <kvm.h>
+#include <paths.h>
+#include <ifaddrs.h>
+#include <unistd.h>
+#include <time.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include <net/if.h>
 #include <sys/resource.h>
 #include <sys/types.h>
 #include <sys/sysctl.h>
@@ -34,6 +44,8 @@
 #undef NANOSEC
 #define NANOSEC ((uint64_t) 1e9)
 
+static char *process_title;
+
 
 int uv__platform_loop_init(uv_loop_t* loop, int default_loop) {
   return 0;
@@ -50,18 +62,20 @@ uint64_t uv_hrtime(void) {
   return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec);
 }
 
+
 void uv_loadavg(double avg[3]) {
   struct loadavg info;
   size_t size = sizeof(info);
   int which[] = {CTL_VM, VM_LOADAVG};
 
-  if (sysctl(which, 2, &info, &size, NULL, 0) < 0) return;
+  if (sysctl(which, 2, &info, &size, NULL, 0) == -1) return;
 
   avg[0] = (double) info.ldavg[0] / info.fscale;
   avg[1] = (double) info.ldavg[1] / info.fscale;
   avg[2] = (double) info.ldavg[2] / info.fscale;
 }
 
+
 int uv_exepath(char* buffer, size_t* size) {
   int mib[4];
   size_t cb;
@@ -78,7 +92,7 @@ int uv_exepath(char* buffer, size_t* size) {
   mib[3] = KERN_PROC_ARGV;
 
   cb = *size;
-  if (sysctl(mib, 4, buffer, &cb, NULL, 0) < 0) {
+  if (sysctl(mib, 4, buffer, &cb, NULL, 0) == -1) {
     *size = 0;
     return -1;
   }
@@ -87,18 +101,20 @@ int uv_exepath(char* buffer, size_t* size) {
   return 0;
 }
 
+
 uint64_t uv_get_free_memory(void) {
   struct uvmexp info;
   size_t size = sizeof(info);
   int which[] = {CTL_VM, VM_UVMEXP};
 
-  if (sysctl(which, 2, &info, &size, NULL, 0) < 0) {
+  if (sysctl(which, 2, &info, &size, NULL, 0) == -1) {
     return -1;
   }
 
   return (uint64_t) info.free * sysconf(_SC_PAGESIZE);
 }
 
+
 uint64_t uv_get_total_memory(void) {
 #if defined(HW_PHYSMEM64)
   uint64_t info;
@@ -109,9 +125,229 @@ uint64_t uv_get_total_memory(void) {
 #endif
   size_t size = sizeof(info);
 
-  if (sysctl(which, 2, &info, &size, NULL, 0) < 0) {
+  if (sysctl(which, 2, &info, &size, NULL, 0) == -1) {
     return -1;
   }
 
   return (uint64_t) info;
 }
+
+
+char** uv_setup_args(int argc, char** argv) {
+  process_title = argc ? strdup(argv[0]) : NULL;
+  return argv;
+}
+
+
+uv_err_t uv_set_process_title(const char* title) {
+  if (process_title) free(process_title);
+
+  process_title = strdup(title);
+  setproctitle("%s", title);
+
+  return uv_ok_;
+}
+
+
+uv_err_t uv_get_process_title(char* buffer, size_t size) {
+  if (process_title) {
+    strncpy(buffer, process_title, size);
+  } else {
+    if (size > 0) {
+      buffer[0] = '\0';
+    }
+  }
+
+  return uv_ok_;
+}
+
+
+uv_err_t uv_resident_set_memory(size_t* rss) {
+  kvm_t *kd = NULL;
+  struct kinfo_proc2 *kinfo = NULL;
+  pid_t pid;
+  int nprocs;
+  int max_size = sizeof(struct kinfo_proc2);
+  int page_size;
+
+  page_size = getpagesize();
+  pid = getpid();
+
+  kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open");
+
+  if (kd == NULL) goto error;
+
+  kinfo = kvm_getproc2(kd, KERN_PROC_PID, pid, max_size, &nprocs);
+  if (kinfo == NULL) goto error;
+
+  *rss = kinfo->p_vm_rssize * page_size;
+
+  kvm_close(kd);
+
+  return uv_ok_;
+
+error:
+  if (kd) kvm_close(kd);
+  return uv__new_sys_error(errno);
+}
+
+
+uv_err_t uv_uptime(double* uptime) {
+  time_t now;
+  struct timeval info;
+  size_t size = sizeof(info);
+  static int which[] = {CTL_KERN, KERN_BOOTTIME};
+
+  if (sysctl(which, 2, &info, &size, NULL, 0) == -1) {
+    return uv__new_sys_error(errno);
+  }
+
+  now = time(NULL);
+
+  *uptime = (double)(now - info.tv_sec);
+  return uv_ok_;
+}
+
+
+uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) {
+  unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK);
+  unsigned int multiplier = ((uint64_t)1000L / ticks);
+  unsigned int cur = 0;
+  uv_cpu_info_t* cpu_info;
+  u_int64_t* cp_times;
+  char model[512];
+  u_int64_t cpuspeed;
+  int numcpus;
+  size_t size;
+  int i;
+
+  size = sizeof(model);
+  if (sysctlbyname("machdep.cpu_brand", &model, &size, NULL, 0) == -1 &&
+      sysctlbyname("hw.model", &model, &size, NULL, 0) == -1) {
+    return uv__new_sys_error(errno);
+  }
+
+  size = sizeof(numcpus);
+  if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0) == -1) {
+    return uv__new_sys_error(errno);
+  }
+  *count = numcpus;
+
+  /* Only i386 and amd64 have machdep.tsc_freq */
+  size = sizeof(cpuspeed);
+  if (sysctlbyname("machdep.tsc_freq", &cpuspeed, &size, NULL, 0) == -1) {
+    cpuspeed = 0;
+  }
+
+  size = numcpus * CPUSTATES * sizeof(*cp_times);
+  cp_times = malloc(size);
+  if (cp_times == NULL) {
+    return uv__new_artificial_error(UV_ENOMEM);
+  }
+  if (sysctlbyname("kern.cp_time", cp_times, &size, NULL, 0) == -1) {
+    return uv__new_sys_error(errno);
+  }
+
+  *cpu_infos = malloc(numcpus * sizeof(**cpu_infos));
+  if (!(*cpu_infos)) {
+    free(cp_times);
+    free(*cpu_infos);
+    return uv__new_artificial_error(UV_ENOMEM);
+  }
+
+  for (i = 0; i < numcpus; i++) {
+    cpu_info = &(*cpu_infos)[i];
+    cpu_info->cpu_times.user = (uint64_t)(cp_times[CP_USER+cur]) * multiplier;
+    cpu_info->cpu_times.nice = (uint64_t)(cp_times[CP_NICE+cur]) * multiplier;
+    cpu_info->cpu_times.sys = (uint64_t)(cp_times[CP_SYS+cur]) * multiplier;
+    cpu_info->cpu_times.idle = (uint64_t)(cp_times[CP_IDLE+cur]) * multiplier;
+    cpu_info->cpu_times.irq = (uint64_t)(cp_times[CP_INTR+cur]) * multiplier;
+    cpu_info->model = strdup(model);
+    cpu_info->speed = (int)(cpuspeed/(uint64_t) 1e6);
+    cur += CPUSTATES;
+  }
+  free(cp_times);
+  return uv_ok_;
+}
+
+void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) {
+  int i;
+
+  for (i = 0; i < count; i++) {
+    free(cpu_infos[i].model);
+  }
+
+  free(cpu_infos);
+}
+
+
+uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, int* count) {
+  struct ifaddrs *addrs;
+  struct ifaddrs *ent;
+  uv_interface_address_t* address;
+
+  if (getifaddrs(&addrs) != 0) {
+    return uv__new_sys_error(errno);
+  }
+
+  *count = 0;
+
+  /* Count the number of interfaces */
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (!(ent->ifa_flags & IFF_UP && ent->ifa_flags & IFF_RUNNING) ||
+        (ent->ifa_addr == NULL) ||
+        (ent->ifa_addr->sa_family != PF_INET)) {
+      continue;
+    }
+    (*count)++;
+  }
+
+  *addresses = malloc(*count * sizeof(**addresses));
+
+  if (!(*addresses)) {
+    return uv__new_artificial_error(UV_ENOMEM);
+  }
+
+  address = *addresses;
+
+  for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
+    if (!(ent->ifa_flags & IFF_UP && ent->ifa_flags & IFF_RUNNING)) {
+      continue;
+    }
+
+    if (ent->ifa_addr == NULL) {
+      continue;
+    }
+
+    if (ent->ifa_addr->sa_family != PF_INET) {
+      continue;
+    }
+
+    address->name = strdup(ent->ifa_name);
+
+    if (ent->ifa_addr->sa_family == AF_INET6) {
+      address->address.address6 = *((struct sockaddr_in6 *)ent->ifa_addr);
+    } else {
+      address->address.address4 = *((struct sockaddr_in *)ent->ifa_addr);
+    }
+
+    address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK) ? 1 : 0;
+
+    address++;
+  }
+
+  freeifaddrs(addrs);
+
+  return uv_ok_;
+}
+
+
+void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) {
+  int i;
+
+  for (i = 0; i < count; i++) {
+    free(addresses[i].name);
+  }
+
+  free(addresses);
+}
index 829f79a..7c552a7 100644 (file)
@@ -117,7 +117,7 @@ static void uv__chld(uv_signal_t* handle, int signum) {
 
 int uv__make_socketpair(int fds[2], int flags) {
 #if __linux__
-  static __read_mostly int no_cloexec;
+  static int no_cloexec;
 
   if (no_cloexec)
     goto skip;
@@ -153,7 +153,7 @@ skip:
 
 int uv__make_pipe(int fds[2], int flags) {
 #if __linux__
-  static __read_mostly int no_pipe2;
+  static int no_pipe2;
 
   if (no_pipe2)
     goto skip;
index 3441185..5321d90 100644 (file)
@@ -62,6 +62,29 @@ static void uv__read(uv_stream_t* stream);
 static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, int events);
 
 
+/* Used by the accept() EMFILE party trick. */
+static int uv__open_cloexec(const char* path, int flags) {
+  int fd;
+
+#if defined(__linux__)
+  fd = open(path, flags | UV__O_CLOEXEC);
+  if (fd != -1)
+    return fd;
+
+  if (errno != EINVAL)
+    return -1;
+
+  /* O_CLOEXEC not supported. */
+#endif
+
+  fd = open(path, flags);
+  if (fd != -1)
+    uv__cloexec(fd, 1);
+
+  return fd;
+}
+
+
 static size_t uv__buf_count(uv_buf_t bufs[], int bufcnt) {
   size_t total = 0;
   int i;
@@ -90,6 +113,9 @@ void uv__stream_init(uv_loop_t* loop,
   ngx_queue_init(&stream->write_completed_queue);
   stream->write_queue_size = 0;
 
+  if (loop->emfile_fd == -1)
+    loop->emfile_fd = uv__open_cloexec("/", O_RDONLY);
+
 #if defined(__APPLE__)
   stream->select = NULL;
 #endif /* defined(__APPLE_) */
@@ -370,10 +396,56 @@ static void uv__next_accept(uv_idle_t* idle, int status) {
 }
 
 
+/* Implements a best effort approach to mitigating accept() EMFILE errors.
+ * We have a spare file descriptor stashed away that we close to get below
+ * the EMFILE limit. Next, we accept all pending connections and close them
+ * immediately to signal the clients that we're overloaded - and we are, but
+ * we still keep on trucking.
+ *
+ * There is one caveat: it's not reliable in a multi-threaded environment.
+ * The file descriptor limit is per process. Our party trick fails if another
+ * thread opens a file or creates a socket in the time window between us
+ * calling close() and accept().
+ */
+static int uv__emfile_trick(uv_loop_t* loop, int accept_fd) {
+  int fd;
+  int r;
+
+  if (loop->emfile_fd == -1)
+    return -1;
+
+  close(loop->emfile_fd);
+
+  for (;;) {
+    fd = uv__accept(accept_fd);
+
+    if (fd != -1) {
+      close(fd);
+      continue;
+    }
+
+    if (errno == EINTR)
+      continue;
+
+    if (errno == EAGAIN || errno == EWOULDBLOCK)
+      r = 0;
+    else
+      r = -1;
+
+    loop->emfile_fd = uv__open_cloexec("/", O_RDONLY);
+
+    return r;
+  }
+}
+
+
 void uv__server_io(uv_loop_t* loop, uv__io_t* w, int events) {
+  static int use_emfile_trick = -1;
+  uv_stream_t* stream;
   int fd;
-  uv_stream_t* stream = container_of(w, uv_stream_t, read_watcher);
+  int r;
 
+  stream = container_of(w, uv_stream_t, read_watcher);
   assert(events == UV__IO_READ);
   assert(!(stream->flags & UV_CLOSING));
 
@@ -389,31 +461,48 @@ void uv__server_io(uv_loop_t* loop, uv__io_t* w, int events) {
     assert(stream->accepted_fd < 0);
     fd = uv__accept(stream->fd);
 
-    if (fd < 0) {
-      if (errno == EAGAIN || errno == EWOULDBLOCK) {
-        /* No problem. */
-        return;
-      } else if (errno == EMFILE) {
-        /* TODO special trick. unlock reserved socket, accept, close. */
-        return;
-      } else if (errno == ECONNABORTED) {
-        /* ignore */
+    if (fd == -1) {
+      switch (errno) {
+#if EWOULDBLOCK != EAGAIN
+      case EWOULDBLOCK:
+#endif
+      case EAGAIN:
+        return; /* Not an error. */
+
+      case ECONNABORTED:
+        continue; /* Ignore. */
+
+      case EMFILE:
+      case ENFILE:
+        if (use_emfile_trick == -1) {
+          const char* val = getenv("UV_ACCEPT_EMFILE_TRICK");
+          use_emfile_trick = (val == NULL || atoi(val) != 0);
+        }
+
+        if (use_emfile_trick) {
+          SAVE_ERRNO(r = uv__emfile_trick(loop, stream->fd));
+          if (r == 0)
+            continue;
+        }
+
+        /* Fall through. */
+
+      default:
+        uv__set_sys_error(loop, errno);
+        stream->connection_cb(stream, -1);
         continue;
-      } else {
-        uv__set_sys_error(stream->loop, errno);
-        stream->connection_cb((uv_stream_t*)stream, -1);
-      }
-    } else {
-      stream->accepted_fd = fd;
-      stream->connection_cb(stream, 0);
-
-      if (stream->accepted_fd != -1 ||
-          (stream->type == UV_TCP && stream->flags == UV_TCP_SINGLE_ACCEPT)) {
-        /* The user hasn't yet accepted called uv_accept() */
-        uv__io_stop(stream->loop, &stream->read_watcher);
-        break;
       }
     }
+
+    stream->accepted_fd = fd;
+    stream->connection_cb(stream, 0);
+
+    if (stream->accepted_fd != -1 ||
+        (stream->type == UV_TCP && stream->flags == UV_TCP_SINGLE_ACCEPT)) {
+      /* The user hasn't yet accepted called uv_accept() */
+      uv__io_stop(loop, &stream->read_watcher);
+      break;
+    }
   }
 
   if (stream->fd != -1 &&
index 441473c..186150b 100644 (file)
@@ -247,6 +247,7 @@ int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) {
 
   if (uv_idle_init(tcp->loop, tcp->idle_handle))
     abort();
+  tcp->idle_handle->flags |= UV__HANDLE_INTERNAL;
 
   tcp->flags |= UV_TCP_SINGLE_ACCEPT;
 
@@ -331,7 +332,10 @@ int uv__tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay) {
   }
 #endif
 
-#ifdef TCP_KEEPALIVE
+  /* Solaris/SmartOS, if you don't support keep-alive,
+   * then don't advertise it in your system headers...
+   */
+#if defined(TCP_KEEPALIVE) && !defined(__sun)
   if (enable && setsockopt(handle->fd,
                            IPPROTO_TCP,
                            TCP_KEEPALIVE,
index 75dfcd8..dd91dbb 100644 (file)
@@ -314,6 +314,15 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
 }
 
 
+unsigned long uv_thread_self(void) {
+#ifdef _WIN32
+  return (unsigned long) GetCurrentThreadId();
+#else
+  return (unsigned long) pthread_self();
+#endif
+}
+
+
 void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg) {
   ngx_queue_t* q;
   uv_handle_t* h;
index 9a83ea8..a9e0e7f 100644 (file)
@@ -144,6 +144,7 @@ uv_err_code uv_translate_sys_error(int sys_errno) {
     case ERROR_BAD_PIPE:                    return UV_EPIPE;
     case ERROR_NO_DATA:                     return UV_EPIPE;
     case ERROR_PIPE_NOT_CONNECTED:          return UV_EPIPE;
+    case WSAESHUTDOWN:                      return UV_EPIPE;
     case ERROR_PIPE_BUSY:                   return UV_EBUSY;
     case ERROR_SEM_TIMEOUT:                 return UV_ETIMEDOUT;
     case WSAETIMEDOUT:                      return UV_ETIMEDOUT;
index b0d1d18..fe38890 100644 (file)
@@ -52,9 +52,8 @@
 #define UV_HANDLE_LISTENING                     0x00000800
 #define UV_HANDLE_CONNECTION                    0x00001000
 #define UV_HANDLE_CONNECTED                     0x00002000
-#define UV_HANDLE_EOF                           0x00004000
-#define UV_HANDLE_SHUTTING                      0x00008000
-#define UV_HANDLE_SHUT                          0x00010000
+#define UV_HANDLE_READABLE                      0x00008000
+#define UV_HANDLE_WRITABLE                      0x00010000
 #define UV_HANDLE_READ_PENDING                  0x00020000
 #define UV_HANDLE_SYNC_BYPASS_IOCP              0x00040000
 #define UV_HANDLE_ZERO_READ                     0x00080000
index 62cdef5..819091e 100644 (file)
@@ -114,7 +114,7 @@ static HANDLE open_named_pipe(WCHAR* name, DWORD* duplex_flags) {
                            FILE_FLAG_OVERLAPPED,
                            NULL);
   if (pipeHandle != INVALID_HANDLE_VALUE) {
-    *duplex_flags = 0;
+    *duplex_flags = UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
     return pipeHandle;
   }
 
@@ -133,7 +133,7 @@ static HANDLE open_named_pipe(WCHAR* name, DWORD* duplex_flags) {
                              NULL);
 
     if (pipeHandle != INVALID_HANDLE_VALUE) {
-      *duplex_flags = UV_HANDLE_SHUTTING;
+      *duplex_flags = UV_HANDLE_READABLE;
       return pipeHandle;
     }
   }
@@ -148,7 +148,7 @@ static HANDLE open_named_pipe(WCHAR* name, DWORD* duplex_flags) {
                              NULL);
 
     if (pipeHandle != INVALID_HANDLE_VALUE) {
-      *duplex_flags = UV_HANDLE_EOF;
+      *duplex_flags = UV_HANDLE_WRITABLE;
       return pipeHandle;
     }
   }
@@ -316,7 +316,7 @@ void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) {
       /* Failure */
       UNREGISTER_HANDLE_REQ(loop, handle, req);
 
-      handle->flags &= ~UV_HANDLE_SHUTTING;
+      handle->flags |= UV_HANDLE_WRITABLE; /* Questionable */
       if (req->cb) {
         uv__set_sys_error(loop, pRtlNtStatusToDosError(nt_status));
         req->cb(req, -1);
@@ -343,7 +343,7 @@ void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) {
       /* Failure. */
       UNREGISTER_HANDLE_REQ(loop, handle, req);
 
-      handle->flags &= ~UV_HANDLE_SHUTTING;
+      handle->flags |= UV_HANDLE_WRITABLE; /* Questionable */
       if (req->cb) {
         uv__set_sys_error(loop, GetLastError());
         req->cb(req, -1);
@@ -630,7 +630,7 @@ void uv_pipe_cleanup(uv_loop_t* loop, uv_pipe_t* handle) {
   }
 
   if (handle->flags & UV_HANDLE_CONNECTION) {
-    handle->flags |= UV_HANDLE_SHUTTING;
+    handle->flags &= ~UV_HANDLE_WRITABLE;
     eof_timer_destroy(handle);
   }
 
@@ -659,6 +659,7 @@ void uv_pipe_close(uv_loop_t* loop, uv_pipe_t* handle) {
     uv_want_endgame(loop, (uv_handle_t*) handle);
   }
 
+  handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
   uv__handle_closing(handle);
 }
 
@@ -746,6 +747,7 @@ int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client) {
     /* Initialize the client handle and copy the pipeHandle to the client */
     uv_pipe_connection_init(pipe_client);
     pipe_client->handle = req->pipeHandle;
+    pipe_client->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
 
     /* Prepare the req to pick up a new connection */
     server->pending_accepts = req->next_pending;
@@ -964,16 +966,6 @@ static int uv_pipe_read_start_impl(uv_pipe_t* handle, uv_alloc_cb alloc_cb,
     uv_read_cb read_cb, uv_read2_cb read2_cb) {
   uv_loop_t* loop = handle->loop;
 
-  if (!(handle->flags & UV_HANDLE_CONNECTION)) {
-    uv__set_artificial_error(loop, UV_EINVAL);
-    return -1;
-  }
-
-  if (handle->flags & UV_HANDLE_EOF) {
-    uv__set_artificial_error(loop, UV_EOF);
-    return -1;
-  }
-
   handle->flags |= UV_HANDLE_READING;
   INCREASE_ACTIVE_COUNT(loop, handle);
   handle->read_cb = read_cb;
@@ -1072,16 +1064,6 @@ static int uv_pipe_write_impl(uv_loop_t* loop, uv_write_t* req,
 
   assert(handle->handle != INVALID_HANDLE_VALUE);
 
-  if (!(handle->flags & UV_HANDLE_CONNECTION)) {
-    uv__set_artificial_error(loop, UV_EINVAL);
-    return -1;
-  }
-
-  if (handle->flags & UV_HANDLE_SHUTTING) {
-    uv__set_artificial_error(loop, UV_EOF);
-    return -1;
-  }
-
   uv_req_init(loop, (uv_req_t*) req);
   req->type = UV_WRITE;
   req->handle = (uv_stream_t*) handle;
@@ -1253,7 +1235,7 @@ static void uv_pipe_read_eof(uv_loop_t* loop, uv_pipe_t* handle,
   /* so discard it. */
   eof_timer_destroy(handle);
 
-  handle->flags |= UV_HANDLE_EOF;
+  handle->flags &= ~UV_HANDLE_READABLE;
   uv_read_stop((uv_stream_t*) handle);
 
   uv__set_artificial_error(loop, UV_EOF);
@@ -1483,8 +1465,8 @@ void uv_process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle,
     uv_queue_non_overlapped_write(handle);
   }
 
-  if (handle->write_reqs_pending == 0 &&
-      handle->flags & UV_HANDLE_SHUTTING) {
+  if (handle->shutdown_req != NULL &&
+      handle->write_reqs_pending == 0) {
     uv_want_endgame(loop, (uv_handle_t*)handle);
   }
 
@@ -1548,7 +1530,7 @@ void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle,
 
   /* Initialize and optionally start the eof timer. */
   /* This makes no sense if we've already seen EOF. */
-  if (!(handle->flags & UV_HANDLE_EOF)) {
+  if (handle->flags & UV_HANDLE_READABLE) {
     eof_timer_init(handle);
 
     /* If reading start the timer right now. */
@@ -1662,6 +1644,7 @@ void uv_pipe_open(uv_pipe_t* pipe, uv_file file) {
 
   uv_pipe_connection_init(pipe);
   pipe->handle = os_handle;
+  pipe->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
 
   if (pipe->ipc) {
     assert(!(pipe->flags & UV_HANDLE_NON_OVERLAPPED_PIPE));
index 7777740..89e2cd9 100644 (file)
@@ -163,6 +163,9 @@ static uv_err_t uv__create_stdio_pipe_pair(uv_loop_t* loop,
     }
   }
 
+  /* The server end is now readable and writable. */
+  server_pipe->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
+
   *child_pipe_ptr = child_pipe;
   return uv_ok_;
 
index 195e0b8..097f349 100644 (file)
@@ -55,6 +55,16 @@ int uv_accept(uv_stream_t* server, uv_stream_t* client) {
 
 int uv_read_start(uv_stream_t* handle, uv_alloc_cb alloc_cb,
     uv_read_cb read_cb) {
+  if (handle->flags & UV_HANDLE_READING) {
+    uv__set_sys_error(handle->loop, UV_EALREADY);
+    return -1;
+  }
+
+  if (!(handle->flags & UV_HANDLE_READABLE)) {
+    uv__set_artificial_error(handle->loop, UV_ENOTCONN);
+    return -1;
+  }
+
   switch (handle->type) {
     case UV_TCP:
       return uv_tcp_read_start((uv_tcp_t*)handle, alloc_cb, read_cb);
@@ -71,6 +81,16 @@ int uv_read_start(uv_stream_t* handle, uv_alloc_cb alloc_cb,
 
 int uv_read2_start(uv_stream_t* handle, uv_alloc_cb alloc_cb,
     uv_read2_cb read_cb) {
+  if (handle->flags & UV_HANDLE_READING) {
+    uv__set_sys_error(handle->loop, UV_EALREADY);
+    return -1;
+  }
+
+  if (!(handle->flags & UV_HANDLE_READABLE)) {
+    uv__set_artificial_error(handle->loop, UV_ENOTCONN);
+    return -1;
+  }
+
   switch (handle->type) {
     case UV_NAMED_PIPE:
       return uv_pipe_read2_start((uv_pipe_t*)handle, alloc_cb, read_cb);
@@ -82,14 +102,15 @@ int uv_read2_start(uv_stream_t* handle, uv_alloc_cb alloc_cb,
 
 
 int uv_read_stop(uv_stream_t* handle) {
+  if (!(handle->flags & UV_HANDLE_READING))
+    return 0;
+
   if (handle->type == UV_TTY) {
     return uv_tty_read_stop((uv_tty_t*) handle);
-  } else if (handle->flags & UV_HANDLE_READING) {
+  } else {
     handle->flags &= ~UV_HANDLE_READING;
     DECREASE_ACTIVE_COUNT(handle->loop, handle);
     return 0;
-  } else {
-    return 0;
   }
 }
 
@@ -98,6 +119,11 @@ int uv_write(uv_write_t* req, uv_stream_t* handle, uv_buf_t bufs[], int bufcnt,
     uv_write_cb cb) {
   uv_loop_t* loop = handle->loop;
 
+  if (!(handle->flags & UV_HANDLE_WRITABLE)) {
+    uv__set_artificial_error(loop, UV_EPIPE);
+    return -1;
+  }
+
   switch (handle->type) {
     case UV_TCP:
       return uv_tcp_write(loop, req, (uv_tcp_t*) handle, bufs, bufcnt, cb);
@@ -117,6 +143,11 @@ int uv_write2(uv_write_t* req, uv_stream_t* handle, uv_buf_t bufs[], int bufcnt,
     uv_stream_t* send_handle, uv_write_cb cb) {
   uv_loop_t* loop = handle->loop;
 
+  if (!(handle->flags & UV_HANDLE_WRITABLE)) {
+    uv__set_artificial_error(loop, UV_EPIPE);
+    return -1;
+  }
+
   switch (handle->type) {
     case UV_NAMED_PIPE:
       return uv_pipe_write2(loop, req, (uv_pipe_t*) handle, bufs, bufcnt, send_handle, cb);
@@ -131,13 +162,13 @@ int uv_write2(uv_write_t* req, uv_stream_t* handle, uv_buf_t bufs[], int bufcnt,
 int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) {
   uv_loop_t* loop = handle->loop;
 
 if (!(handle->flags & UV_HANDLE_CONNECTION)) {
-    uv__set_sys_error(loop, WSAEINVAL);
if (!(handle->flags & UV_HANDLE_WRITABLE)) {
+    uv__set_artificial_error(loop, UV_EPIPE);
     return -1;
   }
 
-  if (handle->flags & UV_HANDLE_SHUTTING) {
-    uv__set_sys_error(loop, WSAESHUTDOWN);
+  if (!(handle->flags & UV_HANDLE_WRITABLE)) {
+    uv__set_artificial_error(loop, UV_EPIPE);
     return -1;
   }
 
@@ -146,7 +177,7 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) {
   req->handle = handle;
   req->cb = cb;
 
-  handle->flags |= UV_HANDLE_SHUTTING;
+  handle->flags &= ~UV_HANDLE_WRITABLE;
   handle->shutdown_req = req;
   handle->reqs_pending++;
   REGISTER_HANDLE_REQ(loop, handle, req);
@@ -158,10 +189,10 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) {
 
 
 int uv_is_readable(const uv_stream_t* handle) {
-  return !(handle->flags & UV_HANDLE_EOF);
+  return !!(handle->flags & UV_HANDLE_READABLE);
 }
 
 
 int uv_is_writable(const uv_stream_t* handle) {
-  return !(handle->flags & UV_HANDLE_SHUTTING);
+  return !!(handle->flags & UV_HANDLE_WRITABLE);
 }
index 111f03c..0bc01c6 100644 (file)
@@ -169,7 +169,6 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) {
       uv__set_artificial_error(loop, UV_ECANCELED);
     } else if (shutdown(handle->socket, SD_SEND) != SOCKET_ERROR) {
       status = 0;
-      handle->flags |= UV_HANDLE_SHUT;
     } else {
       status = -1;
       uv__set_sys_error(loop, WSAGetLastError());
@@ -605,7 +604,7 @@ int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) {
   } else {
     uv_connection_init((uv_stream_t*) client);
     /* AcceptEx() implicitly binds the accepted socket. */
-    client->flags |= UV_HANDLE_BOUND;
+    client->flags |= UV_HANDLE_BOUND | UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
   }
 
   /* Prepare the req to pick up a new connection */
@@ -646,21 +645,6 @@ int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb,
     uv_read_cb read_cb) {
   uv_loop_t* loop = handle->loop;
 
-  if (!(handle->flags & UV_HANDLE_CONNECTION)) {
-    uv__set_sys_error(loop, WSAEINVAL);
-    return -1;
-  }
-
-  if (handle->flags & UV_HANDLE_READING) {
-    uv__set_sys_error(loop, WSAEALREADY);
-    return -1;
-  }
-
-  if (handle->flags & UV_HANDLE_EOF) {
-    uv__set_sys_error(loop, WSAESHUTDOWN);
-    return -1;
-  }
-
   handle->flags |= UV_HANDLE_READING;
   handle->read_cb = read_cb;
   handle->alloc_cb = alloc_cb;
@@ -855,16 +839,6 @@ int uv_tcp_write(uv_loop_t* loop, uv_write_t* req, uv_tcp_t* handle,
   int result;
   DWORD bytes;
 
-  if (!(handle->flags & UV_HANDLE_CONNECTION)) {
-    uv__set_sys_error(loop, WSAEINVAL);
-    return -1;
-  }
-
-  if (handle->flags & UV_HANDLE_SHUTTING) {
-    uv__set_sys_error(loop, WSAESHUTDOWN);
-    return -1;
-  }
-
   uv_req_init(loop, (uv_req_t*) req);
   req->type = UV_WRITE;
   req->handle = (uv_stream_t*) handle;
@@ -970,7 +944,7 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle,
           handle->flags &= ~UV_HANDLE_READING;
           DECREASE_ACTIVE_COUNT(loop, handle);
         }
-        handle->flags |= UV_HANDLE_EOF;
+        handle->flags &= ~UV_HANDLE_READABLE;
 
         uv__set_error(loop, UV_EOF, ERROR_SUCCESS);
         buf.base = 0;
@@ -1001,9 +975,8 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle,
           }
         } else {
           /* Connection closed */
-          handle->flags &= ~UV_HANDLE_READING;
+          handle->flags &= ~(UV_HANDLE_READING | UV_HANDLE_READABLE);
           DECREASE_ACTIVE_COUNT(loop, handle);
-          handle->flags |= UV_HANDLE_EOF;
 
           uv__set_error(loop, UV_EOF, ERROR_SUCCESS);
           handle->read_cb((uv_stream_t*)handle, -1, buf);
@@ -1070,7 +1043,7 @@ void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle,
   }
 
   handle->write_reqs_pending--;
-  if (handle->flags & UV_HANDLE_SHUTTING &&
+  if (handle->shutdown_req != NULL &&
       handle->write_reqs_pending == 0) {
     uv_want_endgame(loop, (uv_handle_t*)handle);
   }
@@ -1139,6 +1112,7 @@ void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle,
                     NULL,
                     0) == 0) {
       uv_connection_init((uv_stream_t*)handle);
+      handle->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
       loop->active_tcp_streams++;
       ((uv_connect_cb)req->cb)(req, 0);
     } else {
@@ -1181,6 +1155,7 @@ int uv_tcp_import(uv_tcp_t* tcp, WSAPROTOCOL_INFOW* socket_protocol_info,
 
   if (tcp_connection) {
     uv_connection_init((uv_stream_t*)tcp);
+    tcp->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE;
   }
 
   tcp->flags |= UV_HANDLE_BOUND;
@@ -1342,7 +1317,6 @@ void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) {
     if (!(tcp->flags & UV_HANDLE_SHARED_TCP_SOCKET)) {
       /* Just do shutdown on non-shared sockets, which ensures graceful close. */
       shutdown(tcp->socket, SD_SEND);
-      tcp->flags |= UV_HANDLE_SHUT;
 
     } else if (uv_tcp_try_cancel_io(tcp) == 0) {
       /* In case of a shared socket, we try to cancel all outstanding I/O, */
@@ -1397,6 +1371,7 @@ void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) {
     tcp->flags |= UV_HANDLE_TCP_SOCKET_CLOSED;
   }
 
+  tcp->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
   uv__handle_closing(tcp);
 
   if (tcp->reqs_pending == 0) {
index 19edf7d..7a84506 100644 (file)
@@ -140,7 +140,7 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int readable) {
   if (readable) {
     /* Initialize TTY input specific fields. */
     tty->original_console_mode = original_console_mode;
-    tty->flags |= UV_HANDLE_TTY_READABLE;
+    tty->flags |= UV_HANDLE_TTY_READABLE | UV_HANDLE_READABLE;
     tty->read_line_handle = NULL;
     tty->read_line_buffer = uv_null_buf_;
     tty->read_raw_wait = NULL;
@@ -152,6 +152,8 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int readable) {
     memset(&tty->last_input_record, 0, sizeof tty->last_input_record);
   } else {
     /* TTY output specific fields. */
+    tty->flags |= UV_HANDLE_READABLE;
+
     /* Init utf8-to-utf16 conversion state. */
     tty->utf8_bytes_left = 0;
     tty->utf8_codepoint = 0;
@@ -826,15 +828,9 @@ int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb,
 
 int uv_tty_read_stop(uv_tty_t* handle) {
   uv_loop_t* loop = handle->loop;
-  if (!(handle->flags & UV_HANDLE_TTY_READABLE)) {
-    uv__set_artificial_error(handle->loop, UV_EINVAL);
-    return -1;
-  }
 
-  if (handle->flags & UV_HANDLE_READING) {
-    handle->flags &= ~UV_HANDLE_READING;
-    DECREASE_ACTIVE_COUNT(loop, handle);
-  }
+  handle->flags &= ~UV_HANDLE_READING;
+  DECREASE_ACTIVE_COUNT(loop, handle);
 
   /* Cancel raw read */
   if ((handle->flags & UV_HANDLE_READ_PENDING) &&
@@ -1750,17 +1746,6 @@ int uv_tty_write(uv_loop_t* loop, uv_write_t* req, uv_tty_t* handle,
     uv_buf_t bufs[], int bufcnt, uv_write_cb cb) {
   DWORD error;
 
-  if (handle->flags & UV_HANDLE_TTY_READABLE) {
-    uv__set_artificial_error(handle->loop, UV_EINVAL);
-    return -1;
-  }
-
-  if ((handle->flags & UV_HANDLE_SHUTTING) ||
-      (handle->flags & UV_HANDLE_CLOSING)) {
-    uv__set_sys_error(loop, WSAESHUTDOWN);
-    return -1;
-  }
-
   uv_req_init(loop, (uv_req_t*) req);
   req->type = UV_WRITE;
   req->handle = (uv_stream_t*) handle;
@@ -1796,7 +1781,7 @@ void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
   }
 
   handle->write_reqs_pending--;
-  if (handle->flags & UV_HANDLE_SHUTTING &&
+  if (handle->shutdown_req != NULL &&
       handle->write_reqs_pending == 0) {
     uv_want_endgame(loop, (uv_handle_t*)handle);
   }
@@ -1808,14 +1793,10 @@ void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle,
 void uv_tty_close(uv_tty_t* handle) {
   CloseHandle(handle->handle);
 
-  if (handle->flags & UV_HANDLE_TTY_READABLE) {
-    /* Readable TTY handle */
+  if (handle->flags & UV_HANDLE_READING)
     uv_tty_read_stop(handle);
-  } else {
-    /* Writable TTY handle */
-    handle->flags |= UV_HANDLE_SHUTTING;
-  }
 
+  handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE);
   uv__handle_closing(handle);
 
   if (handle->reqs_pending == 0) {
index 4d4cfbc..257d64c 100644 (file)
@@ -98,7 +98,8 @@ static void fs_event_cb_dir(uv_fs_event_t* handle, const char* filename,
   ASSERT(handle == &fs_event);
   ASSERT(status == 0);
   ASSERT(events == UV_RENAME);
-  ASSERT(filename == NULL || strcmp(filename, "file1") == 0);
+  ASSERT(filename == NULL || strcmp(filename, "file1") == 0 ||
+         strstr(filename, "watch_dir") != NULL);
   uv_close((uv_handle_t*)handle, close_cb);
 }
 
index 8ce01eb..e44087a 100644 (file)
           'libraries': [ '-lm' ]
         }],
         [ 'OS=="mac"', {
-          'sources': [ 'src/unix/darwin.c' ],
+          'sources': [ 'src/unix/darwin.c', 'src/unix/fsevents.c' ],
           'direct_dependent_settings': {
             'libraries': [
               '$(SDKROOT)/System/Library/Frameworks/CoreServices.framework',
             'EIO_CONFIG_H="config_openbsd.h"',
           ],
         }],
+        [ 'OS=="netbsd"', {
+          'sources': [ 'src/unix/netbsd.c' ],
+          'defines': [
+            'EV_CONFIG_H="config_netbsd.h"',
+            'EIO_CONFIG_H="config_netbsd.h"',
+          ],
+          'direct_dependent_settings': {
+            'libraries': [
+              '-lkvm',
+            ],
+          },
+        }],
         [ 'OS=="mac" or OS=="freebsd" or OS=="openbsd" or OS=="netbsd"', {
           'sources': [ 'src/unix/kqueue.c' ],
         }],