@@ -457,15 +457,16 @@ class ASTProducer : public std::enable_shared_from_this<ASTProducer> {
457457 // / execute. Operations are guaranteed to be in FIFO order, that is the first
458458 // / one in the vector is the oldes build operation.
459459 SmallVector<ASTBuildOperationRef, 4 > BuildOperations = {};
460- llvm::sys::Mutex BuildOperationsMtx;
460+ WorkQueue BuildOperationsQueue = WorkQueue(
461+ WorkQueue::Dequeuing::Serial, " ASTProducer.BuildOperationsQueue" );
461462
462463 // / Erase all finished build operations with a result except for the latest
463464 // / one which contains a successful results.
464465 // / This cleans up all stale build operations (probably containing old ASTs),
465466 // / but keeps the latest AST around, so that new consumers can be served from
466467 // / it, if possible.
467468 // /
468- // / Assumes that \c BuildOperationsMtx has been claimed .
469+ // / Must be executed on \c BuildOperationsQueue .
469470 void cleanBuildOperations () {
470471 auto ReverseOperations = llvm::reverse (BuildOperations);
471472 auto LastOperationWithResultIt =
@@ -485,7 +486,7 @@ class ASTProducer : public std::enable_shared_from_this<ASTProducer> {
485486 // / Returns the latest build operation which can serve the \p Consumer or
486487 // / \c nullptr if no such build operation exists.
487488 // /
488- // / Assumes that \c BuildOperationsMtx has been claimed .
489+ // / Must be executed on \c BuildOperationsQueue .
489490 ASTBuildOperationRef getBuildOperationForConsumer (
490491 SwiftASTConsumerRef Consumer,
491492 IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
@@ -1162,13 +1163,13 @@ bool ASTBuildOperation::addConsumer(SwiftASTConsumerRef Consumer) {
11621163 } else {
11631164 assert (OperationState != State::Finished);
11641165 auto WeakThis = std::weak_ptr<ASTBuildOperation>(shared_from_this ());
1166+ Consumers.push_back (Consumer);
11651167 Consumer->setCancellationRequestCallback (
11661168 [WeakThis](SwiftASTConsumerRef Consumer) {
11671169 if (auto This = WeakThis.lock ()) {
11681170 This->requestConsumerCancellation (Consumer);
11691171 }
11701172 });
1171- Consumers.push_back (Consumer);
11721173 }
11731174 return true ;
11741175}
@@ -1201,35 +1202,40 @@ void ASTProducer::enqueueConsumer(
12011202 SwiftASTConsumerRef Consumer,
12021203 IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
12031204 SwiftASTManagerRef Mgr) {
1204- // We can't use a llvm::sys::ScopedLock here because we need to unlock it
1205- // before calling enqueueConsumer again in the !WasAdded case below.
1206- std::unique_lock<llvm::sys::Mutex> BuildOperationsLock (BuildOperationsMtx);
1207- if (auto BuildOp = getBuildOperationForConsumer (Consumer, FileSystem, Mgr)) {
1208- bool WasAdded = BuildOp->addConsumer (Consumer);
1209- if (!WasAdded) {
1210- // The build operation was cancelled after the call to
1211- // getBuildOperationForConsumer but before the consumer could be added.
1212- // This should be an absolute edge case. Let's just try again.
1213- BuildOperationsLock.unlock ();
1214- enqueueConsumer (Consumer, FileSystem, Mgr);
1215- return ;
1216- }
1217- } else {
1218- auto WeakThis = std::weak_ptr<ASTProducer>(shared_from_this ());
1219- auto DidFinishCallback = [WeakThis, Mgr]() {
1220- if (auto This = WeakThis.lock ()) {
1221- {
1222- llvm::sys::ScopedLock L (This->BuildOperationsMtx );
1223- This->cleanBuildOperations ();
1224- }
1225- // Re-register the object with the cache to update its memory cost.
1226- Mgr->Impl .ASTCache .set (This->InvokRef ->Impl .Key , This);
1205+ // Enqueue the consumer in the background because getBuildOperationForConsumer
1206+ // consults the file system and might be slow. Also, there's no need to do
1207+ // this synchronously since all results will be delivered async anyway.
1208+ auto This = shared_from_this ();
1209+ BuildOperationsQueue.dispatch ([Consumer, FileSystem, Mgr, This]() {
1210+ if (auto BuildOp =
1211+ This->getBuildOperationForConsumer (Consumer, FileSystem, Mgr)) {
1212+ bool WasAdded = BuildOp->addConsumer (Consumer);
1213+ if (!WasAdded) {
1214+ // The build operation was cancelled after the call to
1215+ // getBuildOperationForConsumer but before the consumer could be
1216+ // added. This should be an absolute edge case. Let's just try
1217+ // again.
1218+ This->enqueueConsumer (Consumer, FileSystem, Mgr);
12271219 }
1228- };
1229- ASTBuildOperationRef NewBuildOp = std::make_shared<ASTBuildOperation>(
1230- FileSystem, InvokRef, Mgr, DidFinishCallback);
1231- BuildOperations.push_back (NewBuildOp);
1232- NewBuildOp->addConsumer (Consumer);
1233- NewBuildOp->schedule (Mgr->Impl .ASTBuildQueue );
1234- }
1220+ } else {
1221+ auto WeakThis = std::weak_ptr<ASTProducer>(This);
1222+ auto DidFinishCallback = [WeakThis, Mgr]() {
1223+ if (auto This = WeakThis.lock ()) {
1224+ This->BuildOperationsQueue .dispatchSync (
1225+ [This]() { This->cleanBuildOperations (); });
1226+ // Re-register the object with the cache to update its memory
1227+ // cost.
1228+ Mgr->Impl .ASTCache .set (This->InvokRef ->Impl .Key , This);
1229+ }
1230+ };
1231+ ASTBuildOperationRef NewBuildOp = std::make_shared<ASTBuildOperation>(
1232+ FileSystem, This->InvokRef , Mgr, DidFinishCallback);
1233+ This->BuildOperations .push_back (NewBuildOp);
1234+ bool WasAdded = NewBuildOp->addConsumer (Consumer);
1235+ assert (WasAdded && " Consumer wasn't added to a new build operation "
1236+ " that can't have been cancelled yet?" );
1237+ (void )WasAdded;
1238+ NewBuildOp->schedule (Mgr->Impl .ASTBuildQueue );
1239+ }
1240+ });
12351241}
0 commit comments