@@ -630,13 +630,12 @@ package final actor SemanticIndexManager {
630
630
631
631
// MARK: - Helper functions
632
632
633
- private func prepare (
633
+ private func schedulePreparation (
634
634
targets: [ BuildTargetIdentifier ] ,
635
635
purpose: TargetPreparationPurpose ,
636
636
priority: TaskPriority ? ,
637
- executionStatusChangedCallback: @escaping ( QueuedTask < AnyIndexTaskDescription > , TaskExecutionState ) async -> Void =
638
- { _, _ in }
639
- ) async {
637
+ executionStatusChangedCallback: @escaping ( QueuedTask < AnyIndexTaskDescription > , TaskExecutionState ) async -> Void
638
+ ) async -> QueuedTask < AnyIndexTaskDescription > ? {
640
639
// Perform a quick initial check whether the target is up-to-date, in which case we don't need to schedule a
641
640
// preparation operation at all.
642
641
// We will check the up-to-date status again in `PreparationTaskDescription.execute`. This ensures that if we
@@ -647,7 +646,7 @@ package final actor SemanticIndexManager {
647
646
}
648
647
649
648
guard !targetsToPrepare. isEmpty else {
650
- return
649
+ return nil
651
650
}
652
651
653
652
let taskDescription = AnyIndexTaskDescription (
@@ -660,7 +659,7 @@ package final actor SemanticIndexManager {
660
659
)
661
660
)
662
661
if Task . isCancelled {
663
- return
662
+ return nil
664
663
}
665
664
let preparationTask = await indexTaskScheduler. schedule ( priority: priority, taskDescription) { task, newState in
666
665
await executionStatusChangedCallback ( task, newState)
@@ -689,6 +688,25 @@ package final actor SemanticIndexManager {
689
688
purpose: mergedPurpose
690
689
)
691
690
}
691
+ return preparationTask
692
+ }
693
+
694
+ private func prepare(
695
+ targets: [ BuildTargetIdentifier ] ,
696
+ purpose: TargetPreparationPurpose ,
697
+ priority: TaskPriority ? ,
698
+ executionStatusChangedCallback: @escaping ( QueuedTask < AnyIndexTaskDescription > , TaskExecutionState ) async -> Void =
699
+ { _, _ in }
700
+ ) async {
701
+ let preparationTask = await schedulePreparation (
702
+ targets: targets,
703
+ purpose: purpose,
704
+ priority: priority,
705
+ executionStatusChangedCallback: executionStatusChangedCallback
706
+ )
707
+ guard let preparationTask else {
708
+ return
709
+ }
692
710
await withTaskCancellationHandler {
693
711
return await preparationTask. waitToFinish ( )
694
712
} onCancel: {
@@ -884,22 +902,30 @@ package final actor SemanticIndexManager {
884
902
let preparationTaskID = UUID ( )
885
903
let filesToIndex = targetsBatch. flatMap ( { filesByTarget [ $0] ! } )
886
904
887
- let indexTask = Task ( priority: priority) {
888
- // First prepare the targets.
889
- await prepare ( targets: targetsBatch, purpose: . forIndexing, priority: priority) { task, newState in
890
- if case . executing = newState {
891
- for file in filesToIndex {
892
- if case . waitingForPreparation( preparationTaskID: preparationTaskID, indexTask: let indexTask) =
893
- self . inProgressIndexTasks [ file] ? . state
894
- {
895
- self . inProgressIndexTasks [ file] ? . state = . preparing(
896
- preparationTaskID: preparationTaskID,
897
- indexTask: indexTask
898
- )
899
- }
905
+ // First schedule preparation of the targets. We schedule the preparation outside of `indexTask` so that we
906
+ // deterministically prepare targets in the topological order for indexing. If we triggered preparation inside the
907
+ // indexTask, we would get nondeterministic ordering since Tasks may start executing in any order.
908
+ let preparationTask = await schedulePreparation (
909
+ targets: targetsBatch,
910
+ purpose: . forIndexing,
911
+ priority: priority
912
+ ) { task, newState in
913
+ if case . executing = newState {
914
+ for file in filesToIndex {
915
+ if case . waitingForPreparation( preparationTaskID: preparationTaskID, indexTask: let indexTask) =
916
+ self . inProgressIndexTasks [ file] ? . state
917
+ {
918
+ self . inProgressIndexTasks [ file] ? . state = . preparing(
919
+ preparationTaskID: preparationTaskID,
920
+ indexTask: indexTask
921
+ )
900
922
}
901
923
}
902
924
}
925
+ }
926
+
927
+ let indexTask = Task ( priority: priority) {
928
+ await preparationTask? . waitToFinishPropagatingCancellation ( )
903
929
904
930
// And after preparation is done, index the files in the targets.
905
931
await withTaskGroup ( of: Void . self) { taskGroup in
0 commit comments