65 #include "clang/Frontend/CompilerInvocation.h"
66 #include "clang/Tooling/CompilationDatabase.h"
67 #include "llvm/ADT/FunctionExtras.h"
68 #include "llvm/ADT/STLExtras.h"
69 #include "llvm/ADT/ScopeExit.h"
70 #include "llvm/ADT/SmallVector.h"
71 #include "llvm/ADT/StringExtras.h"
72 #include "llvm/ADT/StringRef.h"
73 #include "llvm/Support/Allocator.h"
74 #include "llvm/Support/Errc.h"
75 #include "llvm/Support/ErrorHandling.h"
76 #include "llvm/Support/FormatVariadic.h"
77 #include "llvm/Support/Path.h"
78 #include "llvm/Support/Threading.h"
79 #include "llvm/Support/raw_ostream.h"
83 #include <condition_variable>
91 #include <type_traits>
97 using std::chrono::steady_clock;
104 constexpr trace::Metric
105 PreambleBuildFilesystemLatency(
"preamble_fs_latency",
109 constexpr trace::Metric PreambleBuildFilesystemLatencyRatio(
112 constexpr trace::Metric PreambleBuildSize(
"preamble_build_size",
114 constexpr trace::Metric PreambleSerializedSize(
"preamble_serialized_size",
117 void reportPreambleBuild(
const PreambleBuildStats &Stats,
118 bool IsFirstPreamble) {
119 auto RecordWithLabel = [&Stats](llvm::StringRef
Label) {
122 PreambleBuildFilesystemLatencyRatio.record(
126 static llvm::once_flag OnceFlag;
127 llvm::call_once(OnceFlag, [&] { RecordWithLabel(
"first_build"); });
128 RecordWithLabel(IsFirstPreamble ?
"first_build_for_file" :
"rebuild");
130 PreambleBuildSize.record(Stats.
BuildSize);
141 return llvm::StringRef(*File);
151 using Key =
const ASTWorker *;
153 ASTCache(
unsigned MaxRetainedASTs) : MaxRetainedASTs(MaxRetainedASTs) {}
158 std::lock_guard<std::mutex> Lock(Mut);
159 auto It = findByKey(
K);
160 if (It == LRU.end() || !It->second)
162 return It->second->getUsedBytes();
167 void put(
Key K, std::unique_ptr<ParsedAST> V) {
168 std::unique_lock<std::mutex> Lock(Mut);
169 assert(findByKey(
K) == LRU.end());
171 LRU.insert(LRU.begin(), {K, std::move(V)});
172 if (LRU.size() <= MaxRetainedASTs)
175 std::unique_ptr<ParsedAST> ForCleanup = std::move(LRU.back().second);
186 std::optional<std::unique_ptr<ParsedAST>>
189 std::unique_lock<std::mutex> Lock(Mut);
190 auto Existing = findByKey(
K);
191 if (Existing == LRU.end()) {
193 AccessMetric->record(1,
"miss");
197 AccessMetric->record(1,
"hit");
198 std::unique_ptr<ParsedAST> V = std::move(Existing->second);
203 return std::optional<std::unique_ptr<ParsedAST>>(std::move(V));
207 using KVPair = std::pair<Key, std::unique_ptr<ParsedAST>>;
209 std::vector<KVPair>::iterator findByKey(
Key K) {
210 return llvm::find_if(LRU, [
K](
const KVPair &P) {
return P.first ==
K; });
214 unsigned MaxRetainedASTs;
217 std::vector<KVPair> LRU;
250 llvm::BumpPtrAllocator Arena;
257 llvm::StringMap<Association, llvm::BumpPtrAllocator &> HeaderToMain;
258 llvm::StringMap<Association *, llvm::BumpPtrAllocator &> MainToFirst;
259 std::atomic<size_t> UsedBytes;
260 mutable std::mutex Mu;
262 void invalidate(Association *First) {
263 Association *Current = First;
265 Association *Next = Current->Next;
266 Current->Next =
nullptr;
268 }
while (Current != First);
272 Association *associate(llvm::StringRef
MainFile,
273 llvm::ArrayRef<std::string> Headers) {
274 Association *First =
nullptr, *Prev =
nullptr;
275 for (
const std::string &Header : Headers) {
276 auto &Assoc = HeaderToMain[Header];
291 void updateMemoryUsage() {
292 auto StringMapHeap = [](
const auto &Map) {
295 return Map.getNumBuckets() * (
sizeof(
void *) +
sizeof(
unsigned));
297 size_t Usage =
Arena.getTotalMemory() + StringMapHeap(MainToFirst) +
298 StringMapHeap(HeaderToMain) +
sizeof(*this);
299 UsedBytes.store(
Usage, std::memory_order_release);
310 std::lock_guard<std::mutex> Lock(Mu);
311 auto It = MainToFirst.try_emplace(
MainFile,
nullptr);
312 Association *&First = It.first->second;
315 First = associate(It.first->first(), Headers);
323 std::lock_guard<std::mutex> Lock(Mu);
324 Association *&First = MainToFirst[
MainFile];
335 std::lock_guard<std::mutex> Lock(Mu);
336 return HeaderToMain.lookup(Header).MainFile.str();
340 return UsedBytes.load(std::memory_order_acquire);
346 bool isReliable(
const tooling::CompileCommand &Cmd) {
347 return Cmd.Heuristic.empty();
352 class SynchronizedTUStatus {
354 SynchronizedTUStatus(
PathRef FileName, ParsingCallbacks &Callbacks)
357 void update(llvm::function_ref<
void(TUStatus &)> Mutator) {
358 std::lock_guard<std::mutex> Lock(StatusMu);
365 std::lock_guard<std::mutex> Lock(StatusMu);
370 void emitStatusLocked() {
372 Callbacks.onFileUpdated(FileName, Status);
379 bool CanPublish =
true;
380 ParsingCallbacks &Callbacks;
386 class PreambleThrottlerRequest {
389 PreambleThrottlerRequest(llvm::StringRef
Filename,
390 PreambleThrottler *Throttler,
391 std::condition_variable &CV)
392 : Throttler(Throttler),
393 Satisfied(Throttler == nullptr) {
398 Satisfied.store(
true, std::memory_order_release);
403 bool satisfied()
const {
return Satisfied.load(std::memory_order_acquire); }
408 ~PreambleThrottlerRequest() {
410 Throttler->release(
ID);
415 PreambleThrottler *Throttler;
416 std::atomic<bool> Satisfied = {
false};
423 class PreambleThread {
425 PreambleThread(llvm::StringRef FileName, ParsingCallbacks &Callbacks,
426 bool StorePreambleInMemory,
bool RunSync,
427 PreambleThrottler *Throttler, SynchronizedTUStatus &Status,
428 TUScheduler::HeaderIncluderCache &HeaderIncluders,
431 StoreInMemory(StorePreambleInMemory), RunSync(RunSync),
432 Throttler(Throttler), Status(Status), ASTPeer(AW),
433 HeaderIncluders(HeaderIncluders) {}
438 void update(std::unique_ptr<CompilerInvocation>
CI, ParseInputs PI,
443 build(std::move(Req));
444 Status.update([](TUStatus &Status) {
450 std::unique_lock<std::mutex> Lock(Mutex);
455 ReqCV.wait(Lock, [
this] {
458 NextReq = std::move(Req);
467 std::optional<PreambleThrottlerRequest> Throttle;
469 std::unique_lock<std::mutex> Lock(Mutex);
470 assert(!CurrentReq &&
"Already processing a request?");
472 ReqCV.wait(Lock, [&] {
return NextReq || Done; });
477 Throttle.emplace(FileName, Throttler, ReqCV);
478 std::optional<trace::Span>
Tracer;
480 if (!Throttle->satisfied()) {
481 Tracer.emplace(
"PreambleThrottle");
482 Status.update([&](TUStatus &Status) {
486 ReqCV.wait(Lock, [&] {
return Throttle->satisfied() || Done; });
493 CurrentReq = std::move(*NextReq);
498 WithContext Guard(std::move(CurrentReq->Ctx));
504 build(std::move(*CurrentReq));
508 bool IsEmpty =
false;
510 std::lock_guard<std::mutex> Lock(Mutex);
518 Status.update([](TUStatus &Status) {
524 dlog(
"Preamble worker for {0} stopped", FileName);
529 dlog(
"Preamble worker for {0} received stop", FileName);
531 std::lock_guard<std::mutex> Lock(Mutex);
539 bool blockUntilIdle(Deadline Timeout)
const {
540 std::unique_lock<std::mutex> Lock(Mutex);
541 return wait(Lock, ReqCV, Timeout, [&] {
return !NextReq && !CurrentReq; });
548 std::unique_ptr<CompilerInvocation>
CI;
556 std::lock_guard<std::mutex> Lock(Mutex);
562 void build(Request Req);
564 mutable std::mutex Mutex;
566 std::optional<Request> NextReq;
567 std::optional<Request> CurrentReq;
570 mutable std::condition_variable ReqCV;
572 std::shared_ptr<const PreambleData> LatestBuild;
575 ParsingCallbacks &Callbacks;
576 const bool StoreInMemory;
578 PreambleThrottler *Throttler;
580 SynchronizedTUStatus &Status;
582 TUScheduler::HeaderIncluderCache &HeaderIncluders;
585 class ASTWorkerHandle;
598 friend class ASTWorkerHandle;
599 ASTWorker(
PathRef FileName,
const GlobalCompilationDatabase &CDB,
600 TUScheduler::ASTCache &LRUCache,
601 TUScheduler::HeaderIncluderCache &HeaderIncluders,
602 Semaphore &Barrier,
bool RunSync,
const TUScheduler::Options &Opts,
603 ParsingCallbacks &Callbacks);
611 static ASTWorkerHandle
612 create(
PathRef FileName,
const GlobalCompilationDatabase &CDB,
613 TUScheduler::ASTCache &IdleASTs,
614 TUScheduler::HeaderIncluderCache &HeaderIncluders,
615 AsyncTaskRunner *Tasks, Semaphore &Barrier,
616 const TUScheduler::Options &Opts, ParsingCallbacks &Callbacks);
621 runWithAST(llvm::StringRef
Name,
622 llvm::unique_function<
void(llvm::Expected<InputsAndAST>)>
Action,
624 bool blockUntilIdle(Deadline Timeout)
const;
626 std::shared_ptr<const PreambleData> getPossiblyStalePreamble(
627 std::shared_ptr<const ASTSignals> *ASTSignals =
nullptr)
const;
633 void updatePreamble(std::unique_ptr<CompilerInvocation>
CI, ParseInputs PI,
634 std::shared_ptr<const PreambleData>
Preamble,
639 void getCurrentPreamble(
640 llvm::unique_function<
void(std::shared_ptr<const PreambleData>)>);
642 tooling::CompileCommand getCurrentCompileCommand()
const;
648 void waitForFirstPreamble()
const;
650 TUScheduler::FileStats stats()
const;
651 bool isASTCached()
const;
669 void generateDiagnostics(std::unique_ptr<CompilerInvocation> Invocation,
672 void updateASTSignals(ParsedAST &AST);
682 void startTask(llvm::StringRef
Name, llvm::unique_function<
void()> Task,
683 std::optional<UpdateType>
Update,
686 void runTask(llvm::StringRef
Name, llvm::function_ref<
void()> Task);
692 Deadline scheduleLocked();
694 bool shouldSkipHeadLocked()
const;
708 TUScheduler::ASTCache &IdleASTs;
709 TUScheduler::HeaderIncluderCache &HeaderIncluders;
712 const DebouncePolicy UpdateDebounce;
716 const std::function<Context(llvm::StringRef)> ContextProvider;
717 const GlobalCompilationDatabase &CDB;
719 ParsingCallbacks &Callbacks;
723 bool RanASTCallback =
false;
725 mutable std::mutex Mutex;
729 ParseInputs FileInputs;
731 llvm::SmallVector<DebouncePolicy::clock::duration>
735 std::deque<Request> Requests;
736 std::optional<Request> CurrentRequest;
739 mutable std::condition_variable RequestsCV;
740 std::shared_ptr<const ASTSignals> LatestASTSignals;
744 std::optional<std::shared_ptr<const PreambleData>> LatestPreamble;
745 std::deque<Request> PreambleRequests;
748 mutable std::condition_variable PreambleCV;
751 std::mutex PublishMu;
758 bool CanPublishResults =
true;
759 std::atomic<unsigned> ASTBuildCount = {0};
760 std::atomic<unsigned> PreambleBuildCount = {0};
762 SynchronizedTUStatus Status;
763 PreambleThread PreamblePeer;
770 class ASTWorkerHandle {
771 friend class ASTWorker;
772 ASTWorkerHandle(std::shared_ptr<ASTWorker> Worker)
773 : Worker(std::move(Worker)) {
774 assert(this->Worker);
778 ASTWorkerHandle(
const ASTWorkerHandle &) =
delete;
779 ASTWorkerHandle &operator=(
const ASTWorkerHandle &) =
delete;
780 ASTWorkerHandle(ASTWorkerHandle &&) =
default;
781 ASTWorkerHandle &operator=(ASTWorkerHandle &&) =
default;
788 ASTWorker &operator*() {
789 assert(Worker &&
"Handle was moved from");
793 ASTWorker *operator->() {
794 assert(Worker &&
"Handle was moved from");
802 std::shared_ptr<const ASTWorker> lock() {
return Worker; }
805 std::shared_ptr<ASTWorker> Worker;
809 ASTWorker::create(
PathRef FileName,
const GlobalCompilationDatabase &CDB,
810 TUScheduler::ASTCache &IdleASTs,
811 TUScheduler::HeaderIncluderCache &HeaderIncluders,
812 AsyncTaskRunner *Tasks, Semaphore &Barrier,
813 const TUScheduler::Options &Opts,
814 ParsingCallbacks &Callbacks) {
815 std::shared_ptr<ASTWorker> Worker(
816 new ASTWorker(FileName, CDB, IdleASTs, HeaderIncluders, Barrier,
817 !Tasks, Opts, Callbacks));
819 Tasks->
runAsync(
"ASTWorker:" + llvm::sys::path::filename(FileName),
820 [Worker]() { Worker->run(); });
821 Tasks->
runAsync(
"PreambleWorker:" + llvm::sys::path::filename(FileName),
822 [Worker]() { Worker->PreamblePeer.run(); });
825 return ASTWorkerHandle(std::move(Worker));
828 ASTWorker::ASTWorker(
PathRef FileName,
const GlobalCompilationDatabase &CDB,
829 TUScheduler::ASTCache &LRUCache,
830 TUScheduler::HeaderIncluderCache &HeaderIncluders,
831 Semaphore &Barrier,
bool RunSync,
832 const TUScheduler::Options &Opts,
833 ParsingCallbacks &Callbacks)
834 : IdleASTs(LRUCache), HeaderIncluders(HeaderIncluders), RunSync(RunSync),
836 ContextProvider(Opts.ContextProvider), CDB(CDB), Callbacks(Callbacks),
837 Barrier(Barrier), Done(false), Status(
FileName, Callbacks),
838 PreamblePeer(
FileName, Callbacks, Opts.StorePreamblesInMemory, RunSync,
839 Opts.PreambleThrottler, Status, HeaderIncluders, *this) {
843 FileInputs.CompileCommand = CDB.getFallbackCommand(FileName);
846 ASTWorker::~ASTWorker() {
850 std::lock_guard<std::mutex> Lock(Mutex);
851 assert(Done &&
"handle was not destroyed");
852 assert(Requests.empty() && !CurrentRequest &&
853 "unprocessed requests when destroying ASTWorker");
859 llvm::StringLiteral TaskName =
"Update";
860 auto Task = [=]()
mutable {
866 auto Cmd = CDB.getCompileCommand(FileName);
869 if (!Cmd || !isReliable(*Cmd)) {
870 std::string ProxyFile = HeaderIncluders.get(FileName);
871 if (!ProxyFile.empty()) {
872 auto ProxyCmd = CDB.getCompileCommand(ProxyFile);
873 if (!ProxyCmd || !isReliable(*ProxyCmd)) {
875 HeaderIncluders.remove(ProxyFile);
878 Cmd = tooling::transferCompileCommand(std::move(*ProxyCmd), FileName);
883 Inputs.CompileCommand = std::move(*Cmd);
885 Inputs.CompileCommand = CDB.getFallbackCommand(FileName);
887 bool InputsAreTheSame =
888 std::tie(FileInputs.CompileCommand, FileInputs.Contents) ==
891 if (!InputsAreTheSame) {
893 RanASTCallback =
false;
898 std::lock_guard<std::mutex> Lock(Mutex);
902 log(
"ASTWorker building file {0} version {1} with command {2}\n[{3}]\n{4}",
903 FileName,
Inputs.Version,
Inputs.CompileCommand.Heuristic,
904 Inputs.CompileCommand.Directory,
907 StoreDiags CompilerInvocationDiagConsumer;
908 std::vector<std::string> CC1Args;
910 Inputs, CompilerInvocationDiagConsumer, &CC1Args);
912 if (!CC1Args.empty())
913 vlog(
"Driver produced command: cc1 {0}",
printArgv(CC1Args));
914 std::vector<Diag> CompilerInvocationDiags =
915 CompilerInvocationDiagConsumer.take();
917 elog(
"Could not build CompilerInvocation for file {0}", FileName);
920 RanASTCallback =
false;
922 Callbacks.onFailedAST(FileName,
Inputs.Version,
923 std::move(CompilerInvocationDiags),
924 [&](llvm::function_ref<
void()> Publish) {
928 std::lock_guard<std::mutex> Lock(PublishMu);
929 if (CanPublishResults)
934 LatestPreamble.emplace();
937 PreambleCV.notify_all();
941 PreamblePeer.update(std::move(Invocation), std::move(
Inputs),
942 std::move(CompilerInvocationDiags),
WantDiags);
943 std::unique_lock<std::mutex> Lock(Mutex);
944 PreambleCV.wait(Lock, [
this] {
950 return LatestPreamble || !PreambleRequests.empty() || Done;
954 TUScheduler::NoInvalidation);
957 void ASTWorker::runWithAST(
958 llvm::StringRef
Name,
959 llvm::unique_function<
void(llvm::Expected<InputsAndAST>)>
Action,
960 TUScheduler::ASTActionInvalidation Invalidation) {
962 static constexpr trace::Metric ASTAccessForRead(
966 return Action(llvm::make_error<CancelledError>(Reason));
967 std::optional<std::unique_ptr<ParsedAST>>
AST =
968 IdleASTs.take(
this, &ASTAccessForRead);
970 StoreDiags CompilerInvocationDiagConsumer;
971 std::unique_ptr<CompilerInvocation> Invocation =
974 vlog(
"ASTWorker rebuilding evicted AST to run {0}: {1} version {2}",
Name,
975 FileName, FileInputs.Version);
979 std::optional<ParsedAST> NewAST;
981 NewAST = ParsedAST::build(FileName, FileInputs, std::move(Invocation),
982 CompilerInvocationDiagConsumer.take(),
983 getPossiblyStalePreamble());
986 AST = NewAST ? std::make_unique<ParsedAST>(std::move(*NewAST)) : nullptr;
989 auto _ = llvm::make_scope_exit(
990 [&
AST,
this]() { IdleASTs.put(
this, std::move(*
AST)); });
993 return Action(
error(llvm::errc::invalid_argument,
"invalid AST"));
994 vlog(
"ASTWorker running {0} on version {2} of {1}",
Name, FileName,
998 startTask(
Name, std::move(Task), std::nullopt, Invalidation);
1002 static void crashDumpCompileCommand(llvm::raw_ostream &
OS,
1003 const tooling::CompileCommand &Command) {
1004 OS <<
" Filename: " << Command.Filename <<
"\n";
1005 OS <<
" Directory: " << Command.Directory <<
"\n";
1006 OS <<
" Command Line:";
1007 for (
auto &Arg : Command.CommandLine) {
1014 static void crashDumpFileContents(llvm::raw_ostream &
OS,
1015 const std::string &Contents) {
1020 if (getenv(
"CLANGD_CRASH_DUMP_SOURCE")) {
1021 OS <<
" Contents:\n";
1022 OS << Contents <<
"\n";
1027 static void crashDumpParseInputs(llvm::raw_ostream &
OS,
1028 const ParseInputs &FileInputs) {
1029 auto &Command = FileInputs.CompileCommand;
1030 crashDumpCompileCommand(
OS, Command);
1031 OS <<
" Version: " << FileInputs.Version <<
"\n";
1032 crashDumpFileContents(
OS, FileInputs.Contents);
1035 void PreambleThread::build(Request Req) {
1036 assert(Req.CI &&
"Got preamble request with null compiler invocation");
1037 const ParseInputs &
Inputs = Req.Inputs;
1038 bool ReusedPreamble =
false;
1040 Status.update([&](TUStatus &Status) {
1041 Status.PreambleActivity = PreambleAction::Building;
1043 auto _ = llvm::make_scope_exit([
this, &Req, &ReusedPreamble] {
1044 ASTPeer.updatePreamble(std::move(Req.CI), std::move(Req.Inputs),
1045 LatestBuild, std::move(Req.CIDiags),
1046 std::move(Req.WantDiags));
1047 if (!ReusedPreamble)
1048 Callbacks.onPreamblePublished(FileName);
1051 if (!LatestBuild ||
Inputs.ForceRebuild) {
1052 vlog(
"Building first preamble for {0} version {1}", FileName,
1055 vlog(
"Reusing preamble version {0} for version {1} of {2}",
1056 LatestBuild->Version,
Inputs.Version, FileName);
1057 ReusedPreamble =
true;
1060 vlog(
"Rebuilding invalidated preamble for {0} version {1} (previous was "
1062 FileName,
Inputs.Version, LatestBuild->Version);
1065 ThreadCrashReporter ScopedReporter([&
Inputs]() {
1066 llvm::errs() <<
"Signalled while building preamble\n";
1067 crashDumpParseInputs(llvm::errs(),
Inputs);
1070 PreambleBuildStats Stats;
1071 bool IsFirstPreamble = !LatestBuild;
1073 FileName, *Req.CI,
Inputs, StoreInMemory,
1074 [&](ASTContext &
Ctx, Preprocessor &
PP,
1075 const CanonicalIncludes &CanonIncludes) {
1076 Callbacks.onPreambleAST(FileName, Inputs.Version, *Req.CI, Ctx, PP,
1082 reportPreambleBuild(Stats, IsFirstPreamble);
1083 if (isReliable(LatestBuild->CompileCommand))
1084 HeaderIncluders.update(FileName, LatestBuild->Includes.allHeaders());
1087 void ASTWorker::updatePreamble(std::unique_ptr<CompilerInvocation>
CI,
1089 std::shared_ptr<const PreambleData>
Preamble,
1092 llvm::StringLiteral TaskName =
"Build AST";
1100 if (!LatestPreamble ||
Preamble != *LatestPreamble) {
1101 ++PreambleBuildCount;
1103 IdleASTs.take(
this);
1104 RanASTCallback =
false;
1105 std::lock_guard<std::mutex> Lock(Mutex);
1109 std::swap(*LatestPreamble,
Preamble);
1111 LatestPreamble = std::move(
Preamble);
1114 PreambleCV.notify_all();
1124 generateDiagnostics(std::move(
CI), std::move(PI), std::move(
CIDiags));
1127 runTask(TaskName, Task);
1131 std::lock_guard<std::mutex> Lock(Mutex);
1132 PreambleRequests.push_back({std::move(Task), std::string(TaskName),
1133 steady_clock::now(), Context::current().clone(),
1134 std::nullopt, std::nullopt,
1135 TUScheduler::NoInvalidation,
nullptr});
1137 PreambleCV.notify_all();
1138 RequestsCV.notify_all();
1141 void ASTWorker::updateASTSignals(ParsedAST &
AST) {
1142 auto Signals = std::make_shared<const ASTSignals>(ASTSignals::derive(
AST));
1146 std::lock_guard<std::mutex> Lock(Mutex);
1147 std::swap(LatestASTSignals, Signals);
1151 void ASTWorker::generateDiagnostics(
1152 std::unique_ptr<CompilerInvocation> Invocation, ParseInputs
Inputs,
1155 static constexpr trace::Metric ASTAccessForDiag(
1158 assert(LatestPreamble);
1161 std::lock_guard<std::mutex> Lock(PublishMu);
1162 if (!CanPublishResults)
1166 bool InputsAreLatest =
1167 std::tie(FileInputs.CompileCommand, FileInputs.Contents) ==
1174 if (InputsAreLatest && RanASTCallback)
1178 std::string TaskName = llvm::formatv(
"Build AST ({0})",
Inputs.Version);
1179 Status.update([&](TUStatus &Status) {
1180 Status.ASTActivity.K = ASTAction::Building;
1181 Status.ASTActivity.Name = std::move(TaskName);
1186 std::optional<std::unique_ptr<ParsedAST>>
AST =
1187 IdleASTs.take(
this, &ASTAccessForDiag);
1188 if (!
AST || !InputsAreLatest) {
1189 auto RebuildStartTime = DebouncePolicy::clock::now();
1190 std::optional<ParsedAST> NewAST = ParsedAST::build(
1191 FileName,
Inputs, std::move(Invocation),
CIDiags, *LatestPreamble);
1192 auto RebuildDuration = DebouncePolicy::clock::now() - RebuildStartTime;
1196 std::unique_lock<std::mutex> Lock(Mutex, std::try_to_lock);
1197 if (Lock.owns_lock()) {
1200 if (RebuildTimes.size() == RebuildTimes.capacity())
1201 RebuildTimes.erase(RebuildTimes.begin());
1202 RebuildTimes.push_back(RebuildDuration);
1205 Status.update([&](TUStatus &Status) {
1206 Status.Details.ReuseAST =
false;
1207 Status.Details.BuildFailed = !NewAST;
1209 AST = NewAST ? std::make_unique<ParsedAST>(std::move(*NewAST)) : nullptr;
1211 log(
"Skipping rebuild of the AST for {0}, inputs are the same.", FileName);
1212 Status.update([](TUStatus &Status) {
1213 Status.Details.ReuseAST =
true;
1214 Status.Details.BuildFailed =
false;
1219 auto RunPublish = [&](llvm::function_ref<void()> Publish) {
1222 std::lock_guard<std::mutex> Lock(PublishMu);
1223 if (CanPublishResults)
1227 trace::Span Span(
"Running main AST callback");
1228 Callbacks.onMainAST(FileName, **
AST, RunPublish);
1229 updateASTSignals(**
AST);
1235 Callbacks.onFailedAST(FileName,
Inputs.Version,
CIDiags, RunPublish);
1241 if (InputsAreLatest) {
1242 RanASTCallback = *
AST !=
nullptr;
1243 IdleASTs.put(
this, std::move(*
AST));
1247 std::shared_ptr<const PreambleData> ASTWorker::getPossiblyStalePreamble(
1248 std::shared_ptr<const ASTSignals> *ASTSignals)
const {
1249 std::lock_guard<std::mutex> Lock(Mutex);
1251 *ASTSignals = LatestASTSignals;
1252 return LatestPreamble ? *LatestPreamble :
nullptr;
1255 void ASTWorker::waitForFirstPreamble()
const {
1256 std::unique_lock<std::mutex> Lock(Mutex);
1257 PreambleCV.wait(Lock, [
this] {
return LatestPreamble || Done; });
1260 tooling::CompileCommand ASTWorker::getCurrentCompileCommand()
const {
1261 std::unique_lock<std::mutex> Lock(Mutex);
1262 return FileInputs.CompileCommand;
1265 TUScheduler::FileStats ASTWorker::stats()
const {
1266 TUScheduler::FileStats Result;
1267 Result.ASTBuilds = ASTBuildCount;
1268 Result.PreambleBuilds = PreambleBuildCount;
1272 Result.UsedBytesAST = IdleASTs.getUsedBytes(
this);
1273 if (
auto Preamble = getPossiblyStalePreamble())
1274 Result.UsedBytesPreamble =
Preamble->Preamble.getSize();
1278 bool ASTWorker::isASTCached()
const {
return IdleASTs.getUsedBytes(
this) != 0; }
1280 void ASTWorker::stop() {
1282 std::lock_guard<std::mutex> Lock(PublishMu);
1283 CanPublishResults =
false;
1286 std::lock_guard<std::mutex> Lock(Mutex);
1287 assert(!Done &&
"stop() called twice");
1290 PreamblePeer.stop();
1292 PreambleCV.notify_all();
1294 RequestsCV.notify_all();
1297 void ASTWorker::runTask(llvm::StringRef
Name, llvm::function_ref<
void()> Task) {
1298 ThreadCrashReporter ScopedReporter([
this,
Name]() {
1299 llvm::errs() <<
"Signalled during AST worker action: " <<
Name <<
"\n";
1300 crashDumpParseInputs(llvm::errs(), FileInputs);
1303 WithContext WithProvidedContext(ContextProvider(FileName));
1307 void ASTWorker::startTask(llvm::StringRef
Name,
1308 llvm::unique_function<
void()> Task,
1309 std::optional<UpdateType>
Update,
1310 TUScheduler::ASTActionInvalidation Invalidation) {
1312 assert(!Done &&
"running a task after stop()");
1313 runTask(
Name, Task);
1318 std::lock_guard<std::mutex> Lock(Mutex);
1319 assert(!Done &&
"running a task after stop()");
1322 for (
auto &R : llvm::reverse(Requests)) {
1323 if (R.InvalidationPolicy == TUScheduler::InvalidateOnUpdate)
1325 if (R.Update && R.Update->ContentChanged)
1334 WithContext WC(std::move(
Ctx));
1336 static_cast<int>(ErrorCode::ContentModified));
1344 WithContext WC(
Ctx.clone());
1349 llvm::json::Array PreambleRequestsNames;
1350 for (
const auto &Req : PreambleRequests)
1351 PreambleRequestsNames.push_back(Req.Name);
1353 std::move(PreambleRequestsNames));
1354 llvm::json::Array RequestsNames;
1355 for (
const auto &Req : Requests)
1356 RequestsNames.push_back(Req.Name);
1361 QueueCtx = Context::current().clone();
1363 Requests.push_back({std::move(Task), std::string(
Name), steady_clock::now(),
1367 RequestsCV.notify_all();
1370 void ASTWorker::run() {
1373 std::unique_lock<std::mutex> Lock(Mutex);
1374 assert(!CurrentRequest &&
"A task is already running, multiple workers?");
1375 for (
auto Wait = scheduleLocked(); !Wait.expired();
1376 Wait = scheduleLocked()) {
1377 assert(PreambleRequests.empty() &&
1378 "Preamble updates should be scheduled immediately");
1380 if (Requests.empty())
1387 std::optional<WithContext>
Ctx;
1388 std::optional<trace::Span>
Tracer;
1389 if (!Requests.empty()) {
1390 Ctx.emplace(Requests.front().Ctx.clone());
1391 Tracer.emplace(
"Debounce");
1393 if (!(Wait == Deadline::infinity())) {
1394 Status.update([&](TUStatus &Status) {
1395 Status.ASTActivity.K = ASTAction::Queued;
1396 Status.ASTActivity.Name = Requests.front().
Name;
1399 std::chrono::duration_cast<std::chrono::milliseconds>(
1400 Wait.time() - steady_clock::now())
1405 wait(Lock, RequestsCV, Wait);
1409 if (!PreambleRequests.empty()) {
1410 CurrentRequest = std::move(PreambleRequests.front());
1411 PreambleRequests.pop_front();
1413 CurrentRequest = std::move(Requests.front());
1414 Requests.pop_front();
1419 CurrentRequest->QueueCtx.reset();
1424 std::unique_lock<Semaphore> Lock(Barrier, std::try_to_lock);
1425 if (!Lock.owns_lock()) {
1426 Status.update([&](TUStatus &Status) {
1427 Status.ASTActivity.K = ASTAction::Queued;
1428 Status.ASTActivity.Name = CurrentRequest->Name;
1432 WithContext Guard(std::move(CurrentRequest->Ctx));
1433 Status.update([&](TUStatus &Status) {
1434 Status.ASTActivity.K = ASTAction::RunningAction;
1435 Status.ASTActivity.Name = CurrentRequest->Name;
1437 runTask(CurrentRequest->Name, CurrentRequest->Action);
1440 bool IsEmpty =
false;
1442 std::lock_guard<std::mutex> Lock(Mutex);
1443 CurrentRequest.reset();
1444 IsEmpty = Requests.empty() && PreambleRequests.empty();
1447 Status.update([&](TUStatus &Status) {
1448 Status.ASTActivity.K = ASTAction::Idle;
1449 Status.ASTActivity.Name =
"";
1452 RequestsCV.notify_all();
1456 Deadline ASTWorker::scheduleLocked() {
1458 if (!PreambleRequests.empty())
1459 return Deadline::zero();
1460 if (Requests.empty())
1461 return Deadline::infinity();
1463 for (
auto I = Requests.begin(),
E = Requests.end(); I !=
E; ++I) {
1466 if (I->Update == std::nullopt)
1471 if (I->Update == std::nullopt) {
1472 Request R = std::move(*I);
1474 Requests.push_front(std::move(R));
1475 return Deadline::zero();
1478 if (I->Update->Diagnostics == WantDiagnostics::Yes)
1479 I->Update->Diagnostics = WantDiagnostics::Auto;
1482 while (shouldSkipHeadLocked()) {
1483 vlog(
"ASTWorker skipping {0} for {1}", Requests.front().Name, FileName);
1484 Requests.pop_front();
1486 assert(!Requests.empty() &&
"skipped the whole queue");
1491 for (
const auto &R : Requests)
1492 if (R.Update == std::nullopt ||
1493 R.Update->Diagnostics == WantDiagnostics::Yes)
1494 return Deadline::zero();
1496 Deadline
D(Requests.front().AddTime + UpdateDebounce.compute(RebuildTimes));
1501 bool ASTWorker::shouldSkipHeadLocked()
const {
1502 assert(!Requests.empty());
1503 auto Next = Requests.begin();
1504 auto Update = Next->Update;
1510 if (Next == Requests.end() || !Next->Update)
1513 switch (
Update->Diagnostics) {
1514 case WantDiagnostics::Yes:
1516 case WantDiagnostics::No:
1518 case WantDiagnostics::Auto:
1520 for (; Next != Requests.end(); ++Next)
1521 if (Next->Update && Next->Update->Diagnostics != WantDiagnostics::No)
1525 llvm_unreachable(
"Unknown WantDiagnostics");
1528 bool ASTWorker::blockUntilIdle(Deadline Timeout)
const {
1529 auto WaitUntilASTWorkerIsIdle = [&] {
1530 std::unique_lock<std::mutex> Lock(Mutex);
1531 return wait(Lock, RequestsCV, Timeout, [&] {
1532 return PreambleRequests.empty() && Requests.empty() && !CurrentRequest;
1537 if (!WaitUntilASTWorkerIsIdle())
1542 if (!PreamblePeer.blockUntilIdle(Timeout))
1544 assert(Requests.empty() &&
1545 "No new normal tasks can be scheduled concurrently with "
1546 "blockUntilIdle(): ASTWorker isn't threadsafe");
1548 return WaitUntilASTWorkerIsIdle();
1555 std::string renderTUAction(
const PreambleAction PA,
const ASTAction &AA) {
1556 llvm::SmallVector<std::string, 2> Result;
1558 case PreambleAction::Building:
1559 Result.push_back(
"parsing includes");
1561 case PreambleAction::Queued:
1562 Result.push_back(
"includes are queued");
1564 case PreambleAction::Idle:
1569 case ASTAction::Queued:
1570 Result.push_back(
"file is queued");
1572 case ASTAction::RunningAction:
1573 Result.push_back(
"running " + AA.Name);
1575 case ASTAction::Building:
1576 Result.push_back(
"parsing main file");
1578 case ASTAction::Idle:
1590 return llvm::heavyweight_hardware_concurrency().compute_thread_count();
1596 FStatus.
state = renderTUAction(PreambleActivity, ASTActivity);
1608 std::unique_ptr<ParsingCallbacks> Callbacks)
1609 : CDB(CDB), Opts(Opts),
1610 Callbacks(Callbacks ? std::move(Callbacks)
1612 Barrier(Opts.AsyncThreadsCount), QuickRunBarrier(Opts.AsyncThreadsCount),
1614 std::make_unique<
ASTCache>(Opts.RetentionPolicy.MaxRetainedASTs)),
1617 if (!Opts.ContextProvider) {
1622 if (0 < Opts.AsyncThreadsCount) {
1623 PreambleTasks.emplace();
1624 WorkerThreads.emplace();
1634 PreambleTasks->wait();
1636 WorkerThreads->wait();
1640 for (
auto &File : Files)
1641 if (!File.getValue()->Worker->blockUntilIdle(
D))
1644 if (!PreambleTasks->wait(
D))
1651 std::unique_ptr<FileData> &FD = Files[File];
1652 bool NewFile = FD ==
nullptr;
1656 ASTWorkerHandle Worker = ASTWorker::create(
1657 File, CDB, *IdleASTs, *HeaderIncluders,
1658 WorkerThreads ? &*WorkerThreads :
nullptr, Barrier, Opts, *Callbacks);
1659 FD = std::unique_ptr<FileData>(
1662 }
else if (FD->Contents !=
Inputs.Contents) {
1664 FD->Contents =
Inputs.Contents;
1670 LastActiveFile = File.str();
1675 bool Removed = Files.erase(File);
1677 elog(
"Trying to remove file from TUScheduler that is not tracked: {0}",
1686 llvm::unique_function<
void()>
Action) {
1691 llvm::unique_function<
void()>
Action) {
1694 runWithSemaphore(
Name,
Path, std::move(
Action), QuickRunBarrier);
1697 void TUScheduler::runWithSemaphore(llvm::StringRef
Name, llvm::StringRef
Path,
1698 llvm::unique_function<
void()>
Action,
1701 Path = LastActiveFile;
1703 LastActiveFile =
Path.str();
1704 if (!PreambleTasks) {
1711 std::lock_guard<Semaphore> BarrierLock(Sem);
1712 WithContext WC(std::move(Ctx));
1713 WithContext WithProvidedContext(Opts.ContextProvider(Path));
1720 llvm::unique_function<
void(llvm::Expected<InputsAndAST>)>
Action,
1722 auto It = Files.find(File);
1723 if (It == Files.end()) {
1724 Action(llvm::make_error<LSPError>(
1728 LastActiveFile = File.str();
1730 It->second->Worker->runWithAST(
Name, std::move(
Action), Invalidation);
1736 auto It = Files.find(File);
1737 if (It == Files.end()) {
1738 Action(llvm::make_error<LSPError>(
1739 "trying to get preamble for non-added document",
1743 LastActiveFile = File.str();
1745 if (!PreambleTasks) {
1748 std::shared_ptr<const ASTSignals> Signals;
1749 std::shared_ptr<const PreambleData>
Preamble =
1750 It->second->Worker->getPossiblyStalePreamble(&Signals);
1751 WithContext WithProvidedContext(Opts.ContextProvider(File));
1753 It->second->Worker->getCurrentCompileCommand(),
1758 std::shared_ptr<const ASTWorker> Worker = It->second->Worker.lock();
1759 auto Task = [Worker, Consistency,
Name =
Name.str(), File = File.str(),
1760 Contents = It->second->Contents,
1761 Command = Worker->getCurrentCompileCommand(),
1766 llvm::errs() <<
"Signalled during preamble action: " <<
Name <<
"\n";
1767 crashDumpCompileCommand(llvm::errs(), Command);
1768 crashDumpFileContents(llvm::errs(), Contents);
1770 std::shared_ptr<const PreambleData>
Preamble;
1771 if (Consistency == PreambleConsistency::Stale) {
1775 Worker->waitForFirstPreamble();
1777 std::shared_ptr<const ASTSignals> Signals;
1778 Preamble = Worker->getPossiblyStalePreamble(&Signals);
1780 std::lock_guard<Semaphore> BarrierLock(Barrier);
1784 WithContext WithProvidedContext(Opts.ContextProvider(File));
1788 PreambleTasks->runAsync(
"task:" + llvm::sys::path::filename(File),
1793 llvm::StringMap<TUScheduler::FileStats> Result;
1794 for (
const auto &PathAndFile : Files)
1795 Result.try_emplace(PathAndFile.first(),
1796 PathAndFile.second->Worker->stats());
1801 std::vector<Path> Result;
1802 for (
auto &&PathAndFile : Files) {
1803 if (!PathAndFile.second->Worker->isASTCached())
1805 Result.push_back(std::string(PathAndFile.first()));
1810 DebouncePolicy::clock::duration
1812 assert(
Min <=
Max &&
"Invalid policy");
1813 if (History.empty())
1818 History = History.take_back(15);
1819 llvm::SmallVector<clock::duration, 15> Recent(History.begin(), History.end());
1820 auto *Median = Recent.begin() + Recent.size() / 2;
1821 std::nth_element(Recent.begin(), Median, Recent.end());
1824 std::chrono::duration_cast<clock::duration>(
RebuildRatio * *Median);
1842 .
addUsage(Opts.StorePreamblesInMemory ? Elem.second.UsedBytesPreamble
1845 MT.
child(
"header_includer_cache").
addUsage(HeaderIncluders->getUsedBytes());