13
13
import CAtomics
14
14
import Foundation
15
15
import LSPLogging
16
+ import SKSupport
16
17
17
18
/// See comment on ``TaskDescriptionProtocol/dependencies(to:taskPriority:)``
18
19
public enum TaskDependencyAction < TaskDescription: TaskDescriptionProtocol > {
@@ -125,10 +126,6 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
125
126
/// Every time `execute` gets called, a new task is placed in this continuation. See comment on `executionTask`.
126
127
private let executionTaskCreatedContinuation : AsyncStream < Task < ExecutionTaskFinishStatus , Never > > . Continuation
127
128
128
- /// Placing a new value in this continuation will cause `resultTask` to query its priority and set
129
- /// `QueuedTask.priority`.
130
- private let updatePriorityContinuation : AsyncStream < Void > . Continuation
131
-
132
129
nonisolated ( unsafe) private var _priority : AtomicUInt8
133
130
134
131
/// The latest known priority of the task.
@@ -189,16 +186,10 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
189
186
description: TaskDescription ,
190
187
executionStateChangedCallback: ( @Sendable ( QueuedTask, TaskExecutionState) async -> Void ) ?
191
188
) async {
192
- self . _priority = . init ( initialValue: priority? . rawValue ?? Task . currentPriority. rawValue)
189
+ self . _priority = AtomicUInt8 ( initialValue: priority? . rawValue ?? Task . currentPriority. rawValue)
193
190
self . description = description
194
191
self . executionStateChangedCallback = executionStateChangedCallback
195
192
196
- var updatePriorityContinuation : AsyncStream < Void > . Continuation !
197
- let updatePriorityStream = AsyncStream {
198
- updatePriorityContinuation = $0
199
- }
200
- self . updatePriorityContinuation = updatePriorityContinuation
201
-
202
193
var executionTaskCreatedContinuation : AsyncStream < Task < ExecutionTaskFinishStatus , Never > > . Continuation !
203
194
let executionTaskCreatedStream = AsyncStream {
204
195
executionTaskCreatedContinuation = $0
@@ -207,38 +198,24 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
207
198
208
199
self . resultTask = Task . detached ( priority: priority) {
209
200
await withTaskCancellationHandler {
210
- await withTaskGroup ( of: Void . self) { taskGroup in
211
- taskGroup. addTask {
212
- for await _ in updatePriorityStream {
213
- if Task . currentPriority != self . priority {
214
- withLoggingSubsystemAndScope ( subsystem: taskSchedulerSubsystem, scope: nil ) {
215
- logger. debug (
216
- " Updating priority of \( self . description. forLogging) from \( self . priority. rawValue) to \( Task . currentPriority. rawValue) "
217
- )
218
- }
219
- self . priority = Task . currentPriority
220
- }
201
+ await withTaskPriorityChangedHandler ( initialPriority: self . priority) {
202
+ for await task in executionTaskCreatedStream {
203
+ switch await task. valuePropagatingCancellation {
204
+ case . cancelledToBeRescheduled:
205
+ // Break the switch and wait for a new `executionTask` to be placed into `executionTaskCreatedStream`.
206
+ break
207
+ case . terminated:
208
+ // The task finished. We are done with this `QueuedTask`
209
+ return
221
210
}
222
211
}
223
- taskGroup. addTask {
224
- for await task in executionTaskCreatedStream {
225
- switch await task. valuePropagatingCancellation {
226
- case . cancelledToBeRescheduled:
227
- // Break the switch and wait for a new `executionTask` to be placed into `executionTaskCreatedStream`.
228
- break
229
- case . terminated:
230
- // The task finished. We are done with this `QueuedTask`
231
- return
232
- }
233
- }
234
- }
235
- // The first (update priority) task never finishes, so this waits for the second (wait for execution) task
236
- // to terminate.
237
- // Afterwards we also cancel the update priority task.
238
- for await _ in taskGroup {
239
- taskGroup. cancelAll ( )
240
- return
212
+ } taskPriorityChanged: {
213
+ withLoggingSubsystemAndScope ( subsystem: taskSchedulerSubsystem, scope: nil ) {
214
+ logger. debug (
215
+ " Updating priority of \( self . description. forLogging) from \( self . priority. rawValue) to \( Task . currentPriority. rawValue) "
216
+ )
241
217
}
218
+ self . priority = Task . currentPriority
242
219
}
243
220
} onCancel: {
244
221
self . resultTaskCancelled. value = true
@@ -291,16 +268,6 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
291
268
self . executionTask = nil
292
269
}
293
270
294
- /// Trigger `QueuedTask.priority` to be updated with the current priority of the underlying task.
295
- ///
296
- /// This is an asynchronous operation that makes no guarantees when the updated priority will be available.
297
- ///
298
- /// This is needed because tasks can't subscribe to priority updates (ie. there is no `withPriorityHandler` similar to
299
- /// `withCancellationHandler`, https://github.com/apple/swift/issues/73367).
300
- func triggerPriorityUpdate( ) {
301
- updatePriorityContinuation. yield ( )
302
- }
303
-
304
271
/// If the priority of this task is less than `targetPriority`, elevate the priority to `targetPriority` by spawning
305
272
/// a new task that depends on it. Otherwise a no-op.
306
273
nonisolated func elevatePriority( to targetPriority: TaskPriority ) {
@@ -399,16 +366,6 @@ public actor TaskScheduler<TaskDescription: TaskDescriptionProtocol> {
399
366
return queuedTask
400
367
}
401
368
402
- /// Trigger all queued tasks to update their priority.
403
- ///
404
- /// Should be called occasionally to elevate tasks in the queue whose underlying `Swift.Task` had their priority
405
- /// elevated because a higher-priority task started depending on them.
406
- private func triggerPriorityUpdateOfQueuedTasks( ) async {
407
- for task in pendingTasks {
408
- await task. triggerPriorityUpdate ( )
409
- }
410
- }
411
-
412
369
/// Returns the maximum number of concurrent tasks that are allowed to execute at the given priority.
413
370
private func maxConcurrentTasks( at priority: TaskPriority ) -> Int {
414
371
for (atPriority, maxConcurrentTasks) in maxConcurrentTasksByPriority {
@@ -431,9 +388,8 @@ public actor TaskScheduler<TaskDescription: TaskDescriptionProtocol> {
431
388
{
432
389
// We don't have any execution slots left. Thus, this poker has nothing to do and is done.
433
390
// When the next task finishes, it calls `poke` again.
434
- // If the low priority task's priority gets elevated, that will be picked up when the next task in the
435
- // `TaskScheduler` finishes, which causes `triggerPriorityUpdateOfQueuedTasks` to be called, which transfers
436
- // the new elevated priority to `QueuedTask.priority` and which can then be picked up by the next `poke` call.
391
+ // If the low priority task's priority gets elevated that task's priority will get elevated and it will be
392
+ // picked up on the next `poke` call.
437
393
return
438
394
}
439
395
let dependencies = task. description. dependencies ( to: currentlyExecutingTasks. map ( \. description) )
@@ -535,7 +491,6 @@ public actor TaskScheduler<TaskDescription: TaskDescriptionProtocol> {
535
491
case . terminated: break
536
492
case . cancelledToBeRescheduled: pendingTasks. append ( task)
537
493
}
538
- await self . triggerPriorityUpdateOfQueuedTasks ( )
539
494
self . poke ( )
540
495
}
541
496
}
0 commit comments