Skip to content

Commit 0ee2405

Browse files
authored
Merge pull request #1370 from ahoppen/cancel-task-before-execution-started
Don’t start executing a task when cancelToBeRescheduled is called before `execute`
2 parents 44c60a2 + cc1280f commit 0ee2405

File tree

1 file changed

+13
-4
lines changed

1 file changed

+13
-4
lines changed

Sources/SKCore/TaskScheduler.swift

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
144144
/// Whether `cancelToBeRescheduled` has been called on this `QueuedTask`.
145145
///
146146
/// Gets reset every time `executionTask` finishes.
147-
nonisolated(unsafe) private var cancelledToBeRescheduled: AtomicBool = .init(initialValue: false)
147+
private var cancelledToBeRescheduled: Bool = false
148148

149149
/// Whether `resultTask` has been cancelled.
150150
private nonisolated(unsafe) var resultTaskCancelled: AtomicBool = .init(initialValue: false)
@@ -228,6 +228,15 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
228228
/// Execution might be canceled to be rescheduled, in which case this returns `.cancelledToBeRescheduled`. In that
229229
/// case the `TaskScheduler` is expected to call `execute` again.
230230
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+
}
231240
precondition(executionTask == nil, "Task started twice")
232241
let task = Task.detached(priority: self.priority) {
233242
if !Task.isCancelled && !self.resultTaskCancelled.value {
@@ -246,9 +255,9 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
246255
private func finalizeExecution() async -> ExecutionTaskFinishStatus {
247256
self.executionTask = nil
248257
_isExecuting.value = false
249-
if Task.isCancelled && self.cancelledToBeRescheduled.value {
258+
if Task.isCancelled && self.cancelledToBeRescheduled {
250259
await executionStateChangedCallback?(self, .cancelledToBeRescheduled)
251-
self.cancelledToBeRescheduled.value = false
260+
self.cancelledToBeRescheduled = false
252261
return ExecutionTaskFinishStatus.cancelledToBeRescheduled
253262
} else {
254263
await executionStateChangedCallback?(self, .finished)
@@ -260,10 +269,10 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
260269
///
261270
/// If the task has not been started yet or has already finished execution, this is a no-op.
262271
func cancelToBeRescheduled() {
272+
self.cancelledToBeRescheduled = true
263273
guard let executionTask else {
264274
return
265275
}
266-
self.cancelledToBeRescheduled.value = true
267276
executionTask.cancel()
268277
self.executionTask = nil
269278
}

0 commit comments

Comments
 (0)