Skip to content

Commit 276a95b

Browse files
committed
[clangd] Decouple preambleworker from astworker, NFCI
Summary: First step to enable deferred preamble builds. Not intending to land it alone, will have follow-ups that will implement full deferred build functionality and will land after all of them are ready. Reviewers: sammccall Subscribers: ilya-biryukov, javed.absar, MaskRay, jkorous, arphaman, usaxena95, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D76125
1 parent a473f0a commit 276a95b

File tree

3 files changed

+196
-40
lines changed

3 files changed

+196
-40
lines changed

clang-tools-extra/clangd/Preamble.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ PreambleData::PreambleData(const ParseInputs &Inputs,
8989
}
9090

9191
std::shared_ptr<const PreambleData>
92-
buildPreamble(PathRef FileName, CompilerInvocation &CI,
92+
buildPreamble(PathRef FileName, CompilerInvocation CI,
9393
std::shared_ptr<const PreambleData> OldPreamble,
9494
const ParseInputs &Inputs, bool StoreInMemory,
9595
PreambleParsedCallback PreambleCallback) {

clang-tools-extra/clangd/Preamble.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,11 @@ using PreambleParsedCallback =
7878
/// building the preamble. Note that if the old preamble was reused, no AST is
7979
/// built and, therefore, the callback will not be executed.
8080
std::shared_ptr<const PreambleData>
81-
buildPreamble(PathRef FileName, CompilerInvocation &CI,
81+
buildPreamble(PathRef FileName, CompilerInvocation CI,
8282
std::shared_ptr<const PreambleData> OldPreamble,
8383
const ParseInputs &Inputs, bool StoreInMemory,
8484
PreambleParsedCallback PreambleCallback);
8585

86-
8786
} // namespace clangd
8887
} // namespace clang
8988

clang-tools-extra/clangd/TUScheduler.cpp

Lines changed: 194 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,16 @@
4949
#include "GlobalCompilationDatabase.h"
5050
#include "Logger.h"
5151
#include "ParsedAST.h"
52+
#include "Path.h"
5253
#include "Preamble.h"
54+
#include "Threading.h"
5355
#include "Trace.h"
5456
#include "index/CanonicalIncludes.h"
5557
#include "clang/Frontend/CompilerInvocation.h"
5658
#include "clang/Tooling/CompilationDatabase.h"
5759
#include "llvm/ADT/Optional.h"
5860
#include "llvm/ADT/ScopeExit.h"
61+
#include "llvm/ADT/StringRef.h"
5962
#include "llvm/Support/Errc.h"
6063
#include "llvm/Support/Path.h"
6164
#include "llvm/Support/Threading.h"
@@ -149,6 +152,177 @@ class TUScheduler::ASTCache {
149152
};
150153

151154
namespace {
155+
/// Responsible for building and providing access to the preamble of a TU.
156+
/// Whenever the thread is idle and the preamble is outdated, it starts to build
157+
/// a fresh preamble from the latest inputs. If RunSync is true, preambles are
158+
/// built synchronously in update() instead.
159+
class PreambleThread {
160+
public:
161+
PreambleThread(llvm::StringRef FileName, ParsingCallbacks &Callbacks,
162+
bool StorePreambleInMemory, bool RunSync)
163+
: FileName(FileName), Callbacks(Callbacks),
164+
StoreInMemory(StorePreambleInMemory), RunSync(RunSync) {}
165+
166+
size_t getUsedBytes() const {
167+
auto Preamble = latest();
168+
return Preamble ? Preamble->Preamble.getSize() : 0;
169+
}
170+
171+
/// It isn't guaranteed that each requested version will be built. If there
172+
/// are multiple update requests while building a preamble, only the last one
173+
/// will be built.
174+
void update(CompilerInvocation *CI, ParseInputs PI) {
175+
// If compiler invocation was broken, just fail out early.
176+
if (!CI) {
177+
TUStatus::BuildDetails Details;
178+
Details.BuildFailed = true;
179+
std::string TaskName = llvm::formatv("Update ({0})", PI.Version);
180+
emitTUStatus({TUAction::BuildingPreamble, std::move(TaskName)}, &Details);
181+
// Make sure anyone waiting for the preamble gets notified it could not be
182+
// built.
183+
BuiltFirst.notify();
184+
return;
185+
}
186+
// Make possibly expensive copy while not holding the lock.
187+
Request Req = {std::make_unique<CompilerInvocation>(*CI), std::move(PI)};
188+
if (RunSync) {
189+
build(std::move(Req));
190+
return;
191+
}
192+
{
193+
std::lock_guard<std::mutex> Lock(Mutex);
194+
assert(!Done && "Build request to PreambleWorker after stop");
195+
NextReq = std::move(Req);
196+
}
197+
// Let the worker thread know there's a request, notify_one is safe as there
198+
// should be a single worker thread waiting on it.
199+
ReqCV.notify_all();
200+
}
201+
202+
/// Blocks until at least a single request has been processed. Note that it
203+
/// will unblock even after an unsuccessful build.
204+
void waitForFirst() const { BuiltFirst.wait(); }
205+
206+
/// Returns the latest built preamble, might be null if no preamble has been
207+
/// built or latest attempt resulted in a failure.
208+
std::shared_ptr<const PreambleData> latest() const {
209+
std::lock_guard<std::mutex> Lock(Mutex);
210+
return LatestBuild;
211+
}
212+
213+
void run() {
214+
dlog("Starting preamble worker for {0}", FileName);
215+
while (true) {
216+
{
217+
std::unique_lock<std::mutex> Lock(Mutex);
218+
assert(!CurrentReq && "Already processing a request?");
219+
// Wait until stop is called or there is a request.
220+
ReqCV.wait(Lock, [this] { return NextReq || Done; });
221+
if (Done)
222+
break;
223+
CurrentReq = std::move(*NextReq);
224+
NextReq.reset();
225+
}
226+
// Build the preamble and let the waiters know about it.
227+
build(std::move(*CurrentReq));
228+
{
229+
std::lock_guard<std::mutex> Lock(Mutex);
230+
CurrentReq.reset();
231+
}
232+
ReqCV.notify_all();
233+
}
234+
// We are no longer going to build any preambles, let the waiters know that.
235+
BuiltFirst.notify();
236+
dlog("Preamble worker for {0} finished", FileName);
237+
}
238+
239+
/// Signals the run loop to exit.
240+
void stop() {
241+
dlog("Stopping preamble worker for {0}", FileName);
242+
{
243+
std::lock_guard<std::mutex> Lock(Mutex);
244+
Done = true;
245+
}
246+
// Let the worker thread know that it should stop.
247+
ReqCV.notify_all();
248+
}
249+
250+
bool blockUntilIdle(Deadline Timeout) const {
251+
std::unique_lock<std::mutex> Lock(Mutex);
252+
return wait(Lock, ReqCV, Timeout, [&] { return !NextReq && !CurrentReq; });
253+
}
254+
255+
private:
256+
/// Holds inputs required for building a preamble. CI is guaranteed to be
257+
/// non-null.
258+
struct Request {
259+
std::unique_ptr<CompilerInvocation> CI;
260+
ParseInputs Inputs;
261+
};
262+
263+
bool isDone() {
264+
std::lock_guard<std::mutex> Lock(Mutex);
265+
return Done;
266+
}
267+
268+
/// Updates the TUStatus and emits it. Only called in the worker thread.
269+
void emitTUStatus(TUAction Action,
270+
const TUStatus::BuildDetails *Details = nullptr) {
271+
// Do not emit TU statuses when the worker is shutting down.
272+
if (isDone())
273+
return;
274+
TUStatus Status({std::move(Action), {}});
275+
if (Details)
276+
Status.Details = *Details;
277+
Callbacks.onFileUpdated(FileName, Status);
278+
}
279+
280+
/// Builds a preamble for Req and caches it. Might re-use the latest built
281+
/// preamble if it is valid for Req. Also signals waiters about the build.
282+
/// FIXME: We shouldn't cache failed preambles, if we've got a successful
283+
/// build before.
284+
void build(Request Req) {
285+
assert(Req.CI && "Got preamble request with null compiler invocation");
286+
const ParseInputs &Inputs = Req.Inputs;
287+
std::shared_ptr<const PreambleData> OldPreamble =
288+
Inputs.ForceRebuild ? nullptr : latest();
289+
290+
std::string TaskName = llvm::formatv("Update ({0})", Inputs.Version);
291+
emitTUStatus({TUAction::BuildingPreamble, std::move(TaskName)});
292+
293+
auto Preamble = clang::clangd::buildPreamble(
294+
FileName, std::move(*Req.CI), OldPreamble, Inputs, StoreInMemory,
295+
[this, Version(Inputs.Version)](
296+
ASTContext &Ctx, std::shared_ptr<clang::Preprocessor> PP,
297+
const CanonicalIncludes &CanonIncludes) {
298+
Callbacks.onPreambleAST(FileName, Version, Ctx, std::move(PP),
299+
CanonIncludes);
300+
});
301+
{
302+
std::lock_guard<std::mutex> Lock(Mutex);
303+
// LatestBuild might be the last reference to old preamble, do not trigger
304+
// destructor while holding the lock.
305+
std::swap(LatestBuild, Preamble);
306+
}
307+
BuiltFirst.notify();
308+
}
309+
310+
mutable std::mutex Mutex;
311+
bool Done = false; /* GUARDED_BY(Mutex) */
312+
llvm::Optional<Request> NextReq; /* GUARDED_BY(Mutex) */
313+
llvm::Optional<Request> CurrentReq; /* GUARDED_BY(Mutex) */
314+
// Signaled whenever a thread populates NextReq or worker thread builds a
315+
// Preamble.
316+
mutable std::condition_variable ReqCV; /* GUARDED_BY(Mutex) */
317+
std::shared_ptr<const PreambleData> LatestBuild; /* GUARDED_BY(Mutex) */
318+
319+
Notification BuiltFirst;
320+
const Path FileName;
321+
ParsingCallbacks &Callbacks;
322+
const bool StoreInMemory;
323+
const bool RunSync;
324+
};
325+
152326
class ASTWorkerHandle;
153327

154328
/// Owns one instance of the AST, schedules updates and reads of it.
@@ -251,8 +425,6 @@ class ASTWorker {
251425
/// File that ASTWorker is responsible for.
252426
const Path FileName;
253427
const GlobalCompilationDatabase &CDB;
254-
/// Whether to keep the built preambles in memory or on disk.
255-
const bool StorePreambleInMemory;
256428
/// Callback invoked when preamble or main file AST is built.
257429
ParsingCallbacks &Callbacks;
258430
/// Only accessed by the worker thread.
@@ -266,13 +438,10 @@ class ASTWorker {
266438
/// File inputs, currently being used by the worker.
267439
/// Inputs are written and read by the worker thread, compile command can also
268440
/// be consumed by clients of ASTWorker.
269-
std::shared_ptr<const ParseInputs> FileInputs; /* GUARDED_BY(Mutex) */
270-
std::shared_ptr<const PreambleData> LastBuiltPreamble; /* GUARDED_BY(Mutex) */
441+
std::shared_ptr<const ParseInputs> FileInputs; /* GUARDED_BY(Mutex) */
271442
/// Times of recent AST rebuilds, used for UpdateDebounce computation.
272443
llvm::SmallVector<DebouncePolicy::clock::duration, 8>
273444
RebuildTimes; /* GUARDED_BY(Mutex) */
274-
/// Becomes ready when the first preamble build finishes.
275-
Notification PreambleWasBuilt;
276445
/// Set to true to signal run() to finish processing.
277446
bool Done; /* GUARDED_BY(Mutex) */
278447
std::deque<Request> Requests; /* GUARDED_BY(Mutex) */
@@ -288,6 +457,8 @@ class ASTWorker {
288457
// don't. When the old handle is destroyed, the old worker will stop reporting
289458
// any results to the user.
290459
bool CanPublishResults = true; /* GUARDED_BY(PublishMu) */
460+
461+
PreambleThread PW;
291462
};
292463

293464
/// A smart-pointer-like class that points to an active ASTWorker.
@@ -340,9 +511,12 @@ ASTWorker::create(PathRef FileName, const GlobalCompilationDatabase &CDB,
340511
std::shared_ptr<ASTWorker> Worker(
341512
new ASTWorker(FileName, CDB, IdleASTs, Barrier, /*RunSync=*/!Tasks,
342513
UpdateDebounce, StorePreamblesInMemory, Callbacks));
343-
if (Tasks)
344-
Tasks->runAsync("worker:" + llvm::sys::path::filename(FileName),
514+
if (Tasks) {
515+
Tasks->runAsync("ASTWorker:" + llvm::sys::path::filename(FileName),
345516
[Worker]() { Worker->run(); });
517+
Tasks->runAsync("PreambleWorker:" + llvm::sys::path::filename(FileName),
518+
[Worker]() { Worker->PW.run(); });
519+
}
346520

347521
return ASTWorkerHandle(std::move(Worker));
348522
}
@@ -353,10 +527,11 @@ ASTWorker::ASTWorker(PathRef FileName, const GlobalCompilationDatabase &CDB,
353527
bool StorePreamblesInMemory, ParsingCallbacks &Callbacks)
354528
: IdleASTs(LRUCache), RunSync(RunSync), UpdateDebounce(UpdateDebounce),
355529
FileName(FileName), CDB(CDB),
356-
StorePreambleInMemory(StorePreamblesInMemory),
357530
Callbacks(Callbacks), Status{TUAction(TUAction::Idle, ""),
358531
TUStatus::BuildDetails()},
359-
Barrier(Barrier), Done(false) {
532+
Barrier(Barrier), Done(false),
533+
// FIXME: Run preambleworker async.
534+
PW(FileName, Callbacks, StorePreamblesInMemory, /*RunSync=*/true) {
360535
auto Inputs = std::make_shared<ParseInputs>();
361536
// Set a fallback command because compile command can be accessed before
362537
// `Inputs` is initialized. Other fields are only used after initialization
@@ -409,7 +584,6 @@ void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags) {
409584
FileInputs = std::make_shared<ParseInputs>(Inputs);
410585
}
411586
RanASTCallback = false;
412-
emitTUStatus({TUAction::BuildingPreamble, TaskName});
413587
log("ASTWorker building file {0} version {1} with command {2}\n[{3}]\n{4}",
414588
FileName, Inputs.Version, Inputs.CompileCommand.Heuristic,
415589
Inputs.CompileCommand.Directory,
@@ -419,6 +593,12 @@ void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags) {
419593
std::vector<std::string> CC1Args;
420594
std::unique_ptr<CompilerInvocation> Invocation = buildCompilerInvocation(
421595
Inputs, CompilerInvocationDiagConsumer, &CC1Args);
596+
// This is true for now, as we always block until new preamble is build.
597+
// Once we start to block preambles out-of-order we need to make sure
598+
// OldPreamble refers to the preamble that was used to build last AST.
599+
auto OldPreamble = PW.latest();
600+
PW.update(Invocation.get(), Inputs);
601+
auto NewPreamble = PW.latest();
422602
// Log cc1 args even (especially!) if creating invocation failed.
423603
if (!CC1Args.empty())
424604
vlog("Driver produced command: cc1 {0}", llvm::join(CC1Args, " "));
@@ -428,40 +608,17 @@ void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags) {
428608
elog("Could not build CompilerInvocation for file {0}", FileName);
429609
// Remove the old AST if it's still in cache.
430610
IdleASTs.take(this);
431-
TUStatus::BuildDetails Details;
432-
Details.BuildFailed = true;
433-
emitTUStatus({TUAction::BuildingPreamble, TaskName}, &Details);
434611
// Report the diagnostics we collected when parsing the command line.
435612
Callbacks.onFailedAST(FileName, Inputs.Version,
436613
std::move(CompilerInvocationDiags), RunPublish);
437-
// Make sure anyone waiting for the preamble gets notified it could not
438-
// be built.
439-
PreambleWasBuilt.notify();
440614
return;
441615
}
442616

443-
std::shared_ptr<const PreambleData> OldPreamble =
444-
Inputs.ForceRebuild ? std::shared_ptr<const PreambleData>()
445-
: getPossiblyStalePreamble();
446-
std::shared_ptr<const PreambleData> NewPreamble = buildPreamble(
447-
FileName, *Invocation, OldPreamble, Inputs, StorePreambleInMemory,
448-
[this, Version(Inputs.Version)](
449-
ASTContext &Ctx, std::shared_ptr<clang::Preprocessor> PP,
450-
const CanonicalIncludes &CanonIncludes) {
451-
Callbacks.onPreambleAST(FileName, Version, Ctx, std::move(PP),
452-
CanonIncludes);
453-
});
454-
455617
bool CanReuseAST = InputsAreTheSame && (OldPreamble == NewPreamble);
456-
{
457-
std::lock_guard<std::mutex> Lock(Mutex);
458-
LastBuiltPreamble = NewPreamble;
459-
}
460618
// Before doing the expensive AST reparse, we want to release our reference
461619
// to the old preamble, so it can be freed if there are no other references
462620
// to it.
463621
OldPreamble.reset();
464-
PreambleWasBuilt.notify();
465622
emitTUStatus({TUAction::BuildingFile, TaskName});
466623
if (!CanReuseAST) {
467624
IdleASTs.take(this); // Remove the old AST if it's still in cache.
@@ -591,8 +748,7 @@ void ASTWorker::runWithAST(
591748

592749
std::shared_ptr<const PreambleData>
593750
ASTWorker::getPossiblyStalePreamble() const {
594-
std::lock_guard<std::mutex> Lock(Mutex);
595-
return LastBuiltPreamble;
751+
return PW.latest();
596752
}
597753

598754
void ASTWorker::getCurrentPreamble(
@@ -625,7 +781,7 @@ void ASTWorker::getCurrentPreamble(
625781
RequestsCV.notify_all();
626782
}
627783

628-
void ASTWorker::waitForFirstPreamble() const { PreambleWasBuilt.wait(); }
784+
void ASTWorker::waitForFirstPreamble() const { PW.waitForFirst(); }
629785

630786
std::shared_ptr<const ParseInputs> ASTWorker::getCurrentFileInputs() const {
631787
std::unique_lock<std::mutex> Lock(Mutex);
@@ -713,6 +869,7 @@ void ASTWorker::emitTUStatus(TUAction Action,
713869
}
714870

715871
void ASTWorker::run() {
872+
auto _ = llvm::make_scope_exit([this] { PW.stop(); });
716873
while (true) {
717874
{
718875
std::unique_lock<std::mutex> Lock(Mutex);

0 commit comments

Comments
 (0)