mirror of https://github.com/nodejs/node.git
uv: upgrade to v0.11.9
This commit is contained in:
parent
48976d29f4
commit
6fa8398853
|
@ -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:
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
static int uv__cf_loop_signal(uv_loop_t* loop, uv_fs_event_t* handle);
|
||||
|
||||
#define UV__FSEVENTS_WALK(handle, block) \
|
||||
{ \
|
||||
QUEUE* curr; \
|
||||
QUEUE 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); \
|
||||
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;
|
||||
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;
|
||||
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 */
|
||||
QUEUE_FOREACH(q, &state->fsevent_handles) {
|
||||
handle = 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;
|
||||
|
||||
|
@ -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);
|
||||
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);
|
||||
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,
|
||||
|
@ -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;
|
||||
|
||||
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;
|
||||
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);
|
||||
|
||||
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);
|
||||
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);
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue