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_storage; 599 pthread_attr_t* attr; 600 int err; 601 602 if (loop->cf_state != NULL) 603 return 0; 604 605 err = uv__fsevents_global_init(); 606 if (err) 607 return err; 608 609 state = uv__calloc(1, sizeof(*state)); 610 if (state == NULL) 611 return UV_ENOMEM; 612 613 err = uv_mutex_init(&loop->cf_mutex); 614 if (err) 615 goto fail_mutex_init; 616 617 err = uv_sem_init(&loop->cf_sem, 0); 618 if (err) 619 goto fail_sem_init; 620 621 QUEUE_INIT(&loop->cf_signals); 622 623 err = uv_sem_init(&state->fsevent_sem, 0); 624 if (err) 625 goto fail_fsevent_sem_init; 626 627 err = uv_mutex_init(&state->fsevent_mutex); 628 if (err) 629 goto fail_fsevent_mutex_init; 630 631 QUEUE_INIT(&state->fsevent_handles); 632 state->fsevent_need_reschedule = 0; 633 state->fsevent_handle_count = 0; 634 635 memset(&ctx, 0, sizeof(ctx)); 636 ctx.info = loop; 637 ctx.perform = uv__cf_loop_cb; 638 state->signal_source = pCFRunLoopSourceCreate(NULL, 0, &ctx); 639 if (state->signal_source == NULL) { 640 err = UV_ENOMEM; 641 goto fail_signal_source_create; 642 } 643 644 /* In the unlikely event that pthread_attr_init() fails, create the thread 645 * with the default stack size. We'll use a little more address space but 646 * that in itself is not a fatal error. 647 */ 648 attr = &attr_storage; 649 if (pthread_attr_init(attr)) 650 attr = NULL; 651 652 if (attr != NULL) 653 if (pthread_attr_setstacksize(attr, 4 * PTHREAD_STACK_MIN)) 654 abort(); 655 656 loop->cf_state = state; 657 658 /* uv_thread_t is an alias for pthread_t. */ 659 err = UV__ERR(pthread_create(&loop->cf_thread, attr, uv__cf_loop_runner, loop)); 660 661 if (attr != NULL) 662 pthread_attr_destroy(attr); 663 664 if (err) 665 goto fail_thread_create; 666 667 /* Synchronize threads */ 668 uv_sem_wait(&loop->cf_sem); 669 return 0; 670 671 fail_thread_create: 672 loop->cf_state = NULL; 673 674 fail_signal_source_create: 675 uv_mutex_destroy(&state->fsevent_mutex); 676 677 fail_fsevent_mutex_init: 678 uv_sem_destroy(&state->fsevent_sem); 679 680 fail_fsevent_sem_init: 681 uv_sem_destroy(&loop->cf_sem); 682 683 fail_sem_init: 684 uv_mutex_destroy(&loop->cf_mutex); 685 686 fail_mutex_init: 687 uv__free(state); 688 return err; 689 } 690 691 692 /* Runs in UV loop */ 693 void uv__fsevents_loop_delete(uv_loop_t* loop) { 694 uv__cf_loop_signal_t* s; 695 uv__cf_loop_state_t* state; 696 QUEUE* q; 697 698 if (loop->cf_state == NULL) 699 return; 700 701 if (uv__cf_loop_signal(loop, NULL, kUVCFLoopSignalRegular) != 0) 702 abort(); 703 704 uv_thread_join(&loop->cf_thread); 705 uv_sem_destroy(&loop->cf_sem); 706 uv_mutex_destroy(&loop->cf_mutex); 707 708 /* Free any remaining data */ 709 while (!QUEUE_EMPTY(&loop->cf_signals)) { 710 q = QUEUE_HEAD(&loop->cf_signals); 711 s = QUEUE_DATA(q, uv__cf_loop_signal_t, member); 712 QUEUE_REMOVE(q); 713 uv__free(s); 714 } 715 716 /* Destroy state */ 717 state = loop->cf_state; 718 uv_sem_destroy(&state->fsevent_sem); 719 uv_mutex_destroy(&state->fsevent_mutex); 720 pCFRelease(state->signal_source); 721 uv__free(state); 722 loop->cf_state = NULL; 723 } 724 725 726 /* Runs in CF thread. This is the CF loop's body */ 727 static void* uv__cf_loop_runner(void* arg) { 728 uv_loop_t* loop; 729 uv__cf_loop_state_t* state; 730 731 loop = arg; 732 state = loop->cf_state; 733 state->loop = pCFRunLoopGetCurrent(); 734 735 pCFRunLoopAddSource(state->loop, 736 state->signal_source, 737 *pkCFRunLoopDefaultMode); 738 739 uv_sem_post(&loop->cf_sem); 740 741 pCFRunLoopRun(); 742 pCFRunLoopRemoveSource(state->loop, 743 state->signal_source, 744 *pkCFRunLoopDefaultMode); 745 746 state->loop = NULL; 747 748 return NULL; 749 } 750 751 752 /* Runs in CF thread, executed after `uv__cf_loop_signal()` */ 753 static void uv__cf_loop_cb(void* arg) { 754 uv_loop_t* loop; 755 uv__cf_loop_state_t* state; 756 QUEUE* item; 757 QUEUE split_head; 758 uv__cf_loop_signal_t* s; 759 760 loop = arg; 761 state = loop->cf_state; 762 763 uv_mutex_lock(&loop->cf_mutex); 764 QUEUE_MOVE(&loop->cf_signals, &split_head); 765 uv_mutex_unlock(&loop->cf_mutex); 766 767 while (!QUEUE_EMPTY(&split_head)) { 768 item = QUEUE_HEAD(&split_head); 769 QUEUE_REMOVE(item); 770 771 s = QUEUE_DATA(item, uv__cf_loop_signal_t, member); 772 773 /* This was a termination signal */ 774 if (s->handle == NULL) 775 pCFRunLoopStop(state->loop); 776 else 777 uv__fsevents_reschedule(s->handle, s->type); 778 779 uv__free(s); 780 } 781 } 782 783 784 /* Runs in UV loop to notify CF thread */ 785 int uv__cf_loop_signal(uv_loop_t* loop, 786 uv_fs_event_t* handle, 787 uv__cf_loop_signal_type_t type) { 788 uv__cf_loop_signal_t* item; 789 uv__cf_loop_state_t* state; 790 791 item = uv__malloc(sizeof(*item)); 792 if (item == NULL) 793 return UV_ENOMEM; 794 795 item->handle = handle; 796 item->type = type; 797 798 uv_mutex_lock(&loop->cf_mutex); 799 QUEUE_INSERT_TAIL(&loop->cf_signals, &item->member); 800 801 state = loop->cf_state; 802 assert(state != NULL); 803 pCFRunLoopSourceSignal(state->signal_source); 804 pCFRunLoopWakeUp(state->loop); 805 806 uv_mutex_unlock(&loop->cf_mutex); 807 808 return 0; 809 } 810 811 812 /* Runs in UV loop to initialize handle */ 813 int uv__fsevents_init(uv_fs_event_t* handle) { 814 int err; 815 uv__cf_loop_state_t* state; 816 817 err = uv__fsevents_loop_init(handle->loop); 818 if (err) 819 return err; 820 821 /* Get absolute path to file */ 822 handle->realpath = realpath(handle->path, NULL); 823 if (handle->realpath == NULL) 824 return UV__ERR(errno); 825 handle->realpath_len = strlen(handle->realpath); 826 827 /* Initialize event queue */ 828 QUEUE_INIT(&handle->cf_events); 829 handle->cf_error = 0; 830 831 /* 832 * Events will occur in other thread. 833 * Initialize callback for getting them back into event loop's thread 834 */ 835 handle->cf_cb = uv__malloc(sizeof(*handle->cf_cb)); 836 if (handle->cf_cb == NULL) { 837 err = UV_ENOMEM; 838 goto fail_cf_cb_malloc; 839 } 840 841 handle->cf_cb->data = handle; 842 uv_async_init(handle->loop, handle->cf_cb, uv__fsevents_cb); 843 handle->cf_cb->flags |= UV_HANDLE_INTERNAL; 844 uv_unref((uv_handle_t*) handle->cf_cb); 845 846 err = uv_mutex_init(&handle->cf_mutex); 847 if (err) 848 goto fail_cf_mutex_init; 849 850 /* Insert handle into the list */ 851 state = handle->loop->cf_state; 852 uv_mutex_lock(&state->fsevent_mutex); 853 QUEUE_INSERT_TAIL(&state->fsevent_handles, &handle->cf_member); 854 state->fsevent_handle_count++; 855 state->fsevent_need_reschedule = 1; 856 uv_mutex_unlock(&state->fsevent_mutex); 857 858 /* Reschedule FSEventStream */ 859 assert(handle != NULL); 860 err = uv__cf_loop_signal(handle->loop, handle, kUVCFLoopSignalRegular); 861 if (err) 862 goto fail_loop_signal; 863 864 return 0; 865 866 fail_loop_signal: 867 uv_mutex_destroy(&handle->cf_mutex); 868 869 fail_cf_mutex_init: 870 uv__free(handle->cf_cb); 871 handle->cf_cb = NULL; 872 873 fail_cf_cb_malloc: 874 uv__free(handle->realpath); 875 handle->realpath = NULL; 876 handle->realpath_len = 0; 877 878 return err; 879 } 880 881 882 /* Runs in UV loop to de-initialize handle */ 883 int uv__fsevents_close(uv_fs_event_t* handle) { 884 int err; 885 uv__cf_loop_state_t* state; 886 887 if (handle->cf_cb == NULL) 888 return UV_EINVAL; 889 890 /* Remove handle from the list */ 891 state = handle->loop->cf_state; 892 uv_mutex_lock(&state->fsevent_mutex); 893 QUEUE_REMOVE(&handle->cf_member); 894 state->fsevent_handle_count--; 895 state->fsevent_need_reschedule = 1; 896 uv_mutex_unlock(&state->fsevent_mutex); 897 898 /* Reschedule FSEventStream */ 899 assert(handle != NULL); 900 err = uv__cf_loop_signal(handle->loop, handle, kUVCFLoopSignalClosing); 901 if (err) 902 return UV__ERR(err); 903 904 /* Wait for deinitialization */ 905 uv_sem_wait(&state->fsevent_sem); 906 907 uv_close((uv_handle_t*) handle->cf_cb, (uv_close_cb) uv__free); 908 handle->cf_cb = NULL; 909 910 /* Free data in queue */ 911 UV__FSEVENTS_PROCESS(handle, { 912 /* NOP */ 913 }); 914 915 uv_mutex_destroy(&handle->cf_mutex); 916 uv__free(handle->realpath); 917 handle->realpath = NULL; 918 handle->realpath_len = 0; 919 920 return 0; 921 } 922 923 #endif /* TARGET_OS_IPHONE */ 924