summaryrefslogtreecommitdiff
path: root/deps/uv
diff options
context:
space:
mode:
authorBert Belder <bertbelder@gmail.com>2013-08-23 19:04:27 +0200
committerBert Belder <bertbelder@gmail.com>2013-08-23 19:04:38 +0200
commit6fa8398853e009eaea0eed2157575e7054bdebb4 (patch)
treec1bd0c7cdfa47dbe022fc0a3a6c52942119b60f2 /deps/uv
parent48976d29f4a2dd72cbef2096dd5e7b822ee19df0 (diff)
downloadandroid-node-v8-6fa8398853e009eaea0eed2157575e7054bdebb4.tar.gz
android-node-v8-6fa8398853e009eaea0eed2157575e7054bdebb4.tar.bz2
android-node-v8-6fa8398853e009eaea0eed2157575e7054bdebb4.zip
uv: upgrade to v0.11.9
Diffstat (limited to 'deps/uv')
-rw-r--r--deps/uv/ChangeLog18
-rw-r--r--deps/uv/include/uv-darwin.h10
-rw-r--r--deps/uv/include/uv.h2
-rw-r--r--deps/uv/src/unix/darwin.c2
-rw-r--r--deps/uv/src/unix/fsevents.c490
-rw-r--r--deps/uv/src/unix/kqueue.c1
-rw-r--r--deps/uv/src/version.c2
-rw-r--r--deps/uv/src/win/process.c58
-rw-r--r--deps/uv/test/benchmark-spawn.c4
-rw-r--r--deps/uv/test/test-ipc.c4
-rw-r--r--deps/uv/test/test-spawn.c18
-rw-r--r--deps/uv/test/test-stdio-over-pipes.c4
12 files changed, 420 insertions, 193 deletions
diff --git a/deps/uv/ChangeLog b/deps/uv/ChangeLog
index 36b328313f..76892acd8e 100644
--- a/deps/uv/ChangeLog
+++ b/deps/uv/ChangeLog
@@ -1,4 +1,20 @@
-2013.08.22, Version 0.11.8 (Unstable)
+2013.08.24, Version 0.11.9 (Unstable)
+
+Changes since version 0.11.8:
+
+* fsevents: share FSEventStream between multiple FS watchers, which removes a
+ limit on the maximum number of file watchers that can be created on OS X.
+ (Fedor Indutny)
+
+* process: the `exit_status` parameter for a uv_process_t's exit callback now
+ is an int64_t, and no longer an int. (Bert Belder)
+
+* process: make uv_spawn() return some types of errors immediately on windows,
+ instead of passing the error code the the exit callback. This brings it on
+ par with libuv's behavior on unix. (Bert Belder)
+
+
+2013.08.22, Version 0.11.8 (Unstable), a5260462db80ab0deab6b9e6a8991dd8f5a9a2f8
Changes since version 0.11.7:
diff --git a/deps/uv/include/uv-darwin.h b/deps/uv/include/uv-darwin.h
index 43b261f5fb..dcdd42ba6d 100644
--- a/deps/uv/include/uv-darwin.h
+++ b/deps/uv/include/uv-darwin.h
@@ -36,8 +36,8 @@
#define UV_PLATFORM_LOOP_FIELDS \
uv_thread_t cf_thread; \
- void* cf_cb; \
- void* cf_loop; \
+ void* _cf_reserved; \
+ void* cf_state; \
uv_mutex_t cf_mutex; \
uv_sem_t cf_sem; \
void* cf_signals[2]; \
@@ -47,10 +47,10 @@
char* realpath; \
int realpath_len; \
int cf_flags; \
- void* cf_eventstream; \
+ void* cf_event; \
uv_async_t* cf_cb; \
- void* cf_events[2]; \
- uv_sem_t cf_sem; \
+ void* cf_member[2]; \
+ uv_sem_t _cf_reserved; \
uv_mutex_t cf_mutex; \
#define UV_STREAM_PRIVATE_PLATFORM_FIELDS \
diff --git a/deps/uv/include/uv.h b/deps/uv/include/uv.h
index 5821cce8aa..32d481b23c 100644
--- a/deps/uv/include/uv.h
+++ b/deps/uv/include/uv.h
@@ -384,7 +384,7 @@ typedef void (*uv_async_cb)(uv_async_t* handle, int status);
typedef void (*uv_prepare_cb)(uv_prepare_t* handle, int status);
typedef void (*uv_check_cb)(uv_check_t* handle, int status);
typedef void (*uv_idle_cb)(uv_idle_t* handle, int status);
-typedef void (*uv_exit_cb)(uv_process_t*, int exit_status, int term_signal);
+typedef void (*uv_exit_cb)(uv_process_t*, int64_t exit_status, int term_signal);
typedef void (*uv_walk_cb)(uv_handle_t* handle, void* arg);
typedef void (*uv_fs_cb)(uv_fs_t* req);
typedef void (*uv_work_cb)(uv_work_t* req);
diff --git a/deps/uv/src/unix/darwin.c b/deps/uv/src/unix/darwin.c
index 8a9b4bab63..a03ef2a9e0 100644
--- a/deps/uv/src/unix/darwin.c
+++ b/deps/uv/src/unix/darwin.c
@@ -38,7 +38,7 @@
int uv__platform_loop_init(uv_loop_t* loop, int default_loop) {
- loop->cf_loop = NULL;
+ loop->cf_state = NULL;
if (uv__kqueue_init(loop))
return -errno;
diff --git a/deps/uv/src/unix/fsevents.c b/deps/uv/src/unix/fsevents.c
index 79ad198bae..4d5e87fe52 100644
--- a/deps/uv/src/unix/fsevents.c
+++ b/deps/uv/src/unix/fsevents.c
@@ -49,70 +49,89 @@ void uv__fsevents_loop_delete(uv_loop_t* loop) {
typedef struct uv__fsevents_event_s uv__fsevents_event_t;
typedef struct uv__cf_loop_signal_s uv__cf_loop_signal_t;
-typedef void (*cf_loop_signal_cb)(void* arg);
+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;
+ void* fsevent_handles[2];
+ int fsevent_handle_count;
+};
struct uv__cf_loop_signal_s {
- cf_loop_signal_cb cb;
QUEUE member;
- void* arg;
+ uv_fs_event_t* handle;
};
struct uv__fsevents_event_s {
int events;
- QUEUE member;
+ void* next;
char path[1];
};
+static const int kFSEventsModified = kFSEventStreamEventFlagItemFinderInfoMod |
+ kFSEventStreamEventFlagItemModified |
+ kFSEventStreamEventFlagItemInodeMetaMod |
+ kFSEventStreamEventFlagItemChangeOwner |
+ kFSEventStreamEventFlagItemXattrMod;
+static const int kFSEventsRenamed = kFSEventStreamEventFlagItemCreated |
+ kFSEventStreamEventFlagItemRemoved |
+ kFSEventStreamEventFlagItemRenamed;
+static const int kFSEventsSystem = kFSEventStreamEventFlagUserDropped |
+ kFSEventStreamEventFlagKernelDropped |
+ kFSEventStreamEventFlagEventIdsWrapped |
+ kFSEventStreamEventFlagHistoryDone |
+ kFSEventStreamEventFlagMount |
+ kFSEventStreamEventFlagUnmount |
+ kFSEventStreamEventFlagRootChanged;
+
/* Forward declarations */
static void uv__cf_loop_cb(void* arg);
static void* uv__cf_loop_runner(void* arg);
-static void uv__cf_loop_signal(uv_loop_t* loop,
- cf_loop_signal_cb cb,
- void* arg);
-
-#define UV__FSEVENTS_WALK(handle, block) \
- { \
- QUEUE* curr; \
- QUEUE split_head; \
+static int uv__cf_loop_signal(uv_loop_t* loop, uv_fs_event_t* handle);
+
+#define UV__FSEVENTS_PROCESS(handle, block) \
+ do { \
uv__fsevents_event_t* event; \
+ uv__fsevents_event_t* next; \
uv_mutex_lock(&(handle)->cf_mutex); \
- QUEUE_INIT(&split_head); \
- if (!QUEUE_EMPTY(&(handle)->cf_events)) { \
- QUEUE* split_pos = QUEUE_HEAD(&(handle)->cf_events); \
- QUEUE_SPLIT(&(handle)->cf_events, split_pos, &split_head); \
- } \
+ event = (handle)->cf_event; \
+ (handle)->cf_event = NULL; \
uv_mutex_unlock(&(handle)->cf_mutex); \
- while (!QUEUE_EMPTY(&split_head)) { \
- curr = QUEUE_HEAD(&split_head); \
+ while (event != NULL) { \
/* Invoke callback */ \
- event = QUEUE_DATA(curr, uv__fsevents_event_t, member); \
- QUEUE_REMOVE(curr); \
/* Invoke block code, but only if handle wasn't closed */ \
- if (((handle)->flags & (UV_CLOSING | UV_CLOSED)) == 0) \
+ if (!uv__is_closing((handle))) \
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) {
uv_fs_event_t* handle;
handle = cb->data;
- UV__FSEVENTS_WALK(handle, {
+ UV__FSEVENTS_PROCESS(handle, {
if (handle->event_watcher.fd != -1)
handle->cb(handle, event->path[0] ? event->path : NULL, event->events, 0);
});
- if ((handle->flags & (UV_CLOSING | UV_CLOSED)) == 0 &&
- handle->event_watcher.fd == -1) {
+ if (!uv__is_closing(handle) && 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,
@@ -125,42 +144,35 @@ static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef,
char* path;
char* pos;
uv_fs_event_t* handle;
+ QUEUE* q;
+ uv_loop_t* loop;
+ uv__cf_loop_state_t* state;
uv__fsevents_event_t* event;
- QUEUE add_list;
- int kFSEventsModified;
- int kFSEventsRenamed;
-
- kFSEventsModified = kFSEventStreamEventFlagItemFinderInfoMod |
- kFSEventStreamEventFlagItemModified |
- kFSEventStreamEventFlagItemInodeMetaMod |
- kFSEventStreamEventFlagItemChangeOwner |
- kFSEventStreamEventFlagItemXattrMod;
- kFSEventsRenamed = kFSEventStreamEventFlagItemCreated |
- kFSEventStreamEventFlagItemRemoved |
- kFSEventStreamEventFlagItemRenamed;
-
- handle = info;
+ uv__fsevents_event_t* tail;
+
+ loop = info;
+ state = loop->cf_state;
+ assert(state != NULL);
paths = eventPaths;
- QUEUE_INIT(&add_list);
-
- for (i = 0; i < numEvents; i++) {
- /* Ignore system events */
- if (eventFlags[i] & (kFSEventStreamEventFlagUserDropped |
- kFSEventStreamEventFlagKernelDropped |
- kFSEventStreamEventFlagEventIdsWrapped |
- kFSEventStreamEventFlagHistoryDone |
- kFSEventStreamEventFlagMount |
- kFSEventStreamEventFlagUnmount |
- kFSEventStreamEventFlagRootChanged)) {
- continue;
- }
- /* TODO: Report errors */
- path = paths[i];
- len = strlen(path);
+ /* For each handle */
+ QUEUE_FOREACH(q, &state->fsevent_handles) {
+ handle = 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;
- /* Remove absolute path prefix */
- if (strstr(path, handle->realpath) == path) {
path += handle->realpath_len;
len -= handle->realpath_len;
@@ -169,79 +181,81 @@ 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) */
- pos = strchr(path, '/');
- if ((handle->cf_flags & UV_FS_EVENT_RECURSIVE) == 0 &&
- pos != NULL &&
- pos != path + 1)
- continue;
+ /* 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;
+ }
#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;
- memcpy(event->path, path, len + 1);
+ memset(event, 0, sizeof(*event));
+ 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;
- QUEUE_INSERT_TAIL(&add_list, &event->member);
- }
- uv_mutex_lock(&handle->cf_mutex);
- QUEUE_ADD(&handle->cf_events, &add_list);
- uv_mutex_unlock(&handle->cf_mutex);
+ if (tail != NULL)
+ tail->next = event;
+ tail = event;
+ }
+
+ 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);
+ }
+ }
}
-static void uv__fsevents_schedule(void* arg) {
- uv_fs_event_t* handle;
+/* Runs in CF thread */
+static void uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) {
+ uv__cf_loop_state_t* state;
FSEventStreamContext ctx;
FSEventStreamRef ref;
- CFStringRef path;
- CFArrayRef paths;
CFAbsoluteTime latency;
FSEventStreamCreateFlags flags;
- handle = arg;
-
/* Initialize context */
ctx.version = 0;
- ctx.info = handle;
+ ctx.info = loop;
ctx.retain = NULL;
ctx.release = NULL;
ctx.copyDescription = NULL;
- /* Initialize paths array */
- path = CFStringCreateWithCString(NULL,
- handle->filename,
- CFStringGetSystemEncoding());
- assert(path != NULL);
- paths = CFArrayCreate(NULL, (const void**)&path, 1, NULL);
- assert(paths != 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,
@@ -250,43 +264,120 @@ static void uv__fsevents_schedule(void* arg) {
latency,
flags);
assert(ref != NULL);
- handle->cf_eventstream = ref;
- FSEventStreamScheduleWithRunLoop(handle->cf_eventstream,
- handle->loop->cf_loop,
+ state = loop->cf_state;
+ FSEventStreamScheduleWithRunLoop(ref,
+ state->loop,
kCFRunLoopDefaultMode);
- if (!FSEventStreamStart(handle->cf_eventstream))
+ if (!FSEventStreamStart(ref))
abort();
+
+ state->fsevent_stream = ref;
}
-static void uv__fsevents_unschedule(void* arg) {
- uv_fs_event_t* handle;
+/* 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;
- handle = arg;
+ /* Flush all accumulated events */
+ FSEventStreamFlushSync(state->fsevent_stream);
/* Stop emitting events */
- FSEventStreamStop(handle->cf_eventstream);
+ FSEventStreamStop(state->fsevent_stream);
/* Release stream */
- FSEventStreamInvalidate(handle->cf_eventstream);
- FSEventStreamRelease(handle->cf_eventstream);
- handle->cf_eventstream = NULL;
+ 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;
+ QUEUE* 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 = QUEUE_NEXT(q);
+ assert(q != &state->fsevent_handles);
+ curr = 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);
- /* Notify main thread that we're done here */
- uv_sem_post(&handle->cf_sem);
+ 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_loop != NULL)
+ 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;
@@ -296,10 +387,27 @@ static int uv__fsevents_loop_init(uv_loop_t* loop) {
goto fail_sem_init;
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;
+
+ 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;
- loop->cf_cb = CFRunLoopSourceCreate(NULL, 0, &ctx);
+ 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
@@ -313,6 +421,8 @@ static int uv__fsevents_loop_init(uv_loop_t* loop) {
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);
@@ -324,26 +434,39 @@ static int uv__fsevents_loop_init(uv_loop_t* loop) {
/* Synchronize threads */
uv_sem_wait(&loop->cf_sem);
- assert(loop->cf_loop != NULL);
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;
QUEUE* q;
- if (loop->cf_loop == NULL)
+ if (loop->cf_state == NULL)
return;
- uv__cf_loop_signal(loop, NULL, NULL);
+ 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);
@@ -355,40 +478,54 @@ void uv__fsevents_loop_delete(uv_loop_t* loop) {
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;
- loop->cf_loop = CFRunLoopGetCurrent();
+ state = loop->cf_state;
+ state->loop = CFRunLoopGetCurrent();
- CFRunLoopAddSource(loop->cf_loop,
- loop->cf_cb,
+ CFRunLoopAddSource(state->loop,
+ state->signal_source,
kCFRunLoopDefaultMode);
uv_sem_post(&loop->cf_sem);
CFRunLoopRun();
- CFRunLoopRemoveSource(loop->cf_loop,
- loop->cf_cb,
+ 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;
QUEUE* item;
QUEUE split_head;
uv__cf_loop_signal_t* s;
loop = arg;
+ state = loop->cf_state;
+ QUEUE_INIT(&split_head);
uv_mutex_lock(&loop->cf_mutex);
- QUEUE_INIT(&split_head);
if (!QUEUE_EMPTY(&loop->cf_signals)) {
QUEUE* split_pos = QUEUE_HEAD(&loop->cf_signals);
QUEUE_SPLIT(&loop->cf_signals, split_pos, &split_head);
@@ -401,10 +538,10 @@ static void uv__cf_loop_cb(void* arg) {
s = QUEUE_DATA(item, uv__cf_loop_signal_t, member);
/* This was a termination signal */
- if (s->cb == NULL)
- CFRunLoopStop(loop->cf_loop);
+ if (s->handle == NULL)
+ CFRunLoopStop(state->loop);
else
- s->cb(s->arg);
+ uv__fsevents_reschedule(s->handle);
QUEUE_REMOVE(item);
free(s);
@@ -412,29 +549,34 @@ static void uv__cf_loop_cb(void* arg) {
}
-void uv__cf_loop_signal(uv_loop_t* loop, cf_loop_signal_cb cb, void* arg) {
+/* 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));
- /* XXX: Fail */
if (item == NULL)
- abort();
+ return -ENOMEM;
- item->arg = arg;
- item->cb = cb;
+ item->handle = handle;
uv_mutex_lock(&loop->cf_mutex);
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);
+ 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)
@@ -442,52 +584,98 @@ int uv__fsevents_init(uv_fs_event_t* handle) {
/* Get absolute path to file */
handle->realpath = realpath(handle->filename, NULL);
- if (handle->realpath != NULL)
- handle->realpath_len = strlen(handle->realpath);
+ if (handle->realpath == NULL)
+ return -errno;
+ handle->realpath_len = strlen(handle->realpath);
+
+ /* Initialize singly-linked list */
+ handle->cf_event = NULL;
- handle->cf_eventstream = NULL;
/*
* 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 -ENOMEM;
+ if (handle->cf_cb == NULL) {
+ err = -ENOMEM;
+ goto fail_cf_cb_malloc;
+ }
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);
- QUEUE_INIT(&handle->cf_events);
-
- uv__cf_loop_signal(handle->loop, uv__fsevents_schedule, handle);
+ err = uv_mutex_init(&handle->cf_mutex);
+ if (err)
+ goto fail_cf_mutex_init;
+
+ /* Insert handle into the list */
+ state = handle->loop->cf_state;
+ uv_mutex_lock(&state->fsevent_mutex);
+ 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;
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_cb == NULL)
return -EINVAL;
- uv__cf_loop_signal(handle->loop, uv__fsevents_unschedule, handle);
+ /* Remove handle from the list */
+ state = handle->loop->cf_state;
+ uv_mutex_lock(&state->fsevent_mutex);
+ 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;
/* Wait for deinitialization */
- uv_sem_wait(&handle->cf_sem);
+ uv_sem_wait(&state->fsevent_sem);
uv_close((uv_handle_t*) handle->cf_cb, (uv_close_cb) free);
handle->cf_cb = NULL;
/* Free data in queue */
- UV__FSEVENTS_WALK(handle, {
+ UV__FSEVENTS_PROCESS(handle, {
/* NOP */
- })
+ });
uv_mutex_destroy(&handle->cf_mutex);
- uv_sem_destroy(&handle->cf_sem);
free(handle->realpath);
handle->realpath = NULL;
handle->realpath_len = 0;
diff --git a/deps/uv/src/unix/kqueue.c b/deps/uv/src/unix/kqueue.c
index 2c68bf36f2..391ab615de 100644
--- a/deps/uv/src/unix/kqueue.c
+++ b/deps/uv/src/unix/kqueue.c
@@ -320,7 +320,6 @@ 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;
diff --git a/deps/uv/src/version.c b/deps/uv/src/version.c
index e0a621f581..342ab51d51 100644
--- a/deps/uv/src/version.c
+++ b/deps/uv/src/version.c
@@ -31,7 +31,7 @@
#define UV_VERSION_MAJOR 0
#define UV_VERSION_MINOR 11
-#define UV_VERSION_PATCH 8
+#define UV_VERSION_PATCH 9
#define UV_VERSION_IS_RELEASE 1
diff --git a/deps/uv/src/win/process.c b/deps/uv/src/win/process.c
index 3230bb2ef5..13dc9d1077 100644
--- a/deps/uv/src/win/process.c
+++ b/deps/uv/src/win/process.c
@@ -758,9 +758,6 @@ void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) {
} else if (!GetExitCodeProcess(handle->process_handle, &status)) {
/* Unable to to obtain the exit code. This should never happen. */
exit_code = uv_translate_sys_error(GetLastError());
- } else {
- /* Make sure the exit code is >= 0. */
- exit_code = status & INT_MAX;
}
/* Fire the exit callback. */
@@ -836,25 +833,25 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
err = uv_utf8_to_utf16_alloc(options.file, &application);
if (err)
- goto done;
+ goto immediate_failure;
err = make_program_args(options.args,
options.flags & UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS,
&arguments);
if (err)
- goto done;
+ goto immediate_failure;
if (options.env) {
err = make_program_env(options.env, &env);
- if (err)
- goto done;
+ if (err)
+ goto immediate_failure;
}
if (options.cwd) {
/* Explicit cwd */
err = uv_utf8_to_utf16_alloc(options.cwd, &cwd);
if (err)
- goto done;
+ goto immediate_failure;
} else {
/* Inherit cwd */
@@ -863,59 +860,60 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
cwd_len = GetCurrentDirectoryW(0, NULL);
if (!cwd_len) {
err = GetLastError();
- goto done;
+ goto immediate_failure;
}
cwd = (WCHAR*) malloc(cwd_len * sizeof(WCHAR));
if (cwd == NULL) {
err = ERROR_OUTOFMEMORY;
- goto done;
+ goto immediate_failure;
}
r = GetCurrentDirectoryW(cwd_len, cwd);
if (r == 0 || r >= cwd_len) {
err = GetLastError();
- goto done;
+ goto immediate_failure;
}
}
- /* Get PATH environment variable. */
+ /* Get PATH environment variable. */
{
DWORD path_len, r;
path_len = GetEnvironmentVariableW(L"PATH", NULL, 0);
if (path_len == 0) {
err = GetLastError();
- goto done;
+ goto immediate_failure;
}
-
path = (WCHAR*) malloc(path_len * sizeof(WCHAR));
if (path == NULL) {
err = ERROR_OUTOFMEMORY;
- goto done;
+ goto immediate_failure;
}
r = GetEnvironmentVariableW(L"PATH", path, path_len);
if (r == 0 || r >= path_len) {
err = GetLastError();
- goto done;
+ goto immediate_failure;
}
}
+ err = uv__stdio_create(loop, &options, &process->child_stdio_buffer);
+ if (err)
+ goto immediate_failure;
+
+ /* Beyond this point, failure is reported asynchronously. */
+
application_path = search_path(application,
cwd,
path);
if (application_path == NULL) {
/* Not found. */
err = ERROR_FILE_NOT_FOUND;
- goto done;
+ goto success_or_async_failure;
}
- err = uv__stdio_create(loop, &options, &process->child_stdio_buffer);
- if (err)
- goto done;
-
startup.cb = sizeof(startup);
startup.lpReserved = NULL;
startup.lpDesktop = NULL;
@@ -1013,7 +1011,9 @@ int uv_spawn(uv_loop_t* loop, uv_process_t* process,
err = GetLastError();
}
-done:
+ /* We get here if we successfully created a process, or when we */
+ /* encountered failure that we want to report asynchronously. */
+ success_or_async_failure:
free(application);
free(application_path);
free(arguments);
@@ -1040,6 +1040,20 @@ done:
}
return 0;
+
+ /* This code path is taken when we run into an error that we want to */
+ /* report immediately. */
+ immediate_failure:
+ free(application);
+ free(application_path);
+ free(arguments);
+ free(cwd);
+ free(env);
+ free(path);
+
+ assert(process->child_stdio_buffer == NULL);
+
+ return uv_translate_sys_error(err);
}
diff --git a/deps/uv/test/benchmark-spawn.c b/deps/uv/test/benchmark-spawn.c
index 4912d9784e..140bfa76bc 100644
--- a/deps/uv/test/benchmark-spawn.c
+++ b/deps/uv/test/benchmark-spawn.c
@@ -64,7 +64,9 @@ static void process_close_cb(uv_handle_t* handle) {
}
-static void exit_cb(uv_process_t* process, int exit_status, int term_signal) {
+static void exit_cb(uv_process_t* process,
+ int64_t exit_status,
+ int term_signal) {
ASSERT(exit_status == 42);
ASSERT(term_signal == 0);
uv_close((uv_handle_t*)process, process_close_cb);
diff --git a/deps/uv/test/test-ipc.c b/deps/uv/test/test-ipc.c
index ba9c7a3e5c..0918a92755 100644
--- a/deps/uv/test/test-ipc.c
+++ b/deps/uv/test/test-ipc.c
@@ -84,7 +84,9 @@ static void on_connection(uv_stream_t* server, int status) {
}
-static void exit_cb(uv_process_t* process, int exit_status, int term_signal) {
+static void exit_cb(uv_process_t* process,
+ int64_t exit_status,
+ int term_signal) {
printf("exit_cb\n");
exit_cb_called++;
ASSERT(exit_status == 0);
diff --git a/deps/uv/test/test-spawn.c b/deps/uv/test/test-spawn.c
index 507ef2d5c6..96cc307aab 100644
--- a/deps/uv/test/test-spawn.c
+++ b/deps/uv/test/test-spawn.c
@@ -52,7 +52,9 @@ static void close_cb(uv_handle_t* handle) {
}
-static void exit_cb(uv_process_t* process, int exit_status, int term_signal) {
+static void exit_cb(uv_process_t* process,
+ int64_t exit_status,
+ int term_signal) {
printf("exit_cb\n");
exit_cb_called++;
ASSERT(exit_status == 1);
@@ -62,7 +64,7 @@ static void exit_cb(uv_process_t* process, int exit_status, int term_signal) {
static void expect(uv_process_t* process,
- int exit_status,
+ int64_t exit_status,
int term_signal,
int err) {
printf("exit_cb\n");
@@ -74,20 +76,22 @@ static void expect(uv_process_t* process,
static void exit_cb_expect_enoent(uv_process_t* process,
- int exit_status,
+ int64_t exit_status,
int term_signal) {
expect(process, exit_status, term_signal, UV_ENOENT);
}
static void exit_cb_expect_eperm(uv_process_t* process,
- int exit_status,
+ int64_t exit_status,
int term_signal) {
expect(process, exit_status, term_signal, UV_EPERM);
}
-static void kill_cb(uv_process_t* process, int exit_status, int term_signal) {
+static void kill_cb(uv_process_t* process,
+ int64_t exit_status,
+ int term_signal) {
int err;
printf("exit_cb\n");
@@ -109,7 +113,7 @@ static void kill_cb(uv_process_t* process, int exit_status, int term_signal) {
ASSERT(err == UV_ESRCH);
}
-static void detach_failure_cb(uv_process_t* process, int exit_status, int term_signal) {
+static void detach_failure_cb(uv_process_t* process, int64_t exit_status, int term_signal) {
printf("detach_cb\n");
exit_cb_called++;
}
@@ -886,7 +890,7 @@ TEST_IMPL(spawn_setgid_fails) {
#ifdef _WIN32
static void exit_cb_unexpected(uv_process_t* process,
- int exit_status,
+ int64_t exit_status,
int term_signal) {
ASSERT(0 && "should not have been called");
}
diff --git a/deps/uv/test/test-stdio-over-pipes.c b/deps/uv/test/test-stdio-over-pipes.c
index 99b0916018..1d6191e771 100644
--- a/deps/uv/test/test-stdio-over-pipes.c
+++ b/deps/uv/test/test-stdio-over-pipes.c
@@ -48,7 +48,9 @@ static void close_cb(uv_handle_t* handle) {
}
-static void exit_cb(uv_process_t* process, int exit_status, int term_signal) {
+static void exit_cb(uv_process_t* process,
+ int64_t exit_status,
+ int term_signal) {
printf("exit_cb\n");
exit_cb_called++;
ASSERT(exit_status == 0);