Skip to content

Commit 29619a6

Browse files
authored
Merge pull request #1864 from ahoppen/no-double-index
Don’t re-index file if we are waiting for its preparation
2 parents b42e62f + bb907a5 commit 29619a6

File tree

1 file changed

+60
-14
lines changed

1 file changed

+60
-14
lines changed

Sources/SemanticIndex/SemanticIndexManager.swift

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ private struct OpaqueQueuedIndexTask: Equatable {
5151
}
5252

5353
private enum InProgressIndexStore {
54-
/// We are waiting for preparation of the file's target to finish before we can index it.
54+
/// We are waiting for preparation of the file's target to be scheduled. The next step is that we wait for
55+
/// prepration to finish before we can update the index store for this file.
5556
///
5657
/// `preparationTaskID` identifies the preparation task so that we can transition a file's index state to
5758
/// `updatingIndexStore` when its preparation task has finished.
@@ -60,6 +61,16 @@ private enum InProgressIndexStore {
6061
/// task is still the sole owner of it and responsible for its cancellation.
6162
case waitingForPreparation(preparationTaskID: UUID, indexTask: Task<Void, Never>)
6263

64+
/// We have started preparing this file and are waiting for preparation to finish before we can update the index
65+
/// store for this file.
66+
///
67+
/// `preparationTaskID` identifies the preparation task so that we can transition a file's index state to
68+
/// `updatingIndexStore` when its preparation task has finished.
69+
///
70+
/// `indexTask` is a task that finishes after both preparation and index store update are done. Whoever owns the index
71+
/// task is still the sole owner of it and responsible for its cancellation.
72+
case preparing(preparationTaskID: UUID, indexTask: Task<Void, Never>)
73+
6374
/// The file's target has been prepared and we are updating the file's index store.
6475
///
6576
/// `updateIndexStoreTask` is the task that updates the index store itself.
@@ -210,7 +221,7 @@ package final actor SemanticIndexManager {
210221
}
211222
let indexTasks = inProgressIndexTasks.mapValues { status in
212223
switch status {
213-
case .waitingForPreparation:
224+
case .waitingForPreparation, .preparing:
214225
return IndexTaskStatus.scheduled
215226
case .updatingIndexStore(updateIndexStoreTask: let updateIndexStoreTask, indexTask: _):
216227
return updateIndexStoreTask.isExecuting ? IndexTaskStatus.executing : IndexTaskStatus.scheduled
@@ -284,7 +295,17 @@ package final actor SemanticIndexManager {
284295
}
285296
if !indexFilesWithUpToDateUnit {
286297
let index = index.checked(for: .modifiedFiles)
287-
filesToIndex = filesToIndex.filter { !index.hasUpToDateUnit(for: $0) }
298+
filesToIndex = filesToIndex.filter {
299+
if index.hasUpToDateUnit(for: $0) {
300+
return false
301+
}
302+
if case .waitingForPreparation = inProgressIndexTasks[$0] {
303+
// We haven't started preparing the file yet. Scheduling a new index operation for it won't produce any
304+
// more recent results.
305+
return false
306+
}
307+
return true
308+
}
288309
}
289310
await scheduleBackgroundIndex(files: filesToIndex, indexFilesWithUpToDateUnit: indexFilesWithUpToDateUnit)
290311
generateBuildGraphTask = nil
@@ -312,11 +333,11 @@ package final actor SemanticIndexManager {
312333
for (_, status) in inProgressIndexTasks {
313334
switch status {
314335
case .waitingForPreparation(preparationTaskID: _, indexTask: let indexTask),
336+
.preparing(preparationTaskID: _, indexTask: let indexTask),
315337
.updatingIndexStore(updateIndexStoreTask: _, indexTask: let indexTask):
316338
taskGroup.addTask {
317339
await indexTask.value
318340
}
319-
320341
}
321342
}
322343
await taskGroup.waitForAll()
@@ -462,7 +483,9 @@ package final actor SemanticIndexManager {
462483
private func prepare(
463484
targets: [BuildTargetIdentifier],
464485
purpose: TargetPreparationPurpose,
465-
priority: TaskPriority?
486+
priority: TaskPriority?,
487+
executionStatusChangedCallback: @escaping (QueuedTask<AnyIndexTaskDescription>, TaskExecutionState) async -> Void =
488+
{ _, _ in }
466489
) async {
467490
// Perform a quick initial check whether the target is up-to-date, in which case we don't need to schedule a
468491
// preparation operation at all.
@@ -490,6 +513,7 @@ package final actor SemanticIndexManager {
490513
return
491514
}
492515
let preparationTask = await indexTaskScheduler.schedule(priority: priority, taskDescription) { task, newState in
516+
await executionStatusChangedCallback(task, newState)
493517
guard case .finished = newState else {
494518
self.indexProgressStatusDidChange()
495519
return
@@ -547,28 +571,36 @@ package final actor SemanticIndexManager {
547571
testHooks: testHooks
548572
)
549573
)
574+
550575
let updateIndexTask = await indexTaskScheduler.schedule(priority: priority, taskDescription) { task, newState in
551576
guard case .finished = newState else {
552577
self.indexProgressStatusDidChange()
553578
return
554579
}
555580
for fileAndTarget in filesAndTargets {
556-
if case .updatingIndexStore(OpaqueQueuedIndexTask(task), _) = self.inProgressIndexTasks[
557-
fileAndTarget.file.sourceFile
558-
] {
559-
self.inProgressIndexTasks[fileAndTarget.file.sourceFile] = nil
581+
switch self.inProgressIndexTasks[fileAndTarget.file.sourceFile] {
582+
case .updatingIndexStore(let registeredTask, _):
583+
if registeredTask == OpaqueQueuedIndexTask(task) {
584+
self.inProgressIndexTasks[fileAndTarget.file.sourceFile] = nil
585+
}
586+
case .waitingForPreparation(let registeredTask, _), .preparing(let registeredTask, _):
587+
if registeredTask == preparationTaskID {
588+
self.inProgressIndexTasks[fileAndTarget.file.sourceFile] = nil
589+
}
590+
case nil:
591+
break
560592
}
561593
}
562594
self.indexProgressStatusDidChange()
563595
}
564596
for fileAndTarget in filesAndTargets {
565-
if case .waitingForPreparation(preparationTaskID, let indexTask) = inProgressIndexTasks[
566-
fileAndTarget.file.sourceFile
567-
] {
597+
switch inProgressIndexTasks[fileAndTarget.file.sourceFile] {
598+
case .waitingForPreparation(preparationTaskID, let indexTask), .preparing(preparationTaskID, let indexTask):
568599
inProgressIndexTasks[fileAndTarget.file.sourceFile] = .updatingIndexStore(
569600
updateIndexStoreTask: OpaqueQueuedIndexTask(updateIndexTask),
570601
indexTask: indexTask
571602
)
603+
default: break
572604
}
573605
}
574606
return await updateIndexTask.waitToFinishPropagatingCancellation()
@@ -639,9 +671,24 @@ package final actor SemanticIndexManager {
639671
// (https://github.com/swiftlang/sourcekit-lsp/issues/1262)
640672
for targetsBatch in sortedTargets.partition(intoBatchesOfSize: 1) {
641673
let preparationTaskID = UUID()
674+
let filesToIndex = targetsBatch.flatMap({ filesByTarget[$0]! })
675+
642676
let indexTask = Task(priority: priority) {
643677
// First prepare the targets.
644-
await prepare(targets: targetsBatch, purpose: .forIndexing, priority: priority)
678+
await prepare(targets: targetsBatch, purpose: .forIndexing, priority: priority) { task, newState in
679+
if case .executing = newState {
680+
for file in filesToIndex {
681+
if case .waitingForPreparation(preparationTaskID: preparationTaskID, indexTask: let indexTask) =
682+
self.inProgressIndexTasks[file.sourceFile]
683+
{
684+
self.inProgressIndexTasks[file.sourceFile] = .preparing(
685+
preparationTaskID: preparationTaskID,
686+
indexTask: indexTask
687+
)
688+
}
689+
}
690+
}
691+
}
645692

646693
// And after preparation is done, index the files in the targets.
647694
await withTaskGroup(of: Void.self) { taskGroup in
@@ -665,7 +712,6 @@ package final actor SemanticIndexManager {
665712
}
666713
indexTasks.append(indexTask)
667714

668-
let filesToIndex = targetsBatch.flatMap({ filesByTarget[$0]! })
669715
// The number of index tasks that don't currently have an in-progress task associated with it.
670716
// The denominator in the index progress should get incremented by this amount.
671717
// We don't want to increment the denominator for tasks that already have an index in progress.

0 commit comments

Comments
 (0)