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