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