Files
swift-mirror/test/Concurrency/Runtime/actor_isIsolatingCurrentContext.swift
Konrad 'ktoso' Malawski e1dc854f8f [Concurrency] Change isIsolatingCurrent... to return Bool?
This changes the isIsolatingCurrentContext function to return `Bool?`
and removes all the witness table trickery we did previously to detect
if it was implemented or not. This comes at a cost of trying to invoke
it always, before `checkIsolated`, but it makes for an simpler
implementation and more checkable even by third party Swift code which
may want to ask this question.

Along with the `withSerialExecutor` function, this now enables us to
check the isolation at runtime when we have an `any Actor` e.g. from
`#isolation`.

Updates SE-0471 according to
https://forums.swift.org/t/se-0471-improved-custom-serialexecutor-isolation-checking-for-concurrency-runtime/78834/
review discussions
2025-04-29 20:08:55 +09:00

131 lines
3.4 KiB
Swift

// RUN: %empty-directory(%t)
// RUN: %target-build-swift %import-libdispatch -Xfrontend -disable-availability-checking -parse-as-library %s -o %t/a.out
// RUN: %target-codesign %t/a.out
// RUN: %target-run %t/a.out | %FileCheck %s
// REQUIRES: executable_test
// REQUIRES: concurrency
// REQUIRES: concurrency_runtime
// REQUIRES: libdispatch
// UNSUPPORTED: back_deployment_runtime
// UNSUPPORTED: back_deploy_concurrency
// UNSUPPORTED: use_os_stdlib
// UNSUPPORTED: freestanding
@available(SwiftStdlib 6.2, *)
final class IsIsolatingExecutor: SerialExecutor {
init() {}
func enqueue(_ job: consuming ExecutorJob) {
job.runSynchronously(on: self.asUnownedSerialExecutor())
}
func asUnownedSerialExecutor() -> UnownedSerialExecutor {
UnownedSerialExecutor(ordinary: self)
}
func checkIsolated() {
print("called: checkIsolated")
}
func isIsolatingCurrentContext() -> Bool? {
print("called: isIsolatingCurrentContext")
return true
}
}
@available(SwiftStdlib 6.2, *)
final class UnknownIfIsIsolatingExecutor: SerialExecutor {
init() {}
func enqueue(_ job: consuming ExecutorJob) {
job.runSynchronously(on: self.asUnownedSerialExecutor())
}
func asUnownedSerialExecutor() -> UnownedSerialExecutor {
UnownedSerialExecutor(ordinary: self)
}
func checkIsolated() {
print("called: checkIsolated")
}
func isIsolatingCurrentContext() -> Bool? {
print("called: isIsolatingCurrentContext; return nil")
return nil
}
}
@available(SwiftStdlib 6.2, *)
final class NoChecksImplementedExecutor: SerialExecutor {
init() {}
func enqueue(_ job: consuming ExecutorJob) {
job.runSynchronously(on: self.asUnownedSerialExecutor())
}
func asUnownedSerialExecutor() -> UnownedSerialExecutor {
UnownedSerialExecutor(ordinary: self)
}
}
@available(SwiftStdlib 6.2, *)
final class JustCheckIsolatedExecutor: SerialExecutor {
init() {}
func enqueue(_ job: consuming ExecutorJob) {
job.runSynchronously(on: self.asUnownedSerialExecutor())
}
func asUnownedSerialExecutor() -> UnownedSerialExecutor {
UnownedSerialExecutor(ordinary: self)
}
func checkIsolated() {
print("called: checkIsolated")
}
}
@available(SwiftStdlib 6.2, *)
actor ActorOnIsCheckImplementingExecutor<Ex: SerialExecutor> {
let executor: Ex
init(on executor: Ex) {
self.executor = executor
}
nonisolated var unownedExecutor: UnownedSerialExecutor {
self.executor.asUnownedSerialExecutor()
}
func checkIsIsolatingCurrentContext() async -> Bool? {
executor.isIsolatingCurrentContext()
}
}
@main struct Main {
static func main() async {
let hasIsIsolatingCurrentContextExecutor = IsIsolatingExecutor()
let hasIsCheckActor = ActorOnIsCheckImplementingExecutor(on: hasIsIsolatingCurrentContextExecutor)
let unknownIsIsolatingActor = ActorOnIsCheckImplementingExecutor(on: UnknownIfIsIsolatingExecutor())
let anyActor: any Actor = hasIsCheckActor
anyActor.withSerialExecutor { se in
let outside = se.isIsolatingCurrentContext()
assert(outside == true) // This is just a mock executor impl that always returns "true" (it is lying)
// CHECK: called: isIsolatingCurrentContext
}
let inside = await hasIsCheckActor.checkIsIsolatingCurrentContext()
assert(inside == true)
// CHECK: called: isIsolatingCurrentContext
_ = unknownIsIsolatingActor.preconditionIsolated()
// CHECK: called: isIsolatingCurrentContext; return nil
// CHECK: called: checkIsolated
}
}