passenger 5.0.8 → 5.0.9
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of passenger might be problematic. Click here for more details.
- checksums.yaml +8 -8
- checksums.yaml.gz.asc +7 -7
- data.tar.gz.asc +7 -7
- data/.editorconfig +20 -0
- data/CHANGELOG +21 -0
- data/bin/passenger-install-apache2-module +3 -1
- data/build/agents.rb +7 -5
- data/build/basics.rb +3 -3
- data/build/common_library.rb +52 -30
- data/build/cxx_tests.rb +20 -13
- data/build/misc.rb +5 -5
- data/doc/Design and Architecture.html +1 -1
- data/doc/Design and Architecture.txt +1 -1
- data/doc/Packaging.html +4 -4
- data/doc/Packaging.txt.md +4 -4
- data/doc/Users guide Apache.html +22 -9
- data/doc/Users guide Apache.idmap.txt +4 -2
- data/doc/Users guide Apache.txt +2 -0
- data/doc/Users guide Nginx.html +22 -9
- data/doc/Users guide Nginx.idmap.txt +4 -2
- data/doc/Users guide Nginx.txt +2 -0
- data/doc/Users guide Standalone.html +14 -9
- data/doc/Users guide Standalone.idmap.txt +4 -2
- data/doc/users_guide_snippets/installation.txt +10 -6
- data/ext/apache2/Hooks.cpp +13 -2
- data/ext/common/ApplicationPool2/Pool/Inspection.h +8 -3
- data/ext/common/BackgroundEventLoop.cpp +249 -67
- data/ext/common/BackgroundEventLoop.h +5 -5
- data/ext/common/Constants.h +1 -1
- data/ext/common/InstanceDirectory.h +8 -6
- data/ext/common/ServerKit/Context.h +8 -2
- data/ext/common/ServerKit/FileBufferedChannel.h +262 -226
- data/ext/common/ServerKit/HeaderTable.h +28 -3
- data/ext/common/ServerKit/HttpHeaderParser.h +37 -13
- data/ext/common/ServerKit/HttpServer.h +17 -1
- data/ext/common/ServerKit/Implementation.cpp +2 -0
- data/ext/common/ServerKit/Server.h +25 -28
- data/ext/common/Utils/IOUtils.cpp +11 -0
- data/ext/common/Utils/ProcessMetricsCollector.h +4 -0
- data/ext/common/Utils/StrIntUtils.cpp +11 -7
- data/ext/common/Utils/StrIntUtils.h +1 -1
- data/ext/common/Utils/StrIntUtilsNoStrictAliasing.cpp +21 -16
- data/ext/common/agents/Base.cpp +6 -0
- data/ext/common/agents/Base.h +2 -0
- data/ext/common/agents/HelperAgent/AdminServer.h +25 -25
- data/ext/common/agents/HelperAgent/Main.cpp +37 -12
- data/ext/common/agents/HelperAgent/RequestHandler.h +18 -20
- data/ext/common/agents/HelperAgent/RequestHandler/AppResponse.h +4 -0
- data/ext/common/agents/HelperAgent/RequestHandler/ForwardResponse.cpp +10 -6
- data/ext/common/agents/HelperAgent/RequestHandler/Hooks.cpp +2 -0
- data/ext/common/agents/HelperAgent/RequestHandler/InitRequest.cpp +1 -1
- data/ext/common/agents/HelperAgent/RequestHandler/SendRequest.cpp +1 -1
- data/ext/common/agents/HelperAgent/RequestHandler/Utils.cpp +9 -2
- data/ext/common/agents/HelperAgent/ResponseCache.h +11 -11
- data/ext/common/agents/LoggingAgent/AdminServer.h +8 -8
- data/ext/common/agents/LoggingAgent/Main.cpp +6 -5
- data/ext/common/agents/Watchdog/AdminServer.h +13 -13
- data/ext/common/agents/Watchdog/Main.cpp +8 -3
- data/ext/libuv/.gitignore +72 -0
- data/ext/libuv/AUTHORS +199 -0
- data/ext/libuv/ChangeLog +2023 -0
- data/ext/libuv/LICENSE +46 -0
- data/ext/libuv/Makefile.am +336 -0
- data/ext/libuv/README.md +197 -0
- data/ext/libuv/checksparse.sh +233 -0
- data/ext/libuv/common.gypi +210 -0
- data/ext/libuv/configure.ac +67 -0
- data/ext/libuv/gyp_uv.py +96 -0
- data/ext/libuv/include/android-ifaddrs.h +54 -0
- data/ext/libuv/include/pthread-fixes.h +72 -0
- data/ext/libuv/include/tree.h +768 -0
- data/ext/libuv/include/uv-aix.h +32 -0
- data/ext/libuv/include/uv-bsd.h +34 -0
- data/ext/libuv/include/uv-darwin.h +61 -0
- data/ext/libuv/include/uv-errno.h +418 -0
- data/ext/libuv/include/uv-linux.h +34 -0
- data/ext/libuv/include/uv-sunos.h +44 -0
- data/ext/libuv/include/uv-threadpool.h +37 -0
- data/ext/libuv/include/uv-unix.h +383 -0
- data/ext/libuv/include/uv-version.h +39 -0
- data/ext/libuv/include/uv.h +1455 -0
- data/ext/libuv/libuv.pc.in +11 -0
- data/ext/libuv/m4/.gitignore +4 -0
- data/ext/libuv/m4/as_case.m4 +21 -0
- data/ext/libuv/m4/libuv-check-flags.m4 +319 -0
- data/ext/libuv/src/fs-poll.c +255 -0
- data/ext/libuv/src/heap-inl.h +245 -0
- data/ext/libuv/src/inet.c +313 -0
- data/ext/libuv/src/queue.h +92 -0
- data/ext/libuv/src/threadpool.c +303 -0
- data/ext/libuv/src/unix/aix.c +1240 -0
- data/ext/libuv/src/unix/android-ifaddrs.c +703 -0
- data/ext/libuv/src/unix/async.c +284 -0
- data/ext/libuv/src/unix/atomic-ops.h +60 -0
- data/ext/libuv/src/unix/core.c +985 -0
- data/ext/libuv/src/unix/darwin-proctitle.c +206 -0
- data/ext/libuv/src/unix/darwin.c +331 -0
- data/ext/libuv/src/unix/dl.c +83 -0
- data/ext/libuv/src/unix/freebsd.c +435 -0
- data/ext/libuv/src/unix/fs.c +1189 -0
- data/ext/libuv/src/unix/fsevents.c +899 -0
- data/ext/libuv/src/unix/getaddrinfo.c +202 -0
- data/ext/libuv/src/unix/getnameinfo.c +120 -0
- data/ext/libuv/src/unix/internal.h +314 -0
- data/ext/libuv/src/unix/kqueue.c +418 -0
- data/ext/libuv/src/unix/linux-core.c +876 -0
- data/ext/libuv/src/unix/linux-inotify.c +257 -0
- data/ext/libuv/src/unix/linux-syscalls.c +471 -0
- data/ext/libuv/src/unix/linux-syscalls.h +158 -0
- data/ext/libuv/src/unix/loop-watcher.c +63 -0
- data/ext/libuv/src/unix/loop.c +135 -0
- data/ext/libuv/src/unix/netbsd.c +368 -0
- data/ext/libuv/src/unix/openbsd.c +384 -0
- data/ext/libuv/src/unix/pipe.c +288 -0
- data/ext/libuv/src/unix/poll.c +113 -0
- data/ext/libuv/src/unix/process.c +551 -0
- data/ext/libuv/src/unix/proctitle.c +102 -0
- data/ext/libuv/src/unix/pthread-fixes.c +103 -0
- data/ext/libuv/src/unix/signal.c +465 -0
- data/ext/libuv/src/unix/spinlock.h +53 -0
- data/ext/libuv/src/unix/stream.c +1598 -0
- data/ext/libuv/src/unix/sunos.c +763 -0
- data/ext/libuv/src/unix/tcp.c +327 -0
- data/ext/libuv/src/unix/thread.c +519 -0
- data/ext/libuv/src/unix/timer.c +172 -0
- data/ext/libuv/src/unix/tty.c +265 -0
- data/ext/libuv/src/unix/udp.c +833 -0
- data/ext/libuv/src/uv-common.c +544 -0
- data/ext/libuv/src/uv-common.h +214 -0
- data/ext/libuv/src/version.c +49 -0
- data/ext/libuv/uv.gyp +487 -0
- data/ext/nginx/ContentHandler.c +21 -10
- data/ext/nginx/ngx_http_passenger_module.c +7 -0
- data/ext/oxt/implementation.cpp +9 -2
- data/ext/oxt/initialize.hpp +5 -1
- data/lib/phusion_passenger.rb +3 -3
- data/lib/phusion_passenger/admin_tools/instance.rb +10 -6
- data/lib/phusion_passenger/admin_tools/instance_registry.rb +6 -2
- data/lib/phusion_passenger/packaging.rb +3 -4
- data/lib/phusion_passenger/platform_info.rb +13 -1
- data/lib/phusion_passenger/platform_info/apache.rb +15 -4
- data/lib/phusion_passenger/platform_info/apache_detector.rb +5 -1
- data/lib/phusion_passenger/rack/thread_handler_extension.rb +184 -99
- data/lib/phusion_passenger/request_handler/thread_handler.rb +13 -6
- data/lib/phusion_passenger/standalone/start_command.rb +2 -2
- data/resources/templates/apache2/apache_install_broken.txt.erb +2 -1
- metadata +99 -22
- metadata.gz.asc +7 -7
- data/ext/libeio/Changes +0 -76
- data/ext/libeio/LICENSE +0 -36
- data/ext/libeio/Makefile.am +0 -15
- data/ext/libeio/Makefile.in +0 -694
- data/ext/libeio/aclocal.m4 +0 -9418
- data/ext/libeio/autogen.sh +0 -3
- data/ext/libeio/config.guess +0 -1540
- data/ext/libeio/config.h.in +0 -136
- data/ext/libeio/config.sub +0 -1779
- data/ext/libeio/configure +0 -14822
- data/ext/libeio/configure.ac +0 -22
- data/ext/libeio/demo.c +0 -194
- data/ext/libeio/ecb.h +0 -714
- data/ext/libeio/eio.c +0 -2818
- data/ext/libeio/eio.h +0 -414
- data/ext/libeio/install-sh +0 -520
- data/ext/libeio/libeio.m4 +0 -195
- data/ext/libeio/ltmain.sh +0 -9636
- data/ext/libeio/missing +0 -376
- data/ext/libeio/xthread.h +0 -166
@@ -0,0 +1,899 @@
|
|
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
|
25
|
+
|
26
|
+
/* iOS (currently) doesn't provide the FSEvents-API (nor CoreServices) */
|
27
|
+
|
28
|
+
int uv__fsevents_init(uv_fs_event_t* handle) {
|
29
|
+
return 0;
|
30
|
+
}
|
31
|
+
|
32
|
+
|
33
|
+
int uv__fsevents_close(uv_fs_event_t* handle) {
|
34
|
+
return 0;
|
35
|
+
}
|
36
|
+
|
37
|
+
|
38
|
+
void uv__fsevents_loop_delete(uv_loop_t* loop) {
|
39
|
+
}
|
40
|
+
|
41
|
+
#else /* TARGET_OS_IPHONE */
|
42
|
+
|
43
|
+
#include <dlfcn.h>
|
44
|
+
#include <assert.h>
|
45
|
+
#include <stdlib.h>
|
46
|
+
#include <pthread.h>
|
47
|
+
|
48
|
+
#include <CoreFoundation/CFRunLoop.h>
|
49
|
+
#include <CoreServices/CoreServices.h>
|
50
|
+
|
51
|
+
/* These are macros to avoid "initializer element is not constant" errors
|
52
|
+
* with old versions of gcc.
|
53
|
+
*/
|
54
|
+
#define kFSEventsModified (kFSEventStreamEventFlagItemFinderInfoMod | \
|
55
|
+
kFSEventStreamEventFlagItemModified | \
|
56
|
+
kFSEventStreamEventFlagItemInodeMetaMod | \
|
57
|
+
kFSEventStreamEventFlagItemChangeOwner | \
|
58
|
+
kFSEventStreamEventFlagItemXattrMod)
|
59
|
+
|
60
|
+
#define kFSEventsRenamed (kFSEventStreamEventFlagItemCreated | \
|
61
|
+
kFSEventStreamEventFlagItemRemoved | \
|
62
|
+
kFSEventStreamEventFlagItemRenamed)
|
63
|
+
|
64
|
+
#define kFSEventsSystem (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
|
+
struct uv__cf_loop_signal_s {
|
77
|
+
QUEUE member;
|
78
|
+
uv_fs_event_t* handle;
|
79
|
+
};
|
80
|
+
|
81
|
+
struct uv__fsevents_event_s {
|
82
|
+
QUEUE member;
|
83
|
+
int events;
|
84
|
+
char path[1];
|
85
|
+
};
|
86
|
+
|
87
|
+
struct uv__cf_loop_state_s {
|
88
|
+
CFRunLoopRef loop;
|
89
|
+
CFRunLoopSourceRef signal_source;
|
90
|
+
int fsevent_need_reschedule;
|
91
|
+
FSEventStreamRef fsevent_stream;
|
92
|
+
uv_sem_t fsevent_sem;
|
93
|
+
uv_mutex_t fsevent_mutex;
|
94
|
+
void* fsevent_handles[2];
|
95
|
+
unsigned int fsevent_handle_count;
|
96
|
+
};
|
97
|
+
|
98
|
+
/* Forward declarations */
|
99
|
+
static void uv__cf_loop_cb(void* arg);
|
100
|
+
static void* uv__cf_loop_runner(void* arg);
|
101
|
+
static int uv__cf_loop_signal(uv_loop_t* loop, uv_fs_event_t* handle);
|
102
|
+
|
103
|
+
/* Lazy-loaded by uv__fsevents_global_init(). */
|
104
|
+
static CFArrayRef (*pCFArrayCreate)(CFAllocatorRef,
|
105
|
+
const void**,
|
106
|
+
CFIndex,
|
107
|
+
const CFArrayCallBacks*);
|
108
|
+
static void (*pCFRelease)(CFTypeRef);
|
109
|
+
static void (*pCFRunLoopAddSource)(CFRunLoopRef,
|
110
|
+
CFRunLoopSourceRef,
|
111
|
+
CFStringRef);
|
112
|
+
static CFRunLoopRef (*pCFRunLoopGetCurrent)(void);
|
113
|
+
static void (*pCFRunLoopRemoveSource)(CFRunLoopRef,
|
114
|
+
CFRunLoopSourceRef,
|
115
|
+
CFStringRef);
|
116
|
+
static void (*pCFRunLoopRun)(void);
|
117
|
+
static CFRunLoopSourceRef (*pCFRunLoopSourceCreate)(CFAllocatorRef,
|
118
|
+
CFIndex,
|
119
|
+
CFRunLoopSourceContext*);
|
120
|
+
static void (*pCFRunLoopSourceSignal)(CFRunLoopSourceRef);
|
121
|
+
static void (*pCFRunLoopStop)(CFRunLoopRef);
|
122
|
+
static void (*pCFRunLoopWakeUp)(CFRunLoopRef);
|
123
|
+
static CFStringRef (*pCFStringCreateWithFileSystemRepresentation)(
|
124
|
+
CFAllocatorRef,
|
125
|
+
const char*);
|
126
|
+
static CFStringEncoding (*pCFStringGetSystemEncoding)(void);
|
127
|
+
static CFStringRef (*pkCFRunLoopDefaultMode);
|
128
|
+
static FSEventStreamRef (*pFSEventStreamCreate)(CFAllocatorRef,
|
129
|
+
FSEventStreamCallback,
|
130
|
+
FSEventStreamContext*,
|
131
|
+
CFArrayRef,
|
132
|
+
FSEventStreamEventId,
|
133
|
+
CFTimeInterval,
|
134
|
+
FSEventStreamCreateFlags);
|
135
|
+
static void (*pFSEventStreamFlushSync)(FSEventStreamRef);
|
136
|
+
static void (*pFSEventStreamInvalidate)(FSEventStreamRef);
|
137
|
+
static void (*pFSEventStreamRelease)(FSEventStreamRef);
|
138
|
+
static void (*pFSEventStreamScheduleWithRunLoop)(FSEventStreamRef,
|
139
|
+
CFRunLoopRef,
|
140
|
+
CFStringRef);
|
141
|
+
static Boolean (*pFSEventStreamStart)(FSEventStreamRef);
|
142
|
+
static void (*pFSEventStreamStop)(FSEventStreamRef);
|
143
|
+
|
144
|
+
#define UV__FSEVENTS_PROCESS(handle, block) \
|
145
|
+
do { \
|
146
|
+
QUEUE events; \
|
147
|
+
QUEUE* q; \
|
148
|
+
uv__fsevents_event_t* event; \
|
149
|
+
int err; \
|
150
|
+
uv_mutex_lock(&(handle)->cf_mutex); \
|
151
|
+
/* Split-off all events and empty original queue */ \
|
152
|
+
QUEUE_INIT(&events); \
|
153
|
+
if (!QUEUE_EMPTY(&(handle)->cf_events)) { \
|
154
|
+
q = QUEUE_HEAD(&(handle)->cf_events); \
|
155
|
+
QUEUE_SPLIT(&(handle)->cf_events, q, &events); \
|
156
|
+
} \
|
157
|
+
/* Get error (if any) and zero original one */ \
|
158
|
+
err = (handle)->cf_error; \
|
159
|
+
(handle)->cf_error = 0; \
|
160
|
+
uv_mutex_unlock(&(handle)->cf_mutex); \
|
161
|
+
/* Loop through events, deallocating each after processing */ \
|
162
|
+
while (!QUEUE_EMPTY(&events)) { \
|
163
|
+
q = QUEUE_HEAD(&events); \
|
164
|
+
event = QUEUE_DATA(q, uv__fsevents_event_t, member); \
|
165
|
+
QUEUE_REMOVE(q); \
|
166
|
+
/* NOTE: Checking uv__is_active() is required here, because handle \
|
167
|
+
* callback may close handle and invoking it after it will lead to \
|
168
|
+
* incorrect behaviour */ \
|
169
|
+
if (!uv__is_closing((handle)) && uv__is_active((handle))) \
|
170
|
+
block \
|
171
|
+
/* Free allocated data */ \
|
172
|
+
free(event); \
|
173
|
+
} \
|
174
|
+
if (err != 0 && !uv__is_closing((handle)) && uv__is_active((handle))) \
|
175
|
+
(handle)->cb((handle), NULL, 0, err); \
|
176
|
+
} while (0)
|
177
|
+
|
178
|
+
|
179
|
+
/* Runs in UV loop's thread, when there're events to report to handle */
|
180
|
+
static void uv__fsevents_cb(uv_async_t* cb) {
|
181
|
+
uv_fs_event_t* handle;
|
182
|
+
|
183
|
+
handle = cb->data;
|
184
|
+
|
185
|
+
UV__FSEVENTS_PROCESS(handle, {
|
186
|
+
handle->cb(handle, event->path[0] ? event->path : NULL, event->events, 0);
|
187
|
+
});
|
188
|
+
}
|
189
|
+
|
190
|
+
|
191
|
+
/* Runs in CF thread, pushed event into handle's event list */
|
192
|
+
static void uv__fsevents_push_event(uv_fs_event_t* handle,
|
193
|
+
QUEUE* events,
|
194
|
+
int err) {
|
195
|
+
assert(events != NULL || err != 0);
|
196
|
+
uv_mutex_lock(&handle->cf_mutex);
|
197
|
+
|
198
|
+
/* Concatenate two queues */
|
199
|
+
if (events != NULL)
|
200
|
+
QUEUE_ADD(&handle->cf_events, events);
|
201
|
+
|
202
|
+
/* Propagate error */
|
203
|
+
if (err != 0)
|
204
|
+
handle->cf_error = err;
|
205
|
+
uv_mutex_unlock(&handle->cf_mutex);
|
206
|
+
|
207
|
+
uv_async_send(handle->cf_cb);
|
208
|
+
}
|
209
|
+
|
210
|
+
|
211
|
+
/* Runs in CF thread, when there're events in FSEventStream */
|
212
|
+
static void uv__fsevents_event_cb(ConstFSEventStreamRef streamRef,
|
213
|
+
void* info,
|
214
|
+
size_t numEvents,
|
215
|
+
void* eventPaths,
|
216
|
+
const FSEventStreamEventFlags eventFlags[],
|
217
|
+
const FSEventStreamEventId eventIds[]) {
|
218
|
+
size_t i;
|
219
|
+
int len;
|
220
|
+
char** paths;
|
221
|
+
char* path;
|
222
|
+
char* pos;
|
223
|
+
uv_fs_event_t* handle;
|
224
|
+
QUEUE* q;
|
225
|
+
uv_loop_t* loop;
|
226
|
+
uv__cf_loop_state_t* state;
|
227
|
+
uv__fsevents_event_t* event;
|
228
|
+
QUEUE head;
|
229
|
+
|
230
|
+
loop = info;
|
231
|
+
state = loop->cf_state;
|
232
|
+
assert(state != NULL);
|
233
|
+
paths = eventPaths;
|
234
|
+
|
235
|
+
/* For each handle */
|
236
|
+
uv_mutex_lock(&state->fsevent_mutex);
|
237
|
+
QUEUE_FOREACH(q, &state->fsevent_handles) {
|
238
|
+
handle = QUEUE_DATA(q, uv_fs_event_t, cf_member);
|
239
|
+
QUEUE_INIT(&head);
|
240
|
+
|
241
|
+
/* Process and filter out events */
|
242
|
+
for (i = 0; i < numEvents; i++) {
|
243
|
+
/* Ignore system events */
|
244
|
+
if (eventFlags[i] & kFSEventsSystem)
|
245
|
+
continue;
|
246
|
+
|
247
|
+
path = paths[i];
|
248
|
+
len = strlen(path);
|
249
|
+
|
250
|
+
/* Filter out paths that are outside handle's request */
|
251
|
+
if (strncmp(path, handle->realpath, handle->realpath_len) != 0)
|
252
|
+
continue;
|
253
|
+
|
254
|
+
if (handle->realpath_len > 1 || *handle->realpath != '/') {
|
255
|
+
path += handle->realpath_len;
|
256
|
+
len -= handle->realpath_len;
|
257
|
+
|
258
|
+
/* Skip forward slash */
|
259
|
+
if (*path != '\0') {
|
260
|
+
path++;
|
261
|
+
len--;
|
262
|
+
}
|
263
|
+
}
|
264
|
+
|
265
|
+
#ifdef MAC_OS_X_VERSION_10_7
|
266
|
+
/* Ignore events with path equal to directory itself */
|
267
|
+
if (len == 0)
|
268
|
+
continue;
|
269
|
+
#endif /* MAC_OS_X_VERSION_10_7 */
|
270
|
+
|
271
|
+
/* Do not emit events from subdirectories (without option set) */
|
272
|
+
if ((handle->cf_flags & UV_FS_EVENT_RECURSIVE) == 0 && *path != 0) {
|
273
|
+
pos = strchr(path + 1, '/');
|
274
|
+
if (pos != NULL)
|
275
|
+
continue;
|
276
|
+
}
|
277
|
+
|
278
|
+
#ifndef MAC_OS_X_VERSION_10_7
|
279
|
+
path = "";
|
280
|
+
len = 0;
|
281
|
+
#endif /* MAC_OS_X_VERSION_10_7 */
|
282
|
+
|
283
|
+
event = malloc(sizeof(*event) + len);
|
284
|
+
if (event == NULL)
|
285
|
+
break;
|
286
|
+
|
287
|
+
memset(event, 0, sizeof(*event));
|
288
|
+
memcpy(event->path, path, len + 1);
|
289
|
+
|
290
|
+
if ((eventFlags[i] & kFSEventsModified) != 0 &&
|
291
|
+
(eventFlags[i] & kFSEventsRenamed) == 0)
|
292
|
+
event->events = UV_CHANGE;
|
293
|
+
else
|
294
|
+
event->events = UV_RENAME;
|
295
|
+
|
296
|
+
QUEUE_INSERT_TAIL(&head, &event->member);
|
297
|
+
}
|
298
|
+
|
299
|
+
if (!QUEUE_EMPTY(&head))
|
300
|
+
uv__fsevents_push_event(handle, &head, 0);
|
301
|
+
}
|
302
|
+
uv_mutex_unlock(&state->fsevent_mutex);
|
303
|
+
}
|
304
|
+
|
305
|
+
|
306
|
+
/* Runs in CF thread */
|
307
|
+
static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) {
|
308
|
+
uv__cf_loop_state_t* state;
|
309
|
+
FSEventStreamContext ctx;
|
310
|
+
FSEventStreamRef ref;
|
311
|
+
CFAbsoluteTime latency;
|
312
|
+
FSEventStreamCreateFlags flags;
|
313
|
+
|
314
|
+
/* Initialize context */
|
315
|
+
ctx.version = 0;
|
316
|
+
ctx.info = loop;
|
317
|
+
ctx.retain = NULL;
|
318
|
+
ctx.release = NULL;
|
319
|
+
ctx.copyDescription = NULL;
|
320
|
+
|
321
|
+
latency = 0.05;
|
322
|
+
|
323
|
+
/* Explanation of selected flags:
|
324
|
+
* 1. NoDefer - without this flag, events that are happening continuously
|
325
|
+
* (i.e. each event is happening after time interval less than `latency`,
|
326
|
+
* counted from previous event), will be deferred and passed to callback
|
327
|
+
* once they'll either fill whole OS buffer, or when this continuous stream
|
328
|
+
* will stop (i.e. there'll be delay between events, bigger than
|
329
|
+
* `latency`).
|
330
|
+
* Specifying this flag will invoke callback after `latency` time passed
|
331
|
+
* since event.
|
332
|
+
* 2. FileEvents - fire callback for file changes too (by default it is firing
|
333
|
+
* it only for directory changes).
|
334
|
+
*/
|
335
|
+
flags = kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents;
|
336
|
+
|
337
|
+
/*
|
338
|
+
* NOTE: It might sound like a good idea to remember last seen StreamEventId,
|
339
|
+
* but in reality one dir might have last StreamEventId less than, the other,
|
340
|
+
* that is being watched now. Which will cause FSEventStream API to report
|
341
|
+
* changes to files from the past.
|
342
|
+
*/
|
343
|
+
ref = pFSEventStreamCreate(NULL,
|
344
|
+
&uv__fsevents_event_cb,
|
345
|
+
&ctx,
|
346
|
+
paths,
|
347
|
+
kFSEventStreamEventIdSinceNow,
|
348
|
+
latency,
|
349
|
+
flags);
|
350
|
+
assert(ref != NULL);
|
351
|
+
|
352
|
+
state = loop->cf_state;
|
353
|
+
pFSEventStreamScheduleWithRunLoop(ref,
|
354
|
+
state->loop,
|
355
|
+
*pkCFRunLoopDefaultMode);
|
356
|
+
if (!pFSEventStreamStart(ref)) {
|
357
|
+
pFSEventStreamInvalidate(ref);
|
358
|
+
pFSEventStreamRelease(ref);
|
359
|
+
return -EMFILE;
|
360
|
+
}
|
361
|
+
|
362
|
+
state->fsevent_stream = ref;
|
363
|
+
return 0;
|
364
|
+
}
|
365
|
+
|
366
|
+
|
367
|
+
/* Runs in CF thread */
|
368
|
+
static void uv__fsevents_destroy_stream(uv_loop_t* loop) {
|
369
|
+
uv__cf_loop_state_t* state;
|
370
|
+
|
371
|
+
state = loop->cf_state;
|
372
|
+
|
373
|
+
if (state->fsevent_stream == NULL)
|
374
|
+
return;
|
375
|
+
|
376
|
+
/* Flush all accumulated events */
|
377
|
+
pFSEventStreamFlushSync(state->fsevent_stream);
|
378
|
+
|
379
|
+
/* Stop emitting events */
|
380
|
+
pFSEventStreamStop(state->fsevent_stream);
|
381
|
+
|
382
|
+
/* Release stream */
|
383
|
+
pFSEventStreamInvalidate(state->fsevent_stream);
|
384
|
+
pFSEventStreamRelease(state->fsevent_stream);
|
385
|
+
state->fsevent_stream = NULL;
|
386
|
+
}
|
387
|
+
|
388
|
+
|
389
|
+
/* Runs in CF thread, when there're new fsevent handles to add to stream */
|
390
|
+
static void uv__fsevents_reschedule(uv_fs_event_t* handle) {
|
391
|
+
uv__cf_loop_state_t* state;
|
392
|
+
QUEUE* q;
|
393
|
+
uv_fs_event_t* curr;
|
394
|
+
CFArrayRef cf_paths;
|
395
|
+
CFStringRef* paths;
|
396
|
+
unsigned int i;
|
397
|
+
int err;
|
398
|
+
unsigned int path_count;
|
399
|
+
|
400
|
+
state = handle->loop->cf_state;
|
401
|
+
paths = NULL;
|
402
|
+
cf_paths = NULL;
|
403
|
+
err = 0;
|
404
|
+
/* NOTE: `i` is used in deallocation loop below */
|
405
|
+
i = 0;
|
406
|
+
|
407
|
+
/* Optimization to prevent O(n^2) time spent when starting to watch
|
408
|
+
* many files simultaneously
|
409
|
+
*/
|
410
|
+
uv_mutex_lock(&state->fsevent_mutex);
|
411
|
+
if (state->fsevent_need_reschedule == 0) {
|
412
|
+
uv_mutex_unlock(&state->fsevent_mutex);
|
413
|
+
goto final;
|
414
|
+
}
|
415
|
+
state->fsevent_need_reschedule = 0;
|
416
|
+
uv_mutex_unlock(&state->fsevent_mutex);
|
417
|
+
|
418
|
+
/* Destroy previous FSEventStream */
|
419
|
+
uv__fsevents_destroy_stream(handle->loop);
|
420
|
+
|
421
|
+
/* Any failure below will be a memory failure */
|
422
|
+
err = -ENOMEM;
|
423
|
+
|
424
|
+
/* Create list of all watched paths */
|
425
|
+
uv_mutex_lock(&state->fsevent_mutex);
|
426
|
+
path_count = state->fsevent_handle_count;
|
427
|
+
if (path_count != 0) {
|
428
|
+
paths = malloc(sizeof(*paths) * path_count);
|
429
|
+
if (paths == NULL) {
|
430
|
+
uv_mutex_unlock(&state->fsevent_mutex);
|
431
|
+
goto final;
|
432
|
+
}
|
433
|
+
|
434
|
+
q = &state->fsevent_handles;
|
435
|
+
for (; i < path_count; i++) {
|
436
|
+
q = QUEUE_NEXT(q);
|
437
|
+
assert(q != &state->fsevent_handles);
|
438
|
+
curr = QUEUE_DATA(q, uv_fs_event_t, cf_member);
|
439
|
+
|
440
|
+
assert(curr->realpath != NULL);
|
441
|
+
paths[i] =
|
442
|
+
pCFStringCreateWithFileSystemRepresentation(NULL, curr->realpath);
|
443
|
+
if (paths[i] == NULL) {
|
444
|
+
uv_mutex_unlock(&state->fsevent_mutex);
|
445
|
+
goto final;
|
446
|
+
}
|
447
|
+
}
|
448
|
+
}
|
449
|
+
uv_mutex_unlock(&state->fsevent_mutex);
|
450
|
+
err = 0;
|
451
|
+
|
452
|
+
if (path_count != 0) {
|
453
|
+
/* Create new FSEventStream */
|
454
|
+
cf_paths = pCFArrayCreate(NULL, (const void**) paths, path_count, NULL);
|
455
|
+
if (cf_paths == NULL) {
|
456
|
+
err = -ENOMEM;
|
457
|
+
goto final;
|
458
|
+
}
|
459
|
+
err = uv__fsevents_create_stream(handle->loop, cf_paths);
|
460
|
+
}
|
461
|
+
|
462
|
+
final:
|
463
|
+
/* Deallocate all paths in case of failure */
|
464
|
+
if (err != 0) {
|
465
|
+
if (cf_paths == NULL) {
|
466
|
+
while (i != 0)
|
467
|
+
pCFRelease(paths[--i]);
|
468
|
+
free(paths);
|
469
|
+
} else {
|
470
|
+
/* CFArray takes ownership of both strings and original C-array */
|
471
|
+
pCFRelease(cf_paths);
|
472
|
+
}
|
473
|
+
|
474
|
+
/* Broadcast error to all handles */
|
475
|
+
uv_mutex_lock(&state->fsevent_mutex);
|
476
|
+
QUEUE_FOREACH(q, &state->fsevent_handles) {
|
477
|
+
curr = QUEUE_DATA(q, uv_fs_event_t, cf_member);
|
478
|
+
uv__fsevents_push_event(curr, NULL, err);
|
479
|
+
}
|
480
|
+
uv_mutex_unlock(&state->fsevent_mutex);
|
481
|
+
}
|
482
|
+
|
483
|
+
/*
|
484
|
+
* Main thread will block until the removal of handle from the list,
|
485
|
+
* we must tell it when we're ready.
|
486
|
+
*
|
487
|
+
* NOTE: This is coupled with `uv_sem_wait()` in `uv__fsevents_close`
|
488
|
+
*/
|
489
|
+
if (!uv__is_active(handle))
|
490
|
+
uv_sem_post(&state->fsevent_sem);
|
491
|
+
}
|
492
|
+
|
493
|
+
|
494
|
+
static int uv__fsevents_global_init(void) {
|
495
|
+
static pthread_mutex_t global_init_mutex = PTHREAD_MUTEX_INITIALIZER;
|
496
|
+
static void* core_foundation_handle;
|
497
|
+
static void* core_services_handle;
|
498
|
+
int err;
|
499
|
+
|
500
|
+
err = 0;
|
501
|
+
pthread_mutex_lock(&global_init_mutex);
|
502
|
+
if (core_foundation_handle != NULL)
|
503
|
+
goto out;
|
504
|
+
|
505
|
+
/* The libraries are never unloaded because we currently don't have a good
|
506
|
+
* mechanism for keeping a reference count. It's unlikely to be an issue
|
507
|
+
* but if it ever becomes one, we can turn the dynamic library handles into
|
508
|
+
* per-event loop properties and have the dynamic linker keep track for us.
|
509
|
+
*/
|
510
|
+
err = -ENOSYS;
|
511
|
+
core_foundation_handle = dlopen("/System/Library/Frameworks/"
|
512
|
+
"CoreFoundation.framework/"
|
513
|
+
"Versions/A/CoreFoundation",
|
514
|
+
RTLD_LAZY | RTLD_LOCAL);
|
515
|
+
if (core_foundation_handle == NULL)
|
516
|
+
goto out;
|
517
|
+
|
518
|
+
core_services_handle = dlopen("/System/Library/Frameworks/"
|
519
|
+
"CoreServices.framework/"
|
520
|
+
"Versions/A/CoreServices",
|
521
|
+
RTLD_LAZY | RTLD_LOCAL);
|
522
|
+
if (core_services_handle == NULL)
|
523
|
+
goto out;
|
524
|
+
|
525
|
+
err = -ENOENT;
|
526
|
+
#define V(handle, symbol) \
|
527
|
+
do { \
|
528
|
+
*(void **)(&p ## symbol) = dlsym((handle), #symbol); \
|
529
|
+
if (p ## symbol == NULL) \
|
530
|
+
goto out; \
|
531
|
+
} \
|
532
|
+
while (0)
|
533
|
+
V(core_foundation_handle, CFArrayCreate);
|
534
|
+
V(core_foundation_handle, CFRelease);
|
535
|
+
V(core_foundation_handle, CFRunLoopAddSource);
|
536
|
+
V(core_foundation_handle, CFRunLoopGetCurrent);
|
537
|
+
V(core_foundation_handle, CFRunLoopRemoveSource);
|
538
|
+
V(core_foundation_handle, CFRunLoopRun);
|
539
|
+
V(core_foundation_handle, CFRunLoopSourceCreate);
|
540
|
+
V(core_foundation_handle, CFRunLoopSourceSignal);
|
541
|
+
V(core_foundation_handle, CFRunLoopStop);
|
542
|
+
V(core_foundation_handle, CFRunLoopWakeUp);
|
543
|
+
V(core_foundation_handle, CFStringCreateWithFileSystemRepresentation);
|
544
|
+
V(core_foundation_handle, CFStringGetSystemEncoding);
|
545
|
+
V(core_foundation_handle, kCFRunLoopDefaultMode);
|
546
|
+
V(core_services_handle, FSEventStreamCreate);
|
547
|
+
V(core_services_handle, FSEventStreamFlushSync);
|
548
|
+
V(core_services_handle, FSEventStreamInvalidate);
|
549
|
+
V(core_services_handle, FSEventStreamRelease);
|
550
|
+
V(core_services_handle, FSEventStreamScheduleWithRunLoop);
|
551
|
+
V(core_services_handle, FSEventStreamStart);
|
552
|
+
V(core_services_handle, FSEventStreamStop);
|
553
|
+
#undef V
|
554
|
+
err = 0;
|
555
|
+
|
556
|
+
out:
|
557
|
+
if (err && core_services_handle != NULL) {
|
558
|
+
dlclose(core_services_handle);
|
559
|
+
core_services_handle = NULL;
|
560
|
+
}
|
561
|
+
|
562
|
+
if (err && core_foundation_handle != NULL) {
|
563
|
+
dlclose(core_foundation_handle);
|
564
|
+
core_foundation_handle = NULL;
|
565
|
+
}
|
566
|
+
|
567
|
+
pthread_mutex_unlock(&global_init_mutex);
|
568
|
+
return err;
|
569
|
+
}
|
570
|
+
|
571
|
+
|
572
|
+
/* Runs in UV loop */
|
573
|
+
static int uv__fsevents_loop_init(uv_loop_t* loop) {
|
574
|
+
CFRunLoopSourceContext ctx;
|
575
|
+
uv__cf_loop_state_t* state;
|
576
|
+
pthread_attr_t attr_storage;
|
577
|
+
pthread_attr_t* attr;
|
578
|
+
int err;
|
579
|
+
|
580
|
+
if (loop->cf_state != NULL)
|
581
|
+
return 0;
|
582
|
+
|
583
|
+
err = uv__fsevents_global_init();
|
584
|
+
if (err)
|
585
|
+
return err;
|
586
|
+
|
587
|
+
state = calloc(1, sizeof(*state));
|
588
|
+
if (state == NULL)
|
589
|
+
return -ENOMEM;
|
590
|
+
|
591
|
+
err = uv_mutex_init(&loop->cf_mutex);
|
592
|
+
if (err)
|
593
|
+
goto fail_mutex_init;
|
594
|
+
|
595
|
+
err = uv_sem_init(&loop->cf_sem, 0);
|
596
|
+
if (err)
|
597
|
+
goto fail_sem_init;
|
598
|
+
|
599
|
+
QUEUE_INIT(&loop->cf_signals);
|
600
|
+
|
601
|
+
err = uv_sem_init(&state->fsevent_sem, 0);
|
602
|
+
if (err)
|
603
|
+
goto fail_fsevent_sem_init;
|
604
|
+
|
605
|
+
err = uv_mutex_init(&state->fsevent_mutex);
|
606
|
+
if (err)
|
607
|
+
goto fail_fsevent_mutex_init;
|
608
|
+
|
609
|
+
QUEUE_INIT(&state->fsevent_handles);
|
610
|
+
state->fsevent_need_reschedule = 0;
|
611
|
+
state->fsevent_handle_count = 0;
|
612
|
+
|
613
|
+
memset(&ctx, 0, sizeof(ctx));
|
614
|
+
ctx.info = loop;
|
615
|
+
ctx.perform = uv__cf_loop_cb;
|
616
|
+
state->signal_source = pCFRunLoopSourceCreate(NULL, 0, &ctx);
|
617
|
+
if (state->signal_source == NULL) {
|
618
|
+
err = -ENOMEM;
|
619
|
+
goto fail_signal_source_create;
|
620
|
+
}
|
621
|
+
|
622
|
+
/* In the unlikely event that pthread_attr_init() fails, create the thread
|
623
|
+
* with the default stack size. We'll use a little more address space but
|
624
|
+
* that in itself is not a fatal error.
|
625
|
+
*/
|
626
|
+
attr = &attr_storage;
|
627
|
+
if (pthread_attr_init(attr))
|
628
|
+
attr = NULL;
|
629
|
+
|
630
|
+
if (attr != NULL)
|
631
|
+
if (pthread_attr_setstacksize(attr, 4 * PTHREAD_STACK_MIN))
|
632
|
+
abort();
|
633
|
+
|
634
|
+
loop->cf_state = state;
|
635
|
+
|
636
|
+
/* uv_thread_t is an alias for pthread_t. */
|
637
|
+
err = -pthread_create(&loop->cf_thread, attr, uv__cf_loop_runner, loop);
|
638
|
+
|
639
|
+
if (attr != NULL)
|
640
|
+
pthread_attr_destroy(attr);
|
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
|
+
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
|
+
QUEUE* q;
|
675
|
+
|
676
|
+
if (loop->cf_state == NULL)
|
677
|
+
return;
|
678
|
+
|
679
|
+
if (uv__cf_loop_signal(loop, NULL) != 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 (!QUEUE_EMPTY(&loop->cf_signals)) {
|
688
|
+
q = QUEUE_HEAD(&loop->cf_signals);
|
689
|
+
s = QUEUE_DATA(q, uv__cf_loop_signal_t, member);
|
690
|
+
QUEUE_REMOVE(q);
|
691
|
+
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
|
+
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
|
+
return NULL;
|
725
|
+
}
|
726
|
+
|
727
|
+
|
728
|
+
/* Runs in CF thread, executed after `uv__cf_loop_signal()` */
|
729
|
+
static void uv__cf_loop_cb(void* arg) {
|
730
|
+
uv_loop_t* loop;
|
731
|
+
uv__cf_loop_state_t* state;
|
732
|
+
QUEUE* item;
|
733
|
+
QUEUE split_head;
|
734
|
+
uv__cf_loop_signal_t* s;
|
735
|
+
|
736
|
+
loop = arg;
|
737
|
+
state = loop->cf_state;
|
738
|
+
QUEUE_INIT(&split_head);
|
739
|
+
|
740
|
+
uv_mutex_lock(&loop->cf_mutex);
|
741
|
+
if (!QUEUE_EMPTY(&loop->cf_signals)) {
|
742
|
+
QUEUE* split_pos = QUEUE_HEAD(&loop->cf_signals);
|
743
|
+
QUEUE_SPLIT(&loop->cf_signals, split_pos, &split_head);
|
744
|
+
}
|
745
|
+
uv_mutex_unlock(&loop->cf_mutex);
|
746
|
+
|
747
|
+
while (!QUEUE_EMPTY(&split_head)) {
|
748
|
+
item = QUEUE_HEAD(&split_head);
|
749
|
+
|
750
|
+
s = QUEUE_DATA(item, uv__cf_loop_signal_t, member);
|
751
|
+
|
752
|
+
/* This was a termination signal */
|
753
|
+
if (s->handle == NULL)
|
754
|
+
pCFRunLoopStop(state->loop);
|
755
|
+
else
|
756
|
+
uv__fsevents_reschedule(s->handle);
|
757
|
+
|
758
|
+
QUEUE_REMOVE(item);
|
759
|
+
free(s);
|
760
|
+
}
|
761
|
+
}
|
762
|
+
|
763
|
+
|
764
|
+
/* Runs in UV loop to notify CF thread */
|
765
|
+
int uv__cf_loop_signal(uv_loop_t* loop, uv_fs_event_t* handle) {
|
766
|
+
uv__cf_loop_signal_t* item;
|
767
|
+
uv__cf_loop_state_t* state;
|
768
|
+
|
769
|
+
item = malloc(sizeof(*item));
|
770
|
+
if (item == NULL)
|
771
|
+
return -ENOMEM;
|
772
|
+
|
773
|
+
item->handle = handle;
|
774
|
+
|
775
|
+
uv_mutex_lock(&loop->cf_mutex);
|
776
|
+
QUEUE_INSERT_TAIL(&loop->cf_signals, &item->member);
|
777
|
+
uv_mutex_unlock(&loop->cf_mutex);
|
778
|
+
|
779
|
+
state = loop->cf_state;
|
780
|
+
assert(state != NULL);
|
781
|
+
pCFRunLoopSourceSignal(state->signal_source);
|
782
|
+
pCFRunLoopWakeUp(state->loop);
|
783
|
+
|
784
|
+
return 0;
|
785
|
+
}
|
786
|
+
|
787
|
+
|
788
|
+
/* Runs in UV loop to initialize handle */
|
789
|
+
int uv__fsevents_init(uv_fs_event_t* handle) {
|
790
|
+
int err;
|
791
|
+
uv__cf_loop_state_t* state;
|
792
|
+
|
793
|
+
err = uv__fsevents_loop_init(handle->loop);
|
794
|
+
if (err)
|
795
|
+
return err;
|
796
|
+
|
797
|
+
/* Get absolute path to file */
|
798
|
+
handle->realpath = realpath(handle->path, NULL);
|
799
|
+
if (handle->realpath == NULL)
|
800
|
+
return -errno;
|
801
|
+
handle->realpath_len = strlen(handle->realpath);
|
802
|
+
|
803
|
+
/* Initialize event queue */
|
804
|
+
QUEUE_INIT(&handle->cf_events);
|
805
|
+
handle->cf_error = 0;
|
806
|
+
|
807
|
+
/*
|
808
|
+
* Events will occur in other thread.
|
809
|
+
* Initialize callback for getting them back into event loop's thread
|
810
|
+
*/
|
811
|
+
handle->cf_cb = malloc(sizeof(*handle->cf_cb));
|
812
|
+
if (handle->cf_cb == NULL) {
|
813
|
+
err = -ENOMEM;
|
814
|
+
goto fail_cf_cb_malloc;
|
815
|
+
}
|
816
|
+
|
817
|
+
handle->cf_cb->data = handle;
|
818
|
+
uv_async_init(handle->loop, handle->cf_cb, uv__fsevents_cb);
|
819
|
+
handle->cf_cb->flags |= UV__HANDLE_INTERNAL;
|
820
|
+
uv_unref((uv_handle_t*) handle->cf_cb);
|
821
|
+
|
822
|
+
err = uv_mutex_init(&handle->cf_mutex);
|
823
|
+
if (err)
|
824
|
+
goto fail_cf_mutex_init;
|
825
|
+
|
826
|
+
/* Insert handle into the list */
|
827
|
+
state = handle->loop->cf_state;
|
828
|
+
uv_mutex_lock(&state->fsevent_mutex);
|
829
|
+
QUEUE_INSERT_TAIL(&state->fsevent_handles, &handle->cf_member);
|
830
|
+
state->fsevent_handle_count++;
|
831
|
+
state->fsevent_need_reschedule = 1;
|
832
|
+
uv_mutex_unlock(&state->fsevent_mutex);
|
833
|
+
|
834
|
+
/* Reschedule FSEventStream */
|
835
|
+
assert(handle != NULL);
|
836
|
+
err = uv__cf_loop_signal(handle->loop, handle);
|
837
|
+
if (err)
|
838
|
+
goto fail_loop_signal;
|
839
|
+
|
840
|
+
return 0;
|
841
|
+
|
842
|
+
fail_loop_signal:
|
843
|
+
uv_mutex_destroy(&handle->cf_mutex);
|
844
|
+
|
845
|
+
fail_cf_mutex_init:
|
846
|
+
free(handle->cf_cb);
|
847
|
+
handle->cf_cb = NULL;
|
848
|
+
|
849
|
+
fail_cf_cb_malloc:
|
850
|
+
free(handle->realpath);
|
851
|
+
handle->realpath = NULL;
|
852
|
+
handle->realpath_len = 0;
|
853
|
+
|
854
|
+
return err;
|
855
|
+
}
|
856
|
+
|
857
|
+
|
858
|
+
/* Runs in UV loop to de-initialize handle */
|
859
|
+
int uv__fsevents_close(uv_fs_event_t* handle) {
|
860
|
+
int err;
|
861
|
+
uv__cf_loop_state_t* state;
|
862
|
+
|
863
|
+
if (handle->cf_cb == NULL)
|
864
|
+
return -EINVAL;
|
865
|
+
|
866
|
+
/* Remove handle from the list */
|
867
|
+
state = handle->loop->cf_state;
|
868
|
+
uv_mutex_lock(&state->fsevent_mutex);
|
869
|
+
QUEUE_REMOVE(&handle->cf_member);
|
870
|
+
state->fsevent_handle_count--;
|
871
|
+
state->fsevent_need_reschedule = 1;
|
872
|
+
uv_mutex_unlock(&state->fsevent_mutex);
|
873
|
+
|
874
|
+
/* Reschedule FSEventStream */
|
875
|
+
assert(handle != NULL);
|
876
|
+
err = uv__cf_loop_signal(handle->loop, handle);
|
877
|
+
if (err)
|
878
|
+
return -err;
|
879
|
+
|
880
|
+
/* Wait for deinitialization */
|
881
|
+
uv_sem_wait(&state->fsevent_sem);
|
882
|
+
|
883
|
+
uv_close((uv_handle_t*) handle->cf_cb, (uv_close_cb) free);
|
884
|
+
handle->cf_cb = NULL;
|
885
|
+
|
886
|
+
/* Free data in queue */
|
887
|
+
UV__FSEVENTS_PROCESS(handle, {
|
888
|
+
/* NOP */
|
889
|
+
});
|
890
|
+
|
891
|
+
uv_mutex_destroy(&handle->cf_mutex);
|
892
|
+
free(handle->realpath);
|
893
|
+
handle->realpath = NULL;
|
894
|
+
handle->realpath_len = 0;
|
895
|
+
|
896
|
+
return 0;
|
897
|
+
}
|
898
|
+
|
899
|
+
#endif /* TARGET_OS_IPHONE */
|