]> granicus.if.org Git - clang/commitdiff
[clang][DirectoryWatcher] Adding llvm::Expected error handling to create.
authorPuyan Lotfi <puyan@puyan.org>
Tue, 6 Aug 2019 05:12:23 +0000 (05:12 +0000)
committerPuyan Lotfi <puyan@puyan.org>
Tue, 6 Aug 2019 05:12:23 +0000 (05:12 +0000)
Prior to this patch Unix style errno error reporting from the inotify layer was
used by DirectoryWatcher::create to simply return a nullptr on error. This
would generally be ok, except that in LLVM we have much more robust error
reporting through the facilities of llvm::Expected.

The other critical thing I stumbled across was that the unit tests for
DirectoryWatcher were not failing abruptly when inotify_init() was reporting an
error, but would continue with the testing and eventually hit a deadlock in a
pathological machine state (ie in the unit test, the return nullptr on ::create
was ignored).

Generally this pathological state never happens on any build bot, so it is
totally understandable that it was overlooked, but on a Linux desktop running
a dubious desktop environment (which I will not name) there is a chance that
said desktop environment could use up enough inotify instances to exceed the
user's limit. These are the conditions that led me to hit the deadlock I am
addressing in this patch with more robust error handling.

With the new llvm::Expected error handling when your system runs out of inotify
instances for your user, the unit test will be forced to handle the error or
crash and report the issue to the user instead of weirdly deadlocking on a
condition variable wait.

Differential Revision: https://reviews.llvm.org/D65704

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@367979 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/DirectoryWatcher/DirectoryWatcher.h
lib/DirectoryWatcher/default/DirectoryWatcher-not-implemented.cpp
lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp
lib/DirectoryWatcher/mac/DirectoryWatcher-mac.cpp
unittests/DirectoryWatcher/DirectoryWatcherTest.cpp

index e74443e0bc81c80943c020a289f622da48fdf850..d2a1662163992f6b5965abe41d66975bc1797ddf 100644 (file)
@@ -11,6 +11,7 @@
 
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
 #include <functional>
 #include <memory>
 #include <string>
@@ -98,10 +99,11 @@ public:
         : Kind(Kind), Filename(Filename) {}
   };
 
-  /// Returns nullptr if \param Path doesn't exist or isn't a directory.
-  /// Returns nullptr if OS kernel API told us we can't start watching. In such
-  /// case it's unclear whether just retrying has any chance to succeeed.
-  static std::unique_ptr<DirectoryWatcher>
+  /// Asserts if \param Path doesn't exist or isn't a directory.
+  /// Returns llvm::Expected Error if OS kernel API told us we can't start
+  /// watching. In such case it's unclear whether just retrying has any chance
+  /// to succeeed.
+  static llvm::Expected<std::unique_ptr<DirectoryWatcher>>
   create(llvm::StringRef Path,
          std::function<void(llvm::ArrayRef<DirectoryWatcher::Event> Events,
                             bool IsInitial)>
index e330ff06f504daa01c9315418199bcaa9a55cc54..200e540624a6f1ba84bea1ffe90755b146e82231 100644 (file)
 using namespace llvm;
 using namespace clang;
 
-std::unique_ptr<DirectoryWatcher> clang::DirectoryWatcher::create(
+llvm::Expected<std::unique_ptr<DirectoryWatcher>> clang::DirectoryWatcher::create(
     StringRef Path,
     std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)> Receiver,
     bool WaitForInitialSync) {
-  return nullptr;
+  return llvm::make_error<llvm::StringError>(
+      "DirectoryWatcher is not implemented for this platform!",
+      llvm::inconvertibleErrorCode());
 }
\ No newline at end of file
index 6998efbb5e84a172f4303fab851d0766208ed1cb..8a5e76c521b24ea22b658462f4a5a13c60ba8670 100644 (file)
@@ -13,6 +13,7 @@
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/Support/AlignOf.h"
 #include "llvm/Support/Errno.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/Mutex.h"
 #include "llvm/Support/Path.h"
 #include <atomic>
@@ -320,16 +321,17 @@ DirectoryWatcherLinux::DirectoryWatcherLinux(
 
 } // namespace
 
-std::unique_ptr<DirectoryWatcher> clang::DirectoryWatcher::create(
+llvm::Expected<std::unique_ptr<DirectoryWatcher>> clang::DirectoryWatcher::create(
     StringRef Path,
     std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)> Receiver,
     bool WaitForInitialSync) {
-  if (Path.empty())
-    return nullptr;
+  assert(!Path.empty() && "Path.empty()");
 
   const int InotifyFD = inotify_init1(IN_CLOEXEC);
   if (InotifyFD == -1)
-    return nullptr;
+    return llvm::make_error<llvm::StringError>(
+        std::string("inotify_init1() error: ") + strerror(errno),
+        llvm::inconvertibleErrorCode());
 
   const int InotifyWD = inotify_add_watch(
       InotifyFD, Path.str().c_str(),
@@ -340,12 +342,16 @@ std::unique_ptr<DirectoryWatcher> clang::DirectoryWatcher::create(
 #endif
       );
   if (InotifyWD == -1)
-    return nullptr;
+    return llvm::make_error<llvm::StringError>(
+        std::string("inotify_add_watch() error: ") + strerror(errno),
+        llvm::inconvertibleErrorCode());
 
   auto InotifyPollingStopper = SemaphorePipe::create();
 
   if (!InotifyPollingStopper)
-    return nullptr;
+    return llvm::make_error<llvm::StringError>(
+        std::string("SemaphorePipe::create() error: ") + strerror(errno),
+        llvm::inconvertibleErrorCode());
 
   return llvm::make_unique<DirectoryWatcherLinux>(
       Path, Receiver, WaitForInitialSync, InotifyFD, InotifyWD,
index ae3cb6141636b4128d1348a743d647c19f7719b1..ade03c2bbc9314bb04729ce37e7002aefdff6a2c 100644 (file)
 
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
 #include "llvm/Support/Path.h"
 #include <CoreServices/CoreServices.h>
 
 using namespace llvm;
 using namespace clang;
 
-static FSEventStreamRef createFSEventStream(
-    StringRef Path,
-    std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)>,
-    dispatch_queue_t);
 static void stopFSEventStream(FSEventStreamRef);
 
 namespace {
@@ -153,8 +150,7 @@ FSEventStreamRef createFSEventStream(
     StringRef Path,
     std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)> Receiver,
     dispatch_queue_t Queue) {
-  if (Path.empty())
-    return nullptr;
+  assert(!Path.empty() && "Path.empty()");
 
   CFMutableArrayRef PathsToWatch = [&]() {
     CFMutableArrayRef PathsToWatch =
@@ -205,20 +201,16 @@ void stopFSEventStream(FSEventStreamRef EventStream) {
   FSEventStreamRelease(EventStream);
 }
 
-std::unique_ptr<DirectoryWatcher> clang::DirectoryWatcher::create(
+llvm::Expected<std::unique_ptr<DirectoryWatcher>> clang::DirectoryWatcher::create(
     StringRef Path,
     std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)> Receiver,
     bool WaitForInitialSync) {
   dispatch_queue_t Queue =
       dispatch_queue_create("DirectoryWatcher", DISPATCH_QUEUE_SERIAL);
 
-  if (Path.empty())
-    return nullptr;
-
+  assert(!Path.empty() && "Path.empty()");
   auto EventStream = createFSEventStream(Path, Receiver, Queue);
-  if (!EventStream) {
-    return nullptr;
-  }
+  assert(EventStream && "EventStream expected to be non-null.")
 
   std::unique_ptr<DirectoryWatcher> Result =
       llvm::make_unique<DirectoryWatcherMac>(EventStream, Receiver, Path);
index 52a69616057a76b6ff30e5bd526baa69b0bc8952..14ef346032769a9c4a66dde9fe8c131b9883a134 100644 (file)
@@ -284,6 +284,7 @@ TEST(DirectoryWatcherTest, InitialScanSync) {
         TestConsumer.consume(Events, IsInitial);
       },
       /*waitForInitialSync=*/true);
+  if (!DW) return;
 
   checkEventualResultWithTimeout(TestConsumer);
 }
@@ -315,6 +316,7 @@ TEST(DirectoryWatcherTest, InitialScanAsync) {
         TestConsumer.consume(Events, IsInitial);
       },
       /*waitForInitialSync=*/false);
+  if (!DW) return;
 
   checkEventualResultWithTimeout(TestConsumer);
 }
@@ -335,6 +337,7 @@ TEST(DirectoryWatcherTest, AddFiles) {
         TestConsumer.consume(Events, IsInitial);
       },
       /*waitForInitialSync=*/true);
+  if (!DW) return;
 
   fixture.addFile("a");
   fixture.addFile("b");
@@ -360,6 +363,7 @@ TEST(DirectoryWatcherTest, ModifyFile) {
         TestConsumer.consume(Events, IsInitial);
       },
       /*waitForInitialSync=*/true);
+  if (!DW) return;
 
   // modify the file
   {
@@ -390,6 +394,7 @@ TEST(DirectoryWatcherTest, DeleteFile) {
         TestConsumer.consume(Events, IsInitial);
       },
       /*waitForInitialSync=*/true);
+  if (!DW) return;
 
   fixture.deleteFile("a");
 
@@ -411,6 +416,7 @@ TEST(DirectoryWatcherTest, DeleteWatchedDir) {
         TestConsumer.consume(Events, IsInitial);
       },
       /*waitForInitialSync=*/true);
+  if (!DW) return;
 
   remove_directories(fixture.TestWatchedDir);
 
@@ -431,6 +437,7 @@ TEST(DirectoryWatcherTest, InvalidatedWatcher) {
           TestConsumer.consume(Events, IsInitial);
         },
         /*waitForInitialSync=*/true);
+    if (!DW) return;
   } // DW is destructed here.
 
   checkEventualResultWithTimeout(TestConsumer);