<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>