From 15d64132151c18b26346afa892444b95e2addad0 Mon Sep 17 00:00:00 2001 From: Timothy J Fontaine Date: Wed, 21 Aug 2013 14:41:23 -0700 Subject: [PATCH 01/20] 2013.08.22, Version 0.10.14 (Stable) Changes since version 0.10.13: * unix: retry waitpid() on EINTR (Ben Noordhuis) --- ChangeLog | 7 +++++++ src/version.c | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index e66ba047..9d97bdd4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2013.08.22, Version 0.10.14 (Stable) + +Changes since version 0.10.13: + +* unix: retry waitpid() on EINTR (Ben Noordhuis) + + 2013.07.26, Version 0.10.13 (Stable), 381312e1fe6fecbabc943ccd56f0e7d114b3d064 Changes since version 0.10.12: diff --git a/src/version.c b/src/version.c index bcbef82d..e351af68 100644 --- a/src/version.c +++ b/src/version.c @@ -35,7 +35,7 @@ #define UV_VERSION_MAJOR 0 #define UV_VERSION_MINOR 10 #define UV_VERSION_PATCH 14 -#define UV_VERSION_IS_RELEASE 0 +#define UV_VERSION_IS_RELEASE 1 #define UV_VERSION ((UV_VERSION_MAJOR << 16) | \ From 24a42a406ae00c2e8060b9f9397365684669db6b Mon Sep 17 00:00:00 2001 From: Timothy J Fontaine Date: Wed, 21 Aug 2013 14:41:26 -0700 Subject: [PATCH 02/20] Now working on v0.10.15 --- ChangeLog | 2 +- src/version.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9d97bdd4..25cc70cb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -2013.08.22, Version 0.10.14 (Stable) +2013.08.22, Version 0.10.14 (Stable), 15d64132151c18b26346afa892444b95e2addad0 Changes since version 0.10.13: diff --git a/src/version.c b/src/version.c index e351af68..c7f0dcdd 100644 --- a/src/version.c +++ b/src/version.c @@ -34,8 +34,8 @@ #define UV_VERSION_MAJOR 0 #define UV_VERSION_MINOR 10 -#define UV_VERSION_PATCH 14 -#define UV_VERSION_IS_RELEASE 1 +#define UV_VERSION_PATCH 15 +#define UV_VERSION_IS_RELEASE 0 #define UV_VERSION ((UV_VERSION_MAJOR << 16) | \ From 9bae606d413327187828155b61babcd52b2d2517 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Tue, 13 Aug 2013 02:02:12 +0200 Subject: [PATCH 03/20] darwin: create fsevents thread on demand * Move CF run loop code to fsevents.c. * Create the fsevents thread on demand rather than at startup. * Remove use of ACCESS_ONCE. All accesses to loop->cf_loop are protected by full memory barriers so no reordering can take place. Fixes #872. Conflicts: src/unix/darwin.c --- src/unix/darwin.c | 131 +-------------------------------- src/unix/fsevents.c | 175 ++++++++++++++++++++++++++++++++++++++++++-- src/unix/internal.h | 4 +- 3 files changed, 170 insertions(+), 140 deletions(-) diff --git a/src/unix/darwin.c b/src/unix/darwin.c index 77e662f4..2358e9f6 100644 --- a/src/unix/darwin.c +++ b/src/unix/darwin.c @@ -28,8 +28,6 @@ #include #include -#include - #include #include #include /* _NSGetExecutablePath */ @@ -37,144 +35,19 @@ #include #include /* 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) { - CFRunLoopSourceContext ctx; - int r; + loop->cf_loop = NULL; 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) { - 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); + uv__fsevents_loop_delete(loop); } diff --git a/src/unix/fsevents.c b/src/unix/fsevents.c index b6d27467..25b10903 100644 --- a/src/unix/fsevents.c +++ b/src/unix/fsevents.c @@ -34,13 +34,27 @@ 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 #include +#include #include 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); + +struct uv__cf_loop_signal_s { + cf_loop_signal_cb cb; + ngx_queue_t member; + void* arg; +}; struct uv__fsevents_event_s { int events; @@ -48,6 +62,12 @@ struct uv__fsevents_event_s { char path[1]; }; +/* 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) \ { \ @@ -75,7 +95,7 @@ struct uv__fsevents_event_s { } -void uv__fsevents_cb(uv_async_t* cb, int status) { +static void uv__fsevents_cb(uv_async_t* cb, int status) { uv_fs_event_t* handle; handle = cb->data; @@ -92,12 +112,12 @@ void uv__fsevents_cb(uv_async_t* cb, int status) { } -void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef, - void* info, - size_t numEvents, - void* eventPaths, - const FSEventStreamEventFlags eventFlags[], - const FSEventStreamEventId eventIds[]) { +static 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; @@ -190,7 +210,7 @@ void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef, } -void uv__fsevents_schedule(void* arg) { +static void uv__fsevents_schedule(void* arg) { uv_fs_event_t* handle; handle = arg; @@ -202,6 +222,140 @@ void uv__fsevents_schedule(void* arg) { } +static int uv__fsevents_loop_init(uv_loop_t* loop) { + CFRunLoopSourceContext ctx; + int err; + + if (loop->cf_loop != NULL) + return 0; + + 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); + memset(&ctx, 0, sizeof(ctx)); + ctx.info = loop; + ctx.perform = uv__cf_loop_cb; + loop->cf_cb = CFRunLoopSourceCreate(NULL, 0, &ctx); + + err = uv_thread_create(&loop->cf_thread, uv__cf_loop_runner, loop); + if (err) + goto fail_thread_create; + + /* Synchronize threads */ + uv_sem_wait(&loop->cf_sem); + assert(loop->cf_loop != NULL); + return 0; + +fail_thread_create: + uv_sem_destroy(&loop->cf_sem); + +fail_sem_init: + uv_mutex_destroy(&loop->cf_mutex); + return err; +} + + +void uv__fsevents_loop_delete(uv_loop_t* loop) { + uv__cf_loop_signal_t* s; + ngx_queue_t* q; + + if (loop->cf_loop == NULL) + return; + + 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)) { + q = ngx_queue_head(&loop->cf_signals); + s = ngx_queue_data(q, uv__cf_loop_signal_t, member); + ngx_queue_remove(q); + free(s); + } +} + + +static void uv__cf_loop_runner(void* arg) { + uv_loop_t* loop; + + loop = arg; + 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_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->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); +} + + int uv__fsevents_init(uv_fs_event_t* handle) { FSEventStreamContext ctx; FSEventStreamRef ref; @@ -209,6 +363,11 @@ int uv__fsevents_init(uv_fs_event_t* handle) { CFArrayRef paths; CFAbsoluteTime latency; FSEventStreamCreateFlags flags; + int err; + + err = uv__fsevents_loop_init(handle->loop); + if (err) + return err; /* Initialize context */ ctx.version = 0; diff --git a/src/unix/internal.h b/src/unix/internal.h index 61cb1ec1..2bb4dc1b 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -216,12 +216,10 @@ 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 From ea4cb77814eeaa7629b29d53ad6a2b8bc4b19046 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Wed, 14 Aug 2013 17:14:35 +0400 Subject: [PATCH 04/20] fsevents: FSEvents is most likely not thread-safe Perform all operation with FSEventStream in the same thread, where it'll be used. Conflicts: src/unix/fsevents.c --- src/unix/fsevents.c | 108 ++++++++++++++++++++++++++------------------ src/unix/kqueue.c | 1 + 2 files changed, 64 insertions(+), 45 deletions(-) diff --git a/src/unix/fsevents.c b/src/unix/fsevents.c index 25b10903..8abf7f83 100644 --- a/src/unix/fsevents.c +++ b/src/unix/fsevents.c @@ -212,12 +212,67 @@ static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef, static void uv__fsevents_schedule(void* arg) { uv_fs_event_t* handle; + FSEventStreamContext ctx; + FSEventStreamRef ref; + CFStringRef path; + CFArrayRef paths; + CFAbsoluteTime latency; + FSEventStreamCreateFlags flags; handle = arg; + + /* Initialize context */ + ctx.version = 0; + ctx.info = handle; + ctx.retain = NULL; + ctx.release = NULL; + ctx.copyDescription = NULL; + + /* Initialize paths array */ + path = CFStringCreateWithCString(NULL, + handle->filename, + CFStringGetSystemEncoding()); + assert(path != NULL); + paths = CFArrayCreate(NULL, (const void**)&path, 1, NULL); + assert(paths != NULL); + + latency = 0.15; + + /* Set appropriate flags */ + flags = kFSEventStreamCreateFlagFileEvents; + + ref = FSEventStreamCreate(NULL, + &uv__fsevents_event_cb, + &ctx, + paths, + kFSEventStreamEventIdSinceNow, + latency, + flags); + assert(ref != NULL); + handle->cf_eventstream = ref; + FSEventStreamScheduleWithRunLoop(handle->cf_eventstream, handle->loop->cf_loop, kCFRunLoopDefaultMode); - FSEventStreamStart(handle->cf_eventstream); + if (!FSEventStreamStart(handle->cf_eventstream)) + abort(); +} + + +static void uv__fsevents_unschedule(void* arg) { + uv_fs_event_t* handle; + + handle = arg; + + /* Stop emitting events */ + FSEventStreamStop(handle->cf_eventstream); + + /* Release stream */ + FSEventStreamInvalidate(handle->cf_eventstream); + FSEventStreamRelease(handle->cf_eventstream); + handle->cf_eventstream = NULL; + + /* Notify main thread that we're done here */ uv_sem_post(&handle->cf_sem); } @@ -357,50 +412,18 @@ 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) { - FSEventStreamContext ctx; - FSEventStreamRef ref; - CFStringRef path; - CFArrayRef paths; - CFAbsoluteTime latency; - FSEventStreamCreateFlags flags; int err; err = uv__fsevents_loop_init(handle->loop); if (err) return err; - /* Initialize context */ - ctx.version = 0; - 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; - - ref = FSEventStreamCreate(NULL, - &uv__fsevents_event_cb, - &ctx, - paths, - kFSEventStreamEventIdSinceNow, - latency, - flags); - handle->cf_eventstream = ref; - + handle->cf_eventstream = NULL; /* * Events will occur in other thread. * Initialize callback for getting them back into event loop's thread @@ -425,21 +448,16 @@ int uv__fsevents_init(uv_fs_event_t* handle) { int uv__fsevents_close(uv_fs_event_t* handle) { - if (handle->cf_eventstream == NULL) - return -1; + if (handle->cf_cb == NULL) + return -EINVAL; - /* Ensure that event stream was scheduled */ + uv__cf_loop_signal(handle->loop, uv__fsevents_unschedule, handle); + + /* Wait for deinitialization */ uv_sem_wait(&handle->cf_sem); - /* Stop emitting events */ - FSEventStreamStop(handle->cf_eventstream); - - /* Release stream */ - FSEventStreamInvalidate(handle->cf_eventstream); - FSEventStreamRelease(handle->cf_eventstream); - handle->cf_eventstream = NULL; - uv_close((uv_handle_t*) handle->cf_cb, (uv_close_cb) free); + handle->cf_cb = NULL; /* Free data in queue */ UV__FSEVENTS_WALK(handle, { diff --git a/src/unix/kqueue.c b/src/unix/kqueue.c index 378903a6..2feb4bab 100644 --- a/src/unix/kqueue.c +++ b/src/unix/kqueue.c @@ -307,6 +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; From 684e2124e7cbe8f94daeba730e83cbf88963430f Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Wed, 21 Aug 2013 01:43:09 +0400 Subject: [PATCH 05/20] fsevents: use shared FSEventStream It seems that number of simultaneously opened FSEventStreams is limited on OSX (i.e. you can have only fixed number of them on one running system), getting past through this limit will cause `FSEventStreamCreate` to return false and write following message to stderr: (CarbonCore.framework) FSEventStreamStart: register_with_server: ERROR: f2d_register_rpc() => (null) (-21) To prevent this, we must use only one shared FSEventStream with a paths for all uv_fsevent_t handles, and then filter out events for each handle using this paths again. See https://github.com/joyent/node/issues/5463 Conflicts: include/uv-private/uv-darwin.h src/unix/fsevents.c --- include/uv-private/uv-darwin.h | 10 +- src/unix/darwin.c | 2 +- src/unix/fsevents.c | 511 +++++++++++++++++++++++---------- src/unix/kqueue.c | 1 - 4 files changed, 366 insertions(+), 158 deletions(-) diff --git a/include/uv-private/uv-darwin.h b/include/uv-private/uv-darwin.h index e861cbab..4037f5c5 100644 --- a/include/uv-private/uv-darwin.h +++ b/include/uv-private/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; \ ngx_queue_t cf_signals; \ @@ -47,10 +47,10 @@ char* realpath; \ int realpath_len; \ int cf_flags; \ - void* cf_eventstream; \ + void* cf_event; \ uv_async_t* cf_cb; \ - ngx_queue_t cf_events; \ - uv_sem_t cf_sem; \ + ngx_queue_t cf_member; \ + uv_sem_t _cf_reserved; \ uv_mutex_t cf_mutex; \ #define UV_STREAM_PRIVATE_PLATFORM_FIELDS \ diff --git a/src/unix/darwin.c b/src/unix/darwin.c index 2358e9f6..08da5139 100644 --- a/src/unix/darwin.c +++ b/src/unix/darwin.c @@ -37,7 +37,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 -1; diff --git a/src/unix/fsevents.c b/src/unix/fsevents.c index 8abf7f83..f849c38f 100644 --- a/src/unix/fsevents.c +++ b/src/unix/fsevents.c @@ -48,70 +48,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; + ngx_queue_t fsevent_handles; + int fsevent_handle_count; +}; struct uv__cf_loop_signal_s { - cf_loop_signal_cb cb; ngx_queue_t member; - void* arg; + uv_fs_event_t* handle; }; struct uv__fsevents_event_s { int events; - ngx_queue_t 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); +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_WALK(handle, block) \ - { \ - ngx_queue_t* curr; \ - ngx_queue_t split_head; \ +#define UV__FSEVENTS_PROCESS(handle, block) \ + do { \ uv__fsevents_event_t* event; \ + uv__fsevents_event_t* next; \ uv_mutex_lock(&(handle)->cf_mutex); \ - ngx_queue_init(&split_head); \ - if (!ngx_queue_empty(&(handle)->cf_events)) { \ - ngx_queue_t* split_pos = ngx_queue_next(&(handle)->cf_events); \ - ngx_queue_split(&(handle)->cf_events, split_pos, &split_head); \ - } \ + event = (handle)->cf_event; \ + (handle)->cf_event = NULL; \ uv_mutex_unlock(&(handle)->cf_mutex); \ - while (!ngx_queue_empty(&split_head)) { \ - curr = ngx_queue_head(&split_head); \ + while (event != NULL) { \ /* Invoke callback */ \ - event = ngx_queue_data(curr, uv__fsevents_event_t, member); \ - ngx_queue_remove(curr); \ /* Invoke block code, but only if handle wasn't closed */ \ - if (((handle)->flags & (UV_CLOSING | UV_CLOSED)) == 0) \ + 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, @@ -124,42 +143,35 @@ static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef, 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; - ngx_queue_t add_list; - int kFSEventsModified; - int kFSEventsRenamed; + uv__fsevents_event_t* tail; - kFSEventsModified = kFSEventStreamEventFlagItemFinderInfoMod | - kFSEventStreamEventFlagItemModified | - kFSEventStreamEventFlagItemInodeMetaMod | - kFSEventStreamEventFlagItemChangeOwner | - kFSEventStreamEventFlagItemXattrMod; - kFSEventsRenamed = kFSEventStreamEventFlagItemCreated | - kFSEventStreamEventFlagItemRemoved | - kFSEventStreamEventFlagItemRenamed; - - handle = info; + loop = info; + state = loop->cf_state; + assert(state != NULL); 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; - /* TODO: Report errors */ - path = paths[i]; - len = strlen(path); + /* 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; @@ -168,79 +180,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; - ngx_queue_insert_tail(&add_list, &event->member); + 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_mutex_lock(&handle->cf_mutex); - ngx_queue_add(&handle->cf_events, &add_list); - uv_mutex_unlock(&handle->cf_mutex); - - uv_async_send(handle->cf_cb); } -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, @@ -249,41 +263,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; - handle = arg; + state = loop->cf_state; + + if (state->fsevent_stream == NULL) + return; + + /* 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; - - /* Notify main thread that we're done here */ - uv_sem_post(&handle->cf_sem); + 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_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; @@ -293,37 +386,86 @@ static int uv__fsevents_loop_init(uv_loop_t* loop) { 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; - 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 + * 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); - err = uv_thread_create(&loop->cf_thread, uv__cf_loop_runner, loop); if (err) goto fail_thread_create; /* 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; ngx_queue_t* 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); @@ -335,38 +477,54 @@ void uv__fsevents_loop_delete(uv_loop_t* loop) { 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; } -static void uv__cf_loop_runner(void* arg) { +/* 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; 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); - ngx_queue_init(&split_head); 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); @@ -379,10 +537,10 @@ static void uv__cf_loop_cb(void* arg) { s = ngx_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); ngx_queue_remove(item); free(s); @@ -390,29 +548,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); 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); + 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) @@ -420,52 +583,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 uv__set_sys_error(handle->loop, ENOMEM); + if (handle->cf_cb == NULL) { + err = uv__set_sys_error(handle->loop, 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); - ngx_queue_init(&handle->cf_events); + err = uv_mutex_init(&handle->cf_mutex); + if (err) + goto fail_cf_mutex_init; - uv__cf_loop_signal(handle->loop, uv__fsevents_schedule, handle); + /* 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; 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); + 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; /* 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/src/unix/kqueue.c b/src/unix/kqueue.c index 2feb4bab..a46c88f4 100644 --- a/src/unix/kqueue.c +++ b/src/unix/kqueue.c @@ -308,7 +308,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; From 221078a8fdd9b853c6b557b3d9a5dd744b4fdd6b Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Fri, 23 Aug 2013 18:57:10 +0200 Subject: [PATCH 06/20] 2013.08.24, Version 0.10.15 (Stable) Changes since version 0.10.14: * fsevents: create FSEvents thread on demand (Ben Noordhuis) * fsevents: use a single thread for interacting with FSEvents, because it's not thread-safe. (Fedor Indutny) * 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) --- ChangeLog | 14 ++++++++++++++ src/version.c | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 25cc70cb..45e308fb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2013.08.24, Version 0.10.15 (Stable) + +Changes since version 0.10.14: + +* fsevents: create FSEvents thread on demand (Ben Noordhuis) + +* fsevents: use a single thread for interacting with FSEvents, because it's not + thread-safe. (Fedor Indutny) + +* 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) + + 2013.08.22, Version 0.10.14 (Stable), 15d64132151c18b26346afa892444b95e2addad0 Changes since version 0.10.13: diff --git a/src/version.c b/src/version.c index c7f0dcdd..98442a65 100644 --- a/src/version.c +++ b/src/version.c @@ -35,7 +35,7 @@ #define UV_VERSION_MAJOR 0 #define UV_VERSION_MINOR 10 #define UV_VERSION_PATCH 15 -#define UV_VERSION_IS_RELEASE 0 +#define UV_VERSION_IS_RELEASE 1 #define UV_VERSION ((UV_VERSION_MAJOR << 16) | \ From c8b6895eaffa26c66d2af2b573687ca7d061f26a Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Fri, 23 Aug 2013 18:57:14 +0200 Subject: [PATCH 07/20] Now working on v0.10.16 --- ChangeLog | 2 +- src/version.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 45e308fb..82b9867a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -2013.08.24, Version 0.10.15 (Stable) +2013.08.24, Version 0.10.15 (Stable), 221078a8fdd9b853c6b557b3d9a5dd744b4fdd6b Changes since version 0.10.14: diff --git a/src/version.c b/src/version.c index 98442a65..1c4ff179 100644 --- a/src/version.c +++ b/src/version.c @@ -34,8 +34,8 @@ #define UV_VERSION_MAJOR 0 #define UV_VERSION_MINOR 10 -#define UV_VERSION_PATCH 15 -#define UV_VERSION_IS_RELEASE 1 +#define UV_VERSION_PATCH 16 +#define UV_VERSION_IS_RELEASE 0 #define UV_VERSION ((UV_VERSION_MAJOR << 16) | \ From 851a6624161219c0a18be8b5c9fc4e55d24f53c4 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Sat, 24 Aug 2013 15:40:53 +0200 Subject: [PATCH 08/20] windows: make uv_fs_chmod() report errors correctly Before this patch libuv would attempt to use GetLastError() to retrieve the cause of NtQueryInformationFile failure, but that's not how it should be done. --- src/win/fs.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/win/fs.c b/src/win/fs.c index e78bc1b8..8b1bbfb3 100644 --- a/src/win/fs.c +++ b/src/win/fs.c @@ -1062,7 +1062,6 @@ static void fs__chmod(uv_fs_t* req) { static void fs__fchmod(uv_fs_t* req) { int fd = req->fd; - int result; HANDLE handle; NTSTATUS nt_status; IO_STATUS_BLOCK io_status; @@ -1070,7 +1069,7 @@ static void fs__fchmod(uv_fs_t* req) { VERIFY_FD(fd, req); - handle = (HANDLE)_get_osfhandle(fd); + handle = (HANDLE) _get_osfhandle(fd); nt_status = pNtQueryInformationFile(handle, &io_status, @@ -1078,9 +1077,9 @@ static void fs__fchmod(uv_fs_t* req) { sizeof file_info, FileBasicInformation); - if (nt_status != STATUS_SUCCESS) { - result = -1; - goto done; + if (!NT_SUCCESS(nt_status)) { + SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status)); + return; } if (req->mode & _S_IWRITE) { @@ -1095,15 +1094,12 @@ static void fs__fchmod(uv_fs_t* req) { sizeof file_info, FileBasicInformation); - if (nt_status != STATUS_SUCCESS) { - result = -1; - goto done; + if (!NT_SUCCESS(nt_status)) { + SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status)); + return; } - result = 0; - -done: - SET_REQ_RESULT(req, result); + SET_REQ_SUCCESS(req); } From 61b20e8d469eb82292ef4ca885d824f429fe4b2a Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 29 Aug 2013 15:04:27 +0200 Subject: [PATCH 09/20] windows: make uv_shutdown() for write-only pipes work A couple of issues prevented uv_shutdown() from working correctly with write-only pipes. * The pipe handle wasn't opened with the right permissions, so an attempt to probe the state of the write buffer would fail with ERROR_ACCESS_DENIED. * The pipe flags for child process stdio pipes were always set to UV_HANDLE_READABLE and UV_HANDLE_WRITABLE, even in cases where it was actually half-duplex. * There was no code path that lead to closing the pipe handle if the pipe was write-only. --- src/win/pipe.c | 10 ++++++++-- src/win/process-stdio.c | 15 +++++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/win/pipe.c b/src/win/pipe.c index 0fb70eae..fd7418d7 100644 --- a/src/win/pipe.c +++ b/src/win/pipe.c @@ -1528,9 +1528,9 @@ void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle, UNREGISTER_HANDLE_REQ(loop, handle, req); - /* Initialize and optionally start the eof timer. */ - /* This makes no sense if we've already seen EOF. */ if (handle->flags & UV_HANDLE_READABLE) { + /* Initialize and optionally start the eof timer. Only do this if the */ + /* pipe is readable and we haven't seen EOF come in ourselves. */ eof_timer_init(handle); /* If reading start the timer right now. */ @@ -1538,6 +1538,12 @@ void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle, if (handle->flags & UV_HANDLE_READ_PENDING) { eof_timer_start(handle); } + + } else { + /* This pipe is not readable. We can just close it to let the other end */ + /* know that we're done writing. */ + CloseHandle(handle->handle); + handle->handle = INVALID_HANDLE_VALUE; } if (req->cb) { diff --git a/src/win/process-stdio.c b/src/win/process-stdio.c index 89e2cd99..e9cf84da 100644 --- a/src/win/process-stdio.c +++ b/src/win/process-stdio.c @@ -104,12 +104,16 @@ static uv_err_t uv__create_stdio_pipe_pair(uv_loop_t* loop, uv_err_t err; if (flags & UV_READABLE_PIPE) { - server_access |= PIPE_ACCESS_OUTBOUND; + /* The server needs inbound access too, otherwise CreateNamedPipe() */ + /* won't give us the FILE_READ_ATTRIBUTES permission. We need that to */ + /* probe the state of the write buffer when we're trying to shutdown */ + /* the pipe. */ + server_access |= PIPE_ACCESS_OUTBOUND | PIPE_ACCESS_INBOUND; client_access |= GENERIC_READ | FILE_WRITE_ATTRIBUTES; } if (flags & UV_WRITABLE_PIPE) { server_access |= PIPE_ACCESS_INBOUND; - client_access |= GENERIC_WRITE; + client_access |= GENERIC_WRITE | FILE_READ_ATTRIBUTES; } /* Create server pipe handle. */ @@ -163,8 +167,11 @@ static uv_err_t uv__create_stdio_pipe_pair(uv_loop_t* loop, } } - /* The server end is now readable and writable. */ - server_pipe->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; + /* The server end is now readable and/or writable. */ + if (flags & UV_READABLE_PIPE) + server_pipe->flags |= UV_HANDLE_WRITABLE; + if (flags & UV_WRITABLE_PIPE) + server_pipe->flags |= UV_HANDLE_READABLE; *child_pipe_ptr = child_pipe; return uv_ok_; From 39bef32906573a0dd645cd12c510693dbabf76cf Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 5 Sep 2013 08:38:07 +0200 Subject: [PATCH 10/20] windows/fs: wrap multi-statement macros in do..while block --- src/win/fs.c | 49 +++++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/src/win/fs.c b/src/win/fs.c index 8b1bbfb3..2b9e6135 100644 --- a/src/win/fs.c +++ b/src/win/fs.c @@ -41,33 +41,41 @@ #define QUEUE_FS_TP_JOB(loop, req) \ - if (!QueueUserWorkItem(&uv_fs_thread_proc, \ - req, \ - WT_EXECUTEDEFAULT)) { \ - uv__set_sys_error((loop), GetLastError()); \ - return -1; \ - } \ - uv__req_register(loop, req); + do { \ + if (!QueueUserWorkItem(&uv_fs_thread_proc, \ + req, \ + WT_EXECUTEDEFAULT)) { \ + uv__set_sys_error((loop), GetLastError()); \ + return -1; \ + } \ + uv__req_register(loop, req); \ + } while (0) #define SET_UV_LAST_ERROR_FROM_REQ(req) \ - uv__set_error(req->loop, req->errorno, req->sys_errno_); + uv__set_error(req->loop, req->errorno, req->sys_errno_) #define SET_REQ_RESULT(req, result_value) \ - req->result = (result_value); \ - if (req->result == -1) { \ - req->sys_errno_ = _doserrno; \ - req->errorno = uv_translate_sys_error(req->sys_errno_); \ - } + do { \ + req->result = (result_value); \ + if (req->result == -1) { \ + req->sys_errno_ = _doserrno; \ + req->errorno = uv_translate_sys_error(req->sys_errno_); \ + } \ + } while (0) #define SET_REQ_WIN32_ERROR(req, sys_errno) \ - req->result = -1; \ - req->sys_errno_ = (sys_errno); \ - req->errorno = uv_translate_sys_error(req->sys_errno_); + do { \ + req->result = -1; \ + req->sys_errno_ = (sys_errno); \ + req->errorno = uv_translate_sys_error(req->sys_errno_); \ + } while (0) #define SET_REQ_UV_ERROR(req, uv_errno, sys_errno) \ - req->result = -1; \ - req->sys_errno_ = (sys_errno); \ - req->errorno = (uv_errno); + do { \ + req->result = -1; \ + req->sys_errno_ = (sys_errno); \ + req->errorno = (uv_errno); \ + } while (0) #define VERIFY_FD(fd, req) \ if (fd == -1) { \ @@ -78,7 +86,7 @@ } #define FILETIME_TO_TIME_T(filetime) \ - ((*((uint64_t*) &(filetime)) - 116444736000000000ULL) / 10000000ULL); + ((*((uint64_t*) &(filetime)) - 116444736000000000ULL) / 10000000ULL) #define TIME_T_TO_FILETIME(time, filetime_ptr) \ do { \ @@ -512,6 +520,7 @@ end: SET_REQ_RESULT(req, result); } + void fs__close(uv_fs_t* req) { int fd = req->fd; int result; From 812717d0dda831594f91126272b7f5b3e323e184 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 5 Sep 2013 08:46:16 +0200 Subject: [PATCH 11/20] windows/fs: make uv_fs_open() report EINVAL correctly Before, when the user passed an invalid paramter to uv_fs_open, libuv would detect this and call SET_REQ_RESULT to set the result value to -1. SET_REQ_RESULT then stored whatever error code was returned by GetLastError(), which would have no relationship to the actual problem, and might as well be zero. --- src/win/fs.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/win/fs.c b/src/win/fs.c index 2b9e6135..f0ceb092 100644 --- a/src/win/fs.c +++ b/src/win/fs.c @@ -421,8 +421,7 @@ void fs__open(uv_fs_t* req) { access = FILE_GENERIC_READ | FILE_GENERIC_WRITE; break; default: - result = -1; - goto end; + goto einval; } if (flags & _O_APPEND) { @@ -459,8 +458,7 @@ void fs__open(uv_fs_t* req) { disposition = CREATE_ALWAYS; break; default: - result = -1; - goto end; + goto einval; } attributes |= FILE_ATTRIBUTE_NORMAL; @@ -489,8 +487,7 @@ void fs__open(uv_fs_t* req) { attributes |= FILE_FLAG_RANDOM_ACCESS; break; default: - result = -1; - goto end; + goto einval; } /* Setting this flag makes it possible to open a directory. */ @@ -516,8 +513,11 @@ void fs__open(uv_fs_t* req) { return; } result = _open_osfhandle((intptr_t) file, flags); -end: SET_REQ_RESULT(req, result); + return; + + einval: + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); } From faf2c5932c89a714fcd6b9668a9b78a986d20701 Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 5 Sep 2013 08:50:04 +0200 Subject: [PATCH 12/20] windows/fs: handle _open_osfhandle() failure correctly Until now we assumed that _open_osfhandle() would set _doserrno on failure. This assumption was very wrong in one obvious case, namely when the CRT file descriptor table would fill up. In that case errno is set to EMFILE, but GetLastError() returns zero - which makes sense because it's not a win32 error but rather a CRT problem. --- src/win/fs.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/win/fs.c b/src/win/fs.c index f0ceb092..7f3704e8 100644 --- a/src/win/fs.c +++ b/src/win/fs.c @@ -400,7 +400,7 @@ void fs__open(uv_fs_t* req) { DWORD disposition; DWORD attributes = 0; HANDLE file; - int result, current_umask; + int fd, current_umask; int flags = req->file_flags; /* Obtain the active umask. umask() never fails and returns the previous */ @@ -512,8 +512,23 @@ void fs__open(uv_fs_t* req) { } return; } - result = _open_osfhandle((intptr_t) file, flags); - SET_REQ_RESULT(req, result); + + fd = _open_osfhandle((intptr_t) file, flags); + if (fd < 0) { + /* The only known failure mode for _open_osfhandle() is EMFILE, in which + * case GetLastError() will return zero. However we'll try to handle other + * errors as well, should they ever occur. + */ + if (errno == EMFILE) + SET_REQ_UV_ERROR(req, UV_EMFILE, ERROR_TOO_MANY_OPEN_FILES); + else if (GetLastError() != ERROR_SUCCESS) + SET_REQ_WIN32_ERROR(req, GetLastError()); + else + SET_REQ_WIN32_ERROR(req, UV_UNKNOWN); + return; + } + + SET_REQ_RESULT(req, fd); return; einval: From 2bce230d81f4853a23662cbeb26fe98010b1084b Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 5 Sep 2013 16:45:45 +0200 Subject: [PATCH 13/20] 2013.09.06, Version 0.10.16 (Stable) Changes since version 0.10.15: * windows: make uv_shutdown() for write-only pipes work (Bert Belder) * windows: make uv_fs_open() report EINVAL when invalid arguments are passed (Bert Belder) * windows: make uv_fs_open() report _open_osfhandle() failure correctly (Bert Belder) * windows: make uv_fs_chmod() report errors correctly (Bert Belder) * windows: wrap multi-statement macros in do..while block (Bert Belder) --- ChangeLog | 17 +++++++++++++++++ src/version.c | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 82b9867a..bcfc1dff 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2013.09.06, Version 0.10.16 (Stable) + +Changes since version 0.10.15: + +* windows: make uv_shutdown() for write-only pipes work (Bert Belder) + +* windows: make uv_fs_open() report EINVAL when invalid arguments are passed + (Bert Belder) + +* windows: make uv_fs_open() report _open_osfhandle() failure correctly (Bert + Belder) + +* windows: make uv_fs_chmod() report errors correctly (Bert Belder) + +* windows: wrap multi-statement macros in do..while block (Bert Belder) + + 2013.08.24, Version 0.10.15 (Stable), 221078a8fdd9b853c6b557b3d9a5dd744b4fdd6b Changes since version 0.10.14: diff --git a/src/version.c b/src/version.c index 1c4ff179..1cd43837 100644 --- a/src/version.c +++ b/src/version.c @@ -35,7 +35,7 @@ #define UV_VERSION_MAJOR 0 #define UV_VERSION_MINOR 10 #define UV_VERSION_PATCH 16 -#define UV_VERSION_IS_RELEASE 0 +#define UV_VERSION_IS_RELEASE 1 #define UV_VERSION ((UV_VERSION_MAJOR << 16) | \ From 7e5c63c88da5b88f39ea1ec3d6b049438da91bde Mon Sep 17 00:00:00 2001 From: Bert Belder Date: Thu, 5 Sep 2013 16:45:51 +0200 Subject: [PATCH 14/20] Now working on v0.10.17 --- ChangeLog | 2 +- src/version.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index bcfc1dff..2b000d36 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -2013.09.06, Version 0.10.16 (Stable) +2013.09.06, Version 0.10.16 (Stable), 2bce230d81f4853a23662cbeb26fe98010b1084b Changes since version 0.10.15: diff --git a/src/version.c b/src/version.c index 1cd43837..213bf507 100644 --- a/src/version.c +++ b/src/version.c @@ -34,8 +34,8 @@ #define UV_VERSION_MAJOR 0 #define UV_VERSION_MINOR 10 -#define UV_VERSION_PATCH 16 -#define UV_VERSION_IS_RELEASE 1 +#define UV_VERSION_PATCH 17 +#define UV_VERSION_IS_RELEASE 0 #define UV_VERSION ((UV_VERSION_MAJOR << 16) | \ From 712835a6733b578d6e8c1bccdfa7febc6454d3b8 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Thu, 5 Sep 2013 21:32:41 +0200 Subject: [PATCH 15/20] build: remove GCC_WARN_ABOUT_MISSING_NEWLINE Not compatible with non-Apple gcc builds. Fixes the following build error: gcc-4.8: error: unrecognized command line option '-Wnewline-eof' --- common.gypi | 1 - 1 file changed, 1 deletion(-) diff --git a/common.gypi b/common.gypi index 67291fdc..4b240d98 100644 --- a/common.gypi +++ b/common.gypi @@ -171,7 +171,6 @@ 'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES', 'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES', # -fvisibility=hidden 'GCC_THREADSAFE_STATICS': 'NO', # -fno-threadsafe-statics - 'GCC_WARN_ABOUT_MISSING_NEWLINE': 'YES', # -Wnewline-eof 'PREBINDING': 'NO', # No -Wl,-prebind 'USE_HEADERMAP': 'NO', 'OTHER_CFLAGS': [ From 983fa68e9f8bdf77a94fc46526a798a350ec39ec Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 28 Aug 2013 11:55:27 +0200 Subject: [PATCH 16/20] darwin: fix 10.6 build error in fsevents.c Work around an 'initializer element is not constant' build error in src/unix/fsevents.c by turning the const int flags into #defines. Only an issue on OS X 10.6 due to the old compiler it uses. Fixes #908. This is a back-port of commit 82f2472 from the master branch. --- src/unix/fsevents.c | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/src/unix/fsevents.c b/src/unix/fsevents.c index f849c38f..7636b80c 100644 --- a/src/unix/fsevents.c +++ b/src/unix/fsevents.c @@ -46,6 +46,27 @@ void uv__fsevents_loop_delete(uv_loop_t* loop) { #include #include +/* 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; @@ -72,22 +93,6 @@ struct uv__fsevents_event_s { 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); From 9670e0a93540c2f0d86c84a375f2303383c11e7e Mon Sep 17 00:00:00 2001 From: Timothy J Fontaine Date: Tue, 24 Sep 2013 13:40:40 -0700 Subject: [PATCH 17/20] 2013.09.25, Version 0.10.17 (Stable) Changes since version 0.10.16: * build: remove GCC_WARN_ABOUT_MISSING_NEWLINE (Ben Noordhuis) * darwin: fix 10.6 build error in fsevents.c (Ben Noordhuis) --- ChangeLog | 9 +++++++++ src/version.c | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 2b000d36..98fb44f9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2013.09.25, Version 0.10.17 (Stable) + +Changes since version 0.10.16: + +* build: remove GCC_WARN_ABOUT_MISSING_NEWLINE (Ben Noordhuis) + +* darwin: fix 10.6 build error in fsevents.c (Ben Noordhuis) + + 2013.09.06, Version 0.10.16 (Stable), 2bce230d81f4853a23662cbeb26fe98010b1084b Changes since version 0.10.15: diff --git a/src/version.c b/src/version.c index 213bf507..54896017 100644 --- a/src/version.c +++ b/src/version.c @@ -35,7 +35,7 @@ #define UV_VERSION_MAJOR 0 #define UV_VERSION_MINOR 10 #define UV_VERSION_PATCH 17 -#define UV_VERSION_IS_RELEASE 0 +#define UV_VERSION_IS_RELEASE 1 #define UV_VERSION ((UV_VERSION_MAJOR << 16) | \ From e9df7cbcbbcf8972aaf4ae64174a37c46dd62eb2 Mon Sep 17 00:00:00 2001 From: Timothy J Fontaine Date: Tue, 24 Sep 2013 13:40:44 -0700 Subject: [PATCH 18/20] Now working on v0.10.18 --- ChangeLog | 2 +- src/version.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 98fb44f9..e20f002d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -2013.09.25, Version 0.10.17 (Stable) +2013.09.25, Version 0.10.17 (Stable), 9670e0a93540c2f0d86c84a375f2303383c11e7e Changes since version 0.10.16: diff --git a/src/version.c b/src/version.c index 54896017..be1729a5 100644 --- a/src/version.c +++ b/src/version.c @@ -34,8 +34,8 @@ #define UV_VERSION_MAJOR 0 #define UV_VERSION_MINOR 10 -#define UV_VERSION_PATCH 17 -#define UV_VERSION_IS_RELEASE 1 +#define UV_VERSION_PATCH 18 +#define UV_VERSION_IS_RELEASE 0 #define UV_VERSION ((UV_VERSION_MAJOR << 16) | \ From 8fe4ca686bcb069f670b0381e89c008ca814f8ba Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 28 Sep 2013 10:29:53 +0200 Subject: [PATCH 19/20] unix: set O_NONBLOCK in uv_pipe_open() Don't rely on the caller to set the O_NONBLOCK flag on the file descriptor. Prevents sporadic stalls when the file descriptor is in blocking mode and exactly as many bytes are read as there are available; in that case, libuv will try to read again and block. Node.js was guilty of this. Fixes #941. --- src/unix/pipe.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/unix/pipe.c b/src/unix/pipe.c index 4b7f966b..6db16812 100644 --- a/src/unix/pipe.c +++ b/src/unix/pipe.c @@ -160,6 +160,9 @@ int uv_pipe_open(uv_pipe_t* handle, uv_file fd) { return -1; #endif /* defined(__APPLE__) */ + if (uv__nonblock(fd, 1)) + return uv__set_sys_error(handle->loop, errno); + return uv__stream_open((uv_stream_t*)handle, fd, UV_STREAM_READABLE | UV_STREAM_WRITABLE); From 8c9cbee1b1fb19786405bdd92af5edfdab4cdbbe Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sat, 28 Sep 2013 20:50:45 +0200 Subject: [PATCH 20/20] Revert "unix: set O_NONBLOCK in uv_pipe_open()" It turns out that node.js relies on the blocking behavior of pipes in some cases, notably when forking worker processes. Reopens #941. This reverts commit 8fe4ca686bcb069f670b0381e89c008ca814f8ba. --- src/unix/pipe.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/unix/pipe.c b/src/unix/pipe.c index 6db16812..4b7f966b 100644 --- a/src/unix/pipe.c +++ b/src/unix/pipe.c @@ -160,9 +160,6 @@ int uv_pipe_open(uv_pipe_t* handle, uv_file fd) { return -1; #endif /* defined(__APPLE__) */ - if (uv__nonblock(fd, 1)) - return uv__set_sys_error(handle->loop, errno); - return uv__stream_open((uv_stream_t*)handle, fd, UV_STREAM_READABLE | UV_STREAM_WRITABLE);