Skip to content

Commit bb907a5

Browse files
committed
Don’t re-index file if we are waiting for its preparation
This fixes an issue that caused the index progress to not make progress at the beginning when opening a new project because of the following: When opening the project, we would cause package resolution and schedule indexing of all source files after the package has been resolved (and dependencies been downloaded). But dependency resolution will add the dependency files to be written to disk and the editor will inform us of these modified files, which will cause us to schedule a new index operation for these files. Thus, when the initial indexing of the files finishes, we won’t increase the number of tasks that we have already indexed. To fix this, don’t schedule new indexing of these files if we are still waiting for their preparation to start. This should not be a big performance win because even before this change, when we were trying to index the file the second time, we would have realized that the index store is up to date and thus do a no-op. The main benefit of this change is to have the index counter be more accurate.
1 parent f900b4e commit bb907a5

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)