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(intptr_t base_path_length, int read_fd, int write_fd, bool recursive) |
| 43 : base_path_length_(base_path_length), |
| 44 read_fd_(read_fd), |
| 45 write_fd_(write_fd), |
| 46 recursive_(recursive), |
| 47 ref_(NULL) {} |
| 48 |
| 49 ~Node() { |
| 50 close(write_fd_); |
| 51 FSEventStreamInvalidate(ref_); |
| 52 FSEventStreamRelease(ref_); |
| 53 } |
| 54 |
| 55 void set_ref(FSEventStreamRef ref) { |
| 56 ref_ = ref; |
| 57 } |
| 58 |
| 59 void Start() { |
| 60 FSEventStreamStart(ref_); |
| 61 } |
| 62 |
| 63 void Stop() { |
| 64 FSEventStreamStop(ref_); |
| 65 } |
| 66 |
| 67 intptr_t base_path_length() const { return base_path_length_; } |
| 68 int read_fd() const { return read_fd_; } |
| 69 int write_fd() const { return write_fd_; } |
| 70 bool recursive() const { return recursive_; } |
| 71 |
| 72 private: |
| 73 intptr_t base_path_length_; |
| 74 int read_fd_; |
| 75 int write_fd_; |
| 76 bool recursive_; |
| 77 FSEventStreamRef ref_; |
| 78 }; |
| 79 |
| 80 FSEventsWatcher() : run_loop_(0), users_(0) { |
| 81 Thread::Start(Run, reinterpret_cast<uword>(this)); |
| 82 } |
| 83 |
| 84 ~FSEventsWatcher() { |
| 85 CFRunLoopStop(run_loop_); |
| 86 } |
| 87 |
| 88 static void TimerCallback(CFRunLoopTimerRef timer, void* context) { |
| 89 // Dummy callback to keep RunLoop alive. |
| 90 } |
| 91 |
| 92 static void Run(uword arg) { |
| 93 FSEventsWatcher* watcher = reinterpret_cast<FSEventsWatcher*>(arg); |
| 94 watcher->run_loop_ = CFRunLoopGetCurrent(); |
| 95 |
| 96 // Notify, as the run-loop is set. |
| 97 watcher_monitor->Notify(); |
| 98 |
| 99 CFRunLoopTimerRef timer = CFRunLoopTimerCreate( |
| 100 NULL, |
| 101 CFAbsoluteTimeGetCurrent() + 1, |
| 102 1, |
| 103 0, |
| 104 0, |
| 105 TimerCallback, |
| 106 NULL); |
| 107 |
| 108 CFRunLoopAddTimer(watcher->run_loop_, timer, kCFRunLoopCommonModes); |
| 109 |
| 110 CFRunLoopRun(); |
| 111 } |
| 112 |
| 113 static void Increment() { |
| 114 if (watcher == NULL) { |
| 115 watcher = new FSEventsWatcher(); |
| 116 watcher_monitor->Enter(); |
| 117 watcher_monitor->Wait(Monitor::kNoTimeout); |
| 118 watcher_monitor->Exit(); |
| 119 } |
| 120 watcher->users_++; |
| 121 } |
| 122 |
| 123 static void Decrement() { |
| 124 ASSERT(watcher->users_ > 0); |
| 125 watcher->users_--; |
| 126 if (watcher->users_ == 0) { |
| 127 delete watcher; |
| 128 watcher = NULL; |
| 129 } |
| 130 } |
| 131 |
| 132 Node* AddPath(const char* path, int events, bool recursive) { |
| 133 int fds[2]; |
| 134 VOID_TEMP_FAILURE_RETRY(pipe(fds)); |
| 135 Socket::SetNonBlocking(fds[0]); |
| 136 Socket::SetBlocking(fds[1]); |
| 137 |
| 138 char base_path[PATH_MAX]; |
| 139 realpath(path, base_path); |
| 140 CFStringRef path_ref = CFStringCreateWithCString( |
| 141 NULL, base_path, kCFStringEncodingUTF8); |
| 142 |
| 143 Node* node = new Node(strlen(base_path), fds[0], fds[1], recursive); |
| 144 |
| 145 FSEventStreamContext context; |
| 146 context.version = 0; |
| 147 context.info = reinterpret_cast<void*>(node); |
| 148 context.retain = NULL; |
| 149 context.release = NULL; |
| 150 context.copyDescription = NULL; |
| 151 FSEventStreamRef ref = FSEventStreamCreate( |
| 152 NULL, |
| 153 Callback, |
| 154 &context, |
| 155 CFArrayCreate(NULL, reinterpret_cast<const void**>(&path_ref), 1, NULL), |
| 156 kFSEventStreamEventIdSinceNow, |
| 157 0.10, |
| 158 kFSEventStreamCreateFlagFileEvents); |
| 159 |
| 160 node->set_ref(ref); |
| 161 |
| 162 FSEventStreamScheduleWithRunLoop( |
| 163 ref, |
| 164 run_loop_, |
| 165 kCFRunLoopDefaultMode); |
| 166 |
| 167 return node; |
| 168 } |
| 169 |
| 170 private: |
| 171 static void Callback(ConstFSEventStreamRef ref, |
| 172 void* client, |
| 173 size_t num_events, |
| 174 void* event_paths, |
| 175 const FSEventStreamEventFlags event_flags[], |
| 176 const FSEventStreamEventId event_ids[]) { |
| 177 Node* node = reinterpret_cast<Node*>(client); |
| 178 for (size_t i = 0; i < num_events; i++) { |
| 179 char *path = reinterpret_cast<char**>(event_paths)[i]; |
| 180 path += node->base_path_length() + 1; |
| 181 if (!node->recursive() && strstr(path, "/") != NULL) continue; |
| 182 FSEvent event; |
| 183 event.data.flags = event_flags[i]; |
| 184 memmove(event.data.path, path, strlen(path) + 1); |
| 185 write(node->write_fd(), event.bytes, sizeof(event)); |
| 186 } |
| 187 } |
| 188 |
| 189 CFRunLoopRef run_loop_; |
| 190 int users_; |
| 191 }; |
| 192 |
| 193 |
| 194 intptr_t FileSystemWatcher::WatchPath(const char* path, |
| 195 int events, |
| 196 bool recursive) { |
| 197 MutexLocker lock(watcher_mutex); |
| 198 FSEventsWatcher::Increment(); |
| 199 |
| 200 FSEventsWatcher::Node* node = watcher->AddPath(path, events, recursive); |
| 201 node->Start(); |
| 202 return reinterpret_cast<intptr_t>(node); |
| 203 } |
| 204 |
| 205 |
| 206 void FileSystemWatcher::UnwatchPath(intptr_t id) { |
| 207 MutexLocker lock(watcher_mutex); |
| 208 |
| 209 FSEventsWatcher::Node* node = reinterpret_cast<FSEventsWatcher::Node*>(id); |
| 210 node->Stop(); |
| 211 delete node; |
| 212 |
| 213 FSEventsWatcher::Decrement(); |
| 214 } |
| 215 |
| 216 |
| 217 intptr_t FileSystemWatcher::GetSocketId(intptr_t id) { |
| 218 return reinterpret_cast<FSEventsWatcher::Node*>(id)->read_fd(); |
| 219 } |
| 220 |
| 221 |
| 222 Dart_Handle FileSystemWatcher::ReadEvents(intptr_t id) { |
| 223 intptr_t fd = GetSocketId(id); |
| 224 intptr_t avail = FDUtils::AvailableBytes(fd); |
| 225 int count = avail / sizeof(FSEvent); |
| 226 if (count <= 0) return Dart_NewList(0); |
| 227 Dart_Handle events = Dart_NewList(count); |
| 228 FSEvent e; |
| 229 for (int i = 0; i < count; i++) { |
| 230 intptr_t bytes = TEMP_FAILURE_RETRY(read(fd, e.bytes, sizeof(e))); |
| 231 if (bytes < 0) { |
| 232 return DartUtils::NewDartOSError(); |
| 233 } |
| 234 Dart_Handle event = Dart_NewList(3); |
| 235 int flags = e.data.flags; |
| 236 int mask = 0; |
| 237 if (flags & kFSEventStreamEventFlagItemModified) mask |= kModifyContent; |
| 238 if (flags & kFSEventStreamEventFlagItemRenamed) mask |= kMove; |
| 239 if (flags & kFSEventStreamEventFlagItemXattrMod) mask |= kModefyAttribute; |
| 240 if (flags & kFSEventStreamEventFlagItemCreated) mask |= kCreate; |
| 241 if (flags & kFSEventStreamEventFlagItemRemoved) mask |= kDelete; |
| 242 Dart_ListSetAt(event, 0, Dart_NewInteger(mask)); |
| 243 Dart_ListSetAt(event, 1, Dart_NewInteger(1)); |
| 244 Dart_ListSetAt(event, 2, Dart_NewStringFromUTF8( |
| 245 reinterpret_cast<uint8_t*>(e.data.path), strlen(e.data.path))); |
| 246 Dart_ListSetAt(events, i, event); |
| 247 } |
| 248 return events; |
| 249 } |
| 250 |
| 251 } // namespace bin |
| 252 } // namespace dart |
| 253 |
| 254 #endif // defined(TARGET_OS_MACOS) |
| 255 |
| 256 |
OLD | NEW |