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 29 int uv__fsevents_init(uv_fs_event_t* handle) { 30 return 0; 31 } 32 33 34 int uv__fsevents_close(uv_fs_event_t* handle) { 35 return 0; 36 } 37 38 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 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()` */ 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 */ 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 */ 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 */ 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