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