@@ -38,6 +38,19 @@ private enum IndexStatus<T> {
38
38
}
39
39
}
40
40
41
+ /// See `SemanticIndexManager.preparationStatus`
42
+ private struct PreparationTaskStatusData {
43
+ /// A UUID to track the task. This is used to ensure that status updates from this task don't update
44
+ /// `preparationStatus` for targets that are tracked by a different task.
45
+ let taskID : UUID
46
+
47
+ /// The list of targets that are being prepared in a joint preparation operation.
48
+ let targets : [ ConfiguredTarget ]
49
+
50
+ /// The task that prepares the target
51
+ let task : Task < Void , Never >
52
+ }
53
+
41
54
/// Schedules index tasks and keeps track of the index status of files.
42
55
public final actor SemanticIndexManager {
43
56
/// The underlying index. This is used to check if the index of a file is already up-to-date, in which case it doesn't
@@ -47,20 +60,16 @@ public final actor SemanticIndexManager {
47
60
/// The build system manager that is used to get compiler arguments for a file.
48
61
private let buildSystemManager : BuildSystemManager
49
62
63
+ private let testHooks : IndexTestHooks
64
+
50
65
/// The task to generate the build graph (resolving package dependencies, generating the build description,
51
66
/// ...). `nil` if no build graph is currently being generated.
52
67
private var generateBuildGraphTask : Task < Void , Never > ?
53
68
54
69
/// The preparation status of the targets that the `SemanticIndexManager` has started preparation for.
55
70
///
56
71
/// Targets will be removed from this dictionary when they are no longer known to be up-to-date.
57
- ///
58
- /// The associated values of the `IndexStatus` are:
59
- /// - A UUID to track the task. This is used to ensure that status updates from this task don't update
60
- /// `preparationStatus` for targets that are tracked by a different task.
61
- /// - The list of targets that are being prepared in a joint preparation operation
62
- /// - The task that prepares the target
63
- private var preparationStatus : [ ConfiguredTarget : IndexStatus < ( UUID , [ ConfiguredTarget ] , Task < Void , Never > ) > ] = [ : ]
72
+ private var preparationStatus : [ ConfiguredTarget : IndexStatus < PreparationTaskStatusData > ] = [ : ]
64
73
65
74
/// The index status of the source files that the `SemanticIndexManager` knows about.
66
75
///
@@ -114,12 +123,14 @@ public final actor SemanticIndexManager {
114
123
public init (
115
124
index: UncheckedIndex ,
116
125
buildSystemManager: BuildSystemManager ,
126
+ testHooks: IndexTestHooks ,
117
127
indexTaskScheduler: TaskScheduler < AnyIndexTaskDescription > ,
118
128
indexTasksWereScheduled: @escaping @Sendable ( Int ) -> Void ,
119
129
indexTaskDidFinish: @escaping @Sendable ( ) -> Void
120
130
) {
121
131
self . index = index
122
132
self . buildSystemManager = buildSystemManager
133
+ self . testHooks = testHooks
123
134
self . indexTaskScheduler = indexTaskScheduler
124
135
self . indexTasksWereScheduled = indexTasksWereScheduled
125
136
self . indexTaskDidFinish = indexTaskDidFinish
@@ -278,16 +289,16 @@ public final actor SemanticIndexManager {
278
289
switch preparationStatus [ target] {
279
290
case . upToDate:
280
291
break
281
- case . scheduled( ( _ , let existingTaskTargets , let task ) ) , . executing( ( _ , let existingTaskTargets , let task ) ) :
292
+ case . scheduled( let existingTaskData ) , . executing( let existingTaskData ) :
282
293
// If we already have a task scheduled that prepares fewer targets, await that instead of overriding the
283
294
// target's preparation status with a longer-running task. The key benefit here is that when we get many
284
295
// preparation requests for the same target (eg. one for every text document request sent to a file), we don't
285
296
// re-create new `PreparationTaskDescription`s for every preparation request. Instead, all the preparation
286
297
// requests await the same task. At the same time, if we have a multi-file preparation request and then get a
287
298
// single-file preparation request, we will override the preparation of that target with the single-file
288
299
// preparation task, ensuring that the task gets prepared as quickly as possible.
289
- if existingTaskTargets . count <= targets. count {
290
- preparationTasksToAwait. append ( task)
300
+ if existingTaskData . targets . count <= targets. count {
301
+ preparationTasksToAwait. append ( existingTaskData . task)
291
302
} else {
292
303
targetsToPrepare. append ( target)
293
304
}
@@ -299,7 +310,8 @@ public final actor SemanticIndexManager {
299
310
let taskDescription = AnyIndexTaskDescription (
300
311
PreparationTaskDescription (
301
312
targetsToPrepare: targetsToPrepare,
302
- buildSystemManager: self . buildSystemManager
313
+ buildSystemManager: self . buildSystemManager,
314
+ testHooks: testHooks
303
315
)
304
316
)
305
317
if !targetsToPrepare. isEmpty {
@@ -311,20 +323,22 @@ public final actor SemanticIndexManager {
311
323
switch newState {
312
324
case . executing:
313
325
for target in targetsToPrepare {
314
- if case . scheduled( ( taskID, let targets, let task) ) = self . preparationStatus [ target] {
315
- self . preparationStatus [ target] = . executing( ( taskID, targets, task) )
326
+ if case . scheduled( let existingTaskData) = self . preparationStatus [ target] , existingTaskData. taskID == taskID
327
+ {
328
+ self . preparationStatus [ target] = . executing( existingTaskData)
316
329
}
317
330
}
318
331
case . cancelledToBeRescheduled:
319
332
for target in targetsToPrepare {
320
- if case . executing( ( taskID, let targets, let task) ) = self . preparationStatus [ target] {
321
- self . preparationStatus [ target] = . scheduled( ( taskID, targets, task) )
333
+ if case . executing( let existingTaskData) = self . preparationStatus [ target] , existingTaskData. taskID == taskID
334
+ {
335
+ self . preparationStatus [ target] = . scheduled( existingTaskData)
322
336
}
323
337
}
324
338
case . finished:
325
339
for target in targetsToPrepare {
326
340
switch self . preparationStatus [ target] {
327
- case . executing( ( taskID, _ , _ ) ) :
341
+ case . executing( let existingTaskData ) where existingTaskData . taskID == taskID :
328
342
self . preparationStatus [ target] = . upToDate
329
343
default :
330
344
break
@@ -334,14 +348,16 @@ public final actor SemanticIndexManager {
334
348
}
335
349
}
336
350
for target in targetsToPrepare {
337
- preparationStatus [ target] = . scheduled( ( taskID, targetsToPrepare, preparationTask) )
351
+ preparationStatus [ target] = . scheduled(
352
+ PreparationTaskStatusData ( taskID: taskID, targets: targetsToPrepare, task: preparationTask)
353
+ )
338
354
}
339
355
preparationTasksToAwait. append ( preparationTask)
340
356
}
341
357
await withTaskGroup ( of: Void . self) { taskGroup in
342
358
for task in preparationTasksToAwait {
343
359
taskGroup. addTask {
344
- await task. value
360
+ await task. valuePropagatingCancellation
345
361
}
346
362
}
347
363
await taskGroup. waitForAll ( )
@@ -354,7 +370,8 @@ public final actor SemanticIndexManager {
354
370
UpdateIndexStoreTaskDescription (
355
371
filesToIndex: filesAndTargets,
356
372
buildSystemManager: self . buildSystemManager,
357
- index: index
373
+ index: index,
374
+ testHooks: testHooks
358
375
)
359
376
)
360
377
let updateIndexStoreTask = await self . indexTaskScheduler. schedule ( priority: priority, taskDescription) { newState in
0 commit comments