@@ -630,13 +630,12 @@ package final actor SemanticIndexManager {
630630
631631 // MARK: - Helper functions
632632
633- private func prepare (
633+ private func schedulePreparation (
634634 targets: [ BuildTargetIdentifier ] ,
635635 purpose: TargetPreparationPurpose ,
636636 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 > ? {
640639 // Perform a quick initial check whether the target is up-to-date, in which case we don't need to schedule a
641640 // preparation operation at all.
642641 // 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 {
647646 }
648647
649648 guard !targetsToPrepare. isEmpty else {
650- return
649+ return nil
651650 }
652651
653652 let taskDescription = AnyIndexTaskDescription (
@@ -660,7 +659,7 @@ package final actor SemanticIndexManager {
660659 )
661660 )
662661 if Task . isCancelled {
663- return
662+ return nil
664663 }
665664 let preparationTask = await indexTaskScheduler. schedule ( priority: priority, taskDescription) { task, newState in
666665 await executionStatusChangedCallback ( task, newState)
@@ -689,6 +688,25 @@ package final actor SemanticIndexManager {
689688 purpose: mergedPurpose
690689 )
691690 }
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+ }
692710 await withTaskCancellationHandler {
693711 return await preparationTask. waitToFinish ( )
694712 } onCancel: {
@@ -884,22 +902,30 @@ package final actor SemanticIndexManager {
884902 let preparationTaskID = UUID ( )
885903 let filesToIndex = targetsBatch. flatMap ( { filesByTarget [ $0] ! } )
886904
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+ )
900922 }
901923 }
902924 }
925+ }
926+
927+ let indexTask = Task ( priority: priority) {
928+ await preparationTask? . waitToFinishPropagatingCancellation ( )
903929
904930 // And after preparation is done, index the files in the targets.
905931 await withTaskGroup ( of: Void . self) { taskGroup in
0 commit comments