diff options
author | Reid Kleckner <rnk@google.com> | 2019-07-09 23:22:01 +0000 |
---|---|---|
committer | Reid Kleckner <rnk@google.com> | 2019-07-09 23:22:01 +0000 |
commit | abce8c457dd3de6b156756e547cc0eefb7653c79 (patch) | |
tree | 4e876876539fa08be4d6a6c1f72258e57a44a126 /clang/lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp | |
parent | 1a697aa60722d71dfb7d6bd4d53fc361a6177fcc (diff) | |
download | bcm5719-llvm-abce8c457dd3de6b156756e547cc0eefb7653c79.tar.gz bcm5719-llvm-abce8c457dd3de6b156756e547cc0eefb7653c79.zip |
Revert [clang] DirectoryWatcher
This reverts r365574 (git commit 31babea94a3ed38a140540f2252cf043dacec1f7)
llvm-svn: 365581
Diffstat (limited to 'clang/lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp')
-rw-r--r-- | clang/lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp | 345 |
1 files changed, 0 insertions, 345 deletions
diff --git a/clang/lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp b/clang/lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp deleted file mode 100644 index 986ebc5d95f..00000000000 --- a/clang/lib/DirectoryWatcher/linux/DirectoryWatcher-linux.cpp +++ /dev/null @@ -1,345 +0,0 @@ -//===- DirectoryWatcher-linux.cpp - Linux-platform directory watching -----===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "DirectoryScanner.h" -#include "clang/DirectoryWatcher/DirectoryWatcher.h" - -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/ScopeExit.h" -#include "llvm/Support/AlignOf.h" -#include "llvm/Support/Errno.h" -#include "llvm/Support/Mutex.h" -#include "llvm/Support/Path.h" -#include <atomic> -#include <condition_variable> -#include <mutex> -#include <queue> -#include <string> -#include <thread> -#include <vector> - -#include <fcntl.h> -#include <sys/epoll.h> -#include <sys/inotify.h> -#include <unistd.h> - -namespace { - -using namespace llvm; -using namespace clang; - -/// Pipe for inter-thread synchronization - for epoll-ing on multiple -/// conditions. It is meant for uni-directional 1:1 signalling - specifically: -/// no multiple consumers, no data passing. Thread waiting for signal should -/// poll the FDRead. Signalling thread should call signal() which writes single -/// character to FDRead. -struct SemaphorePipe { - // Expects two file-descriptors opened as a pipe in the canonical POSIX - // order: pipefd[0] refers to the read end of the pipe. pipefd[1] refers to - // the write end of the pipe. - SemaphorePipe(int pipefd[2]) - : FDRead(pipefd[0]), FDWrite(pipefd[1]), OwnsFDs(true) {} - SemaphorePipe(const SemaphorePipe &) = delete; - void operator=(const SemaphorePipe &) = delete; - SemaphorePipe(SemaphorePipe &&other) - : FDRead(other.FDRead), FDWrite(other.FDWrite), - OwnsFDs(other.OwnsFDs) // Someone could have moved from the other - // instance before. - { - other.OwnsFDs = false; - }; - - void signal() { - ssize_t Result = llvm::sys::RetryAfterSignal(-1, write, FDWrite, "A", 1); - assert(Result != -1); - } - ~SemaphorePipe() { - if (OwnsFDs) { - close(FDWrite); - close(FDRead); - } - } - const int FDRead; - const int FDWrite; - bool OwnsFDs; - - static llvm::Optional<SemaphorePipe> create() { - int InotifyPollingStopperFDs[2]; - if (pipe2(InotifyPollingStopperFDs, O_CLOEXEC) == -1) - return llvm::None; - return SemaphorePipe(InotifyPollingStopperFDs); - } -}; - -/// Mutex-protected queue of Events. -class EventQueue { - std::mutex Mtx; - std::condition_variable NonEmpty; - std::queue<DirectoryWatcher::Event> Events; - -public: - void push_back(const DirectoryWatcher::Event::EventKind K, - StringRef Filename) { - { - std::unique_lock<std::mutex> L(Mtx); - Events.emplace(K, Filename); - } - NonEmpty.notify_one(); - } - - // Blocks on caller thread and uses codition_variable to wait until there's an - // event to return. - DirectoryWatcher::Event pop_front_blocking() { - std::unique_lock<std::mutex> L(Mtx); - while (true) { - // Since we might have missed all the prior notifications on NonEmpty we - // have to check the queue first (under lock). - if (!Events.empty()) { - DirectoryWatcher::Event Front = Events.front(); - Events.pop(); - return Front; - } - NonEmpty.wait(L, [this]() { return !Events.empty(); }); - } - } -}; - -class DirectoryWatcherLinux : public clang::DirectoryWatcher { -public: - DirectoryWatcherLinux( - llvm::StringRef WatchedDirPath, - std::function<void(llvm::ArrayRef<Event>, bool)> Receiver, - bool WaitForInitialSync, int InotifyFD, int InotifyWD, - SemaphorePipe &&InotifyPollingStopSignal); - - ~DirectoryWatcherLinux() override { - StopWork(); - InotifyPollingThread.join(); - EventsReceivingThread.join(); - inotify_rm_watch(InotifyFD, InotifyWD); - llvm::sys::RetryAfterSignal(-1, close, InotifyFD); - } - -private: - const std::string WatchedDirPath; - // inotify file descriptor - int InotifyFD = -1; - // inotify watch descriptor - int InotifyWD = -1; - - EventQueue Queue; - - // Make sure lifetime of Receiver fully contains lifetime of - // EventsReceivingThread. - std::function<void(llvm::ArrayRef<Event>, bool)> Receiver; - - // Consumes inotify events and pushes directory watcher events to the Queue. - void InotifyPollingLoop(); - std::thread InotifyPollingThread; - // Using pipe so we can epoll two file descriptors at once - inotify and - // stopping condition. - SemaphorePipe InotifyPollingStopSignal; - - // Does the initial scan of the directory - directly calling Receiver, - // bypassing the Queue. Both InitialScan and EventReceivingLoop use Receiver - // which isn't necessarily thread-safe. - void InitialScan(); - - // Processing events from the Queue. - // In case client doesn't want to do the initial scan synchronously - // (WaitForInitialSync=false in ctor) we do the initial scan at the beginning - // of this thread. - std::thread EventsReceivingThread; - // Push event of WatcherGotInvalidated kind to the Queue to stop the loop. - // Both InitialScan and EventReceivingLoop use Receiver which isn't - // necessarily thread-safe. - void EventReceivingLoop(); - - // Stops all the async work. Reentrant. - void StopWork() { - Queue.push_back(DirectoryWatcher::Event::EventKind::WatcherGotInvalidated, - ""); - InotifyPollingStopSignal.signal(); - } -}; - -void DirectoryWatcherLinux::InotifyPollingLoop() { - // We want to be able to read ~30 events at once even in the worst case - // (obscenely long filenames). - constexpr size_t EventBufferLength = - 30 * (sizeof(struct inotify_event) + NAME_MAX + 1); - // http://man7.org/linux/man-pages/man7/inotify.7.html - // Some systems cannot read integer variables if they are not - // properly aligned. On other systems, incorrect alignment may - // decrease performance. Hence, the buffer used for reading from - // the inotify file descriptor should have the same alignment as - // struct inotify_event. - - auto ManagedBuffer = - llvm::make_unique<llvm::AlignedCharArray<alignof(struct inotify_event), - EventBufferLength>>(); - char *const Buf = ManagedBuffer->buffer; - - const int EpollFD = epoll_create1(EPOLL_CLOEXEC); - if (EpollFD == -1) { - StopWork(); - return; - } - auto EpollFDGuard = llvm::make_scope_exit([EpollFD]() { close(EpollFD); }); - - struct epoll_event EventSpec; - EventSpec.events = EPOLLIN; - EventSpec.data.fd = InotifyFD; - if (epoll_ctl(EpollFD, EPOLL_CTL_ADD, InotifyFD, &EventSpec) == -1) { - StopWork(); - return; - } - - EventSpec.data.fd = InotifyPollingStopSignal.FDRead; - if (epoll_ctl(EpollFD, EPOLL_CTL_ADD, InotifyPollingStopSignal.FDRead, - &EventSpec) == -1) { - StopWork(); - return; - } - - std::array<struct epoll_event, 2> EpollEventBuffer; - - while (true) { - const int EpollWaitResult = llvm::sys::RetryAfterSignal( - -1, epoll_wait, EpollFD, EpollEventBuffer.data(), - EpollEventBuffer.size(), /*timeout=*/-1 /*== infinity*/); - if (EpollWaitResult == -1) { - StopWork(); - return; - } - - // Multiple epoll_events can be received for a single file descriptor per - // epoll_wait call. - for (const auto &EpollEvent : EpollEventBuffer) { - if (EpollEvent.data.fd == InotifyPollingStopSignal.FDRead) { - StopWork(); - return; - } - } - - // epoll_wait() always return either error or >0 events. Since there was no - // event for stopping, it must be an inotify event ready for reading. - ssize_t NumRead = llvm::sys::RetryAfterSignal(-1, read, InotifyFD, Buf, - EventBufferLength); - for (char *P = Buf; P < Buf + NumRead;) { - if (P + sizeof(struct inotify_event) > Buf + NumRead) { - StopWork(); - llvm_unreachable("an incomplete inotify_event was read"); - return; - } - - struct inotify_event *Event = reinterpret_cast<struct inotify_event *>(P); - P += sizeof(struct inotify_event) + Event->len; - - if (Event->mask & (IN_CREATE | IN_MODIFY | IN_MOVED_TO | IN_DELETE) && - Event->len <= 0) { - StopWork(); - llvm_unreachable("expected a filename from inotify"); - return; - } - - if (Event->mask & (IN_CREATE | IN_MOVED_TO | IN_MODIFY)) { - Queue.push_back(DirectoryWatcher::Event::EventKind::Modified, - Event->name); - } else if (Event->mask & (IN_DELETE | IN_MOVED_FROM)) { - Queue.push_back(DirectoryWatcher::Event::EventKind::Removed, - Event->name); - } else if (Event->mask & (IN_DELETE_SELF | IN_MOVE_SELF)) { - Queue.push_back(DirectoryWatcher::Event::EventKind::WatchedDirRemoved, - ""); - StopWork(); - return; - } else if (Event->mask & IN_IGNORED) { - StopWork(); - return; - } else { - StopWork(); - llvm_unreachable("Unknown event type."); - return; - } - } - } -} - -void DirectoryWatcherLinux::InitialScan() { - this->Receiver(getAsFileEvents(scanDirectory(WatchedDirPath)), - /*IsInitial=*/true); -} - -void DirectoryWatcherLinux::EventReceivingLoop() { - while (true) { - DirectoryWatcher::Event Event = this->Queue.pop_front_blocking(); - this->Receiver(Event, false); - if (Event.Kind == - DirectoryWatcher::Event::EventKind::WatcherGotInvalidated) { - StopWork(); - return; - } - } -} - -DirectoryWatcherLinux::DirectoryWatcherLinux( - StringRef WatchedDirPath, - std::function<void(llvm::ArrayRef<Event>, bool)> Receiver, - bool WaitForInitialSync, int InotifyFD, int InotifyWD, - SemaphorePipe &&InotifyPollingStopSignal) - : WatchedDirPath(WatchedDirPath), InotifyFD(InotifyFD), - InotifyWD(InotifyWD), Receiver(Receiver), - InotifyPollingStopSignal(std::move(InotifyPollingStopSignal)) { - - InotifyPollingThread = std::thread([this]() { InotifyPollingLoop(); }); - // We have no guarantees about thread safety of the Receiver which is being - // used in both InitialScan and EventReceivingLoop. We shouldn't run these - // only synchronously. - if (WaitForInitialSync) { - InitialScan(); - EventsReceivingThread = std::thread([this]() { EventReceivingLoop(); }); - } else { - EventsReceivingThread = std::thread([this]() { - // FIXME: We might want to terminate an async initial scan early in case - // of a failure in EventsReceivingThread. - InitialScan(); - EventReceivingLoop(); - }); - } -} - -} // namespace - -std::unique_ptr<DirectoryWatcher> clang::DirectoryWatcher::create( - StringRef Path, - std::function<void(llvm::ArrayRef<DirectoryWatcher::Event>, bool)> Receiver, - bool WaitForInitialSync) { - if (Path.empty()) - return nullptr; - - const int InotifyFD = inotify_init1(IN_CLOEXEC); - if (InotifyFD == -1) - return nullptr; - - const int InotifyWD = inotify_add_watch( - InotifyFD, Path.str().c_str(), - IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_EXCL_UNLINK | IN_MODIFY | - IN_MOVED_FROM | IN_MOVE_SELF | IN_MOVED_TO | IN_ONLYDIR | IN_IGNORED); - if (InotifyWD == -1) - return nullptr; - - auto InotifyPollingStopper = SemaphorePipe::create(); - - if (!InotifyPollingStopper) - return nullptr; - - return llvm::make_unique<DirectoryWatcherLinux>( - Path, Receiver, WaitForInitialSync, InotifyFD, InotifyWD, - std::move(*InotifyPollingStopper)); -}
\ No newline at end of file |