1 //===--- Background.h - Build an index in a background thread ----*- 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_TOOLS_EXTRA_CLANGD_INDEX_BACKGROUND_H 10 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INDEX_BACKGROUND_H 11 12 #include "GlobalCompilationDatabase.h" 13 #include "SourceCode.h" 14 #include "index/BackgroundRebuild.h" 15 #include "index/FileIndex.h" 16 #include "index/Index.h" 17 #include "index/Serialization.h" 18 #include "support/Context.h" 19 #include "support/MemoryTree.h" 20 #include "support/Path.h" 21 #include "support/Threading.h" 22 #include "support/ThreadsafeFS.h" 23 #include "clang/Tooling/CompilationDatabase.h" 24 #include "llvm/ADT/StringMap.h" 25 #include "llvm/Support/Threading.h" 26 #include <atomic> 27 #include <condition_variable> 28 #include <deque> 29 #include <mutex> 30 #include <queue> 31 #include <string> 32 #include <thread> 33 #include <vector> 34 35 namespace clang { 36 namespace clangd { 37 38 // Handles storage and retrieval of index shards. Both store and load 39 // operations can be called from multiple-threads concurrently. 40 class BackgroundIndexStorage { 41 public: 42 virtual ~BackgroundIndexStorage() = default; 43 44 // Shards of the index are stored and retrieved independently, keyed by shard 45 // identifier - in practice this is a source file name 46 virtual llvm::Error storeShard(llvm::StringRef ShardIdentifier, 47 IndexFileOut Shard) const = 0; 48 49 // Tries to load shard with given identifier, returns nullptr if shard 50 // couldn't be loaded. 51 virtual std::unique_ptr<IndexFileIn> 52 loadShard(llvm::StringRef ShardIdentifier) const = 0; 53 54 // The factory provides storage for each File. 55 // It keeps ownership of the storage instances, and should manage caching 56 // itself. Factory must be threadsafe and never returns nullptr. 57 using Factory = llvm::unique_function<BackgroundIndexStorage *(PathRef)>; 58 59 // Creates an Index Storage that saves shards into disk. Index storage uses 60 // CDBDirectory + ".cache/clangd/index/" as the folder to save shards. 61 // CDBDirectory is the first directory containing a CDB in parent directories 62 // of a file, or user cache directory if none was found, e.g. stdlib headers. 63 static Factory createDiskBackedStorageFactory( 64 std::function<llvm::Optional<ProjectInfo>(PathRef)> GetProjectInfo); 65 }; 66 67 // A priority queue of tasks which can be run on (external) worker threads. 68 class BackgroundQueue { 69 public: 70 /// A work item on the thread pool's queue. 71 struct Task { TaskTask72 explicit Task(std::function<void()> Run) : Run(std::move(Run)) {} 73 74 std::function<void()> Run; 75 llvm::ThreadPriority ThreadPri = llvm::ThreadPriority::Low; 76 unsigned QueuePri = 0; // Higher-priority tasks will run first. 77 std::string Tag; // Allows priority to be boosted later. 78 uint64_t Key = 0; // If the key matches a previous task, drop this one. 79 // (in practice this means we never reindex a file). 80 81 bool operator<(const Task &O) const { return QueuePri < O.QueuePri; } 82 }; 83 84 // Describes the number of tasks processed by the queue. 85 struct Stats { 86 unsigned Enqueued = 0; // Total number of tasks ever enqueued. 87 unsigned Active = 0; // Tasks being currently processed by a worker. 88 unsigned Completed = 0; // Tasks that have been finished. 89 unsigned LastIdle = 0; // Number of completed tasks when last empty. 90 }; 91 92 BackgroundQueue(std::function<void(Stats)> OnProgress = nullptr) OnProgress(OnProgress)93 : OnProgress(OnProgress) {} 94 95 // Add tasks to the queue. 96 void push(Task); 97 void append(std::vector<Task>); 98 // Boost priority of current and new tasks with matching Tag, if they are 99 // lower priority. 100 // Reducing the boost of a tag affects future tasks but not current ones. 101 void boost(llvm::StringRef Tag, unsigned NewPriority); 102 103 // Process items on the queue until the queue is stopped. 104 // If the queue becomes empty, OnIdle will be called (on one worker). 105 void work(std::function<void()> OnIdle = nullptr); 106 107 // Stop processing new tasks, allowing all work() calls to return soon. 108 void stop(); 109 110 // Disables thread priority lowering to ensure progress on loaded systems. 111 // Only affects tasks that run after the call. 112 static void preventThreadStarvationInTests(); 113 LLVM_NODISCARD bool 114 blockUntilIdleForTest(llvm::Optional<double> TimeoutSeconds); 115 116 private: 117 void notifyProgress() const; // Requires lock Mu 118 bool adjust(Task &T); 119 120 std::mutex Mu; 121 Stats Stat; 122 std::condition_variable CV; 123 bool ShouldStop = false; 124 std::vector<Task> Queue; // max-heap 125 llvm::StringMap<unsigned> Boosts; 126 std::function<void(Stats)> OnProgress; 127 llvm::DenseSet<uint64_t> SeenKeys; 128 }; 129 130 // Builds an in-memory index by by running the static indexer action over 131 // all commands in a compilation database. Indexing happens in the background. 132 // FIXME: it should watch for changes to files on disk. 133 class BackgroundIndex : public SwapIndex { 134 public: 135 struct Options { 136 // Arbitrary value to ensure some concurrency in tests. 137 // In production an explicit value is specified. 138 size_t ThreadPoolSize = 4; 139 // Thread priority when indexing files. 140 llvm::ThreadPriority IndexingPriority = llvm::ThreadPriority::Low; 141 // Callback that provides notifications as indexing makes progress. 142 std::function<void(BackgroundQueue::Stats)> OnProgress = nullptr; 143 // Function called to obtain the Context to use while indexing the specified 144 // file. Called with the empty string for other tasks. 145 // (When called, the context from BackgroundIndex construction is active). 146 std::function<Context(PathRef)> ContextProvider = nullptr; 147 }; 148 149 /// Creates a new background index and starts its threads. 150 /// The current Context will be propagated to each worker thread. 151 BackgroundIndex(const ThreadsafeFS &, const GlobalCompilationDatabase &CDB, 152 BackgroundIndexStorage::Factory IndexStorageFactory, 153 Options Opts); 154 ~BackgroundIndex(); // Blocks while the current task finishes. 155 156 // Enqueue translation units for indexing. 157 // The indexing happens in a background thread, so the symbols will be 158 // available sometime later. enqueue(const std::vector<std::string> & ChangedFiles)159 void enqueue(const std::vector<std::string> &ChangedFiles) { 160 Queue.push(changedFilesTask(ChangedFiles)); 161 } 162 163 /// Boosts priority of indexing related to Path. 164 /// Typically used to index TUs when headers are opened. 165 void boostRelated(llvm::StringRef Path); 166 167 // Cause background threads to stop after ther current task, any remaining 168 // tasks will be discarded. stop()169 void stop() { 170 Rebuilder.shutdown(); 171 Queue.stop(); 172 } 173 174 // Wait until the queue is empty, to allow deterministic testing. 175 LLVM_NODISCARD bool 176 blockUntilIdleForTest(llvm::Optional<double> TimeoutSeconds = 10) { 177 return Queue.blockUntilIdleForTest(TimeoutSeconds); 178 } 179 180 void profile(MemoryTree &MT) const; 181 182 private: 183 /// Represents the state of a single file when indexing was performed. 184 struct ShardVersion { 185 FileDigest Digest{{0}}; 186 bool HadErrors = false; 187 }; 188 189 /// Given index results from a TU, only update symbols coming from files with 190 /// different digests than \p ShardVersionsSnapshot. Also stores new index 191 /// information on IndexStorage. 192 void update(llvm::StringRef MainFile, IndexFileIn Index, 193 const llvm::StringMap<ShardVersion> &ShardVersionsSnapshot, 194 bool HadErrors); 195 196 // configuration 197 const ThreadsafeFS &TFS; 198 const GlobalCompilationDatabase &CDB; 199 llvm::ThreadPriority IndexingPriority; 200 std::function<Context(PathRef)> ContextProvider; 201 202 llvm::Error index(tooling::CompileCommand); 203 204 FileSymbols IndexedSymbols; 205 BackgroundIndexRebuilder Rebuilder; 206 llvm::StringMap<ShardVersion> ShardVersions; // Key is absolute file path. 207 std::mutex ShardVersionsMu; 208 209 BackgroundIndexStorage::Factory IndexStorageFactory; 210 // Tries to load shards for the MainFiles and their dependencies. 211 std::vector<std::string> loadProject(std::vector<std::string> MainFiles); 212 213 BackgroundQueue::Task 214 changedFilesTask(const std::vector<std::string> &ChangedFiles); 215 BackgroundQueue::Task indexFileTask(std::string Path); 216 217 // from lowest to highest priority 218 enum QueuePriority { 219 IndexFile, 220 IndexBoostedFile, 221 LoadShards, 222 }; 223 BackgroundQueue Queue; 224 AsyncTaskRunner ThreadPool; 225 GlobalCompilationDatabase::CommandChanged::Subscription CommandsChanged; 226 }; 227 228 } // namespace clangd 229 } // namespace clang 230 231 #endif 232