1313import CAtomics
1414import Foundation
1515import LSPLogging
16+ import SKSupport
1617
1718/// See comment on ``TaskDescriptionProtocol/dependencies(to:taskPriority:)``
1819public enum TaskDependencyAction < TaskDescription: TaskDescriptionProtocol > {
@@ -125,10 +126,6 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
125126 /// Every time `execute` gets called, a new task is placed in this continuation. See comment on `executionTask`.
126127 private let executionTaskCreatedContinuation : AsyncStream < Task < ExecutionTaskFinishStatus , Never > > . Continuation
127128
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-
132129 nonisolated ( unsafe) private var _priority : AtomicUInt8
133130
134131 /// The latest known priority of the task.
@@ -189,16 +186,10 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
189186 description: TaskDescription ,
190187 executionStateChangedCallback: ( @Sendable ( QueuedTask, TaskExecutionState) async -> Void ) ?
191188 ) async {
192- self . _priority = . init ( initialValue: priority? . rawValue ?? Task . currentPriority. rawValue)
189+ self . _priority = AtomicUInt8 ( initialValue: priority? . rawValue ?? Task . currentPriority. rawValue)
193190 self . description = description
194191 self . executionStateChangedCallback = executionStateChangedCallback
195192
196- var updatePriorityContinuation : AsyncStream < Void > . Continuation !
197- let updatePriorityStream = AsyncStream {
198- updatePriorityContinuation = $0
199- }
200- self . updatePriorityContinuation = updatePriorityContinuation
201-
202193 var executionTaskCreatedContinuation : AsyncStream < Task < ExecutionTaskFinishStatus , Never > > . Continuation !
203194 let executionTaskCreatedStream = AsyncStream {
204195 executionTaskCreatedContinuation = $0
@@ -207,38 +198,24 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
207198
208199 self . resultTask = Task . detached ( priority: priority) {
209200 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
221210 }
222211 }
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+ )
241217 }
218+ self . priority = Task . currentPriority
242219 }
243220 } onCancel: {
244221 self . resultTaskCancelled. value = true
@@ -291,16 +268,6 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
291268 self . executionTask = nil
292269 }
293270
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-
304271 /// If the priority of this task is less than `targetPriority`, elevate the priority to `targetPriority` by spawning
305272 /// a new task that depends on it. Otherwise a no-op.
306273 nonisolated func elevatePriority( to targetPriority: TaskPriority ) {
@@ -399,16 +366,6 @@ public actor TaskScheduler<TaskDescription: TaskDescriptionProtocol> {
399366 return queuedTask
400367 }
401368
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-
412369 /// Returns the maximum number of concurrent tasks that are allowed to execute at the given priority.
413370 private func maxConcurrentTasks( at priority: TaskPriority ) -> Int {
414371 for (atPriority, maxConcurrentTasks) in maxConcurrentTasksByPriority {
@@ -431,9 +388,8 @@ public actor TaskScheduler<TaskDescription: TaskDescriptionProtocol> {
431388 {
432389 // We don't have any execution slots left. Thus, this poker has nothing to do and is done.
433390 // 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.
437393 return
438394 }
439395 let dependencies = task. description. dependencies ( to: currentlyExecutingTasks. map ( \. description) )
@@ -535,7 +491,6 @@ public actor TaskScheduler<TaskDescription: TaskDescriptionProtocol> {
535491 case . terminated: break
536492 case . cancelledToBeRescheduled: pendingTasks. append ( task)
537493 }
538- await self . triggerPriorityUpdateOfQueuedTasks ( )
539494 self . poke ( )
540495 }
541496}
0 commit comments