mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
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.
159 lines
4.4 KiB
Swift
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()
|