mirror of https://github.com/lukechilds/node.git
Ben Noordhuis
12 years ago
27 changed files with 896 additions and 149 deletions
@ -0,0 +1,225 @@ |
|||
/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
|
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to |
|||
* deal in the Software without restriction, including without limitation the |
|||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
|||
* sell copies of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in |
|||
* all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|||
* IN THE SOFTWARE. |
|||
*/ |
|||
|
|||
#include "uv.h" |
|||
#include "internal.h" |
|||
|
|||
#include <assert.h> |
|||
#include <stdlib.h> |
|||
#include <CoreServices/CoreServices.h> |
|||
|
|||
typedef struct uv__fsevents_event_s uv__fsevents_event_t; |
|||
|
|||
struct uv__fsevents_event_s { |
|||
int events; |
|||
ngx_queue_t member; |
|||
char path[1]; |
|||
}; |
|||
|
|||
|
|||
#define UV__FSEVENTS_WALK(handle, block) \ |
|||
{ \ |
|||
ngx_queue_t* curr; \ |
|||
ngx_queue_t split_head; \ |
|||
uv__fsevents_event_t* event; \ |
|||
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); \ |
|||
} \ |
|||
uv_mutex_unlock(&(handle)->cf_mutex); \ |
|||
while (!ngx_queue_empty(&split_head)) { \ |
|||
curr = ngx_queue_head(&split_head); \ |
|||
/* Invoke callback */ \ |
|||
event = ngx_queue_data(curr, uv__fsevents_event_t, member); \ |
|||
ngx_queue_remove(curr); \ |
|||
/* Invoke block code, but only if handle wasn't closed */ \ |
|||
if (((handle)->flags & (UV_CLOSING | UV_CLOSED)) == 0) \ |
|||
block \ |
|||
/* Free allocated data */ \ |
|||
free(event); \ |
|||
} \ |
|||
} |
|||
|
|||
|
|||
void uv__fsevents_cb(uv_async_t* cb, int status) { |
|||
uv_fs_event_t* handle; |
|||
|
|||
handle = cb->data; |
|||
|
|||
UV__FSEVENTS_WALK(handle, { |
|||
if (handle->fd != -1) |
|||
handle->cb(handle, event->path, event->events, 0); |
|||
}) |
|||
|
|||
if ((handle->flags & (UV_CLOSING | UV_CLOSED)) == 0 && handle->fd == -1) |
|||
uv__fsevents_close(handle); |
|||
} |
|||
|
|||
|
|||
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; |
|||
uv_fs_event_t* handle; |
|||
uv__fsevents_event_t* event; |
|||
ngx_queue_t add_list; |
|||
|
|||
handle = info; |
|||
paths = eventPaths; |
|||
ngx_queue_init(&add_list); |
|||
|
|||
for (i = 0; i < numEvents; i++) { |
|||
/* Ignore system events */ |
|||
if (eventFlags[i] & (kFSEventStreamEventFlagUserDropped | |
|||
kFSEventStreamEventFlagKernelDropped | |
|||
kFSEventStreamEventFlagEventIdsWrapped | |
|||
kFSEventStreamEventFlagHistoryDone | |
|||
kFSEventStreamEventFlagMount | |
|||
kFSEventStreamEventFlagUnmount)) { |
|||
continue; |
|||
} |
|||
|
|||
/* TODO: Report errors */ |
|||
len = strlen(paths[i]); |
|||
event = malloc(sizeof(*event) + len); |
|||
if (event == NULL) |
|||
break; |
|||
|
|||
memcpy(event->path, paths[i], len + 1); |
|||
|
|||
if (eventFlags[i] & kFSEventStreamEventFlagItemModified) |
|||
event->events = UV_CHANGE; |
|||
else |
|||
event->events = UV_RENAME; |
|||
|
|||
ngx_queue_insert_tail(&add_list, &event->member); |
|||
} |
|||
uv_mutex_lock(&handle->cf_mutex); |
|||
ngx_queue_add(&handle->cf_events, &add_list); |
|||
uv_mutex_unlock(&handle->cf_mutex); |
|||
|
|||
uv_async_send(handle->cf_cb); |
|||
} |
|||
|
|||
|
|||
void uv__fsevents_schedule(void* arg) { |
|||
uv_fs_event_t* handle; |
|||
|
|||
handle = arg; |
|||
FSEventStreamScheduleWithRunLoop(handle->cf_eventstream, |
|||
handle->loop->cf_loop, |
|||
kCFRunLoopDefaultMode); |
|||
FSEventStreamStart(handle->cf_eventstream); |
|||
uv_sem_post(&handle->cf_sem); |
|||
} |
|||
|
|||
|
|||
int uv__fsevents_init(uv_fs_event_t* handle) { |
|||
FSEventStreamContext ctx; |
|||
FSEventStreamRef ref; |
|||
CFStringRef path; |
|||
CFArrayRef paths; |
|||
CFAbsoluteTime latency; |
|||
FSEventStreamCreateFlags flags; |
|||
|
|||
/* Initialize context */ |
|||
ctx.version = 0; |
|||
ctx.info = handle; |
|||
ctx.retain = NULL; |
|||
ctx.release = NULL; |
|||
ctx.copyDescription = NULL; |
|||
|
|||
/* 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; |
|||
|
|||
/*
|
|||
* 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); |
|||
|
|||
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); |
|||
|
|||
uv__cf_loop_signal(handle->loop, uv__fsevents_schedule, handle); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
int uv__fsevents_close(uv_fs_event_t* handle) { |
|||
if (handle->cf_eventstream == NULL) |
|||
return -1; |
|||
|
|||
/* Ensure that event stream was scheduled */ |
|||
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); |
|||
|
|||
/* Free data in queue */ |
|||
UV__FSEVENTS_WALK(handle, { |
|||
/* NOP */ |
|||
}) |
|||
|
|||
uv_mutex_destroy(&handle->cf_mutex); |
|||
uv_sem_destroy(&handle->cf_sem); |
|||
|
|||
return 0; |
|||
} |
Loading…
Reference in new issue