62 #include "clang/Frontend/CompilerInvocation.h"
63 #include "clang/Tooling/CompilationDatabase.h"
64 #include "llvm/ADT/FunctionExtras.h"
65 #include "llvm/ADT/None.h"
66 #include "llvm/ADT/Optional.h"
67 #include "llvm/ADT/STLExtras.h"
68 #include "llvm/ADT/ScopeExit.h"
69 #include "llvm/ADT/SmallVector.h"
70 #include "llvm/ADT/StringExtras.h"
71 #include "llvm/ADT/StringRef.h"
72 #include "llvm/Support/Errc.h"
73 #include "llvm/Support/ErrorHandling.h"
74 #include "llvm/Support/FormatVariadic.h"
75 #include "llvm/Support/Path.h"
76 #include "llvm/Support/Threading.h"
79 #include <condition_variable>
86 #include <type_traits>
92 using std::chrono::steady_clock;
102 return llvm::StringRef(*
File);
112 using Key =
const ASTWorker *;
114 ASTCache(
unsigned MaxRetainedASTs) : MaxRetainedASTs(MaxRetainedASTs) {}
119 std::lock_guard<std::mutex> Lock(Mut);
120 auto It = findByKey(K);
121 if (It == LRU.end() || !It->second)
123 return It->second->getUsedBytes();
128 void put(
Key K, std::unique_ptr<ParsedAST> V) {
129 std::unique_lock<std::mutex> Lock(Mut);
130 assert(findByKey(K) == LRU.end());
132 LRU.insert(LRU.begin(), {K, std::move(V)});
133 if (LRU.size() <= MaxRetainedASTs)
136 std::unique_ptr<ParsedAST> ForCleanup = std::move(LRU.back().second);
147 llvm::Optional<std::unique_ptr<ParsedAST>>
150 std::unique_lock<std::mutex> Lock(Mut);
151 auto Existing = findByKey(K);
152 if (Existing == LRU.end()) {
154 AccessMetric->record(1,
"miss");
158 AccessMetric->record(1,
"hit");
159 std::unique_ptr<ParsedAST> V = std::move(Existing->second);
164 return llvm::Optional<std::unique_ptr<ParsedAST>>(std::move(V));
168 using KVPair = std::pair<Key, std::unique_ptr<ParsedAST>>;
170 std::vector<KVPair>::iterator findByKey(
Key K) {
171 return llvm::find_if(LRU, [K](
const KVPair &P) {
return P.first == K; });
175 unsigned MaxRetainedASTs;
178 std::vector<KVPair> LRU;
184 class SynchronizedTUStatus {
186 SynchronizedTUStatus(
PathRef FileName, ParsingCallbacks &Callbacks)
189 void update(llvm::function_ref<
void(TUStatus &)> Mutator) {
190 std::lock_guard<std::mutex> Lock(StatusMu);
197 std::lock_guard<std::mutex> Lock(StatusMu);
202 void emitStatusLocked() {
204 Callbacks.onFileUpdated(FileName, Status);
211 bool CanPublish =
true;
212 ParsingCallbacks &Callbacks;
219 class PreambleThread {
221 PreambleThread(llvm::StringRef FileName, ParsingCallbacks &Callbacks,
222 bool StorePreambleInMemory,
bool RunSync,
223 SynchronizedTUStatus &Status, ASTWorker &AW)
225 StoreInMemory(StorePreambleInMemory), RunSync(RunSync), Status(Status),
231 void update(std::unique_ptr<CompilerInvocation>
CI, ParseInputs PI,
236 build(std::move(Req));
237 Status.update([](TUStatus &Status) {
243 std::unique_lock<std::mutex> Lock(Mutex);
248 ReqCV.wait(Lock, [
this] {
251 NextReq = std::move(Req);
261 std::unique_lock<std::mutex> Lock(Mutex);
262 assert(!CurrentReq &&
"Already processing a request?");
264 ReqCV.wait(Lock, [
this] {
return NextReq || Done; });
267 CurrentReq = std::move(*NextReq);
272 WithContext Guard(std::move(CurrentReq->Ctx));
278 build(std::move(*CurrentReq));
280 bool IsEmpty =
false;
282 std::lock_guard<std::mutex> Lock(Mutex);
284 IsEmpty = !NextReq.hasValue();
290 Status.update([](TUStatus &Status) {
296 dlog(
"Preamble worker for {0} stopped", FileName);
301 dlog(
"Preamble worker for {0} received stop", FileName);
303 std::lock_guard<std::mutex> Lock(Mutex);
311 bool blockUntilIdle(Deadline Timeout)
const {
312 std::unique_lock<std::mutex> Lock(Mutex);
313 return wait(Lock, ReqCV, Timeout, [&] {
return !NextReq && !CurrentReq; });
320 std::unique_ptr<CompilerInvocation>
CI;
328 std::lock_guard<std::mutex> Lock(Mutex);
334 void build(Request Req);
336 mutable std::mutex Mutex;
338 llvm::Optional<Request> NextReq;
339 llvm::Optional<Request> CurrentReq;
342 mutable std::condition_variable ReqCV;
344 std::shared_ptr<const PreambleData> LatestBuild;
347 ParsingCallbacks &Callbacks;
348 const bool StoreInMemory;
351 SynchronizedTUStatus &Status;
355 class ASTWorkerHandle;
368 friend class ASTWorkerHandle;
369 ASTWorker(
PathRef FileName,
const GlobalCompilationDatabase &CDB,
370 TUScheduler::ASTCache &LRUCache, Semaphore &Barrier,
bool RunSync,
371 const TUScheduler::Options &Opts, ParsingCallbacks &Callbacks);
379 static ASTWorkerHandle create(
PathRef FileName,
380 const GlobalCompilationDatabase &CDB,
381 TUScheduler::ASTCache &IdleASTs,
382 AsyncTaskRunner *Tasks, Semaphore &Barrier,
383 const TUScheduler::Options &Opts,
384 ParsingCallbacks &Callbacks);
389 runWithAST(llvm::StringRef
Name,
390 llvm::unique_function<
void(llvm::Expected<InputsAndAST>)>
Action,
392 bool blockUntilIdle(Deadline Timeout)
const;
394 std::shared_ptr<const PreambleData> getPossiblyStalePreamble()
const;
400 void updatePreamble(std::unique_ptr<CompilerInvocation>
CI, ParseInputs PI,
401 std::shared_ptr<const PreambleData>
Preamble,
406 void getCurrentPreamble(
407 llvm::unique_function<
void(std::shared_ptr<const PreambleData>)>);
409 tooling::CompileCommand getCurrentCompileCommand()
const;
415 void waitForFirstPreamble()
const;
417 TUScheduler::FileStats stats()
const;
418 bool isASTCached()
const;
424 void generateDiagnostics(std::unique_ptr<CompilerInvocation> Invocation,
434 void startTask(llvm::StringRef
Name, llvm::unique_function<
void()> Task,
442 Deadline scheduleLocked();
444 bool shouldSkipHeadLocked()
const;
457 TUScheduler::ASTCache &IdleASTs;
460 const DebouncePolicy UpdateDebounce;
464 const std::function<Context(llvm::StringRef)> ContextProvider;
465 const GlobalCompilationDatabase &CDB;
467 ParsingCallbacks &Callbacks;
471 bool RanASTCallback =
false;
473 mutable std::mutex Mutex;
477 ParseInputs FileInputs;
479 llvm::SmallVector<DebouncePolicy::clock::duration, 8>
483 std::deque<Request> Requests;
484 llvm::Optional<Request> CurrentRequest;
487 mutable std::condition_variable RequestsCV;
491 llvm::Optional<std::shared_ptr<const PreambleData>> LatestPreamble;
492 std::queue<Request> PreambleRequests;
495 mutable std::condition_variable PreambleCV;
498 std::mutex PublishMu;
505 bool CanPublishResults =
true;
506 std::atomic<unsigned> ASTBuildCount = {0};
507 std::atomic<unsigned> PreambleBuildCount = {0};
509 SynchronizedTUStatus Status;
510 PreambleThread PreamblePeer;
517 class ASTWorkerHandle {
518 friend class ASTWorker;
519 ASTWorkerHandle(std::shared_ptr<ASTWorker> Worker)
520 : Worker(std::move(Worker)) {
521 assert(this->Worker);
525 ASTWorkerHandle(
const ASTWorkerHandle &) =
delete;
526 ASTWorkerHandle &operator=(
const ASTWorkerHandle &) =
delete;
527 ASTWorkerHandle(ASTWorkerHandle &&) =
default;
528 ASTWorkerHandle &operator=(ASTWorkerHandle &&) =
default;
535 ASTWorker &operator*() {
536 assert(Worker &&
"Handle was moved from");
540 ASTWorker *operator->() {
541 assert(Worker &&
"Handle was moved from");
549 std::shared_ptr<const ASTWorker> lock() {
return Worker; }
552 std::shared_ptr<ASTWorker> Worker;
555 ASTWorkerHandle ASTWorker::create(
PathRef FileName,
556 const GlobalCompilationDatabase &CDB,
557 TUScheduler::ASTCache &IdleASTs,
558 AsyncTaskRunner *Tasks, Semaphore &Barrier,
559 const TUScheduler::Options &Opts,
560 ParsingCallbacks &Callbacks) {
561 std::shared_ptr<ASTWorker> Worker(
new ASTWorker(
562 FileName, CDB, IdleASTs, Barrier, !Tasks, Opts, Callbacks));
564 Tasks->runAsync(
"ASTWorker:" + llvm::sys::path::filename(FileName),
565 [Worker]() { Worker->run(); });
566 Tasks->runAsync(
"PreambleWorker:" + llvm::sys::path::filename(FileName),
567 [Worker]() { Worker->PreamblePeer.run(); });
570 return ASTWorkerHandle(std::move(Worker));
573 ASTWorker::ASTWorker(
PathRef FileName,
const GlobalCompilationDatabase &CDB,
574 TUScheduler::ASTCache &LRUCache, Semaphore &Barrier,
575 bool RunSync,
const TUScheduler::Options &Opts,
576 ParsingCallbacks &Callbacks)
577 : IdleASTs(LRUCache), RunSync(RunSync), UpdateDebounce(Opts.UpdateDebounce),
579 Callbacks(Callbacks), Barrier(Barrier), Done(false),
581 PreamblePeer(
FileName, Callbacks, Opts.StorePreamblesInMemory,
582 RunSync || !Opts.AsyncPreambleBuilds, Status, *this) {
586 FileInputs.CompileCommand = CDB.getFallbackCommand(FileName);
589 ASTWorker::~ASTWorker() {
593 std::lock_guard<std::mutex> Lock(Mutex);
594 assert(Done &&
"handle was not destroyed");
595 assert(Requests.empty() && !CurrentRequest &&
596 "unprocessed requests when destroying ASTWorker");
601 std::string TaskName = llvm::formatv(
"Update ({0})",
Inputs.Version);
602 auto Task = [=]()
mutable {
608 if (
auto Cmd = CDB.getCompileCommand(FileName))
609 Inputs.CompileCommand = *Cmd;
612 Inputs.CompileCommand = CDB.getFallbackCommand(FileName);
614 bool InputsAreTheSame =
615 std::tie(FileInputs.CompileCommand, FileInputs.Contents) ==
618 if (!InputsAreTheSame) {
620 RanASTCallback =
false;
625 std::lock_guard<std::mutex> Lock(Mutex);
629 log(
"ASTWorker building file {0} version {1} with command {2}\n[{3}]\n{4}",
630 FileName,
Inputs.Version,
Inputs.CompileCommand.Heuristic,
631 Inputs.CompileCommand.Directory,
634 StoreDiags CompilerInvocationDiagConsumer;
635 std::vector<std::string> CC1Args;
637 Inputs, CompilerInvocationDiagConsumer, &CC1Args);
639 if (!CC1Args.empty())
640 vlog(
"Driver produced command: cc1 {0}",
llvm::join(CC1Args,
" "));
641 std::vector<Diag> CompilerInvocationDiags =
642 CompilerInvocationDiagConsumer.take();
644 elog(
"Could not build CompilerInvocation for file {0}", FileName);
647 RanASTCallback =
false;
649 Callbacks.onFailedAST(FileName,
Inputs.Version,
650 std::move(CompilerInvocationDiags),
651 [&](llvm::function_ref<
void()> Publish) {
655 std::lock_guard<std::mutex> Lock(PublishMu);
656 if (CanPublishResults)
661 LatestPreamble.emplace();
664 PreambleCV.notify_all();
668 PreamblePeer.update(std::move(Invocation), std::move(
Inputs),
669 std::move(CompilerInvocationDiags),
WantDiags);
670 std::unique_lock<std::mutex> Lock(Mutex);
671 PreambleCV.wait(Lock, [
this] {
677 return LatestPreamble || !PreambleRequests.empty() || Done;
681 startTask(TaskName, std::move(Task),
WantDiags, TUScheduler::NoInvalidation);
684 void ASTWorker::runWithAST(
685 llvm::StringRef
Name,
686 llvm::unique_function<
void(llvm::Expected<InputsAndAST>)>
Action,
687 TUScheduler::ASTActionInvalidation Invalidation) {
689 static constexpr trace::Metric ASTAccessForRead(
693 return Action(llvm::make_error<CancelledError>(Reason));
694 llvm::Optional<std::unique_ptr<ParsedAST>>
AST =
695 IdleASTs.take(
this, &ASTAccessForRead);
697 StoreDiags CompilerInvocationDiagConsumer;
698 std::unique_ptr<CompilerInvocation> Invocation =
701 vlog(
"ASTWorker rebuilding evicted AST to run {0}: {1} version {2}",
Name,
702 FileName, FileInputs.Version);
706 llvm::Optional<ParsedAST> NewAST;
708 NewAST = ParsedAST::build(FileName, FileInputs, std::move(Invocation),
709 CompilerInvocationDiagConsumer.take(),
710 getPossiblyStalePreamble());
713 AST = NewAST ? std::make_unique<ParsedAST>(std::move(*NewAST)) : nullptr;
716 auto _ = llvm::make_scope_exit(
717 [&
AST,
this]() { IdleASTs.put(
this, std::move(*
AST)); });
720 return Action(llvm::make_error<llvm::StringError>(
721 "invalid AST", llvm::errc::invalid_argument));
722 vlog(
"ASTWorker running {0} on version {2} of {1}",
Name, FileName,
726 startTask(
Name, std::move(Task), None, Invalidation);
729 void PreambleThread::build(Request Req) {
730 assert(Req.CI &&
"Got preamble request with null compiler invocation");
731 const ParseInputs &
Inputs = Req.Inputs;
733 Status.update([&](TUStatus &Status) {
734 Status.PreambleActivity = PreambleAction::Building;
736 auto _ = llvm::make_scope_exit([
this, &Req] {
737 ASTPeer.updatePreamble(std::move(Req.CI), std::move(Req.Inputs),
738 LatestBuild, std::move(Req.CIDiags),
739 std::move(Req.WantDiags));
742 if (!LatestBuild ||
Inputs.ForceRebuild) {
743 vlog(
"Building first preamble for {0} version {1}", FileName,
746 vlog(
"Reusing preamble version {0} for version {1} of {2}",
747 LatestBuild->Version,
Inputs.Version, FileName);
750 vlog(
"Rebuilding invalidated preamble for {0} version {1} (previous was "
752 FileName,
Inputs.Version, LatestBuild->Version);
756 FileName, *Req.CI,
Inputs, StoreInMemory,
757 [
this, Version(
Inputs.Version)](ASTContext &
Ctx,
758 std::shared_ptr<clang::Preprocessor>
PP,
759 const CanonicalIncludes &CanonIncludes) {
760 Callbacks.onPreambleAST(FileName, Version, Ctx, std::move(PP),
765 void ASTWorker::updatePreamble(std::unique_ptr<CompilerInvocation>
CI,
767 std::shared_ptr<const PreambleData>
Preamble,
770 std::string TaskName = llvm::formatv(
"Build AST for ({0})", PI.Version);
778 if (!LatestPreamble ||
Preamble != *LatestPreamble) {
779 ++PreambleBuildCount;
782 RanASTCallback =
false;
783 std::lock_guard<std::mutex> Lock(Mutex);
787 std::swap(*LatestPreamble,
Preamble);
789 LatestPreamble = std::move(
Preamble);
792 PreambleCV.notify_all();
802 generateDiagnostics(std::move(
CI), std::move(PI), std::move(
CIDiags));
809 std::lock_guard<std::mutex> Lock(Mutex);
810 PreambleRequests.push({std::move(Task), std::move(TaskName),
811 steady_clock::now(), Context::current().clone(),
812 llvm::None, TUScheduler::NoInvalidation,
nullptr});
814 PreambleCV.notify_all();
815 RequestsCV.notify_all();
818 void ASTWorker::generateDiagnostics(
819 std::unique_ptr<CompilerInvocation> Invocation, ParseInputs
Inputs,
822 static constexpr trace::Metric ASTAccessForDiag(
825 assert(LatestPreamble);
828 std::lock_guard<std::mutex> Lock(PublishMu);
829 if (!CanPublishResults)
833 bool InputsAreLatest =
834 std::tie(FileInputs.CompileCommand, FileInputs.Contents) ==
841 if (InputsAreLatest && RanASTCallback)
845 std::string TaskName = llvm::formatv(
"Build AST ({0})",
Inputs.Version);
846 Status.update([&](TUStatus &Status) {
847 Status.ASTActivity.K = ASTAction::Building;
848 Status.ASTActivity.Name = std::move(TaskName);
853 llvm::Optional<std::unique_ptr<ParsedAST>>
AST =
854 IdleASTs.take(
this, &ASTAccessForDiag);
855 if (!
AST || !InputsAreLatest) {
856 auto RebuildStartTime = DebouncePolicy::clock::now();
857 llvm::Optional<ParsedAST> NewAST = ParsedAST::build(
858 FileName,
Inputs, std::move(Invocation),
CIDiags, *LatestPreamble);
859 auto RebuildDuration = DebouncePolicy::clock::now() - RebuildStartTime;
863 std::unique_lock<std::mutex> Lock(Mutex, std::try_to_lock);
864 if (Lock.owns_lock()) {
867 if (RebuildTimes.size() == RebuildTimes.capacity())
868 RebuildTimes.erase(RebuildTimes.begin());
869 RebuildTimes.push_back(RebuildDuration);
872 Status.update([&](TUStatus &Status) {
873 Status.Details.ReuseAST =
false;
874 Status.Details.BuildFailed = !NewAST.hasValue();
876 AST = NewAST ? std::make_unique<ParsedAST>(std::move(*NewAST)) : nullptr;
878 log(
"Skipping rebuild of the AST for {0}, inputs are the same.", FileName);
879 Status.update([](TUStatus &Status) {
880 Status.Details.ReuseAST =
true;
881 Status.Details.BuildFailed =
false;
886 auto RunPublish = [&](llvm::function_ref<void()> Publish) {
889 std::lock_guard<std::mutex> Lock(PublishMu);
890 if (CanPublishResults)
894 trace::Span Span(
"Running main AST callback");
895 Callbacks.onMainAST(FileName, **
AST, RunPublish);
901 Callbacks.onFailedAST(FileName,
Inputs.Version,
CIDiags, RunPublish);
907 if (InputsAreLatest) {
908 RanASTCallback = *
AST !=
nullptr;
909 IdleASTs.put(
this, std::move(*
AST));
913 std::shared_ptr<const PreambleData>
914 ASTWorker::getPossiblyStalePreamble()
const {
915 std::lock_guard<std::mutex> Lock(Mutex);
916 return LatestPreamble ? *LatestPreamble :
nullptr;
919 void ASTWorker::waitForFirstPreamble()
const {
920 std::unique_lock<std::mutex> Lock(Mutex);
921 PreambleCV.wait(Lock, [
this] {
return LatestPreamble.hasValue() || Done; });
924 tooling::CompileCommand ASTWorker::getCurrentCompileCommand()
const {
925 std::unique_lock<std::mutex> Lock(Mutex);
926 return FileInputs.CompileCommand;
929 TUScheduler::FileStats ASTWorker::stats()
const {
930 TUScheduler::FileStats Result;
931 Result.ASTBuilds = ASTBuildCount;
932 Result.PreambleBuilds = PreambleBuildCount;
936 Result.UsedBytes = IdleASTs.getUsedBytes(
this);
937 if (
auto Preamble = getPossiblyStalePreamble())
938 Result.UsedBytes +=
Preamble->Preamble.getSize();
942 bool ASTWorker::isASTCached()
const {
return IdleASTs.getUsedBytes(
this) != 0; }
944 void ASTWorker::stop() {
946 std::lock_guard<std::mutex> Lock(PublishMu);
947 CanPublishResults =
false;
950 std::lock_guard<std::mutex> Lock(Mutex);
951 assert(!Done &&
"stop() called twice");
956 PreambleCV.notify_all();
958 RequestsCV.notify_all();
961 void ASTWorker::startTask(llvm::StringRef
Name,
962 llvm::unique_function<
void()> Task,
964 TUScheduler::ASTActionInvalidation Invalidation) {
966 assert(!Done &&
"running a task after stop()");
967 trace::Span
Tracer(
Name +
":" + llvm::sys::path::filename(FileName));
973 std::lock_guard<std::mutex> Lock(Mutex);
974 assert(!Done &&
"running a task after stop()");
977 for (
auto &R : llvm::reverse(Requests)) {
978 if (R.InvalidationPolicy == TUScheduler::InvalidateOnUpdate)
989 WithContext WC(std::move(
Ctx));
991 static_cast<int>(ErrorCode::ContentModified));
993 Requests.push_back({std::move(Task), std::string(
Name), steady_clock::now(),
997 RequestsCV.notify_all();
1000 void ASTWorker::run() {
1003 std::unique_lock<std::mutex> Lock(Mutex);
1004 assert(!CurrentRequest &&
"A task is already running, multiple workers?");
1005 for (
auto Wait = scheduleLocked(); !Wait.expired();
1006 Wait = scheduleLocked()) {
1007 assert(PreambleRequests.empty() &&
1008 "Preamble updates should be scheduled immediately");
1010 if (Requests.empty())
1017 llvm::Optional<WithContext>
Ctx;
1018 llvm::Optional<trace::Span>
Tracer;
1019 if (!Requests.empty()) {
1020 Ctx.emplace(Requests.front().Ctx.clone());
1021 Tracer.emplace(
"Debounce");
1023 if (!(Wait == Deadline::infinity())) {
1024 Status.update([&](TUStatus &Status) {
1025 Status.ASTActivity.K = ASTAction::Queued;
1026 Status.ASTActivity.Name = Requests.front().Name;
1029 std::chrono::duration_cast<std::chrono::milliseconds>(
1030 Wait.time() - steady_clock::now())
1035 wait(Lock, RequestsCV, Wait);
1039 if (!PreambleRequests.empty()) {
1040 CurrentRequest = std::move(PreambleRequests.front());
1041 PreambleRequests.pop();
1043 CurrentRequest = std::move(Requests.front());
1044 Requests.pop_front();
1051 std::unique_lock<Semaphore> Lock(Barrier, std::try_to_lock);
1052 if (!Lock.owns_lock()) {
1053 Status.update([&](TUStatus &Status) {
1054 Status.ASTActivity.K = ASTAction::Queued;
1055 Status.ASTActivity.Name = CurrentRequest->Name;
1059 WithContext Guard(std::move(CurrentRequest->Ctx));
1060 trace::Span
Tracer(CurrentRequest->Name);
1061 Status.update([&](TUStatus &Status) {
1062 Status.ASTActivity.K = ASTAction::RunningAction;
1063 Status.ASTActivity.Name = CurrentRequest->Name;
1065 llvm::Optional<WithContext> WithProvidedContext;
1066 if (ContextProvider)
1067 WithProvidedContext.emplace(ContextProvider(FileName));
1068 CurrentRequest->Action();
1071 bool IsEmpty =
false;
1073 std::lock_guard<std::mutex> Lock(Mutex);
1074 CurrentRequest.reset();
1075 IsEmpty = Requests.empty() && PreambleRequests.empty();
1078 Status.update([&](TUStatus &Status) {
1079 Status.ASTActivity.K = ASTAction::Idle;
1080 Status.ASTActivity.Name =
"";
1083 RequestsCV.notify_all();
1087 Deadline ASTWorker::scheduleLocked() {
1089 if (!PreambleRequests.empty())
1090 return Deadline::zero();
1091 if (Requests.empty())
1092 return Deadline::infinity();
1094 for (
auto I = Requests.begin(),
E = Requests.end(); I !=
E; ++I) {
1097 if (I->UpdateType == None)
1102 if (I->UpdateType == None) {
1103 Request R = std::move(*I);
1105 Requests.push_front(std::move(R));
1106 return Deadline::zero();
1109 if (I->UpdateType == WantDiagnostics::Yes)
1110 I->UpdateType = WantDiagnostics::Auto;
1113 while (shouldSkipHeadLocked()) {
1114 vlog(
"ASTWorker skipping {0} for {1}", Requests.front().Name,
FileName);
1115 Requests.pop_front();
1117 assert(!Requests.empty() &&
"skipped the whole queue");
1122 for (
const auto &R : Requests)
1123 if (R.UpdateType == None || R.UpdateType == WantDiagnostics::Yes)
1124 return Deadline::zero();
1126 Deadline D(Requests.front().AddTime + UpdateDebounce.compute(RebuildTimes));
1131 bool ASTWorker::shouldSkipHeadLocked()
const {
1132 assert(!Requests.empty());
1133 auto Next = Requests.begin();
1140 if (Next == Requests.end() || !Next->UpdateType)
1144 case WantDiagnostics::Yes:
1146 case WantDiagnostics::No:
1148 case WantDiagnostics::Auto:
1150 for (; Next != Requests.end(); ++Next)
1151 if (Next->UpdateType == WantDiagnostics::Yes ||
1152 Next->UpdateType == WantDiagnostics::Auto)
1156 llvm_unreachable(
"Unknown WantDiagnostics");
1159 bool ASTWorker::blockUntilIdle(Deadline Timeout)
const {
1160 auto WaitUntilASTWorkerIsIdle = [&] {
1161 std::unique_lock<std::mutex> Lock(Mutex);
1162 return wait(Lock, RequestsCV, Timeout, [&] {
1163 return PreambleRequests.empty() && Requests.empty() && !CurrentRequest;
1168 WaitUntilASTWorkerIsIdle();
1172 PreamblePeer.blockUntilIdle(Timeout);
1173 assert(Requests.empty() &&
1174 "No new normal tasks can be scheduled concurrently with "
1175 "blockUntilIdle(): ASTWorker isn't threadsafe");
1177 return WaitUntilASTWorkerIsIdle();
1184 std::string renderTUAction(
const PreambleAction PA,
const ASTAction &AA) {
1185 llvm::SmallVector<std::string, 2> Result;
1187 case PreambleAction::Building:
1188 Result.push_back(
"parsing includes");
1190 case PreambleAction::Idle:
1195 case ASTAction::Queued:
1196 Result.push_back(
"file is queued");
1198 case ASTAction::RunningAction:
1199 Result.push_back(
"running " + AA.Name);
1201 case ASTAction::Building:
1202 Result.push_back(
"parsing main file");
1204 case ASTAction::Idle:
1216 return llvm::heavyweight_hardware_concurrency().compute_thread_count();
1222 FStatus.
state = renderTUAction(PreambleActivity, ASTActivity);
1234 std::unique_ptr<ParsingCallbacks> Callbacks)
1235 : CDB(CDB), Opts(Opts),
1236 Callbacks(Callbacks ? move(Callbacks)
1238 Barrier(Opts.AsyncThreadsCount),
1240 std::make_unique<
ASTCache>(Opts.RetentionPolicy.MaxRetainedASTs)) {
1241 if (0 < Opts.AsyncThreadsCount) {
1242 PreambleTasks.emplace();
1243 WorkerThreads.emplace();
1253 PreambleTasks->wait();
1255 WorkerThreads->wait();
1259 for (
auto &
File : Files)
1260 if (!
File.getValue()->Worker->blockUntilIdle(D))
1263 if (!PreambleTasks->wait(D))
1270 std::unique_ptr<FileData> &FD = Files[
File];
1271 bool NewFile = FD ==
nullptr;
1274 ASTWorkerHandle Worker =
1275 ASTWorker::create(
File, CDB, *IdleASTs,
1276 WorkerThreads ? WorkerThreads.getPointer() :
nullptr,
1277 Barrier, Opts, *Callbacks);
1278 FD = std::unique_ptr<FileData>(
1281 FD->Contents =
Inputs.Contents;
1288 bool Removed = Files.erase(
File);
1290 elog(
"Trying to remove file from TUScheduler that is not tracked: {0}",
1295 llvm::StringMap<std::string>
Results;
1296 for (
auto &It : Files)
1297 Results.try_emplace(It.getKey(), It.getValue()->Contents);
1302 llvm::unique_function<
void()>
Action) {
1308 std::lock_guard<Semaphore> BarrierLock(Barrier);
1310 llvm::Optional<WithContext> WithProvidedContext;
1311 if (Opts.ContextProvider)
1312 WithProvidedContext.emplace(Opts.ContextProvider(
Path));
1319 llvm::unique_function<
void(llvm::Expected<InputsAndAST>)>
Action,
1321 auto It = Files.find(
File);
1322 if (It == Files.end()) {
1323 Action(llvm::make_error<LSPError>(
1328 It->second->Worker->runWithAST(
Name, std::move(
Action), Invalidation);
1334 auto It = Files.find(
File);
1335 if (It == Files.end()) {
1336 Action(llvm::make_error<LSPError>(
1337 "trying to get preamble for non-added document",
1342 if (!PreambleTasks) {
1345 std::shared_ptr<const PreambleData>
Preamble =
1346 It->second->Worker->getPossiblyStalePreamble();
1348 It->second->Worker->getCurrentCompileCommand(),
1353 std::shared_ptr<const ASTWorker> Worker = It->second->Worker.lock();
1357 Command = Worker->getCurrentCompileCommand(),
1360 std::shared_ptr<const PreambleData>
Preamble;
1361 if (Consistency == PreambleConsistency::Stale) {
1365 Worker->waitForFirstPreamble();
1367 Preamble = Worker->getPossiblyStalePreamble();
1369 std::lock_guard<Semaphore> BarrierLock(Barrier);
1373 llvm::Optional<WithContext> WithProvidedContext;
1374 if (Opts.ContextProvider)
1375 WithProvidedContext.emplace(Opts.ContextProvider(
File));
1379 PreambleTasks->runAsync(
"task:" + llvm::sys::path::filename(
File),
1384 llvm::StringMap<TUScheduler::FileStats> Result;
1385 for (
const auto &PathAndFile : Files)
1386 Result.try_emplace(PathAndFile.first(),
1387 PathAndFile.second->Worker->stats());
1392 std::vector<Path> Result;
1393 for (
auto &&PathAndFile : Files) {
1394 if (!PathAndFile.second->Worker->isASTCached())
1396 Result.push_back(std::string(PathAndFile.first()));
1401 DebouncePolicy::clock::duration
1403 assert(
Min <=
Max &&
"Invalid policy");
1404 if (History.empty())
1409 History = History.take_back(15);
1410 llvm::SmallVector<clock::duration, 15> Recent(History.begin(), History.end());
1411 auto Median = Recent.begin() + Recent.size() / 2;
1412 std::nth_element(Recent.begin(), Median, Recent.end());
1414 clock::duration Target =
1415 std::chrono::duration_cast<clock::duration>(
RebuildRatio * *Median);