<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">Hi Mikael,<div class=""><br class=""></div><div class="">Thanks for the notice.</div><div class=""><br class=""></div><div class="">How about this?</div><div class=""><a href="https://reviews.llvm.org/D64764" class="">https://reviews.llvm.org/D64764</a></div><div class=""><br class=""></div><div class="">Jan<br class=""><div><br class=""><blockquote type="cite" class=""><div class="">On Jul 14, 2019, at 11:45 PM, Mikael Holmén <<a href="mailto:mikael.holmen@ericsson.com" class="">mikael.holmen@ericsson.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div class="">Hi Jan,<br class=""><br class=""><blockquote type="cite" class="">+elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")<br class="">+ check_include_files("sys/inotify.h" HAVE_INOTIFY)<br class="">+ if(HAVE_INOTIFY)<br class="">+ list(APPEND DIRECTORY_WATCHER_SOURCES <br class=""></blockquote>linux/DirectoryWatcher-linux.cpp)<br class=""><blockquote type="cite" class="">+ find_package(Threads REQUIRED)<br class="">+ endif()<br class=""></blockquote><br class="">I don't think the above is enough, I think the version needs to be <br class="">checked some way as well to make sure we find an inotify.h that really <br class="">defines IN_EXCL_UNLINK.<br class=""><br class="">We have RHEL 6.10 buildbots with glibc 2.12.2 and kernel 2.6.32 where I see<br class=""><br class="">-- Looking for include file sys/inotify.h<br class="">-- Looking for include file sys/inotify.h - found<br class=""><br class="">but then get<br class=""><br class="">/repo/uabelho/master/clang/lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp:335:48: <br class="">error: 'IN_EXCL_UNLINK' was not declared in this scope<br class=""> IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_EXCL_UNLINK | <br class="">IN_MODIFY |<br class=""> ^~~~~~~~~~~~~~<br class=""><br class="">Regards,<br class="">Mikael<br class=""><br class="">On 2019-07-12 22:34, Jan Korous via cfe-commits wrote:<br class=""><blockquote type="cite" class="">Author: jkorous<br class="">Date: Fri Jul 12 13:34:10 2019<br class="">New Revision: 365954<br class=""><br class="">URL: <a href="http://llvm.org/viewvc/llvm-project?rev=365954&view=rev" class="">http://llvm.org/viewvc/llvm-project?rev=365954&view=rev</a><br class="">Log:<br class="">Reland [clang] DirectoryWatcher<br class=""><br class="">This reverts commit f561227d133224d2d6a5a016abe4be051fa75501.<br class=""><br class="">- DirectoryWatcher<br class="">- Fix the build for platforms that don't have DW implementated.<br class="">- Fix the threading dependencies (thanks to compnerd).<br class=""><br class="">Added:<br class=""> cfe/trunk/include/clang/DirectoryWatcher/<br class=""> cfe/trunk/include/clang/DirectoryWatcher/DirectoryWatcher.h<br class=""> cfe/trunk/lib/DirectoryWatcher/<br class=""> cfe/trunk/lib/DirectoryWatcher/CMakeLists.txt<br class=""> cfe/trunk/lib/DirectoryWatcher/DirectoryScanner.cpp<br class=""> cfe/trunk/lib/DirectoryWatcher/DirectoryScanner.h<br class=""> cfe/trunk/lib/DirectoryWatcher/default/<br class=""> cfe/trunk/lib/DirectoryWatcher/default/DirectoryWatcher-not-implemented.cpp<br class=""> cfe/trunk/lib/DirectoryWatcher/linux/<br class=""> cfe/trunk/lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp<br class=""> cfe/trunk/lib/DirectoryWatcher/mac/<br class=""> cfe/trunk/lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp<br class=""> cfe/trunk/unittests/DirectoryWatcher/<br class=""> cfe/trunk/unittests/DirectoryWatcher/CMakeLists.txt<br class=""> cfe/trunk/unittests/DirectoryWatcher/DirectoryWatcherTest.cpp<br class="">Modified:<br class=""> cfe/trunk/lib/CMakeLists.txt<br class=""> cfe/trunk/unittests/CMakeLists.txt<br class=""><br class="">Added: cfe/trunk/include/clang/DirectoryWatcher/DirectoryWatcher.h<br class="">URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/DirectoryWatcher/DirectoryWatcher.h?rev=365954&view=auto" class="">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/DirectoryWatcher/DirectoryWatcher.h?rev=365954&view=auto</a><br class="">==============================================================================<br class="">--- cfe/trunk/include/clang/DirectoryWatcher/DirectoryWatcher.h (added)<br class="">+++ cfe/trunk/include/clang/DirectoryWatcher/DirectoryWatcher.h Fri Jul 12 13:34:10 2019<br class="">@@ -0,0 +1,123 @@<br class="">+//===- DirectoryWatcher.h - Listens for directory file changes --*- C++ -*-===//<br class="">+//<br class="">+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.<br class="">+// See <a href="https://llvm.org/LICENSE.txt" class="">https://llvm.org/LICENSE.txt</a> for license information.<br class="">+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception<br class="">+//<br class="">+//===----------------------------------------------------------------------===//<br class="">+<br class="">+#ifndef LLVM_CLANG_DIRECTORYWATCHER_DIRECTORYWATCHER_H<br class="">+#define LLVM_CLANG_DIRECTORYWATCHER_DIRECTORYWATCHER_H<br class="">+<br class="">+#include "llvm/ADT/ArrayRef.h"<br class="">+#include "llvm/ADT/StringRef.h"<br class="">+#include <functional><br class="">+#include <memory><br class="">+#include <string><br class="">+<br class="">+namespace clang {<br class="">+/// Provides notifications for file changes in a directory.<br class="">+///<br class="">+/// Invokes client-provided function on every filesystem event in the watched<br class="">+/// directory. Initially the the watched directory is scanned and for every file<br class="">+/// found, an event is synthesized as if the file was added.<br class="">+///<br class="">+/// This is not a general purpose directory monitoring tool - list of<br class="">+/// limitations follows.<br class="">+///<br class="">+/// Only flat directories with no subdirectories are supported. In case<br class="">+/// subdirectories are present the behavior is unspecified - events *might* be<br class="">+/// passed to Receiver on macOS (due to FSEvents being used) while they<br class="">+/// *probably* won't be passed on Linux (due to inotify being used).<br class="">+///<br class="">+/// Known potential inconsistencies<br class="">+/// - For files that are deleted befor the initial scan processed them, clients<br class="">+/// might receive Removed notification without any prior Added notification.<br class="">+/// - Multiple notifications might be produced when a file is added to the<br class="">+/// watched directory during the initial scan. We are choosing the lesser evil<br class="">+/// here as the only known alternative strategy would be to invalidate the<br class="">+/// watcher instance and force user to create a new one whenever filesystem<br class="">+/// event occurs during the initial scan but that would introduce continuous<br class="">+/// restarting failure mode (watched directory is not always "owned" by the same<br class="">+/// process that is consuming it). Since existing clients can handle duplicate<br class="">+/// events well, we decided for simplicity.<br class="">+///<br class="">+/// Notifications are provided only for changes done through local user-space<br class="">+/// filesystem interface. Specifically, it's unspecified if notification would<br class="">+/// be provided in case of a:<br class="">+/// - a file mmap-ed and changed<br class="">+/// - a file changed via remote (NFS) or virtual (/proc) FS access to monitored<br class="">+/// directory<br class="">+/// - another filesystem mounted to the watched directory<br class="">+///<br class="">+/// No support for LLVM VFS.<br class="">+///<br class="">+/// It is unspecified whether notifications for files being deleted are sent in<br class="">+/// case the whole watched directory is sent.<br class="">+///<br class="">+/// Directories containing "too many" files and/or receiving events "too<br class="">+/// frequently" are not supported - if the initial scan can't be finished before<br class="">+/// the watcher instance gets invalidated (see WatcherGotInvalidated) there's no<br class="">+/// good error handling strategy - the only option for client is to destroy the<br class="">+/// watcher, restart watching with new instance and hope it won't repeat.<br class="">+class DirectoryWatcher {<br class="">+public:<br class="">+ struct Event {<br class="">+ enum class EventKind {<br class="">+ Removed,<br class="">+ /// Content of a file was modified.<br class="">+ Modified,<br class="">+ /// The watched directory got deleted.<br class="">+ WatchedDirRemoved,<br class="">+ /// The DirectoryWatcher that originated this event is no longer valid and<br class="">+ /// its behavior is unspecified.<br class="">+ ///<br class="">+ /// The prime case is kernel signalling to OS-specific implementation of<br class="">+ /// DirectoryWatcher some resource limit being hit.<br class="">+ /// *Usually* kernel starts dropping or squashing events together after<br class="">+ /// that and so would DirectoryWatcher. This means that *some* events<br class="">+ /// might still be passed to Receiver but this behavior is unspecified.<br class="">+ ///<br class="">+ /// Another case is after the watched directory itself is deleted.<br class="">+ /// WatcherGotInvalidated will be received at least once during<br class="">+ /// DirectoryWatcher instance lifetime - when handling errors this is done<br class="">+ /// on best effort basis, when an instance is being destroyed then this is<br class="">+ /// guaranteed.<br class="">+ ///<br class="">+ /// The only proper response to this kind of event is to destruct the<br class="">+ /// originating DirectoryWatcher instance and create a new one.<br class="">+ WatcherGotInvalidated<br class="">+ };<br class="">+<br class="">+ EventKind Kind;<br class="">+ /// Filename that this event is related to or an empty string in<br class="">+ /// case this event is related to the watched directory itself.<br class="">+ std::string Filename;<br class="">+<br class="">+ Event(EventKind Kind, llvm::StringRef Filename)<br class="">+ : Kind(Kind), Filename(Filename) {}<br class="">+ };<br class="">+<br class="">+ /// Returns nullptr if \param Path doesn't exist.<br class="">+ /// Returns nullptr if \param Path isn't a directory.<br class="">+ /// Returns nullptr if OS kernel API told us we can't start watching. In such<br class="">+ /// case it's unclear whether just retrying has any chance to succeeed.<br class="">+ static std::unique_ptr<DirectoryWatcher><br class="">+ create(llvm::StringRef Path,<br class="">+ std::function<void(llvm::ArrayRef<DirectoryWatcher::Event> Events,<br class="">+ bool IsInitial)><br class="">+ Receiver,<br class="">+ bool WaitForInitialSync);<br class="">+<br class="">+ virtual ~DirectoryWatcher() = default;<br class="">+ DirectoryWatcher(const DirectoryWatcher &) = delete;<br class="">+ DirectoryWatcher &operator=(const DirectoryWatcher &) = delete;<br class="">+ DirectoryWatcher(DirectoryWatcher &&) = default;<br class="">+<br class="">+protected:<br class="">+ DirectoryWatcher() = default;<br class="">+};<br class="">+<br class="">+} // namespace clang<br class="">+<br class="">+#endif // LLVM_CLANG_DIRECTORYWATCHER_DIRECTORYWATCHER_H<br class=""><br class="">Modified: cfe/trunk/lib/CMakeLists.txt<br class="">URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CMakeLists.txt?rev=365954&r1=365953&r2=365954&view=diff" class="">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CMakeLists.txt?rev=365954&r1=365953&r2=365954&view=diff</a><br class="">==============================================================================<br class="">--- cfe/trunk/lib/CMakeLists.txt (original)<br class="">+++ cfe/trunk/lib/CMakeLists.txt Fri Jul 12 13:34:10 2019<br class="">@@ -18,6 +18,7 @@ add_subdirectory(Serialization)<br class=""> add_subdirectory(Frontend)<br class=""> add_subdirectory(FrontendTool)<br class=""> add_subdirectory(Tooling)<br class="">+add_subdirectory(DirectoryWatcher)<br class=""> add_subdirectory(Index)<br class=""> if(CLANG_ENABLE_STATIC_ANALYZER)<br class=""> add_subdirectory(StaticAnalyzer)<br class=""><br class="">Added: cfe/trunk/lib/DirectoryWatcher/CMakeLists.txt<br class="">URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/DirectoryWatcher/CMakeLists.txt?rev=365954&view=auto" class="">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/DirectoryWatcher/CMakeLists.txt?rev=365954&view=auto</a><br class="">==============================================================================<br class="">--- cfe/trunk/lib/DirectoryWatcher/CMakeLists.txt (added)<br class="">+++ cfe/trunk/lib/DirectoryWatcher/CMakeLists.txt Fri Jul 12 13:34:10 2019<br class="">@@ -0,0 +1,29 @@<br class="">+include(CheckIncludeFiles)<br class="">+<br class="">+set(LLVM_LINK_COMPONENTS support)<br class="">+<br class="">+set(DIRECTORY_WATCHER_SOURCES DirectoryScanner.cpp)<br class="">+set(DIRECTORY_WATCHER_LINK_LIBS "")<br class="">+<br class="">+if(APPLE)<br class="">+ check_include_files("CoreServices/CoreServices.h" HAVE_CORESERVICES)<br class="">+ if(HAVE_CORESERVICES)<br class="">+ list(APPEND DIRECTORY_WATCHER_SOURCES mac/DirectoryWatcher-mac.cpp)<br class="">+ set(DIRECTORY_WATCHER_LINK_LIBS "-framework CoreServices")<br class="">+ endif()<br class="">+elseif(CMAKE_SYSTEM_NAME MATCHES "Linux")<br class="">+ check_include_files("sys/inotify.h" HAVE_INOTIFY)<br class="">+ if(HAVE_INOTIFY)<br class="">+ list(APPEND DIRECTORY_WATCHER_SOURCES linux/DirectoryWatcher-linux.cpp)<br class="">+ find_package(Threads REQUIRED)<br class="">+ endif()<br class="">+else()<br class="">+ list(APPEND DIRECTORY_WATCHER_SOURCES default/DirectoryWatcher-not-implemented.cpp)<br class="">+endif()<br class="">+<br class="">+add_clang_library(clangDirectoryWatcher<br class="">+ ${DIRECTORY_WATCHER_SOURCES}<br class="">+ )<br class="">+<br class="">+target_link_libraries(clangDirectoryWatcher PUBLIC ${CMAKE_THREAD_LIBS_INIT})<br class="">+target_link_libraries(clangDirectoryWatcher PRIVATE ${DIRECTORY_WATCHER_LINK_LIBS})<br class=""><br class="">Added: cfe/trunk/lib/DirectoryWatcher/DirectoryScanner.cpp<br class="">URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/DirectoryWatcher/DirectoryScanner.cpp?rev=365954&view=auto" class="">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/DirectoryWatcher/DirectoryScanner.cpp?rev=365954&view=auto</a><br class="">==============================================================================<br class="">--- cfe/trunk/lib/DirectoryWatcher/DirectoryScanner.cpp (added)<br class="">+++ cfe/trunk/lib/DirectoryWatcher/DirectoryScanner.cpp Fri Jul 12 13:34:10 2019<br class="">@@ -0,0 +1,54 @@<br class="">+//===- DirectoryScanner.cpp - Utility functions for DirectoryWatcher ------===//<br class="">+//<br class="">+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.<br class="">+// See <a href="https://llvm.org/LICENSE.txt" class="">https://llvm.org/LICENSE.txt</a> for license information.<br class="">+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception<br class="">+//<br class="">+//===----------------------------------------------------------------------===//<br class="">+<br class="">+#include "DirectoryScanner.h"<br class="">+<br class="">+#include "llvm/Support/Path.h"<br class="">+<br class="">+namespace clang {<br class="">+<br class="">+using namespace llvm;<br class="">+<br class="">+Optional<sys::fs::file_status> getFileStatus(StringRef Path) {<br class="">+ sys::fs::file_status Status;<br class="">+ std::error_code EC = status(Path, Status);<br class="">+ if (EC)<br class="">+ return None;<br class="">+ return Status;<br class="">+}<br class="">+<br class="">+std::vector<std::string> scanDirectory(StringRef Path) {<br class="">+ using namespace llvm::sys;<br class="">+ std::vector<std::string> Result;<br class="">+<br class="">+ std::error_code EC;<br class="">+ for (auto It = fs::directory_iterator(Path, EC),<br class="">+ End = fs::directory_iterator();<br class="">+ !EC && It != End; It.increment(EC)) {<br class="">+ auto status = getFileStatus(It->path());<br class="">+ if (!status.hasValue())<br class="">+ continue;<br class="">+ Result.emplace_back(sys::path::filename(It->path()));<br class="">+ }<br class="">+<br class="">+ return Result;<br class="">+}<br class="">+<br class="">+std::vector<DirectoryWatcher::Event><br class="">+getAsFileEvents(const std::vector<std::string> &Scan) {<br class="">+ std::vector<DirectoryWatcher::Event> Events;<br class="">+ Events.reserve(Scan.size());<br class="">+<br class="">+ for (const auto &File : Scan) {<br class="">+ Events.emplace_back(DirectoryWatcher::Event{<br class="">+ DirectoryWatcher::Event::EventKind::Modified, File});<br class="">+ }<br class="">+ return Events;<br class="">+}<br class="">+<br class="">+} // namespace clang<br class="">\ No newline at end of file<br class=""><br class="">Added: cfe/trunk/lib/DirectoryWatcher/DirectoryScanner.h<br class="">URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/DirectoryWatcher/DirectoryScanner.h?rev=365954&view=auto" class="">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/DirectoryWatcher/DirectoryScanner.h?rev=365954&view=auto</a><br class="">==============================================================================<br class="">--- cfe/trunk/lib/DirectoryWatcher/DirectoryScanner.h (added)<br class="">+++ cfe/trunk/lib/DirectoryWatcher/DirectoryScanner.h Fri Jul 12 13:34:10 2019<br class="">@@ -0,0 +1,29 @@<br class="">+//===- DirectoryScanner.h - Utility functions for DirectoryWatcher --------===//<br class="">+//<br class="">+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.<br class="">+// See <a href="https://llvm.org/LICENSE.txt" class="">https://llvm.org/LICENSE.txt</a> for license information.<br class="">+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception<br class="">+//<br class="">+//===----------------------------------------------------------------------===//<br class="">+<br class="">+#include "clang/DirectoryWatcher/DirectoryWatcher.h"<br class="">+#include "llvm/Support/FileSystem.h"<br class="">+#include <string><br class="">+#include <vector><br class="">+<br class="">+namespace clang {<br class="">+<br class="">+/// Gets names (filenames) of items in directory at \p Path.<br class="">+/// \returns empty vector if \p Path is not a directory, doesn't exist or can't<br class="">+/// be read from.<br class="">+std::vector<std::string> scanDirectory(llvm::StringRef Path);<br class="">+<br class="">+/// Create event with EventKind::Added for every element in \p Scan.<br class="">+std::vector<DirectoryWatcher::Event><br class="">+getAsFileEvents(const std::vector<std::string> &Scan);<br class="">+<br class="">+/// Gets status of file (or directory) at \p Path.<br class="">+/// \returns llvm::None if \p Path doesn't exist or can't get the status.<br class="">+llvm::Optional<llvm::sys::fs::file_status> getFileStatus(llvm::StringRef Path);<br class="">+<br class="">+} // namespace clang<br class="">\ No newline at end of file<br class=""><br class="">Added: cfe/trunk/lib/DirectoryWatcher/default/DirectoryWatcher-not-implemented.cpp<br class="">URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/DirectoryWatcher/default/DirectoryWatcher-not-implemented.cpp?rev=365954&view=auto" class="">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/DirectoryWatcher/default/DirectoryWatcher-not-implemented.cpp?rev=365954&view=auto</a><br class="">==============================================================================<br class="">--- cfe/trunk/lib/DirectoryWatcher/default/DirectoryWatcher-not-implemented.cpp (added)<br class="">+++ cfe/trunk/lib/DirectoryWatcher/default/DirectoryWatcher-not-implemented.cpp Fri Jul 12 13:34:10 2019<br class="">@@ -0,0 +1,19 @@<br class="">+//===- DirectoryWatcher-not-implemented.cpp -------------------------------===//<br class="">+//<br class="">+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.<br class="">+// See <a href="https://llvm.org/LICENSE.txt" class="">https://llvm.org/LICENSE.txt</a> for license information.<br class="">+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception<br class="">+//<br class="">+//===----------------------------------------------------------------------===//<br class="">+<br class="">+#include "clang/DirectoryWatcher/DirectoryWatcher.h"<br class="">+<br class="">+using namespace llvm;<br class="">+using namespace clang;<br class="">+<br class="">+std::unique_ptr<DirectoryWatcher> clang::DirectoryWatcher::create(<br class="">+ StringRef Path,<br class="">+ std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)> Receiver,<br class="">+ bool WaitForInitialSync) {<br class="">+ return nullptr;<br class="">+}<br class="">\ No newline at end of file<br class=""><br class="">Added: cfe/trunk/lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp<br class="">URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp?rev=365954&view=auto" class="">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp?rev=365954&view=auto</a><br class="">==============================================================================<br class="">--- cfe/trunk/lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp (added)<br class="">+++ cfe/trunk/lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp Fri Jul 12 13:34:10 2019<br class="">@@ -0,0 +1,345 @@<br class="">+//===- DirectoryWatcher-linux.cpp - Linux-platform directory watching -----===//<br class="">+//<br class="">+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.<br class="">+// See <a href="https://llvm.org/LICENSE.txt" class="">https://llvm.org/LICENSE.txt</a> for license information.<br class="">+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception<br class="">+//<br class="">+//===----------------------------------------------------------------------===//<br class="">+<br class="">+#include "DirectoryScanner.h"<br class="">+#include "clang/DirectoryWatcher/DirectoryWatcher.h"<br class="">+<br class="">+#include "llvm/ADT/STLExtras.h"<br class="">+#include "llvm/ADT/ScopeExit.h"<br class="">+#include "llvm/Support/AlignOf.h"<br class="">+#include "llvm/Support/Errno.h"<br class="">+#include "llvm/Support/Mutex.h"<br class="">+#include "llvm/Support/Path.h"<br class="">+#include <atomic><br class="">+#include <condition_variable><br class="">+#include <mutex><br class="">+#include <queue><br class="">+#include <string><br class="">+#include <thread><br class="">+#include <vector><br class="">+<br class="">+#include <fcntl.h><br class="">+#include <sys/epoll.h><br class="">+#include <sys/inotify.h><br class="">+#include <unistd.h><br class="">+<br class="">+namespace {<br class="">+<br class="">+using namespace llvm;<br class="">+using namespace clang;<br class="">+<br class="">+/// Pipe for inter-thread synchronization - for epoll-ing on multiple<br class="">+/// conditions. It is meant for uni-directional 1:1 signalling - specifically:<br class="">+/// no multiple consumers, no data passing. Thread waiting for signal should<br class="">+/// poll the FDRead. Signalling thread should call signal() which writes single<br class="">+/// character to FDRead.<br class="">+struct SemaphorePipe {<br class="">+ // Expects two file-descriptors opened as a pipe in the canonical POSIX<br class="">+ // order: pipefd[0] refers to the read end of the pipe. pipefd[1] refers to<br class="">+ // the write end of the pipe.<br class="">+ SemaphorePipe(int pipefd[2])<br class="">+ : FDRead(pipefd[0]), FDWrite(pipefd[1]), OwnsFDs(true) {}<br class="">+ SemaphorePipe(const SemaphorePipe &) = delete;<br class="">+ void operator=(const SemaphorePipe &) = delete;<br class="">+ SemaphorePipe(SemaphorePipe &&other)<br class="">+ : FDRead(other.FDRead), FDWrite(other.FDWrite),<br class="">+ OwnsFDs(other.OwnsFDs) // Someone could have moved from the other<br class="">+ // instance before.<br class="">+ {<br class="">+ other.OwnsFDs = false;<br class="">+ };<br class="">+<br class="">+ void signal() {<br class="">+ ssize_t Result = llvm::sys::RetryAfterSignal(-1, write, FDWrite, "A", 1);<br class="">+ assert(Result != -1);<br class="">+ }<br class="">+ ~SemaphorePipe() {<br class="">+ if (OwnsFDs) {<br class="">+ close(FDWrite);<br class="">+ close(FDRead);<br class="">+ }<br class="">+ }<br class="">+ const int FDRead;<br class="">+ const int FDWrite;<br class="">+ bool OwnsFDs;<br class="">+<br class="">+ static llvm::Optional<SemaphorePipe> create() {<br class="">+ int InotifyPollingStopperFDs[2];<br class="">+ if (pipe2(InotifyPollingStopperFDs, O_CLOEXEC) == -1)<br class="">+ return llvm::None;<br class="">+ return SemaphorePipe(InotifyPollingStopperFDs);<br class="">+ }<br class="">+};<br class="">+<br class="">+/// Mutex-protected queue of Events.<br class="">+class EventQueue {<br class="">+ std::mutex Mtx;<br class="">+ std::condition_variable NonEmpty;<br class="">+ std::queue<DirectoryWatcher::Event> Events;<br class="">+<br class="">+public:<br class="">+ void push_back(const DirectoryWatcher::Event::EventKind K,<br class="">+ StringRef Filename) {<br class="">+ {<br class="">+ std::unique_lock<std::mutex> L(Mtx);<br class="">+ Events.emplace(K, Filename);<br class="">+ }<br class="">+ NonEmpty.notify_one();<br class="">+ }<br class="">+<br class="">+ // Blocks on caller thread and uses codition_variable to wait until there's an<br class="">+ // event to return.<br class="">+ DirectoryWatcher::Event pop_front_blocking() {<br class="">+ std::unique_lock<std::mutex> L(Mtx);<br class="">+ while (true) {<br class="">+ // Since we might have missed all the prior notifications on NonEmpty we<br class="">+ // have to check the queue first (under lock).<br class="">+ if (!Events.empty()) {<br class="">+ DirectoryWatcher::Event Front = Events.front();<br class="">+ Events.pop();<br class="">+ return Front;<br class="">+ }<br class="">+ NonEmpty.wait(L, [this]() { return !Events.empty(); });<br class="">+ }<br class="">+ }<br class="">+};<br class="">+<br class="">+class DirectoryWatcherLinux : public clang::DirectoryWatcher {<br class="">+public:<br class="">+ DirectoryWatcherLinux(<br class="">+ llvm::StringRef WatchedDirPath,<br class="">+ std::function<void(llvm::ArrayRef<Event>, bool)> Receiver,<br class="">+ bool WaitForInitialSync, int InotifyFD, int InotifyWD,<br class="">+ SemaphorePipe &&InotifyPollingStopSignal);<br class="">+<br class="">+ ~DirectoryWatcherLinux() override {<br class="">+ StopWork();<br class="">+ InotifyPollingThread.join();<br class="">+ EventsReceivingThread.join();<br class="">+ inotify_rm_watch(InotifyFD, InotifyWD);<br class="">+ llvm::sys::RetryAfterSignal(-1, close, InotifyFD);<br class="">+ }<br class="">+<br class="">+private:<br class="">+ const std::string WatchedDirPath;<br class="">+ // inotify file descriptor<br class="">+ int InotifyFD = -1;<br class="">+ // inotify watch descriptor<br class="">+ int InotifyWD = -1;<br class="">+<br class="">+ EventQueue Queue;<br class="">+<br class="">+ // Make sure lifetime of Receiver fully contains lifetime of<br class="">+ // EventsReceivingThread.<br class="">+ std::function<void(llvm::ArrayRef<Event>, bool)> Receiver;<br class="">+<br class="">+ // Consumes inotify events and pushes directory watcher events to the Queue.<br class="">+ void InotifyPollingLoop();<br class="">+ std::thread InotifyPollingThread;<br class="">+ // Using pipe so we can epoll two file descriptors at once - inotify and<br class="">+ // stopping condition.<br class="">+ SemaphorePipe InotifyPollingStopSignal;<br class="">+<br class="">+ // Does the initial scan of the directory - directly calling Receiver,<br class="">+ // bypassing the Queue. Both InitialScan and EventReceivingLoop use Receiver<br class="">+ // which isn't necessarily thread-safe.<br class="">+ void InitialScan();<br class="">+<br class="">+ // Processing events from the Queue.<br class="">+ // In case client doesn't want to do the initial scan synchronously<br class="">+ // (WaitForInitialSync=false in ctor) we do the initial scan at the beginning<br class="">+ // of this thread.<br class="">+ std::thread EventsReceivingThread;<br class="">+ // Push event of WatcherGotInvalidated kind to the Queue to stop the loop.<br class="">+ // Both InitialScan and EventReceivingLoop use Receiver which isn't<br class="">+ // necessarily thread-safe.<br class="">+ void EventReceivingLoop();<br class="">+<br class="">+ // Stops all the async work. Reentrant.<br class="">+ void StopWork() {<br class="">+ Queue.push_back(DirectoryWatcher::Event::EventKind::WatcherGotInvalidated,<br class="">+ "");<br class="">+ InotifyPollingStopSignal.signal();<br class="">+ }<br class="">+};<br class="">+<br class="">+void DirectoryWatcherLinux::InotifyPollingLoop() {<br class="">+ // We want to be able to read ~30 events at once even in the worst case<br class="">+ // (obscenely long filenames).<br class="">+ constexpr size_t EventBufferLength =<br class="">+ 30 * (sizeof(struct inotify_event) + NAME_MAX + 1);<br class="">+ // <a href="http://man7.org/linux/man-pages/man7/inotify.7.html" class="">http://man7.org/linux/man-pages/man7/inotify.7.html</a><br class="">+ // Some systems cannot read integer variables if they are not<br class="">+ // properly aligned. On other systems, incorrect alignment may<br class="">+ // decrease performance. Hence, the buffer used for reading from<br class="">+ // the inotify file descriptor should have the same alignment as<br class="">+ // struct inotify_event.<br class="">+<br class="">+ auto ManagedBuffer =<br class="">+ llvm::make_unique<llvm::AlignedCharArray<alignof(struct inotify_event),<br class="">+ EventBufferLength>>();<br class="">+ char *const Buf = ManagedBuffer->buffer;<br class="">+<br class="">+ const int EpollFD = epoll_create1(EPOLL_CLOEXEC);<br class="">+ if (EpollFD == -1) {<br class="">+ StopWork();<br class="">+ return;<br class="">+ }<br class="">+ auto EpollFDGuard = llvm::make_scope_exit([EpollFD]() { close(EpollFD); });<br class="">+<br class="">+ struct epoll_event EventSpec;<br class="">+ EventSpec.events = EPOLLIN;<br class="">+ EventSpec.data.fd = InotifyFD;<br class="">+ if (epoll_ctl(EpollFD, EPOLL_CTL_ADD, InotifyFD, &EventSpec) == -1) {<br class="">+ StopWork();<br class="">+ return;<br class="">+ }<br class="">+<br class="">+ EventSpec.data.fd = InotifyPollingStopSignal.FDRead;<br class="">+ if (epoll_ctl(EpollFD, EPOLL_CTL_ADD, InotifyPollingStopSignal.FDRead,<br class="">+ &EventSpec) == -1) {<br class="">+ StopWork();<br class="">+ return;<br class="">+ }<br class="">+<br class="">+ std::array<struct epoll_event, 2> EpollEventBuffer;<br class="">+<br class="">+ while (true) {<br class="">+ const int EpollWaitResult = llvm::sys::RetryAfterSignal(<br class="">+ -1, epoll_wait, EpollFD, EpollEventBuffer.data(),<br class="">+ EpollEventBuffer.size(), /*timeout=*/-1 /*== infinity*/);<br class="">+ if (EpollWaitResult == -1) {<br class="">+ StopWork();<br class="">+ return;<br class="">+ }<br class="">+<br class="">+ // Multiple epoll_events can be received for a single file descriptor per<br class="">+ // epoll_wait call.<br class="">+ for (const auto &EpollEvent : EpollEventBuffer) {<br class="">+ if (EpollEvent.data.fd == InotifyPollingStopSignal.FDRead) {<br class="">+ StopWork();<br class="">+ return;<br class="">+ }<br class="">+ }<br class="">+<br class="">+ // epoll_wait() always return either error or >0 events. Since there was no<br class="">+ // event for stopping, it must be an inotify event ready for reading.<br class="">+ ssize_t NumRead = llvm::sys::RetryAfterSignal(-1, read, InotifyFD, Buf,<br class="">+ EventBufferLength);<br class="">+ for (char *P = Buf; P < Buf + NumRead;) {<br class="">+ if (P + sizeof(struct inotify_event) > Buf + NumRead) {<br class="">+ StopWork();<br class="">+ llvm_unreachable("an incomplete inotify_event was read");<br class="">+ return;<br class="">+ }<br class="">+<br class="">+ struct inotify_event *Event = reinterpret_cast<struct inotify_event *>(P);<br class="">+ P += sizeof(struct inotify_event) + Event->len;<br class="">+<br class="">+ if (Event->mask & (IN_CREATE | IN_MODIFY | IN_MOVED_TO | IN_DELETE) &&<br class="">+ Event->len <= 0) {<br class="">+ StopWork();<br class="">+ llvm_unreachable("expected a filename from inotify");<br class="">+ return;<br class="">+ }<br class="">+<br class="">+ if (Event->mask & (IN_CREATE | IN_MOVED_TO | IN_MODIFY)) {<br class="">+ Queue.push_back(DirectoryWatcher::Event::EventKind::Modified,<br class="">+ Event->name);<br class="">+ } else if (Event->mask & (IN_DELETE | IN_MOVED_FROM)) {<br class="">+ Queue.push_back(DirectoryWatcher::Event::EventKind::Removed,<br class="">+ Event->name);<br class="">+ } else if (Event->mask & (IN_DELETE_SELF | IN_MOVE_SELF)) {<br class="">+ Queue.push_back(DirectoryWatcher::Event::EventKind::WatchedDirRemoved,<br class="">+ "");<br class="">+ StopWork();<br class="">+ return;<br class="">+ } else if (Event->mask & IN_IGNORED) {<br class="">+ StopWork();<br class="">+ return;<br class="">+ } else {<br class="">+ StopWork();<br class="">+ llvm_unreachable("Unknown event type.");<br class="">+ return;<br class="">+ }<br class="">+ }<br class="">+ }<br class="">+}<br class="">+<br class="">+void DirectoryWatcherLinux::InitialScan() {<br class="">+ this->Receiver(getAsFileEvents(scanDirectory(WatchedDirPath)),<br class="">+ /*IsInitial=*/true);<br class="">+}<br class="">+<br class="">+void DirectoryWatcherLinux::EventReceivingLoop() {<br class="">+ while (true) {<br class="">+ DirectoryWatcher::Event Event = this->Queue.pop_front_blocking();<br class="">+ this->Receiver(Event, false);<br class="">+ if (Event.Kind ==<br class="">+ DirectoryWatcher::Event::EventKind::WatcherGotInvalidated) {<br class="">+ StopWork();<br class="">+ return;<br class="">+ }<br class="">+ }<br class="">+}<br class="">+<br class="">+DirectoryWatcherLinux::DirectoryWatcherLinux(<br class="">+ StringRef WatchedDirPath,<br class="">+ std::function<void(llvm::ArrayRef<Event>, bool)> Receiver,<br class="">+ bool WaitForInitialSync, int InotifyFD, int InotifyWD,<br class="">+ SemaphorePipe &&InotifyPollingStopSignal)<br class="">+ : WatchedDirPath(WatchedDirPath), InotifyFD(InotifyFD),<br class="">+ InotifyWD(InotifyWD), Receiver(Receiver),<br class="">+ InotifyPollingStopSignal(std::move(InotifyPollingStopSignal)) {<br class="">+<br class="">+ InotifyPollingThread = std::thread([this]() { InotifyPollingLoop(); });<br class="">+ // We have no guarantees about thread safety of the Receiver which is being<br class="">+ // used in both InitialScan and EventReceivingLoop. We shouldn't run these<br class="">+ // only synchronously.<br class="">+ if (WaitForInitialSync) {<br class="">+ InitialScan();<br class="">+ EventsReceivingThread = std::thread([this]() { EventReceivingLoop(); });<br class="">+ } else {<br class="">+ EventsReceivingThread = std::thread([this]() {<br class="">+ // FIXME: We might want to terminate an async initial scan early in case<br class="">+ // of a failure in EventsReceivingThread.<br class="">+ InitialScan();<br class="">+ EventReceivingLoop();<br class="">+ });<br class="">+ }<br class="">+}<br class="">+<br class="">+} // namespace<br class="">+<br class="">+std::unique_ptr<DirectoryWatcher> clang::DirectoryWatcher::create(<br class="">+ StringRef Path,<br class="">+ std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)> Receiver,<br class="">+ bool WaitForInitialSync) {<br class="">+ if (Path.empty())<br class="">+ return nullptr;<br class="">+<br class="">+ const int InotifyFD = inotify_init1(IN_CLOEXEC);<br class="">+ if (InotifyFD == -1)<br class="">+ return nullptr;<br class="">+<br class="">+ const int InotifyWD = inotify_add_watch(<br class="">+ InotifyFD, Path.str().c_str(),<br class="">+ IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_EXCL_UNLINK | IN_MODIFY |<br class="">+ IN_MOVED_FROM | IN_MOVE_SELF | IN_MOVED_TO | IN_ONLYDIR | IN_IGNORED);<br class="">+ if (InotifyWD == -1)<br class="">+ return nullptr;<br class="">+<br class="">+ auto InotifyPollingStopper = SemaphorePipe::create();<br class="">+<br class="">+ if (!InotifyPollingStopper)<br class="">+ return nullptr;<br class="">+<br class="">+ return llvm::make_unique<DirectoryWatcherLinux>(<br class="">+ Path, Receiver, WaitForInitialSync, InotifyFD, InotifyWD,<br class="">+ std::move(*InotifyPollingStopper));<br class="">+}<br class="">\ No newline at end of file<br class=""><br class="">Added: cfe/trunk/lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp<br class="">URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp?rev=365954&view=auto" class="">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp?rev=365954&view=auto</a><br class="">==============================================================================<br class="">--- cfe/trunk/lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp (added)<br class="">+++ cfe/trunk/lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp Fri Jul 12 13:34:10 2019<br class="">@@ -0,0 +1,233 @@<br class="">+//===- DirectoryWatcher-mac.cpp - Mac-platform directory watching ---------===//<br class="">+//<br class="">+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.<br class="">+// See <a href="https://llvm.org/LICENSE.txt" class="">https://llvm.org/LICENSE.txt</a> for license information.<br class="">+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception<br class="">+//<br class="">+//===----------------------------------------------------------------------===//<br class="">+<br class="">+#include "DirectoryScanner.h"<br class="">+#include "clang/DirectoryWatcher/DirectoryWatcher.h"<br class="">+<br class="">+#include "llvm/ADT/STLExtras.h"<br class="">+#include "llvm/ADT/StringRef.h"<br class="">+#include "llvm/Support/Path.h"<br class="">+#include <CoreServices/CoreServices.h><br class="">+<br class="">+using namespace llvm;<br class="">+using namespace clang;<br class="">+<br class="">+static FSEventStreamRef createFSEventStream(<br class="">+ StringRef Path,<br class="">+ std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)>,<br class="">+ dispatch_queue_t);<br class="">+static void stopFSEventStream(FSEventStreamRef);<br class="">+<br class="">+namespace {<br class="">+<br class="">+class DirectoryWatcherMac : public clang::DirectoryWatcher {<br class="">+public:<br class="">+ DirectoryWatcherMac(<br class="">+ FSEventStreamRef EventStream,<br class="">+ std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)><br class="">+ Receiver,<br class="">+ llvm::StringRef WatchedDirPath)<br class="">+ : EventStream(EventStream), Receiver(Receiver),<br class="">+ WatchedDirPath(WatchedDirPath) {}<br class="">+<br class="">+ ~DirectoryWatcherMac() override {<br class="">+ stopFSEventStream(EventStream);<br class="">+ EventStream = nullptr;<br class="">+ // Now it's safe to use Receiver as the only other concurrent use would have<br class="">+ // been in EventStream processing.<br class="">+ Receiver(DirectoryWatcher::Event(<br class="">+ DirectoryWatcher::Event::EventKind::WatcherGotInvalidated, ""),<br class="">+ false);<br class="">+ }<br class="">+<br class="">+private:<br class="">+ FSEventStreamRef EventStream;<br class="">+ std::function<void(llvm::ArrayRef<Event>, bool)> Receiver;<br class="">+ const std::string WatchedDirPath;<br class="">+};<br class="">+<br class="">+struct EventStreamContextData {<br class="">+ std::string WatchedPath;<br class="">+ std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)> Receiver;<br class="">+<br class="">+ EventStreamContextData(<br class="">+ std::string &&WatchedPath,<br class="">+ std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)><br class="">+ Receiver)<br class="">+ : WatchedPath(std::move(WatchedPath)), Receiver(Receiver) {}<br class="">+<br class="">+ // Needed for FSEvents<br class="">+ static void dispose(const void *ctx) {<br class="">+ delete static_cast<const EventStreamContextData *>(ctx);<br class="">+ }<br class="">+};<br class="">+} // namespace<br class="">+<br class="">+constexpr const FSEventStreamEventFlags StreamInvalidatingFlags =<br class="">+ kFSEventStreamEventFlagUserDropped | kFSEventStreamEventFlagKernelDropped |<br class="">+ kFSEventStreamEventFlagMustScanSubDirs;<br class="">+<br class="">+constexpr const FSEventStreamEventFlags ModifyingFileEvents =<br class="">+ kFSEventStreamEventFlagItemCreated | kFSEventStreamEventFlagItemRenamed |<br class="">+ kFSEventStreamEventFlagItemModified;<br class="">+<br class="">+static void eventStreamCallback(ConstFSEventStreamRef Stream,<br class="">+ void *ClientCallBackInfo, size_t NumEvents,<br class="">+ void *EventPaths,<br class="">+ const FSEventStreamEventFlags EventFlags[],<br class="">+ const FSEventStreamEventId EventIds[]) {<br class="">+ auto *ctx = static_cast<EventStreamContextData *>(ClientCallBackInfo);<br class="">+<br class="">+ std::vector<DirectoryWatcher::Event> Events;<br class="">+ for (size_t i = 0; i < NumEvents; ++i) {<br class="">+ StringRef Path = ((const char **)EventPaths)[i];<br class="">+ const FSEventStreamEventFlags Flags = EventFlags[i];<br class="">+<br class="">+ if (Flags & StreamInvalidatingFlags) {<br class="">+ Events.emplace_back(DirectoryWatcher::Event{<br class="">+ DirectoryWatcher::Event::EventKind::WatcherGotInvalidated, ""});<br class="">+ break;<br class="">+ } else if (!(Flags & kFSEventStreamEventFlagItemIsFile)) {<br class="">+ // Subdirectories aren't supported - if some directory got removed it<br class="">+ // must've been the watched directory itself.<br class="">+ if ((Flags & kFSEventStreamEventFlagItemRemoved) &&<br class="">+ Path == ctx->WatchedPath) {<br class="">+ Events.emplace_back(DirectoryWatcher::Event{<br class="">+ DirectoryWatcher::Event::EventKind::WatchedDirRemoved, ""});<br class="">+ Events.emplace_back(DirectoryWatcher::Event{<br class="">+ DirectoryWatcher::Event::EventKind::WatcherGotInvalidated, ""});<br class="">+ break;<br class="">+ }<br class="">+ // No support for subdirectories - just ignore everything.<br class="">+ continue;<br class="">+ } else if (Flags & kFSEventStreamEventFlagItemRemoved) {<br class="">+ Events.emplace_back(DirectoryWatcher::Event::EventKind::Removed,<br class="">+ llvm::sys::path::filename(Path));<br class="">+ continue;<br class="">+ } else if (Flags & ModifyingFileEvents) {<br class="">+ if (!getFileStatus(Path).hasValue()) {<br class="">+ Events.emplace_back(DirectoryWatcher::Event::EventKind::Removed,<br class="">+ llvm::sys::path::filename(Path));<br class="">+ } else {<br class="">+ Events.emplace_back(DirectoryWatcher::Event::EventKind::Modified,<br class="">+ llvm::sys::path::filename(Path));<br class="">+ }<br class="">+ continue;<br class="">+ }<br class="">+<br class="">+ // default<br class="">+ Events.emplace_back(DirectoryWatcher::Event{<br class="">+ DirectoryWatcher::Event::EventKind::WatcherGotInvalidated, ""});<br class="">+ llvm_unreachable("Unknown FSEvent type.");<br class="">+ }<br class="">+<br class="">+ if (!Events.empty()) {<br class="">+ ctx->Receiver(Events, /*IsInitial=*/false);<br class="">+ }<br class="">+}<br class="">+<br class="">+FSEventStreamRef createFSEventStream(<br class="">+ StringRef Path,<br class="">+ std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)> Receiver,<br class="">+ dispatch_queue_t Queue) {<br class="">+ if (Path.empty())<br class="">+ return nullptr;<br class="">+<br class="">+ CFMutableArrayRef PathsToWatch = [&]() {<br class="">+ CFMutableArrayRef PathsToWatch =<br class="">+ CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks);<br class="">+ CFStringRef CfPathStr =<br class="">+ CFStringCreateWithBytes(nullptr, (const UInt8 *)Path.data(),<br class="">+ Path.size(), kCFStringEncodingUTF8, false);<br class="">+ CFArrayAppendValue(PathsToWatch, CfPathStr);<br class="">+ CFRelease(CfPathStr);<br class="">+ return PathsToWatch;<br class="">+ }();<br class="">+<br class="">+ FSEventStreamContext Context = [&]() {<br class="">+ std::string RealPath;<br class="">+ {<br class="">+ SmallString<128> Storage;<br class="">+ StringRef P = llvm::Twine(Path).toNullTerminatedStringRef(Storage);<br class="">+ char Buffer[PATH_MAX];<br class="">+ if (::realpath(P.begin(), Buffer) != nullptr)<br class="">+ RealPath = Buffer;<br class="">+ else<br class="">+ RealPath = Path;<br class="">+ }<br class="">+<br class="">+ FSEventStreamContext Context;<br class="">+ Context.version = 0;<br class="">+ <a href="http://Context.info" class="">Context.info</a> = new EventStreamContextData(std::move(RealPath), Receiver);<br class="">+ Context.retain = nullptr;<br class="">+ Context.release = EventStreamContextData::dispose;<br class="">+ Context.copyDescription = nullptr;<br class="">+ return Context;<br class="">+ }();<br class="">+<br class="">+ FSEventStreamRef Result = FSEventStreamCreate(<br class="">+ nullptr, eventStreamCallback, &Context, PathsToWatch,<br class="">+ kFSEventStreamEventIdSinceNow, /* latency in seconds */ 0.0,<br class="">+ kFSEventStreamCreateFlagFileEvents | kFSEventStreamCreateFlagNoDefer);<br class="">+ CFRelease(PathsToWatch);<br class="">+<br class="">+ return Result;<br class="">+}<br class="">+<br class="">+void stopFSEventStream(FSEventStreamRef EventStream) {<br class="">+ if (!EventStream)<br class="">+ return;<br class="">+ FSEventStreamStop(EventStream);<br class="">+ FSEventStreamInvalidate(EventStream);<br class="">+ FSEventStreamRelease(EventStream);<br class="">+}<br class="">+<br class="">+std::unique_ptr<DirectoryWatcher> clang::DirectoryWatcher::create(<br class="">+ StringRef Path,<br class="">+ std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)> Receiver,<br class="">+ bool WaitForInitialSync) {<br class="">+ dispatch_queue_t Queue =<br class="">+ dispatch_queue_create("DirectoryWatcher", DISPATCH_QUEUE_SERIAL);<br class="">+<br class="">+ if (Path.empty())<br class="">+ return nullptr;<br class="">+<br class="">+ auto EventStream = createFSEventStream(Path, Receiver, Queue);<br class="">+ if (!EventStream) {<br class="">+ return nullptr;<br class="">+ }<br class="">+<br class="">+ std::unique_ptr<DirectoryWatcher> Result =<br class="">+ llvm::make_unique<DirectoryWatcherMac>(EventStream, Receiver, Path);<br class="">+<br class="">+ // We need to copy the data so the lifetime is ok after a const copy is made<br class="">+ // for the block.<br class="">+ const std::string CopiedPath = Path;<br class="">+<br class="">+ auto InitWork = ^{<br class="">+ // We need to start watching the directory before we start scanning in order<br class="">+ // to not miss any event. By dispatching this on the same serial Queue as<br class="">+ // the FSEvents will be handled we manage to start watching BEFORE the<br class="">+ // inital scan and handling events ONLY AFTER the scan finishes.<br class="">+ FSEventStreamSetDispatchQueue(EventStream, Queue);<br class="">+ FSEventStreamStart(EventStream);<br class="">+ // We need to decrement the ref count for Queue as initialize() will return<br class="">+ // and FSEvents has incremented it. Since we have to wait for FSEvents to<br class="">+ // take ownership it's the easiest to do it here rather than main thread.<br class="">+ dispatch_release(Queue);<br class="">+ Receiver(getAsFileEvents(scanDirectory(CopiedPath)), /*IsInitial=*/true);<br class="">+ };<br class="">+<br class="">+ if (WaitForInitialSync) {<br class="">+ dispatch_sync(Queue, InitWork);<br class="">+ } else {<br class="">+ dispatch_async(Queue, InitWork);<br class="">+ }<br class="">+<br class="">+ return Result;<br class="">+}<br class=""><br class="">Modified: cfe/trunk/unittests/CMakeLists.txt<br class="">URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/CMakeLists.txt?rev=365954&r1=365953&r2=365954&view=diff" class="">http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/CMakeLists.txt?rev=365954&r1=365953&r2=365954&view=diff</a><br class="">==============================================================================<br class="">--- cfe/trunk/unittests/CMakeLists.txt (original)<br class="">+++ cfe/trunk/unittests/CMakeLists.txt Fri Jul 12 13:34:10 2019<br class="">@@ -30,6 +30,7 @@ add_subdirectory(CodeGen)<br class=""> if(NOT WIN32 AND CLANG_TOOL_LIBCLANG_BUILD)<br class=""> add_subdirectory(libclang)<br class=""> endif()<br class="">+add_subdirectory(DirectoryWatcher)<br class=""> add_subdirectory(Rename)<br class=""> add_subdirectory(Index)<br class=""> add_subdirectory(Serialization)<br class=""><br class="">Added: cfe/trunk/unittests/DirectoryWatcher/CMakeLists.txt<br class="">URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/DirectoryWatcher/CMakeLists.txt?rev=365954&view=auto" class="">http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/DirectoryWatcher/CMakeLists.txt?rev=365954&view=auto</a><br class="">==============================================================================<br class="">--- cfe/trunk/unittests/DirectoryWatcher/CMakeLists.txt (added)<br class="">+++ cfe/trunk/unittests/DirectoryWatcher/CMakeLists.txt Fri Jul 12 13:34:10 2019<br class="">@@ -0,0 +1,17 @@<br class="">+if(APPLE OR CMAKE_SYSTEM_NAME MATCHES "Linux")<br class="">+<br class="">+ set(LLVM_LINK_COMPONENTS<br class="">+ Support<br class="">+ )<br class="">+<br class="">+ add_clang_unittest(DirectoryWatcherTests<br class="">+ DirectoryWatcherTest.cpp<br class="">+ )<br class="">+<br class="">+ target_link_libraries(DirectoryWatcherTests<br class="">+ PRIVATE<br class="">+ clangDirectoryWatcher<br class="">+ clangBasic<br class="">+ )<br class="">+<br class="">+endif()<br class="">\ No newline at end of file<br class=""><br class="">Added: cfe/trunk/unittests/DirectoryWatcher/DirectoryWatcherTest.cpp<br class="">URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/DirectoryWatcher/DirectoryWatcherTest.cpp?rev=365954&view=auto" class="">http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/DirectoryWatcher/DirectoryWatcherTest.cpp?rev=365954&view=auto</a><br class="">==============================================================================<br class="">--- cfe/trunk/unittests/DirectoryWatcher/DirectoryWatcherTest.cpp (added)<br class="">+++ cfe/trunk/unittests/DirectoryWatcher/DirectoryWatcherTest.cpp Fri Jul 12 13:34:10 2019<br class="">@@ -0,0 +1,426 @@<br class="">+//===- unittests/DirectoryWatcher/DirectoryWatcherTest.cpp ----------------===//<br class="">+//<br class="">+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.<br class="">+// See <a href="https://llvm.org/LICENSE.txt" class="">https://llvm.org/LICENSE.txt</a> for license information.<br class="">+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception<br class="">+//<br class="">+//===----------------------------------------------------------------------===//<br class="">+<br class="">+#include "clang/DirectoryWatcher/DirectoryWatcher.h"<br class="">+#include "llvm/Support/FileSystem.h"<br class="">+#include "llvm/Support/Mutex.h"<br class="">+#include "llvm/Support/Path.h"<br class="">+#include "llvm/Support/raw_ostream.h"<br class="">+#include "gtest/gtest.h"<br class="">+#include <condition_variable><br class="">+#include <future><br class="">+#include <mutex><br class="">+#include <thread><br class="">+<br class="">+using namespace llvm;<br class="">+using namespace llvm::sys;<br class="">+using namespace llvm::sys::fs;<br class="">+using namespace clang;<br class="">+<br class="">+namespace clang {<br class="">+static bool operator==(const DirectoryWatcher::Event &lhs,<br class="">+ const DirectoryWatcher::Event &rhs) {<br class="">+ return lhs.Filename == rhs.Filename &&<br class="">+ static_cast<int>(lhs.Kind) == static_cast<int>(rhs.Kind);<br class="">+}<br class="">+} // namespace clang<br class="">+<br class="">+namespace {<br class="">+<br class="">+struct DirectoryWatcherTestFixture {<br class="">+ std::string TestRootDir;<br class="">+ std::string TestWatchedDir;<br class="">+<br class="">+ DirectoryWatcherTestFixture() {<br class="">+ SmallString<128> pathBuf;<br class="">+ std::error_code UniqDirRes = createUniqueDirectory("dirwatcher", pathBuf);<br class="">+ assert(!UniqDirRes);<br class="">+ TestRootDir = pathBuf.str();<br class="">+ path::append(pathBuf, "watch");<br class="">+ TestWatchedDir = pathBuf.str();<br class="">+ std::error_code CreateDirRes = create_directory(TestWatchedDir, false);<br class="">+ assert(!CreateDirRes);<br class="">+ }<br class="">+<br class="">+ ~DirectoryWatcherTestFixture() { remove_directories(TestRootDir); }<br class="">+<br class="">+ SmallString<128> getPathInWatched(const std::string &testFile) {<br class="">+ SmallString<128> pathBuf;<br class="">+ pathBuf = TestWatchedDir;<br class="">+ path::append(pathBuf, testFile);<br class="">+ return pathBuf;<br class="">+ }<br class="">+<br class="">+ void addFile(const std::string &testFile) {<br class="">+ Expected<file_t> ft = openNativeFileForWrite(getPathInWatched(testFile),<br class="">+ CD_CreateNew, OF_None);<br class="">+ if (ft) {<br class="">+ closeFile(*ft);<br class="">+ } else {<br class="">+ llvm::errs() << llvm::toString(ft.takeError()) << "\n";<br class="">+ llvm::errs() << getPathInWatched(testFile) << "\n";<br class="">+ llvm_unreachable("Couldn't create test file.");<br class="">+ }<br class="">+ }<br class="">+<br class="">+ void deleteFile(const std::string &testFile) {<br class="">+ std::error_code EC =<br class="">+ remove(getPathInWatched(testFile), /*IgnoreNonExisting=*/false);<br class="">+ ASSERT_FALSE(EC);<br class="">+ }<br class="">+};<br class="">+<br class="">+std::string eventKindToString(const DirectoryWatcher::Event::EventKind K) {<br class="">+ switch (K) {<br class="">+ case DirectoryWatcher::Event::EventKind::Removed:<br class="">+ return "Removed";<br class="">+ case DirectoryWatcher::Event::EventKind::Modified:<br class="">+ return "Modified";<br class="">+ case DirectoryWatcher::Event::EventKind::WatchedDirRemoved:<br class="">+ return "WatchedDirRemoved";<br class="">+ case DirectoryWatcher::Event::EventKind::WatcherGotInvalidated:<br class="">+ return "WatcherGotInvalidated";<br class="">+ }<br class="">+ llvm_unreachable("unknown event kind");<br class="">+}<br class="">+<br class="">+struct VerifyingConsumer {<br class="">+ std::vector<DirectoryWatcher::Event> ExpectedInitial;<br class="">+ std::vector<DirectoryWatcher::Event> ExpectedNonInitial;<br class="">+ std::vector<DirectoryWatcher::Event> OptionalNonInitial;<br class="">+ std::vector<DirectoryWatcher::Event> UnexpectedInitial;<br class="">+ std::vector<DirectoryWatcher::Event> UnexpectedNonInitial;<br class="">+ std::mutex Mtx;<br class="">+ std::condition_variable ResultIsReady;<br class="">+<br class="">+ VerifyingConsumer(<br class="">+ const std::vector<DirectoryWatcher::Event> &ExpectedInitial,<br class="">+ const std::vector<DirectoryWatcher::Event> &ExpectedNonInitial,<br class="">+ const std::vector<DirectoryWatcher::Event> &OptionalNonInitial = {})<br class="">+ : ExpectedInitial(ExpectedInitial),<br class="">+ ExpectedNonInitial(ExpectedNonInitial),<br class="">+ OptionalNonInitial(OptionalNonInitial) {}<br class="">+<br class="">+ // This method is used by DirectoryWatcher.<br class="">+ void consume(DirectoryWatcher::Event E, bool IsInitial) {<br class="">+ if (IsInitial)<br class="">+ consumeInitial(E);<br class="">+ else<br class="">+ consumeNonInitial(E);<br class="">+ }<br class="">+<br class="">+ void consumeInitial(DirectoryWatcher::Event E) {<br class="">+ std::unique_lock<std::mutex> L(Mtx);<br class="">+ auto It = std::find(ExpectedInitial.begin(), ExpectedInitial.end(), E);<br class="">+ if (It == ExpectedInitial.end()) {<br class="">+ UnexpectedInitial.push_back(E);<br class="">+ } else {<br class="">+ ExpectedInitial.erase(It);<br class="">+ }<br class="">+ if (result())<br class="">+ ResultIsReady.notify_one();<br class="">+ }<br class="">+<br class="">+ void consumeNonInitial(DirectoryWatcher::Event E) {<br class="">+ std::unique_lock<std::mutex> L(Mtx);<br class="">+ auto It =<br class="">+ std::find(ExpectedNonInitial.begin(), ExpectedNonInitial.end(), E);<br class="">+ if (It == ExpectedNonInitial.end()) {<br class="">+ auto OptIt =<br class="">+ std::find(OptionalNonInitial.begin(), OptionalNonInitial.end(), E);<br class="">+ if (OptIt != OptionalNonInitial.end()) {<br class="">+ OptionalNonInitial.erase(OptIt);<br class="">+ } else {<br class="">+ UnexpectedNonInitial.push_back(E);<br class="">+ }<br class="">+ } else {<br class="">+ ExpectedNonInitial.erase(It);<br class="">+ }<br class="">+ if (result())<br class="">+ ResultIsReady.notify_one();<br class="">+ }<br class="">+<br class="">+ // This method is used by DirectoryWatcher.<br class="">+ void consume(llvm::ArrayRef<DirectoryWatcher::Event> Es, bool IsInitial) {<br class="">+ for (const auto &E : Es)<br class="">+ consume(E, IsInitial);<br class="">+ }<br class="">+<br class="">+ // Not locking - caller has to lock Mtx.<br class="">+ llvm::Optional<bool> result() const {<br class="">+ if (ExpectedInitial.empty() && ExpectedNonInitial.empty() &&<br class="">+ UnexpectedInitial.empty() && UnexpectedNonInitial.empty())<br class="">+ return true;<br class="">+ if (!UnexpectedInitial.empty() || !UnexpectedNonInitial.empty())<br class="">+ return false;<br class="">+ return llvm::None;<br class="">+ }<br class="">+<br class="">+ // This method is used by tests.<br class="">+ // \returns true on success<br class="">+ bool blockUntilResult() {<br class="">+ std::unique_lock<std::mutex> L(Mtx);<br class="">+ while (true) {<br class="">+ if (result())<br class="">+ return *result();<br class="">+<br class="">+ ResultIsReady.wait(L, [this]() { return result().hasValue(); });<br class="">+ }<br class="">+ return false; // Just to make compiler happy.<br class="">+ }<br class="">+<br class="">+ void printUnmetExpectations(llvm::raw_ostream &OS) {<br class="">+ if (!ExpectedInitial.empty()) {<br class="">+ OS << "Expected but not seen initial events: \n";<br class="">+ for (const auto &E : ExpectedInitial) {<br class="">+ OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";<br class="">+ }<br class="">+ }<br class="">+ if (!ExpectedNonInitial.empty()) {<br class="">+ OS << "Expected but not seen non-initial events: \n";<br class="">+ for (const auto &E : ExpectedNonInitial) {<br class="">+ OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";<br class="">+ }<br class="">+ }<br class="">+ if (!UnexpectedInitial.empty()) {<br class="">+ OS << "Unexpected initial events seen: \n";<br class="">+ for (const auto &E : UnexpectedInitial) {<br class="">+ OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";<br class="">+ }<br class="">+ }<br class="">+ if (!UnexpectedNonInitial.empty()) {<br class="">+ OS << "Unexpected non-initial events seen: \n";<br class="">+ for (const auto &E : UnexpectedNonInitial) {<br class="">+ OS << eventKindToString(E.Kind) << " " << E.Filename << "\n";<br class="">+ }<br class="">+ }<br class="">+ }<br class="">+};<br class="">+<br class="">+void checkEventualResultWithTimeout(VerifyingConsumer &TestConsumer) {<br class="">+ std::packaged_task<int(void)> task(<br class="">+ [&TestConsumer]() { return TestConsumer.blockUntilResult(); });<br class="">+ std::future<int> WaitForExpectedStateResult = task.get_future();<br class="">+ std::thread worker(std::move(task));<br class="">+ worker.detach();<br class="">+<br class="">+ EXPECT_TRUE(WaitForExpectedStateResult.wait_for(std::chrono::seconds(3)) ==<br class="">+ std::future_status::ready)<br class="">+ << "The expected result state wasn't reached before the time-out.";<br class="">+ EXPECT_TRUE(TestConsumer.result().hasValue());<br class="">+ if (TestConsumer.result().hasValue()) {<br class="">+ EXPECT_TRUE(*TestConsumer.result());<br class="">+ }<br class="">+ if ((TestConsumer.result().hasValue() && !TestConsumer.result().getValue()) ||<br class="">+ !TestConsumer.result().hasValue())<br class="">+ TestConsumer.printUnmetExpectations(llvm::outs());<br class="">+}<br class="">+<br class="">+} // namespace<br class="">+<br class="">+TEST(DirectoryWatcherTest, InitialScanSync) {<br class="">+ DirectoryWatcherTestFixture fixture;<br class="">+<br class="">+ fixture.addFile("a");<br class="">+ fixture.addFile("b");<br class="">+ fixture.addFile("c");<br class="">+<br class="">+ VerifyingConsumer TestConsumer{<br class="">+ {{DirectoryWatcher::Event::EventKind::Modified, "a"},<br class="">+ {DirectoryWatcher::Event::EventKind::Modified, "b"},<br class="">+ {DirectoryWatcher::Event::EventKind::Modified, "c"}},<br class="">+ {}};<br class="">+<br class="">+ auto DW = DirectoryWatcher::create(<br class="">+ fixture.TestWatchedDir,<br class="">+ [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,<br class="">+ bool IsInitial) {<br class="">+ TestConsumer.consume(Events, IsInitial);<br class="">+ },<br class="">+ /*waitForInitialSync=*/true);<br class="">+<br class="">+ checkEventualResultWithTimeout(TestConsumer);<br class="">+}<br class="">+<br class="">+TEST(DirectoryWatcherTest, InitialScanAsync) {<br class="">+ DirectoryWatcherTestFixture fixture;<br class="">+<br class="">+ fixture.addFile("a");<br class="">+ fixture.addFile("b");<br class="">+ fixture.addFile("c");<br class="">+<br class="">+ VerifyingConsumer TestConsumer{<br class="">+ {{DirectoryWatcher::Event::EventKind::Modified, "a"},<br class="">+ {DirectoryWatcher::Event::EventKind::Modified, "b"},<br class="">+ {DirectoryWatcher::Event::EventKind::Modified, "c"}},<br class="">+ {}};<br class="">+<br class="">+ auto DW = DirectoryWatcher::create(<br class="">+ fixture.TestWatchedDir,<br class="">+ [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,<br class="">+ bool IsInitial) {<br class="">+ TestConsumer.consume(Events, IsInitial);<br class="">+ },<br class="">+ /*waitForInitialSync=*/false);<br class="">+<br class="">+ checkEventualResultWithTimeout(TestConsumer);<br class="">+}<br class="">+<br class="">+TEST(DirectoryWatcherTest, AddFiles) {<br class="">+ DirectoryWatcherTestFixture fixture;<br class="">+<br class="">+ VerifyingConsumer TestConsumer{<br class="">+ {},<br class="">+ {{DirectoryWatcher::Event::EventKind::Modified, "a"},<br class="">+ {DirectoryWatcher::Event::EventKind::Modified, "b"},<br class="">+ {DirectoryWatcher::Event::EventKind::Modified, "c"}}};<br class="">+<br class="">+ auto DW = DirectoryWatcher::create(<br class="">+ fixture.TestWatchedDir,<br class="">+ [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,<br class="">+ bool IsInitial) {<br class="">+ TestConsumer.consume(Events, IsInitial);<br class="">+ },<br class="">+ /*waitForInitialSync=*/true);<br class="">+<br class="">+ fixture.addFile("a");<br class="">+ fixture.addFile("b");<br class="">+ fixture.addFile("c");<br class="">+<br class="">+ checkEventualResultWithTimeout(TestConsumer);<br class="">+}<br class="">+<br class="">+TEST(DirectoryWatcherTest, ModifyFile) {<br class="">+ DirectoryWatcherTestFixture fixture;<br class="">+<br class="">+ fixture.addFile("a");<br class="">+<br class="">+ VerifyingConsumer TestConsumer{<br class="">+ {{DirectoryWatcher::Event::EventKind::Modified, "a"}},<br class="">+ {{DirectoryWatcher::Event::EventKind::Modified, "a"}}};<br class="">+<br class="">+ auto DW = DirectoryWatcher::create(<br class="">+ fixture.TestWatchedDir,<br class="">+ [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,<br class="">+ bool IsInitial) {<br class="">+ TestConsumer.consume(Events, IsInitial);<br class="">+ },<br class="">+ /*waitForInitialSync=*/true);<br class="">+<br class="">+ // modify the file<br class="">+ {<br class="">+ std::error_code error;<br class="">+ llvm::raw_fd_ostream bStream(fixture.getPathInWatched("a"), error,<br class="">+ CD_OpenExisting);<br class="">+ assert(!error);<br class="">+ bStream << "foo";<br class="">+ }<br class="">+<br class="">+ checkEventualResultWithTimeout(TestConsumer);<br class="">+}<br class="">+<br class="">+TEST(DirectoryWatcherTest, DeleteFile) {<br class="">+ DirectoryWatcherTestFixture fixture;<br class="">+<br class="">+ fixture.addFile("a");<br class="">+<br class="">+ VerifyingConsumer TestConsumer{<br class="">+ {{DirectoryWatcher::Event::EventKind::Modified, "a"}},<br class="">+ {{DirectoryWatcher::Event::EventKind::Removed, "a"}}};<br class="">+<br class="">+ auto DW = DirectoryWatcher::create(<br class="">+ fixture.TestWatchedDir,<br class="">+ [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,<br class="">+ bool IsInitial) {<br class="">+ TestConsumer.consume(Events, IsInitial);<br class="">+ },<br class="">+ /*waitForInitialSync=*/true);<br class="">+<br class="">+ fixture.deleteFile("a");<br class="">+<br class="">+ checkEventualResultWithTimeout(TestConsumer);<br class="">+}<br class="">+<br class="">+TEST(DirectoryWatcherTest, DeleteWatchedDir) {<br class="">+ DirectoryWatcherTestFixture fixture;<br class="">+<br class="">+ VerifyingConsumer TestConsumer{<br class="">+ {},<br class="">+ {{DirectoryWatcher::Event::EventKind::WatchedDirRemoved, ""},<br class="">+ {DirectoryWatcher::Event::EventKind::WatcherGotInvalidated, ""}}};<br class="">+<br class="">+ auto DW = DirectoryWatcher::create(<br class="">+ fixture.TestWatchedDir,<br class="">+ [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,<br class="">+ bool IsInitial) {<br class="">+ TestConsumer.consume(Events, IsInitial);<br class="">+ },<br class="">+ /*waitForInitialSync=*/true);<br class="">+<br class="">+ remove_directories(fixture.TestWatchedDir);<br class="">+<br class="">+ checkEventualResultWithTimeout(TestConsumer);<br class="">+}<br class="">+<br class="">+TEST(DirectoryWatcherTest, InvalidatedWatcher) {<br class="">+ DirectoryWatcherTestFixture fixture;<br class="">+<br class="">+ VerifyingConsumer TestConsumer{<br class="">+ {}, {{DirectoryWatcher::Event::EventKind::WatcherGotInvalidated, ""}}};<br class="">+<br class="">+ {<br class="">+ auto DW = DirectoryWatcher::create(<br class="">+ fixture.TestWatchedDir,<br class="">+ [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,<br class="">+ bool IsInitial) {<br class="">+ TestConsumer.consume(Events, IsInitial);<br class="">+ },<br class="">+ /*waitForInitialSync=*/true);<br class="">+ } // DW is destructed here.<br class="">+<br class="">+ checkEventualResultWithTimeout(TestConsumer);<br class="">+}<br class="">+<br class="">+TEST(DirectoryWatcherTest, ChangeMetadata) {<br class="">+ DirectoryWatcherTestFixture fixture;<br class="">+ fixture.addFile("a");<br class="">+<br class="">+ VerifyingConsumer TestConsumer{<br class="">+ {{DirectoryWatcher::Event::EventKind::Modified, "a"}},<br class="">+ // We don't expect any notification for file having access file changed.<br class="">+ {},<br class="">+ // Given the timing we are ok with receiving the duplicate event.<br class="">+ {{DirectoryWatcher::Event::EventKind::Modified, "a"}}};<br class="">+<br class="">+ auto DW = DirectoryWatcher::create(<br class="">+ fixture.TestWatchedDir,<br class="">+ [&TestConsumer](llvm::ArrayRef<DirectoryWatcher::Event> Events,<br class="">+ bool IsInitial) {<br class="">+ TestConsumer.consume(Events, IsInitial);<br class="">+ },<br class="">+ /*waitForInitialSync=*/true);<br class="">+<br class="">+ { // Change access and modification time of file a.<br class="">+ Expected<file_t> HopefullyTheFD = llvm::sys::fs::openNativeFileForWrite(<br class="">+ fixture.getPathInWatched("a"), CD_OpenExisting, OF_None);<br class="">+ if (!HopefullyTheFD) {<br class="">+ llvm::outs() << HopefullyTheFD.takeError();<br class="">+ }<br class="">+<br class="">+ const int FD = HopefullyTheFD.get();<br class="">+ const TimePoint<> NewTimePt =<br class="">+ std::chrono::system_clock::now() - std::chrono::minutes(1);<br class="">+<br class="">+ std::error_code setTimeRes =<br class="">+ llvm::sys::fs::setLastAccessAndModificationTime(FD, NewTimePt,<br class="">+ NewTimePt);<br class="">+ assert(!setTimeRes);<br class="">+ }<br class="">+<br class="">+ checkEventualResultWithTimeout(TestConsumer);<br class="">+}<br class=""><br class=""><br class="">_______________________________________________<br class="">cfe-commits mailing list<br class=""><a href="mailto:cfe-commits@lists.llvm.org" class="">cfe-commits@lists.llvm.org</a><br class="">https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits<br class=""><br class=""></blockquote></div></div></blockquote></div><br class=""></div></body></html>