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