@@ -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
@@ -54,13 +67,7 @@ public final actor SemanticIndexManager {
54
67
/// The preparation status of the targets that the `SemanticIndexManager` has started preparation for.
55
68
///
56
69
/// 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 > ) > ] = [ : ]
70
+ private var preparationStatus : [ ConfiguredTarget : IndexStatus < PreparationTaskStatusData > ] = [ : ]
64
71
65
72
/// The index status of the source files that the `SemanticIndexManager` knows about.
66
73
///
@@ -268,16 +275,16 @@ public final actor SemanticIndexManager {
268
275
switch preparationStatus [ target] {
269
276
case . upToDate:
270
277
break
271
- case . scheduled( ( _ , let existingTaskTargets , let task ) ) , . executing( ( _ , let existingTaskTargets , let task ) ) :
278
+ case . scheduled( let existingTaskData ) , . executing( let existingTaskData ) :
272
279
// If we already have a task scheduled that prepares fewer targets, await that instead of overriding the
273
280
// target's preparation status with a longer-running task. The key benefit here is that when we get many
274
281
// preparation requests for the same target (eg. one for every text document request sent to a file), we don't
275
282
// re-create new `PreparationTaskDescription`s for every preparation request. Instead, all the preparation
276
283
// requests await the same task. At the same time, if we have a multi-file preparation request and then get a
277
284
// single-file preparation request, we will override the preparation of that target with the single-file
278
285
// preparation task, ensuring that the task gets prepared as quickly as possible.
279
- if existingTaskTargets . count <= targets. count {
280
- preparationTasksToAwait. append ( task)
286
+ if existingTaskData . targets . count <= targets. count {
287
+ preparationTasksToAwait. append ( existingTaskData . task)
281
288
} else {
282
289
targetsToPrepare. append ( target)
283
290
}
@@ -301,20 +308,22 @@ public final actor SemanticIndexManager {
301
308
switch newState {
302
309
case . executing:
303
310
for target in targetsToPrepare {
304
- if case . scheduled( ( taskID, let targets, let task) ) = self . preparationStatus [ target] {
305
- self . preparationStatus [ target] = . executing( ( taskID, targets, task) )
311
+ if case . scheduled( let existingTaskData) = self . preparationStatus [ target] , existingTaskData. taskID == taskID
312
+ {
313
+ self . preparationStatus [ target] = . executing( existingTaskData)
306
314
}
307
315
}
308
316
case . cancelledToBeRescheduled:
309
317
for target in targetsToPrepare {
310
- if case . executing( ( taskID, let targets, let task) ) = self . preparationStatus [ target] {
311
- self . preparationStatus [ target] = . scheduled( ( taskID, targets, task) )
318
+ if case . executing( let existingTaskData) = self . preparationStatus [ target] , existingTaskData. taskID == taskID
319
+ {
320
+ self . preparationStatus [ target] = . scheduled( existingTaskData)
312
321
}
313
322
}
314
323
case . finished:
315
324
for target in targetsToPrepare {
316
325
switch self . preparationStatus [ target] {
317
- case . executing( ( taskID, _ , _ ) ) :
326
+ case . executing( let existingTaskData ) where existingTaskData . taskID == taskID :
318
327
self . preparationStatus [ target] = . upToDate
319
328
default :
320
329
break
@@ -324,14 +333,16 @@ public final actor SemanticIndexManager {
324
333
}
325
334
}
326
335
for target in targetsToPrepare {
327
- preparationStatus [ target] = . scheduled( ( taskID, targetsToPrepare, preparationTask) )
336
+ preparationStatus [ target] = . scheduled(
337
+ PreparationTaskStatusData ( taskID: taskID, targets: targetsToPrepare, task: preparationTask)
338
+ )
328
339
}
329
340
preparationTasksToAwait. append ( preparationTask)
330
341
}
331
342
await withTaskGroup ( of: Void . self) { taskGroup in
332
343
for task in preparationTasksToAwait {
333
344
taskGroup. addTask {
334
- await task. value
345
+ await task. valuePropagatingCancellation
335
346
}
336
347
}
337
348
await taskGroup. waitForAll ( )
0 commit comments