@@ -451,6 +451,9 @@ class ASTBuildOperation
451451 // / consumer, removes it from the \c Consumers severed by this build operation
452452 // / and, if no consumers are left, cancels the AST build of this operation.
453453 void requestConsumerCancellation (SwiftASTConsumerRef Consumer);
454+
455+ // / Cancels all consumers for the given operation.
456+ void cancelAllConsumers ();
454457};
455458
456459using ASTBuildOperationRef = std::shared_ptr<ASTBuildOperation>;
@@ -517,6 +520,9 @@ class ASTProducer : public std::enable_shared_from_this<ASTProducer> {
517520 IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
518521 SwiftASTManagerRef Mgr);
519522
523+ // / Cancel all currently running build operations.
524+ void cancelAllBuilds ();
525+
520526 size_t getMemoryCost () const {
521527 size_t Cost = sizeof (*this );
522528 for (auto &BuildOp : BuildOperations) {
@@ -631,7 +637,16 @@ struct SwiftASTManager::Implementation {
631637 });
632638 }
633639
634- ASTProducerRef getASTProducer (SwiftInvocationRef InvokRef);
640+ // / Retrieve the ASTProducer for a given invocation, creating one if needed.
641+ ASTProducerRef getOrCreateASTProducer (SwiftInvocationRef InvokRef);
642+
643+ // / Retrieve the ASTProducer for a given invocation, returning \c nullopt if
644+ // / not present.
645+ std::optional<ASTProducerRef> getASTProducer (SwiftInvocationRef Invok);
646+
647+ // / Updates the cache entry to account for any changes to the ASTProducer
648+ // / for the given invocation.
649+ void updateASTProducer (SwiftInvocationRef Invok);
635650
636651 FileContent
637652 getFileContent (StringRef FilePath, bool IsPrimary,
@@ -780,7 +795,7 @@ void SwiftASTManager::processASTAsync(
780795 const void *OncePerASTToken, SourceKitCancellationToken CancellationToken,
781796 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> fileSystem) {
782797 assert (fileSystem);
783- ASTProducerRef Producer = Impl.getASTProducer (InvokRef);
798+ ASTProducerRef Producer = Impl.getOrCreateASTProducer (InvokRef);
784799
785800 Impl.cleanDeletedConsumers ();
786801 {
@@ -816,12 +831,39 @@ void SwiftASTManager::processASTAsync(
816831 });
817832}
818833
834+ std::optional<ASTProducerRef>
835+ SwiftASTManager::Implementation::getASTProducer (SwiftInvocationRef Invok) {
836+ llvm::sys::ScopedLock L (CacheMtx);
837+ return ASTCache.get (Invok->Impl .Key );
838+ }
839+
840+ void SwiftASTManager::Implementation::updateASTProducer (
841+ SwiftInvocationRef Invok) {
842+ llvm::sys::ScopedLock L (CacheMtx);
843+
844+ // Get and set the producer to update its cost in the cache. If we don't
845+ // have a value, then this is a race where we've removed the cached AST, but
846+ // still have a build waiting to complete after cancellation, we don't need
847+ // to do anything in that case.
848+ if (auto Producer = ASTCache.get (Invok->Impl .Key ))
849+ ASTCache.set (Invok->Impl .Key , *Producer);
850+ }
851+
819852void SwiftASTManager::removeCachedAST (SwiftInvocationRef Invok) {
853+ llvm::sys::ScopedLock L (Impl.CacheMtx );
820854 Impl.ASTCache .remove (Invok->Impl .Key );
821855}
822856
823- ASTProducerRef
824- SwiftASTManager::Implementation::getASTProducer (SwiftInvocationRef InvokRef) {
857+ void SwiftASTManager::cancelBuildsForCachedAST (SwiftInvocationRef Invok) {
858+ auto Result = Impl.getASTProducer (Invok);
859+ if (!Result)
860+ return ;
861+
862+ (*Result)->cancelAllBuilds ();
863+ }
864+
865+ ASTProducerRef SwiftASTManager::Implementation::getOrCreateASTProducer (
866+ SwiftInvocationRef InvokRef) {
825867 llvm::sys::ScopedLock L (CacheMtx);
826868 std::optional<ASTProducerRef> OptProducer = ASTCache.get (InvokRef->Impl .Key );
827869 if (OptProducer.has_value ())
@@ -978,6 +1020,24 @@ void ASTBuildOperation::requestConsumerCancellation(
9781020 });
9791021}
9801022
1023+ void ASTBuildOperation::cancelAllConsumers () {
1024+ if (isFinished ())
1025+ return ;
1026+
1027+ llvm::sys::ScopedLock L (ConsumersAndResultMtx);
1028+ CancellationFlag->store (true , std::memory_order_relaxed);
1029+
1030+ // Take the consumers, and notify them of the cancellation.
1031+ decltype (this ->Consumers ) Consumers;
1032+ std::swap (Consumers, this ->Consumers );
1033+
1034+ ASTManager->Impl .ConsumerNotificationQueue .dispatch (
1035+ [Consumers = std::move (Consumers)] {
1036+ for (auto &Consumer : Consumers)
1037+ Consumer->cancelled ();
1038+ });
1039+ }
1040+
9811041static void collectModuleDependencies (ModuleDecl *TopMod,
9821042 llvm::SmallPtrSetImpl<ModuleDecl *> &Visited,
9831043 SmallVectorImpl<std::string> &Filenames) {
@@ -1302,6 +1362,15 @@ ASTBuildOperationRef ASTProducer::getBuildOperationForConsumer(
13021362 return LatestUsableOp;
13031363}
13041364
1365+ void ASTProducer::cancelAllBuilds () {
1366+ // Cancel all build operations, cleanup will happen when each operation
1367+ // terminates.
1368+ BuildOperationsQueue.dispatch ([This = shared_from_this ()] {
1369+ for (auto &BuildOp : This->BuildOperations )
1370+ BuildOp->cancelAllConsumers ();
1371+ });
1372+ }
1373+
13051374void ASTProducer::enqueueConsumer (
13061375 SwiftASTConsumerRef Consumer,
13071376 IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
@@ -1342,7 +1411,7 @@ void ASTProducer::enqueueConsumer(
13421411 [This]() { This->cleanBuildOperations (); });
13431412 // Re-register the object with the cache to update its memory
13441413 // cost.
1345- Mgr->Impl .ASTCache . set (This->InvokRef -> Impl . Key , This );
1414+ Mgr->Impl .updateASTProducer (This->InvokRef );
13461415 }
13471416 };
13481417
0 commit comments