Skip to content

Commit 22d34d9

Browse files
authored
Merge pull request #58978 from ahoppen/pr/no-deadlock-cancellation-bigger-change
[SoruceKit] Audit SwiftASTManager to prevent deadlocks
2 parents da34a4e + 453f6cd commit 22d34d9

File tree

2 files changed

+33
-19
lines changed

2 files changed

+33
-19
lines changed

tools/SourceKit/lib/SwiftLang/SwiftASTManager.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,6 +1182,7 @@ void ASTBuildOperation::schedule(WorkQueue Queue) {
11821182
/*ExpectedOldState=*/State::Running);
11831183
};
11841184

1185+
SmallVector<SwiftASTConsumerRef, 4> ConsumersToCancel;
11851186
{
11861187
llvm::sys::ScopedLock L(ConsumersAndResultMtx);
11871188
if (Consumers.empty()) {
@@ -1192,24 +1193,25 @@ void ASTBuildOperation::schedule(WorkQueue Queue) {
11921193
if (CancellationFlag->load(std::memory_order_relaxed)) {
11931194
assert(false && "We should only set the cancellation flag if there "
11941195
"are no more consumers");
1195-
for (auto &Consumer : Consumers) {
1196-
Consumer->cancelled();
1197-
}
1196+
ConsumersToCancel = Consumers;
11981197
}
11991198
}
1199+
for (auto &Consumer : ConsumersToCancel) {
1200+
Consumer->cancelled();
1201+
}
12001202

12011203
std::string Error;
12021204
assert(!Result && "We should only be producing a result once");
12031205
ASTUnitRef AST = buildASTUnit(Error);
1204-
SmallVector<SwiftASTConsumerRef, 4> LocalConsumers;
1206+
SmallVector<SwiftASTConsumerRef, 4> ConsumersToInform;
12051207
{
12061208
llvm::sys::ScopedLock L(ConsumersAndResultMtx);
12071209
bool WasCancelled = CancellationFlag->load(std::memory_order_relaxed);
12081210
Result.emplace(AST, Error, WasCancelled);
1209-
LocalConsumers = Consumers;
1211+
ConsumersToInform = Consumers;
12101212
Consumers = {};
12111213
}
1212-
for (auto &Consumer : LocalConsumers) {
1214+
for (auto &Consumer : ConsumersToInform) {
12131215
informConsumer(Consumer);
12141216
}
12151217
DidFinishCallback();

tools/SourceKit/lib/SwiftLang/SwiftASTManager.h

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -135,8 +135,9 @@ class ASTUnit : public SourceKit::ThreadSafeRefCountedBase<ASTUnit> {
135135
typedef IntrusiveRefCntPtr<ASTUnit> ASTUnitRef;
136136

137137
class SwiftASTConsumer : public std::enable_shared_from_this<SwiftASTConsumer> {
138-
/// Mutex guarding all accesses to CancellationRequestCallback
139-
llvm::sys::Mutex CancellationRequestCallbackMtx;
138+
/// Mutex guarding all accesses to \c CancellationRequestCallback and \c
139+
/// IsCancelled.
140+
llvm::sys::Mutex CancellationRequestCallbackAndIsCancelledMtx;
140141

141142
/// A callback that informs the \c ASTBuildOperation, which is producing the
142143
/// AST for this consumer, that the consumer is no longer of interest. Calling
@@ -160,12 +161,17 @@ class SwiftASTConsumer : public std::enable_shared_from_this<SwiftASTConsumer> {
160161
/// cause the \c ASTBuildOperation to be cancelled if no other consumer is
161162
/// depending on it.
162163
void requestCancellation() {
163-
llvm::sys::ScopedLock L(CancellationRequestCallbackMtx);
164-
IsCancelled = true;
165-
if (CancellationRequestCallback.has_value()) {
166-
(*CancellationRequestCallback)(shared_from_this());
164+
Optional<std::function<void(std::shared_ptr<SwiftASTConsumer>)>>
165+
CallbackToCall;
166+
{
167+
llvm::sys::ScopedLock L(CancellationRequestCallbackAndIsCancelledMtx);
168+
IsCancelled = true;
169+
CallbackToCall = CancellationRequestCallback;
167170
CancellationRequestCallback = None;
168171
}
172+
if (CallbackToCall.has_value()) {
173+
(*CallbackToCall)(shared_from_this());
174+
}
169175
}
170176

171177
/// Set a cancellation request callback that informs a \c ASTBuildOperation
@@ -177,20 +183,26 @@ class SwiftASTConsumer : public std::enable_shared_from_this<SwiftASTConsumer> {
177183
/// called, \c NewCallback will be called immediately.
178184
void setCancellationRequestCallback(
179185
std::function<void(std::shared_ptr<SwiftASTConsumer>)> NewCallback) {
180-
llvm::sys::ScopedLock L(CancellationRequestCallbackMtx);
181-
assert(!CancellationRequestCallback.has_value() &&
182-
"Can't set two cancellation callbacks on a SwiftASTConsumer");
183-
if (IsCancelled) {
186+
bool ShouldCallCallback = false;
187+
{
188+
llvm::sys::ScopedLock L(CancellationRequestCallbackAndIsCancelledMtx);
189+
assert(!CancellationRequestCallback.has_value() &&
190+
"Can't set two cancellation callbacks on a SwiftASTConsumer");
191+
if (IsCancelled) {
192+
ShouldCallCallback = true;
193+
} else {
194+
CancellationRequestCallback = NewCallback;
195+
}
196+
}
197+
if (ShouldCallCallback) {
184198
NewCallback(shared_from_this());
185-
} else {
186-
CancellationRequestCallback = NewCallback;
187199
}
188200
}
189201

190202
/// Removes the cancellation request callback previously set by \c
191203
/// setCancellationRequestCallback.
192204
void removeCancellationRequestCallback() {
193-
llvm::sys::ScopedLock L(CancellationRequestCallbackMtx);
205+
llvm::sys::ScopedLock L(CancellationRequestCallbackAndIsCancelledMtx);
194206
CancellationRequestCallback = None;
195207
}
196208

0 commit comments

Comments
 (0)