llvm-project
1871 строка · 71.7 Кб
1//===--- TUScheduler.cpp -----------------------------------------*-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// TUScheduler manages a worker per active file. This ASTWorker processes
9// updates (modifications to file contents) and reads (actions performed on
10// preamble/AST) to the file.
11//
12// Each ASTWorker owns a dedicated thread to process updates and reads to the
13// relevant file. Any request gets queued in FIFO order to be processed by that
14// thread.
15//
16// An update request replaces current praser inputs to ensure any subsequent
17// read sees the version of the file they were requested. It will also issue a
18// build for new inputs.
19//
20// ASTWorker processes the file in two parts, a preamble and a main-file
21// section. A preamble can be reused between multiple versions of the file until
22// invalidated by a modification to a header, compile commands or modification
23// to relevant part of the current file. Such a preamble is called compatible.
24// An update is considered dead if no read was issued for that version and
25// diagnostics weren't requested by client or could be generated for a later
26// version of the file. ASTWorker eliminates such requests as they are
27// redundant.
28//
29// In the presence of stale (non-compatible) preambles, ASTWorker won't publish
30// diagnostics for update requests. Read requests will be served with ASTs build
31// with stale preambles, unless the read is picky and requires a compatible
32// preamble. In such cases it will block until new preamble is built.
33//
34// ASTWorker owns a PreambleThread for building preambles. If the preamble gets
35// invalidated by an update request, a new build will be requested on
36// PreambleThread. Since PreambleThread only receives requests for newer
37// versions of the file, in case of multiple requests it will only build the
38// last one and skip requests in between. Unless client force requested
39// diagnostics(WantDiagnostics::Yes).
40//
41// When a new preamble is built, a "golden" AST is immediately built from that
42// version of the file. This ensures diagnostics get updated even if the queue
43// is full.
44//
45// Some read requests might just need preamble. Since preambles can be read
46// concurrently, ASTWorker runs these requests on their own thread. These
47// requests will receive latest build preamble, which might possibly be stale.
48
49#include "TUScheduler.h"50#include "CompileCommands.h"51#include "Compiler.h"52#include "Config.h"53#include "Diagnostics.h"54#include "GlobalCompilationDatabase.h"55#include "ParsedAST.h"56#include "Preamble.h"57#include "clang-include-cleaner/Record.h"58#include "support/Cancellation.h"59#include "support/Context.h"60#include "support/Logger.h"61#include "support/MemoryTree.h"62#include "support/Path.h"63#include "support/ThreadCrashReporter.h"64#include "support/Threading.h"65#include "support/Trace.h"66#include "clang/Basic/Stack.h"67#include "clang/Frontend/CompilerInvocation.h"68#include "clang/Tooling/CompilationDatabase.h"69#include "llvm/ADT/FunctionExtras.h"70#include "llvm/ADT/STLExtras.h"71#include "llvm/ADT/ScopeExit.h"72#include "llvm/ADT/SmallVector.h"73#include "llvm/ADT/StringExtras.h"74#include "llvm/ADT/StringRef.h"75#include "llvm/Support/Allocator.h"76#include "llvm/Support/Errc.h"77#include "llvm/Support/ErrorHandling.h"78#include "llvm/Support/FormatVariadic.h"79#include "llvm/Support/Path.h"80#include "llvm/Support/Threading.h"81#include "llvm/Support/raw_ostream.h"82#include <algorithm>83#include <atomic>84#include <chrono>85#include <condition_variable>86#include <functional>87#include <memory>88#include <mutex>89#include <optional>90#include <queue>91#include <string>92#include <thread>93#include <type_traits>94#include <utility>95#include <vector>96
97namespace clang {98namespace clangd {99using std::chrono::steady_clock;100
101namespace {102// Tracks latency (in seconds) of FS operations done during a preamble build.
103// build_type allows to split by expected VFS cache state (cold on first
104// preamble, somewhat warm after that when building first preamble for new file,
105// likely ~everything cached on preamble rebuild.
106constexpr trace::Metric107PreambleBuildFilesystemLatency("preamble_fs_latency",108trace::Metric::Distribution, "build_type");109// Tracks latency of FS operations done during a preamble build as a ratio of
110// preamble build time. build_type is same as above.
111constexpr trace::Metric PreambleBuildFilesystemLatencyRatio(112"preamble_fs_latency_ratio", trace::Metric::Distribution, "build_type");113
114constexpr trace::Metric PreambleBuildSize("preamble_build_size",115trace::Metric::Distribution);116constexpr trace::Metric PreambleSerializedSize("preamble_serialized_size",117trace::Metric::Distribution);118
119void reportPreambleBuild(const PreambleBuildStats &Stats,120bool IsFirstPreamble) {121auto RecordWithLabel = [&Stats](llvm::StringRef Label) {122PreambleBuildFilesystemLatency.record(Stats.FileSystemTime, Label);123if (Stats.TotalBuildTime > 0) // Avoid division by zero.124PreambleBuildFilesystemLatencyRatio.record(125Stats.FileSystemTime / Stats.TotalBuildTime, Label);126};127
128static llvm::once_flag OnceFlag;129llvm::call_once(OnceFlag, [&] { RecordWithLabel("first_build"); });130RecordWithLabel(IsFirstPreamble ? "first_build_for_file" : "rebuild");131
132PreambleBuildSize.record(Stats.BuildSize);133PreambleSerializedSize.record(Stats.SerializedSize);134}
135
136class ASTWorker;137} // namespace138
139static clang::clangd::Key<std::string> FileBeingProcessed;140
141std::optional<llvm::StringRef> TUScheduler::getFileBeingProcessedInContext() {142if (auto *File = Context::current().get(FileBeingProcessed))143return llvm::StringRef(*File);144return std::nullopt;145}
146
147/// An LRU cache of idle ASTs.
148/// Because we want to limit the overall number of these we retain, the cache
149/// owns ASTs (and may evict them) while their workers are idle.
150/// Workers borrow ASTs when active, and return them when done.
151class TUScheduler::ASTCache {152public:153using Key = const ASTWorker *;154
155ASTCache(unsigned MaxRetainedASTs) : MaxRetainedASTs(MaxRetainedASTs) {}156
157/// Returns result of getUsedBytes() for the AST cached by \p K.158/// If no AST is cached, 0 is returned.159std::size_t getUsedBytes(Key K) {160std::lock_guard<std::mutex> Lock(Mut);161auto It = findByKey(K);162if (It == LRU.end() || !It->second)163return 0;164return It->second->getUsedBytes();165}166
167/// Store the value in the pool, possibly removing the last used AST.168/// The value should not be in the pool when this function is called.169void put(Key K, std::unique_ptr<ParsedAST> V) {170std::unique_lock<std::mutex> Lock(Mut);171assert(findByKey(K) == LRU.end());172
173LRU.insert(LRU.begin(), {K, std::move(V)});174if (LRU.size() <= MaxRetainedASTs)175return;176// We're past the limit, remove the last element.177std::unique_ptr<ParsedAST> ForCleanup = std::move(LRU.back().second);178LRU.pop_back();179// Run the expensive destructor outside the lock.180Lock.unlock();181ForCleanup.reset();182}183
184/// Returns the cached value for \p K, or std::nullopt if the value is not in185/// the cache anymore. If nullptr was cached for \p K, this function will186/// return a null unique_ptr wrapped into an optional.187/// If \p AccessMetric is set records whether there was a hit or miss.188std::optional<std::unique_ptr<ParsedAST>>189take(Key K, const trace::Metric *AccessMetric = nullptr) {190// Record metric after unlocking the mutex.191std::unique_lock<std::mutex> Lock(Mut);192auto Existing = findByKey(K);193if (Existing == LRU.end()) {194if (AccessMetric)195AccessMetric->record(1, "miss");196return std::nullopt;197}198if (AccessMetric)199AccessMetric->record(1, "hit");200std::unique_ptr<ParsedAST> V = std::move(Existing->second);201LRU.erase(Existing);202// GCC 4.8 fails to compile `return V;`, as it tries to call the copy203// constructor of unique_ptr, so we call the move ctor explicitly to avoid204// this miscompile.205return std::optional<std::unique_ptr<ParsedAST>>(std::move(V));206}207
208private:209using KVPair = std::pair<Key, std::unique_ptr<ParsedAST>>;210
211std::vector<KVPair>::iterator findByKey(Key K) {212return llvm::find_if(LRU, [K](const KVPair &P) { return P.first == K; });213}214
215std::mutex Mut;216unsigned MaxRetainedASTs;217/// Items sorted in LRU order, i.e. first item is the most recently accessed218/// one.219std::vector<KVPair> LRU; /* GUARDED_BY(Mut) */220};221
222/// A map from header files to an opened "proxy" file that includes them.
223/// If you open the header, the compile command from the proxy file is used.
224///
225/// This inclusion information could also naturally live in the index, but there
226/// are advantages to using open files instead:
227/// - it's easier to achieve a *stable* choice of proxy, which is important
228/// to avoid invalidating the preamble
229/// - context-sensitive flags for libraries with multiple configurations
230/// (e.g. C++ stdlib sensitivity to -std version)
231/// - predictable behavior, e.g. guarantees that go-to-def landing on a header
232/// will have a suitable command available
233/// - fewer scaling problems to solve (project include graphs are big!)
234///
235/// Implementation details:
236/// - We only record this for mainfiles where the command was trustworthy
237/// (i.e. not inferred). This avoids a bad inference "infecting" other files.
238/// - Once we've picked a proxy file for a header, we stick with it until the
239/// proxy file is invalidated *and* a new candidate proxy file is built.
240/// Switching proxies is expensive, as the compile flags will (probably)
241/// change and therefore we'll end up rebuilding the header's preamble.
242/// - We don't capture the actual compile command, but just the filename we
243/// should query to get it. This avoids getting out of sync with the CDB.
244///
245/// All methods are threadsafe. In practice, update() comes from preamble
246/// threads, remove()s mostly from the main thread, and get() from ASTWorker.
247/// Writes are rare and reads are cheap, so we don't expect much contention.
248class TUScheduler::HeaderIncluderCache {249// We should be a little careful how we store the include graph of open250// files, as each can have a large number of transitive headers.251// This representation is O(unique transitive source files).252llvm::BumpPtrAllocator Arena;253struct Association {254llvm::StringRef MainFile;255// Circular-linked-list of associations with the same mainFile.256// Null indicates that the mainfile was removed.257Association *Next;258};259llvm::StringMap<Association, llvm::BumpPtrAllocator &> HeaderToMain;260llvm::StringMap<Association *, llvm::BumpPtrAllocator &> MainToFirst;261std::atomic<size_t> UsedBytes; // Updated after writes.262mutable std::mutex Mu;263
264void invalidate(Association *First) {265Association *Current = First;266do {267Association *Next = Current->Next;268Current->Next = nullptr;269Current = Next;270} while (Current != First);271}272
273// Create the circular list and return the head of it.274Association *associate(llvm::StringRef MainFile,275llvm::ArrayRef<std::string> Headers) {276Association *First = nullptr, *Prev = nullptr;277for (const std::string &Header : Headers) {278auto &Assoc = HeaderToMain[Header];279if (Assoc.Next)280continue; // Already has a valid association.281
282Assoc.MainFile = MainFile;283Assoc.Next = Prev;284Prev = &Assoc;285if (!First)286First = &Assoc;287}288if (First)289First->Next = Prev;290return First;291}292
293void updateMemoryUsage() {294auto StringMapHeap = [](const auto &Map) {295// StringMap stores the hashtable on the heap.296// It contains pointers to the entries, and a hashcode for each.297return Map.getNumBuckets() * (sizeof(void *) + sizeof(unsigned));298};299size_t Usage = Arena.getTotalMemory() + StringMapHeap(MainToFirst) +300StringMapHeap(HeaderToMain) + sizeof(*this);301UsedBytes.store(Usage, std::memory_order_release);302}303
304public:305HeaderIncluderCache() : HeaderToMain(Arena), MainToFirst(Arena) {306updateMemoryUsage();307}308
309// Associate each header with MainFile (unless already associated).310// Headers not in the list will have their associations removed.311void update(PathRef MainFile, llvm::ArrayRef<std::string> Headers) {312std::lock_guard<std::mutex> Lock(Mu);313auto It = MainToFirst.try_emplace(MainFile, nullptr);314Association *&First = It.first->second;315if (First)316invalidate(First);317First = associate(It.first->first(), Headers);318updateMemoryUsage();319}320
321// Mark MainFile as gone.322// This will *not* disassociate headers with MainFile immediately, but they323// will be eligible for association with other files that get update()d.324void remove(PathRef MainFile) {325std::lock_guard<std::mutex> Lock(Mu);326Association *&First = MainToFirst[MainFile];327if (First) {328invalidate(First);329First = nullptr;330}331// MainToFirst entry should stay alive, as Associations might be pointing at332// its key.333}334
335/// Get the mainfile associated with Header, or the empty string if none.336std::string get(PathRef Header) const {337std::lock_guard<std::mutex> Lock(Mu);338return HeaderToMain.lookup(Header).MainFile.str();339}340
341size_t getUsedBytes() const {342return UsedBytes.load(std::memory_order_acquire);343}344};345
346namespace {347
348bool isReliable(const tooling::CompileCommand &Cmd) {349return Cmd.Heuristic.empty();350}
351
352/// Threadsafe manager for updating a TUStatus and emitting it after each
353/// update.
354class SynchronizedTUStatus {355public:356SynchronizedTUStatus(PathRef FileName, ParsingCallbacks &Callbacks)357: FileName(FileName), Callbacks(Callbacks) {}358
359void update(llvm::function_ref<void(TUStatus &)> Mutator) {360std::lock_guard<std::mutex> Lock(StatusMu);361Mutator(Status);362emitStatusLocked();363}364
365/// Prevents emitting of further updates.366void stop() {367std::lock_guard<std::mutex> Lock(StatusMu);368CanPublish = false;369}370
371private:372void emitStatusLocked() {373if (CanPublish)374Callbacks.onFileUpdated(FileName, Status);375}376
377const Path FileName;378
379std::mutex StatusMu;380TUStatus Status;381bool CanPublish = true;382ParsingCallbacks &Callbacks;383};384
385// An attempt to acquire resources for a task using PreambleThrottler.
386// Initially it is unsatisfied, it (hopefully) becomes satisfied later but may
387// be destroyed before then. Destruction releases all resources.
388class PreambleThrottlerRequest {389public:390// The condition variable is signalled when the request is satisfied.391PreambleThrottlerRequest(llvm::StringRef Filename,392PreambleThrottler *Throttler,393std::condition_variable &CV)394: Throttler(Throttler),395Satisfied(Throttler == nullptr) {396// If there is no throttler, this dummy request is always satisfied.397if (!Throttler)398return;399ID = Throttler->acquire(Filename, [&] {400Satisfied.store(true, std::memory_order_release);401CV.notify_all();402});403}404
405bool satisfied() const { return Satisfied.load(std::memory_order_acquire); }406
407// When the request is destroyed:408// - if resources are not yet obtained, stop trying to get them.409// - if resources were obtained, release them.410~PreambleThrottlerRequest() {411if (Throttler)412Throttler->release(ID);413}414
415private:416PreambleThrottler::RequestID ID;417PreambleThrottler *Throttler;418std::atomic<bool> Satisfied = {false};419};420
421/// Responsible for building preambles. Whenever the thread is idle and the
422/// preamble is outdated, it starts to build a fresh preamble from the latest
423/// inputs. If RunSync is true, preambles are built synchronously in update()
424/// instead.
425class PreambleThread {426public:427PreambleThread(llvm::StringRef FileName, ParsingCallbacks &Callbacks,428bool StorePreambleInMemory, bool RunSync,429PreambleThrottler *Throttler, SynchronizedTUStatus &Status,430TUScheduler::HeaderIncluderCache &HeaderIncluders,431ASTWorker &AW)432: FileName(FileName), Callbacks(Callbacks),433StoreInMemory(StorePreambleInMemory), RunSync(RunSync),434Throttler(Throttler), Status(Status), ASTPeer(AW),435HeaderIncluders(HeaderIncluders) {}436
437/// It isn't guaranteed that each requested version will be built. If there438/// are multiple update requests while building a preamble, only the last one439/// will be built.440void update(std::unique_ptr<CompilerInvocation> CI, ParseInputs PI,441std::vector<Diag> CIDiags, WantDiagnostics WantDiags) {442Request Req = {std::move(CI), std::move(PI), std::move(CIDiags), WantDiags,443Context::current().clone()};444if (RunSync) {445build(std::move(Req));446Status.update([](TUStatus &Status) {447Status.PreambleActivity = PreambleAction::Idle;448});449return;450}451{452std::unique_lock<std::mutex> Lock(Mutex);453// If NextReq was requested with WantDiagnostics::Yes we cannot just drop454// that on the floor. Block until we start building it. This won't455// dead-lock as we are blocking the caller thread, while builds continue456// on preamble thread.457ReqCV.wait(Lock, [this] {458return !NextReq || NextReq->WantDiags != WantDiagnostics::Yes;459});460NextReq = std::move(Req);461}462// Let the worker thread know there's a request, notify_one is safe as there463// should be a single worker thread waiting on it.464ReqCV.notify_all();465}466
467void run() {468// We mark the current as the stack bottom so that clang running on this469// thread can notice the stack usage and prevent stack overflow with best470// efforts. Same applies to other calls thoughout clangd.471clang::noteBottomOfStack();472while (true) {473std::optional<PreambleThrottlerRequest> Throttle;474{475std::unique_lock<std::mutex> Lock(Mutex);476assert(!CurrentReq && "Already processing a request?");477// Wait until stop is called or there is a request.478ReqCV.wait(Lock, [&] { return NextReq || Done; });479if (Done)480break;481
482{483Throttle.emplace(FileName, Throttler, ReqCV);484std::optional<trace::Span> Tracer;485// If acquire succeeded synchronously, avoid status jitter.486if (!Throttle->satisfied()) {487Tracer.emplace("PreambleThrottle");488Status.update([&](TUStatus &Status) {489Status.PreambleActivity = PreambleAction::Queued;490});491}492ReqCV.wait(Lock, [&] { return Throttle->satisfied() || Done; });493}494if (Done)495break;496// While waiting for the throttler, the request may have been updated!497// That's fine though, there's still guaranteed to be some request.498
499CurrentReq = std::move(*NextReq);500NextReq.reset();501}502
503{504WithContext Guard(std::move(CurrentReq->Ctx));505// Note that we don't make use of the ContextProvider here.506// Preamble tasks are always scheduled by ASTWorker tasks, and we507// reuse the context/config that was created at that level.508
509// Build the preamble and let the waiters know about it.510build(std::move(*CurrentReq));511}512// Releasing the throttle before destroying the request assists testing.513Throttle.reset();514bool IsEmpty = false;515{516std::lock_guard<std::mutex> Lock(Mutex);517CurrentReq.reset();518IsEmpty = !NextReq;519}520if (IsEmpty) {521// We don't perform this above, before waiting for a request to make522// tests more deterministic. As there can be a race between this thread523// and client thread(clangdserver).524Status.update([](TUStatus &Status) {525Status.PreambleActivity = PreambleAction::Idle;526});527}528ReqCV.notify_all();529}530dlog("Preamble worker for {0} stopped", FileName);531}532
533/// Signals the run loop to exit.534void stop() {535dlog("Preamble worker for {0} received stop", FileName);536{537std::lock_guard<std::mutex> Lock(Mutex);538Done = true;539NextReq.reset();540}541// Let the worker thread know that it should stop.542ReqCV.notify_all();543}544
545bool blockUntilIdle(Deadline Timeout) const {546std::unique_lock<std::mutex> Lock(Mutex);547return wait(Lock, ReqCV, Timeout, [&] { return !NextReq && !CurrentReq; });548}549
550private:551/// Holds inputs required for building a preamble. CI is guaranteed to be552/// non-null.553struct Request {554std::unique_ptr<CompilerInvocation> CI;555ParseInputs Inputs;556std::vector<Diag> CIDiags;557WantDiagnostics WantDiags;558Context Ctx;559};560
561bool isDone() {562std::lock_guard<std::mutex> Lock(Mutex);563return Done;564}565
566/// Builds a preamble for \p Req, might reuse LatestBuild if possible.567/// Notifies ASTWorker after build finishes.568void build(Request Req);569
570mutable std::mutex Mutex;571bool Done = false; /* GUARDED_BY(Mutex) */572std::optional<Request> NextReq; /* GUARDED_BY(Mutex) */573std::optional<Request> CurrentReq; /* GUARDED_BY(Mutex) */574// Signaled whenever a thread populates NextReq or worker thread builds a575// Preamble.576mutable std::condition_variable ReqCV; /* GUARDED_BY(Mutex) */577// Accessed only by preamble thread.578std::shared_ptr<const PreambleData> LatestBuild;579
580const Path FileName;581ParsingCallbacks &Callbacks;582const bool StoreInMemory;583const bool RunSync;584PreambleThrottler *Throttler;585
586SynchronizedTUStatus &Status;587ASTWorker &ASTPeer;588TUScheduler::HeaderIncluderCache &HeaderIncluders;589};590
591class ASTWorkerHandle;592
593/// Owns one instance of the AST, schedules updates and reads of it.
594/// Also responsible for building and providing access to the preamble.
595/// Each ASTWorker processes the async requests sent to it on a separate
596/// dedicated thread.
597/// The ASTWorker that manages the AST is shared by both the processing thread
598/// and the TUScheduler. The TUScheduler should discard an ASTWorker when
599/// remove() is called, but its thread may be busy and we don't want to block.
600/// So the workers are accessed via an ASTWorkerHandle. Destroying the handle
601/// signals the worker to exit its run loop and gives up shared ownership of the
602/// worker.
603class ASTWorker {604friend class ASTWorkerHandle;605ASTWorker(PathRef FileName, const GlobalCompilationDatabase &CDB,606TUScheduler::ASTCache &LRUCache,607TUScheduler::HeaderIncluderCache &HeaderIncluders,608Semaphore &Barrier, bool RunSync, const TUScheduler::Options &Opts,609ParsingCallbacks &Callbacks);610
611public:612/// Create a new ASTWorker and return a handle to it.613/// The processing thread is spawned using \p Tasks. However, when \p Tasks614/// is null, all requests will be processed on the calling thread615/// synchronously instead. \p Barrier is acquired when processing each616/// request, it is used to limit the number of actively running threads.617static ASTWorkerHandle618create(PathRef FileName, const GlobalCompilationDatabase &CDB,619TUScheduler::ASTCache &IdleASTs,620TUScheduler::HeaderIncluderCache &HeaderIncluders,621AsyncTaskRunner *Tasks, Semaphore &Barrier,622const TUScheduler::Options &Opts, ParsingCallbacks &Callbacks);623~ASTWorker();624
625void update(ParseInputs Inputs, WantDiagnostics, bool ContentChanged);626void627runWithAST(llvm::StringRef Name,628llvm::unique_function<void(llvm::Expected<InputsAndAST>)> Action,629TUScheduler::ASTActionInvalidation);630bool blockUntilIdle(Deadline Timeout) const;631
632std::shared_ptr<const PreambleData> getPossiblyStalePreamble(633std::shared_ptr<const ASTSignals> *ASTSignals = nullptr) const;634
635/// Used to inform ASTWorker about a new preamble build by PreambleThread.636/// Diagnostics are only published through this callback. This ensures they637/// are always for newer versions of the file, as the callback gets called in638/// the same order as update requests.639void updatePreamble(std::unique_ptr<CompilerInvocation> CI, ParseInputs PI,640std::shared_ptr<const PreambleData> Preamble,641std::vector<Diag> CIDiags, WantDiagnostics WantDiags);642
643/// Returns compile command from the current file inputs.644tooling::CompileCommand getCurrentCompileCommand() const;645
646/// Wait for the first build of preamble to finish. Preamble itself can be647/// accessed via getPossiblyStalePreamble(). Note that this function will648/// return after an unsuccessful build of the preamble too, i.e. result of649/// getPossiblyStalePreamble() can be null even after this function returns.650void waitForFirstPreamble() const;651
652TUScheduler::FileStats stats() const;653bool isASTCached() const;654
655private:656// Details of an update request that are relevant to scheduling.657struct UpdateType {658// Do we want diagnostics from this version?659// If Yes, we must always build this version.660// If No, we only need to build this version if it's read.661// If Auto, we build if it's read or if the debounce expires.662WantDiagnostics Diagnostics;663// Did the main-file content of the document change?664// If so, we're allowed to cancel certain invalidated preceding reads.665bool ContentChanged;666};667
668/// Publishes diagnostics for \p Inputs. It will build an AST or reuse the669/// cached one if applicable. Assumes LatestPreamble is compatible for \p670/// Inputs.671void generateDiagnostics(std::unique_ptr<CompilerInvocation> Invocation,672ParseInputs Inputs, std::vector<Diag> CIDiags);673
674void updateASTSignals(ParsedAST &AST);675
676// Must be called exactly once on processing thread. Will return after677// stop() is called on a separate thread and all pending requests are678// processed.679void run();680/// Signal that run() should finish processing pending requests and exit.681void stop();682
683/// Adds a new task to the end of the request queue.684void startTask(llvm::StringRef Name, llvm::unique_function<void()> Task,685std::optional<UpdateType> Update,686TUScheduler::ASTActionInvalidation);687/// Runs a task synchronously.688void runTask(llvm::StringRef Name, llvm::function_ref<void()> Task);689
690/// Determines the next action to perform.691/// All actions that should never run are discarded.692/// Returns a deadline for the next action. If it's expired, run now.693/// scheduleLocked() is called again at the deadline, or if requests arrive.694Deadline scheduleLocked();695/// Should the first task in the queue be skipped instead of run?696bool shouldSkipHeadLocked() const;697
698struct Request {699llvm::unique_function<void()> Action;700std::string Name;701steady_clock::time_point AddTime;702Context Ctx;703std::optional<Context> QueueCtx;704std::optional<UpdateType> Update;705TUScheduler::ASTActionInvalidation InvalidationPolicy;706Canceler Invalidate;707};708
709/// Handles retention of ASTs.710TUScheduler::ASTCache &IdleASTs;711TUScheduler::HeaderIncluderCache &HeaderIncluders;712const bool RunSync;713/// Time to wait after an update to see whether another update obsoletes it.714const DebouncePolicy UpdateDebounce;715/// File that ASTWorker is responsible for.716const Path FileName;717/// Callback to create processing contexts for tasks.718const std::function<Context(llvm::StringRef)> ContextProvider;719const GlobalCompilationDatabase &CDB;720/// Callback invoked when preamble or main file AST is built.721ParsingCallbacks &Callbacks;722
723Semaphore &Barrier;724/// Whether the 'onMainAST' callback ran for the current FileInputs.725bool RanASTCallback = false;726/// Guards members used by both TUScheduler and the worker thread.727mutable std::mutex Mutex;728/// File inputs, currently being used by the worker.729/// Writes and reads from unknown threads are locked. Reads from the worker730/// thread are not locked, as it's the only writer.731ParseInputs FileInputs; /* GUARDED_BY(Mutex) */732/// Times of recent AST rebuilds, used for UpdateDebounce computation.733llvm::SmallVector<DebouncePolicy::clock::duration>734RebuildTimes; /* GUARDED_BY(Mutex) */735/// Set to true to signal run() to finish processing.736bool Done; /* GUARDED_BY(Mutex) */737std::deque<Request> Requests; /* GUARDED_BY(Mutex) */738std::optional<Request> CurrentRequest; /* GUARDED_BY(Mutex) */739/// Signalled whenever a new request has been scheduled or processing of a740/// request has completed.741mutable std::condition_variable RequestsCV;742std::shared_ptr<const ASTSignals> LatestASTSignals; /* GUARDED_BY(Mutex) */743/// Latest build preamble for current TU.744/// std::nullopt means no builds yet, null means there was an error while745/// building. Only written by ASTWorker's thread.746std::optional<std::shared_ptr<const PreambleData>> LatestPreamble;747std::deque<Request> PreambleRequests; /* GUARDED_BY(Mutex) */748/// Signaled whenever LatestPreamble changes state or there's a new749/// PreambleRequest.750mutable std::condition_variable PreambleCV;751/// Guards the callback that publishes results of AST-related computations752/// (diagnostics) and file statuses.753std::mutex PublishMu;754// Used to prevent remove document + add document races that lead to755// out-of-order callbacks for publishing results of onMainAST callback.756//757// The lifetime of the old/new ASTWorkers will overlap, but their handles758// don't. When the old handle is destroyed, the old worker will stop reporting759// any results to the user.760bool CanPublishResults = true; /* GUARDED_BY(PublishMu) */761std::atomic<unsigned> ASTBuildCount = {0};762std::atomic<unsigned> PreambleBuildCount = {0};763
764SynchronizedTUStatus Status;765PreambleThread PreamblePeer;766};767
768/// A smart-pointer-like class that points to an active ASTWorker.
769/// In destructor, signals to the underlying ASTWorker that no new requests will
770/// be sent and the processing loop may exit (after running all pending
771/// requests).
772class ASTWorkerHandle {773friend class ASTWorker;774ASTWorkerHandle(std::shared_ptr<ASTWorker> Worker)775: Worker(std::move(Worker)) {776assert(this->Worker);777}778
779public:780ASTWorkerHandle(const ASTWorkerHandle &) = delete;781ASTWorkerHandle &operator=(const ASTWorkerHandle &) = delete;782ASTWorkerHandle(ASTWorkerHandle &&) = default;783ASTWorkerHandle &operator=(ASTWorkerHandle &&) = default;784
785~ASTWorkerHandle() {786if (Worker)787Worker->stop();788}789
790ASTWorker &operator*() {791assert(Worker && "Handle was moved from");792return *Worker;793}794
795ASTWorker *operator->() {796assert(Worker && "Handle was moved from");797return Worker.get();798}799
800/// Returns an owning reference to the underlying ASTWorker that can outlive801/// the ASTWorkerHandle. However, no new requests to an active ASTWorker can802/// be schedule via the returned reference, i.e. only reads of the preamble803/// are possible.804std::shared_ptr<const ASTWorker> lock() { return Worker; }805
806private:807std::shared_ptr<ASTWorker> Worker;808};809
810ASTWorkerHandle
811ASTWorker::create(PathRef FileName, const GlobalCompilationDatabase &CDB,812TUScheduler::ASTCache &IdleASTs,813TUScheduler::HeaderIncluderCache &HeaderIncluders,814AsyncTaskRunner *Tasks, Semaphore &Barrier,815const TUScheduler::Options &Opts,816ParsingCallbacks &Callbacks) {817std::shared_ptr<ASTWorker> Worker(818new ASTWorker(FileName, CDB, IdleASTs, HeaderIncluders, Barrier,819/*RunSync=*/!Tasks, Opts, Callbacks));820if (Tasks) {821Tasks->runAsync("ASTWorker:" + llvm::sys::path::filename(FileName),822[Worker]() { Worker->run(); });823Tasks->runAsync("PreambleWorker:" + llvm::sys::path::filename(FileName),824[Worker]() { Worker->PreamblePeer.run(); });825}826
827return ASTWorkerHandle(std::move(Worker));828}
829
830ASTWorker::ASTWorker(PathRef FileName, const GlobalCompilationDatabase &CDB,831TUScheduler::ASTCache &LRUCache,832TUScheduler::HeaderIncluderCache &HeaderIncluders,833Semaphore &Barrier, bool RunSync,834const TUScheduler::Options &Opts,835ParsingCallbacks &Callbacks)836: IdleASTs(LRUCache), HeaderIncluders(HeaderIncluders), RunSync(RunSync),837UpdateDebounce(Opts.UpdateDebounce), FileName(FileName),838ContextProvider(Opts.ContextProvider), CDB(CDB), Callbacks(Callbacks),839Barrier(Barrier), Done(false), Status(FileName, Callbacks),840PreamblePeer(FileName, Callbacks, Opts.StorePreamblesInMemory, RunSync,841Opts.PreambleThrottler, Status, HeaderIncluders, *this) {842// Set a fallback command because compile command can be accessed before843// `Inputs` is initialized. Other fields are only used after initialization844// from client inputs.845FileInputs.CompileCommand = CDB.getFallbackCommand(FileName);846}
847
848ASTWorker::~ASTWorker() {849// Make sure we remove the cached AST, if any.850IdleASTs.take(this);851#ifndef NDEBUG852std::lock_guard<std::mutex> Lock(Mutex);853assert(Done && "handle was not destroyed");854assert(Requests.empty() && !CurrentRequest &&855"unprocessed requests when destroying ASTWorker");856#endif857}
858
859void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags,860bool ContentChanged) {861llvm::StringLiteral TaskName = "Update";862auto Task = [=]() mutable {863// Get the actual command as `Inputs` does not have a command.864// FIXME: some build systems like Bazel will take time to preparing865// environment to build the file, it would be nice if we could emit a866// "PreparingBuild" status to inform users, it is non-trivial given the867// current implementation.868auto Cmd = CDB.getCompileCommand(FileName);869// If we don't have a reliable command for this file, it may be a header.870// Try to find a file that includes it, to borrow its command.871if (!Cmd || !isReliable(*Cmd)) {872std::string ProxyFile = HeaderIncluders.get(FileName);873if (!ProxyFile.empty()) {874auto ProxyCmd = CDB.getCompileCommand(ProxyFile);875if (!ProxyCmd || !isReliable(*ProxyCmd)) {876// This command is supposed to be reliable! It's probably gone.877HeaderIncluders.remove(ProxyFile);878} else {879// We have a reliable command for an including file, use it.880Cmd = tooling::transferCompileCommand(std::move(*ProxyCmd), FileName);881}882}883}884if (Cmd)885Inputs.CompileCommand = std::move(*Cmd);886else887Inputs.CompileCommand = CDB.getFallbackCommand(FileName);888
889bool InputsAreTheSame =890std::tie(FileInputs.CompileCommand, FileInputs.Contents) ==891std::tie(Inputs.CompileCommand, Inputs.Contents);892// Cached AST is invalidated.893if (!InputsAreTheSame) {894IdleASTs.take(this);895RanASTCallback = false;896}897
898// Update current inputs so that subsequent reads can see them.899{900std::lock_guard<std::mutex> Lock(Mutex);901FileInputs = Inputs;902}903
904log("ASTWorker building file {0} version {1} with command {2}\n[{3}]\n{4}",905FileName, Inputs.Version, Inputs.CompileCommand.Heuristic,906Inputs.CompileCommand.Directory,907printArgv(Inputs.CompileCommand.CommandLine));908
909StoreDiags CompilerInvocationDiagConsumer;910std::vector<std::string> CC1Args;911std::unique_ptr<CompilerInvocation> Invocation = buildCompilerInvocation(912Inputs, CompilerInvocationDiagConsumer, &CC1Args);913// Log cc1 args even (especially!) if creating invocation failed.914if (!CC1Args.empty())915vlog("Driver produced command: cc1 {0}", printArgv(CC1Args));916std::vector<Diag> CompilerInvocationDiags =917CompilerInvocationDiagConsumer.take();918if (!Invocation) {919elog("Could not build CompilerInvocation for file {0}", FileName);920// Remove the old AST if it's still in cache.921IdleASTs.take(this);922RanASTCallback = false;923// Report the diagnostics we collected when parsing the command line.924Callbacks.onFailedAST(FileName, Inputs.Version,925std::move(CompilerInvocationDiags),926[&](llvm::function_ref<void()> Publish) {927// Ensure we only publish results from the worker928// if the file was not removed, making sure there929// are not race conditions.930std::lock_guard<std::mutex> Lock(PublishMu);931if (CanPublishResults)932Publish();933});934// Note that this might throw away a stale preamble that might still be935// useful, but this is how we communicate a build error.936LatestPreamble.emplace();937// Make sure anyone waiting for the preamble gets notified it could not be938// built.939PreambleCV.notify_all();940return;941}942
943// Inform preamble peer, before attempting to build diagnostics so that they944// can be built concurrently.945PreamblePeer.update(std::make_unique<CompilerInvocation>(*Invocation),946Inputs, CompilerInvocationDiags, WantDiags);947
948// Emit diagnostics from (possibly) stale preamble while waiting for a949// rebuild. Newly built preamble cannot emit diagnostics before this call950// finishes (ast callbacks are called from astpeer thread), hence we951// guarantee eventual consistency.952if (LatestPreamble && WantDiags != WantDiagnostics::No)953generateDiagnostics(std::move(Invocation), std::move(Inputs),954std::move(CompilerInvocationDiags));955
956std::unique_lock<std::mutex> Lock(Mutex);957PreambleCV.wait(Lock, [this] {958// Block until we reiceve a preamble request, unless a preamble already959// exists, as patching an empty preamble would imply rebuilding it from960// scratch.961// We block here instead of the consumer to prevent any deadlocks. Since962// LatestPreamble is only populated by ASTWorker thread.963return LatestPreamble || !PreambleRequests.empty() || Done;964});965};966startTask(TaskName, std::move(Task), UpdateType{WantDiags, ContentChanged},967TUScheduler::NoInvalidation);968}
969
970void ASTWorker::runWithAST(971llvm::StringRef Name,972llvm::unique_function<void(llvm::Expected<InputsAndAST>)> Action,973TUScheduler::ASTActionInvalidation Invalidation) {974// Tracks ast cache accesses for read operations.975static constexpr trace::Metric ASTAccessForRead(976"ast_access_read", trace::Metric::Counter, "result");977auto Task = [=, Action = std::move(Action)]() mutable {978if (auto Reason = isCancelled())979return Action(llvm::make_error<CancelledError>(Reason));980std::optional<std::unique_ptr<ParsedAST>> AST =981IdleASTs.take(this, &ASTAccessForRead);982if (!AST) {983StoreDiags CompilerInvocationDiagConsumer;984std::unique_ptr<CompilerInvocation> Invocation =985buildCompilerInvocation(FileInputs, CompilerInvocationDiagConsumer);986// Try rebuilding the AST.987vlog("ASTWorker rebuilding evicted AST to run {0}: {1} version {2}", Name,988FileName, FileInputs.Version);989// FIXME: We might need to build a patched ast once preamble thread starts990// running async. Currently getPossiblyStalePreamble below will always991// return a compatible preamble as ASTWorker::update blocks.992std::optional<ParsedAST> NewAST;993if (Invocation) {994NewAST = ParsedAST::build(FileName, FileInputs, std::move(Invocation),995CompilerInvocationDiagConsumer.take(),996getPossiblyStalePreamble());997++ASTBuildCount;998}999AST = NewAST ? std::make_unique<ParsedAST>(std::move(*NewAST)) : nullptr;1000}1001// Make sure we put the AST back into the LRU cache.1002auto _ = llvm::make_scope_exit(1003[&AST, this]() { IdleASTs.put(this, std::move(*AST)); });1004// Run the user-provided action.1005if (!*AST)1006return Action(error(llvm::errc::invalid_argument, "invalid AST"));1007vlog("ASTWorker running {0} on version {2} of {1}", Name, FileName,1008FileInputs.Version);1009Action(InputsAndAST{FileInputs, **AST});1010};1011startTask(Name, std::move(Task), /*Update=*/std::nullopt, Invalidation);1012}
1013
1014/// To be called from ThreadCrashReporter's signal handler.
1015static void crashDumpCompileCommand(llvm::raw_ostream &OS,1016const tooling::CompileCommand &Command) {1017OS << " Filename: " << Command.Filename << "\n";1018OS << " Directory: " << Command.Directory << "\n";1019OS << " Command Line:";1020for (auto &Arg : Command.CommandLine) {1021OS << " " << Arg;1022}1023OS << "\n";1024}
1025
1026/// To be called from ThreadCrashReporter's signal handler.
1027static void crashDumpFileContents(llvm::raw_ostream &OS,1028const std::string &Contents) {1029// Avoid flooding the terminal with source code by default, but allow clients1030// to opt in. Use an env var to preserve backwards compatibility of the1031// command line interface, while allowing it to be set outside the clangd1032// launch site for more flexibility.1033if (getenv("CLANGD_CRASH_DUMP_SOURCE")) {1034OS << " Contents:\n";1035OS << Contents << "\n";1036}1037}
1038
1039/// To be called from ThreadCrashReporter's signal handler.
1040static void crashDumpParseInputs(llvm::raw_ostream &OS,1041const ParseInputs &FileInputs) {1042auto &Command = FileInputs.CompileCommand;1043crashDumpCompileCommand(OS, Command);1044OS << " Version: " << FileInputs.Version << "\n";1045crashDumpFileContents(OS, FileInputs.Contents);1046}
1047
1048void PreambleThread::build(Request Req) {1049assert(Req.CI && "Got preamble request with null compiler invocation");1050const ParseInputs &Inputs = Req.Inputs;1051bool ReusedPreamble = false;1052
1053Status.update([&](TUStatus &Status) {1054Status.PreambleActivity = PreambleAction::Building;1055});1056auto _ = llvm::make_scope_exit([this, &Req, &ReusedPreamble] {1057ASTPeer.updatePreamble(std::move(Req.CI), std::move(Req.Inputs),1058LatestBuild, std::move(Req.CIDiags),1059std::move(Req.WantDiags));1060if (!ReusedPreamble)1061Callbacks.onPreamblePublished(FileName);1062});1063
1064if (!LatestBuild || Inputs.ForceRebuild) {1065vlog("Building first preamble for {0} version {1}", FileName,1066Inputs.Version);1067} else if (isPreambleCompatible(*LatestBuild, Inputs, FileName, *Req.CI)) {1068vlog("Reusing preamble version {0} for version {1} of {2}",1069LatestBuild->Version, Inputs.Version, FileName);1070ReusedPreamble = true;1071return;1072} else {1073vlog("Rebuilding invalidated preamble for {0} version {1} (previous was "1074"version {2})",1075FileName, Inputs.Version, LatestBuild->Version);1076}1077
1078ThreadCrashReporter ScopedReporter([&Inputs]() {1079llvm::errs() << "Signalled while building preamble\n";1080crashDumpParseInputs(llvm::errs(), Inputs);1081});1082
1083PreambleBuildStats Stats;1084bool IsFirstPreamble = !LatestBuild;1085LatestBuild = clang::clangd::buildPreamble(1086FileName, *Req.CI, Inputs, StoreInMemory,1087[&](CapturedASTCtx ASTCtx,1088std::shared_ptr<const include_cleaner::PragmaIncludes> PI) {1089Callbacks.onPreambleAST(FileName, Inputs.Version, std::move(ASTCtx),1090std::move(PI));1091},1092&Stats);1093if (!LatestBuild)1094return;1095reportPreambleBuild(Stats, IsFirstPreamble);1096if (isReliable(LatestBuild->CompileCommand))1097HeaderIncluders.update(FileName, LatestBuild->Includes.allHeaders());1098}
1099
1100void ASTWorker::updatePreamble(std::unique_ptr<CompilerInvocation> CI,1101ParseInputs PI,1102std::shared_ptr<const PreambleData> Preamble,1103std::vector<Diag> CIDiags,1104WantDiagnostics WantDiags) {1105llvm::StringLiteral TaskName = "Build AST";1106// Store preamble and build diagnostics with new preamble if requested.1107auto Task = [this, Preamble = std::move(Preamble), CI = std::move(CI),1108CIDiags = std::move(CIDiags),1109WantDiags = std::move(WantDiags)]() mutable {1110// Update the preamble inside ASTWorker queue to ensure atomicity. As a task1111// running inside ASTWorker assumes internals won't change until it1112// finishes.1113if (!LatestPreamble || Preamble != *LatestPreamble) {1114++PreambleBuildCount;1115// Cached AST is no longer valid.1116IdleASTs.take(this);1117RanASTCallback = false;1118std::lock_guard<std::mutex> Lock(Mutex);1119// LatestPreamble might be the last reference to old preamble, do not1120// trigger destructor while holding the lock.1121if (LatestPreamble)1122std::swap(*LatestPreamble, Preamble);1123else1124LatestPreamble = std::move(Preamble);1125}1126// Notify anyone waiting for a preamble.1127PreambleCV.notify_all();1128// Give up our ownership to old preamble before starting expensive AST1129// build.1130Preamble.reset();1131// We only need to build the AST if diagnostics were requested.1132if (WantDiags == WantDiagnostics::No)1133return;1134// Since the file may have been edited since we started building this1135// preamble, we use the current contents of the file instead. This provides1136// more up-to-date diagnostics, and avoids diagnostics going backwards (we1137// may have already emitted staler-preamble diagnostics for the new1138// version).1139// We still have eventual consistency: at some point updatePreamble() will1140// catch up to the current file.1141// Report diagnostics with the new preamble to ensure progress. Otherwise1142// diagnostics might get stale indefinitely if user keeps invalidating the1143// preamble.1144generateDiagnostics(std::move(CI), FileInputs, std::move(CIDiags));1145};1146if (RunSync) {1147runTask(TaskName, Task);1148return;1149}1150{1151std::lock_guard<std::mutex> Lock(Mutex);1152PreambleRequests.push_back({std::move(Task), std::string(TaskName),1153steady_clock::now(), Context::current().clone(),1154std::nullopt, std::nullopt,1155TUScheduler::NoInvalidation, nullptr});1156}1157PreambleCV.notify_all();1158RequestsCV.notify_all();1159}
1160
1161void ASTWorker::updateASTSignals(ParsedAST &AST) {1162auto Signals = std::make_shared<const ASTSignals>(ASTSignals::derive(AST));1163// Existing readers of ASTSignals will have their copy preserved until the1164// read is completed. The last reader deletes the old ASTSignals.1165{1166std::lock_guard<std::mutex> Lock(Mutex);1167std::swap(LatestASTSignals, Signals);1168}1169}
1170
1171void ASTWorker::generateDiagnostics(1172std::unique_ptr<CompilerInvocation> Invocation, ParseInputs Inputs,1173std::vector<Diag> CIDiags) {1174// Tracks ast cache accesses for publishing diags.1175static constexpr trace::Metric ASTAccessForDiag(1176"ast_access_diag", trace::Metric::Counter, "result");1177assert(Invocation);1178assert(LatestPreamble);1179// No need to rebuild the AST if we won't send the diagnostics.1180{1181std::lock_guard<std::mutex> Lock(PublishMu);1182if (!CanPublishResults)1183return;1184}1185// Used to check whether we can update AST cache.1186bool InputsAreLatest =1187std::tie(FileInputs.CompileCommand, FileInputs.Contents) ==1188std::tie(Inputs.CompileCommand, Inputs.Contents);1189// Take a shortcut and don't report the diagnostics, since they should be the1190// same. All the clients should handle the lack of OnUpdated() call anyway to1191// handle empty result from buildAST.1192// FIXME: the AST could actually change if non-preamble includes changed,1193// but we choose to ignore it.1194if (InputsAreLatest && RanASTCallback)1195return;1196
1197// Get the AST for diagnostics, either build it or use the cached one.1198std::string TaskName = llvm::formatv("Build AST ({0})", Inputs.Version);1199Status.update([&](TUStatus &Status) {1200Status.ASTActivity.K = ASTAction::Building;1201Status.ASTActivity.Name = std::move(TaskName);1202});1203// We might be able to reuse the last we've built for a read request.1204// FIXME: It might be better to not reuse this AST. That way queued AST builds1205// won't be required for diags.1206std::optional<std::unique_ptr<ParsedAST>> AST =1207IdleASTs.take(this, &ASTAccessForDiag);1208if (!AST || !InputsAreLatest) {1209auto RebuildStartTime = DebouncePolicy::clock::now();1210std::optional<ParsedAST> NewAST = ParsedAST::build(1211FileName, Inputs, std::move(Invocation), CIDiags, *LatestPreamble);1212auto RebuildDuration = DebouncePolicy::clock::now() - RebuildStartTime;1213++ASTBuildCount;1214// Try to record the AST-build time, to inform future update debouncing.1215// This is best-effort only: if the lock is held, don't bother.1216std::unique_lock<std::mutex> Lock(Mutex, std::try_to_lock);1217if (Lock.owns_lock()) {1218// Do not let RebuildTimes grow beyond its small-size (i.e.1219// capacity).1220if (RebuildTimes.size() == RebuildTimes.capacity())1221RebuildTimes.erase(RebuildTimes.begin());1222RebuildTimes.push_back(RebuildDuration);1223Lock.unlock();1224}1225Status.update([&](TUStatus &Status) {1226Status.Details.ReuseAST = false;1227Status.Details.BuildFailed = !NewAST;1228});1229AST = NewAST ? std::make_unique<ParsedAST>(std::move(*NewAST)) : nullptr;1230} else {1231log("Skipping rebuild of the AST for {0}, inputs are the same.", FileName);1232Status.update([](TUStatus &Status) {1233Status.Details.ReuseAST = true;1234Status.Details.BuildFailed = false;1235});1236}1237
1238// Publish diagnostics.1239auto RunPublish = [&](llvm::function_ref<void()> Publish) {1240// Ensure we only publish results from the worker if the file was not1241// removed, making sure there are not race conditions.1242std::lock_guard<std::mutex> Lock(PublishMu);1243if (CanPublishResults)1244Publish();1245};1246if (*AST) {1247trace::Span Span("Running main AST callback");1248Callbacks.onMainAST(FileName, **AST, RunPublish);1249updateASTSignals(**AST);1250} else {1251// Failed to build the AST, at least report diagnostics from the1252// command line if there were any.1253// FIXME: we might have got more errors while trying to build the1254// AST, surface them too.1255Callbacks.onFailedAST(FileName, Inputs.Version, CIDiags, RunPublish);1256}1257
1258// AST might've been built for an older version of the source, as ASTWorker1259// queue raced ahead while we were waiting on the preamble. In that case the1260// queue can't reuse the AST.1261if (InputsAreLatest) {1262RanASTCallback = *AST != nullptr;1263IdleASTs.put(this, std::move(*AST));1264}1265}
1266
1267std::shared_ptr<const PreambleData> ASTWorker::getPossiblyStalePreamble(1268std::shared_ptr<const ASTSignals> *ASTSignals) const {1269std::lock_guard<std::mutex> Lock(Mutex);1270if (ASTSignals)1271*ASTSignals = LatestASTSignals;1272return LatestPreamble ? *LatestPreamble : nullptr;1273}
1274
1275void ASTWorker::waitForFirstPreamble() const {1276std::unique_lock<std::mutex> Lock(Mutex);1277PreambleCV.wait(Lock, [this] { return LatestPreamble || Done; });1278}
1279
1280tooling::CompileCommand ASTWorker::getCurrentCompileCommand() const {1281std::unique_lock<std::mutex> Lock(Mutex);1282return FileInputs.CompileCommand;1283}
1284
1285TUScheduler::FileStats ASTWorker::stats() const {1286TUScheduler::FileStats Result;1287Result.ASTBuilds = ASTBuildCount;1288Result.PreambleBuilds = PreambleBuildCount;1289// Note that we don't report the size of ASTs currently used for processing1290// the in-flight requests. We used this information for debugging purposes1291// only, so this should be fine.1292Result.UsedBytesAST = IdleASTs.getUsedBytes(this);1293if (auto Preamble = getPossiblyStalePreamble())1294Result.UsedBytesPreamble = Preamble->Preamble.getSize();1295return Result;1296}
1297
1298bool ASTWorker::isASTCached() const { return IdleASTs.getUsedBytes(this) != 0; }1299
1300void ASTWorker::stop() {1301{1302std::lock_guard<std::mutex> Lock(PublishMu);1303CanPublishResults = false;1304}1305{1306std::lock_guard<std::mutex> Lock(Mutex);1307assert(!Done && "stop() called twice");1308Done = true;1309}1310PreamblePeer.stop();1311// We are no longer going to build any preambles, let the waiters know that.1312PreambleCV.notify_all();1313Status.stop();1314RequestsCV.notify_all();1315}
1316
1317void ASTWorker::runTask(llvm::StringRef Name, llvm::function_ref<void()> Task) {1318ThreadCrashReporter ScopedReporter([this, Name]() {1319llvm::errs() << "Signalled during AST worker action: " << Name << "\n";1320crashDumpParseInputs(llvm::errs(), FileInputs);1321});1322trace::Span Tracer(Name);1323WithContext WithProvidedContext(ContextProvider(FileName));1324Task();1325}
1326
1327void ASTWorker::startTask(llvm::StringRef Name,1328llvm::unique_function<void()> Task,1329std::optional<UpdateType> Update,1330TUScheduler::ASTActionInvalidation Invalidation) {1331if (RunSync) {1332assert(!Done && "running a task after stop()");1333runTask(Name, Task);1334return;1335}1336
1337{1338std::lock_guard<std::mutex> Lock(Mutex);1339assert(!Done && "running a task after stop()");1340// Cancel any requests invalidated by this request.1341if (Update && Update->ContentChanged) {1342for (auto &R : llvm::reverse(Requests)) {1343if (R.InvalidationPolicy == TUScheduler::InvalidateOnUpdate)1344R.Invalidate();1345if (R.Update && R.Update->ContentChanged)1346break; // Older requests were already invalidated by the older update.1347}1348}1349
1350// Allow this request to be cancelled if invalidated.1351Context Ctx = Context::current().derive(FileBeingProcessed, FileName);1352Canceler Invalidate = nullptr;1353if (Invalidation) {1354WithContext WC(std::move(Ctx));1355std::tie(Ctx, Invalidate) = cancelableTask(1356/*Reason=*/static_cast<int>(ErrorCode::ContentModified));1357}1358// Trace the time the request spends in the queue, and the requests that1359// it's going to wait for.1360std::optional<Context> QueueCtx;1361if (trace::enabled()) {1362// Tracers that follow threads and need strict nesting will see a tiny1363// instantaneous event "we're enqueueing", and sometime later it runs.1364WithContext WC(Ctx.clone());1365trace::Span Tracer("Queued:" + Name);1366if (Tracer.Args) {1367if (CurrentRequest)1368SPAN_ATTACH(Tracer, "CurrentRequest", CurrentRequest->Name);1369llvm::json::Array PreambleRequestsNames;1370for (const auto &Req : PreambleRequests)1371PreambleRequestsNames.push_back(Req.Name);1372SPAN_ATTACH(Tracer, "PreambleRequestsNames",1373std::move(PreambleRequestsNames));1374llvm::json::Array RequestsNames;1375for (const auto &Req : Requests)1376RequestsNames.push_back(Req.Name);1377SPAN_ATTACH(Tracer, "RequestsNames", std::move(RequestsNames));1378}1379// For tracers that follow contexts, keep the trace span's context alive1380// until we dequeue the request, so they see the full duration.1381QueueCtx = Context::current().clone();1382}1383Requests.push_back({std::move(Task), std::string(Name), steady_clock::now(),1384std::move(Ctx), std::move(QueueCtx), Update,1385Invalidation, std::move(Invalidate)});1386}1387RequestsCV.notify_all();1388}
1389
1390void ASTWorker::run() {1391clang::noteBottomOfStack();1392while (true) {1393{1394std::unique_lock<std::mutex> Lock(Mutex);1395assert(!CurrentRequest && "A task is already running, multiple workers?");1396for (auto Wait = scheduleLocked(); !Wait.expired();1397Wait = scheduleLocked()) {1398assert(PreambleRequests.empty() &&1399"Preamble updates should be scheduled immediately");1400if (Done) {1401if (Requests.empty())1402return;1403// Even though Done is set, finish pending requests.1404break; // However, skip delays to shutdown fast.1405}1406
1407// Tracing: we have a next request, attribute this sleep to it.1408std::optional<WithContext> Ctx;1409std::optional<trace::Span> Tracer;1410if (!Requests.empty()) {1411Ctx.emplace(Requests.front().Ctx.clone());1412Tracer.emplace("Debounce");1413SPAN_ATTACH(*Tracer, "next_request", Requests.front().Name);1414if (!(Wait == Deadline::infinity())) {1415Status.update([&](TUStatus &Status) {1416Status.ASTActivity.K = ASTAction::Queued;1417Status.ASTActivity.Name = Requests.front().Name;1418});1419SPAN_ATTACH(*Tracer, "sleep_ms",1420std::chrono::duration_cast<std::chrono::milliseconds>(1421Wait.time() - steady_clock::now())1422.count());1423}1424}1425
1426wait(Lock, RequestsCV, Wait);1427}1428// Any request in ReceivedPreambles is at least as old as the1429// Requests.front(), so prefer them first to preserve LSP order.1430if (!PreambleRequests.empty()) {1431CurrentRequest = std::move(PreambleRequests.front());1432PreambleRequests.pop_front();1433} else {1434CurrentRequest = std::move(Requests.front());1435Requests.pop_front();1436}1437} // unlock Mutex1438
1439// Inform tracing that the request was dequeued.1440CurrentRequest->QueueCtx.reset();1441
1442// It is safe to perform reads to CurrentRequest without holding the lock as1443// only writer is also this thread.1444{1445std::unique_lock<Semaphore> Lock(Barrier, std::try_to_lock);1446if (!Lock.owns_lock()) {1447Status.update([&](TUStatus &Status) {1448Status.ASTActivity.K = ASTAction::Queued;1449Status.ASTActivity.Name = CurrentRequest->Name;1450});1451Lock.lock();1452}1453WithContext Guard(std::move(CurrentRequest->Ctx));1454Status.update([&](TUStatus &Status) {1455Status.ASTActivity.K = ASTAction::RunningAction;1456Status.ASTActivity.Name = CurrentRequest->Name;1457});1458runTask(CurrentRequest->Name, CurrentRequest->Action);1459}1460
1461bool IsEmpty = false;1462{1463std::lock_guard<std::mutex> Lock(Mutex);1464CurrentRequest.reset();1465IsEmpty = Requests.empty() && PreambleRequests.empty();1466}1467if (IsEmpty) {1468Status.update([&](TUStatus &Status) {1469Status.ASTActivity.K = ASTAction::Idle;1470Status.ASTActivity.Name = "";1471});1472}1473RequestsCV.notify_all();1474}1475}
1476
1477Deadline ASTWorker::scheduleLocked() {1478// Process new preambles immediately.1479if (!PreambleRequests.empty())1480return Deadline::zero();1481if (Requests.empty())1482return Deadline::infinity(); // Wait for new requests.1483// Handle cancelled requests first so the rest of the scheduler doesn't.1484for (auto I = Requests.begin(), E = Requests.end(); I != E; ++I) {1485if (!isCancelled(I->Ctx)) {1486// Cancellations after the first read don't affect current scheduling.1487if (I->Update == std::nullopt)1488break;1489continue;1490}1491// Cancelled reads are moved to the front of the queue and run immediately.1492if (I->Update == std::nullopt) {1493Request R = std::move(*I);1494Requests.erase(I);1495Requests.push_front(std::move(R));1496return Deadline::zero();1497}1498// Cancelled updates are downgraded to auto-diagnostics, and may be elided.1499if (I->Update->Diagnostics == WantDiagnostics::Yes)1500I->Update->Diagnostics = WantDiagnostics::Auto;1501}1502
1503while (shouldSkipHeadLocked()) {1504vlog("ASTWorker skipping {0} for {1}", Requests.front().Name, FileName);1505Requests.pop_front();1506}1507assert(!Requests.empty() && "skipped the whole queue");1508// Some updates aren't dead yet, but never end up being used.1509// e.g. the first keystroke is live until obsoleted by the second.1510// We debounce "maybe-unused" writes, sleeping in case they become dead.1511// But don't delay reads (including updates where diagnostics are needed).1512for (const auto &R : Requests)1513if (R.Update == std::nullopt ||1514R.Update->Diagnostics == WantDiagnostics::Yes)1515return Deadline::zero();1516// Front request needs to be debounced, so determine when we're ready.1517Deadline D(Requests.front().AddTime + UpdateDebounce.compute(RebuildTimes));1518return D;1519}
1520
1521// Returns true if Requests.front() is a dead update that can be skipped.
1522bool ASTWorker::shouldSkipHeadLocked() const {1523assert(!Requests.empty());1524auto Next = Requests.begin();1525auto Update = Next->Update;1526if (!Update) // Only skip updates.1527return false;1528++Next;1529// An update is live if its AST might still be read.1530// That is, if it's not immediately followed by another update.1531if (Next == Requests.end() || !Next->Update)1532return false;1533// The other way an update can be live is if its diagnostics might be used.1534switch (Update->Diagnostics) {1535case WantDiagnostics::Yes:1536return false; // Always used.1537case WantDiagnostics::No:1538return true; // Always dead.1539case WantDiagnostics::Auto:1540// Used unless followed by an update that generates diagnostics.1541for (; Next != Requests.end(); ++Next)1542if (Next->Update && Next->Update->Diagnostics != WantDiagnostics::No)1543return true; // Prefer later diagnostics.1544return false;1545}1546llvm_unreachable("Unknown WantDiagnostics");1547}
1548
1549bool ASTWorker::blockUntilIdle(Deadline Timeout) const {1550auto WaitUntilASTWorkerIsIdle = [&] {1551std::unique_lock<std::mutex> Lock(Mutex);1552return wait(Lock, RequestsCV, Timeout, [&] {1553return PreambleRequests.empty() && Requests.empty() && !CurrentRequest;1554});1555};1556// Make sure ASTWorker has processed all requests, which might issue new1557// updates to PreamblePeer.1558if (!WaitUntilASTWorkerIsIdle())1559return false;1560// Now that ASTWorker processed all requests, ensure PreamblePeer has served1561// all update requests. This might create new PreambleRequests for the1562// ASTWorker.1563if (!PreamblePeer.blockUntilIdle(Timeout))1564return false;1565assert(Requests.empty() &&1566"No new normal tasks can be scheduled concurrently with "1567"blockUntilIdle(): ASTWorker isn't threadsafe");1568// Finally make sure ASTWorker has processed all of the preamble updates.1569return WaitUntilASTWorkerIsIdle();1570}
1571
1572// Render a TUAction to a user-facing string representation.
1573// TUAction represents clangd-internal states, we don't intend to expose them
1574// to users (say C++ programmers) directly to avoid confusion, we use terms that
1575// are familiar by C++ programmers.
1576std::string renderTUAction(const PreambleAction PA, const ASTAction &AA) {1577llvm::SmallVector<std::string, 2> Result;1578switch (PA) {1579case PreambleAction::Building:1580Result.push_back("parsing includes");1581break;1582case PreambleAction::Queued:1583Result.push_back("includes are queued");1584break;1585case PreambleAction::Idle:1586// We handle idle specially below.1587break;1588}1589switch (AA.K) {1590case ASTAction::Queued:1591Result.push_back("file is queued");1592break;1593case ASTAction::RunningAction:1594Result.push_back("running " + AA.Name);1595break;1596case ASTAction::Building:1597Result.push_back("parsing main file");1598break;1599case ASTAction::Idle:1600// We handle idle specially below.1601break;1602}1603if (Result.empty())1604return "idle";1605return llvm::join(Result, ", ");1606}
1607
1608} // namespace1609
1610unsigned getDefaultAsyncThreadsCount() {1611return llvm::heavyweight_hardware_concurrency().compute_thread_count();1612}
1613
1614FileStatus TUStatus::render(PathRef File) const {1615FileStatus FStatus;1616FStatus.uri = URIForFile::canonicalize(File, /*TUPath=*/File);1617FStatus.state = renderTUAction(PreambleActivity, ASTActivity);1618return FStatus;1619}
1620
1621struct TUScheduler::FileData {1622/// Latest inputs, passed to TUScheduler::update().1623std::string Contents;1624ASTWorkerHandle Worker;1625};1626
1627TUScheduler::TUScheduler(const GlobalCompilationDatabase &CDB,1628const Options &Opts,1629std::unique_ptr<ParsingCallbacks> Callbacks)1630: CDB(CDB), Opts(Opts),1631Callbacks(Callbacks ? std::move(Callbacks)1632: std::make_unique<ParsingCallbacks>()),1633Barrier(Opts.AsyncThreadsCount), QuickRunBarrier(Opts.AsyncThreadsCount),1634IdleASTs(1635std::make_unique<ASTCache>(Opts.RetentionPolicy.MaxRetainedASTs)),1636HeaderIncluders(std::make_unique<HeaderIncluderCache>()) {1637// Avoid null checks everywhere.1638if (!Opts.ContextProvider) {1639this->Opts.ContextProvider = [](llvm::StringRef) {1640return Context::current().clone();1641};1642}1643if (0 < Opts.AsyncThreadsCount) {1644PreambleTasks.emplace();1645WorkerThreads.emplace();1646}1647}
1648
1649TUScheduler::~TUScheduler() {1650// Notify all workers that they need to stop.1651Files.clear();1652
1653// Wait for all in-flight tasks to finish.1654if (PreambleTasks)1655PreambleTasks->wait();1656if (WorkerThreads)1657WorkerThreads->wait();1658}
1659
1660bool TUScheduler::blockUntilIdle(Deadline D) const {1661for (auto &File : Files)1662if (!File.getValue()->Worker->blockUntilIdle(D))1663return false;1664if (PreambleTasks)1665if (!PreambleTasks->wait(D))1666return false;1667return true;1668}
1669
1670bool TUScheduler::update(PathRef File, ParseInputs Inputs,1671WantDiagnostics WantDiags) {1672std::unique_ptr<FileData> &FD = Files[File];1673bool NewFile = FD == nullptr;1674bool ContentChanged = false;1675if (!FD) {1676// Create a new worker to process the AST-related tasks.1677ASTWorkerHandle Worker = ASTWorker::create(1678File, CDB, *IdleASTs, *HeaderIncluders,1679WorkerThreads ? &*WorkerThreads : nullptr, Barrier, Opts, *Callbacks);1680FD = std::unique_ptr<FileData>(1681new FileData{Inputs.Contents, std::move(Worker)});1682ContentChanged = true;1683} else if (FD->Contents != Inputs.Contents) {1684ContentChanged = true;1685FD->Contents = Inputs.Contents;1686}1687FD->Worker->update(std::move(Inputs), WantDiags, ContentChanged);1688// There might be synthetic update requests, don't change the LastActiveFile1689// in such cases.1690if (ContentChanged)1691LastActiveFile = File.str();1692return NewFile;1693}
1694
1695void TUScheduler::remove(PathRef File) {1696bool Removed = Files.erase(File);1697if (!Removed)1698elog("Trying to remove file from TUScheduler that is not tracked: {0}",1699File);1700// We don't call HeaderIncluders.remove(File) here.1701// If we did, we'd avoid potentially stale header/mainfile associations.1702// However, it would mean that closing a mainfile could invalidate the1703// preamble of several open headers.1704}
1705
1706void TUScheduler::run(llvm::StringRef Name, llvm::StringRef Path,1707llvm::unique_function<void()> Action) {1708runWithSemaphore(Name, Path, std::move(Action), Barrier);1709}
1710
1711void TUScheduler::runQuick(llvm::StringRef Name, llvm::StringRef Path,1712llvm::unique_function<void()> Action) {1713// Use QuickRunBarrier to serialize quick tasks: we are ignoring1714// the parallelism level set by the user, don't abuse it1715runWithSemaphore(Name, Path, std::move(Action), QuickRunBarrier);1716}
1717
1718void TUScheduler::runWithSemaphore(llvm::StringRef Name, llvm::StringRef Path,1719llvm::unique_function<void()> Action,1720Semaphore &Sem) {1721if (Path.empty())1722Path = LastActiveFile;1723else1724LastActiveFile = Path.str();1725if (!PreambleTasks) {1726WithContext WithProvidedContext(Opts.ContextProvider(Path));1727return Action();1728}1729PreambleTasks->runAsync(Name, [this, &Sem, Ctx = Context::current().clone(),1730Path(Path.str()),1731Action = std::move(Action)]() mutable {1732std::lock_guard<Semaphore> BarrierLock(Sem);1733WithContext WC(std::move(Ctx));1734WithContext WithProvidedContext(Opts.ContextProvider(Path));1735Action();1736});1737}
1738
1739void TUScheduler::runWithAST(1740llvm::StringRef Name, PathRef File,1741llvm::unique_function<void(llvm::Expected<InputsAndAST>)> Action,1742TUScheduler::ASTActionInvalidation Invalidation) {1743auto It = Files.find(File);1744if (It == Files.end()) {1745Action(llvm::make_error<LSPError>(1746"trying to get AST for non-added document", ErrorCode::InvalidParams));1747return;1748}1749LastActiveFile = File.str();1750
1751It->second->Worker->runWithAST(Name, std::move(Action), Invalidation);1752}
1753
1754void TUScheduler::runWithPreamble(llvm::StringRef Name, PathRef File,1755PreambleConsistency Consistency,1756Callback<InputsAndPreamble> Action) {1757auto It = Files.find(File);1758if (It == Files.end()) {1759Action(llvm::make_error<LSPError>(1760"trying to get preamble for non-added document",1761ErrorCode::InvalidParams));1762return;1763}1764LastActiveFile = File.str();1765
1766if (!PreambleTasks) {1767trace::Span Tracer(Name);1768SPAN_ATTACH(Tracer, "file", File);1769std::shared_ptr<const ASTSignals> Signals;1770std::shared_ptr<const PreambleData> Preamble =1771It->second->Worker->getPossiblyStalePreamble(&Signals);1772WithContext WithProvidedContext(Opts.ContextProvider(File));1773Action(InputsAndPreamble{It->second->Contents,1774It->second->Worker->getCurrentCompileCommand(),1775Preamble.get(), Signals.get()});1776return;1777}1778
1779std::shared_ptr<const ASTWorker> Worker = It->second->Worker.lock();1780auto Task = [Worker, Consistency, Name = Name.str(), File = File.str(),1781Contents = It->second->Contents,1782Command = Worker->getCurrentCompileCommand(),1783Ctx = Context::current().derive(FileBeingProcessed,1784std::string(File)),1785Action = std::move(Action), this]() mutable {1786clang::noteBottomOfStack();1787ThreadCrashReporter ScopedReporter([&Name, &Contents, &Command]() {1788llvm::errs() << "Signalled during preamble action: " << Name << "\n";1789crashDumpCompileCommand(llvm::errs(), Command);1790crashDumpFileContents(llvm::errs(), Contents);1791});1792std::shared_ptr<const PreambleData> Preamble;1793if (Consistency == PreambleConsistency::Stale) {1794// Wait until the preamble is built for the first time, if preamble1795// is required. This avoids extra work of processing the preamble1796// headers in parallel multiple times.1797Worker->waitForFirstPreamble();1798}1799std::shared_ptr<const ASTSignals> Signals;1800Preamble = Worker->getPossiblyStalePreamble(&Signals);1801
1802std::lock_guard<Semaphore> BarrierLock(Barrier);1803WithContext Guard(std::move(Ctx));1804trace::Span Tracer(Name);1805SPAN_ATTACH(Tracer, "file", File);1806WithContext WithProvidedContext(Opts.ContextProvider(File));1807Action(InputsAndPreamble{Contents, Command, Preamble.get(), Signals.get()});1808};1809
1810PreambleTasks->runAsync("task:" + llvm::sys::path::filename(File),1811std::move(Task));1812}
1813
1814llvm::StringMap<TUScheduler::FileStats> TUScheduler::fileStats() const {1815llvm::StringMap<TUScheduler::FileStats> Result;1816for (const auto &PathAndFile : Files)1817Result.try_emplace(PathAndFile.first(),1818PathAndFile.second->Worker->stats());1819return Result;1820}
1821
1822std::vector<Path> TUScheduler::getFilesWithCachedAST() const {1823std::vector<Path> Result;1824for (auto &&PathAndFile : Files) {1825if (!PathAndFile.second->Worker->isASTCached())1826continue;1827Result.push_back(std::string(PathAndFile.first()));1828}1829return Result;1830}
1831
1832DebouncePolicy::clock::duration1833DebouncePolicy::compute(llvm::ArrayRef<clock::duration> History) const {1834assert(Min <= Max && "Invalid policy");1835if (History.empty())1836return Max; // Arbitrary.1837
1838// Base the result on the median rebuild.1839// nth_element needs a mutable array, take the chance to bound the data size.1840History = History.take_back(15);1841llvm::SmallVector<clock::duration, 15> Recent(History.begin(), History.end());1842auto *Median = Recent.begin() + Recent.size() / 2;1843std::nth_element(Recent.begin(), Median, Recent.end());1844
1845clock::duration Target =1846std::chrono::duration_cast<clock::duration>(RebuildRatio * *Median);1847if (Target > Max)1848return Max;1849if (Target < Min)1850return Min;1851return Target;1852}
1853
1854DebouncePolicy DebouncePolicy::fixed(clock::duration T) {1855DebouncePolicy P;1856P.Min = P.Max = T;1857return P;1858}
1859
1860void TUScheduler::profile(MemoryTree &MT) const {1861for (const auto &Elem : fileStats()) {1862MT.detail(Elem.first())1863.child("preamble")1864.addUsage(Opts.StorePreamblesInMemory ? Elem.second.UsedBytesPreamble1865: 0);1866MT.detail(Elem.first()).child("ast").addUsage(Elem.second.UsedBytesAST);1867MT.child("header_includer_cache").addUsage(HeaderIncluders->getUsedBytes());1868}1869}
1870} // namespace clangd1871} // namespace clang1872