@@ -457,15 +457,16 @@ class ASTProducer : public std::enable_shared_from_this<ASTProducer> {
457
457
// / execute. Operations are guaranteed to be in FIFO order, that is the first
458
458
// / one in the vector is the oldes build operation.
459
459
SmallVector<ASTBuildOperationRef, 4 > BuildOperations = {};
460
- llvm::sys::Mutex BuildOperationsMtx;
460
+ WorkQueue BuildOperationsQueue = WorkQueue(
461
+ WorkQueue::Dequeuing::Serial, " ASTProducer.BuildOperationsQueue" );
461
462
462
463
// / Erase all finished build operations with a result except for the latest
463
464
// / one which contains a successful results.
464
465
// / This cleans up all stale build operations (probably containing old ASTs),
465
466
// / but keeps the latest AST around, so that new consumers can be served from
466
467
// / it, if possible.
467
468
// /
468
- // / Assumes that \c BuildOperationsMtx has been claimed .
469
+ // / Must be executed on \c BuildOperationsQueue .
469
470
void cleanBuildOperations () {
470
471
auto ReverseOperations = llvm::reverse (BuildOperations);
471
472
auto LastOperationWithResultIt =
@@ -485,7 +486,7 @@ class ASTProducer : public std::enable_shared_from_this<ASTProducer> {
485
486
// / Returns the latest build operation which can serve the \p Consumer or
486
487
// / \c nullptr if no such build operation exists.
487
488
// /
488
- // / Assumes that \c BuildOperationsMtx has been claimed .
489
+ // / Must be executed on \c BuildOperationsQueue .
489
490
ASTBuildOperationRef getBuildOperationForConsumer (
490
491
SwiftASTConsumerRef Consumer,
491
492
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
@@ -1162,13 +1163,13 @@ bool ASTBuildOperation::addConsumer(SwiftASTConsumerRef Consumer) {
1162
1163
} else {
1163
1164
assert (OperationState != State::Finished);
1164
1165
auto WeakThis = std::weak_ptr<ASTBuildOperation>(shared_from_this ());
1166
+ Consumers.push_back (Consumer);
1165
1167
Consumer->setCancellationRequestCallback (
1166
1168
[WeakThis](SwiftASTConsumerRef Consumer) {
1167
1169
if (auto This = WeakThis.lock ()) {
1168
1170
This->requestConsumerCancellation (Consumer);
1169
1171
}
1170
1172
});
1171
- Consumers.push_back (Consumer);
1172
1173
}
1173
1174
return true ;
1174
1175
}
@@ -1201,35 +1202,40 @@ void ASTProducer::enqueueConsumer(
1201
1202
SwiftASTConsumerRef Consumer,
1202
1203
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
1203
1204
SwiftASTManagerRef Mgr) {
1204
- // We can't use a llvm::sys::ScopedLock here because we need to unlock it
1205
- // before calling enqueueConsumer again in the !WasAdded case below.
1206
- std::unique_lock<llvm::sys::Mutex> BuildOperationsLock (BuildOperationsMtx);
1207
- if (auto BuildOp = getBuildOperationForConsumer (Consumer, FileSystem, Mgr)) {
1208
- bool WasAdded = BuildOp->addConsumer (Consumer);
1209
- if (!WasAdded) {
1210
- // The build operation was cancelled after the call to
1211
- // getBuildOperationForConsumer but before the consumer could be added.
1212
- // This should be an absolute edge case. Let's just try again.
1213
- BuildOperationsLock.unlock ();
1214
- enqueueConsumer (Consumer, FileSystem, Mgr);
1215
- return ;
1216
- }
1217
- } else {
1218
- auto WeakThis = std::weak_ptr<ASTProducer>(shared_from_this ());
1219
- auto DidFinishCallback = [WeakThis, Mgr]() {
1220
- if (auto This = WeakThis.lock ()) {
1221
- {
1222
- llvm::sys::ScopedLock L (This->BuildOperationsMtx );
1223
- This->cleanBuildOperations ();
1224
- }
1225
- // Re-register the object with the cache to update its memory cost.
1226
- Mgr->Impl .ASTCache .set (This->InvokRef ->Impl .Key , This);
1205
+ // Enqueue the consumer in the background because getBuildOperationForConsumer
1206
+ // consults the file system and might be slow. Also, there's no need to do
1207
+ // this synchronously since all results will be delivered async anyway.
1208
+ auto This = shared_from_this ();
1209
+ BuildOperationsQueue.dispatch ([Consumer, FileSystem, Mgr, This]() {
1210
+ if (auto BuildOp =
1211
+ This->getBuildOperationForConsumer (Consumer, FileSystem, Mgr)) {
1212
+ bool WasAdded = BuildOp->addConsumer (Consumer);
1213
+ if (!WasAdded) {
1214
+ // The build operation was cancelled after the call to
1215
+ // getBuildOperationForConsumer but before the consumer could be
1216
+ // added. This should be an absolute edge case. Let's just try
1217
+ // again.
1218
+ This->enqueueConsumer (Consumer, FileSystem, Mgr);
1227
1219
}
1228
- };
1229
- ASTBuildOperationRef NewBuildOp = std::make_shared<ASTBuildOperation>(
1230
- FileSystem, InvokRef, Mgr, DidFinishCallback);
1231
- BuildOperations.push_back (NewBuildOp);
1232
- NewBuildOp->addConsumer (Consumer);
1233
- NewBuildOp->schedule (Mgr->Impl .ASTBuildQueue );
1234
- }
1220
+ } else {
1221
+ auto WeakThis = std::weak_ptr<ASTProducer>(This);
1222
+ auto DidFinishCallback = [WeakThis, Mgr]() {
1223
+ if (auto This = WeakThis.lock ()) {
1224
+ This->BuildOperationsQueue .dispatchSync (
1225
+ [This]() { This->cleanBuildOperations (); });
1226
+ // Re-register the object with the cache to update its memory
1227
+ // cost.
1228
+ Mgr->Impl .ASTCache .set (This->InvokRef ->Impl .Key , This);
1229
+ }
1230
+ };
1231
+ ASTBuildOperationRef NewBuildOp = std::make_shared<ASTBuildOperation>(
1232
+ FileSystem, This->InvokRef , Mgr, DidFinishCallback);
1233
+ This->BuildOperations .push_back (NewBuildOp);
1234
+ bool WasAdded = NewBuildOp->addConsumer (Consumer);
1235
+ assert (WasAdded && " Consumer wasn't added to a new build operation "
1236
+ " that can't have been cancelled yet?" );
1237
+ (void )WasAdded;
1238
+ NewBuildOp->schedule (Mgr->Impl .ASTBuildQueue );
1239
+ }
1240
+ });
1235
1241
}
0 commit comments