Skip to content

Commit 7da91bd

Browse files
authored
Merge pull request #73323 from hamishknight/cancel-on-close
[SourceKit] Cancel in-flight builds on `editor.close`
2 parents bb502c5 + de9806e commit 7da91bd

File tree

12 files changed

+366
-87
lines changed

12 files changed

+366
-87
lines changed

tools/SourceKit/include/SourceKit/Core/LangSupport.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1105,7 +1105,8 @@ class LangSupport {
11051105
SourceKitCancellationToken CancellationToken,
11061106
std::shared_ptr<EditorConsumer> Consumer) = 0;
11071107

1108-
virtual void editorClose(StringRef Name, bool RemoveCache) = 0;
1108+
virtual void editorClose(StringRef Name, bool CancelBuilds,
1109+
bool RemoveCache) = 0;
11091110

11101111
virtual void editorReplaceText(StringRef Name, llvm::MemoryBuffer *Buf,
11111112
unsigned Offset, unsigned Length,

tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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

456459
using 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+
819852
void 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+
9811041
static 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+
13051374
void 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

tools/SourceKit/lib/SwiftLang/SwiftASTManager.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ class SwiftASTManager : public std::enable_shared_from_this<SwiftASTManager> {
313313
bool AllowInputs = true);
314314

315315
void removeCachedAST(SwiftInvocationRef Invok);
316+
void cancelBuildsForCachedAST(SwiftInvocationRef Invok);
316317

317318
struct Implementation;
318319
Implementation &Impl;

tools/SourceKit/lib/SwiftLang/SwiftEditor.cpp

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,13 @@ class SwiftDocumentSemanticInfo :
707707
}
708708
}
709709

710+
void cancelBuildsForCachedAST() {
711+
if (!InvokRef)
712+
return;
713+
if (auto ASTMgr = this->ASTMgr.lock())
714+
ASTMgr->cancelBuildsForCachedAST(InvokRef);
715+
}
716+
710717
private:
711718
std::vector<SwiftSemanticToken> takeSemanticTokens(
712719
ImmutableTextSnapshotRef NewSnapshot);
@@ -2174,6 +2181,10 @@ void SwiftEditorDocument::removeCachedAST() {
21742181
Impl.SemanticInfo->removeCachedAST();
21752182
}
21762183

2184+
void SwiftEditorDocument::cancelBuildsForCachedAST() {
2185+
Impl.SemanticInfo->cancelBuildsForCachedAST();
2186+
}
2187+
21772188
void SwiftEditorDocument::applyFormatOptions(OptionsDictionary &FmtOptions) {
21782189
static UIdent KeyUseTabs("key.editor.format.usetabs");
21792190
static UIdent KeyIndentWidth("key.editor.format.indentwidth");
@@ -2436,20 +2447,25 @@ void SwiftLangSupport::editorOpen(StringRef Name, llvm::MemoryBuffer *Buf,
24362447
// EditorClose
24372448
//===----------------------------------------------------------------------===//
24382449

2439-
void SwiftLangSupport::editorClose(StringRef Name, bool RemoveCache) {
2450+
void SwiftLangSupport::editorClose(StringRef Name, bool CancelBuilds,
2451+
bool RemoveCache) {
24402452
auto Removed = EditorDocuments->remove(Name);
2441-
if (Removed) {
2442-
--Stats->numOpenDocs;
2443-
} else {
2453+
if (!Removed) {
2454+
// FIXME: Report error if Name did not apply to anything ?
24442455
IFaceGenContexts.remove(Name);
2456+
return;
24452457
}
2458+
--Stats->numOpenDocs;
24462459

2447-
if (Removed && RemoveCache)
2460+
// Cancel any in-flight builds for the given AST if needed.
2461+
if (CancelBuilds)
2462+
Removed->cancelBuildsForCachedAST();
2463+
2464+
// Then remove the cached AST if we've been asked to do so.
2465+
if (RemoveCache)
24482466
Removed->removeCachedAST();
2449-
// FIXME: Report error if Name did not apply to anything ?
24502467
}
24512468

2452-
24532469
//===----------------------------------------------------------------------===//
24542470
// EditorReplaceText
24552471
//===----------------------------------------------------------------------===//

tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ class SwiftEditorDocument :
107107
void updateSemaInfo(SourceKitCancellationToken CancellationToken);
108108

109109
void removeCachedAST();
110+
void cancelBuildsForCachedAST();
110111

111112
ImmutableTextSnapshotRef getLatestSnapshot() const;
112113

@@ -614,7 +615,8 @@ class SwiftLangSupport : public LangSupport {
614615
SourceKitCancellationToken CancellationToken,
615616
std::shared_ptr<EditorConsumer> Consumer) override;
616617

617-
void editorClose(StringRef Name, bool RemoveCache) override;
618+
void editorClose(StringRef Name, bool CancelBuilds,
619+
bool RemoveCache) override;
618620

619621
void editorReplaceText(StringRef Name, llvm::MemoryBuffer *Buf,
620622
unsigned Offset, unsigned Length,

tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -288,8 +288,8 @@ static sourcekitd_response_t editorExtractTextFromComment(StringRef Source);
288288
289289
static sourcekitd_response_t editorConvertMarkupToXML(StringRef Source);
290290
291-
static sourcekitd_response_t
292-
editorClose(StringRef Name, bool RemoveCache);
291+
static sourcekitd_response_t editorClose(StringRef Name, bool CancelBuilds,
292+
bool RemoveCache);
293293
294294
static sourcekitd_response_t
295295
editorReplaceText(StringRef Name, llvm::MemoryBuffer *Buf, unsigned Offset,
@@ -831,10 +831,14 @@ handleRequestEditorClose(const RequestDict &Req,
831831
if (!Name.has_value())
832832
return Rec(createErrorRequestInvalid("missing 'key.name'"));
833833
834+
// Whether to cancel in-flight builds, default true.
835+
int64_t CancelBuilds = true;
836+
Req.getInt64(KeyCancelBuilds, CancelBuilds, /*isOptional=*/true);
837+
834838
// Whether we remove the cached AST from libcache, by default, false.
835839
int64_t RemoveCache = false;
836840
Req.getInt64(KeyRemoveCache, RemoveCache, /*isOptional=*/true);
837-
return Rec(editorClose(*Name, RemoveCache));
841+
return Rec(editorClose(*Name, CancelBuilds, RemoveCache));
838842
}
839843
}
840844
@@ -3656,11 +3660,11 @@ editorOpenHeaderInterface(StringRef Name, StringRef HeaderName,
36563660
return EditC.createResponse();
36573661
}
36583662

3659-
static sourcekitd_response_t
3660-
editorClose(StringRef Name, bool RemoveCache) {
3663+
static sourcekitd_response_t editorClose(StringRef Name, bool CancelBuilds,
3664+
bool RemoveCache) {
36613665
ResponseBuilder RespBuilder;
36623666
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
3663-
Lang.editorClose(Name, RemoveCache);
3667+
Lang.editorClose(Name, CancelBuilds, RemoveCache);
36643668
return RespBuilder.createResponse();
36653669
}
36663670

unittests/SourceKit/SwiftLang/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
if(NOT SWIFT_HOST_VARIANT MATCHES "${SWIFT_DARWIN_EMBEDDED_VARIANTS}")
22
add_swift_unittest(SourceKitSwiftLangTests
33
CursorInfoTest.cpp
4+
CloseTest.cpp
45
EditingTest.cpp
56
)
67
target_link_libraries(SourceKitSwiftLangTests PRIVATE SourceKitSwiftLang)

0 commit comments

Comments
 (0)