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 <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;
85 uv_fs_event_t* handle;
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,
110 uv_fs_event_t* handle,
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 */
uv__fsevents_cb(uv_async_t * cb)186 static void uv__fsevents_cb(uv_async_t* cb) {
187 uv_fs_event_t* handle;
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 */
uv__fsevents_push_event(uv_fs_event_t * handle,QUEUE * events,int err)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 */
uv__fsevents_event_cb(ConstFSEventStreamRef streamRef,void * info,size_t numEvents,void * eventPaths,const FSEventStreamEventFlags eventFlags[],const FSEventStreamEventId eventIds[])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;
229 uv_fs_event_t* handle;
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 */
uv__fsevents_create_stream(uv_loop_t * loop,CFArrayRef paths)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 */
uv__fsevents_destroy_stream(uv_loop_t * loop)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 */
uv__fsevents_reschedule(uv_fs_event_t * handle,uv__cf_loop_signal_type_t type)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
uv__fsevents_global_init(void)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 */
uv__fsevents_loop_init(uv_loop_t * 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 */
uv__fsevents_loop_delete(uv_loop_t * loop)697 void uv__fsevents_loop_delete(uv_loop_t* 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 */
uv__cf_loop_runner(void * arg)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()` */
uv__cf_loop_cb(void * arg)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 */
uv__cf_loop_signal(uv_loop_t * loop,uv_fs_event_t * handle,uv__cf_loop_signal_type_t type)789 int uv__cf_loop_signal(uv_loop_t* loop,
790 uv_fs_event_t* handle,
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 */
uv__fsevents_init(uv_fs_event_t * handle)817 int uv__fsevents_init(uv_fs_event_t* 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 */
uv__fsevents_close(uv_fs_event_t * handle)887 int uv__fsevents_close(uv_fs_event_t* 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
911 uv_close((uv_handle_t*) handle->cf_cb, (uv_close_cb) uv__free);
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 */
928