Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Kitware
GitHub Repository: Kitware/CMake
Path: blob/master/Utilities/cmlibuv/src/unix/fsevents.c
3156 views
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
918