387 lines · c
1//===--- TUScheduler.h -------------------------------------------*-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-exception6//7//===----------------------------------------------------------------------===//8 9#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_TUSCHEDULER_H10#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_TUSCHEDULER_H11 12#include "ASTSignals.h"13#include "Compiler.h"14#include "Diagnostics.h"15#include "GlobalCompilationDatabase.h"16#include "clang-include-cleaner/Record.h"17#include "support/Function.h"18#include "support/MemoryTree.h"19#include "support/Path.h"20#include "support/Threading.h"21#include "llvm/ADT/StringMap.h"22#include "llvm/ADT/StringRef.h"23#include <chrono>24#include <memory>25#include <optional>26#include <string>27 28namespace clang {29namespace clangd {30class ParsedAST;31struct PreambleData;32 33/// Returns a number of a default async threads to use for TUScheduler.34/// Returned value is always >= 1 (i.e. will not cause requests to be processed35/// synchronously).36unsigned getDefaultAsyncThreadsCount();37 38struct InputsAndAST {39 const ParseInputs &Inputs;40 ParsedAST &AST;41};42 43struct InputsAndPreamble {44 llvm::StringRef Contents;45 const tooling::CompileCommand &Command;46 // This can be nullptr if no preamble is available.47 const PreambleData *Preamble;48 // This can be nullptr if no ASTSignals are available.49 const ASTSignals *Signals;50};51 52/// Determines whether diagnostics should be generated for a file snapshot.53enum class WantDiagnostics {54 Yes, /// Diagnostics must be generated for this snapshot.55 No, /// Diagnostics must not be generated for this snapshot.56 Auto, /// Diagnostics must be generated for this snapshot or a subsequent one,57 /// within a bounded amount of time.58};59 60/// Configuration of the AST retention policy. This only covers retention of61/// *idle* ASTs. If queue has operations requiring the AST, they might be62/// kept in memory.63struct ASTRetentionPolicy {64 /// Maximum number of ASTs to be retained in memory when there are no pending65 /// requests for them.66 unsigned MaxRetainedASTs = 3;67};68 69/// Clangd may wait after an update to see if another one comes along.70/// This is so we rebuild once the user stops typing, not when they start.71/// Debounce may be disabled/interrupted if we must build this version.72/// The debounce time is responsive to user preferences and rebuild time.73/// In the future, we could also consider different types of edits.74struct DebouncePolicy {75 using clock = std::chrono::steady_clock;76 77 /// The minimum time that we always debounce for.78 clock::duration Min = /*zero*/ {};79 /// The maximum time we may debounce for.80 clock::duration Max = /*zero*/ {};81 /// Target debounce, as a fraction of file rebuild time.82 /// e.g. RebuildRatio = 2, recent builds took 200ms => debounce for 400ms.83 float RebuildRatio = 1;84 85 /// Compute the time to debounce based on this policy and recent build times.86 clock::duration compute(llvm::ArrayRef<clock::duration> History) const;87 /// A policy that always returns the same duration, useful for tests.88 static DebouncePolicy fixed(clock::duration);89};90 91/// PreambleThrottler controls which preambles can build at any given time.92/// This can be used to limit overall concurrency, and to prioritize some93/// preambles over others.94/// In a distributed environment, a throttler may be able to coordinate resource95/// use across several clangd instances.96///97/// This class is threadsafe.98class PreambleThrottler {99public:100 virtual ~PreambleThrottler() = default;101 102 using RequestID = unsigned;103 using Callback = llvm::unique_function<void()>;104 /// Attempt to acquire resources to build a file's preamble.105 ///106 /// Does not block, may eventually invoke the callback to satisfy the request.107 /// If the callback is invoked, release() must be called afterwards.108 virtual RequestID acquire(llvm::StringRef Filename, Callback) = 0;109 /// Abandons the request/releases any resources that have been acquired.110 ///111 /// Must be called exactly once after acquire().112 /// acquire()'s callback will not be invoked after release() returns.113 virtual void release(RequestID) = 0;114 115 // FIXME: we may want to be able attach signals to filenames.116 // this would allow the throttler to make better scheduling decisions.117};118 119enum class PreambleAction {120 Queued,121 Building,122 Idle,123};124 125struct ASTAction {126 enum Kind {127 Queued, // The action is pending in the thread task queue to be run.128 RunningAction, // Started running actions on the TU.129 Building, // The AST is being built.130 Idle, // Indicates the worker thread is idle, and ready to run any upcoming131 // actions.132 };133 ASTAction() = default;134 ASTAction(Kind K, llvm::StringRef Name) : K(K), Name(Name) {}135 Kind K = ASTAction::Idle;136 /// The name of the action currently running, e.g. Update, GoToDef, Hover.137 /// Empty if we are in the idle state.138 std::string Name;139};140 141// Internal status of the TU in TUScheduler.142struct TUStatus {143 struct BuildDetails {144 /// Indicates whether clang failed to build the TU.145 bool BuildFailed = false;146 /// Indicates whether we reused the prebuilt AST.147 bool ReuseAST = false;148 };149 /// Serialize this to an LSP file status item.150 FileStatus render(PathRef File) const;151 152 PreambleAction PreambleActivity = PreambleAction::Idle;153 ASTAction ASTActivity;154 /// Stores status of the last build for the translation unit.155 BuildDetails Details;156};157 158class ParsingCallbacks {159public:160 virtual ~ParsingCallbacks() = default;161 162 /// Called on the AST that was built for emitting the preamble. The built AST163 /// contains only AST nodes from the #include directives at the start of the164 /// file. AST node in the current file should be observed on onMainAST call.165 virtual void166 onPreambleAST(PathRef Path, llvm::StringRef Version, CapturedASTCtx Ctx,167 std::shared_ptr<const include_cleaner::PragmaIncludes>) {}168 /// The argument function is run under the critical section guarding against169 /// races when closing the files.170 using PublishFn = llvm::function_ref<void(llvm::function_ref<void()>)>;171 /// Called on the AST built for the file itself. Note that preamble AST nodes172 /// are not deserialized and should be processed in the onPreambleAST call173 /// instead.174 /// The \p AST always contains all AST nodes for the main file itself, and175 /// only a portion of the AST nodes deserialized from the preamble. Note that176 /// some nodes from the preamble may have been deserialized and may also be177 /// accessed from the main file AST, e.g. redecls of functions from preamble,178 /// etc. Clients are expected to process only the AST nodes from the main file179 /// in this callback (obtained via ParsedAST::getLocalTopLevelDecls) to obtain180 /// optimal performance.181 ///182 /// When information about the file (e.g. diagnostics) is183 /// published to clients, this should be wrapped in Publish, e.g.184 /// void onMainAST(...) {185 /// Diags = renderDiagnostics();186 /// Publish([&] { notifyDiagnostics(Path, Diags); });187 /// }188 /// This guarantees that clients will see results in the correct sequence if189 /// the file is concurrently closed and/or reopened. (The lambda passed to190 /// Publish() may never run in this case).191 virtual void onMainAST(PathRef Path, ParsedAST &AST, PublishFn Publish) {}192 193 /// Called whenever the AST fails to build. \p Diags will have the diagnostics194 /// that led to failure.195 virtual void onFailedAST(PathRef Path, llvm::StringRef Version,196 std::vector<Diag> Diags, PublishFn Publish) {}197 198 /// Called whenever the TU status is updated.199 virtual void onFileUpdated(PathRef File, const TUStatus &Status) {}200 201 /// Preamble for the TU have changed. This might imply new semantics (e.g.202 /// different highlightings). Any actions on the file are guranteed to see new203 /// preamble after the callback.204 virtual void onPreamblePublished(PathRef File) {}205};206 207/// Handles running tasks for ClangdServer and managing the resources (e.g.,208/// preambles and ASTs) for opened files.209/// TUScheduler is not thread-safe, only one thread should be providing updates210/// and scheduling tasks.211/// Callbacks are run on a threadpool and it's appropriate to do slow work in212/// them. Each task has a name, used for tracing (should be UpperCamelCase).213class TUScheduler {214public:215 struct Options {216 /// Number of concurrent actions.217 /// Governs per-file worker threads and threads spawned for other tasks.218 /// (This does not prevent threads being spawned, but rather blocks them).219 /// If 0, executes actions synchronously on the calling thread.220 unsigned AsyncThreadsCount = getDefaultAsyncThreadsCount();221 222 /// Cache (large) preamble data in RAM rather than temporary files on disk.223 bool StorePreamblesInMemory = false;224 225 /// Time to wait after an update to see if another one comes along.226 /// This tries to ensure we rebuild once the user stops typing.227 DebouncePolicy UpdateDebounce;228 229 /// Determines when to keep idle ASTs in memory for future use.230 ASTRetentionPolicy RetentionPolicy;231 232 /// This throttler controls which preambles may be built at a given time.233 clangd::PreambleThrottler *PreambleThrottler = nullptr;234 235 /// Used to create a context that wraps each single operation.236 /// Typically to inject per-file configuration.237 /// If the path is empty, context sholud be "generic".238 std::function<Context(PathRef)> ContextProvider;239 };240 241 TUScheduler(const GlobalCompilationDatabase &CDB, const Options &Opts,242 std::unique_ptr<ParsingCallbacks> ASTCallbacks = nullptr);243 ~TUScheduler();244 245 TUScheduler(const TUScheduler &other) = delete;246 TUScheduler &operator=(const TUScheduler &other) = delete;247 248 struct FileStats {249 std::size_t UsedBytesAST = 0;250 std::size_t UsedBytesPreamble = 0;251 unsigned PreambleBuilds = 0;252 unsigned ASTBuilds = 0;253 };254 /// Returns resources used for each of the currently open files.255 /// Results are inherently racy as they measure activity of other threads.256 llvm::StringMap<FileStats> fileStats() const;257 258 /// Returns a list of files with ASTs currently stored in memory. This method259 /// is not very reliable and is only used for test. E.g., the results will not260 /// contain files that currently run something over their AST.261 std::vector<Path> getFilesWithCachedAST() const;262 263 /// Schedule an update for \p File.264 /// The compile command in \p Inputs is ignored; worker queries CDB to get265 /// the actual compile command.266 /// If diagnostics are requested (Yes), and the context is cancelled267 /// before they are prepared, they may be skipped if eventual-consistency268 /// permits it (i.e. WantDiagnostics is downgraded to Auto).269 /// Returns true if the file was not previously tracked.270 bool update(PathRef File, ParseInputs Inputs, WantDiagnostics WD);271 272 /// Remove \p File from the list of tracked files and schedule removal of its273 /// resources. Pending diagnostics for closed files may not be delivered, even274 /// if requested with WantDiags::Auto or WantDiags::Yes.275 void remove(PathRef File);276 277 /// Schedule an async task with no dependencies.278 /// Path may be empty (it is used only to set the Context).279 void run(llvm::StringRef Name, llvm::StringRef Path,280 llvm::unique_function<void()> Action);281 282 /// Similar to run, except the task is expected to be quick.283 /// This function will not honor AsyncThreadsCount (except284 /// if threading is disabled with AsyncThreadsCount=0)285 /// It is intended to run quick tasks that need to run ASAP286 void runQuick(llvm::StringRef Name, llvm::StringRef Path,287 llvm::unique_function<void()> Action);288 289 /// Defines how a runWithAST action is implicitly cancelled by other actions.290 enum ASTActionInvalidation {291 /// The request will run unless explicitly cancelled.292 NoInvalidation,293 /// The request will be implicitly cancelled by a subsequent update().294 /// (Only if the request was not yet cancelled).295 /// Useful for requests that are generated by clients, without any explicit296 /// user action. These can otherwise e.g. force every version to be built.297 InvalidateOnUpdate,298 };299 300 /// Schedule an async read of the AST. \p Action will be called when AST is301 /// ready. The AST passed to \p Action refers to the version of \p File302 /// tracked at the time of the call, even if new updates are received before303 /// \p Action is executed.304 /// If an error occurs during processing, it is forwarded to the \p Action305 /// callback.306 /// If the context is cancelled before the AST is ready, or the invalidation307 /// policy is triggered, the callback will receive a CancelledError.308 void runWithAST(llvm::StringRef Name, PathRef File,309 Callback<InputsAndAST> Action,310 ASTActionInvalidation = NoInvalidation);311 312 /// Controls whether preamble reads wait for the preamble to be up-to-date.313 enum PreambleConsistency {314 /// The preamble may be generated from an older version of the file.315 /// Reading from locations in the preamble may cause files to be re-read.316 /// This gives callers two options:317 /// - validate that the preamble is still valid, and only use it if so318 /// - accept that the preamble contents may be outdated, and try to avoid319 /// reading source code from headers.320 /// This is the fastest option, usually a preamble is available immediately.321 Stale,322 /// Besides accepting stale preamble, this also allow preamble to be absent323 /// (not ready or failed to build).324 StaleOrAbsent,325 };326 327 /// Schedule an async read of the preamble.328 /// If there's no up-to-date preamble, we follow the PreambleConsistency329 /// policy.330 /// If an error occurs, it is forwarded to the \p Action callback.331 /// Context cancellation is ignored and should be handled by the Action.332 /// (In practice, the Action is almost always executed immediately).333 void runWithPreamble(llvm::StringRef Name, PathRef File,334 PreambleConsistency Consistency,335 Callback<InputsAndPreamble> Action);336 337 /// Wait until there are no scheduled or running tasks.338 /// Mostly useful for synchronizing tests.339 bool blockUntilIdle(Deadline D) const;340 341private:342 /// This class stores per-file data in the Files map.343 struct FileData;344 345public:346 /// Responsible for retaining and rebuilding idle ASTs. An implementation is347 /// an LRU cache.348 class ASTCache;349 /// Tracks headers included by open files, to get known-good compile commands.350 class HeaderIncluderCache;351 352 // The file being built/processed in the current thread. This is a hack in353 // order to get the file name into the index implementations. Do not depend on354 // this inside clangd.355 // FIXME: remove this when there is proper index support via build system356 // integration.357 // FIXME: move to ClangdServer via createProcessingContext.358 static std::optional<llvm::StringRef> getFileBeingProcessedInContext();359 360 void profile(MemoryTree &MT) const;361 362private:363 void runWithSemaphore(llvm::StringRef Name, llvm::StringRef Path,364 llvm::unique_function<void()> Action, Semaphore &Sem);365 366 const GlobalCompilationDatabase &CDB;367 Options Opts;368 std::unique_ptr<ParsingCallbacks> Callbacks; // not nullptr369 Semaphore Barrier;370 Semaphore QuickRunBarrier;371 llvm::StringMap<std::unique_ptr<FileData>> Files;372 std::unique_ptr<ASTCache> IdleASTs;373 std::unique_ptr<HeaderIncluderCache> HeaderIncluders;374 // std::nullopt when running tasks synchronously and non-std::nullopt when375 // running tasks asynchronously.376 std::optional<AsyncTaskRunner> PreambleTasks;377 std::optional<AsyncTaskRunner> WorkerThreads;378 // Used to create contexts for operations that are not bound to a particular379 // file (e.g. index queries).380 std::string LastActiveFile;381};382 383} // namespace clangd384} // namespace clang385 386#endif387