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