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