1 /* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2 * Permission is hereby granted, free of charge, to any person obtaining a copy
3 * of this software and associated documentation files (the "Software"), to
4 * deal in the Software without restriction, including without limitation the
5 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
6 * sell copies of the Software, and to permit persons to whom the Software is
7 * furnished to do so, subject to the following conditions:
8 *
9 * The above copyright notice and this permission notice shall be included in
10 * all copies or substantial portions of the Software.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
17 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
18 * IN THE SOFTWARE.
19 */
20
21 #include "uv.h"
22 #include "internal.h"
23
24 #if TARGET_OS_IPHONE || MAC_OS_X_VERSION_MAX_ALLOWED < 1070
25
26 /* iOS (currently) doesn't provide the FSEvents-API (nor CoreServices) */
27 /* macOS prior to 10.7 doesn't provide the full FSEvents API so use kqueue */
28
uv__fsevents_init(uv_fs_event_t * handle)29 int uv__fsevents_init(uv_fs_event_t* handle) {
30 return 0;
31 }
32
33
uv__fsevents_close(uv_fs_event_t * handle)34 int uv__fsevents_close(uv_fs_event_t* handle) {
35 return 0;
36 }
37
38
uv__fsevents_loop_delete(uv_loop_t * loop)39 void uv__fsevents_loop_delete(uv_loop_t* loop) {
40 }
41
42 #else /* TARGET_OS_IPHONE */
43
44 #include "darwin-stub.h"
45
46 #include <dlfcn.h>
47 #include <assert.h>
48 #include <stdlib.h>
49 #include <pthread.h>
50
51 static const int kFSEventsModified =
52 kFSEventStreamEventFlagItemChangeOwner |
53 kFSEventStreamEventFlagItemFinderInfoMod |
54 kFSEventStreamEventFlagItemInodeMetaMod |
55 kFSEventStreamEventFlagItemModified |
56 kFSEventStreamEventFlagItemXattrMod;
57
58 static const int kFSEventsRenamed =
59 kFSEventStreamEventFlagItemCreated |
60 kFSEventStreamEventFlagItemRemoved |
61 kFSEventStreamEventFlagItemRenamed;
62
63 static const int kFSEventsSystem =
64 kFSEventStreamEventFlagUserDropped |
65 kFSEventStreamEventFlagKernelDropped |
66 kFSEventStreamEventFlagEventIdsWrapped |
67 kFSEventStreamEventFlagHistoryDone |
68 kFSEventStreamEventFlagMount |
69 kFSEventStreamEventFlagUnmount |
70 kFSEventStreamEventFlagRootChanged;
71
72 typedef struct uv__fsevents_event_s uv__fsevents_event_t;
73 typedef struct uv__cf_loop_signal_s uv__cf_loop_signal_t;
74 typedef struct uv__cf_loop_state_s uv__cf_loop_state_t;
75
76 enum uv__cf_loop_signal_type_e {
77 kUVCFLoopSignalRegular,
78 kUVCFLoopSignalClosing
79 };
80 typedef enum uv__cf_loop_signal_type_e uv__cf_loop_signal_type_t;
81
82 struct uv__cf_loop_signal_s {
83 struct uv__queue member;
84 uv_fs_event_t* handle;
85 uv__cf_loop_signal_type_t type;
86 };
87
88 struct uv__fsevents_event_s {
89 struct uv__queue member;
90 int events;
91 char path[1];
92 };
93
94 struct uv__cf_loop_state_s {
95 CFRunLoopRef loop;
96 CFRunLoopSourceRef signal_source;
97 int fsevent_need_reschedule;
98 FSEventStreamRef fsevent_stream;
99 uv_sem_t fsevent_sem;
100 uv_mutex_t fsevent_mutex;
101 struct uv__queue fsevent_handles;
102 unsigned int fsevent_handle_count;
103 };
104
105 /* Forward declarations */
106 static void uv__cf_loop_cb(void* arg);
107 static void* uv__cf_loop_runner(void* arg);
108 static int uv__cf_loop_signal(uv_loop_t* loop,
109 uv_fs_event_t* handle,
110 uv__cf_loop_signal_type_t type);
111
112 /* Lazy-loaded by uv__fsevents_global_init(). */
113 static CFArrayRef (*pCFArrayCreate)(CFAllocatorRef,
114 const void**,
115 CFIndex,
116 const CFArrayCallBacks*);
117 static void (*pCFRelease)(CFTypeRef);
118 static void (*pCFRunLoopAddSource)(CFRunLoopRef,
119 CFRunLoopSourceRef,
120 CFStringRef);
121 static CFRunLoopRef (*pCFRunLoopGetCurrent)(void);
122 static void (*pCFRunLoopRemoveSource)(CFRunLoopRef,
123 CFRunLoopSourceRef,
124 CFStringRef);
125 static void (*pCFRunLoopRun)(void);
126 static CFRunLoopSourceRef (*pCFRunLoopSourceCreate)(CFAllocatorRef,
127 CFIndex,
128 CFRunLoopSourceContext*);
129 static void (*pCFRunLoopSourceSignal)(CFRunLoopSourceRef);
130 static void (*pCFRunLoopStop)(CFRunLoopRef);
131 static void (*pCFRunLoopWakeUp)(CFRunLoopRef);
132 static CFStringRef (*pCFStringCreateWithFileSystemRepresentation)(
133 CFAllocatorRef,
134 const char*);
135 static CFStringRef (*pkCFRunLoopDefaultMode);
136 static FSEventStreamRef (*pFSEventStreamCreate)(CFAllocatorRef,
137 FSEventStreamCallback,
138 FSEventStreamContext*,
139 CFArrayRef,
140 FSEventStreamEventId,
141 CFTimeInterval,
142 FSEventStreamCreateFlags);
143 static void (*pFSEventStreamInvalidate)(FSEventStreamRef);
144 static void (*pFSEventStreamRelease)(FSEventStreamRef);
145 static void (*pFSEventStreamScheduleWithRunLoop)(FSEventStreamRef,
146 CFRunLoopRef,
147 CFStringRef);
148 static int (*pFSEventStreamStart)(FSEventStreamRef);
149 static void (*pFSEventStreamStop)(FSEventStreamRef);
150
151 #define UV__FSEVENTS_PROCESS(handle, block) \
152 do { \
153 struct uv__queue events; \
154 struct uv__queue* q; \
155 uv__fsevents_event_t* event; \
156 int err; \
157 uv_mutex_lock(&(handle)->cf_mutex); \
158 /* Split-off all events and empty original queue */ \
159 uv__queue_move(&(handle)->cf_events, &events); \
160 /* Get error (if any) and zero original one */ \
161 err = (handle)->cf_error; \
162 (handle)->cf_error = 0; \
163 uv_mutex_unlock(&(handle)->cf_mutex); \
164 /* Loop through events, deallocating each after processing */ \
165 while (!uv__queue_empty(&events)) { \
166 q = uv__queue_head(&events); \
167 event = uv__queue_data(q, uv__fsevents_event_t, member); \
168 uv__queue_remove(q); \
169 /* NOTE: Checking uv__is_active() is required here, because handle \
170 * callback may close handle and invoking it after it will lead to \
171 * incorrect behaviour */ \
172 if (!uv__is_closing((handle)) && uv__is_active((handle))) \
173 block \
174 /* Free allocated data */ \
175 uv__free(event); \
176 } \
177 if (err != 0 && !uv__is_closing((handle)) && uv__is_active((handle))) \
178 (handle)->cb((handle), NULL, 0, err); \
179 } while (0)
180
181
182 /* Runs in UV loop's thread, when there're events to report to handle */
uv__fsevents_cb(uv_async_t * cb)183 static void uv__fsevents_cb(uv_async_t* cb) {
184 uv_fs_event_t* handle;
185
186 handle = cb->data;
187
188 UV__FSEVENTS_PROCESS(handle, {
189 handle->cb(handle, event->path[0] ? event->path : NULL, event->events, 0);
190 });
191 }
192
193
194 /* Runs in CF thread, pushed event into handle's event list */
uv__fsevents_push_event(uv_fs_event_t * handle,struct uv__queue * events,int err)195 static void uv__fsevents_push_event(uv_fs_event_t* handle,
196 struct uv__queue* events,
197 int err) {
198 assert(events != NULL || err != 0);
199 uv_mutex_lock(&handle->cf_mutex);
200
201 /* Concatenate two queues */
202 if (events != NULL)
203 uv__queue_add(&handle->cf_events, events);
204
205 /* Propagate error */
206 if (err != 0)
207 handle->cf_error = err;
208 uv_mutex_unlock(&handle->cf_mutex);
209
210 uv_async_send(handle->cf_cb);
211 }
212
213
214 /* Runs in CF thread, when there're events in FSEventStream */
uv__fsevents_event_cb(const FSEventStreamRef streamRef,void * info,size_t numEvents,void * eventPaths,const FSEventStreamEventFlags eventFlags[],const FSEventStreamEventId eventIds[])215 static void uv__fsevents_event_cb(const FSEventStreamRef streamRef,
216 void* info,
217 size_t numEvents,
218 void* eventPaths,
219 const FSEventStreamEventFlags eventFlags[],
220 const FSEventStreamEventId eventIds[]) {
221 size_t i;
222 int len;
223 char** paths;
224 char* path;
225 char* pos;
226 uv_fs_event_t* handle;
227 struct uv__queue* q;
228 uv_loop_t* loop;
229 uv__cf_loop_state_t* state;
230 uv__fsevents_event_t* event;
231 FSEventStreamEventFlags flags;
232 struct uv__queue head;
233
234 loop = info;
235 state = loop->cf_state;
236 assert(state != NULL);
237 paths = eventPaths;
238
239 /* For each handle */
240 uv_mutex_lock(&state->fsevent_mutex);
241 uv__queue_foreach(q, &state->fsevent_handles) {
242 handle = uv__queue_data(q, uv_fs_event_t, cf_member);
243 uv__queue_init(&head);
244
245 /* Process and filter out events */
246 for (i = 0; i < numEvents; i++) {
247 flags = eventFlags[i];
248
249 /* Ignore system events */
250 if (flags & kFSEventsSystem)
251 continue;
252
253 path = paths[i];
254 len = strlen(path);
255
256 if (handle->realpath_len == 0)
257 continue; /* This should be unreachable */
258
259 /* Filter out paths that are outside handle's request */
260 if (len < handle->realpath_len)
261 continue;
262
263 /* Make sure that realpath actually named a directory,
264 * (unless watching root, which alone keeps a trailing slash on the realpath)
265 * or that we matched the whole string */
266 if (handle->realpath_len != len &&
267 handle->realpath_len > 1 &&
268 path[handle->realpath_len] != '/')
269 continue;
270
271 if (memcmp(path, handle->realpath, handle->realpath_len) != 0)
272 continue;
273
274 if (!(handle->realpath_len == 1 && handle->realpath[0] == '/')) {
275 /* Remove common prefix, unless the watched folder is "/" */
276 path += handle->realpath_len;
277 len -= handle->realpath_len;
278
279 /* 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 */
uv__fsevents_create_stream(uv__cf_loop_state_t * state,uv_loop_t * loop,CFArrayRef paths)332 static 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 */
uv__fsevents_destroy_stream(uv__cf_loop_state_t * state)388 static 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 */
uv__fsevents_reschedule(uv__cf_loop_state_t * state,uv_loop_t * loop,uv__cf_loop_signal_type_t type)403 static 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
475 final:
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
uv__fsevents_global_init(void)507 static 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
567 out:
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 */
uv__fsevents_loop_init(uv_loop_t * loop)584 static 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
653 fail_thread_create:
654 loop->cf_state = NULL;
655
656 fail_signal_source_create:
657 uv_mutex_destroy(&state->fsevent_mutex);
658
659 fail_fsevent_mutex_init:
660 uv_sem_destroy(&state->fsevent_sem);
661
662 fail_fsevent_sem_init:
663 uv_sem_destroy(&loop->cf_sem);
664
665 fail_sem_init:
666 uv_mutex_destroy(&loop->cf_mutex);
667
668 fail_mutex_init:
669 uv__free(state);
670 return err;
671 }
672
673
674 /* Runs in UV loop */
uv__fsevents_loop_delete(uv_loop_t * loop)675 void 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 */
uv__cf_loop_runner(void * arg)709 static 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()` */
uv__cf_loop_cb(void * arg)735 static 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 */
uv__cf_loop_signal(uv_loop_t * loop,uv_fs_event_t * handle,uv__cf_loop_signal_type_t type)767 int 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 */
uv__fsevents_init(uv_fs_event_t * handle)795 int 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
848 fail_loop_signal:
849 uv_mutex_destroy(&handle->cf_mutex);
850
851 fail_cf_mutex_init:
852 uv__free(handle->cf_cb);
853 handle->cf_cb = NULL;
854
855 fail_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 */
uv__fsevents_close(uv_fs_event_t * handle)865 int 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