Merge pull request #58978 from ahoppen/pr/no-deadlock-cancellation-bigger-change

[SoruceKit] Audit SwiftASTManager to prevent deadlocks
This commit is contained in:
Alex Hoppen
2023-06-26 13:11:08 +02:00
committed by GitHub
2 changed files with 33 additions and 19 deletions

View File

@@ -1182,6 +1182,7 @@ void ASTBuildOperation::schedule(WorkQueue Queue) {
/*ExpectedOldState=*/State::Running); /*ExpectedOldState=*/State::Running);
}; };
SmallVector<SwiftASTConsumerRef, 4> ConsumersToCancel;
{ {
llvm::sys::ScopedLock L(ConsumersAndResultMtx); llvm::sys::ScopedLock L(ConsumersAndResultMtx);
if (Consumers.empty()) { if (Consumers.empty()) {
@@ -1192,24 +1193,25 @@ void ASTBuildOperation::schedule(WorkQueue Queue) {
if (CancellationFlag->load(std::memory_order_relaxed)) { if (CancellationFlag->load(std::memory_order_relaxed)) {
assert(false && "We should only set the cancellation flag if there " assert(false && "We should only set the cancellation flag if there "
"are no more consumers"); "are no more consumers");
for (auto &Consumer : Consumers) { ConsumersToCancel = Consumers;
Consumer->cancelled();
}
} }
} }
for (auto &Consumer : ConsumersToCancel) {
Consumer->cancelled();
}
std::string Error; std::string Error;
assert(!Result && "We should only be producing a result once"); assert(!Result && "We should only be producing a result once");
ASTUnitRef AST = buildASTUnit(Error); ASTUnitRef AST = buildASTUnit(Error);
SmallVector<SwiftASTConsumerRef, 4> LocalConsumers; SmallVector<SwiftASTConsumerRef, 4> ConsumersToInform;
{ {
llvm::sys::ScopedLock L(ConsumersAndResultMtx); llvm::sys::ScopedLock L(ConsumersAndResultMtx);
bool WasCancelled = CancellationFlag->load(std::memory_order_relaxed); bool WasCancelled = CancellationFlag->load(std::memory_order_relaxed);
Result.emplace(AST, Error, WasCancelled); Result.emplace(AST, Error, WasCancelled);
LocalConsumers = Consumers; ConsumersToInform = Consumers;
Consumers = {}; Consumers = {};
} }
for (auto &Consumer : LocalConsumers) { for (auto &Consumer : ConsumersToInform) {
informConsumer(Consumer); informConsumer(Consumer);
} }
DidFinishCallback(); DidFinishCallback();

View File

@@ -135,8 +135,9 @@ public:
typedef IntrusiveRefCntPtr<ASTUnit> ASTUnitRef; typedef IntrusiveRefCntPtr<ASTUnit> ASTUnitRef;
class SwiftASTConsumer : public std::enable_shared_from_this<SwiftASTConsumer> { class SwiftASTConsumer : public std::enable_shared_from_this<SwiftASTConsumer> {
/// Mutex guarding all accesses to CancellationRequestCallback /// Mutex guarding all accesses to \c CancellationRequestCallback and \c
llvm::sys::Mutex CancellationRequestCallbackMtx; /// IsCancelled.
llvm::sys::Mutex CancellationRequestCallbackAndIsCancelledMtx;
/// A callback that informs the \c ASTBuildOperation, which is producing the /// A callback that informs the \c ASTBuildOperation, which is producing the
/// AST for this consumer, that the consumer is no longer of interest. Calling /// AST for this consumer, that the consumer is no longer of interest. Calling
@@ -160,12 +161,17 @@ public:
/// cause the \c ASTBuildOperation to be cancelled if no other consumer is /// cause the \c ASTBuildOperation to be cancelled if no other consumer is
/// depending on it. /// depending on it.
void requestCancellation() { void requestCancellation() {
llvm::sys::ScopedLock L(CancellationRequestCallbackMtx); Optional<std::function<void(std::shared_ptr<SwiftASTConsumer>)>>
IsCancelled = true; CallbackToCall;
if (CancellationRequestCallback.has_value()) { {
(*CancellationRequestCallback)(shared_from_this()); llvm::sys::ScopedLock L(CancellationRequestCallbackAndIsCancelledMtx);
IsCancelled = true;
CallbackToCall = CancellationRequestCallback;
CancellationRequestCallback = None; CancellationRequestCallback = None;
} }
if (CallbackToCall.has_value()) {
(*CallbackToCall)(shared_from_this());
}
} }
/// Set a cancellation request callback that informs a \c ASTBuildOperation /// Set a cancellation request callback that informs a \c ASTBuildOperation
@@ -177,20 +183,26 @@ public:
/// called, \c NewCallback will be called immediately. /// called, \c NewCallback will be called immediately.
void setCancellationRequestCallback( void setCancellationRequestCallback(
std::function<void(std::shared_ptr<SwiftASTConsumer>)> NewCallback) { std::function<void(std::shared_ptr<SwiftASTConsumer>)> NewCallback) {
llvm::sys::ScopedLock L(CancellationRequestCallbackMtx); bool ShouldCallCallback = false;
assert(!CancellationRequestCallback.has_value() && {
"Can't set two cancellation callbacks on a SwiftASTConsumer"); llvm::sys::ScopedLock L(CancellationRequestCallbackAndIsCancelledMtx);
if (IsCancelled) { assert(!CancellationRequestCallback.has_value() &&
"Can't set two cancellation callbacks on a SwiftASTConsumer");
if (IsCancelled) {
ShouldCallCallback = true;
} else {
CancellationRequestCallback = NewCallback;
}
}
if (ShouldCallCallback) {
NewCallback(shared_from_this()); NewCallback(shared_from_this());
} else {
CancellationRequestCallback = NewCallback;
} }
} }
/// Removes the cancellation request callback previously set by \c /// Removes the cancellation request callback previously set by \c
/// setCancellationRequestCallback. /// setCancellationRequestCallback.
void removeCancellationRequestCallback() { void removeCancellationRequestCallback() {
llvm::sys::ScopedLock L(CancellationRequestCallbackMtx); llvm::sys::ScopedLock L(CancellationRequestCallbackAndIsCancelledMtx);
CancellationRequestCallback = None; CancellationRequestCallback = None;
} }