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