[clangd][NFC] Explode ReceivedPreamble into a CV

Summary:
Instead of a notification, we make use of a CV and store the boolean on
LatestPreamble by converting it into an optional.

Depends on D80293.

Reviewers: sammccall

Subscribers: ilya-biryukov, javed.absar, MaskRay, jkorous, arphaman, usaxena95, cfe-commits

Tags: #clang

Differential Revision: https://reviews.llvm.org/D80784
This commit is contained in:
Kadir Cetinkaya 2020-05-29 11:45:06 +02:00
parent 55c365d247
commit 406ac49fb0
No known key found for this signature in database
GPG Key ID: E39E36B8D2057ED6
1 changed files with 41 additions and 23 deletions

View File

@ -76,6 +76,7 @@
#include "llvm/Support/Threading.h" #include "llvm/Support/Threading.h"
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
#include <condition_variable>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
@ -458,9 +459,6 @@ private:
const GlobalCompilationDatabase &CDB; const GlobalCompilationDatabase &CDB;
/// Callback invoked when preamble or main file AST is built. /// Callback invoked when preamble or main file AST is built.
ParsingCallbacks &Callbacks; ParsingCallbacks &Callbacks;
/// Latest build preamble for current TU.
std::shared_ptr<const PreambleData> LatestPreamble;
Notification BuiltFirstPreamble;
Semaphore &Barrier; Semaphore &Barrier;
/// Whether the 'onMainAST' callback ran for the current FileInputs. /// Whether the 'onMainAST' callback ran for the current FileInputs.
@ -477,10 +475,18 @@ private:
/// Set to true to signal run() to finish processing. /// Set to true to signal run() to finish processing.
bool Done; /* GUARDED_BY(Mutex) */ bool Done; /* GUARDED_BY(Mutex) */
std::deque<Request> Requests; /* GUARDED_BY(Mutex) */ std::deque<Request> Requests; /* GUARDED_BY(Mutex) */
std::queue<Request> PreambleRequests; /* GUARDED_BY(Mutex) */
llvm::Optional<Request> CurrentRequest; /* GUARDED_BY(Mutex) */ llvm::Optional<Request> CurrentRequest; /* GUARDED_BY(Mutex) */
/// Signalled whenever a new request has been scheduled or processing of a
/// request has completed.
mutable std::condition_variable RequestsCV; mutable std::condition_variable RequestsCV;
Notification ReceivedPreamble; /// Latest build preamble for current TU.
/// None means no builds yet, null means there was an error while building.
/// Only written by ASTWorker's thread.
llvm::Optional<std::shared_ptr<const PreambleData>> LatestPreamble;
std::queue<Request> PreambleRequests; /* GUARDED_BY(Mutex) */
/// Signaled whenever LatestPreamble changes state or there's a new
/// PreambleRequest.
mutable std::condition_variable PreambleCV;
/// Guards the callback that publishes results of AST-related computations /// Guards the callback that publishes results of AST-related computations
/// (diagnostics, highlightings) and file statuses. /// (diagnostics, highlightings) and file statuses.
std::mutex PublishMu; std::mutex PublishMu;
@ -643,20 +649,26 @@ void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags) {
if (CanPublishResults) if (CanPublishResults)
Publish(); Publish();
}); });
// Note that this might throw away a stale preamble that might still be
// useful, but this is how we communicate a build error.
LatestPreamble.emplace();
// Make sure anyone waiting for the preamble gets notified it could not be // Make sure anyone waiting for the preamble gets notified it could not be
// built. // built.
BuiltFirstPreamble.notify(); PreambleCV.notify_all();
return; return;
} }
PreamblePeer.update(std::move(Invocation), std::move(Inputs), PreamblePeer.update(std::move(Invocation), std::move(Inputs),
std::move(CompilerInvocationDiags), WantDiags); std::move(CompilerInvocationDiags), WantDiags);
// Block until first preamble is ready, as patching an empty preamble would std::unique_lock<std::mutex> Lock(Mutex);
// imply rebuilding it from scratch. PreambleCV.wait(Lock, [this] {
// This isn't the natural place to block, rather where the preamble would be // Block until we reiceve a preamble request, unless a preamble already
// consumed. But that's too late, we'd be running on the worker thread with // exists, as patching an empty preamble would imply rebuilding it from
// the PreambleTask scheduled and so we'd deadlock. // scratch.
ReceivedPreamble.wait(); // We block here instead of the consumer to prevent any deadlocks. Since
// LatestPreamble is only populated by ASTWorker thread.
return LatestPreamble || !PreambleRequests.empty() || Done;
});
return; return;
}; };
startTask(TaskName, std::move(Task), WantDiags, TUScheduler::NoInvalidation); startTask(TaskName, std::move(Task), WantDiags, TUScheduler::NoInvalidation);
@ -756,7 +768,7 @@ void ASTWorker::updatePreamble(std::unique_ptr<CompilerInvocation> CI,
// Update the preamble inside ASTWorker queue to ensure atomicity. As a task // Update the preamble inside ASTWorker queue to ensure atomicity. As a task
// running inside ASTWorker assumes internals won't change until it // running inside ASTWorker assumes internals won't change until it
// finishes. // finishes.
if (Preamble != LatestPreamble) { if (!LatestPreamble || Preamble != *LatestPreamble) {
++PreambleBuildCount; ++PreambleBuildCount;
// Cached AST is no longer valid. // Cached AST is no longer valid.
IdleASTs.take(this); IdleASTs.take(this);
@ -764,12 +776,16 @@ void ASTWorker::updatePreamble(std::unique_ptr<CompilerInvocation> CI,
std::lock_guard<std::mutex> Lock(Mutex); std::lock_guard<std::mutex> Lock(Mutex);
// LatestPreamble might be the last reference to old preamble, do not // LatestPreamble might be the last reference to old preamble, do not
// trigger destructor while holding the lock. // trigger destructor while holding the lock.
std::swap(LatestPreamble, Preamble); if (LatestPreamble)
std::swap(*LatestPreamble, Preamble);
else
LatestPreamble = std::move(Preamble);
} }
// Notify anyone waiting for a preamble.
PreambleCV.notify_all();
// Give up our ownership to old preamble before starting expensive AST // Give up our ownership to old preamble before starting expensive AST
// build. // build.
Preamble.reset(); Preamble.reset();
BuiltFirstPreamble.notify();
// We only need to build the AST if diagnostics were requested. // We only need to build the AST if diagnostics were requested.
if (WantDiags == WantDiagnostics::No) if (WantDiags == WantDiagnostics::No)
return; return;
@ -780,7 +796,6 @@ void ASTWorker::updatePreamble(std::unique_ptr<CompilerInvocation> CI,
}; };
if (RunSync) { if (RunSync) {
Task(); Task();
ReceivedPreamble.notify();
return; return;
} }
{ {
@ -789,7 +804,7 @@ void ASTWorker::updatePreamble(std::unique_ptr<CompilerInvocation> CI,
steady_clock::now(), Context::current().clone(), steady_clock::now(), Context::current().clone(),
llvm::None, TUScheduler::NoInvalidation, nullptr}); llvm::None, TUScheduler::NoInvalidation, nullptr});
} }
ReceivedPreamble.notify(); PreambleCV.notify_all();
RequestsCV.notify_all(); RequestsCV.notify_all();
} }
@ -800,6 +815,7 @@ void ASTWorker::generateDiagnostics(
static constexpr trace::Metric ASTAccessForDiag( static constexpr trace::Metric ASTAccessForDiag(
"ast_access_diag", trace::Metric::Counter, "result"); "ast_access_diag", trace::Metric::Counter, "result");
assert(Invocation); assert(Invocation);
assert(LatestPreamble);
// No need to rebuild the AST if we won't send the diagnostics. // No need to rebuild the AST if we won't send the diagnostics.
{ {
std::lock_guard<std::mutex> Lock(PublishMu); std::lock_guard<std::mutex> Lock(PublishMu);
@ -832,7 +848,7 @@ void ASTWorker::generateDiagnostics(
if (!AST || !InputsAreLatest) { if (!AST || !InputsAreLatest) {
auto RebuildStartTime = DebouncePolicy::clock::now(); auto RebuildStartTime = DebouncePolicy::clock::now();
llvm::Optional<ParsedAST> NewAST = ParsedAST::build( llvm::Optional<ParsedAST> NewAST = ParsedAST::build(
FileName, Inputs, std::move(Invocation), CIDiags, LatestPreamble); FileName, Inputs, std::move(Invocation), CIDiags, *LatestPreamble);
auto RebuildDuration = DebouncePolicy::clock::now() - RebuildStartTime; auto RebuildDuration = DebouncePolicy::clock::now() - RebuildStartTime;
++ASTBuildCount; ++ASTBuildCount;
// Try to record the AST-build time, to inform future update debouncing. // Try to record the AST-build time, to inform future update debouncing.
@ -890,10 +906,13 @@ void ASTWorker::generateDiagnostics(
std::shared_ptr<const PreambleData> std::shared_ptr<const PreambleData>
ASTWorker::getPossiblyStalePreamble() const { ASTWorker::getPossiblyStalePreamble() const {
std::lock_guard<std::mutex> Lock(Mutex); std::lock_guard<std::mutex> Lock(Mutex);
return LatestPreamble; return LatestPreamble ? *LatestPreamble : nullptr;
} }
void ASTWorker::waitForFirstPreamble() const { BuiltFirstPreamble.wait(); } void ASTWorker::waitForFirstPreamble() const {
std::unique_lock<std::mutex> Lock(Mutex);
PreambleCV.wait(Lock, [this] { return LatestPreamble.hasValue() || Done; });
}
tooling::CompileCommand ASTWorker::getCurrentCompileCommand() const { tooling::CompileCommand ASTWorker::getCurrentCompileCommand() const {
std::unique_lock<std::mutex> Lock(Mutex); std::unique_lock<std::mutex> Lock(Mutex);
@ -925,10 +944,9 @@ void ASTWorker::stop() {
assert(!Done && "stop() called twice"); assert(!Done && "stop() called twice");
Done = true; Done = true;
} }
// We are no longer going to build any preambles, let the waiters know that.
ReceivedPreamble.notify();
BuiltFirstPreamble.notify();
PreamblePeer.stop(); PreamblePeer.stop();
// We are no longer going to build any preambles, let the waiters know that.
PreambleCV.notify_all();
Status.stop(); Status.stop();
RequestsCV.notify_all(); RequestsCV.notify_all();
} }