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


grpc
Author(s):
autogenerated on Fri May 16 2025 02:58:25