[SourceKit] Resolve a nondeterministic deadlock in SourceKit while cancelling

This fixes a race-conditioned deadlock which could occur while cancelling SourceKit AST build request

We have one thread that claimed `CancellationRequestCallbackMtx` in `SwiftASTConsumer::requestCancellation` and wants to claim `ConsumersAndResultMtx` in `ASTBuildOperation::requestConsumerCancellation`

Another thread claimed `ConsumersAndResultMtx` in `ASTBuildOperation::schedule` and now wants to claim `CancellationRequestCallbackMtx` in `SwiftASTConsumer::removeCancellationRequestCallback`.

In both cases we could actually release one lock before claiming the other.

Fixes rdar://90870793
This commit is contained in:
Alex Hoppen
2022-04-08 10:42:40 +02:00
parent 1e1d4e3714
commit d7eada412f

View File

@@ -1137,38 +1137,42 @@ void ASTBuildOperation::schedule(WorkQueue Queue) {
std::string Error;
assert(!Result && "We should only be producing a result once");
ASTUnitRef AST = buildASTUnit(Error);
SmallVector<SwiftASTConsumerRef, 4> LocalConsumers;
{
llvm::sys::ScopedLock L(ConsumersAndResultMtx);
bool WasCancelled = CancellationFlag->load(std::memory_order_relaxed);
Result.emplace(AST, Error, WasCancelled);
for (auto &Consumer : Consumers) {
informConsumer(Consumer);
}
LocalConsumers = Consumers;
Consumers = {};
}
for (auto &Consumer : LocalConsumers) {
informConsumer(Consumer);
}
DidFinishCallback();
},
/*isStackDeep=*/true);
}
bool ASTBuildOperation::addConsumer(SwiftASTConsumerRef Consumer) {
llvm::sys::ScopedLock L(ConsumersAndResultMtx);
if (isCancelled()) {
return false;
}
if (Result) {
informConsumer(Consumer);
} else {
{
llvm::sys::ScopedLock L(ConsumersAndResultMtx);
if (isCancelled()) {
return false;
}
if (Result) {
informConsumer(Consumer);
return true;
}
assert(OperationState != State::Finished);
auto WeakThis = std::weak_ptr<ASTBuildOperation>(shared_from_this());
Consumers.push_back(Consumer);
Consumer->setCancellationRequestCallback(
[WeakThis](SwiftASTConsumerRef Consumer) {
if (auto This = WeakThis.lock()) {
This->requestConsumerCancellation(Consumer);
}
});
}
auto WeakThis = std::weak_ptr<ASTBuildOperation>(shared_from_this());
Consumer->setCancellationRequestCallback(
[WeakThis](SwiftASTConsumerRef Consumer) {
if (auto This = WeakThis.lock()) {
This->requestConsumerCancellation(Consumer);
}
});
return true;
}