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