From 6fa8398853e009eaea0eed2157575e7054bdebb4 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Fri, 23 Aug 2013 19:04:27 +0200 Subject: [PATCH] uv: upgrade to v0.11.9 --- deps/uv/ChangeLog | 18 +- deps/uv/include/uv-darwin.h | 10 +- deps/uv/include/uv.h | 2 +- deps/uv/src/unix/darwin.c | 2 +- deps/uv/src/unix/fsevents.c | 490 ++++++++++++++++++--------- deps/uv/src/unix/kqueue.c | 1 - deps/uv/src/version.c | 2 +- deps/uv/src/win/process.c | 58 ++-- deps/uv/test/benchmark-spawn.c | 4 +- deps/uv/test/test-ipc.c | 4 +- deps/uv/test/test-spawn.c | 18 +- deps/uv/test/test-stdio-over-pipes.c | 4 +- 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);