Home | History | Annotate | Line # | Download | only in linux
      1 //===- DirectoryWatcher-linux.cpp - Linux-platform directory watching -----===//
      2 //
      3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4 // See https://llvm.org/LICENSE.txt for license information.
      5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 
      9 #include "DirectoryScanner.h"
     10 #include "clang/DirectoryWatcher/DirectoryWatcher.h"
     11 
     12 #include "llvm/ADT/STLExtras.h"
     13 #include "llvm/ADT/ScopeExit.h"
     14 #include "llvm/Support/AlignOf.h"
     15 #include "llvm/Support/Errno.h"
     16 #include "llvm/Support/Error.h"
     17 #include "llvm/Support/Path.h"
     18 #include <atomic>
     19 #include <condition_variable>
     20 #include <mutex>
     21 #include <queue>
     22 #include <string>
     23 #include <thread>
     24 #include <vector>
     25 
     26 #include <fcntl.h>
     27 #include <sys/epoll.h>
     28 #include <sys/inotify.h>
     29 #include <unistd.h>
     30 
     31 namespace {
     32 
     33 using namespace llvm;
     34 using namespace clang;
     35 
     36 /// Pipe for inter-thread synchronization - for epoll-ing on multiple
     37 /// conditions. It is meant for uni-directional 1:1 signalling - specifically:
     38 /// no multiple consumers, no data passing. Thread waiting for signal should
     39 /// poll the FDRead. Signalling thread should call signal() which writes single
     40 /// character to FDRead.
     41 struct SemaphorePipe {
     42   // Expects two file-descriptors opened as a pipe in the canonical POSIX
     43   // order: pipefd[0] refers to the read end of the pipe. pipefd[1] refers to
     44   // the write end of the pipe.
     45   SemaphorePipe(int pipefd[2])
     46       : FDRead(pipefd[0]), FDWrite(pipefd[1]), OwnsFDs(true) {}
     47   SemaphorePipe(const SemaphorePipe &) = delete;
     48   void operator=(const SemaphorePipe &) = delete;
     49   SemaphorePipe(SemaphorePipe &&other)
     50       : FDRead(other.FDRead), FDWrite(other.FDWrite),
     51         OwnsFDs(other.OwnsFDs) // Someone could have moved from the other
     52                                // instance before.
     53   {
     54     other.OwnsFDs = false;
     55   };
     56 
     57   void signal() {
     58 #ifndef NDEBUG
     59     ssize_t Result =
     60 #endif
     61     llvm::sys::RetryAfterSignal(-1, write, FDWrite, "A", 1);
     62     assert(Result != -1);
     63   }
     64   ~SemaphorePipe() {
     65     if (OwnsFDs) {
     66       close(FDWrite);
     67       close(FDRead);
     68     }
     69   }
     70   const int FDRead;
     71   const int FDWrite;
     72   bool OwnsFDs;
     73 
     74   static llvm::Optional<SemaphorePipe> create() {
     75     int InotifyPollingStopperFDs[2];
     76     if (pipe2(InotifyPollingStopperFDs, O_CLOEXEC) == -1)
     77       return llvm::None;
     78     return SemaphorePipe(InotifyPollingStopperFDs);
     79   }
     80 };
     81 
     82 /// Mutex-protected queue of Events.
     83 class EventQueue {
     84   std::mutex Mtx;
     85   std::condition_variable NonEmpty;
     86   std::queue<DirectoryWatcher::Event> Events;
     87 
     88 public:
     89   void push_back(const DirectoryWatcher::Event::EventKind K,
     90                  StringRef Filename) {
     91     {
     92       std::unique_lock<std::mutex> L(Mtx);
     93       Events.emplace(K, Filename);
     94     }
     95     NonEmpty.notify_one();
     96   }
     97 
     98   // Blocks on caller thread and uses codition_variable to wait until there's an
     99   // event to return.
    100   DirectoryWatcher::Event pop_front_blocking() {
    101     std::unique_lock<std::mutex> L(Mtx);
    102     while (true) {
    103       // Since we might have missed all the prior notifications on NonEmpty we
    104       // have to check the queue first (under lock).
    105       if (!Events.empty()) {
    106         DirectoryWatcher::Event Front = Events.front();
    107         Events.pop();
    108         return Front;
    109       }
    110       NonEmpty.wait(L, [this]() { return !Events.empty(); });
    111     }
    112   }
    113 };
    114 
    115 class DirectoryWatcherLinux : public clang::DirectoryWatcher {
    116 public:
    117   DirectoryWatcherLinux(
    118       llvm::StringRef WatchedDirPath,
    119       std::function<void(llvm::ArrayRef<Event>, bool)> Receiver,
    120       bool WaitForInitialSync, int InotifyFD, int InotifyWD,
    121       SemaphorePipe &&InotifyPollingStopSignal);
    122 
    123   ~DirectoryWatcherLinux() override {
    124     StopWork();
    125     InotifyPollingThread.join();
    126     EventsReceivingThread.join();
    127     inotify_rm_watch(InotifyFD, InotifyWD);
    128     llvm::sys::RetryAfterSignal(-1, close, InotifyFD);
    129   }
    130 
    131 private:
    132   const std::string WatchedDirPath;
    133   // inotify file descriptor
    134   int InotifyFD = -1;
    135   // inotify watch descriptor
    136   int InotifyWD = -1;
    137 
    138   EventQueue Queue;
    139 
    140   // Make sure lifetime of Receiver fully contains lifetime of
    141   // EventsReceivingThread.
    142   std::function<void(llvm::ArrayRef<Event>, bool)> Receiver;
    143 
    144   // Consumes inotify events and pushes directory watcher events to the Queue.
    145   void InotifyPollingLoop();
    146   std::thread InotifyPollingThread;
    147   // Using pipe so we can epoll two file descriptors at once - inotify and
    148   // stopping condition.
    149   SemaphorePipe InotifyPollingStopSignal;
    150 
    151   // Does the initial scan of the directory - directly calling Receiver,
    152   // bypassing the Queue. Both InitialScan and EventReceivingLoop use Receiver
    153   // which isn't necessarily thread-safe.
    154   void InitialScan();
    155 
    156   // Processing events from the Queue.
    157   // In case client doesn't want to do the initial scan synchronously
    158   // (WaitForInitialSync=false in ctor) we do the initial scan at the beginning
    159   // of this thread.
    160   std::thread EventsReceivingThread;
    161   // Push event of WatcherGotInvalidated kind to the Queue to stop the loop.
    162   // Both InitialScan and EventReceivingLoop use Receiver which isn't
    163   // necessarily thread-safe.
    164   void EventReceivingLoop();
    165 
    166   // Stops all the async work. Reentrant.
    167   void StopWork() {
    168     Queue.push_back(DirectoryWatcher::Event::EventKind::WatcherGotInvalidated,
    169                     "");
    170     InotifyPollingStopSignal.signal();
    171   }
    172 };
    173 
    174 void DirectoryWatcherLinux::InotifyPollingLoop() {
    175   // We want to be able to read ~30 events at once even in the worst case
    176   // (obscenely long filenames).
    177   constexpr size_t EventBufferLength =
    178       30 * (sizeof(struct inotify_event) + NAME_MAX + 1);
    179   // http://man7.org/linux/man-pages/man7/inotify.7.html
    180   // Some systems cannot read integer variables if they are not
    181   // properly aligned. On other systems, incorrect alignment may
    182   // decrease performance. Hence, the buffer used for reading from
    183   // the inotify file descriptor should have the same alignment as
    184   // struct inotify_event.
    185 
    186   struct Buffer {
    187     alignas(struct inotify_event) char buffer[EventBufferLength];
    188   };
    189   auto ManagedBuffer = std::make_unique<Buffer>();
    190   char *const Buf = ManagedBuffer->buffer;
    191 
    192   const int EpollFD = epoll_create1(EPOLL_CLOEXEC);
    193   if (EpollFD == -1) {
    194     StopWork();
    195     return;
    196   }
    197   auto EpollFDGuard = llvm::make_scope_exit([EpollFD]() { close(EpollFD); });
    198 
    199   struct epoll_event EventSpec;
    200   EventSpec.events = EPOLLIN;
    201   EventSpec.data.fd = InotifyFD;
    202   if (epoll_ctl(EpollFD, EPOLL_CTL_ADD, InotifyFD, &EventSpec) == -1) {
    203     StopWork();
    204     return;
    205   }
    206 
    207   EventSpec.data.fd = InotifyPollingStopSignal.FDRead;
    208   if (epoll_ctl(EpollFD, EPOLL_CTL_ADD, InotifyPollingStopSignal.FDRead,
    209                 &EventSpec) == -1) {
    210     StopWork();
    211     return;
    212   }
    213 
    214   std::array<struct epoll_event, 2> EpollEventBuffer;
    215 
    216   while (true) {
    217     const int EpollWaitResult = llvm::sys::RetryAfterSignal(
    218         -1, epoll_wait, EpollFD, EpollEventBuffer.data(),
    219         EpollEventBuffer.size(), /*timeout=*/-1 /*== infinity*/);
    220     if (EpollWaitResult == -1) {
    221       StopWork();
    222       return;
    223     }
    224 
    225     // Multiple epoll_events can be received for a single file descriptor per
    226     // epoll_wait call.
    227     for (int i = 0; i < EpollWaitResult; ++i) {
    228       if (EpollEventBuffer[i].data.fd == InotifyPollingStopSignal.FDRead) {
    229         StopWork();
    230         return;
    231       }
    232     }
    233 
    234     // epoll_wait() always return either error or >0 events. Since there was no
    235     // event for stopping, it must be an inotify event ready for reading.
    236     ssize_t NumRead = llvm::sys::RetryAfterSignal(-1, read, InotifyFD, Buf,
    237                                                   EventBufferLength);
    238     for (char *P = Buf; P < Buf + NumRead;) {
    239       if (P + sizeof(struct inotify_event) > Buf + NumRead) {
    240         StopWork();
    241         llvm_unreachable("an incomplete inotify_event was read");
    242         return;
    243       }
    244 
    245       struct inotify_event *Event = reinterpret_cast<struct inotify_event *>(P);
    246       P += sizeof(struct inotify_event) + Event->len;
    247 
    248       if (Event->mask & (IN_CREATE | IN_MODIFY | IN_MOVED_TO | IN_DELETE) &&
    249           Event->len <= 0) {
    250         StopWork();
    251         llvm_unreachable("expected a filename from inotify");
    252         return;
    253       }
    254 
    255       if (Event->mask & (IN_CREATE | IN_MOVED_TO | IN_MODIFY)) {
    256         Queue.push_back(DirectoryWatcher::Event::EventKind::Modified,
    257                         Event->name);
    258       } else if (Event->mask & (IN_DELETE | IN_MOVED_FROM)) {
    259         Queue.push_back(DirectoryWatcher::Event::EventKind::Removed,
    260                         Event->name);
    261       } else if (Event->mask & (IN_DELETE_SELF | IN_MOVE_SELF)) {
    262         Queue.push_back(DirectoryWatcher::Event::EventKind::WatchedDirRemoved,
    263                         "");
    264         StopWork();
    265         return;
    266       } else if (Event->mask & IN_IGNORED) {
    267         StopWork();
    268         return;
    269       } else {
    270         StopWork();
    271         llvm_unreachable("Unknown event type.");
    272         return;
    273       }
    274     }
    275   }
    276 }
    277 
    278 void DirectoryWatcherLinux::InitialScan() {
    279   this->Receiver(getAsFileEvents(scanDirectory(WatchedDirPath)),
    280                  /*IsInitial=*/true);
    281 }
    282 
    283 void DirectoryWatcherLinux::EventReceivingLoop() {
    284   while (true) {
    285     DirectoryWatcher::Event Event = this->Queue.pop_front_blocking();
    286     this->Receiver(Event, false);
    287     if (Event.Kind ==
    288         DirectoryWatcher::Event::EventKind::WatcherGotInvalidated) {
    289       StopWork();
    290       return;
    291     }
    292   }
    293 }
    294 
    295 DirectoryWatcherLinux::DirectoryWatcherLinux(
    296     StringRef WatchedDirPath,
    297     std::function<void(llvm::ArrayRef<Event>, bool)> Receiver,
    298     bool WaitForInitialSync, int InotifyFD, int InotifyWD,
    299     SemaphorePipe &&InotifyPollingStopSignal)
    300     : WatchedDirPath(WatchedDirPath), InotifyFD(InotifyFD),
    301       InotifyWD(InotifyWD), Receiver(Receiver),
    302       InotifyPollingStopSignal(std::move(InotifyPollingStopSignal)) {
    303 
    304   InotifyPollingThread = std::thread([this]() { InotifyPollingLoop(); });
    305   // We have no guarantees about thread safety of the Receiver which is being
    306   // used in both InitialScan and EventReceivingLoop. We shouldn't run these
    307   // only synchronously.
    308   if (WaitForInitialSync) {
    309     InitialScan();
    310     EventsReceivingThread = std::thread([this]() { EventReceivingLoop(); });
    311   } else {
    312     EventsReceivingThread = std::thread([this]() {
    313       // FIXME: We might want to terminate an async initial scan early in case
    314       // of a failure in EventsReceivingThread.
    315       InitialScan();
    316       EventReceivingLoop();
    317     });
    318   }
    319 }
    320 
    321 } // namespace
    322 
    323 llvm::Expected<std::unique_ptr<DirectoryWatcher>> clang::DirectoryWatcher::create(
    324     StringRef Path,
    325     std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)> Receiver,
    326     bool WaitForInitialSync) {
    327   if (Path.empty())
    328     llvm::report_fatal_error(
    329         "DirectoryWatcher::create can not accept an empty Path.");
    330 
    331   const int InotifyFD = inotify_init1(IN_CLOEXEC);
    332   if (InotifyFD == -1)
    333     return llvm::make_error<llvm::StringError>(
    334         std::string("inotify_init1() error: ") + strerror(errno),
    335         llvm::inconvertibleErrorCode());
    336 
    337   const int InotifyWD = inotify_add_watch(
    338       InotifyFD, Path.str().c_str(),
    339       IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY |
    340       IN_MOVED_FROM | IN_MOVE_SELF | IN_MOVED_TO | IN_ONLYDIR | IN_IGNORED
    341 #ifdef IN_EXCL_UNLINK
    342       | IN_EXCL_UNLINK
    343 #endif
    344       );
    345   if (InotifyWD == -1)
    346     return llvm::make_error<llvm::StringError>(
    347         std::string("inotify_add_watch() error: ") + strerror(errno),
    348         llvm::inconvertibleErrorCode());
    349 
    350   auto InotifyPollingStopper = SemaphorePipe::create();
    351 
    352   if (!InotifyPollingStopper)
    353     return llvm::make_error<llvm::StringError>(
    354         std::string("SemaphorePipe::create() error: ") + strerror(errno),
    355         llvm::inconvertibleErrorCode());
    356 
    357   return std::make_unique<DirectoryWatcherLinux>(
    358       Path, Receiver, WaitForInitialSync, InotifyFD, InotifyWD,
    359       std::move(*InotifyPollingStopper));
    360 }
    361