@@ -144,7 +144,7 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
144
144
/// Whether `cancelToBeRescheduled` has been called on this `QueuedTask`.
145
145
///
146
146
/// Gets reset every time `executionTask` finishes.
147
- nonisolated ( unsafe ) private var cancelledToBeRescheduled : AtomicBool = . init ( initialValue : false )
147
+ private var cancelledToBeRescheduled : Bool = false
148
148
149
149
/// Whether `resultTask` has been cancelled.
150
150
private nonisolated ( unsafe) var resultTaskCancelled: AtomicBool = . init( initialValue: false )
@@ -228,6 +228,15 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
228
228
/// Execution might be canceled to be rescheduled, in which case this returns `.cancelledToBeRescheduled`. In that
229
229
/// case the `TaskScheduler` is expected to call `execute` again.
230
230
func execute( ) async -> ExecutionTaskFinishStatus {
231
+ if cancelledToBeRescheduled {
232
+ // `QueuedTask.execute` is called from a detached task in `TaskScheduler.poke` but we insert it into the
233
+ // `currentlyExecutingTasks` queue beforehand. This leaves a short windows in which we could cancel the task to
234
+ // reschedule it before it actually starts executing.
235
+ // If this happens, we don't have to do anything in `execute` and can immediately return. `execute` will be called
236
+ // again when the task gets rescheduled.
237
+ cancelledToBeRescheduled = false
238
+ return . cancelledToBeRescheduled
239
+ }
231
240
precondition ( executionTask == nil , " Task started twice " )
232
241
let task = Task . detached ( priority: self . priority) {
233
242
if !Task. isCancelled && !self . resultTaskCancelled. value {
@@ -246,9 +255,9 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
246
255
private func finalizeExecution( ) async -> ExecutionTaskFinishStatus {
247
256
self . executionTask = nil
248
257
_isExecuting. value = false
249
- if Task . isCancelled && self . cancelledToBeRescheduled. value {
258
+ if Task . isCancelled && self . cancelledToBeRescheduled {
250
259
await executionStateChangedCallback ? ( self , . cancelledToBeRescheduled)
251
- self . cancelledToBeRescheduled. value = false
260
+ self . cancelledToBeRescheduled = false
252
261
return ExecutionTaskFinishStatus . cancelledToBeRescheduled
253
262
} else {
254
263
await executionStateChangedCallback ? ( self , . finished)
@@ -260,10 +269,10 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
260
269
///
261
270
/// If the task has not been started yet or has already finished execution, this is a no-op.
262
271
func cancelToBeRescheduled( ) {
272
+ self . cancelledToBeRescheduled = true
263
273
guard let executionTask else {
264
274
return
265
275
}
266
- self . cancelledToBeRescheduled. value = true
267
276
executionTask. cancel ( )
268
277
self . executionTask = nil
269
278
}
0 commit comments