• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2  * Permission is hereby granted, free of charge, to any person obtaining a copy
3  * of this software and associated documentation files (the "Software"), to
4  * deal in the Software without restriction, including without limitation the
5  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
6  * sell copies of the Software, and to permit persons to whom the Software is
7  * furnished to do so, subject to the following conditions:
8  *
9  * The above copyright notice and this permission notice shall be included in
10  * all copies or substantial portions of the Software.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
17  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
18  * IN THE SOFTWARE.
19  */
20 
21 #include "uv.h"
22 #include "internal.h"
23 
24 #if TARGET_OS_IPHONE || MAC_OS_X_VERSION_MAX_ALLOWED < 1070
25 
26 /* iOS (currently) doesn't provide the FSEvents-API (nor CoreServices) */
27 /* macOS prior to 10.7 doesn't provide the full FSEvents API so use kqueue */
28 
uv__fsevents_init(uv_fs_event_t * handle)29 int uv__fsevents_init(uv_fs_event_t* handle) {
30   return 0;
31 }
32 
33 
uv__fsevents_close(uv_fs_event_t * handle)34 int uv__fsevents_close(uv_fs_event_t* handle) {
35   return 0;
36 }
37 
38 
uv__fsevents_loop_delete(uv_loop_t * loop)39 void uv__fsevents_loop_delete(uv_loop_t* loop) {
40 }
41 
42 #else /* TARGET_OS_IPHONE */
43 
44 #include "darwin-stub.h"
45 
46 #include <dlfcn.h>
47 #include <assert.h>
48 #include <stdlib.h>
49 #include <pthread.h>
50 
51 static const int kFSEventsModified =
52     kFSEventStreamEventFlagItemChangeOwner |
53     kFSEventStreamEventFlagItemFinderInfoMod |
54     kFSEventStreamEventFlagItemInodeMetaMod |
55     kFSEventStreamEventFlagItemModified |
56     kFSEventStreamEventFlagItemXattrMod;
57 
58 static const int kFSEventsRenamed =
59     kFSEventStreamEventFlagItemCreated |
60     kFSEventStreamEventFlagItemRemoved |
61     kFSEventStreamEventFlagItemRenamed;
62 
63 static const int kFSEventsSystem =
64     kFSEventStreamEventFlagUserDropped |
65     kFSEventStreamEventFlagKernelDropped |
66     kFSEventStreamEventFlagEventIdsWrapped |
67     kFSEventStreamEventFlagHistoryDone |
68     kFSEventStreamEventFlagMount |
69     kFSEventStreamEventFlagUnmount |
70     kFSEventStreamEventFlagRootChanged;
71 
72 typedef struct uv__fsevents_event_s uv__fsevents_event_t;
73 typedef struct uv__cf_loop_signal_s uv__cf_loop_signal_t;
74 typedef struct uv__cf_loop_state_s uv__cf_loop_state_t;
75 
76 enum uv__cf_loop_signal_type_e {
77   kUVCFLoopSignalRegular,
78   kUVCFLoopSignalClosing
79 };
80 typedef enum uv__cf_loop_signal_type_e uv__cf_loop_signal_type_t;
81 
82 struct uv__cf_loop_signal_s {
83   struct uv__queue member;
84   uv_fs_event_t* handle;
85   uv__cf_loop_signal_type_t type;
86 };
87 
88 struct uv__fsevents_event_s {
89   struct uv__queue member;
90   int events;
91   char path[1];
92 };
93 
94 struct uv__cf_loop_state_s {
95   CFRunLoopRef loop;
96   CFRunLoopSourceRef signal_source;
97   int fsevent_need_reschedule;
98   FSEventStreamRef fsevent_stream;
99   uv_sem_t fsevent_sem;
100   uv_mutex_t fsevent_mutex;
101   struct uv__queue fsevent_handles;
102   unsigned int fsevent_handle_count;
103 };
104 
105 /* Forward declarations */
106 static void uv__cf_loop_cb(void* arg);
107 static void* uv__cf_loop_runner(void* arg);
108 static int uv__cf_loop_signal(uv_loop_t* loop,
109                               uv_fs_event_t* handle,
110                               uv__cf_loop_signal_type_t type);
111 
112 /* Lazy-loaded by uv__fsevents_global_init(). */
113 static CFArrayRef (*pCFArrayCreate)(CFAllocatorRef,
114                                     const void**,
115                                     CFIndex,
116                                     const CFArrayCallBacks*);
117 static void (*pCFRelease)(CFTypeRef);
118 static void (*pCFRunLoopAddSource)(CFRunLoopRef,
119                                    CFRunLoopSourceRef,
120                                    CFStringRef);
121 static CFRunLoopRef (*pCFRunLoopGetCurrent)(void);
122 static void (*pCFRunLoopRemoveSource)(CFRunLoopRef,
123                                       CFRunLoopSourceRef,
124                                       CFStringRef);
125 static void (*pCFRunLoopRun)(void);
126 static CFRunLoopSourceRef (*pCFRunLoopSourceCreate)(CFAllocatorRef,
127                                                     CFIndex,
128                                                     CFRunLoopSourceContext*);
129 static void (*pCFRunLoopSourceSignal)(CFRunLoopSourceRef);
130 static void (*pCFRunLoopStop)(CFRunLoopRef);
131 static void (*pCFRunLoopWakeUp)(CFRunLoopRef);
132 static CFStringRef (*pCFStringCreateWithFileSystemRepresentation)(
133     CFAllocatorRef,
134     const char*);
135 static CFStringRef (*pkCFRunLoopDefaultMode);
136 static FSEventStreamRef (*pFSEventStreamCreate)(CFAllocatorRef,
137                                                 FSEventStreamCallback,
138                                                 FSEventStreamContext*,
139                                                 CFArrayRef,
140                                                 FSEventStreamEventId,
141                                                 CFTimeInterval,
142                                                 FSEventStreamCreateFlags);
143 static void (*pFSEventStreamInvalidate)(FSEventStreamRef);
144 static void (*pFSEventStreamRelease)(FSEventStreamRef);
145 static void (*pFSEventStreamScheduleWithRunLoop)(FSEventStreamRef,
146                                                  CFRunLoopRef,
147                                                  CFStringRef);
148 static int (*pFSEventStreamStart)(FSEventStreamRef);
149 static void (*pFSEventStreamStop)(FSEventStreamRef);
150 
151 #define UV__FSEVENTS_PROCESS(handle, block)                                   \
152     do {                                                                      \
153       struct uv__queue events;                                                \
154       struct uv__queue* q;                                                    \
155       uv__fsevents_event_t* event;                                            \
156       int err;                                                                \
157       uv_mutex_lock(&(handle)->cf_mutex);                                     \
158       /* Split-off all events and empty original queue */                     \
159       uv__queue_move(&(handle)->cf_events, &events);                          \
160       /* Get error (if any) and zero original one */                          \
161       err = (handle)->cf_error;                                               \
162       (handle)->cf_error = 0;                                                 \
163       uv_mutex_unlock(&(handle)->cf_mutex);                                   \
164       /* Loop through events, deallocating each after processing */           \
165       while (!uv__queue_empty(&events)) {                                     \
166         q = uv__queue_head(&events);                                          \
167         event = uv__queue_data(q, uv__fsevents_event_t, member);              \
168         uv__queue_remove(q);                                                  \
169         /* NOTE: Checking uv__is_active() is required here, because handle    \
170          * callback may close handle and invoking it after it will lead to    \
171          * incorrect behaviour */                                             \
172         if (!uv__is_closing((handle)) && uv__is_active((handle)))             \
173           block                                                               \
174         /* Free allocated data */                                             \
175         uv__free(event);                                                      \
176       }                                                                       \
177       if (err != 0 && !uv__is_closing((handle)) && uv__is_active((handle)))   \
178         (handle)->cb((handle), NULL, 0, err);                                 \
179     } while (0)
180 
181 
182 /* Runs in UV loop's thread, when there're events to report to handle */
uv__fsevents_cb(uv_async_t * cb)183 static void uv__fsevents_cb(uv_async_t* cb) {
184   uv_fs_event_t* handle;
185 
186   handle = cb->data;
187 
188   UV__FSEVENTS_PROCESS(handle, {
189     handle->cb(handle, event->path[0] ? event->path : NULL, event->events, 0);
190   });
191 }
192 
193 
194 /* Runs in CF thread, pushed event into handle's event list */
uv__fsevents_push_event(uv_fs_event_t * handle,struct uv__queue * events,int err)195 static void uv__fsevents_push_event(uv_fs_event_t* handle,
196                                     struct uv__queue* events,
197                                     int err) {
198   assert(events != NULL || err != 0);
199   uv_mutex_lock(&handle->cf_mutex);
200 
201   /* Concatenate two queues */
202   if (events != NULL)
203     uv__queue_add(&handle->cf_events, events);
204 
205   /* Propagate error */
206   if (err != 0)
207     handle->cf_error = err;
208   uv_mutex_unlock(&handle->cf_mutex);
209 
210   uv_async_send(handle->cf_cb);
211 }
212 
213 
214 /* Runs in CF thread, when there're events in FSEventStream */
uv__fsevents_event_cb(const FSEventStreamRef streamRef,void * info,size_t numEvents,void * eventPaths,const FSEventStreamEventFlags eventFlags[],const FSEventStreamEventId eventIds[])215 static void uv__fsevents_event_cb(const FSEventStreamRef streamRef,
216                                   void* info,
217                                   size_t numEvents,
218                                   void* eventPaths,
219                                   const FSEventStreamEventFlags eventFlags[],
220                                   const FSEventStreamEventId eventIds[]) {
221   size_t i;
222   int len;
223   char** paths;
224   char* path;
225   char* pos;
226   uv_fs_event_t* handle;
227   struct uv__queue* q;
228   uv_loop_t* loop;
229   uv__cf_loop_state_t* state;
230   uv__fsevents_event_t* event;
231   FSEventStreamEventFlags flags;
232   struct uv__queue head;
233 
234   loop = info;
235   state = loop->cf_state;
236   assert(state != NULL);
237   paths = eventPaths;
238 
239   /* For each handle */
240   uv_mutex_lock(&state->fsevent_mutex);
241   uv__queue_foreach(q, &state->fsevent_handles) {
242     handle = uv__queue_data(q, uv_fs_event_t, cf_member);
243     uv__queue_init(&head);
244 
245     /* Process and filter out events */
246     for (i = 0; i < numEvents; i++) {
247       flags = eventFlags[i];
248 
249       /* Ignore system events */
250       if (flags & kFSEventsSystem)
251         continue;
252 
253       path = paths[i];
254       len = strlen(path);
255 
256       if (handle->realpath_len == 0)
257         continue; /* This should be unreachable */
258 
259       /* Filter out paths that are outside handle's request */
260       if (len < handle->realpath_len)
261         continue;
262 
263       /* Make sure that realpath actually named a directory,
264        * (unless watching root, which alone keeps a trailing slash on the realpath)
265        * or that we matched the whole string */
266       if (handle->realpath_len != len &&
267           handle->realpath_len > 1 &&
268           path[handle->realpath_len] != '/')
269         continue;
270 
271       if (memcmp(path, handle->realpath, handle->realpath_len) != 0)
272         continue;
273 
274       if (!(handle->realpath_len == 1 && handle->realpath[0] == '/')) {
275         /* Remove common prefix, unless the watched folder is "/" */
276         path += handle->realpath_len;
277         len -= handle->realpath_len;
278 
279         /* Ignore events with path equal to directory itself */
280         if (len <= 1 && (flags & kFSEventStreamEventFlagItemIsDir))
281           continue;
282 
283         if (len == 0) {
284           /* Since we're using fsevents to watch the file itself,
285            * realpath == path, and we now need to get the basename of the file back
286            * (for commonality with other codepaths and platforms). */
287           while (len < handle->realpath_len && path[-1] != '/') {
288             path--;
289             len++;
290           }
291           /* Created and Removed seem to be always set, but don't make sense */
292           flags &= ~kFSEventsRenamed;
293         } else {
294           /* Skip forward slash */
295           path++;
296           len--;
297         }
298       }
299 
300       /* Do not emit events from subdirectories (without option set) */
301       if ((handle->cf_flags & UV_FS_EVENT_RECURSIVE) == 0 && *path != '\0') {
302         pos = strchr(path + 1, '/');
303         if (pos != NULL)
304           continue;
305       }
306 
307       event = uv__malloc(sizeof(*event) + len);
308       if (event == NULL)
309         break;
310 
311       memset(event, 0, sizeof(*event));
312       memcpy(event->path, path, len + 1);
313       event->events = UV_RENAME;
314 
315       if (0 == (flags & kFSEventsRenamed)) {
316         if (0 != (flags & kFSEventsModified) ||
317             0 == (flags & kFSEventStreamEventFlagItemIsDir))
318           event->events = UV_CHANGE;
319       }
320 
321       uv__queue_insert_tail(&head, &event->member);
322     }
323 
324     if (!uv__queue_empty(&head))
325       uv__fsevents_push_event(handle, &head, 0);
326   }
327   uv_mutex_unlock(&state->fsevent_mutex);
328 }
329 
330 
331 /* Runs in CF thread */
uv__fsevents_create_stream(uv__cf_loop_state_t * state,uv_loop_t * loop,CFArrayRef paths)332 static int uv__fsevents_create_stream(uv__cf_loop_state_t* state,
333                                       uv_loop_t* loop,
334                                       CFArrayRef paths) {
335   FSEventStreamContext ctx;
336   FSEventStreamRef ref;
337   CFAbsoluteTime latency;
338   FSEventStreamCreateFlags flags;
339 
340   /* Initialize context */
341   memset(&ctx, 0, sizeof(ctx));
342   ctx.info = loop;
343 
344   latency = 0.05;
345 
346   /* Explanation of selected flags:
347    * 1. NoDefer - without this flag, events that are happening continuously
348    *    (i.e. each event is happening after time interval less than `latency`,
349    *    counted from previous event), will be deferred and passed to callback
350    *    once they'll either fill whole OS buffer, or when this continuous stream
351    *    will stop (i.e. there'll be delay between events, bigger than
352    *    `latency`).
353    *    Specifying this flag will invoke callback after `latency` time passed
354    *    since event.
355    * 2. FileEvents - fire callback for file changes too (by default it is firing
356    *    it only for directory changes).
357    */
358   flags = kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents;
359 
360   /*
361    * NOTE: It might sound like a good idea to remember last seen StreamEventId,
362    * but in reality one dir might have last StreamEventId less than, the other,
363    * that is being watched now. Which will cause FSEventStream API to report
364    * changes to files from the past.
365    */
366   ref = pFSEventStreamCreate(NULL,
367                              &uv__fsevents_event_cb,
368                              &ctx,
369                              paths,
370                              kFSEventStreamEventIdSinceNow,
371                              latency,
372                              flags);
373   assert(ref != NULL);
374 
375   pFSEventStreamScheduleWithRunLoop(ref, state->loop, *pkCFRunLoopDefaultMode);
376   if (!pFSEventStreamStart(ref)) {
377     pFSEventStreamInvalidate(ref);
378     pFSEventStreamRelease(ref);
379     return UV_EMFILE;
380   }
381 
382   state->fsevent_stream = ref;
383   return 0;
384 }
385 
386 
387 /* Runs in CF thread */
uv__fsevents_destroy_stream(uv__cf_loop_state_t * state)388 static void uv__fsevents_destroy_stream(uv__cf_loop_state_t* state) {
389   if (state->fsevent_stream == NULL)
390     return;
391 
392   /* Stop emitting events */
393   pFSEventStreamStop(state->fsevent_stream);
394 
395   /* Release stream */
396   pFSEventStreamInvalidate(state->fsevent_stream);
397   pFSEventStreamRelease(state->fsevent_stream);
398   state->fsevent_stream = NULL;
399 }
400 
401 
402 /* Runs in CF thread, when there're new fsevent handles to add to stream */
uv__fsevents_reschedule(uv__cf_loop_state_t * state,uv_loop_t * loop,uv__cf_loop_signal_type_t type)403 static void uv__fsevents_reschedule(uv__cf_loop_state_t* state,
404                                     uv_loop_t* loop,
405                                     uv__cf_loop_signal_type_t type) {
406   struct uv__queue* q;
407   uv_fs_event_t* curr;
408   CFArrayRef cf_paths;
409   CFStringRef* paths;
410   unsigned int i;
411   int err;
412   unsigned int path_count;
413 
414   paths = NULL;
415   cf_paths = NULL;
416   err = 0;
417   /* NOTE: `i` is used in deallocation loop below */
418   i = 0;
419 
420   /* Optimization to prevent O(n^2) time spent when starting to watch
421    * many files simultaneously
422    */
423   uv_mutex_lock(&state->fsevent_mutex);
424   if (state->fsevent_need_reschedule == 0) {
425     uv_mutex_unlock(&state->fsevent_mutex);
426     goto final;
427   }
428   state->fsevent_need_reschedule = 0;
429   uv_mutex_unlock(&state->fsevent_mutex);
430 
431   /* Destroy previous FSEventStream */
432   uv__fsevents_destroy_stream(state);
433 
434   /* Any failure below will be a memory failure */
435   err = UV_ENOMEM;
436 
437   /* Create list of all watched paths */
438   uv_mutex_lock(&state->fsevent_mutex);
439   path_count = state->fsevent_handle_count;
440   if (path_count != 0) {
441     paths = uv__malloc(sizeof(*paths) * path_count);
442     if (paths == NULL) {
443       uv_mutex_unlock(&state->fsevent_mutex);
444       goto final;
445     }
446 
447     q = &state->fsevent_handles;
448     for (; i < path_count; i++) {
449       q = uv__queue_next(q);
450       assert(q != &state->fsevent_handles);
451       curr = uv__queue_data(q, uv_fs_event_t, cf_member);
452 
453       assert(curr->realpath != NULL);
454       paths[i] =
455           pCFStringCreateWithFileSystemRepresentation(NULL, curr->realpath);
456       if (paths[i] == NULL) {
457         uv_mutex_unlock(&state->fsevent_mutex);
458         goto final;
459       }
460     }
461   }
462   uv_mutex_unlock(&state->fsevent_mutex);
463   err = 0;
464 
465   if (path_count != 0) {
466     /* Create new FSEventStream */
467     cf_paths = pCFArrayCreate(NULL, (const void**) paths, path_count, NULL);
468     if (cf_paths == NULL) {
469       err = UV_ENOMEM;
470       goto final;
471     }
472     err = uv__fsevents_create_stream(state, loop, cf_paths);
473   }
474 
475 final:
476   /* Deallocate all paths in case of failure */
477   if (err != 0) {
478     if (cf_paths == NULL) {
479       while (i != 0)
480         pCFRelease(paths[--i]);
481       uv__free(paths);
482     } else {
483       /* CFArray takes ownership of both strings and original C-array */
484       pCFRelease(cf_paths);
485     }
486 
487     /* Broadcast error to all handles */
488     uv_mutex_lock(&state->fsevent_mutex);
489     uv__queue_foreach(q, &state->fsevent_handles) {
490       curr = uv__queue_data(q, uv_fs_event_t, cf_member);
491       uv__fsevents_push_event(curr, NULL, err);
492     }
493     uv_mutex_unlock(&state->fsevent_mutex);
494   }
495 
496   /*
497    * Main thread will block until the removal of handle from the list,
498    * we must tell it when we're ready.
499    *
500    * NOTE: This is coupled with `uv_sem_wait()` in `uv__fsevents_close`
501    */
502   if (type == kUVCFLoopSignalClosing)
503     uv_sem_post(&state->fsevent_sem);
504 }
505 
506 
uv__fsevents_global_init(void)507 static int uv__fsevents_global_init(void) {
508   static pthread_mutex_t global_init_mutex = PTHREAD_MUTEX_INITIALIZER;
509   static void* core_foundation_handle;
510   static void* core_services_handle;
511   int err;
512 
513   err = 0;
514   pthread_mutex_lock(&global_init_mutex);
515   if (core_foundation_handle != NULL)
516     goto out;
517 
518   /* The libraries are never unloaded because we currently don't have a good
519    * mechanism for keeping a reference count. It's unlikely to be an issue
520    * but if it ever becomes one, we can turn the dynamic library handles into
521    * per-event loop properties and have the dynamic linker keep track for us.
522    */
523   err = UV_ENOSYS;
524   core_foundation_handle = dlopen("/System/Library/Frameworks/"
525                                   "CoreFoundation.framework/"
526                                   "Versions/A/CoreFoundation",
527                                   RTLD_LAZY | RTLD_LOCAL);
528   if (core_foundation_handle == NULL)
529     goto out;
530 
531   core_services_handle = dlopen("/System/Library/Frameworks/"
532                                 "CoreServices.framework/"
533                                 "Versions/A/CoreServices",
534                                 RTLD_LAZY | RTLD_LOCAL);
535   if (core_services_handle == NULL)
536     goto out;
537 
538   err = UV_ENOENT;
539 #define V(handle, symbol)                                                     \
540   do {                                                                        \
541     *(void **)(&p ## symbol) = dlsym((handle), #symbol);                      \
542     if (p ## symbol == NULL)                                                  \
543       goto out;                                                               \
544   }                                                                           \
545   while (0)
546   V(core_foundation_handle, CFArrayCreate);
547   V(core_foundation_handle, CFRelease);
548   V(core_foundation_handle, CFRunLoopAddSource);
549   V(core_foundation_handle, CFRunLoopGetCurrent);
550   V(core_foundation_handle, CFRunLoopRemoveSource);
551   V(core_foundation_handle, CFRunLoopRun);
552   V(core_foundation_handle, CFRunLoopSourceCreate);
553   V(core_foundation_handle, CFRunLoopSourceSignal);
554   V(core_foundation_handle, CFRunLoopStop);
555   V(core_foundation_handle, CFRunLoopWakeUp);
556   V(core_foundation_handle, CFStringCreateWithFileSystemRepresentation);
557   V(core_foundation_handle, kCFRunLoopDefaultMode);
558   V(core_services_handle, FSEventStreamCreate);
559   V(core_services_handle, FSEventStreamInvalidate);
560   V(core_services_handle, FSEventStreamRelease);
561   V(core_services_handle, FSEventStreamScheduleWithRunLoop);
562   V(core_services_handle, FSEventStreamStart);
563   V(core_services_handle, FSEventStreamStop);
564 #undef V
565   err = 0;
566 
567 out:
568   if (err && core_services_handle != NULL) {
569     dlclose(core_services_handle);
570     core_services_handle = NULL;
571   }
572 
573   if (err && core_foundation_handle != NULL) {
574     dlclose(core_foundation_handle);
575     core_foundation_handle = NULL;
576   }
577 
578   pthread_mutex_unlock(&global_init_mutex);
579   return err;
580 }
581 
582 
583 /* Runs in UV loop */
uv__fsevents_loop_init(uv_loop_t * loop)584 static int uv__fsevents_loop_init(uv_loop_t* loop) {
585   CFRunLoopSourceContext ctx;
586   uv__cf_loop_state_t* state;
587   pthread_attr_t attr;
588   int err;
589 
590   if (loop->cf_state != NULL)
591     return 0;
592 
593   err = uv__fsevents_global_init();
594   if (err)
595     return err;
596 
597   state = uv__calloc(1, sizeof(*state));
598   if (state == NULL)
599     return UV_ENOMEM;
600 
601   err = uv_mutex_init(&loop->cf_mutex);
602   if (err)
603     goto fail_mutex_init;
604 
605   err = uv_sem_init(&loop->cf_sem, 0);
606   if (err)
607     goto fail_sem_init;
608 
609   uv__queue_init(&loop->cf_signals);
610 
611   err = uv_sem_init(&state->fsevent_sem, 0);
612   if (err)
613     goto fail_fsevent_sem_init;
614 
615   err = uv_mutex_init(&state->fsevent_mutex);
616   if (err)
617     goto fail_fsevent_mutex_init;
618 
619   uv__queue_init(&state->fsevent_handles);
620   state->fsevent_need_reschedule = 0;
621   state->fsevent_handle_count = 0;
622 
623   memset(&ctx, 0, sizeof(ctx));
624   ctx.info = loop;
625   ctx.perform = uv__cf_loop_cb;
626   state->signal_source = pCFRunLoopSourceCreate(NULL, 0, &ctx);
627   if (state->signal_source == NULL) {
628     err = UV_ENOMEM;
629     goto fail_signal_source_create;
630   }
631 
632   if (pthread_attr_init(&attr))
633     abort();
634 
635   if (pthread_attr_setstacksize(&attr, uv__thread_stack_size()))
636     abort();
637 
638   loop->cf_state = state;
639 
640   /* uv_thread_t is an alias for pthread_t. */
641   err = UV__ERR(pthread_create(&loop->cf_thread, &attr, uv__cf_loop_runner, loop));
642 
643   if (pthread_attr_destroy(&attr))
644     abort();
645 
646   if (err)
647     goto fail_thread_create;
648 
649   /* Synchronize threads */
650   uv_sem_wait(&loop->cf_sem);
651   return 0;
652 
653 fail_thread_create:
654   loop->cf_state = NULL;
655 
656 fail_signal_source_create:
657   uv_mutex_destroy(&state->fsevent_mutex);
658 
659 fail_fsevent_mutex_init:
660   uv_sem_destroy(&state->fsevent_sem);
661 
662 fail_fsevent_sem_init:
663   uv_sem_destroy(&loop->cf_sem);
664 
665 fail_sem_init:
666   uv_mutex_destroy(&loop->cf_mutex);
667 
668 fail_mutex_init:
669   uv__free(state);
670   return err;
671 }
672 
673 
674 /* Runs in UV loop */
uv__fsevents_loop_delete(uv_loop_t * loop)675 void uv__fsevents_loop_delete(uv_loop_t* loop) {
676   uv__cf_loop_signal_t* s;
677   uv__cf_loop_state_t* state;
678   struct uv__queue* q;
679 
680   if (loop->cf_state == NULL)
681     return;
682 
683   if (uv__cf_loop_signal(loop, NULL, kUVCFLoopSignalRegular) != 0)
684     abort();
685 
686   uv_thread_join(&loop->cf_thread);
687   uv_sem_destroy(&loop->cf_sem);
688   uv_mutex_destroy(&loop->cf_mutex);
689 
690   /* Free any remaining data */
691   while (!uv__queue_empty(&loop->cf_signals)) {
692     q = uv__queue_head(&loop->cf_signals);
693     s = uv__queue_data(q, uv__cf_loop_signal_t, member);
694     uv__queue_remove(q);
695     uv__free(s);
696   }
697 
698   /* Destroy state */
699   state = loop->cf_state;
700   uv_sem_destroy(&state->fsevent_sem);
701   uv_mutex_destroy(&state->fsevent_mutex);
702   pCFRelease(state->signal_source);
703   uv__free(state);
704   loop->cf_state = NULL;
705 }
706 
707 
708 /* Runs in CF thread. This is the CF loop's body */
uv__cf_loop_runner(void * arg)709 static void* uv__cf_loop_runner(void* arg) {
710   uv_loop_t* loop;
711   uv__cf_loop_state_t* state;
712 
713   loop = arg;
714   state = loop->cf_state;
715   state->loop = pCFRunLoopGetCurrent();
716 
717   pCFRunLoopAddSource(state->loop,
718                       state->signal_source,
719                       *pkCFRunLoopDefaultMode);
720 
721   uv_sem_post(&loop->cf_sem);
722 
723   pCFRunLoopRun();
724   pCFRunLoopRemoveSource(state->loop,
725                          state->signal_source,
726                          *pkCFRunLoopDefaultMode);
727 
728   state->loop = NULL;
729 
730   return NULL;
731 }
732 
733 
734 /* Runs in CF thread, executed after `uv__cf_loop_signal()` */
uv__cf_loop_cb(void * arg)735 static void uv__cf_loop_cb(void* arg) {
736   uv_loop_t* loop;
737   uv__cf_loop_state_t* state;
738   struct uv__queue* item;
739   struct uv__queue split_head;
740   uv__cf_loop_signal_t* s;
741 
742   loop = arg;
743   state = loop->cf_state;
744 
745   uv_mutex_lock(&loop->cf_mutex);
746   uv__queue_move(&loop->cf_signals, &split_head);
747   uv_mutex_unlock(&loop->cf_mutex);
748 
749   while (!uv__queue_empty(&split_head)) {
750     item = uv__queue_head(&split_head);
751     uv__queue_remove(item);
752 
753     s = uv__queue_data(item, uv__cf_loop_signal_t, member);
754 
755     /* This was a termination signal */
756     if (s->handle == NULL)
757       pCFRunLoopStop(state->loop);
758     else
759       uv__fsevents_reschedule(state, loop, s->type);
760 
761     uv__free(s);
762   }
763 }
764 
765 
766 /* Runs in UV loop to notify CF thread */
uv__cf_loop_signal(uv_loop_t * loop,uv_fs_event_t * handle,uv__cf_loop_signal_type_t type)767 int uv__cf_loop_signal(uv_loop_t* loop,
768                        uv_fs_event_t* handle,
769                        uv__cf_loop_signal_type_t type) {
770   uv__cf_loop_signal_t* item;
771   uv__cf_loop_state_t* state;
772 
773   item = uv__malloc(sizeof(*item));
774   if (item == NULL)
775     return UV_ENOMEM;
776 
777   item->handle = handle;
778   item->type = type;
779 
780   uv_mutex_lock(&loop->cf_mutex);
781   uv__queue_insert_tail(&loop->cf_signals, &item->member);
782 
783   state = loop->cf_state;
784   assert(state != NULL);
785   pCFRunLoopSourceSignal(state->signal_source);
786   pCFRunLoopWakeUp(state->loop);
787 
788   uv_mutex_unlock(&loop->cf_mutex);
789 
790   return 0;
791 }
792 
793 
794 /* Runs in UV loop to initialize handle */
uv__fsevents_init(uv_fs_event_t * handle)795 int uv__fsevents_init(uv_fs_event_t* handle) {
796   int err;
797   uv__cf_loop_state_t* state;
798 
799   err = uv__fsevents_loop_init(handle->loop);
800   if (err)
801     return err;
802 
803   /* Get absolute path to file */
804   handle->realpath = realpath(handle->path, NULL);
805   if (handle->realpath == NULL)
806     return UV__ERR(errno);
807   handle->realpath_len = strlen(handle->realpath);
808 
809   /* Initialize event queue */
810   uv__queue_init(&handle->cf_events);
811   handle->cf_error = 0;
812 
813   /*
814    * Events will occur in other thread.
815    * Initialize callback for getting them back into event loop's thread
816    */
817   handle->cf_cb = uv__malloc(sizeof(*handle->cf_cb));
818   if (handle->cf_cb == NULL) {
819     err = UV_ENOMEM;
820     goto fail_cf_cb_malloc;
821   }
822 
823   handle->cf_cb->data = handle;
824   uv_async_init(handle->loop, handle->cf_cb, uv__fsevents_cb);
825   handle->cf_cb->flags |= UV_HANDLE_INTERNAL;
826   uv_unref((uv_handle_t*) handle->cf_cb);
827 
828   err = uv_mutex_init(&handle->cf_mutex);
829   if (err)
830     goto fail_cf_mutex_init;
831 
832   /* Insert handle into the list */
833   state = handle->loop->cf_state;
834   uv_mutex_lock(&state->fsevent_mutex);
835   uv__queue_insert_tail(&state->fsevent_handles, &handle->cf_member);
836   state->fsevent_handle_count++;
837   state->fsevent_need_reschedule = 1;
838   uv_mutex_unlock(&state->fsevent_mutex);
839 
840   /* Reschedule FSEventStream */
841   assert(handle != NULL);
842   err = uv__cf_loop_signal(handle->loop, handle, kUVCFLoopSignalRegular);
843   if (err)
844     goto fail_loop_signal;
845 
846   return 0;
847 
848 fail_loop_signal:
849   uv_mutex_destroy(&handle->cf_mutex);
850 
851 fail_cf_mutex_init:
852   uv__free(handle->cf_cb);
853   handle->cf_cb = NULL;
854 
855 fail_cf_cb_malloc:
856   uv__free(handle->realpath);
857   handle->realpath = NULL;
858   handle->realpath_len = 0;
859 
860   return err;
861 }
862 
863 
864 /* Runs in UV loop to de-initialize handle */
uv__fsevents_close(uv_fs_event_t * handle)865 int uv__fsevents_close(uv_fs_event_t* handle) {
866   int err;
867   uv__cf_loop_state_t* state;
868 
869   if (handle->cf_cb == NULL)
870     return UV_EINVAL;
871 
872   /* Remove handle from  the list */
873   state = handle->loop->cf_state;
874   uv_mutex_lock(&state->fsevent_mutex);
875   uv__queue_remove(&handle->cf_member);
876   state->fsevent_handle_count--;
877   state->fsevent_need_reschedule = 1;
878   uv_mutex_unlock(&state->fsevent_mutex);
879 
880   /* Reschedule FSEventStream */
881   assert(handle != NULL);
882   err = uv__cf_loop_signal(handle->loop, handle, kUVCFLoopSignalClosing);
883   if (err)
884     return UV__ERR(err);
885 
886   /* Wait for deinitialization */
887   uv_sem_wait(&state->fsevent_sem);
888 
889   uv_close((uv_handle_t*) handle->cf_cb, (uv_close_cb) uv__free);
890   handle->cf_cb = NULL;
891 
892   /* Free data in queue */
893   UV__FSEVENTS_PROCESS(handle, {
894     /* NOP */
895   });
896 
897   uv_mutex_destroy(&handle->cf_mutex);
898   uv__free(handle->realpath);
899   handle->realpath = NULL;
900   handle->realpath_len = 0;
901 
902   return 0;
903 }
904 
905 #endif /* TARGET_OS_IPHONE */
906