Skip to content

Commit b91328c

Browse files
authored
Merge pull request #66866 from ahoppen/ahoppen/cursor-info-deadlock
[SourceKit] Fix a potential deadlock when a cursor info request is cancelled while another one is inside a cancellation callback
2 parents d72cc2c + 35661f2 commit b91328c

File tree

2 files changed

+39
-12
lines changed

2 files changed

+39
-12
lines changed

tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,13 @@ struct SwiftASTManager::Implementation {
613613
WorkQueue ASTBuildQueue{ WorkQueue::Dequeuing::Serial,
614614
"sourcekit.swift.ASTBuilding" };
615615

616+
/// Queue on which consumers may be notified about results and cancellation.
617+
/// This is essentially just a background queue to which we can jump to inform
618+
/// consumers while making sure that no locks are currently claimed.
619+
WorkQueue ConsumerNotificationQueue{
620+
WorkQueue::Dequeuing::Concurrent,
621+
"SwiftASTManager::Implementation::ConsumerNotificationQueue"};
622+
616623
/// Remove all scheduled consumers that don't exist anymore. This is just a
617624
/// garbage-collection operation to make sure the \c ScheduledConsumers vector
618625
/// doesn't explode. One should never make assumptions that all consumers in
@@ -782,9 +789,11 @@ void SwiftASTManager::processASTAsync(
782789
// Cancel any consumers with the same OncePerASTToken.
783790
for (auto ScheduledConsumer : Impl.ScheduledConsumers) {
784791
if (ScheduledConsumer.OncePerASTToken == OncePerASTToken) {
785-
if (auto Consumer = ScheduledConsumer.Consumer.lock()) {
786-
Consumer->requestCancellation();
787-
}
792+
Impl.ConsumerNotificationQueue.dispatch([ScheduledConsumer]() {
793+
if (auto Consumer = ScheduledConsumer.Consumer.lock()) {
794+
Consumer->requestCancellation();
795+
}
796+
});
788797
}
789798
}
790799
}
@@ -794,11 +803,17 @@ void SwiftASTManager::processASTAsync(
794803
Producer->enqueueConsumer(ASTConsumer, fileSystem, shared_from_this());
795804

796805
auto WeakConsumer = SwiftASTConsumerWeakRef(ASTConsumer);
797-
Impl.ReqTracker->setCancellationHandler(CancellationToken, [WeakConsumer] {
798-
if (auto Consumer = WeakConsumer.lock()) {
799-
Consumer->requestCancellation();
800-
}
801-
});
806+
auto WeakThis = std::weak_ptr<SwiftASTManager>(shared_from_this());
807+
Impl.ReqTracker->setCancellationHandler(
808+
CancellationToken, [WeakConsumer, WeakThis] {
809+
if (auto This = WeakThis.lock()) {
810+
This->Impl.ConsumerNotificationQueue.dispatch([WeakConsumer]() {
811+
if (auto Consumer = WeakConsumer.lock()) {
812+
Consumer->requestCancellation();
813+
}
814+
});
815+
}
816+
});
802817
}
803818

804819
void SwiftASTManager::removeCachedAST(SwiftInvocationRef Invok) {
@@ -950,12 +965,14 @@ void ASTBuildOperation::requestConsumerCancellation(
950965
return;
951966
}
952967
Consumers.erase(ConsumerIndex);
953-
Consumer->cancelled();
954968
if (Consumers.empty()) {
955969
// If there are no more consumers waiting for this result, cancel the AST
956970
// build.
957971
CancellationFlag->store(true, std::memory_order_relaxed);
958972
}
973+
ASTManager->Impl.ConsumerNotificationQueue.dispatch([Consumer] {
974+
Consumer->cancelled();
975+
});
959976
}
960977

961978
static void collectModuleDependencies(ModuleDecl *TopMod,
@@ -1027,11 +1044,15 @@ void ASTBuildOperation::informConsumer(SwiftASTConsumerRef Consumer) {
10271044
"more consumers attached to it and should not accept any "
10281045
"new consumers if the build operation was cancelled. Thus "
10291046
"this case should never happen.");
1030-
Consumer->cancelled();
1047+
ASTManager->Impl.ConsumerNotificationQueue.dispatch([Consumer] {
1048+
Consumer->cancelled();
1049+
});
10311050
} else if (Result.AST) {
10321051
Result.AST->Impl.consumeAsync(Consumer, Result.AST);
10331052
} else {
1034-
Consumer->failed(Result.Error);
1053+
ASTManager->Impl.ConsumerNotificationQueue.dispatch([Consumer, Error = Result.Error] {
1054+
Consumer->failed(Error);
1055+
});
10351056
}
10361057
}
10371058

tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2041,8 +2041,14 @@ void SwiftLangSupport::getCursorInfo(
20412041
fileSystem, Receiver, Offset, Actionables,
20422042
SymbolGraph](
20432043
const RequestResult<CursorInfoData> &Res) {
2044+
if (Res.isCancelled()) {
2045+
// If the AST-based result got cancelled, we don’t want to start
2046+
// solver-based cursor info anymore.
2047+
Receiver(Res);
2048+
return;
2049+
}
20442050
// AST based completion *always* produces a result
2045-
bool NoResults = Res.isError() || Res.isCancelled();
2051+
bool NoResults = Res.isError();
20462052
if (Res.isValue()) {
20472053
NoResults = Res.value().Symbols.empty();
20482054
}

0 commit comments

Comments
 (0)