OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 #include "platform/globals.h" | |
6 #if defined(TARGET_OS_MACOS) | |
7 | |
8 #include "bin/file_system_watcher.h" | |
9 | |
10 #include <errno.h> // NOLINT | |
11 #include <fcntl.h> // NOLINT | |
12 #include <unistd.h> // NOLINT | |
13 #include <CoreServices/CoreServices.h> // NOLINT | |
14 | |
15 #include "bin/eventhandler.h" | |
16 #include "bin/fdutils.h" | |
17 #include "bin/socket.h" | |
18 #include "bin/thread.h" | |
19 | |
20 | |
21 namespace dart { | |
22 namespace bin { | |
23 | |
24 static Mutex* watcher_mutex = new Mutex(); | |
25 static Monitor* watcher_monitor = new Monitor(); | |
26 | |
27 class FSEventsWatcher; | |
28 static FSEventsWatcher* watcher = NULL; | |
29 | |
30 union FSEvent { | |
31 struct { | |
32 uint32_t flags; | |
33 char path[PATH_MAX]; | |
34 } data; | |
35 uint8_t bytes[PATH_MAX + 4]; | |
36 }; | |
37 | |
38 class FSEventsWatcher { | |
39 public: | |
40 class Node { | |
41 public: | |
42 Node(FSEventStreamRef ref, int fds[2]) | |
43 : ref_(ref) { | |
44 fds_[0] = fds[0]; | |
45 fds_[1] = fds[1]; | |
46 } | |
47 ~Node() { | |
48 close(fds_[1]); | |
49 FSEventStreamInvalidate(ref_); | |
50 FSEventStreamRelease(ref_); | |
51 } | |
52 | |
53 void Start() { | |
54 FSEventStreamStart(ref_); | |
55 } | |
56 | |
57 void Stop() { | |
58 FSEventStreamStop(ref_); | |
59 } | |
60 | |
61 int read_fd() const { return fds_[0]; } | |
62 | |
63 private: | |
64 FSEventStreamRef ref_; | |
65 int fds_[2]; | |
66 }; | |
67 | |
68 FSEventsWatcher() : run_loop_(0), users_(0) { | |
69 Thread::Start(Run, reinterpret_cast<uword>(this)); | |
70 } | |
71 | |
72 static void TimerCallback(CFRunLoopTimerRef timer, void* context) { | |
73 // Dummy callback to keep RunLoop alive. | |
74 } | |
75 | |
76 static void Run(uword arg) { | |
77 FSEventsWatcher* watcher = reinterpret_cast<FSEventsWatcher*>(arg); | |
78 watcher->run_loop_ = CFRunLoopGetCurrent(); | |
79 | |
80 // Notify, as the run-loop is set. | |
81 watcher_monitor->Notify(); | |
82 | |
83 CFRunLoopTimerRef timer = CFRunLoopTimerCreate( | |
84 NULL, | |
85 CFAbsoluteTimeGetCurrent() + 1, | |
86 1, | |
87 0, | |
88 0, | |
89 TimerCallback, | |
90 NULL); | |
91 | |
92 CFRunLoopAddTimer(watcher->run_loop_, timer, kCFRunLoopCommonModes); | |
93 | |
94 CFRunLoopRun(); | |
95 } | |
96 | |
97 void Increment() { | |
98 users_++; | |
99 } | |
100 | |
101 void Decrement() { | |
102 ASSERT(users_ > 0); | |
103 users_--; | |
104 if (users_ == 0) { | |
105 CFRunLoopStop(run_loop_); | |
106 watcher = NULL; | |
107 delete this; | |
108 } | |
109 } | |
110 | |
111 Node* AddPath(const char* path, int events) { | |
112 int fds[2]; | |
113 VOID_TEMP_FAILURE_RETRY(pipe(fds)); | |
114 Socket::SetNonBlocking(fds[0]); | |
115 Socket::SetBlocking(fds[1]); | |
116 | |
117 CFStringRef path_ref = CFStringCreateWithCString( | |
118 NULL, path, kCFStringEncodingUTF8); | |
119 | |
120 FSEventStreamContext context; | |
121 context.version = 0; | |
122 context.info = reinterpret_cast<void*>(fds[1]); | |
123 context.retain = NULL; | |
124 context.release = NULL; | |
125 context.copyDescription = NULL; | |
126 FSEventStreamRef ref = FSEventStreamCreate( | |
127 NULL, | |
128 Callback, | |
129 &context, | |
130 CFArrayCreate(NULL, reinterpret_cast<const void**>(&path_ref), 1, NULL), | |
131 kFSEventStreamEventIdSinceNow, | |
132 0.10, | |
133 kFSEventStreamCreateFlagFileEvents); | |
134 | |
135 | |
136 FSEventStreamScheduleWithRunLoop( | |
137 ref, | |
138 run_loop_, | |
139 kCFRunLoopDefaultMode); | |
140 | |
141 return new Node(ref, fds); | |
142 } | |
143 | |
144 private: | |
145 static void Callback(ConstFSEventStreamRef ref, | |
146 void* client, | |
147 size_t num_events, | |
148 void* event_paths, | |
149 const FSEventStreamEventFlags event_flags[], | |
150 const FSEventStreamEventId event_ids[]) { | |
151 int fd = reinterpret_cast<int>(client); | |
152 for (size_t i = 0; i < num_events; i++) { | |
153 char *path = reinterpret_cast<char**>(event_paths)[i]; | |
154 FSEvent event; | |
155 event.data.flags = event_flags[i]; | |
156 memmove(event.data.path, path, strlen(path) + 1); | |
157 write(fd, event.bytes, sizeof(event)); | |
158 } | |
159 } | |
160 | |
161 CFRunLoopRef run_loop_; | |
162 int users_; | |
163 }; | |
164 | |
165 intptr_t FileSystemWatcher::Init() { | |
166 MutexLocker lock(watcher_mutex); | |
167 if (watcher == NULL) { | |
168 watcher = new FSEventsWatcher(); | |
169 watcher_monitor->Enter(); | |
170 watcher_monitor->Wait(Monitor::kNoTimeout); | |
171 watcher_monitor->Exit(); | |
172 } | |
173 watcher->Increment(); | |
174 return 0; | |
175 } | |
176 | |
177 | |
178 void FileSystemWatcher::Stop(intptr_t id) { | |
179 MutexLocker lock(watcher_mutex); | |
180 watcher->Decrement(); | |
181 } | |
182 | |
183 | |
184 intptr_t FileSystemWatcher::GetSocketId(intptr_t id, intptr_t path_id) { | |
185 if (path_id == -1) return -1; | |
186 FSEventsWatcher::Node* node = | |
187 reinterpret_cast<FSEventsWatcher::Node*>(path_id); | |
188 return node->read_fd(); | |
189 } | |
190 | |
191 | |
192 intptr_t FileSystemWatcher::AddPath(intptr_t id, const char* path, int events) { | |
193 FSEventsWatcher::Node* node = watcher->AddPath(path, events); | |
194 node->Start(); | |
195 return reinterpret_cast<intptr_t>(node); | |
196 } | |
197 | |
198 | |
199 bool FileSystemWatcher::RemovePath(intptr_t id, intptr_t path_id) { | |
200 FSEventsWatcher::Node* node = | |
201 reinterpret_cast<FSEventsWatcher::Node*>(path_id); | |
202 node->Stop(); | |
203 delete node; | |
204 return true; | |
205 } | |
206 | |
207 | |
208 intptr_t FileSystemWatcher::ReadEvents(intptr_t id, | |
Søren Gjesse
2013/08/26 07:51:55
Again this should just be the _NativeSocket. If th
Anders Johnsen
2013/09/03 11:36:23
Done.
| |
209 intptr_t path_id, | |
210 Event** events) { | |
211 intptr_t fd = GetSocketId(id, path_id); | |
212 intptr_t avail = FDUtils::AvailableBytes(fd); | |
213 int count = avail / sizeof(FSEvent); | |
214 if (count <= 0) return 0; | |
215 *events = new Event[count]; | |
216 FSEvent e; | |
217 for (int i = 0; i < count; i++) { | |
218 intptr_t bytes = TEMP_FAILURE_RETRY(read(fd, e.bytes, sizeof(e))); | |
219 if (bytes < 0) return -1; | |
220 Event* event = *events + i; | |
221 event->event = 0; | |
222 event->path_id = id; | |
223 event->link = 1; | |
224 int flags = e.data.flags; | |
225 if (flags & kFSEventStreamEventFlagItemModified) { | |
226 event->event |= kModifyContent; | |
227 } | |
228 if (flags & kFSEventStreamEventFlagItemRenamed) event->event |= kMove; | |
229 if (flags & kFSEventStreamEventFlagItemXattrMod) { | |
230 event->event |= kModefyAttribute; | |
231 } | |
232 if (flags & kFSEventStreamEventFlagItemCreated) event->event |= kCreate; | |
233 if (flags & kFSEventStreamEventFlagItemRemoved) event->event |= kDelete; | |
234 event->filename = strdup(e.data.path); | |
235 } | |
236 return count; | |
237 } | |
238 | |
239 } // namespace bin | |
240 } // namespace dart | |
241 | |
242 #endif // defined(TARGET_OS_MACOS) | |
243 | |
244 | |
OLD | NEW |