Chromium Code Reviews| 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 { | |
|
Søren Gjesse
2013/08/23 07:47:45
Please rename this to FileSystemEventsWatcher. The
| |
| 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]; | |
|
Søren Gjesse
2013/08/23 07:47:45
Use two named members
read_fd_
write_fd_
| |
| 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]); | |
|
Søren Gjesse
2013/08/23 07:47:45
SetCloseOnExec on both fds?
| |
| 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); | |
|
Søren Gjesse
2013/08/23 07:47:45
Two blank lines.
| |
| 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, | |
| 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 |