mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user