xref: /netbsd-src/external/mit/libuv/dist/src/unix/fsevents.c (revision 9fb66d812c00ebfb445c0b47dea128f32aa6fe96)
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 
29 int uv__fsevents_init(uv_fs_event_t* handle) {
30   return 0;
31 }
32 
33 
34 int uv__fsevents_close(uv_fs_event_t* handle) {
35   return 0;
36 }
37 
38 
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 */
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 */
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 */
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 */
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 */
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 */
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 
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 */
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_storage;
599   pthread_attr_t* attr;
600   int err;
601 
602   if (loop->cf_state != NULL)
603     return 0;
604 
605   err = uv__fsevents_global_init();
606   if (err)
607     return err;
608 
609   state = uv__calloc(1, sizeof(*state));
610   if (state == NULL)
611     return UV_ENOMEM;
612 
613   err = uv_mutex_init(&loop->cf_mutex);
614   if (err)
615     goto fail_mutex_init;
616 
617   err = uv_sem_init(&loop->cf_sem, 0);
618   if (err)
619     goto fail_sem_init;
620 
621   QUEUE_INIT(&loop->cf_signals);
622 
623   err = uv_sem_init(&state->fsevent_sem, 0);
624   if (err)
625     goto fail_fsevent_sem_init;
626 
627   err = uv_mutex_init(&state->fsevent_mutex);
628   if (err)
629     goto fail_fsevent_mutex_init;
630 
631   QUEUE_INIT(&state->fsevent_handles);
632   state->fsevent_need_reschedule = 0;
633   state->fsevent_handle_count = 0;
634 
635   memset(&ctx, 0, sizeof(ctx));
636   ctx.info = loop;
637   ctx.perform = uv__cf_loop_cb;
638   state->signal_source = pCFRunLoopSourceCreate(NULL, 0, &ctx);
639   if (state->signal_source == NULL) {
640     err = UV_ENOMEM;
641     goto fail_signal_source_create;
642   }
643 
644   /* In the unlikely event that pthread_attr_init() fails, create the thread
645    * with the default stack size. We'll use a little more address space but
646    * that in itself is not a fatal error.
647    */
648   attr = &attr_storage;
649   if (pthread_attr_init(attr))
650     attr = NULL;
651 
652   if (attr != NULL)
653     if (pthread_attr_setstacksize(attr, 4 * PTHREAD_STACK_MIN))
654       abort();
655 
656   loop->cf_state = state;
657 
658   /* uv_thread_t is an alias for pthread_t. */
659   err = UV__ERR(pthread_create(&loop->cf_thread, attr, uv__cf_loop_runner, loop));
660 
661   if (attr != NULL)
662     pthread_attr_destroy(attr);
663 
664   if (err)
665     goto fail_thread_create;
666 
667   /* Synchronize threads */
668   uv_sem_wait(&loop->cf_sem);
669   return 0;
670 
671 fail_thread_create:
672   loop->cf_state = NULL;
673 
674 fail_signal_source_create:
675   uv_mutex_destroy(&state->fsevent_mutex);
676 
677 fail_fsevent_mutex_init:
678   uv_sem_destroy(&state->fsevent_sem);
679 
680 fail_fsevent_sem_init:
681   uv_sem_destroy(&loop->cf_sem);
682 
683 fail_sem_init:
684   uv_mutex_destroy(&loop->cf_mutex);
685 
686 fail_mutex_init:
687   uv__free(state);
688   return err;
689 }
690 
691 
692 /* Runs in UV loop */
693 void uv__fsevents_loop_delete(uv_loop_t* loop) {
694   uv__cf_loop_signal_t* s;
695   uv__cf_loop_state_t* state;
696   QUEUE* q;
697 
698   if (loop->cf_state == NULL)
699     return;
700 
701   if (uv__cf_loop_signal(loop, NULL, kUVCFLoopSignalRegular) != 0)
702     abort();
703 
704   uv_thread_join(&loop->cf_thread);
705   uv_sem_destroy(&loop->cf_sem);
706   uv_mutex_destroy(&loop->cf_mutex);
707 
708   /* Free any remaining data */
709   while (!QUEUE_EMPTY(&loop->cf_signals)) {
710     q = QUEUE_HEAD(&loop->cf_signals);
711     s = QUEUE_DATA(q, uv__cf_loop_signal_t, member);
712     QUEUE_REMOVE(q);
713     uv__free(s);
714   }
715 
716   /* Destroy state */
717   state = loop->cf_state;
718   uv_sem_destroy(&state->fsevent_sem);
719   uv_mutex_destroy(&state->fsevent_mutex);
720   pCFRelease(state->signal_source);
721   uv__free(state);
722   loop->cf_state = NULL;
723 }
724 
725 
726 /* Runs in CF thread. This is the CF loop's body */
727 static void* uv__cf_loop_runner(void* arg) {
728   uv_loop_t* loop;
729   uv__cf_loop_state_t* state;
730 
731   loop = arg;
732   state = loop->cf_state;
733   state->loop = pCFRunLoopGetCurrent();
734 
735   pCFRunLoopAddSource(state->loop,
736                       state->signal_source,
737                       *pkCFRunLoopDefaultMode);
738 
739   uv_sem_post(&loop->cf_sem);
740 
741   pCFRunLoopRun();
742   pCFRunLoopRemoveSource(state->loop,
743                          state->signal_source,
744                          *pkCFRunLoopDefaultMode);
745 
746   state->loop = NULL;
747 
748   return NULL;
749 }
750 
751 
752 /* Runs in CF thread, executed after `uv__cf_loop_signal()` */
753 static void uv__cf_loop_cb(void* arg) {
754   uv_loop_t* loop;
755   uv__cf_loop_state_t* state;
756   QUEUE* item;
757   QUEUE split_head;
758   uv__cf_loop_signal_t* s;
759 
760   loop = arg;
761   state = loop->cf_state;
762 
763   uv_mutex_lock(&loop->cf_mutex);
764   QUEUE_MOVE(&loop->cf_signals, &split_head);
765   uv_mutex_unlock(&loop->cf_mutex);
766 
767   while (!QUEUE_EMPTY(&split_head)) {
768     item = QUEUE_HEAD(&split_head);
769     QUEUE_REMOVE(item);
770 
771     s = QUEUE_DATA(item, uv__cf_loop_signal_t, member);
772 
773     /* This was a termination signal */
774     if (s->handle == NULL)
775       pCFRunLoopStop(state->loop);
776     else
777       uv__fsevents_reschedule(s->handle, s->type);
778 
779     uv__free(s);
780   }
781 }
782 
783 
784 /* Runs in UV loop to notify CF thread */
785 int uv__cf_loop_signal(uv_loop_t* loop,
786                        uv_fs_event_t* handle,
787                        uv__cf_loop_signal_type_t type) {
788   uv__cf_loop_signal_t* item;
789   uv__cf_loop_state_t* state;
790 
791   item = uv__malloc(sizeof(*item));
792   if (item == NULL)
793     return UV_ENOMEM;
794 
795   item->handle = handle;
796   item->type = type;
797 
798   uv_mutex_lock(&loop->cf_mutex);
799   QUEUE_INSERT_TAIL(&loop->cf_signals, &item->member);
800 
801   state = loop->cf_state;
802   assert(state != NULL);
803   pCFRunLoopSourceSignal(state->signal_source);
804   pCFRunLoopWakeUp(state->loop);
805 
806   uv_mutex_unlock(&loop->cf_mutex);
807 
808   return 0;
809 }
810 
811 
812 /* Runs in UV loop to initialize handle */
813 int uv__fsevents_init(uv_fs_event_t* handle) {
814   int err;
815   uv__cf_loop_state_t* state;
816 
817   err = uv__fsevents_loop_init(handle->loop);
818   if (err)
819     return err;
820 
821   /* Get absolute path to file */
822   handle->realpath = realpath(handle->path, NULL);
823   if (handle->realpath == NULL)
824     return UV__ERR(errno);
825   handle->realpath_len = strlen(handle->realpath);
826 
827   /* Initialize event queue */
828   QUEUE_INIT(&handle->cf_events);
829   handle->cf_error = 0;
830 
831   /*
832    * Events will occur in other thread.
833    * Initialize callback for getting them back into event loop's thread
834    */
835   handle->cf_cb = uv__malloc(sizeof(*handle->cf_cb));
836   if (handle->cf_cb == NULL) {
837     err = UV_ENOMEM;
838     goto fail_cf_cb_malloc;
839   }
840 
841   handle->cf_cb->data = handle;
842   uv_async_init(handle->loop, handle->cf_cb, uv__fsevents_cb);
843   handle->cf_cb->flags |= UV_HANDLE_INTERNAL;
844   uv_unref((uv_handle_t*) handle->cf_cb);
845 
846   err = uv_mutex_init(&handle->cf_mutex);
847   if (err)
848     goto fail_cf_mutex_init;
849 
850   /* Insert handle into the list */
851   state = handle->loop->cf_state;
852   uv_mutex_lock(&state->fsevent_mutex);
853   QUEUE_INSERT_TAIL(&state->fsevent_handles, &handle->cf_member);
854   state->fsevent_handle_count++;
855   state->fsevent_need_reschedule = 1;
856   uv_mutex_unlock(&state->fsevent_mutex);
857 
858   /* Reschedule FSEventStream */
859   assert(handle != NULL);
860   err = uv__cf_loop_signal(handle->loop, handle, kUVCFLoopSignalRegular);
861   if (err)
862     goto fail_loop_signal;
863 
864   return 0;
865 
866 fail_loop_signal:
867   uv_mutex_destroy(&handle->cf_mutex);
868 
869 fail_cf_mutex_init:
870   uv__free(handle->cf_cb);
871   handle->cf_cb = NULL;
872 
873 fail_cf_cb_malloc:
874   uv__free(handle->realpath);
875   handle->realpath = NULL;
876   handle->realpath_len = 0;
877 
878   return err;
879 }
880 
881 
882 /* Runs in UV loop to de-initialize handle */
883 int uv__fsevents_close(uv_fs_event_t* handle) {
884   int err;
885   uv__cf_loop_state_t* state;
886 
887   if (handle->cf_cb == NULL)
888     return UV_EINVAL;
889 
890   /* Remove handle from  the list */
891   state = handle->loop->cf_state;
892   uv_mutex_lock(&state->fsevent_mutex);
893   QUEUE_REMOVE(&handle->cf_member);
894   state->fsevent_handle_count--;
895   state->fsevent_need_reschedule = 1;
896   uv_mutex_unlock(&state->fsevent_mutex);
897 
898   /* Reschedule FSEventStream */
899   assert(handle != NULL);
900   err = uv__cf_loop_signal(handle->loop, handle, kUVCFLoopSignalClosing);
901   if (err)
902     return UV__ERR(err);
903 
904   /* Wait for deinitialization */
905   uv_sem_wait(&state->fsevent_sem);
906 
907   uv_close((uv_handle_t*) handle->cf_cb, (uv_close_cb) uv__free);
908   handle->cf_cb = NULL;
909 
910   /* Free data in queue */
911   UV__FSEVENTS_PROCESS(handle, {
912     /* NOP */
913   });
914 
915   uv_mutex_destroy(&handle->cf_mutex);
916   uv__free(handle->realpath);
917   handle->realpath = NULL;
918   handle->realpath_len = 0;
919 
920   return 0;
921 }
922 
923 #endif /* TARGET_OS_IPHONE */
924