diff options
Diffstat (limited to 'clang/lib/DirectoryWatcher')
6 files changed, 0 insertions, 709 deletions
diff --git a/clang/lib/DirectoryWatcher/CMakeLists.txt b/clang/lib/DirectoryWatcher/CMakeLists.txt deleted file mode 100644 index 45a6443a173..00000000000 --- a/clang/lib/DirectoryWatcher/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -include(CheckIncludeFiles) - -set(LLVM_LINK_COMPONENTS support) - -set(DIRECTORY_WATCHER_SOURCES DirectoryScanner.cpp) -set(DIRECTORY_WATCHER_LINK_LIBS "") - -if(APPLE) - check_include_files("CoreServices/CoreServices.h" HAVE_CORESERVICES) - if(HAVE_CORESERVICES) - list(APPEND DIRECTORY_WATCHER_SOURCES mac/DirectoryWatcher-mac.cpp) - set(DIRECTORY_WATCHER_LINK_LIBS "-framework CoreServices") - endif() -elseif(CMAKE_SYSTEM_NAME MATCHES "Linux") - check_include_files("sys/inotify.h" HAVE_INOTIFY) - if(HAVE_INOTIFY) - list(APPEND DIRECTORY_WATCHER_SOURCES linux/DirectoryWatcher-linux.cpp) - find_package(Threads REQUIRED) - set(DIRECTORY_WATCHER_LINK_LIBS ${CMAKE_THREAD_LIBS_INIT}) - endif() -else() - list(APPEND DIRECTORY_WATCHER_SOURCES default/DirectoryWatcher-not-implemented.cpp) -endif() - -add_clang_library(clangDirectoryWatcher - ${DIRECTORY_WATCHER_SOURCES} - ) - -target_link_libraries(clangDirectoryWatcher PRIVATE ${DIRECTORY_WATCHER_LINK_LIBS}) diff --git a/clang/lib/DirectoryWatcher/DirectoryScanner.cpp b/clang/lib/DirectoryWatcher/DirectoryScanner.cpp deleted file mode 100644 index ecfec52f459..00000000000 --- a/clang/lib/DirectoryWatcher/DirectoryScanner.cpp +++ /dev/null @@ -1,54 +0,0 @@ -//===- DirectoryScanner.cpp - Utility functions for DirectoryWatcher ------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "DirectoryScanner.h" - -#include "llvm/Support/Path.h" - -namespace clang { - -using namespace llvm; - -Optional<sys::fs::file_status> getFileStatus(StringRef Path) { - sys::fs::file_status Status; - std::error_code EC = status(Path, Status); - if (EC) - return None; - return Status; -} - -std::vector<std::string> scanDirectory(StringRef Path) { - using namespace llvm::sys; - std::vector<std::string> Result; - - std::error_code EC; - for (auto It = fs::directory_iterator(Path, EC), - End = fs::directory_iterator(); - !EC && It != End; It.increment(EC)) { - auto status = getFileStatus(It->path()); - if (!status.hasValue()) - continue; - Result.emplace_back(sys::path::filename(It->path())); - } - - return Result; -} - -std::vector<DirectoryWatcher::Event> -getAsFileEvents(const std::vector<std::string> &Scan) { - std::vector<DirectoryWatcher::Event> Events; - Events.reserve(Scan.size()); - - for (const auto &File : Scan) { - Events.emplace_back(DirectoryWatcher::Event{ - DirectoryWatcher::Event::EventKind::Modified, File}); - } - return Events; -} - -} // namespace clang
\ No newline at end of file diff --git a/clang/lib/DirectoryWatcher/DirectoryScanner.h b/clang/lib/DirectoryWatcher/DirectoryScanner.h deleted file mode 100644 index 55731225e25..00000000000 --- a/clang/lib/DirectoryWatcher/DirectoryScanner.h +++ /dev/null @@ -1,29 +0,0 @@ -//===- DirectoryScanner.h - Utility functions for DirectoryWatcher --------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "clang/DirectoryWatcher/DirectoryWatcher.h" -#include "llvm/Support/FileSystem.h" -#include <string> -#include <vector> - -namespace clang { - -/// Gets names (filenames) of items in directory at \p Path. -/// \returns empty vector if \p Path is not a directory, doesn't exist or can't -/// be read from. -std::vector<std::string> scanDirectory(llvm::StringRef Path); - -/// Create event with EventKind::Added for every element in \p Scan. -std::vector<DirectoryWatcher::Event> -getAsFileEvents(const std::vector<std::string> &Scan); - -/// Gets status of file (or directory) at \p Path. -/// \returns llvm::None if \p Path doesn't exist or can't get the status. -llvm::Optional<llvm::sys::fs::file_status> getFileStatus(llvm::StringRef Path); - -} // namespace clang
\ No newline at end of file diff --git a/clang/lib/DirectoryWatcher/default/DirectoryWatcher-not-implemented.cpp b/clang/lib/DirectoryWatcher/default/DirectoryWatcher-not-implemented.cpp deleted file mode 100644 index e330ff06f50..00000000000 --- a/clang/lib/DirectoryWatcher/default/DirectoryWatcher-not-implemented.cpp +++ /dev/null @@ -1,19 +0,0 @@ -//===- DirectoryWatcher-not-implemented.cpp -------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "clang/DirectoryWatcher/DirectoryWatcher.h" - -using namespace llvm; -using namespace clang; - -std::unique_ptr<DirectoryWatcher> clang::DirectoryWatcher::create( - StringRef Path, - std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)> Receiver, - bool WaitForInitialSync) { - return nullptr; -}
\ No newline at end of file diff --git a/clang/lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp b/clang/lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp deleted file mode 100644 index 986ebc5d95f..00000000000 --- a/clang/lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp +++ /dev/null @@ -1,345 +0,0 @@ -//===- DirectoryWatcher-linux.cpp - Linux-platform directory watching -----===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "DirectoryScanner.h" -#include "clang/DirectoryWatcher/DirectoryWatcher.h" - -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/ScopeExit.h" -#include "llvm/Support/AlignOf.h" -#include "llvm/Support/Errno.h" -#include "llvm/Support/Mutex.h" -#include "llvm/Support/Path.h" -#include <atomic> -#include <condition_variable> -#include <mutex> -#include <queue> -#include <string> -#include <thread> -#include <vector> - -#include <fcntl.h> -#include <sys/epoll.h> -#include <sys/inotify.h> -#include <unistd.h> - -namespace { - -using namespace llvm; -using namespace clang; - -/// Pipe for inter-thread synchronization - for epoll-ing on multiple -/// conditions. It is meant for uni-directional 1:1 signalling - specifically: -/// no multiple consumers, no data passing. Thread waiting for signal should -/// poll the FDRead. Signalling thread should call signal() which writes single -/// character to FDRead. -struct SemaphorePipe { - // Expects two file-descriptors opened as a pipe in the canonical POSIX - // order: pipefd[0] refers to the read end of the pipe. pipefd[1] refers to - // the write end of the pipe. - SemaphorePipe(int pipefd[2]) - : FDRead(pipefd[0]), FDWrite(pipefd[1]), OwnsFDs(true) {} - SemaphorePipe(const SemaphorePipe &) = delete; - void operator=(const SemaphorePipe &) = delete; - SemaphorePipe(SemaphorePipe &&other) - : FDRead(other.FDRead), FDWrite(other.FDWrite), - OwnsFDs(other.OwnsFDs) // Someone could have moved from the other - // instance before. - { - other.OwnsFDs = false; - }; - - void signal() { - ssize_t Result = llvm::sys::RetryAfterSignal(-1, write, FDWrite, "A", 1); - assert(Result != -1); - } - ~SemaphorePipe() { - if (OwnsFDs) { - close(FDWrite); - close(FDRead); - } - } - const int FDRead; - const int FDWrite; - bool OwnsFDs; - - static llvm::Optional<SemaphorePipe> create() { - int InotifyPollingStopperFDs[2]; - if (pipe2(InotifyPollingStopperFDs, O_CLOEXEC) == -1) - return llvm::None; - return SemaphorePipe(InotifyPollingStopperFDs); - } -}; - -/// Mutex-protected queue of Events. -class EventQueue { - std::mutex Mtx; - std::condition_variable NonEmpty; - std::queue<DirectoryWatcher::Event> Events; - -public: - void push_back(const DirectoryWatcher::Event::EventKind K, - StringRef Filename) { - { - std::unique_lock<std::mutex> L(Mtx); - Events.emplace(K, Filename); - } - NonEmpty.notify_one(); - } - - // Blocks on caller thread and uses codition_variable to wait until there's an - // event to return. - DirectoryWatcher::Event pop_front_blocking() { - std::unique_lock<std::mutex> L(Mtx); - while (true) { - // Since we might have missed all the prior notifications on NonEmpty we - // have to check the queue first (under lock). - if (!Events.empty()) { - DirectoryWatcher::Event Front = Events.front(); - Events.pop(); - return Front; - } - NonEmpty.wait(L, [this]() { return !Events.empty(); }); - } - } -}; - -class DirectoryWatcherLinux : public clang::DirectoryWatcher { -public: - DirectoryWatcherLinux( - llvm::StringRef WatchedDirPath, - std::function<void(llvm::ArrayRef<Event>, bool)> Receiver, - bool WaitForInitialSync, int InotifyFD, int InotifyWD, - SemaphorePipe &&InotifyPollingStopSignal); - - ~DirectoryWatcherLinux() override { - StopWork(); - InotifyPollingThread.join(); - EventsReceivingThread.join(); - inotify_rm_watch(InotifyFD, InotifyWD); - llvm::sys::RetryAfterSignal(-1, close, InotifyFD); - } - -private: - const std::string WatchedDirPath; - // inotify file descriptor - int InotifyFD = -1; - // inotify watch descriptor - int InotifyWD = -1; - - EventQueue Queue; - - // Make sure lifetime of Receiver fully contains lifetime of - // EventsReceivingThread. - std::function<void(llvm::ArrayRef<Event>, bool)> Receiver; - - // Consumes inotify events and pushes directory watcher events to the Queue. - void InotifyPollingLoop(); - std::thread InotifyPollingThread; - // Using pipe so we can epoll two file descriptors at once - inotify and - // stopping condition. - SemaphorePipe InotifyPollingStopSignal; - - // Does the initial scan of the directory - directly calling Receiver, - // bypassing the Queue. Both InitialScan and EventReceivingLoop use Receiver - // which isn't necessarily thread-safe. - void InitialScan(); - - // Processing events from the Queue. - // In case client doesn't want to do the initial scan synchronously - // (WaitForInitialSync=false in ctor) we do the initial scan at the beginning - // of this thread. - std::thread EventsReceivingThread; - // Push event of WatcherGotInvalidated kind to the Queue to stop the loop. - // Both InitialScan and EventReceivingLoop use Receiver which isn't - // necessarily thread-safe. - void EventReceivingLoop(); - - // Stops all the async work. Reentrant. - void StopWork() { - Queue.push_back(DirectoryWatcher::Event::EventKind::WatcherGotInvalidated, - ""); - InotifyPollingStopSignal.signal(); - } -}; - -void DirectoryWatcherLinux::InotifyPollingLoop() { - // We want to be able to read ~30 events at once even in the worst case - // (obscenely long filenames). - constexpr size_t EventBufferLength = - 30 * (sizeof(struct inotify_event) + NAME_MAX + 1); - // http://man7.org/linux/man-pages/man7/inotify.7.html - // Some systems cannot read integer variables if they are not - // properly aligned. On other systems, incorrect alignment may - // decrease performance. Hence, the buffer used for reading from - // the inotify file descriptor should have the same alignment as - // struct inotify_event. - - auto ManagedBuffer = - llvm::make_unique<llvm::AlignedCharArray<alignof(struct inotify_event), - EventBufferLength>>(); - char *const Buf = ManagedBuffer->buffer; - - const int EpollFD = epoll_create1(EPOLL_CLOEXEC); - if (EpollFD == -1) { - StopWork(); - return; - } - auto EpollFDGuard = llvm::make_scope_exit([EpollFD]() { close(EpollFD); }); - - struct epoll_event EventSpec; - EventSpec.events = EPOLLIN; - EventSpec.data.fd = InotifyFD; - if (epoll_ctl(EpollFD, EPOLL_CTL_ADD, InotifyFD, &EventSpec) == -1) { - StopWork(); - return; - } - - EventSpec.data.fd = InotifyPollingStopSignal.FDRead; - if (epoll_ctl(EpollFD, EPOLL_CTL_ADD, InotifyPollingStopSignal.FDRead, - &EventSpec) == -1) { - StopWork(); - return; - } - - std::array<struct epoll_event, 2> EpollEventBuffer; - - while (true) { - const int EpollWaitResult = llvm::sys::RetryAfterSignal( - -1, epoll_wait, EpollFD, EpollEventBuffer.data(), - EpollEventBuffer.size(), /*timeout=*/-1 /*== infinity*/); - if (EpollWaitResult == -1) { - StopWork(); - return; - } - - // Multiple epoll_events can be received for a single file descriptor per - // epoll_wait call. - for (const auto &EpollEvent : EpollEventBuffer) { - if (EpollEvent.data.fd == InotifyPollingStopSignal.FDRead) { - StopWork(); - return; - } - } - - // epoll_wait() always return either error or >0 events. Since there was no - // event for stopping, it must be an inotify event ready for reading. - ssize_t NumRead = llvm::sys::RetryAfterSignal(-1, read, InotifyFD, Buf, - EventBufferLength); - for (char *P = Buf; P < Buf + NumRead;) { - if (P + sizeof(struct inotify_event) > Buf + NumRead) { - StopWork(); - llvm_unreachable("an incomplete inotify_event was read"); - return; - } - - struct inotify_event *Event = reinterpret_cast<struct inotify_event *>(P); - P += sizeof(struct inotify_event) + Event->len; - - if (Event->mask & (IN_CREATE | IN_MODIFY | IN_MOVED_TO | IN_DELETE) && - Event->len <= 0) { - StopWork(); - llvm_unreachable("expected a filename from inotify"); - return; - } - - if (Event->mask & (IN_CREATE | IN_MOVED_TO | IN_MODIFY)) { - Queue.push_back(DirectoryWatcher::Event::EventKind::Modified, - Event->name); - } else if (Event->mask & (IN_DELETE | IN_MOVED_FROM)) { - Queue.push_back(DirectoryWatcher::Event::EventKind::Removed, - Event->name); - } else if (Event->mask & (IN_DELETE_SELF | IN_MOVE_SELF)) { - Queue.push_back(DirectoryWatcher::Event::EventKind::WatchedDirRemoved, - ""); - StopWork(); - return; - } else if (Event->mask & IN_IGNORED) { - StopWork(); - return; - } else { - StopWork(); - llvm_unreachable("Unknown event type."); - return; - } - } - } -} - -void DirectoryWatcherLinux::InitialScan() { - this->Receiver(getAsFileEvents(scanDirectory(WatchedDirPath)), - /*IsInitial=*/true); -} - -void DirectoryWatcherLinux::EventReceivingLoop() { - while (true) { - DirectoryWatcher::Event Event = this->Queue.pop_front_blocking(); - this->Receiver(Event, false); - if (Event.Kind == - DirectoryWatcher::Event::EventKind::WatcherGotInvalidated) { - StopWork(); - return; - } - } -} - -DirectoryWatcherLinux::DirectoryWatcherLinux( - StringRef WatchedDirPath, - std::function<void(llvm::ArrayRef<Event>, bool)> Receiver, - bool WaitForInitialSync, int InotifyFD, int InotifyWD, - SemaphorePipe &&InotifyPollingStopSignal) - : WatchedDirPath(WatchedDirPath), InotifyFD(InotifyFD), - InotifyWD(InotifyWD), Receiver(Receiver), - InotifyPollingStopSignal(std::move(InotifyPollingStopSignal)) { - - InotifyPollingThread = std::thread([this]() { InotifyPollingLoop(); }); - // We have no guarantees about thread safety of the Receiver which is being - // used in both InitialScan and EventReceivingLoop. We shouldn't run these - // only synchronously. - if (WaitForInitialSync) { - InitialScan(); - EventsReceivingThread = std::thread([this]() { EventReceivingLoop(); }); - } else { - EventsReceivingThread = std::thread([this]() { - // FIXME: We might want to terminate an async initial scan early in case - // of a failure in EventsReceivingThread. - InitialScan(); - EventReceivingLoop(); - }); - } -} - -} // namespace - -std::unique_ptr<DirectoryWatcher> clang::DirectoryWatcher::create( - StringRef Path, - std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)> Receiver, - bool WaitForInitialSync) { - if (Path.empty()) - return nullptr; - - const int InotifyFD = inotify_init1(IN_CLOEXEC); - if (InotifyFD == -1) - return nullptr; - - const int InotifyWD = inotify_add_watch( - InotifyFD, Path.str().c_str(), - IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_EXCL_UNLINK | IN_MODIFY | - IN_MOVED_FROM | IN_MOVE_SELF | IN_MOVED_TO | IN_ONLYDIR | IN_IGNORED); - if (InotifyWD == -1) - return nullptr; - - auto InotifyPollingStopper = SemaphorePipe::create(); - - if (!InotifyPollingStopper) - return nullptr; - - return llvm::make_unique<DirectoryWatcherLinux>( - Path, Receiver, WaitForInitialSync, InotifyFD, InotifyWD, - std::move(*InotifyPollingStopper)); -}
\ No newline at end of file diff --git a/clang/lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp b/clang/lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp deleted file mode 100644 index 3df79ac48a4..00000000000 --- a/clang/lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp +++ /dev/null @@ -1,233 +0,0 @@ -//===- DirectoryWatcher-mac.cpp - Mac-platform directory watching ---------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "DirectoryScanner.h" -#include "clang/DirectoryWatcher/DirectoryWatcher.h" - -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/Path.h" -#include <CoreServices/CoreServices.h> - -using namespace llvm; -using namespace clang; - -static FSEventStreamRef createFSEventStream( - StringRef Path, - std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)>, - dispatch_queue_t); -static void stopFSEventStream(FSEventStreamRef); - -namespace { - -class DirectoryWatcherMac : public clang::DirectoryWatcher { -public: - DirectoryWatcherMac( - FSEventStreamRef EventStream, - std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)> - Receiver, - llvm::StringRef WatchedDirPath) - : EventStream(EventStream), Receiver(Receiver), - WatchedDirPath(WatchedDirPath) {} - - ~DirectoryWatcherMac() override { - stopFSEventStream(EventStream); - EventStream = nullptr; - // Now it's safe to use Receiver as the only other concurrent use would have - // been in EventStream processing. - Receiver(DirectoryWatcher::Event( - DirectoryWatcher::Event::EventKind::WatcherGotInvalidated, ""), - false); - } - -private: - FSEventStreamRef EventStream; - std::function<void(llvm::ArrayRef<Event>, bool)> Receiver; - const std::string WatchedDirPath; -}; - -struct EventStreamContextData { - std::string WatchedPath; - std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)> Receiver; - - EventStreamContextData( - std::string &&WatchedPath, - std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)> - Receiver) - : WatchedPath(std::move(WatchedPath)), Receiver(Receiver) {} - - // Needed for FSEvents - static void dispose(const void *ctx) { - delete static_cast<const EventStreamContextData *>(ctx); - } -}; -} // namespace - -constexpr const FSEventStreamEventFlags StreamInvalidatingFlags = - kFSEventStreamEventFlagUserDropped | kFSEventStreamEventFlagKernelDropped | - kFSEventStreamEventFlagMustScanSubDirs; - -constexpr const FSEventStreamEventFlags ModifyingFileEvents = - kFSEventStreamEventFlagItemCreated | kFSEventStreamEventFlagItemRenamed | - kFSEventStreamEventFlagItemModified; - -static void eventStreamCallback(ConstFSEventStreamRef Stream, - void *ClientCallBackInfo, size_t NumEvents, - void *EventPaths, - const FSEventStreamEventFlags EventFlags[], - const FSEventStreamEventId EventIds[]) { - auto *ctx = static_cast<EventStreamContextData *>(ClientCallBackInfo); - - std::vector<DirectoryWatcher::Event> Events; - for (size_t i = 0; i < NumEvents; ++i) { - StringRef Path = ((const char **)EventPaths)[i]; - const FSEventStreamEventFlags Flags = EventFlags[i]; - - if (Flags & StreamInvalidatingFlags) { - Events.emplace_back(DirectoryWatcher::Event{ - DirectoryWatcher::Event::EventKind::WatcherGotInvalidated, ""}); - break; - } else if (!(Flags & kFSEventStreamEventFlagItemIsFile)) { - // Subdirectories aren't supported - if some directory got removed it - // must've been the watched directory itself. - if ((Flags & kFSEventStreamEventFlagItemRemoved) && - Path == ctx->WatchedPath) { - Events.emplace_back(DirectoryWatcher::Event{ - DirectoryWatcher::Event::EventKind::WatchedDirRemoved, ""}); - Events.emplace_back(DirectoryWatcher::Event{ - DirectoryWatcher::Event::EventKind::WatcherGotInvalidated, ""}); - break; - } - // No support for subdirectories - just ignore everything. - continue; - } else if (Flags & kFSEventStreamEventFlagItemRemoved) { - Events.emplace_back(DirectoryWatcher::Event::EventKind::Removed, - llvm::sys::path::filename(Path)); - continue; - } else if (Flags & ModifyingFileEvents) { - if (!getFileStatus(Path).hasValue()) { - Events.emplace_back(DirectoryWatcher::Event::EventKind::Removed, - llvm::sys::path::filename(Path)); - } else { - Events.emplace_back(DirectoryWatcher::Event::EventKind::Modified, - llvm::sys::path::filename(Path)); - } - continue; - } - - // default - Events.emplace_back(DirectoryWatcher::Event{ - DirectoryWatcher::Event::EventKind::WatcherGotInvalidated, ""}); - llvm_unreachable("Unknown FSEvent type."); - } - - if (!Events.empty()) { - ctx->Receiver(Events, /*IsInitial=*/false); - } -} - -FSEventStreamRef createFSEventStream( - StringRef Path, - std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)> Receiver, - dispatch_queue_t Queue) { - if (Path.empty()) - return nullptr; - - CFMutableArrayRef PathsToWatch = [&]() { - CFMutableArrayRef PathsToWatch = - CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks); - CFStringRef CfPathStr = - CFStringCreateWithBytes(nullptr, (const UInt8 *)Path.data(), - Path.size(), kCFStringEncodingUTF8, false); - CFArrayAppendValue(PathsToWatch, CfPathStr); - CFRelease(CfPathStr); - return PathsToWatch; - }(); - - FSEventStreamContext Context = [&]() { - std::string RealPath; - { - SmallString<128> Storage; - StringRef P = llvm::Twine(Path).toNullTerminatedStringRef(Storage); - char Buffer[PATH_MAX]; - if (::realpath(P.begin(), Buffer) != nullptr) - RealPath = Buffer; - else - RealPath = Path; - } - - FSEventStreamContext Context; - Context.version = 0; - Context.info = new EventStreamContextData(std::move(RealPath), Receiver); - Context.retain = nullptr; - Context.release = EventStreamContextData::dispose; - Context.copyDescription = nullptr; - return Context; - }(); - - FSEventStreamRef Result = FSEventStreamCreate( - nullptr, eventStreamCallback, &Context, PathsToWatch, - kFSEventStreamEventIdSinceNow, /* latency in seconds */ 0.0, - kFSEventStreamCreateFlagFileEvents | kFSEventStreamCreateFlagNoDefer); - CFRelease(PathsToWatch); - - return Result; -} - -void stopFSEventStream(FSEventStreamRef EventStream) { - if (!EventStream) - return; - FSEventStreamStop(EventStream); - FSEventStreamInvalidate(EventStream); - FSEventStreamRelease(EventStream); -} - -std::unique_ptr<DirectoryWatcher> clang::DirectoryWatcher::create( - StringRef Path, - std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)> Receiver, - bool WaitForInitialSync) { - dispatch_queue_t Queue = - dispatch_queue_create("DirectoryWatcher", DISPATCH_QUEUE_SERIAL); - - if (Path.empty()) - return nullptr; - - auto EventStream = createFSEventStream(Path, Receiver, Queue); - if (!EventStream) { - return nullptr; - } - - std::unique_ptr<DirectoryWatcher> Result = - llvm::make_unique<DirectoryWatcherMac>(EventStream, Receiver, Path); - - // We need to copy the data so the lifetime is ok after a const copy is made - // for the block. - const std::string CopiedPath = Path; - - auto InitWork = ^{ - // We need to start watching the directory before we start scanning in order - // to not miss any event. By dispatching this on the same serial Queue as - // the FSEvents will be handled we manage to start watching BEFORE the - // inital scan and handling events ONLY AFTER the scan finishes. - FSEventStreamSetDispatchQueue(EventStream, Queue); - FSEventStreamStart(EventStream); - // We need to decrement the ref count for Queue as initialize() will return - // and FSEvents has incremented it. Since we have to wait for FSEvents to - // take ownership it's the easiest to do it here rather than main thread. - dispatch_release(Queue); - Receiver(getAsFileEvents(scanDirectory(CopiedPath)), /*IsInitial=*/true); - }; - - if (WaitForInitialSync) { - dispatch_sync(Queue, InitWork); - } else { - dispatch_async(Queue, InitWork); - } - - return Result; -} |