| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922 |
- /* 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"
- #if TARGET_OS_IPHONE || MAC_OS_X_VERSION_MAX_ALLOWED < 1070
- /* iOS (currently) doesn't provide the FSEvents-API (nor CoreServices) */
- /* macOS prior to 10.7 doesn't provide the full FSEvents API so use kqueue */
- int uv__fsevents_init(uv_fs_event_t* handle) {
- return 0;
- }
- int uv__fsevents_close(uv_fs_event_t* handle) {
- return 0;
- }
- void uv__fsevents_loop_delete(uv_loop_t* loop) {
- }
- #else /* TARGET_OS_IPHONE */
- #include <dlfcn.h>
- #include <assert.h>
- #include <stdlib.h>
- #include <pthread.h>
- #include <CoreFoundation/CFRunLoop.h>
- #include <CoreServices/CoreServices.h>
- /* 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;
- enum uv__cf_loop_signal_type_e {
- kUVCFLoopSignalRegular,
- kUVCFLoopSignalClosing
- };
- typedef enum uv__cf_loop_signal_type_e uv__cf_loop_signal_type_t;
- struct uv__cf_loop_signal_s {
- QUEUE member;
- uv_fs_event_t* handle;
- uv__cf_loop_signal_type_t type;
- };
- struct uv__fsevents_event_s {
- QUEUE member;
- int events;
- char path[1];
- };
- struct uv__cf_loop_state_s {
- CFRunLoopRef loop;
- CFRunLoopSourceRef signal_source;
- int fsevent_need_reschedule;
- FSEventStreamRef fsevent_stream;
- uv_sem_t fsevent_sem;
- uv_mutex_t fsevent_mutex;
- void* fsevent_handles[2];
- unsigned int fsevent_handle_count;
- };
- /* Forward declarations */
- static void uv__cf_loop_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,
- uv__cf_loop_signal_type_t type);
- /* Lazy-loaded by uv__fsevents_global_init(). */
- static CFArrayRef (*pCFArrayCreate)(CFAllocatorRef,
- const void**,
- CFIndex,
- const CFArrayCallBacks*);
- static void (*pCFRelease)(CFTypeRef);
- static void (*pCFRunLoopAddSource)(CFRunLoopRef,
- CFRunLoopSourceRef,
- CFStringRef);
- static CFRunLoopRef (*pCFRunLoopGetCurrent)(void);
- static void (*pCFRunLoopRemoveSource)(CFRunLoopRef,
- CFRunLoopSourceRef,
- CFStringRef);
- static void (*pCFRunLoopRun)(void);
- static CFRunLoopSourceRef (*pCFRunLoopSourceCreate)(CFAllocatorRef,
- CFIndex,
- CFRunLoopSourceContext*);
- static void (*pCFRunLoopSourceSignal)(CFRunLoopSourceRef);
- static void (*pCFRunLoopStop)(CFRunLoopRef);
- static void (*pCFRunLoopWakeUp)(CFRunLoopRef);
- static CFStringRef (*pCFStringCreateWithFileSystemRepresentation)(
- CFAllocatorRef,
- const char*);
- static CFStringEncoding (*pCFStringGetSystemEncoding)(void);
- static CFStringRef (*pkCFRunLoopDefaultMode);
- static FSEventStreamRef (*pFSEventStreamCreate)(CFAllocatorRef,
- FSEventStreamCallback,
- FSEventStreamContext*,
- CFArrayRef,
- FSEventStreamEventId,
- CFTimeInterval,
- FSEventStreamCreateFlags);
- static void (*pFSEventStreamFlushSync)(FSEventStreamRef);
- static void (*pFSEventStreamInvalidate)(FSEventStreamRef);
- static void (*pFSEventStreamRelease)(FSEventStreamRef);
- static void (*pFSEventStreamScheduleWithRunLoop)(FSEventStreamRef,
- CFRunLoopRef,
- CFStringRef);
- static Boolean (*pFSEventStreamStart)(FSEventStreamRef);
- static void (*pFSEventStreamStop)(FSEventStreamRef);
- #define UV__FSEVENTS_PROCESS(handle, block) \
- do { \
- QUEUE events; \
- QUEUE* q; \
- uv__fsevents_event_t* event; \
- int err; \
- uv_mutex_lock(&(handle)->cf_mutex); \
- /* Split-off all events and empty original queue */ \
- QUEUE_MOVE(&(handle)->cf_events, &events); \
- /* Get error (if any) and zero original one */ \
- err = (handle)->cf_error; \
- (handle)->cf_error = 0; \
- uv_mutex_unlock(&(handle)->cf_mutex); \
- /* Loop through events, deallocating each after processing */ \
- while (!QUEUE_EMPTY(&events)) { \
- q = QUEUE_HEAD(&events); \
- event = QUEUE_DATA(q, uv__fsevents_event_t, member); \
- QUEUE_REMOVE(q); \
- /* NOTE: Checking uv__is_active() is required here, because handle \
- * callback may close handle and invoking it after it will lead to \
- * incorrect behaviour */ \
- if (!uv__is_closing((handle)) && uv__is_active((handle))) \
- block \
- /* Free allocated data */ \
- uv__free(event); \
- } \
- if (err != 0 && !uv__is_closing((handle)) && uv__is_active((handle))) \
- (handle)->cb((handle), NULL, 0, err); \
- } 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) {
- uv_fs_event_t* handle;
- handle = cb->data;
- UV__FSEVENTS_PROCESS(handle, {
- handle->cb(handle, event->path[0] ? event->path : NULL, event->events, 0);
- });
- }
- /* Runs in CF thread, pushed event into handle's event list */
- static void uv__fsevents_push_event(uv_fs_event_t* handle,
- QUEUE* events,
- int err) {
- assert(events != NULL || err != 0);
- uv_mutex_lock(&handle->cf_mutex);
- /* Concatenate two queues */
- if (events != NULL)
- QUEUE_ADD(&handle->cf_events, events);
- /* Propagate error */
- if (err != 0)
- handle->cf_error = err;
- uv_mutex_unlock(&handle->cf_mutex);
- uv_async_send(handle->cf_cb);
- }
- /* Runs in CF thread, when there're events in FSEventStream */
- 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;
- 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;
- FSEventStreamEventFlags flags;
- QUEUE head;
- loop = info;
- state = loop->cf_state;
- assert(state != NULL);
- paths = eventPaths;
- /* For each handle */
- uv_mutex_lock(&state->fsevent_mutex);
- QUEUE_FOREACH(q, &state->fsevent_handles) {
- handle = QUEUE_DATA(q, uv_fs_event_t, cf_member);
- QUEUE_INIT(&head);
- /* Process and filter out events */
- for (i = 0; i < numEvents; i++) {
- flags = eventFlags[i];
- /* Ignore system events */
- if (flags & kFSEventsSystem)
- continue;
- path = paths[i];
- len = strlen(path);
- if (handle->realpath_len == 0)
- continue; /* This should be unreachable */
- /* Filter out paths that are outside handle's request */
- if (len < handle->realpath_len)
- continue;
- if (handle->realpath_len != len &&
- path[handle->realpath_len] != '/')
- /* Make sure that realpath actually named a directory,
- * or that we matched the whole string */
- continue;
- if (memcmp(path, handle->realpath, handle->realpath_len) != 0)
- continue;
- if (!(handle->realpath_len == 1 && handle->realpath[0] == '/')) {
- /* Remove common prefix, unless the watched folder is "/" */
- path += handle->realpath_len;
- len -= handle->realpath_len;
- /* Ignore events with path equal to directory itself */
- if (len <= 1 && (flags & kFSEventStreamEventFlagItemIsDir))
- continue;
- if (len == 0) {
- /* Since we're using fsevents to watch the file itself,
- * realpath == path, and we now need to get the basename of the file back
- * (for commonality with other codepaths and platforms). */
- while (len < handle->realpath_len && path[-1] != '/') {
- path--;
- len++;
- }
- /* Created and Removed seem to be always set, but don't make sense */
- flags &= ~kFSEventsRenamed;
- } else {
- /* Skip forward slash */
- path++;
- len--;
- }
- }
- /* Do not emit events from subdirectories (without option set) */
- if ((handle->cf_flags & UV_FS_EVENT_RECURSIVE) == 0 && *path != '\0') {
- pos = strchr(path + 1, '/');
- if (pos != NULL)
- continue;
- }
- event = uv__malloc(sizeof(*event) + len);
- if (event == NULL)
- break;
- memset(event, 0, sizeof(*event));
- memcpy(event->path, path, len + 1);
- event->events = UV_RENAME;
- if (0 == (flags & kFSEventsRenamed)) {
- if (0 != (flags & kFSEventsModified) ||
- 0 == (flags & kFSEventStreamEventFlagItemIsDir))
- event->events = UV_CHANGE;
- }
- QUEUE_INSERT_TAIL(&head, &event->member);
- }
- if (!QUEUE_EMPTY(&head))
- uv__fsevents_push_event(handle, &head, 0);
- }
- uv_mutex_unlock(&state->fsevent_mutex);
- }
- /* Runs in CF thread */
- static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) {
- uv__cf_loop_state_t* state;
- FSEventStreamContext ctx;
- FSEventStreamRef ref;
- CFAbsoluteTime latency;
- FSEventStreamCreateFlags flags;
- /* Initialize context */
- ctx.version = 0;
- ctx.info = loop;
- ctx.retain = NULL;
- ctx.release = NULL;
- ctx.copyDescription = NULL;
- latency = 0.05;
- /* Explanation of selected flags:
- * 1. NoDefer - without this flag, events that are happening continuously
- * (i.e. each event is happening after time interval less than `latency`,
- * counted from previous event), will be deferred and passed to callback
- * once they'll either fill whole OS buffer, or when this continuous stream
- * will stop (i.e. there'll be delay between events, bigger than
- * `latency`).
- * Specifying this flag will invoke callback after `latency` time passed
- * since event.
- * 2. FileEvents - fire callback for file changes too (by default it is firing
- * it only for directory changes).
- */
- flags = kFSEventStreamCreateFlagNoDefer | 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 = pFSEventStreamCreate(NULL,
- &uv__fsevents_event_cb,
- &ctx,
- paths,
- kFSEventStreamEventIdSinceNow,
- latency,
- flags);
- assert(ref != NULL);
- state = loop->cf_state;
- pFSEventStreamScheduleWithRunLoop(ref,
- state->loop,
- *pkCFRunLoopDefaultMode);
- if (!pFSEventStreamStart(ref)) {
- pFSEventStreamInvalidate(ref);
- pFSEventStreamRelease(ref);
- return UV_EMFILE;
- }
- state->fsevent_stream = ref;
- return 0;
- }
- /* Runs in CF thread */
- static void uv__fsevents_destroy_stream(uv_loop_t* loop) {
- uv__cf_loop_state_t* state;
- state = loop->cf_state;
- if (state->fsevent_stream == NULL)
- return;
- /* Stop emitting events */
- pFSEventStreamStop(state->fsevent_stream);
- /* Release stream */
- pFSEventStreamInvalidate(state->fsevent_stream);
- pFSEventStreamRelease(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_signal_type_t type) {
- uv__cf_loop_state_t* state;
- QUEUE* q;
- uv_fs_event_t* curr;
- CFArrayRef cf_paths;
- CFStringRef* paths;
- unsigned int i;
- int err;
- unsigned int path_count;
- state = handle->loop->cf_state;
- paths = NULL;
- cf_paths = NULL;
- err = 0;
- /* NOTE: `i` is used in deallocation loop below */
- i = 0;
- /* Optimization to prevent O(n^2) time spent when starting to watch
- * many files simultaneously
- */
- uv_mutex_lock(&state->fsevent_mutex);
- if (state->fsevent_need_reschedule == 0) {
- uv_mutex_unlock(&state->fsevent_mutex);
- goto final;
- }
- state->fsevent_need_reschedule = 0;
- uv_mutex_unlock(&state->fsevent_mutex);
- /* Destroy previous FSEventStream */
- uv__fsevents_destroy_stream(handle->loop);
- /* Any failure below will be a memory failure */
- err = UV_ENOMEM;
- /* Create list of all watched paths */
- uv_mutex_lock(&state->fsevent_mutex);
- path_count = state->fsevent_handle_count;
- if (path_count != 0) {
- paths = uv__malloc(sizeof(*paths) * path_count);
- if (paths == NULL) {
- uv_mutex_unlock(&state->fsevent_mutex);
- goto final;
- }
- q = &state->fsevent_handles;
- for (; 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] =
- pCFStringCreateWithFileSystemRepresentation(NULL, curr->realpath);
- if (paths[i] == NULL) {
- uv_mutex_unlock(&state->fsevent_mutex);
- goto final;
- }
- }
- }
- uv_mutex_unlock(&state->fsevent_mutex);
- err = 0;
- if (path_count != 0) {
- /* Create new FSEventStream */
- cf_paths = pCFArrayCreate(NULL, (const void**) paths, path_count, NULL);
- if (cf_paths == NULL) {
- err = UV_ENOMEM;
- goto final;
- }
- err = uv__fsevents_create_stream(handle->loop, cf_paths);
- }
- final:
- /* Deallocate all paths in case of failure */
- if (err != 0) {
- if (cf_paths == NULL) {
- while (i != 0)
- pCFRelease(paths[--i]);
- uv__free(paths);
- } else {
- /* CFArray takes ownership of both strings and original C-array */
- pCFRelease(cf_paths);
- }
- /* Broadcast error to all handles */
- uv_mutex_lock(&state->fsevent_mutex);
- QUEUE_FOREACH(q, &state->fsevent_handles) {
- curr = QUEUE_DATA(q, uv_fs_event_t, cf_member);
- uv__fsevents_push_event(curr, NULL, err);
- }
- uv_mutex_unlock(&state->fsevent_mutex);
- }
- /*
- * 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 (type == kUVCFLoopSignalClosing)
- uv_sem_post(&state->fsevent_sem);
- }
- static int uv__fsevents_global_init(void) {
- static pthread_mutex_t global_init_mutex = PTHREAD_MUTEX_INITIALIZER;
- static void* core_foundation_handle;
- static void* core_services_handle;
- int err;
- err = 0;
- pthread_mutex_lock(&global_init_mutex);
- if (core_foundation_handle != NULL)
- goto out;
- /* The libraries are never unloaded because we currently don't have a good
- * mechanism for keeping a reference count. It's unlikely to be an issue
- * but if it ever becomes one, we can turn the dynamic library handles into
- * per-event loop properties and have the dynamic linker keep track for us.
- */
- err = UV_ENOSYS;
- core_foundation_handle = dlopen("/System/Library/Frameworks/"
- "CoreFoundation.framework/"
- "Versions/A/CoreFoundation",
- RTLD_LAZY | RTLD_LOCAL);
- if (core_foundation_handle == NULL)
- goto out;
- core_services_handle = dlopen("/System/Library/Frameworks/"
- "CoreServices.framework/"
- "Versions/A/CoreServices",
- RTLD_LAZY | RTLD_LOCAL);
- if (core_services_handle == NULL)
- goto out;
- err = UV_ENOENT;
- #define V(handle, symbol) \
- do { \
- *(void **)(&p ## symbol) = dlsym((handle), #symbol); \
- if (p ## symbol == NULL) \
- goto out; \
- } \
- while (0)
- V(core_foundation_handle, CFArrayCreate);
- V(core_foundation_handle, CFRelease);
- V(core_foundation_handle, CFRunLoopAddSource);
- V(core_foundation_handle, CFRunLoopGetCurrent);
- V(core_foundation_handle, CFRunLoopRemoveSource);
- V(core_foundation_handle, CFRunLoopRun);
- V(core_foundation_handle, CFRunLoopSourceCreate);
- V(core_foundation_handle, CFRunLoopSourceSignal);
- V(core_foundation_handle, CFRunLoopStop);
- V(core_foundation_handle, CFRunLoopWakeUp);
- V(core_foundation_handle, CFStringCreateWithFileSystemRepresentation);
- V(core_foundation_handle, CFStringGetSystemEncoding);
- V(core_foundation_handle, kCFRunLoopDefaultMode);
- V(core_services_handle, FSEventStreamCreate);
- V(core_services_handle, FSEventStreamFlushSync);
- V(core_services_handle, FSEventStreamInvalidate);
- V(core_services_handle, FSEventStreamRelease);
- V(core_services_handle, FSEventStreamScheduleWithRunLoop);
- V(core_services_handle, FSEventStreamStart);
- V(core_services_handle, FSEventStreamStop);
- #undef V
- err = 0;
- out:
- if (err && core_services_handle != NULL) {
- dlclose(core_services_handle);
- core_services_handle = NULL;
- }
- if (err && core_foundation_handle != NULL) {
- dlclose(core_foundation_handle);
- core_foundation_handle = NULL;
- }
- pthread_mutex_unlock(&global_init_mutex);
- return err;
- }
- /* 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_state != NULL)
- return 0;
- err = uv__fsevents_global_init();
- if (err)
- return err;
- state = uv__calloc(1, sizeof(*state));
- if (state == NULL)
- return UV_ENOMEM;
- err = uv_mutex_init(&loop->cf_mutex);
- if (err)
- goto fail_mutex_init;
- err = uv_sem_init(&loop->cf_sem, 0);
- if (err)
- 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;
- state->signal_source = pCFRunLoopSourceCreate(NULL, 0, &ctx);
- if (state->signal_source == NULL) {
- err = UV_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, 4 * PTHREAD_STACK_MIN))
- abort();
- loop->cf_state = state;
- /* uv_thread_t is an alias for pthread_t. */
- err = UV__ERR(pthread_create(&loop->cf_thread, attr, uv__cf_loop_runner, loop));
- if (attr != NULL)
- pthread_attr_destroy(attr);
- if (err)
- goto fail_thread_create;
- /* Synchronize threads */
- uv_sem_wait(&loop->cf_sem);
- 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);
- fail_mutex_init:
- uv__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_state == NULL)
- return;
- if (uv__cf_loop_signal(loop, NULL, kUVCFLoopSignalRegular) != 0)
- abort();
- uv_thread_join(&loop->cf_thread);
- uv_sem_destroy(&loop->cf_sem);
- uv_mutex_destroy(&loop->cf_mutex);
- /* Free any remaining data */
- while (!QUEUE_EMPTY(&loop->cf_signals)) {
- q = QUEUE_HEAD(&loop->cf_signals);
- s = QUEUE_DATA(q, uv__cf_loop_signal_t, member);
- QUEUE_REMOVE(q);
- uv__free(s);
- }
- /* Destroy state */
- state = loop->cf_state;
- uv_sem_destroy(&state->fsevent_sem);
- uv_mutex_destroy(&state->fsevent_mutex);
- pCFRelease(state->signal_source);
- uv__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;
- state = loop->cf_state;
- state->loop = pCFRunLoopGetCurrent();
- pCFRunLoopAddSource(state->loop,
- state->signal_source,
- *pkCFRunLoopDefaultMode);
- uv_sem_post(&loop->cf_sem);
- pCFRunLoopRun();
- pCFRunLoopRemoveSource(state->loop,
- state->signal_source,
- *pkCFRunLoopDefaultMode);
- 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;
- uv_mutex_lock(&loop->cf_mutex);
- QUEUE_MOVE(&loop->cf_signals, &split_head);
- uv_mutex_unlock(&loop->cf_mutex);
- while (!QUEUE_EMPTY(&split_head)) {
- item = QUEUE_HEAD(&split_head);
- QUEUE_REMOVE(item);
- s = QUEUE_DATA(item, uv__cf_loop_signal_t, member);
- /* This was a termination signal */
- if (s->handle == NULL)
- pCFRunLoopStop(state->loop);
- else
- uv__fsevents_reschedule(s->handle, s->type);
- uv__free(s);
- }
- }
- /* 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_type_t type) {
- uv__cf_loop_signal_t* item;
- uv__cf_loop_state_t* state;
- item = uv__malloc(sizeof(*item));
- if (item == NULL)
- return UV_ENOMEM;
- item->handle = handle;
- item->type = type;
- uv_mutex_lock(&loop->cf_mutex);
- QUEUE_INSERT_TAIL(&loop->cf_signals, &item->member);
- uv_mutex_unlock(&loop->cf_mutex);
- state = loop->cf_state;
- assert(state != NULL);
- pCFRunLoopSourceSignal(state->signal_source);
- pCFRunLoopWakeUp(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)
- return err;
- /* Get absolute path to file */
- handle->realpath = realpath(handle->path, NULL);
- if (handle->realpath == NULL)
- return UV__ERR(errno);
- handle->realpath_len = strlen(handle->realpath);
- /* Initialize event queue */
- QUEUE_INIT(&handle->cf_events);
- handle->cf_error = 0;
- /*
- * Events will occur in other thread.
- * Initialize callback for getting them back into event loop's thread
- */
- handle->cf_cb = uv__malloc(sizeof(*handle->cf_cb));
- if (handle->cf_cb == NULL) {
- err = UV_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);
- err = uv_mutex_init(&handle->cf_mutex);
- if (err)
- goto fail_cf_mutex_init;
- /* Insert handle into the list */
- state = handle->loop->cf_state;
- uv_mutex_lock(&state->fsevent_mutex);
- QUEUE_INSERT_TAIL(&state->fsevent_handles, &handle->cf_member);
- state->fsevent_handle_count++;
- state->fsevent_need_reschedule = 1;
- uv_mutex_unlock(&state->fsevent_mutex);
- /* Reschedule FSEventStream */
- assert(handle != NULL);
- err = uv__cf_loop_signal(handle->loop, handle, kUVCFLoopSignalRegular);
- if (err)
- goto fail_loop_signal;
- return 0;
- fail_loop_signal:
- uv_mutex_destroy(&handle->cf_mutex);
- fail_cf_mutex_init:
- uv__free(handle->cf_cb);
- handle->cf_cb = NULL;
- fail_cf_cb_malloc:
- uv__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 UV_EINVAL;
- /* 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, kUVCFLoopSignalClosing);
- if (err)
- return UV__ERR(err);
- /* Wait for deinitialization */
- uv_sem_wait(&state->fsevent_sem);
- uv_close((uv_handle_t*) handle->cf_cb, (uv_close_cb) uv__free);
- handle->cf_cb = NULL;
- /* Free data in queue */
- UV__FSEVENTS_PROCESS(handle, {
- /* NOP */
- });
- uv_mutex_destroy(&handle->cf_mutex);
- uv__free(handle->realpath);
- handle->realpath = NULL;
- handle->realpath_len = 0;
- return 0;
- }
- #endif /* TARGET_OS_IPHONE */
|