1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
|
// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking -parse-as-library) | %FileCheck %s
// REQUIRES: executable_test
// REQUIRES: concurrency
// REQUIRES: distributed
// UNSUPPORTED: use_os_stdlib
// UNSUPPORTED: back_deployment_runtime
// FIXME(distributed): Distributed actors currently have some issues on windows, isRemote always returns false. rdar://82593574
// UNSUPPORTED: OS=windows-msvc
import Distributed
actor A {}
distributed actor DA {
init(system: FakeActorSystem) {
self.actorSystem = system
}
}
distributed actor DA_userDefined {
init(system: FakeActorSystem) {
self.actorSystem = system
}
deinit {}
}
distributed actor DA_userDefined2 {
init(system: FakeActorSystem) {
self.actorSystem = system
}
deinit {
print("Deinitializing \(self.id) remote:\(__isRemoteActor(self))")
}
}
distributed actor DA_state {
var name = "Hello"
var age = 42
init(system: FakeActorSystem) {
self.actorSystem = system
}
deinit {
print("Deinitializing \(self.id) remote:\(__isRemoteActor(self))")
return
}
}
// ==== Fake Transport ---------------------------------------------------------
struct ActorAddress: Sendable, Hashable, Codable {
let address: String
init(parse address : String) {
self.address = address
}
}
final class FakeActorSystem: @unchecked Sendable, DistributedActorSystem {
typealias ActorID = ActorAddress
typealias SerializationRequirement = Codable
typealias InvocationDecoder = FakeDistributedInvocationEncoder
typealias InvocationEncoder = FakeDistributedInvocationEncoder
typealias ResultHandler = FakeResultHandler
var n = 0
func resolve<Act>(id: ActorID, as actorType: Act.Type) throws -> Act?
where Act: DistributedActor,
Act.ID == ActorID {
print("resolve type:\(actorType), address:\(id)")
return nil
}
func assignID<Act>(_ actorType: Act.Type) -> ActorID
where Act: DistributedActor,
Act.ID == ActorID {
n += 1
let address = ActorAddress(parse: "addr-\(n)")
print("assign type:\(actorType), address:\(address)")
return address
}
func actorReady<Act>(_ actor: Act)
where Act: DistributedActor,
Act.ID == ActorID {
print("ready actor:\(actor), address:\(actor.id)")
}
func resignID(_ id: ActorID) {
print("resign address:\(id)")
}
@inlinable func makeInvocationEncoder() -> InvocationEncoder {
.init()
}
func remoteCall<Act, Err, Res>(
on actor: Act,
target: RemoteCallTarget,
invocation invocationEncoder: inout InvocationEncoder,
throwing: Err.Type,
returning: Res.Type
) async throws -> Res
where Act: DistributedActor,
Act.ID == ActorID,
Err: Error,
Res: SerializationRequirement {
fatalError("not implemented: \(#function)")
}
func remoteCallVoid<Act, Err>(
on actor: Act,
target: RemoteCallTarget,
invocation invocationEncoder: inout InvocationEncoder,
throwing: Err.Type
) async throws
where Act: DistributedActor,
Act.ID == ActorID,
Err: Error {
fatalError("not implemented: \(#function)")
}
}
class FakeDistributedInvocationEncoder: DistributedTargetInvocationEncoder, DistributedTargetInvocationDecoder {
typealias SerializationRequirement = Codable
func recordGenericSubstitution<T>(_ type: T.Type) throws { }
func recordArgument<Value: SerializationRequirement>(_ argument: RemoteCallArgument<Value>) throws { }
func recordReturnType<R: SerializationRequirement>(_ type: R.Type) throws { }
func recordErrorType<E: Error>(_ type: E.Type) throws { }
func doneRecording() throws { }
// === Receiving / decoding -------------------------------------------------
func decodeGenericSubstitutions() throws -> [Any.Type] {
[]
}
func decodeNextArgument<Argument: SerializationRequirement>() throws -> Argument {
fatalError()
}
func decodeReturnType() throws -> Any.Type? {
nil
}
func decodeErrorType() throws -> Any.Type? {
nil
}
}
@available(SwiftStdlib 5.5, *)
public struct FakeResultHandler: DistributedTargetInvocationResultHandler {
public typealias SerializationRequirement = Codable
public func onReturn<Success: SerializationRequirement>(value: Success) async throws {
fatalError("Not implemented: \(#function)")
}
public func onReturnVoid() async throws {
fatalError("Not implemented: \(#function)")
}
public func onThrow<Err: Error>(error: Err) async throws {
fatalError("Not implemented: \(#function)")
}
}
typealias DefaultDistributedActorSystem = FakeActorSystem
// ==== Execute ----------------------------------------------------------------
func test() {
let system = DefaultDistributedActorSystem()
// no lifecycle things make sense for a normal actor, double check we didn't emit them
print("before A")
_ = A()
print("after A")
// CHECK: before A
// CHECK: after A
_ = { () -> DA in
DA(system: system)
}()
// CHECK: assign type:DA, address:[[ADDRESS:.*]]
// CHECK: ready actor:main.DA, address:ActorAddress(address: "[[ADDR1:addr-[0-9]]]")
// CHECK: resign address:ActorAddress(address: "[[ADDR1]]")
_ = { () -> DA_userDefined in
DA_userDefined(system: system)
}()
// CHECK: assign type:DA_userDefined, address:[[ADDRESS:.*]]
// CHECK: ready actor:main.DA_userDefined, address:ActorAddress(address: "[[ADDR2:addr-[0-9]]]")
// CHECK: resign address:ActorAddress(address: "[[ADDR2]]")
// resign must happen as the _last thing_ after user-deinit completed
_ = { () -> DA_userDefined2 in
DA_userDefined2(system: system)
}()
// CHECK: assign type:DA_userDefined2, address:[[ADDRESS:.*]]
// CHECK: ready actor:main.DA_userDefined2, address:ActorAddress(address: "[[ADDR3:addr-[0-9]]]")
// CHECK: Deinitializing ActorAddress(address: "[[ADDR3]]") remote:false
// CHECK-NEXT: resign address:ActorAddress(address: "[[ADDR3]]")
// resign must happen as the _last thing_ after user-deinit completed
_ = { () -> DA_state in
DA_state(system: system)
}()
// CHECK: assign type:DA_state, address:[[ADDRESS:.*]]
// CHECK: ready actor:main.DA_state, address:ActorAddress(address: "[[ADDR4:addr-[0-9]]]")
// CHECK: Deinitializing ActorAddress(address: "[[ADDR4]]") remote:false
// CHECK-NEXT: resign address:ActorAddress(address: "[[ADDR4]]")
// a remote actor should not resign it's address, it was never "assigned" it
let address = ActorAddress(parse: "remote-1")
_ = { () -> DA_userDefined2 in
try! DA_userDefined2.resolve(id: address, using: system)
}()
// CHECK-NEXT: resolve type:DA_userDefined2, address:ActorAddress(address: "[[ADDR5:remote-1]]")
// MUST NOT run deinit body for a remote distributed actor
// CHECK-NOT: Deinitializing ActorAddress(address: "remote-1") remote:true
print("DONE")
// CHECK-NEXT: DONE
}
@main struct Main {
static func main() async {
test()
}
}
|