Skip to content

Don’t start executing a task when cancelToBeRescheduled is called before execute #1370

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions Sources/SKCore/TaskScheduler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
/// Whether `cancelToBeRescheduled` has been called on this `QueuedTask`.
///
/// Gets reset every time `executionTask` finishes.
nonisolated(unsafe) private var cancelledToBeRescheduled: AtomicBool = .init(initialValue: false)
private var cancelledToBeRescheduled: Bool = false

/// Whether `resultTask` has been cancelled.
private nonisolated(unsafe) var resultTaskCancelled: AtomicBool = .init(initialValue: false)
Expand Down Expand Up @@ -228,6 +228,15 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
/// Execution might be canceled to be rescheduled, in which case this returns `.cancelledToBeRescheduled`. In that
/// case the `TaskScheduler` is expected to call `execute` again.
func execute() async -> ExecutionTaskFinishStatus {
if cancelledToBeRescheduled {
// `QueuedTask.execute` is called from a detached task in `TaskScheduler.poke` but we insert it into the
// `currentlyExecutingTasks` queue beforehand. This leaves a short windows in which we could cancel the task to
// reschedule it before it actually starts executing.
// If this happens, we don't have to do anything in `execute` and can immediately return. `execute` will be called
// again when the task gets rescheduled.
cancelledToBeRescheduled = false
return .cancelledToBeRescheduled
}
precondition(executionTask == nil, "Task started twice")
let task = Task.detached(priority: self.priority) {
if !Task.isCancelled && !self.resultTaskCancelled.value {
Expand All @@ -246,9 +255,9 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
private func finalizeExecution() async -> ExecutionTaskFinishStatus {
self.executionTask = nil
_isExecuting.value = false
if Task.isCancelled && self.cancelledToBeRescheduled.value {
if Task.isCancelled && self.cancelledToBeRescheduled {
await executionStateChangedCallback?(self, .cancelledToBeRescheduled)
self.cancelledToBeRescheduled.value = false
self.cancelledToBeRescheduled = false
return ExecutionTaskFinishStatus.cancelledToBeRescheduled
} else {
await executionStateChangedCallback?(self, .finished)
Expand All @@ -260,10 +269,10 @@ public actor QueuedTask<TaskDescription: TaskDescriptionProtocol> {
///
/// If the task has not been started yet or has already finished execution, this is a no-op.
func cancelToBeRescheduled() {
self.cancelledToBeRescheduled = true
guard let executionTask else {
return
}
self.cancelledToBeRescheduled.value = true
executionTask.cancel()
self.executionTask = nil
}
Expand Down