Files
swift-mirror/test/Distributed/Runtime/distributed_actor_remote_functions.swift
Doug Gregor c2ba06c3d2 Make the actor transport an associated type of the DistributedActor protocol.
Eliminate the required use of existentials in distributed actors by
introducing the `Transport` associated type into the
`DistributedActor` protocol. Each distributed actor has a known
(concrete) actor transport type, reducing storage requirements and
transport dynamism when it isn't needed.

Distributed actors can manually specify their `Transport` associated
type or pick up a default by looking for a type named
`DefaultActorTransport`. A library that vends an actor transport can
make create a public typealias `DefaultActorTransport` referring to
its transport, so importing that library and defining a distributed
actor will use that library's transport.

Introduce a type-erased `AnyActorTransport` type to provide an
explicitly dynamic actor transport. This is still an important option,
e.g., for cases where one wants to be able to dynamically change the
transport for testing or different kinds of deployment. For now, we
default to this transport in the library (via `DefaultActorTransport`),
but we may very well want to eliminate this because it will be
ambiguous with client libraries that vend their own
`DefaultActorTransport`.
2021-11-01 22:37:33 -07:00

256 lines
7.6 KiB
Swift

// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -Xfrontend -enable-experimental-distributed -parse-as-library) | %FileCheck %s --dump-input=always
// REQUIRES: executable_test
// REQUIRES: concurrency
// REQUIRES: distributed
// rdar://76038845
// UNSUPPORTED: use_os_stdlib
// UNSUPPORTED: back_deployment_runtime
// rdar://77798215
// UNSUPPORTED: OS=windows-msvc
import _Distributed
import _Concurrency
struct Boom: Error {
let whoFailed: String
init(_ whoFailed: String) {
self.whoFailed = whoFailed
}
}
@available(SwiftStdlib 5.6, *)
distributed actor SomeSpecificDistributedActor {
let state: String = "hi there"
distributed func helloAsyncThrows() async throws -> String {
"local(\(#function))"
}
distributed func helloAsync() async -> String {
"local(\(#function))"
}
distributed func helloThrows() throws -> String {
"local(\(#function))"
}
distributed func hello() -> String {
"local(\(#function))"
}
distributed func callTaskSelf_inner() async throws -> String {
"local(\(#function))"
}
distributed func callTaskSelf() async -> String {
do {
return try await Task {
let called = try await callTaskSelf_inner() // shouldn't use the distributed thunk!
return "local(\(#function)) -> \(called)"
}.value
} catch {
return "WRONG local(\(#function)) thrown(\(error))"
}
}
distributed func callDetachedSelf() async -> String {
do {
return try await Task.detached {
let called = try await self.callTaskSelf_inner() // shouldn't use the distributed thunk!
return "local(\(#function)) -> \(called)"
}.value
} catch {
return "WRONG local(\(#function)) thrown(\(error))"
}
}
// === errors
distributed func helloThrowsImplBoom() throws -> String {
throw Boom("impl")
}
distributed func helloThrowsTransportBoom() throws -> String {
"local(\(#function))"
}
}
@available(SwiftStdlib 5.6, *)
extension SomeSpecificDistributedActor {
@_dynamicReplacement(for:_remote_helloAsyncThrows())
nonisolated func _remote_impl_helloAsyncThrows() async throws -> String {
"remote(\(#function))"
}
@_dynamicReplacement(for:_remote_helloAsync())
nonisolated func _remote_impl_helloAsync() async throws -> String {
"remote(\(#function))"
}
@_dynamicReplacement(for:_remote_helloThrows())
nonisolated func _remote_impl_helloThrows() async throws -> String {
"remote(\(#function))"
}
@_dynamicReplacement(for:_remote_hello())
nonisolated func _remote_impl_hello() async throws -> String {
"remote(\(#function))"
}
@_dynamicReplacement(for:_remote_callTaskSelf())
nonisolated func _remote_impl_callTaskSelf() async throws -> String {
"remote(\(#function))"
}
@_dynamicReplacement(for:_remote_callDetachedSelf())
nonisolated func _remote_impl_callDetachedSelf() async throws -> String {
"remote(\(#function))"
}
@_dynamicReplacement(for:_remote_callTaskSelf_inner())
nonisolated func _remote_impl_callTaskSelf_inner() async throws -> String {
"remote(\(#function))"
}
// === errors
@_dynamicReplacement(for:_remote_helloThrowsImplBoom())
nonisolated func _remote_impl_helloThrowsImplBoom() async throws -> String {
"remote(\(#function))"
}
@_dynamicReplacement(for:_remote_helloThrowsTransportBoom())
nonisolated func _remote_impl_helloThrowsTransportBoom() async throws -> String {
throw Boom("transport")
}
}
// ==== Execute ----------------------------------------------------------------
@_silgen_name("swift_distributed_actor_is_remote")
func __isRemoteActor(_ actor: AnyObject) -> Bool
func __isLocalActor(_ actor: AnyObject) -> Bool {
return !__isRemoteActor(actor)
}
// ==== Fake Transport ---------------------------------------------------------
@available(SwiftStdlib 5.6, *)
struct ActorAddress: ActorIdentity {
let address: String
}
@available(SwiftStdlib 5.6, *)
struct FakeTransport: ActorTransport {
func decodeIdentity(from decoder: Decoder) throws -> AnyActorIdentity {
fatalError("not implemented:\(#function)")
}
func resolve<Act>(_ identity: AnyActorIdentity, as actorType: Act.Type) throws -> Act?
where Act: DistributedActor {
nil
}
func assignIdentity<Act>(_ actorType: Act.Type) -> AnyActorIdentity
where Act: DistributedActor {
.init(ActorAddress(address: ""))
}
func actorReady<Act>(_ actor: Act) where Act: DistributedActor {
}
func resignIdentity(_ id: AnyActorIdentity) {
}
}
@available(SwiftStdlib 5.5, *)
typealias DefaultActorTransport = FakeTransport
// ==== Execute ----------------------------------------------------------------
@available(SwiftStdlib 5.6, *)
func test_remote_invoke(address: ActorAddress, transport: FakeTransport) async {
func check(actor: SomeSpecificDistributedActor) async {
let personality = __isRemoteActor(actor) ? "remote" : "local"
let h1 = try! await actor.helloAsyncThrows()
print("\(personality) - helloAsyncThrows: \(h1)")
let h2 = try! await actor.helloAsync()
print("\(personality) - helloAsync: \(h2)")
let h3 = try! await actor.helloThrows()
print("\(personality) - helloThrows: \(h3)")
let h4 = try! await actor.hello()
print("\(personality) - hello: \(h4)")
let h5 = try! await actor.callTaskSelf()
print("\(personality) - callTaskSelf: \(h5)")
let h6 = try! await actor.callDetachedSelf()
print("\(personality) - callDetachedSelf: \(h6)")
// error throws
if __isRemoteActor(actor) {
do {
_ = try await actor.helloThrowsTransportBoom()
print("WRONG: helloThrowsTransportBoom: should have thrown")
} catch {
print("\(personality) - helloThrowsTransportBoom: \(error)")
}
} else {
do {
_ = try await actor.helloThrowsImplBoom()
print("WRONG: helloThrowsImplBoom: Should have thrown")
} catch {
print("\(personality) - helloThrowsImplBoom: \(error)")
}
}
}
let remote = try! SomeSpecificDistributedActor.resolve(.init(address), using: transport)
assert(__isRemoteActor(remote) == true, "should be remote")
let local = SomeSpecificDistributedActor(transport: transport)
assert(__isRemoteActor(local) == false, "should be local")
print("local isRemote: \(__isRemoteActor(local))")
// CHECK: local isRemote: false
await check(actor: local)
// CHECK: local - helloAsyncThrows: local(helloAsyncThrows())
// CHECK: local - helloAsync: local(helloAsync())
// CHECK: local - helloThrows: local(helloThrows())
// CHECK: local - hello: local(hello())
// CHECK: local - callTaskSelf: local(callTaskSelf()) -> local(callTaskSelf_inner())
// CHECK: local - callDetachedSelf: local(callDetachedSelf()) -> local(callTaskSelf_inner())
// CHECK: local - helloThrowsImplBoom: Boom(whoFailed: "impl")
print("remote isRemote: \(__isRemoteActor(remote))")
// CHECK: remote isRemote: true
await check(actor: remote)
// CHECK: remote - helloAsyncThrows: remote(_remote_impl_helloAsyncThrows())
// CHECK: remote - helloAsync: remote(_remote_impl_helloAsync())
// CHECK: remote - helloThrows: remote(_remote_impl_helloThrows())
// CHECK: remote - hello: remote(_remote_impl_hello())
// CHECK: remote - callTaskSelf: remote(_remote_impl_callTaskSelf())
// CHECK: remote - callDetachedSelf: remote(_remote_impl_callDetachedSelf())
// CHECK: remote - helloThrowsTransportBoom: Boom(whoFailed: "transport")
print(local)
print(remote)
}
@available(SwiftStdlib 5.6, *)
@main struct Main {
static func main() async {
let address = ActorAddress(address: "")
let transport = FakeTransport()
await test_remote_invoke(address: address, transport: transport)
}
}