Skip to content

Commit bb90140

Browse files
committed
Offer LocalTestingDistributedActorSystem
Add local-only actor system `LocalTestingDistributedActorSystem` to make code and tutorial development easier. rdar://89580224
1 parent fa9eb49 commit bb90140

File tree

3 files changed

+163
-3
lines changed

3 files changed

+163
-3
lines changed

stdlib/public/Distributed/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#
33
# This source file is part of the Swift.org open source project
44
#
5-
# Copyright (c) 2019 - 2020 Apple Inc. and the Swift project authors
5+
# Copyright (c) 2019 - 2022 Apple Inc. and the Swift project authors
66
# Licensed under Apache License v2.0 with Runtime Library Exception
77
#
88
# See https://swift.org/LICENSE.txt for license information
@@ -19,6 +19,7 @@ add_swift_target_library(swift_Distributed ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I
1919
DistributedActor.swift
2020
DistributedActorSystem.swift
2121
DistributedMetadata.swift
22+
LocalTestingDistributedActorSystem.swift
2223

2324
SWIFT_MODULE_DEPENDS_LINUX Glibc
2425
SWIFT_MODULE_DEPENDS_FREEBSD Glibc

stdlib/public/Distributed/DistributedActorSystem.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public protocol DistributedActorSystem: Sendable {
7878
///
7979
/// The `actor.id` of the passed actor must be an `ActorID` that this system previously has assigned.
8080
///
81-
/// If the `actorReady` gets called with some unknown ID, it should crash immediately as it signifies some
81+
/// If `actorReady` gets called with some unknown ID, it should crash immediately as it signifies some
8282
/// very unexpected use of the system.
8383
///
8484
/// - Parameter actor: reference to the (local) actor that was just fully initialized.
@@ -93,7 +93,7 @@ public protocol DistributedActorSystem: Sendable {
9393
/// and not re-cycled by the system), i.e. if it is called during a failure to initialize completely,
9494
/// the call from the actor's deinitalizer will not happen (as under these circumstances, `deinit` will be run).
9595
///
96-
/// If the `actorReady` gets called with some unknown ID, it should crash immediately as it signifies some
96+
/// If `resignID` gets called with some unknown ID, it should crash immediately as it signifies some
9797
/// very unexpected use of the system.
9898
///
9999
/// - Parameter id: the id of an actor managed by this system that has begun its `deinit`.
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
// TODO(distributed): not thread safe...
14+
public final class LocalTestingDistributedActorSystem: DistributedActorSystem, @unchecked Sendable {
15+
public typealias ActorID = ActorAddress
16+
public typealias InvocationEncoder = LocalInvocationEncoder
17+
public typealias InvocationDecoder = LocalInvocationDecoder
18+
public typealias SerializationRequirement = Codable
19+
20+
private var activeActors: [ActorID: any DistributedActor] = [:]
21+
22+
private var idProvider: ActorIDProvider = ActorIDProvider()
23+
private var assignedIDs: Set<ActorID> = []
24+
25+
public init() {}
26+
27+
public func resolve<Act>(id: ActorID, as actorType: Act.Type)
28+
throws -> Act? where Act: DistributedActor {
29+
guard let anyActor = self.activeActors[id] else {
30+
throw LocalTestingDistributedActorSystemError(message: "Unable to locate id '\(id)' locally")
31+
}
32+
guard let actor = anyActor as? Act else {
33+
throw LocalTestingDistributedActorSystemError(message: "Failed to resolve id '\(id)' as \(Act.Type)")
34+
}
35+
return actor
36+
}
37+
38+
public func assignID<Act>(_ actorType: Act.Type) -> ActorID
39+
where Act: DistributedActor {
40+
let id = ActorAddress(parse: "\(self.idProvider.next())")
41+
print("| assign id: \(id) for \(actorType)")
42+
self.assignedIDs.insert(id)
43+
return id
44+
}
45+
46+
public func actorReady<Act>(_ actor: Act)
47+
where Act: DistributedActor,
48+
Act.ID == ActorID {
49+
guard self.assignedIDs.contains(actor.id) else {
50+
fatalError("Attempted to mark an unknown actor '\(actor.id)' ready")
51+
}
52+
print("| actor ready: \(actor)")
53+
self.activeActors[actor.id] = actor
54+
}
55+
56+
public func resignID(_ id: ActorID) {
57+
guard self.assignedIDs.contains(actor.id) else {
58+
fatalError("Attempted to resign unknown id '\(id)'")
59+
}
60+
self.activeActors.removeValue(forKey: id)
61+
}
62+
63+
public func makeInvocationEncoder() -> InvocationEncoder {
64+
.init()
65+
}
66+
67+
public func remoteCall<Act, Err, Res>(
68+
on actor: Act,
69+
target: RemoteCallTarget,
70+
invocation: inout InvocationEncoder,
71+
throwing errorType: Err.Type,
72+
returning returnType: Res.Type
73+
) async throws -> Res
74+
where Act: DistributedActor,
75+
Act.ID == ActorID,
76+
Err: Error,
77+
Res: SerializationRequirement {
78+
fatalError("Attempted to make remote call on actor \(actor) in a local-only actor system")
79+
}
80+
81+
public func remoteCallVoid<Act, Err>(
82+
on actor: Act,
83+
target: RemoteCallTarget,
84+
invocation: inout InvocationEncoder,
85+
throwing errorType: Err.Type
86+
) async throws
87+
where Act: DistributedActor,
88+
Act.ID == ActorID,
89+
Err: Error {
90+
fatalError("Attempted to make remote call on actor \(actor) in a local-only actor system")
91+
}
92+
93+
// TODO(distributed): not thread safe...
94+
private struct ActorIDProvider {
95+
private var counter: Int = 0
96+
97+
init() {}
98+
99+
mutating func next() -> ActorAddress {
100+
self.counter += 1
101+
return ActorAddress(parse: "\(self.counter)")
102+
}
103+
}
104+
}
105+
106+
public struct LocalInvocationEncoder: DistributedTargetInvocationEncoder {
107+
public typealias SerializationRequirement = Codable
108+
109+
public mutating func recordGenericSubstitution<T>(_ type: T.Type) throws {
110+
fatalError("Attempted to call encoder method in a local-only actor system")
111+
}
112+
113+
public mutating func recordArgument<Argument: SerializationRequirement>(_ argument: Argument) throws {
114+
fatalError("Attempted to call encoder method in a local-only actor system")
115+
}
116+
117+
public mutating func recordErrorType<E: Error>(_ type: E.Type) throws {
118+
fatalError("Attempted to call encoder method in a local-only actor system")
119+
}
120+
121+
public mutating func recordReturnType<R: SerializationRequirement>(_ type: R.Type) throws {
122+
fatalError("Attempted to call encoder method in a local-only actor system")
123+
}
124+
125+
public mutating func doneRecording() throws {
126+
fatalError("Attempted to call encoder method in a local-only actor system")
127+
}
128+
}
129+
130+
public class LocalInvocationDecoder : DistributedTargetInvocationDecoder {
131+
public typealias SerializationRequirement = Codable
132+
133+
public func decodeGenericSubstitutions() throws -> [Any.Type] {
134+
fatalError("Attempted to call decoder method in a local-only actor system")
135+
}
136+
137+
public func decodeNextArgument<Argument: SerializationRequirement>() throws -> Argument {
138+
fatalError("Attempted to call decoder method in a local-only actor system")
139+
}
140+
141+
public func decodeErrorType() throws -> Any.Type? {
142+
fatalError("Attempted to call decoder method in a local-only actor system")
143+
}
144+
145+
public func decodeReturnType() throws -> Any.Type? {
146+
fatalError("Attempted to call decoder method in a local-only actor system")
147+
}
148+
}
149+
150+
// === errors ----------------------------------------------------------------
151+
152+
@available(SwiftStdlib 5.7, *)
153+
public struct LocalTestingDistributedActorSystemError: DistributedActorSystemError {
154+
public let message: String
155+
156+
public init(message: String) {
157+
self.message = message
158+
}
159+
}

0 commit comments

Comments
 (0)