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