65 #include "clang/Frontend/CompilerInvocation.h"
66 #include "clang/Tooling/CompilationDatabase.h"
67 #include "llvm/ADT/FunctionExtras.h"
68 #include "llvm/ADT/None.h"
69 #include "llvm/ADT/Optional.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"
85 #include <condition_variable>
92 #include <type_traits>
98 using std::chrono::steady_clock;
105 constexpr trace::Metric
106 PreambleBuildFilesystemLatency(
"preamble_fs_latency",
110 constexpr trace::Metric PreambleBuildFilesystemLatencyRatio(
113 constexpr trace::Metric PreambleBuildSize(
"preamble_build_size",
115 constexpr trace::Metric PreambleSerializedSize(
"preamble_serialized_size",
118 void reportPreambleBuild(
const PreambleBuildStats &Stats,
119 bool IsFirstPreamble) {
120 auto RecordWithLabel = [&Stats](llvm::StringRef
Label) {
123 PreambleBuildFilesystemLatencyRatio.record(
127 static llvm::once_flag OnceFlag;
128 llvm::call_once(OnceFlag, [&] { RecordWithLabel(
"first_build"); });
129 RecordWithLabel(IsFirstPreamble ?
"first_build_for_file" :
"rebuild");
131 PreambleBuildSize.record(Stats.
BuildSize);
142 return llvm::StringRef(*File);
152 using Key =
const ASTWorker *;
154 ASTCache(
unsigned MaxRetainedASTs) : MaxRetainedASTs(MaxRetainedASTs) {}
159 std::lock_guard<std::mutex> Lock(Mut);
160 auto It = findByKey(
K);
161 if (It == LRU.end() || !It->second)
163 return It->second->getUsedBytes();
168 void put(
Key K, std::unique_ptr<ParsedAST> V) {
169 std::unique_lock<std::mutex> Lock(Mut);
170 assert(findByKey(
K) == LRU.end());
172 LRU.insert(LRU.begin(), {K, std::move(V)});
173 if (LRU.size() <= MaxRetainedASTs)
176 std::unique_ptr<ParsedAST> ForCleanup = std::move(LRU.back().second);
187 llvm::Optional<std::unique_ptr<ParsedAST>>
190 std::unique_lock<std::mutex> Lock(Mut);
191 auto Existing = findByKey(
K);
192 if (Existing == LRU.end()) {
194 AccessMetric->record(1,
"miss");
198 AccessMetric->record(1,
"hit");
199 std::unique_ptr<ParsedAST> V = std::move(Existing->second);
204 return llvm::Optional<std::unique_ptr<ParsedAST>>(std::move(V));
208 using KVPair = std::pair<Key, std::unique_ptr<ParsedAST>>;
210 std::vector<KVPair>::iterator findByKey(
Key K) {
211 return llvm::find_if(LRU, [
K](
const KVPair &P) {
return P.first ==
K; });
215 unsigned MaxRetainedASTs;
218 std::vector<KVPair> LRU;
251 llvm::BumpPtrAllocator Arena;
258 llvm::StringMap<Association, llvm::BumpPtrAllocator &> HeaderToMain;
259 llvm::StringMap<Association *, llvm::BumpPtrAllocator &> MainToFirst;
260 std::atomic<size_t> UsedBytes;
261 mutable std::mutex Mu;
263 void invalidate(Association *First) {
264 Association *Current = First;
266 Association *Next = Current->Next;
267 Current->Next =
nullptr;
269 }
while (Current != First);
273 Association *associate(llvm::StringRef
MainFile,
274 llvm::ArrayRef<std::string> Headers) {
275 Association *First =
nullptr, *Prev =
nullptr;
276 for (
const std::string &Header : Headers) {
277 auto &Assoc = HeaderToMain[Header];
292 void updateMemoryUsage() {
293 auto StringMapHeap = [](
const auto &Map) {
296 return Map.getNumBuckets() * (
sizeof(
void *) +
sizeof(
unsigned));
298 size_t Usage =
Arena.getTotalMemory() + StringMapHeap(MainToFirst) +
299 StringMapHeap(HeaderToMain) +
sizeof(*this);
300 UsedBytes.store(
Usage, std::memory_order_release);
311 std::lock_guard<std::mutex> Lock(Mu);
312 auto It = MainToFirst.try_emplace(
MainFile,
nullptr);
313 Association *&First = It.first->second;
316 First = associate(It.first->first(), Headers);
324 std::lock_guard<std::mutex> Lock(Mu);
325 Association *&First = MainToFirst[
MainFile];
336 std::lock_guard<std::mutex> Lock(Mu);
337 return HeaderToMain.lookup(Header).MainFile.str();
341 return UsedBytes.load(std::memory_order_acquire);
347 bool isReliable(
const tooling::CompileCommand &Cmd) {
348 return Cmd.Heuristic.empty();
353 class SynchronizedTUStatus {
355 SynchronizedTUStatus(
PathRef FileName, ParsingCallbacks &Callbacks)
358 void update(llvm::function_ref<
void(TUStatus &)> Mutator) {
359 std::lock_guard<std::mutex> Lock(StatusMu);
366 std::lock_guard<std::mutex> Lock(StatusMu);
371 void emitStatusLocked() {
373 Callbacks.onFileUpdated(FileName, Status);
380 bool CanPublish =
true;
381 ParsingCallbacks &Callbacks;
388 class PreambleThread {
390 PreambleThread(llvm::StringRef FileName, ParsingCallbacks &Callbacks,
391 bool StorePreambleInMemory,
bool RunSync,
392 SynchronizedTUStatus &Status,
393 TUScheduler::HeaderIncluderCache &HeaderIncluders,
396 StoreInMemory(StorePreambleInMemory), RunSync(RunSync), Status(Status),
397 ASTPeer(AW), HeaderIncluders(HeaderIncluders) {}
402 void update(std::unique_ptr<CompilerInvocation>
CI, ParseInputs PI,
407 build(std::move(Req));
408 Status.update([](TUStatus &Status) {
414 std::unique_lock<std::mutex> Lock(Mutex);
419 ReqCV.wait(Lock, [
this] {
422 NextReq = std::move(Req);
432 std::unique_lock<std::mutex> Lock(Mutex);
433 assert(!CurrentReq &&
"Already processing a request?");
435 ReqCV.wait(Lock, [
this] {
return NextReq || Done; });
438 CurrentReq = std::move(*NextReq);
443 WithContext Guard(std::move(CurrentReq->Ctx));
449 build(std::move(*CurrentReq));
451 bool IsEmpty =
false;
453 std::lock_guard<std::mutex> Lock(Mutex);
461 Status.update([](TUStatus &Status) {
467 dlog(
"Preamble worker for {0} stopped", FileName);
472 dlog(
"Preamble worker for {0} received stop", FileName);
474 std::lock_guard<std::mutex> Lock(Mutex);
482 bool blockUntilIdle(Deadline Timeout)
const {
483 std::unique_lock<std::mutex> Lock(Mutex);
484 return wait(Lock, ReqCV, Timeout, [&] {
return !NextReq && !CurrentReq; });
491 std::unique_ptr<CompilerInvocation>
CI;
499 std::lock_guard<std::mutex> Lock(Mutex);
505 void build(Request Req);
507 mutable std::mutex Mutex;
509 llvm::Optional<Request> NextReq;
510 llvm::Optional<Request> CurrentReq;
513 mutable std::condition_variable ReqCV;
515 std::shared_ptr<const PreambleData> LatestBuild;
518 ParsingCallbacks &Callbacks;
519 const bool StoreInMemory;
522 SynchronizedTUStatus &Status;
524 TUScheduler::HeaderIncluderCache &HeaderIncluders;
527 class ASTWorkerHandle;
540 friend class ASTWorkerHandle;
541 ASTWorker(
PathRef FileName,
const GlobalCompilationDatabase &CDB,
542 TUScheduler::ASTCache &LRUCache,
543 TUScheduler::HeaderIncluderCache &HeaderIncluders,
544 Semaphore &Barrier,
bool RunSync,
const TUScheduler::Options &Opts,
545 ParsingCallbacks &Callbacks);
553 static ASTWorkerHandle
554 create(
PathRef FileName,
const GlobalCompilationDatabase &CDB,
555 TUScheduler::ASTCache &IdleASTs,
556 TUScheduler::HeaderIncluderCache &HeaderIncluders,
557 AsyncTaskRunner *Tasks, Semaphore &Barrier,
558 const TUScheduler::Options &Opts, ParsingCallbacks &Callbacks);
563 runWithAST(llvm::StringRef
Name,
564 llvm::unique_function<
void(llvm::Expected<InputsAndAST>)>
Action,
566 bool blockUntilIdle(Deadline Timeout)
const;
568 std::shared_ptr<const PreambleData> getPossiblyStalePreamble(
569 std::shared_ptr<const ASTSignals> *ASTSignals =
nullptr)
const;
575 void updatePreamble(std::unique_ptr<CompilerInvocation>
CI, ParseInputs PI,
576 std::shared_ptr<const PreambleData>
Preamble,
581 void getCurrentPreamble(
582 llvm::unique_function<
void(std::shared_ptr<const PreambleData>)>);
584 tooling::CompileCommand getCurrentCompileCommand()
const;
590 void waitForFirstPreamble()
const;
592 TUScheduler::FileStats stats()
const;
593 bool isASTCached()
const;
611 void generateDiagnostics(std::unique_ptr<CompilerInvocation> Invocation,
614 void updateASTSignals(ParsedAST &AST);
624 void startTask(llvm::StringRef
Name, llvm::unique_function<
void()> Task,
625 llvm::Optional<UpdateType>
Update,
628 void runTask(llvm::StringRef
Name, llvm::function_ref<
void()> Task);
634 Deadline scheduleLocked();
636 bool shouldSkipHeadLocked()
const;
650 TUScheduler::ASTCache &IdleASTs;
651 TUScheduler::HeaderIncluderCache &HeaderIncluders;
654 const DebouncePolicy UpdateDebounce;
658 const std::function<Context(llvm::StringRef)> ContextProvider;
659 const GlobalCompilationDatabase &CDB;
661 ParsingCallbacks &Callbacks;
665 bool RanASTCallback =
false;
667 mutable std::mutex Mutex;
671 ParseInputs FileInputs;
673 llvm::SmallVector<DebouncePolicy::clock::duration>
677 std::deque<Request> Requests;
678 llvm::Optional<Request> CurrentRequest;
681 mutable std::condition_variable RequestsCV;
682 std::shared_ptr<const ASTSignals> LatestASTSignals;
686 llvm::Optional<std::shared_ptr<const PreambleData>> LatestPreamble;
687 std::deque<Request> PreambleRequests;
690 mutable std::condition_variable PreambleCV;
693 std::mutex PublishMu;
700 bool CanPublishResults =
true;
701 std::atomic<unsigned> ASTBuildCount = {0};
702 std::atomic<unsigned> PreambleBuildCount = {0};
704 SynchronizedTUStatus Status;
705 PreambleThread PreamblePeer;
712 class ASTWorkerHandle {
713 friend class ASTWorker;
714 ASTWorkerHandle(std::shared_ptr<ASTWorker> Worker)
715 : Worker(std::move(Worker)) {
716 assert(this->Worker);
720 ASTWorkerHandle(
const ASTWorkerHandle &) =
delete;
721 ASTWorkerHandle &operator=(
const ASTWorkerHandle &) =
delete;
722 ASTWorkerHandle(ASTWorkerHandle &&) =
default;
723 ASTWorkerHandle &operator=(ASTWorkerHandle &&) =
default;
730 ASTWorker &operator*() {
731 assert(Worker &&
"Handle was moved from");
735 ASTWorker *operator->() {
736 assert(Worker &&
"Handle was moved from");
744 std::shared_ptr<const ASTWorker> lock() {
return Worker; }
747 std::shared_ptr<ASTWorker> Worker;
751 ASTWorker::create(
PathRef FileName,
const GlobalCompilationDatabase &CDB,
752 TUScheduler::ASTCache &IdleASTs,
753 TUScheduler::HeaderIncluderCache &HeaderIncluders,
754 AsyncTaskRunner *Tasks, Semaphore &Barrier,
755 const TUScheduler::Options &Opts,
756 ParsingCallbacks &Callbacks) {
757 std::shared_ptr<ASTWorker> Worker(
758 new ASTWorker(FileName, CDB, IdleASTs, HeaderIncluders, Barrier,
759 !Tasks, Opts, Callbacks));
761 Tasks->
runAsync(
"ASTWorker:" + llvm::sys::path::filename(FileName),
762 [Worker]() { Worker->run(); });
763 Tasks->
runAsync(
"PreambleWorker:" + llvm::sys::path::filename(FileName),
764 [Worker]() { Worker->PreamblePeer.run(); });
767 return ASTWorkerHandle(std::move(Worker));
770 ASTWorker::ASTWorker(
PathRef FileName,
const GlobalCompilationDatabase &CDB,
771 TUScheduler::ASTCache &LRUCache,
772 TUScheduler::HeaderIncluderCache &HeaderIncluders,
773 Semaphore &Barrier,
bool RunSync,
774 const TUScheduler::Options &Opts,
775 ParsingCallbacks &Callbacks)
776 : IdleASTs(LRUCache), HeaderIncluders(HeaderIncluders), RunSync(RunSync),
778 ContextProvider(Opts.ContextProvider), CDB(CDB), Callbacks(Callbacks),
779 Barrier(Barrier), Done(false), Status(
FileName, Callbacks),
780 PreamblePeer(
FileName, Callbacks, Opts.StorePreamblesInMemory, RunSync,
781 Status, HeaderIncluders, *this) {
785 FileInputs.CompileCommand = CDB.getFallbackCommand(FileName);
788 ASTWorker::~ASTWorker() {
792 std::lock_guard<std::mutex> Lock(Mutex);
793 assert(Done &&
"handle was not destroyed");
794 assert(Requests.empty() && !CurrentRequest &&
795 "unprocessed requests when destroying ASTWorker");
801 llvm::StringLiteral TaskName =
"Update";
802 auto Task = [=]()
mutable {
808 auto Cmd = CDB.getCompileCommand(FileName);
811 if (!Cmd || !isReliable(*Cmd)) {
812 std::string ProxyFile = HeaderIncluders.get(FileName);
813 if (!ProxyFile.empty()) {
814 auto ProxyCmd = CDB.getCompileCommand(ProxyFile);
815 if (!ProxyCmd || !isReliable(*ProxyCmd)) {
817 HeaderIncluders.remove(ProxyFile);
820 Cmd = tooling::transferCompileCommand(std::move(*ProxyCmd), FileName);
825 Inputs.CompileCommand = std::move(*Cmd);
827 Inputs.CompileCommand = CDB.getFallbackCommand(FileName);
829 bool InputsAreTheSame =
830 std::tie(FileInputs.CompileCommand, FileInputs.Contents) ==
833 if (!InputsAreTheSame) {
835 RanASTCallback =
false;
840 std::lock_guard<std::mutex> Lock(Mutex);
844 log(
"ASTWorker building file {0} version {1} with command {2}\n[{3}]\n{4}",
845 FileName,
Inputs.Version,
Inputs.CompileCommand.Heuristic,
846 Inputs.CompileCommand.Directory,
849 StoreDiags CompilerInvocationDiagConsumer;
850 std::vector<std::string> CC1Args;
852 Inputs, CompilerInvocationDiagConsumer, &CC1Args);
854 if (!CC1Args.empty())
855 vlog(
"Driver produced command: cc1 {0}",
printArgv(CC1Args));
856 std::vector<Diag> CompilerInvocationDiags =
857 CompilerInvocationDiagConsumer.take();
859 elog(
"Could not build CompilerInvocation for file {0}", FileName);
862 RanASTCallback =
false;
864 Callbacks.onFailedAST(FileName,
Inputs.Version,
865 std::move(CompilerInvocationDiags),
866 [&](llvm::function_ref<
void()> Publish) {
870 std::lock_guard<std::mutex> Lock(PublishMu);
871 if (CanPublishResults)
876 LatestPreamble.emplace();
879 PreambleCV.notify_all();
883 PreamblePeer.update(std::move(Invocation), std::move(
Inputs),
884 std::move(CompilerInvocationDiags),
WantDiags);
885 std::unique_lock<std::mutex> Lock(Mutex);
886 PreambleCV.wait(Lock, [
this] {
892 return LatestPreamble || !PreambleRequests.empty() || Done;
896 TUScheduler::NoInvalidation);
899 void ASTWorker::runWithAST(
900 llvm::StringRef
Name,
901 llvm::unique_function<
void(llvm::Expected<InputsAndAST>)>
Action,
902 TUScheduler::ASTActionInvalidation Invalidation) {
904 static constexpr trace::Metric ASTAccessForRead(
908 return Action(llvm::make_error<CancelledError>(Reason));
909 llvm::Optional<std::unique_ptr<ParsedAST>>
AST =
910 IdleASTs.take(
this, &ASTAccessForRead);
912 StoreDiags CompilerInvocationDiagConsumer;
913 std::unique_ptr<CompilerInvocation> Invocation =
916 vlog(
"ASTWorker rebuilding evicted AST to run {0}: {1} version {2}",
Name,
917 FileName, FileInputs.Version);
921 llvm::Optional<ParsedAST> NewAST;
923 NewAST = ParsedAST::build(FileName, FileInputs, std::move(Invocation),
924 CompilerInvocationDiagConsumer.take(),
925 getPossiblyStalePreamble());
928 AST = NewAST ? std::make_unique<ParsedAST>(std::move(*NewAST)) : nullptr;
931 auto _ = llvm::make_scope_exit(
932 [&
AST,
this]() { IdleASTs.put(
this, std::move(*
AST)); });
935 return Action(
error(llvm::errc::invalid_argument,
"invalid AST"));
936 vlog(
"ASTWorker running {0} on version {2} of {1}",
Name, FileName,
940 startTask(
Name, std::move(Task), None, Invalidation);
944 static void crashDumpCompileCommand(llvm::raw_ostream &
OS,
945 const tooling::CompileCommand &Command) {
946 OS <<
" Filename: " << Command.Filename <<
"\n";
947 OS <<
" Directory: " << Command.Directory <<
"\n";
948 OS <<
" Command Line:";
949 for (
auto &Arg : Command.CommandLine) {
956 static void crashDumpFileContents(llvm::raw_ostream &
OS,
957 const std::string &Contents) {
962 if (getenv(
"CLANGD_CRASH_DUMP_SOURCE")) {
963 OS <<
" Contents:\n";
964 OS << Contents <<
"\n";
969 static void crashDumpParseInputs(llvm::raw_ostream &
OS,
970 const ParseInputs &FileInputs) {
971 auto &Command = FileInputs.CompileCommand;
972 crashDumpCompileCommand(
OS, Command);
973 OS <<
" Version: " << FileInputs.Version <<
"\n";
974 crashDumpFileContents(
OS, FileInputs.Contents);
977 void PreambleThread::build(Request Req) {
978 assert(Req.CI &&
"Got preamble request with null compiler invocation");
979 const ParseInputs &
Inputs = Req.Inputs;
980 bool ReusedPreamble =
false;
982 Status.update([&](TUStatus &Status) {
983 Status.PreambleActivity = PreambleAction::Building;
985 auto _ = llvm::make_scope_exit([
this, &Req, &ReusedPreamble] {
986 ASTPeer.updatePreamble(std::move(Req.CI), std::move(Req.Inputs),
987 LatestBuild, std::move(Req.CIDiags),
988 std::move(Req.WantDiags));
990 Callbacks.onPreamblePublished(FileName);
993 if (!LatestBuild ||
Inputs.ForceRebuild) {
994 vlog(
"Building first preamble for {0} version {1}", FileName,
997 vlog(
"Reusing preamble version {0} for version {1} of {2}",
998 LatestBuild->Version,
Inputs.Version, FileName);
999 ReusedPreamble =
true;
1002 vlog(
"Rebuilding invalidated preamble for {0} version {1} (previous was "
1004 FileName,
Inputs.Version, LatestBuild->Version);
1007 ThreadCrashReporter ScopedReporter([&
Inputs]() {
1008 llvm::errs() <<
"Signalled while building preamble\n";
1009 crashDumpParseInputs(llvm::errs(),
Inputs);
1012 PreambleBuildStats Stats;
1013 bool IsFirstPreamble = !LatestBuild;
1015 FileName, *Req.CI,
Inputs, StoreInMemory,
1016 [&](ASTContext &
Ctx, Preprocessor &
PP,
1017 const CanonicalIncludes &CanonIncludes) {
1018 Callbacks.onPreambleAST(FileName, Inputs.Version, *Req.CI, Ctx, PP,
1024 reportPreambleBuild(Stats, IsFirstPreamble);
1025 if (isReliable(LatestBuild->CompileCommand))
1026 HeaderIncluders.update(FileName, LatestBuild->Includes.allHeaders());
1029 void ASTWorker::updatePreamble(std::unique_ptr<CompilerInvocation>
CI,
1031 std::shared_ptr<const PreambleData>
Preamble,
1034 llvm::StringLiteral TaskName =
"Build AST";
1042 if (!LatestPreamble ||
Preamble != *LatestPreamble) {
1043 ++PreambleBuildCount;
1045 IdleASTs.take(
this);
1046 RanASTCallback =
false;
1047 std::lock_guard<std::mutex> Lock(Mutex);
1051 std::swap(*LatestPreamble,
Preamble);
1053 LatestPreamble = std::move(
Preamble);
1056 PreambleCV.notify_all();
1066 generateDiagnostics(std::move(
CI), std::move(PI), std::move(
CIDiags));
1069 runTask(TaskName, Task);
1073 std::lock_guard<std::mutex> Lock(Mutex);
1074 PreambleRequests.push_back({std::move(Task), std::string(TaskName),
1075 steady_clock::now(), Context::current().clone(),
1076 llvm::None, llvm::None,
1077 TUScheduler::NoInvalidation,
nullptr});
1079 PreambleCV.notify_all();
1080 RequestsCV.notify_all();
1083 void ASTWorker::updateASTSignals(ParsedAST &
AST) {
1084 auto Signals = std::make_shared<const ASTSignals>(ASTSignals::derive(
AST));
1088 std::lock_guard<std::mutex> Lock(Mutex);
1089 std::swap(LatestASTSignals, Signals);
1093 void ASTWorker::generateDiagnostics(
1094 std::unique_ptr<CompilerInvocation> Invocation, ParseInputs
Inputs,
1097 static constexpr trace::Metric ASTAccessForDiag(
1100 assert(LatestPreamble);
1103 std::lock_guard<std::mutex> Lock(PublishMu);
1104 if (!CanPublishResults)
1108 bool InputsAreLatest =
1109 std::tie(FileInputs.CompileCommand, FileInputs.Contents) ==
1116 if (InputsAreLatest && RanASTCallback)
1120 std::string TaskName = llvm::formatv(
"Build AST ({0})",
Inputs.Version);
1121 Status.update([&](TUStatus &Status) {
1122 Status.ASTActivity.K = ASTAction::Building;
1123 Status.ASTActivity.Name = std::move(TaskName);
1128 llvm::Optional<std::unique_ptr<ParsedAST>>
AST =
1129 IdleASTs.take(
this, &ASTAccessForDiag);
1130 if (!
AST || !InputsAreLatest) {
1131 auto RebuildStartTime = DebouncePolicy::clock::now();
1132 llvm::Optional<ParsedAST> NewAST = ParsedAST::build(
1133 FileName,
Inputs, std::move(Invocation),
CIDiags, *LatestPreamble);
1134 auto RebuildDuration = DebouncePolicy::clock::now() - RebuildStartTime;
1138 std::unique_lock<std::mutex> Lock(Mutex, std::try_to_lock);
1139 if (Lock.owns_lock()) {
1142 if (RebuildTimes.size() == RebuildTimes.capacity())
1143 RebuildTimes.erase(RebuildTimes.begin());
1144 RebuildTimes.push_back(RebuildDuration);
1147 Status.update([&](TUStatus &Status) {
1148 Status.Details.ReuseAST =
false;
1149 Status.Details.BuildFailed = !NewAST;
1151 AST = NewAST ? std::make_unique<ParsedAST>(std::move(*NewAST)) : nullptr;
1153 log(
"Skipping rebuild of the AST for {0}, inputs are the same.", FileName);
1154 Status.update([](TUStatus &Status) {
1155 Status.Details.ReuseAST =
true;
1156 Status.Details.BuildFailed =
false;
1161 auto RunPublish = [&](llvm::function_ref<void()> Publish) {
1164 std::lock_guard<std::mutex> Lock(PublishMu);
1165 if (CanPublishResults)
1169 trace::Span Span(
"Running main AST callback");
1170 Callbacks.onMainAST(FileName, **
AST, RunPublish);
1171 updateASTSignals(**
AST);
1177 Callbacks.onFailedAST(FileName,
Inputs.Version,
CIDiags, RunPublish);
1183 if (InputsAreLatest) {
1184 RanASTCallback = *
AST !=
nullptr;
1185 IdleASTs.put(
this, std::move(*
AST));
1189 std::shared_ptr<const PreambleData> ASTWorker::getPossiblyStalePreamble(
1190 std::shared_ptr<const ASTSignals> *ASTSignals)
const {
1191 std::lock_guard<std::mutex> Lock(Mutex);
1193 *ASTSignals = LatestASTSignals;
1194 return LatestPreamble ? *LatestPreamble :
nullptr;
1197 void ASTWorker::waitForFirstPreamble()
const {
1198 std::unique_lock<std::mutex> Lock(Mutex);
1199 PreambleCV.wait(Lock, [
this] {
return LatestPreamble || Done; });
1202 tooling::CompileCommand ASTWorker::getCurrentCompileCommand()
const {
1203 std::unique_lock<std::mutex> Lock(Mutex);
1204 return FileInputs.CompileCommand;
1207 TUScheduler::FileStats ASTWorker::stats()
const {
1208 TUScheduler::FileStats Result;
1209 Result.ASTBuilds = ASTBuildCount;
1210 Result.PreambleBuilds = PreambleBuildCount;
1214 Result.UsedBytesAST = IdleASTs.getUsedBytes(
this);
1215 if (
auto Preamble = getPossiblyStalePreamble())
1216 Result.UsedBytesPreamble =
Preamble->Preamble.getSize();
1220 bool ASTWorker::isASTCached()
const {
return IdleASTs.getUsedBytes(
this) != 0; }
1222 void ASTWorker::stop() {
1224 std::lock_guard<std::mutex> Lock(PublishMu);
1225 CanPublishResults =
false;
1228 std::lock_guard<std::mutex> Lock(Mutex);
1229 assert(!Done &&
"stop() called twice");
1232 PreamblePeer.stop();
1234 PreambleCV.notify_all();
1236 RequestsCV.notify_all();
1239 void ASTWorker::runTask(llvm::StringRef
Name, llvm::function_ref<
void()> Task) {
1240 ThreadCrashReporter ScopedReporter([
this,
Name]() {
1241 llvm::errs() <<
"Signalled during AST worker action: " <<
Name <<
"\n";
1242 crashDumpParseInputs(llvm::errs(), FileInputs);
1245 WithContext WithProvidedContext(ContextProvider(FileName));
1249 void ASTWorker::startTask(llvm::StringRef
Name,
1250 llvm::unique_function<
void()> Task,
1251 llvm::Optional<UpdateType>
Update,
1252 TUScheduler::ASTActionInvalidation Invalidation) {
1254 assert(!Done &&
"running a task after stop()");
1255 runTask(
Name, Task);
1260 std::lock_guard<std::mutex> Lock(Mutex);
1261 assert(!Done &&
"running a task after stop()");
1264 for (
auto &R : llvm::reverse(Requests)) {
1265 if (R.InvalidationPolicy == TUScheduler::InvalidateOnUpdate)
1267 if (R.Update && R.Update->ContentChanged)
1276 WithContext WC(std::move(
Ctx));
1278 static_cast<int>(ErrorCode::ContentModified));
1286 WithContext WC(
Ctx.clone());
1291 llvm::json::Array PreambleRequestsNames;
1292 for (
const auto &Req : PreambleRequests)
1293 PreambleRequestsNames.push_back(Req.Name);
1295 std::move(PreambleRequestsNames));
1296 llvm::json::Array RequestsNames;
1297 for (
const auto &Req : Requests)
1298 RequestsNames.push_back(Req.Name);
1303 QueueCtx = Context::current().clone();
1305 Requests.push_back({std::move(Task), std::string(
Name), steady_clock::now(),
1309 RequestsCV.notify_all();
1312 void ASTWorker::run() {
1315 std::unique_lock<std::mutex> Lock(Mutex);
1316 assert(!CurrentRequest &&
"A task is already running, multiple workers?");
1317 for (
auto Wait = scheduleLocked(); !Wait.expired();
1318 Wait = scheduleLocked()) {
1319 assert(PreambleRequests.empty() &&
1320 "Preamble updates should be scheduled immediately");
1322 if (Requests.empty())
1329 llvm::Optional<WithContext>
Ctx;
1330 llvm::Optional<trace::Span>
Tracer;
1331 if (!Requests.empty()) {
1332 Ctx.emplace(Requests.front().Ctx.clone());
1333 Tracer.emplace(
"Debounce");
1335 if (!(Wait == Deadline::infinity())) {
1336 Status.update([&](TUStatus &Status) {
1337 Status.ASTActivity.K = ASTAction::Queued;
1338 Status.ASTActivity.Name = Requests.front().
Name;
1341 std::chrono::duration_cast<std::chrono::milliseconds>(
1342 Wait.time() - steady_clock::now())
1347 wait(Lock, RequestsCV, Wait);
1351 if (!PreambleRequests.empty()) {
1352 CurrentRequest = std::move(PreambleRequests.front());
1353 PreambleRequests.pop_front();
1355 CurrentRequest = std::move(Requests.front());
1356 Requests.pop_front();
1361 CurrentRequest->QueueCtx.reset();
1366 std::unique_lock<Semaphore> Lock(Barrier, std::try_to_lock);
1367 if (!Lock.owns_lock()) {
1368 Status.update([&](TUStatus &Status) {
1369 Status.ASTActivity.K = ASTAction::Queued;
1370 Status.ASTActivity.Name = CurrentRequest->Name;
1374 WithContext Guard(std::move(CurrentRequest->Ctx));
1375 Status.update([&](TUStatus &Status) {
1376 Status.ASTActivity.K = ASTAction::RunningAction;
1377 Status.ASTActivity.Name = CurrentRequest->Name;
1379 runTask(CurrentRequest->Name, CurrentRequest->Action);
1382 bool IsEmpty =
false;
1384 std::lock_guard<std::mutex> Lock(Mutex);
1385 CurrentRequest.reset();
1386 IsEmpty = Requests.empty() && PreambleRequests.empty();
1389 Status.update([&](TUStatus &Status) {
1390 Status.ASTActivity.K = ASTAction::Idle;
1391 Status.ASTActivity.Name =
"";
1394 RequestsCV.notify_all();
1398 Deadline ASTWorker::scheduleLocked() {
1400 if (!PreambleRequests.empty())
1401 return Deadline::zero();
1402 if (Requests.empty())
1403 return Deadline::infinity();
1405 for (
auto I = Requests.begin(),
E = Requests.end(); I !=
E; ++I) {
1408 if (I->Update == None)
1413 if (I->Update == None) {
1414 Request R = std::move(*I);
1416 Requests.push_front(std::move(R));
1417 return Deadline::zero();
1420 if (I->Update->Diagnostics == WantDiagnostics::Yes)
1421 I->Update->Diagnostics = WantDiagnostics::Auto;
1424 while (shouldSkipHeadLocked()) {
1425 vlog(
"ASTWorker skipping {0} for {1}", Requests.front().Name, FileName);
1426 Requests.pop_front();
1428 assert(!Requests.empty() &&
"skipped the whole queue");
1433 for (
const auto &R : Requests)
1434 if (R.Update == None || R.Update->Diagnostics == WantDiagnostics::Yes)
1435 return Deadline::zero();
1437 Deadline
D(Requests.front().AddTime + UpdateDebounce.compute(RebuildTimes));
1442 bool ASTWorker::shouldSkipHeadLocked()
const {
1443 assert(!Requests.empty());
1444 auto Next = Requests.begin();
1445 auto Update = Next->Update;
1451 if (Next == Requests.end() || !Next->Update)
1454 switch (
Update->Diagnostics) {
1455 case WantDiagnostics::Yes:
1457 case WantDiagnostics::No:
1459 case WantDiagnostics::Auto:
1461 for (; Next != Requests.end(); ++Next)
1462 if (Next->Update && Next->Update->Diagnostics != WantDiagnostics::No)
1466 llvm_unreachable(
"Unknown WantDiagnostics");
1469 bool ASTWorker::blockUntilIdle(Deadline Timeout)
const {
1470 auto WaitUntilASTWorkerIsIdle = [&] {
1471 std::unique_lock<std::mutex> Lock(Mutex);
1472 return wait(Lock, RequestsCV, Timeout, [&] {
1473 return PreambleRequests.empty() && Requests.empty() && !CurrentRequest;
1478 if (!WaitUntilASTWorkerIsIdle())
1483 if (!PreamblePeer.blockUntilIdle(Timeout))
1485 assert(Requests.empty() &&
1486 "No new normal tasks can be scheduled concurrently with "
1487 "blockUntilIdle(): ASTWorker isn't threadsafe");
1489 return WaitUntilASTWorkerIsIdle();
1496 std::string renderTUAction(
const PreambleAction PA,
const ASTAction &AA) {
1497 llvm::SmallVector<std::string, 2> Result;
1499 case PreambleAction::Building:
1500 Result.push_back(
"parsing includes");
1502 case PreambleAction::Idle:
1507 case ASTAction::Queued:
1508 Result.push_back(
"file is queued");
1510 case ASTAction::RunningAction:
1511 Result.push_back(
"running " + AA.Name);
1513 case ASTAction::Building:
1514 Result.push_back(
"parsing main file");
1516 case ASTAction::Idle:
1528 return llvm::heavyweight_hardware_concurrency().compute_thread_count();
1534 FStatus.
state = renderTUAction(PreambleActivity, ASTActivity);
1546 std::unique_ptr<ParsingCallbacks> Callbacks)
1547 : CDB(CDB), Opts(Opts),
1548 Callbacks(Callbacks ? std::move(Callbacks)
1550 Barrier(Opts.AsyncThreadsCount), QuickRunBarrier(Opts.AsyncThreadsCount),
1552 std::make_unique<
ASTCache>(Opts.RetentionPolicy.MaxRetainedASTs)),
1555 if (!Opts.ContextProvider) {
1560 if (0 < Opts.AsyncThreadsCount) {
1561 PreambleTasks.emplace();
1562 WorkerThreads.emplace();
1572 PreambleTasks->wait();
1574 WorkerThreads->wait();
1578 for (
auto &File : Files)
1579 if (!File.getValue()->Worker->blockUntilIdle(
D))
1582 if (!PreambleTasks->wait(
D))
1589 std::unique_ptr<FileData> &FD = Files[File];
1590 bool NewFile = FD ==
nullptr;
1594 ASTWorkerHandle Worker =
1595 ASTWorker::create(File, CDB, *IdleASTs, *HeaderIncluders,
1596 WorkerThreads ? WorkerThreads.getPointer() :
nullptr,
1597 Barrier, Opts, *Callbacks);
1598 FD = std::unique_ptr<FileData>(
1601 }
else if (FD->Contents !=
Inputs.Contents) {
1603 FD->Contents =
Inputs.Contents;
1609 LastActiveFile = File.str();
1614 bool Removed = Files.erase(File);
1616 elog(
"Trying to remove file from TUScheduler that is not tracked: {0}",
1625 llvm::unique_function<
void()>
Action) {
1630 llvm::unique_function<
void()>
Action) {
1633 runWithSemaphore(
Name,
Path, std::move(
Action), QuickRunBarrier);
1636 void TUScheduler::runWithSemaphore(llvm::StringRef
Name, llvm::StringRef
Path,
1637 llvm::unique_function<
void()>
Action,
1640 Path = LastActiveFile;
1642 LastActiveFile =
Path.str();
1643 if (!PreambleTasks) {
1650 std::lock_guard<Semaphore> BarrierLock(Sem);
1651 WithContext WC(std::move(Ctx));
1652 WithContext WithProvidedContext(Opts.ContextProvider(Path));
1659 llvm::unique_function<
void(llvm::Expected<InputsAndAST>)>
Action,
1661 auto It = Files.find(File);
1662 if (It == Files.end()) {
1663 Action(llvm::make_error<LSPError>(
1667 LastActiveFile = File.str();
1669 It->second->Worker->runWithAST(
Name, std::move(
Action), Invalidation);
1675 auto It = Files.find(File);
1676 if (It == Files.end()) {
1677 Action(llvm::make_error<LSPError>(
1678 "trying to get preamble for non-added document",
1682 LastActiveFile = File.str();
1684 if (!PreambleTasks) {
1687 std::shared_ptr<const ASTSignals> Signals;
1688 std::shared_ptr<const PreambleData>
Preamble =
1689 It->second->Worker->getPossiblyStalePreamble(&Signals);
1690 WithContext WithProvidedContext(Opts.ContextProvider(File));
1692 It->second->Worker->getCurrentCompileCommand(),
1697 std::shared_ptr<const ASTWorker> Worker = It->second->Worker.lock();
1698 auto Task = [Worker, Consistency,
Name =
Name.str(), File = File.str(),
1699 Contents = It->second->Contents,
1700 Command = Worker->getCurrentCompileCommand(),
1705 llvm::errs() <<
"Signalled during preamble action: " <<
Name <<
"\n";
1706 crashDumpCompileCommand(llvm::errs(), Command);
1707 crashDumpFileContents(llvm::errs(), Contents);
1709 std::shared_ptr<const PreambleData>
Preamble;
1710 if (Consistency == PreambleConsistency::Stale) {
1714 Worker->waitForFirstPreamble();
1716 std::shared_ptr<const ASTSignals> Signals;
1717 Preamble = Worker->getPossiblyStalePreamble(&Signals);
1719 std::lock_guard<Semaphore> BarrierLock(Barrier);
1723 WithContext WithProvidedContext(Opts.ContextProvider(File));
1727 PreambleTasks->runAsync(
"task:" + llvm::sys::path::filename(File),
1732 llvm::StringMap<TUScheduler::FileStats> Result;
1733 for (
const auto &PathAndFile : Files)
1734 Result.try_emplace(PathAndFile.first(),
1735 PathAndFile.second->Worker->stats());
1740 std::vector<Path> Result;
1741 for (
auto &&PathAndFile : Files) {
1742 if (!PathAndFile.second->Worker->isASTCached())
1744 Result.push_back(std::string(PathAndFile.first()));
1749 DebouncePolicy::clock::duration
1751 assert(
Min <=
Max &&
"Invalid policy");
1752 if (History.empty())
1757 History = History.take_back(15);
1758 llvm::SmallVector<clock::duration, 15> Recent(History.begin(), History.end());
1759 auto *Median = Recent.begin() + Recent.size() / 2;
1760 std::nth_element(Recent.begin(), Median, Recent.end());
1763 std::chrono::duration_cast<clock::duration>(
RebuildRatio * *Median);
1781 .
addUsage(Opts.StorePreamblesInMemory ? Elem.second.UsedBytesPreamble
1784 MT.
child(
"header_includer_cache").
addUsage(HeaderIncluders->getUsedBytes());