uv: Upgrade to v0.10.18
authorTimothy J Fontaine <tjfontaine@gmail.com>
Fri, 18 Oct 2013 20:52:15 +0000 (13:52 -0700)
committerTimothy J Fontaine <tjfontaine@gmail.com>
Fri, 18 Oct 2013 20:52:15 +0000 (13:52 -0700)
deps/uv/ChangeLog
deps/uv/include/uv-private/uv-darwin.h
deps/uv/src/unix/darwin.c
deps/uv/src/unix/fsevents.c
deps/uv/src/unix/internal.h
deps/uv/src/unix/kqueue.c
deps/uv/src/unix/process.c
deps/uv/src/unix/signal.c
deps/uv/src/version.c

index 98fb44f..794764e 100644 (file)
@@ -1,4 +1,17 @@
-2013.09.25, Version 0.10.17 (Stable)
+2013.10.19, Version 0.10.18 (Stable)
+
+Changes since version 0.10.17:
+
+* unix: fix uv_spawn() NULL pointer deref on ENOMEM (Ben Noordhuis)
+
+* unix: don't close inherited fds on uv_spawn() fail (Ben Noordhuis)
+
+* unix: revert recent FSEvent changes (Ben Noordhuis)
+
+* unix: fix non-synchronized access in signal.c (Ben Noordhuis)
+
+
+2013.09.25, Version 0.10.17 (Stable), 9670e0a93540c2f0d86c84a375f2303383c11e7e
 
 Changes since version 0.10.16:
 
index 4037f5c..e861cba 100644 (file)
@@ -36,8 +36,8 @@
 
 #define UV_PLATFORM_LOOP_FIELDS                                               \
   uv_thread_t cf_thread;                                                      \
-  void* _cf_reserved;                                                         \
-  void* cf_state;                                                             \
+  void* cf_cb;                                                                \
+  void* cf_loop;                                                              \
   uv_mutex_t cf_mutex;                                                        \
   uv_sem_t cf_sem;                                                            \
   ngx_queue_t cf_signals;                                                     \
   char* realpath;                                                             \
   int realpath_len;                                                           \
   int cf_flags;                                                               \
-  void* cf_event;                                                             \
+  void* cf_eventstream;                                                       \
   uv_async_t* cf_cb;                                                          \
-  ngx_queue_t cf_member;                                                      \
-  uv_sem_t _cf_reserved;                                                      \
+  ngx_queue_t cf_events;                                                      \
+  uv_sem_t cf_sem;                                                            \
   uv_mutex_t cf_mutex;                                                        \
 
 #define UV_STREAM_PRIVATE_PLATFORM_FIELDS                                     \
index 08da513..77e662f 100644 (file)
@@ -28,6 +28,8 @@
 #include <ifaddrs.h>
 #include <net/if.h>
 
+#include <CoreFoundation/CFRunLoop.h>
+
 #include <mach/mach.h>
 #include <mach/mach_time.h>
 #include <mach-o/dyld.h> /* _NSGetExecutablePath */
 #include <sys/sysctl.h>
 #include <unistd.h>  /* sysconf */
 
+/* Forward declarations */
+static void uv__cf_loop_runner(void* arg);
+static 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) {
-  loop->cf_state = NULL;
+  CFRunLoopSourceContext ctx;
+  int r;
 
   if (uv__kqueue_init(loop))
     return -1;
 
+  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(ACCESS_ONCE(CFRunLoopRef, loop->cf_loop) != NULL);
+
   return 0;
 }
 
 
 void uv__platform_loop_delete(uv_loop_t* loop) {
-  uv__fsevents_loop_delete(loop);
+  ngx_queue_t* item;
+  uv__cf_loop_signal_t* s;
+
+  assert(loop->cf_loop != NULL);
+  uv__cf_loop_signal(loop, NULL, NULL);
+  uv_thread_join(&loop->cf_thread);
+
+  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);
+  }
+}
+
+
+static void uv__cf_loop_runner(void* arg) {
+  uv_loop_t* loop;
+
+  loop = arg;
+
+  /* Get thread's loop */
+  ACCESS_ONCE(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);
+}
+
+
+static 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);
+
+    /* This was a termination signal */
+    if (s->cb == NULL)
+      CFRunLoopStop(loop->cf_loop);
+    else
+      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);
 }
 
 
index 7636b80..b6d2746 100644 (file)
@@ -34,149 +34,112 @@ int uv__fsevents_close(uv_fs_event_t* handle) {
   return 0;
 }
 
-
-void uv__fsevents_loop_delete(uv_loop_t* loop) {
-  return 0;
-}
-
 #else /* TARGET_OS_IPHONE */
 
 #include <assert.h>
 #include <stdlib.h>
-#include <CoreFoundation/CFRunLoop.h>
 #include <CoreServices/CoreServices.h>
 
-/* These are macros to avoid "initializer element is not constant" errors
- * with old versions of gcc.
- */
-#define kFSEventsModified (kFSEventStreamEventFlagItemFinderInfoMod |         \
-                           kFSEventStreamEventFlagItemModified |              \
-                           kFSEventStreamEventFlagItemInodeMetaMod |          \
-                           kFSEventStreamEventFlagItemChangeOwner |           \
-                           kFSEventStreamEventFlagItemXattrMod)
-
-#define kFSEventsRenamed  (kFSEventStreamEventFlagItemCreated |               \
-                           kFSEventStreamEventFlagItemRemoved |               \
-                           kFSEventStreamEventFlagItemRenamed)
-
-#define kFSEventsSystem   (kFSEventStreamEventFlagUserDropped |               \
-                           kFSEventStreamEventFlagKernelDropped |             \
-                           kFSEventStreamEventFlagEventIdsWrapped |           \
-                           kFSEventStreamEventFlagHistoryDone |               \
-                           kFSEventStreamEventFlagMount |                     \
-                           kFSEventStreamEventFlagUnmount |                   \
-                           kFSEventStreamEventFlagRootChanged)
-
 typedef struct uv__fsevents_event_s uv__fsevents_event_t;
-typedef struct uv__cf_loop_signal_s uv__cf_loop_signal_t;
-typedef struct uv__cf_loop_state_s uv__cf_loop_state_t;
-
-struct uv__cf_loop_state_s {
-  CFRunLoopRef loop;
-  CFRunLoopSourceRef signal_source;
-  volatile int fsevent_need_reschedule;
-  FSEventStreamRef fsevent_stream;
-  uv_sem_t fsevent_sem;
-  uv_mutex_t fsevent_mutex;
-  ngx_queue_t fsevent_handles;
-  int fsevent_handle_count;
-};
-
-struct uv__cf_loop_signal_s {
-  ngx_queue_t member;
-  uv_fs_event_t* handle;
-};
 
 struct uv__fsevents_event_s {
   int events;
-  void* next;
+  ngx_queue_t member;
   char path[1];
 };
 
-/* Forward declarations */
-static void uv__cf_loop_cb(void* arg);
-static void* uv__cf_loop_runner(void* arg);
-static int uv__cf_loop_signal(uv_loop_t* loop, uv_fs_event_t* handle);
 
-#define UV__FSEVENTS_PROCESS(handle, block)                                   \
-    do {                                                                      \
+#define UV__FSEVENTS_WALK(handle, block)                                      \
+    {                                                                         \
+      ngx_queue_t* curr;                                                      \
+      ngx_queue_t split_head;                                                 \
       uv__fsevents_event_t* event;                                            \
-      uv__fsevents_event_t* next;                                             \
       uv_mutex_lock(&(handle)->cf_mutex);                                     \
-      event = (handle)->cf_event;                                             \
-      (handle)->cf_event = NULL;                                              \
+      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 (event != NULL) {                                                 \
+      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 (!uv__is_closing((handle)))                                        \
+        if (((handle)->flags & (UV_CLOSING | UV_CLOSED)) == 0)                \
           block                                                               \
         /* Free allocated data */                                             \
-        next = event->next;                                                   \
         free(event);                                                          \
-        event = next;                                                         \
       }                                                                       \
-    } while (0)
+    }
 
 
-/* Runs in UV loop's thread, when there're events to report to handle */
-static void uv__fsevents_cb(uv_async_t* cb, int status) {
+void uv__fsevents_cb(uv_async_t* cb, int status) {
   uv_fs_event_t* handle;
 
   handle = cb->data;
 
-  UV__FSEVENTS_PROCESS(handle, {
+  UV__FSEVENTS_WALK(handle, {
     if (handle->event_watcher.fd != -1)
       handle->cb(handle, event->path[0] ? event->path : NULL, event->events, 0);
   });
 
-  if (!uv__is_closing(handle) && handle->event_watcher.fd == -1)
+  if ((handle->flags & (UV_CLOSING | UV_CLOSED)) == 0 &&
+      handle->event_watcher.fd == -1) {
     uv__fsevents_close(handle);
+  }
 }
 
 
-/* Runs in CF thread, when there're events in FSEventStream */
-static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef,
-                                  void* info,
-                                  size_t numEvents,
-                                  void* eventPaths,
-                                  const FSEventStreamEventFlags eventFlags[],
-                                  const FSEventStreamEventId eventIds[]) {
+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;
   char* path;
   char* pos;
   uv_fs_event_t* handle;
-  ngx_queue_t* q;
-  uv_loop_t* loop;
-  uv__cf_loop_state_t* state;
   uv__fsevents_event_t* event;
-  uv__fsevents_event_t* tail;
-
-  loop = info;
-  state = loop->cf_state;
-  assert(state != NULL);
+  ngx_queue_t add_list;
+  int kFSEventsModified;
+  int kFSEventsRenamed;
+
+  kFSEventsModified = kFSEventStreamEventFlagItemFinderInfoMod |
+                      kFSEventStreamEventFlagItemModified |
+                      kFSEventStreamEventFlagItemInodeMetaMod |
+                      kFSEventStreamEventFlagItemChangeOwner |
+                      kFSEventStreamEventFlagItemXattrMod;
+  kFSEventsRenamed = kFSEventStreamEventFlagItemCreated |
+                     kFSEventStreamEventFlagItemRemoved |
+                     kFSEventStreamEventFlagItemRenamed;
+
+  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 |
+                         kFSEventStreamEventFlagRootChanged)) {
+      continue;
+    }
 
-  /* For each handle */
-  ngx_queue_foreach(q, &state->fsevent_handles) {
-    handle = ngx_queue_data(q, uv_fs_event_t, cf_member);
-    tail = NULL;
-
-    /* Process and filter out events */
-    for (i = 0; i < numEvents; i++) {
-      /* Ignore system events */
-      if (eventFlags[i] & kFSEventsSystem)
-        continue;
-
-      path = paths[i];
-      len = strlen(path);
-
-      /* Filter out paths that are outside handle's request */
-      if (strncmp(path, handle->realpath, handle->realpath_len) != 0)
-        continue;
+    /* TODO: Report errors */
+    path = paths[i];
+    len = strlen(path);
 
+    /* Remove absolute path prefix */
+    if (strstr(path, handle->realpath) == path) {
       path += handle->realpath_len;
       len -= handle->realpath_len;
 
@@ -185,81 +148,91 @@ static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef,
         path++;
         len--;
       }
+    }
 
 #ifdef MAC_OS_X_VERSION_10_7
-      /* Ignore events with path equal to directory itself */
-      if (len == 0)
-        continue;
+    /* Ignore events with path equal to directory itself */
+    if (len == 0)
+      continue;
 #endif /* MAC_OS_X_VERSION_10_7 */
 
-      /* Do not emit events from subdirectories (without option set) */
-      if ((handle->cf_flags & UV_FS_EVENT_RECURSIVE) == 0) {
-        pos = strchr(path, '/');
-        if (pos != NULL && pos != path + 1)
-          continue;
-      }
+    /* Do not emit events from subdirectories (without option set) */
+    pos = strchr(path, '/');
+    if ((handle->cf_flags & UV_FS_EVENT_RECURSIVE) == 0 &&
+        pos != NULL &&
+        pos != path + 1)
+      continue;
 
 #ifndef MAC_OS_X_VERSION_10_7
-      path = "";
-      len = 0;
+    path = "";
+    len = 0;
 #endif /* MAC_OS_X_VERSION_10_7 */
 
-      event = malloc(sizeof(*event) + len);
-      if (event == NULL)
-        break;
+    event = malloc(sizeof(*event) + len);
+    if (event == NULL)
+      break;
 
-      memset(event, 0, sizeof(*event));
-      memcpy(event->path, path, len + 1);
+    memcpy(event->path, path, len + 1);
 
-      if ((eventFlags[i] & kFSEventsModified) != 0 &&
-          (eventFlags[i] & kFSEventsRenamed) == 0)
-        event->events = UV_CHANGE;
-      else
-        event->events = UV_RENAME;
+    if ((eventFlags[i] & kFSEventsModified) != 0 &&
+        (eventFlags[i] & kFSEventsRenamed) == 0)
+      event->events = UV_CHANGE;
+    else
+      event->events = UV_RENAME;
 
-      if (tail != NULL)
-        tail->next = event;
-      tail = event;
-    }
+    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);
 
-    if (tail != NULL) {
-      uv_mutex_lock(&handle->cf_mutex);
-      tail->next = handle->cf_event;
-      handle->cf_event = tail;
-      uv_mutex_unlock(&handle->cf_mutex);
+  uv_async_send(handle->cf_cb);
+}
 
-      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);
 }
 
 
-/* Runs in CF thread */
-static void uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) {
-  uv__cf_loop_state_t* state;
+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 = loop;
+  ctx.info = handle;
   ctx.retain = NULL;
   ctx.release = NULL;
   ctx.copyDescription = NULL;
 
+  /* Get absolute path to file */
+  handle->realpath = realpath(handle->filename, NULL);
+  if (handle->realpath != NULL)
+    handle->realpath_len = strlen(handle->realpath);
+
+  /* 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;
 
-  /*
-   * NOTE: It might sound like a good idea to remember last seen StreamEventId,
-   * but in reality one dir might have last StreamEventId less than, the other,
-   * that is being watched now. Which will cause FSEventStream API to report
-   * changes to files from the past.
-   */
   ref = FSEventStreamCreate(NULL,
                             &uv__fsevents_event_cb,
                             &ctx,
@@ -267,419 +240,55 @@ static void uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) {
                             kFSEventStreamEventIdSinceNow,
                             latency,
                             flags);
-  assert(ref != NULL);
-
-  state = loop->cf_state;
-  FSEventStreamScheduleWithRunLoop(ref,
-                                   state->loop,
-                                   kCFRunLoopDefaultMode);
-  if (!FSEventStreamStart(ref))
-    abort();
-
-  state->fsevent_stream = ref;
-}
-
-
-/* Runs in CF thread */
-static void uv__fsevents_destroy_stream(uv_loop_t* loop) {
-  uv__cf_loop_state_t* state;
-
-  state = loop->cf_state;
-
-  if (state->fsevent_stream == NULL)
-    return;
-
-  /* Flush all accumulated events */
-  FSEventStreamFlushSync(state->fsevent_stream);
-
-  /* Stop emitting events */
-  FSEventStreamStop(state->fsevent_stream);
-
-  /* Release stream */
-  FSEventStreamInvalidate(state->fsevent_stream);
-  FSEventStreamRelease(state->fsevent_stream);
-  state->fsevent_stream = NULL;
-}
-
-
-/* Runs in CF thread, when there're new fsevent handles to add to stream */
-static void uv__fsevents_reschedule(uv_fs_event_t* handle) {
-  uv__cf_loop_state_t* state;
-  ngx_queue_t* q;
-  uv_fs_event_t* curr;
-  CFArrayRef cf_paths;
-  CFStringRef* paths;
-  int i;
-  int path_count;
-
-  state = handle->loop->cf_state;
-
-  /* Optimization to prevent O(n^2) time spent when starting to watch
-   * many files simultaneously
-   */
-  if (!state->fsevent_need_reschedule)
-    return;
-  state->fsevent_need_reschedule = 0;
-
-  /* Destroy previous FSEventStream */
-  uv__fsevents_destroy_stream(handle->loop);
-
-  /* Create list of all watched paths */
-  uv_mutex_lock(&state->fsevent_mutex);
-  path_count = state->fsevent_handle_count;
-  if (path_count != 0) {
-    paths = malloc(sizeof(*paths) * path_count);
-    if (paths == NULL)
-      abort();
-
-    q = &state->fsevent_handles;
-    for (i = 0; i < path_count; i++) {
-      q = ngx_queue_next(q);
-      assert(q != &state->fsevent_handles);
-      curr = ngx_queue_data(q, uv_fs_event_t, cf_member);
-
-      assert(curr->realpath != NULL);
-      paths[i] = CFStringCreateWithCString(NULL,
-                                           curr->realpath,
-                                           CFStringGetSystemEncoding());
-      if (paths[i] == NULL)
-        abort();
-    }
-  }
-  uv_mutex_unlock(&state->fsevent_mutex);
-
-  if (path_count != 0) {
-    /* Create new FSEventStream */
-    cf_paths = CFArrayCreate(NULL, (const void**) paths, path_count, NULL);
-    if (cf_paths == NULL)
-      abort();
-    uv__fsevents_create_stream(handle->loop, cf_paths);
-  }
-
-  /*
-   * Main thread will block until the removal of handle from the list,
-   * we must tell it when we're ready.
-   *
-   * NOTE: This is coupled with `uv_sem_wait()` in `uv__fsevents_close`
-   */
-  if (uv__is_closing(handle))
-    uv_sem_post(&state->fsevent_sem);
-}
-
-
-/* Runs in UV loop */
-static int uv__fsevents_loop_init(uv_loop_t* loop) {
-  CFRunLoopSourceContext ctx;
-  uv__cf_loop_state_t* state;
-  pthread_attr_t attr_storage;
-  pthread_attr_t* attr;
-  int err;
-
-  if (loop->cf_state != NULL)
-    return 0;
-
-  state = calloc(1, sizeof(*state));
-  if (state == NULL)
-    return -ENOMEM;
-
-  err = uv_mutex_init(&loop->cf_mutex);
-  if (err)
-    return err;
-
-  err = uv_sem_init(&loop->cf_sem, 0);
-  if (err)
-    goto fail_sem_init;
-
-  ngx_queue_init(&loop->cf_signals);
-
-  err = uv_sem_init(&state->fsevent_sem, 0);
-  if (err)
-    goto fail_fsevent_sem_init;
-
-  err = uv_mutex_init(&state->fsevent_mutex);
-  if (err)
-    goto fail_fsevent_mutex_init;
-
-  ngx_queue_init(&state->fsevent_handles);
-  state->fsevent_need_reschedule = 0;
-  state->fsevent_handle_count = 0;
-
-  memset(&ctx, 0, sizeof(ctx));
-  ctx.info = loop;
-  ctx.perform = uv__cf_loop_cb;
-  state->signal_source = CFRunLoopSourceCreate(NULL, 0, &ctx);
-  if (state->signal_source == NULL) {
-    err = -ENOMEM;
-    goto fail_signal_source_create;
-  }
-
-  /* In the unlikely event that pthread_attr_init() fails, create the thread
-   * with the default stack size. We'll use a little more address space but
-   * that in itself is not a fatal error.
-   */
-  attr = &attr_storage;
-  if (pthread_attr_init(attr))
-    attr = NULL;
-
-  if (attr != NULL)
-    if (pthread_attr_setstacksize(attr, 3 * PTHREAD_STACK_MIN))
-      abort();
-
-  loop->cf_state = state;
-
-  /* uv_thread_t is an alias for pthread_t. */
-  err = -pthread_create(&loop->cf_thread, attr, uv__cf_loop_runner, loop);
-
-  if (attr != NULL)
-    pthread_attr_destroy(attr);
-
-  if (err)
-    goto fail_thread_create;
-
-  /* Synchronize threads */
-  uv_sem_wait(&loop->cf_sem);
-  return 0;
-
-fail_thread_create:
-  loop->cf_state = NULL;
-
-fail_signal_source_create:
-  uv_mutex_destroy(&state->fsevent_mutex);
-
-fail_fsevent_mutex_init:
-  uv_sem_destroy(&state->fsevent_sem);
-
-fail_fsevent_sem_init:
-  uv_sem_destroy(&loop->cf_sem);
-
-fail_sem_init:
-  uv_mutex_destroy(&loop->cf_mutex);
-  free(state);
-  return err;
-}
-
-
-/* Runs in UV loop */
-void uv__fsevents_loop_delete(uv_loop_t* loop) {
-  uv__cf_loop_signal_t* s;
-  uv__cf_loop_state_t* state;
-  ngx_queue_t* q;
-
-  if (loop->cf_state == NULL)
-    return;
-
-  if (uv__cf_loop_signal(loop, NULL) != 0)
-    abort();
-
-  uv_thread_join(&loop->cf_thread);
-  uv_sem_destroy(&loop->cf_sem);
-  uv_mutex_destroy(&loop->cf_mutex);
-
-  /* Free any remaining data */
-  while (!ngx_queue_empty(&loop->cf_signals)) {
-    q = ngx_queue_head(&loop->cf_signals);
-    s = ngx_queue_data(q, uv__cf_loop_signal_t, member);
-    ngx_queue_remove(q);
-    free(s);
-  }
-
-  /* Destroy state */
-  state = loop->cf_state;
-  uv_sem_destroy(&state->fsevent_sem);
-  uv_mutex_destroy(&state->fsevent_mutex);
-  CFRelease(state->signal_source);
-  free(state);
-  loop->cf_state = NULL;
-}
-
-
-/* Runs in CF thread. This is the CF loop's body */
-static void* uv__cf_loop_runner(void* arg) {
-  uv_loop_t* loop;
-  uv__cf_loop_state_t* state;
-
-  loop = arg;
-  state = loop->cf_state;
-  state->loop = CFRunLoopGetCurrent();
-
-  CFRunLoopAddSource(state->loop,
-                     state->signal_source,
-                     kCFRunLoopDefaultMode);
-
-  uv_sem_post(&loop->cf_sem);
-
-  CFRunLoopRun();
-  CFRunLoopRemoveSource(state->loop,
-                        state->signal_source,
-                        kCFRunLoopDefaultMode);
-
-  return NULL;
-}
-
-
-/* Runs in CF thread, executed after `uv__cf_loop_signal()` */
-static void uv__cf_loop_cb(void* arg) {
-  uv_loop_t* loop;
-  uv__cf_loop_state_t* state;
-  ngx_queue_t* item;
-  ngx_queue_t split_head;
-  uv__cf_loop_signal_t* s;
-
-  loop = arg;
-  state = loop->cf_state;
-  ngx_queue_init(&split_head);
-
-  uv_mutex_lock(&loop->cf_mutex);
-  if (!ngx_queue_empty(&loop->cf_signals)) {
-    ngx_queue_t* split_pos = ngx_queue_head(&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);
-
-    /* This was a termination signal */
-    if (s->handle == NULL)
-      CFRunLoopStop(state->loop);
-    else
-      uv__fsevents_reschedule(s->handle);
-
-    ngx_queue_remove(item);
-    free(s);
-  }
-}
-
-
-/* Runs in UV loop to notify CF thread */
-int uv__cf_loop_signal(uv_loop_t* loop, uv_fs_event_t* handle) {
-  uv__cf_loop_signal_t* item;
-  uv__cf_loop_state_t* state;
-
-  item = malloc(sizeof(*item));
-  if (item == NULL)
-    return -ENOMEM;
-
-  item->handle = handle;
-
-  uv_mutex_lock(&loop->cf_mutex);
-  ngx_queue_insert_tail(&loop->cf_signals, &item->member);
-  uv_mutex_unlock(&loop->cf_mutex);
-
-  state = loop->cf_state;
-  assert(state != NULL);
-  CFRunLoopSourceSignal(state->signal_source);
-  CFRunLoopWakeUp(state->loop);
-
-  return 0;
-}
-
-
-/* Runs in UV loop to initialize handle */
-int uv__fsevents_init(uv_fs_event_t* handle) {
-  int err;
-  uv__cf_loop_state_t* state;
-
-  err = uv__fsevents_loop_init(handle->loop);
-  if (err)
-    return err;
-
-  /* Get absolute path to file */
-  handle->realpath = realpath(handle->filename, NULL);
-  if (handle->realpath == NULL)
-    return -errno;
-  handle->realpath_len = strlen(handle->realpath);
-
-  /* Initialize singly-linked list */
-  handle->cf_event = NULL;
+  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) {
-    err = uv__set_sys_error(handle->loop, ENOMEM);
-    goto fail_cf_cb_malloc;
-  }
+  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);
 
-  err = uv_mutex_init(&handle->cf_mutex);
-  if (err)
-    goto fail_cf_mutex_init;
+  uv_mutex_init(&handle->cf_mutex);
+  uv_sem_init(&handle->cf_sem, 0);
+  ngx_queue_init(&handle->cf_events);
 
-  /* Insert handle into the list */
-  state = handle->loop->cf_state;
-  uv_mutex_lock(&state->fsevent_mutex);
-  ngx_queue_insert_tail(&state->fsevent_handles, &handle->cf_member);
-  state->fsevent_handle_count++;
-  state->fsevent_need_reschedule = 1;
-  uv_mutex_unlock(&state->fsevent_mutex);
-
-  /* Reschedule FSEventStream */
-  assert(handle != NULL);
-  err = uv__cf_loop_signal(handle->loop, handle);
-  if (err)
-    goto fail_loop_signal;
+  uv__cf_loop_signal(handle->loop, uv__fsevents_schedule, handle);
 
   return 0;
-
-fail_loop_signal:
-  uv_mutex_destroy(&handle->cf_mutex);
-
-fail_cf_mutex_init:
-  free(handle->cf_cb);
-  handle->cf_cb = NULL;
-
-fail_cf_cb_malloc:
-  free(handle->realpath);
-  handle->realpath = NULL;
-  handle->realpath_len = 0;
-
-  return err;
 }
 
 
-/* Runs in UV loop to de-initialize handle */
 int uv__fsevents_close(uv_fs_event_t* handle) {
-  int err;
-  uv__cf_loop_state_t* state;
+  if (handle->cf_eventstream == NULL)
+    return -1;
 
-  if (handle->cf_cb == NULL)
-    return -EINVAL;
+  /* Ensure that event stream was scheduled */
+  uv_sem_wait(&handle->cf_sem);
 
-  /* Remove handle from  the list */
-  state = handle->loop->cf_state;
-  uv_mutex_lock(&state->fsevent_mutex);
-  ngx_queue_remove(&handle->cf_member);
-  state->fsevent_handle_count--;
-  state->fsevent_need_reschedule = 1;
-  uv_mutex_unlock(&state->fsevent_mutex);
-
-  /* Reschedule FSEventStream */
-  assert(handle != NULL);
-  err = uv__cf_loop_signal(handle->loop, handle);
-  if (err)
-    return -err;
+  /* Stop emitting events */
+  FSEventStreamStop(handle->cf_eventstream);
 
-  /* Wait for deinitialization */
-  uv_sem_wait(&state->fsevent_sem);
+  /* 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);
-  handle->cf_cb = NULL;
 
   /* Free data in queue */
-  UV__FSEVENTS_PROCESS(handle, {
+  UV__FSEVENTS_WALK(handle, {
     /* NOP */
-  });
+  })
 
   uv_mutex_destroy(&handle->cf_mutex);
+  uv_sem_destroy(&handle->cf_sem);
   free(handle->realpath);
   handle->realpath = NULL;
   handle->realpath_len = 0;
index 2bb4dc1..61cb1ec 100644 (file)
@@ -216,10 +216,12 @@ 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);
-void uv__fsevents_loop_delete(uv_loop_t* loop);
 
 /* OSX < 10.7 has no file events, polyfill them */
 #ifndef MAC_OS_X_VERSION_10_7
index a46c88f..378903a 100644 (file)
@@ -307,7 +307,7 @@ int uv_fs_event_init(uv_loop_t* loop,
 
 #if defined(__APPLE__)
   /* Nullify field to perform checks later */
-  handle->cf_cb = NULL;
+  handle->cf_eventstream = NULL;
   handle->realpath = NULL;
   handle->realpath_len = 0;
   handle->cf_flags = flags;
index 7ef84d0..389817f 100644 (file)
@@ -186,7 +186,7 @@ skip:
 
 /*
  * Used for initializing stdio streams like options.stdin_stream. Returns
- * zero on success.
+ * zero on success. See also the cleanup section in uv_spawn().
  */
 static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) {
   int mask;
@@ -463,11 +463,18 @@ int uv_spawn(uv_loop_t* loop,
 error:
   uv__set_sys_error(process->loop, errno);
 
-  for (i = 0; i < stdio_count; i++) {
-    close(pipes[i][0]);
-    close(pipes[i][1]);
+  if (pipes != NULL) {
+    for (i = 0; i < stdio_count; i++) {
+      if (i < options.stdio_count)
+        if (options.stdio[i].flags & (UV_INHERIT_FD | UV_INHERIT_STREAM))
+          continue;
+      if (pipes[i][0] != -1)
+        close(pipes[i][0]);
+      if (pipes[i][1] != -1)
+        close(pipes[i][1]);
+    }
+    free(pipes);
   }
-  free(pipes);
 
   return -1;
 }
index 22c7783..aa84ff2 100644 (file)
@@ -141,7 +141,10 @@ static void uv__signal_handler(int signum) {
   saved_errno = errno;
   memset(&msg, 0, sizeof msg);
 
-  uv__signal_lock();
+  if (uv__signal_lock()) {
+    errno = saved_errno;
+    return;
+  }
 
   for (handle = uv__signal_first_handle(signum);
        handle != NULL && handle->signum == signum;
index 5489601..e272d5f 100644 (file)
@@ -34,7 +34,7 @@
 
 #define UV_VERSION_MAJOR 0
 #define UV_VERSION_MINOR 10
-#define UV_VERSION_PATCH 17
+#define UV_VERSION_PATCH 18
 #define UV_VERSION_IS_RELEASE 1