DirectoryWatcher.h revision 360784
1//===- DirectoryWatcher.h - Listens for directory file changes --*- C++ -*-===//
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#ifndef LLVM_CLANG_DIRECTORYWATCHER_DIRECTORYWATCHER_H
10#define LLVM_CLANG_DIRECTORYWATCHER_DIRECTORYWATCHER_H
11
12#include "llvm/ADT/ArrayRef.h"
13#include "llvm/ADT/StringRef.h"
14#include "llvm/Support/Error.h"
15#include <functional>
16#include <memory>
17#include <string>
18
19namespace clang {
20/// Provides notifications for file changes in a directory.
21///
22/// Invokes client-provided function on every filesystem event in the watched
23/// directory. Initially the the watched directory is scanned and for every file
24/// found, an event is synthesized as if the file was added.
25///
26/// This is not a general purpose directory monitoring tool - list of
27/// limitations follows.
28///
29/// Only flat directories with no subdirectories are supported. In case
30/// subdirectories are present the behavior is unspecified - events *might* be
31/// passed to Receiver on macOS (due to FSEvents being used) while they
32/// *probably* won't be passed on Linux (due to inotify being used).
33///
34/// Known potential inconsistencies
35/// - For files that are deleted befor the initial scan processed them, clients
36/// might receive Removed notification without any prior Added notification.
37/// - Multiple notifications might be produced when a file is added to the
38/// watched directory during the initial scan. We are choosing the lesser evil
39/// here as the only known alternative strategy would be to invalidate the
40/// watcher instance and force user to create a new one whenever filesystem
41/// event occurs during the initial scan but that would introduce continuous
42/// restarting failure mode (watched directory is not always "owned" by the same
43/// process that is consuming it). Since existing clients can handle duplicate
44/// events well, we decided for simplicity.
45///
46/// Notifications are provided only for changes done through local user-space
47/// filesystem interface. Specifically, it's unspecified if notification would
48/// be provided in case of a:
49/// - a file mmap-ed and changed
50/// - a file changed via remote (NFS) or virtual (/proc) FS access to monitored
51/// directory
52/// - another filesystem mounted to the watched directory
53///
54/// No support for LLVM VFS.
55///
56/// It is unspecified whether notifications for files being deleted are sent in
57/// case the whole watched directory is sent.
58///
59/// Directories containing "too many" files and/or receiving events "too
60/// frequently" are not supported - if the initial scan can't be finished before
61/// the watcher instance gets invalidated (see WatcherGotInvalidated) there's no
62/// good error handling strategy - the only option for client is to destroy the
63/// watcher, restart watching with new instance and hope it won't repeat.
64class DirectoryWatcher {
65public:
66  struct Event {
67    enum class EventKind {
68      Removed,
69      /// Content of a file was modified.
70      Modified,
71      /// The watched directory got deleted.
72      WatchedDirRemoved,
73      /// The DirectoryWatcher that originated this event is no longer valid and
74      /// its behavior is unspecified.
75      ///
76      /// The prime case is kernel signalling to OS-specific implementation of
77      /// DirectoryWatcher some resource limit being hit.
78      /// *Usually* kernel starts dropping or squashing events together after
79      /// that and so would DirectoryWatcher. This means that *some* events
80      /// might still be passed to Receiver but this behavior is unspecified.
81      ///
82      /// Another case is after the watched directory itself is deleted.
83      /// WatcherGotInvalidated will be received at least once during
84      /// DirectoryWatcher instance lifetime - when handling errors this is done
85      /// on best effort basis, when an instance is being destroyed then this is
86      /// guaranteed.
87      ///
88      /// The only proper response to this kind of event is to destruct the
89      /// originating DirectoryWatcher instance and create a new one.
90      WatcherGotInvalidated
91    };
92
93    EventKind Kind;
94    /// Filename that this event is related to or an empty string in
95    /// case this event is related to the watched directory itself.
96    std::string Filename;
97
98    Event(EventKind Kind, llvm::StringRef Filename)
99        : Kind(Kind), Filename(Filename) {}
100  };
101
102  /// llvm fatal_error if \param Path doesn't exist or isn't a directory.
103  /// Returns llvm::Expected Error if OS kernel API told us we can't start
104  /// watching. In such case it's unclear whether just retrying has any chance
105  /// to succeed.
106  static llvm::Expected<std::unique_ptr<DirectoryWatcher>>
107  create(llvm::StringRef Path,
108         std::function<void(llvm::ArrayRef<DirectoryWatcher::Event> Events,
109                            bool IsInitial)>
110             Receiver,
111         bool WaitForInitialSync);
112
113  virtual ~DirectoryWatcher() = default;
114  DirectoryWatcher(const DirectoryWatcher &) = delete;
115  DirectoryWatcher &operator=(const DirectoryWatcher &) = delete;
116  DirectoryWatcher(DirectoryWatcher &&) = default;
117
118protected:
119  DirectoryWatcher() = default;
120};
121
122} // namespace clang
123
124#endif // LLVM_CLANG_DIRECTORYWATCHER_DIRECTORYWATCHER_H
125