Files
swift-mirror/test/Concurrency/Runtime/runOnMainActor.swift
Michael Gottesman b1750bad1a [concurrency] Implement swift_task_runOnMainActor.
This routine takes a synchronous non-throwing main actor isolated closure
without a result. If we are dynamically on the main actor, we just run the
closure synchronously. Otherwise, we run a new task on the main actor and call
the closure on that.

This builds on top of the previous commit by using
swift_task_isCurrentExecutorWithFlags in the implementation of this function.

To backwards deploy this function on Darwin, I used some tricks from libdispatch
to validate that we are on the main queue.
2024-10-17 11:17:38 -07:00

159 lines
4.4 KiB
Swift

// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -Xfrontend -disable-dynamic-actor-isolation -swift-version 6 -g -import-objc-header %S/Inputs/RunOnMainActor.h %import-libdispatch )
// REQUIRES: executable_test
// REQUIRES: concurrency
// REQUIRES: concurrency_runtime
// REQUIRES: libdispatch
// REQUIRES: asserts
// UNSUPPORTED: freestanding
// For now we do not support back deployment or use os stdlib
// UNSUPPORTED: back_deployment_concurrency
// UNSUPPORTED: use_os_stdlib
import StdlibUnittest
import Dispatch
// MARK: Test runOnMainActor in all modes. We use the one future proof API in
// dispatch: dispatch_assert_queue.
////////////////////////
// MARK: Declarations //
////////////////////////
@_silgen_name("dispatch_assert_queue")
func dispatch_assertQueue(_ ptr: UnsafeRawPointer)
func checkIfOnMainQueue() {
dispatch_assertQueue(getDispatchMain())
}
actor Custom {
}
@globalActor
struct CustomActor {
static var shared: Custom {
return Custom()
}
}
/////////////////
// MARK: Tests //
/////////////////
let tests = TestSuite("RunOnMainActor")
tests.test("checkIfOnMainQueue does not crash on the main queue") { @MainActor () -> () in
// Why do we crash if this is synchronous.
expectCrashLater()
checkIfOnMainQueue()
}
tests.test("checkIfOnMainQueue does not crash on the main queue") { @MainActor () async -> () in
checkIfOnMainQueue()
}
tests.test("checkIfOnMainQueue crashes off the main queue") {
expectCrashLater()
await { @CustomActor in
print("=> checkIfOnMainQueue crashes off the main queue")
checkIfOnMainQueue()
}()
}
tests.test("checkIfOnMainQueue crashes off the main queue 2") { @CustomActor () async -> () in
expectCrashLater()
print("=> checkIfOnMainQueue crashes off the main queue 2")
checkIfOnMainQueue()
}
tests.test("checkIfOnMainQueue crashes using pure dispatch queue") { @CustomActor () async -> () in
expectCrashLater()
let queue = DispatchQueue(label: "")
await withCheckedContinuation { cont in
queue.async {
checkIfOnMainQueue()
cont.resume()
}
}
}
tests.test("RunOnMainActor on MainActor") { @MainActor () async -> () in
// This is main actor isolated.
_taskRunOnMainActor { @MainActor in
print("=> RunOnMainActor on MainActor")
checkIfOnMainQueue()
}
}
tests.test("RunOnMainActor off MainActor") {
await { @CustomActor in
_taskRunOnMainActor { @MainActor in
print("=> RunOnMainActor off MainActor")
checkIfOnMainQueue()
}
}()
}
tests.test("RunOnMainActor off MainActor 2") { @CustomActor () async -> () in
_taskRunOnMainActor {
print("=> RunOnMainActor off MainActor 2")
checkIfOnMainQueue()
}
}
// This succeeds since assert understands that a child queue runs on the main
// queue.
tests.test("checkIfOnMainQueue in SubQueue from MainActor") { @CustomActor () async -> () in
let childQueue = DispatchQueue.init(label: "", qos: .background, attributes: [], autoreleaseFrequency: .inherit, target: DispatchQueue.main)
print("=> checkIfOnMainQueue in SubQueue from MainActor start!")
// We cannot use the checked continuation, since we are not going to come back
// to the custom actor.
await withCheckedContinuation { cont in
childQueue.async {
print("=> checkIfOnMainQueue in SubQueue from MainActor")
checkIfOnMainQueue()
cont.resume()
}
}
}
// This should not fail when backwards deployed since we use the backwards
// compatibility trick.
tests.test("taskRunOnMainActor in SubQueue from MainActor") { @CustomActor () async -> () in
let childQueue = DispatchQueue.init(label: "", qos: .background, attributes: [], autoreleaseFrequency: .inherit, target: DispatchQueue.main)
await withCheckedContinuation { cont in
childQueue.async {
_taskRunOnMainActor { @MainActor () -> () in
print("=> taskRunOnMainActor in SubQueue from MainActor")
checkIfOnMainQueue()
cont.resume()
}
}
}
}
tests.test("taskRunOnMainActor in SubQueue off MainActor") { @CustomActor () async -> () in
let d = DispatchQueue.init(label: "", qos: .background)
let childQueue = DispatchQueue.init(label: "", qos: .background, attributes: [], autoreleaseFrequency: .inherit, target: d)
await withCheckedContinuation { cont in
childQueue.async {
_taskRunOnMainActor { @MainActor in
print("=> taskRunOnMainActor in SubQueue from MainActor")
checkIfOnMainQueue()
cont.resume()
}
}
}
}
await runAllTestsAsync()