I have created a global function, passing the isolation as parameter with #isolation
as default.
Inside this global function withTaskCancellationHandler
is called, expecting it to run operation
on the same actor, because it itself has a isolation
param with #isolation
as default value.
Now I run the global function from a MainActor
isolation context, but the operation
closure does not execute on the MainActor .
The onCancel
closure works as expected, the operation
not.
My guess is that because the operation
closure is async it hops off the MainActor to the global concurrent actor. Or it simply doesn't respect the global function's correct #isolation
value.
Here is a relatively simple example with comments to understand what's going on (tested on Swift 6, Xcode 16.3) - run it inside an Xcode command line tool or Swift executable:
import Foundation
// isolation param necessary, otherwise first precondion would fail, too
func myGlobalMethod(isolation: isolated (any Actor)? = #isolation) async throws {
MainActor.preconditionIsolated() // doesn't abort
// if in a global function it aborts
await withTaskCancellationHandler(
operation: { MainActor.preconditionIsolated() }, // <-- this aborts --
onCancel: { MainActor.preconditionIsolated() } // this doesn't abort
)
}
@MainActor
class MyClass {
func myMethod() async throws {
try await myGlobalMethod()
// this instead would work just fine (maybe the actor
// await withTaskCancellationHandler(
// operation: { MainActor.preconditionIsolated() },
// onCancel: { }
// )
}
}
let task = Task.detached {
try await MyClass().myMethod()
}
// this is to test of `onCancel` has the same isolation issues
let cancelTask = Task.detached {
task.cancel() // canceled without isolation, so global concurrent actor
}
try await task.value
await cancelTask.value // makes sure cancellation gets executed