@@ -17,29 +17,185 @@ import _Concurrency
17
17
18
18
/// Common protocol to which all distributed actors conform implicitly.
19
19
///
20
- /// It is not possible to conform to this protocol manually explicitly.
21
- /// Only a 'distributed actor' declaration or protocol with 'DistributedActor'
22
- /// requirement may conform to this protocol.
20
+ /// The `DistributedActor` protocol generalizes over all distributed actor types.
21
+ /// Distributed actor types implicitly conform to this protocol.
23
22
///
24
- /// The 'DistributedActor' protocol provides the core functionality of any
25
- /// distributed actor.
23
+ /// It is not possible to conform to this protocol by any other declaration other than a `distributed actor`.
26
24
///
27
- /// ## Implicit `Codable` conformance
28
- /// If created with an actor system whose `ActorID` is `Codable`, the
25
+ /// It is possible to require a type to conform to the
26
+ /// ``DistributedActor`` protocol by refining it with another protocol,
27
+ /// or by using a generic constraint.
28
+ ///
29
+ /// ## Synthesized properties
30
+ /// For every concrete distributed actor declaration, the compiler synthesizes two properties: `actorSystem` and `id`.
31
+ /// They witness the ``actorSystem`` and ``id`` protocol requirements of the ``DistributedActor`` protocol.
32
+ ///
33
+ /// It is not possible to implement these properties explicitly in user code.
34
+ /// These properties are `nonisolated` and accessible even if the instance is _remote_,
35
+ /// because _all_ distributed actor references must store the actor system remote calls
36
+ /// will be delivered through, as well as the id identifying the target of those calls.
37
+ ///
38
+ /// ## The ActorSystem associated type
39
+ /// Every distributed actor must declare what type of actor system
40
+ /// it is part of by implementing the ``ActorSystem`` associated type requirement.
41
+ ///
42
+ /// This causes a number of other properties of the actor to be inferred:
43
+ /// - the ``SerializationRequirement`` that will be used at compile time to
44
+ /// verify `distributed` target declarations are well formed,
45
+ /// - if the distributed actor is `Codable`, based on the ``ID`` being Codable or not,
46
+ /// - the type of the ``ActorSystem`` accepted in the synthesized default initializer.
47
+ ///
48
+ /// A distributed actor must declare what type of actor system it is ready to
49
+ /// work with by fulfilling the ``ActorSystem`` type member requirement:
50
+ ///
51
+ /// ```swift
52
+ /// distributed actor Greeter {
53
+ /// typealias ActorSystem = GreetingSystem // which conforms to DistributedActorSystem
54
+ ///
55
+ /// func greet() -> String { "Hello!" }
56
+ /// }
57
+ /// ```
58
+ ///
59
+ /// ### The DefaultDistributedActorSystem type alias
60
+ /// Since it is fairly common to only be using one specific type of actor system
61
+ /// within a module or entire codebase, it is possible to declare the default type
62
+ /// of actor system all distributed actors will be using in a module by declaring
63
+ /// a `DefaultDistributedActorSystem` module wide typealias:
64
+ ///
65
+ /// ```swift
66
+ /// import Distributed
67
+ /// import AmazingActorSystemLibrary
68
+ ///
69
+ /// typealias DefaultDistributedActorSystem = AmazingActorSystem
70
+ ///
71
+ /// distributed actor Greeter {} // ActorSystem == AmazingActorSystem
72
+ /// ```
73
+ ///
74
+ /// This declaration makes all `distributed actor` declarations
75
+ /// that do not explicitly specify an ``ActorSystem`` type alias to assume the
76
+ /// `AmazingActorSystem` as their `ActorSystem`.
77
+ ///
78
+ /// It is possible for a specific actor to override the system it is using,
79
+ /// by declaring an ``ActorSystem`` type alias as usual:
80
+ ///
81
+ /// ```swift
82
+ /// typealias DefaultDistributedActorSystem = AmazingActorSystem
83
+ ///
84
+ /// distributed actor Amazing {
85
+ /// // ActorSystem == AmazingActorSystem
86
+ /// }
87
+ ///
88
+ /// distributed actor Superb {
89
+ /// typealias ActorSystem = SuperbActorSystem
90
+ /// }
91
+ /// ```
92
+ ///
93
+ /// In general the `DefaultDistributedActorSystem` should not be declared public,
94
+ /// as picking the default should be left up to each specific module of a project.
95
+ ///
96
+ /// ## Default initializer
97
+ /// While classes and actors receive a synthesized *argument-free default
98
+ /// initializer* (`init()`), distributed actors synthesize a default initializer
99
+ /// that accepts a distributed actor system the actor is part of: `init(actorSystem:)`.
100
+ ///
101
+ /// The accepted actor system must be of the `Self.ActorSystem` type, which
102
+ /// must conform to the ``DistributedActorSystem`` protocol. This is required
103
+ /// because distributed actors are always managed by a concrete
104
+ /// distributed actor system and cannot exist on their own without one.
105
+ ///
106
+ /// It is possible to explicitly declare a parameter-free initializer (`init()`),
107
+ /// however the `actorSystem` property still must be assigned a concrete actor
108
+ /// system instance the actor shall be part of.
109
+ ///
110
+ /// In general it is recommended to always have an `actorSystem` parameter as
111
+ /// the last non-defaulted non-closure parameter in every actor's
112
+ /// initializer parameter list. This way it is simple to swap in a "test actor
113
+ /// system" instance in unit tests, and avoid relying on global state which could
114
+ /// make testing more difficult.
115
+ ///
116
+ /// ## Implicit properties
117
+ /// Every concrete `distributed actor` type receives two synthesized properties,
118
+ /// which implement the protocol requirements of this protocol: `id` and `actorSystem`.
119
+ ///
120
+ /// ### Property: Actor System
121
+ /// The ``actorSystem`` property is an important part of every distributed actor's lifecycle management.
122
+ ///
123
+ /// Both initialization as well as de-initialization require interactions with the actor system,
124
+ /// and it is the actor system that handles all remote interactions of an actor, by both sending
125
+ /// or receiving remote calls made on the actor.
126
+ ///
127
+ /// The ``actorSystem`` property must be assigned in every designated initializer
128
+ /// of a distributed actor explicitly. It is highly recommended to make it a
129
+ /// parameter of every distributed actor initializer, and simply forward the
130
+ /// value to the stored property, like this:
131
+ ///
132
+ /// ```swift
133
+ /// init(name: String, actorSystem: Self.ActorSystem) {
134
+ /// self.name = name
135
+ /// self.actorSystem = actorSystem
136
+ /// }
137
+ /// ```
138
+ ///
139
+ /// Forgetting to initialize the actor system, will result in a compile time error:
140
+ ///
141
+ /// ```swift
142
+ /// // BAD
143
+ /// init(name: String, actorSystem: Self.ActorSystem) {
144
+ /// self.name = name
145
+ /// // BAD, will cause compile-time error; the `actorSystem` was not initialized.
146
+ /// }
147
+ /// ```
148
+ ///
149
+ /// ### Property: Distributed Actor Identity
150
+ /// ``id`` is assigned by the actor system during the distributed actor's
151
+ /// initialization, and cannot be set or mutated by the actor itself.
152
+ ///
153
+ /// ``id`` is the effective identity of the actor, and is used in equality checks,
154
+ /// as well as the actor's synthesized ``Codable`` conformance if the ``ID`` type
155
+ /// conforms to ``Codable``.
156
+ ///
157
+ /// ## Automatic Conformances
158
+ ///
159
+ /// ### Hashable and Identifiable conformance
160
+ /// Every distributed actor conforms to the `Hashable` and `Identifiable` protocols.
161
+ /// Its identity is strictly driven by its ``id``, and therefore hash and equality
162
+ /// implementations directly delegate to the ``id`` property.
163
+ ///
164
+ /// Comparing a local distributed actor instance and a remote reference to it
165
+ /// (both using the same ``id``) always returns true, as they both conceptually
166
+ /// point at the same distributed actor.
167
+ ///
168
+ /// It is not possible to implement these protocols relying on the actual actor's
169
+ /// state, because it may be remote and the state may not be available. In other
170
+ /// words, since these protocols must be implemented using `nonisolated` functions,
171
+ /// only `nonisolated` `id` and `actorSystem` properties are accessible for their
172
+ /// implementations.
173
+ ///
174
+ /// ### Implicit Codable conformance
175
+ /// If created with an actor system whose ``DistributedActorSystem/ActorID`` is ``Codable``, the
29
176
/// compiler will synthesize code for the concrete distributed actor to conform
30
- /// to `Codable` as well. This is necessary to support distributed calls where
31
- /// the `SerializationRequirement` is `Codable` and thus users may want to pass
32
- /// actors as arguments to remote calls.
33
- ///
34
- /// The synthesized implementations use a single `SingleValueContainer` to
35
- /// encode/decode the `self.id` property of the actor. The `Decoder` required
36
- /// `init(from:)` is implemented by retrieving an actor system from the
37
- /// decoders' `userInfo`, effectively like this:
38
- /// `decoder.userInfo[.actorSystemKey] as? ActorSystem`. The obtained actor
39
- /// system is then used to `resolve(id:using:)` the decoded ID.
40
- ///
41
- /// Use the `CodingUserInfoKey.actorSystemKey` to provide the necessary
177
+ /// to ``Codable`` as well.
178
+ ///
179
+ /// This is necessary to support distributed calls where the `SerializationRequirement`
180
+ /// is ``Codable`` and thus users may want to pass actors as arguments to remote calls.
181
+ ///
182
+ /// The synthesized implementations use a single ``SingleValueEncodingContainer`` to
183
+ /// encode/decode the ``id`` property of the actor. The ``Decoder`` required
184
+ /// ``Decoder/init(from:)`` is implemented by retrieving an actor system from the
185
+ /// decoders' `userInfo`, effectively like as follows:
186
+ ///
187
+ /// ```swift
188
+ /// decoder.userInfo[.actorSystemKey] as? ActorSystem
189
+ // ```
190
+ ///
191
+ /// The such obtained actor system is then used to ``resolve(id:using:)`` the decoded ``ID``.
192
+ ///
193
+ /// Use the ``CodingUserInfoKey/actorSystemKey`` to provide the necessary
42
194
/// actor system for the decoding initializer when decoding a distributed actor.
195
+ ///
196
+ /// - SeeAlso: ``DistributedActorSystem``
197
+ /// - SeeAlso: ``Actor``
198
+ /// - SeeAlso: ``AnyActor``
43
199
@available ( SwiftStdlib 5 . 7 , * )
44
200
public protocol DistributedActor : AnyActor , Identifiable , Hashable
45
201
where ID == ActorSystem . ActorID ,
@@ -58,17 +214,32 @@ public protocol DistributedActor: AnyActor, Identifiable, Hashable
58
214
/// to return the same exact resolved actor instance, however all the references would
59
215
/// represent logically references to the same distributed actor, e.g. on a different node.
60
216
///
61
- /// Conformance to this requirement is synthesized automatically for any
62
- /// `distributed actor` declaration.
217
+ /// Depending on the capabilities of the actor system producing the identifiers,
218
+ /// the `ID` may also be used to store instance specific metadata.
219
+ ///
220
+ /// ## Synthesized property
221
+ ///
222
+ /// In concrete distributed actor declarations, a witness for this protocol requirement is synthesized by the compiler.
223
+ ///
224
+ /// It is not possible to assign a value to the `id` directly; instead, it is assigned during an actors `init` (or `resolve`),
225
+ /// by the managing actor system.
63
226
nonisolated override var id : ID { get }
64
227
65
- /// The `ActorSystem ` that is managing this distributed actor.
228
+ /// The ``DistributedActorSystem` ` that is managing this distributed actor.
66
229
///
67
- /// It is immutable and equal to the system passed in the local/resolve
68
- /// initializer .
230
+ /// It is immutable and equal to the system assigned during the distributed actor's local initializer
231
+ /// (or to the system passed to the ``resolve(id:using:)`` static function) .
69
232
///
70
- /// Conformance to this requirement is synthesized automatically for any
71
- /// `distributed actor` declaration.
233
+ /// ## Synthesized property
234
+ ///
235
+ /// In concrete distributed actor declarations, a witness for this protocol requirement is synthesized by the compiler.
236
+ ///
237
+ /// It is required to assign an initial value to the `actorSystem` property inside a distributed actor's designated initializer.
238
+ /// Semantically, it can be treated as a `let` declaration, that must be assigned in order to fully-initialize the instance.
239
+ ///
240
+ /// If a distributed actor declares no initializer, its default initializer will take the shape of `init(actorSystem:)`,
241
+ /// and initialize this property using the passed ``DistributedActorSystem``. If any user-defined initializer exists,
242
+ /// the default initializer is not synthesized, and all the user-defined initializers must take care to initialize this property.
72
243
nonisolated var actorSystem : ActorSystem { get }
73
244
74
245
/// Resolves the passed in `id` against the `system`, returning
@@ -81,6 +252,8 @@ public protocol DistributedActor: AnyActor, Identifiable, Hashable
81
252
/// the system, allowing it to take over the remote messaging with the
82
253
/// remote actor instance.
83
254
///
255
+ /// - Postcondition: upon successful return, the returned actor's ``id`` and ``actorSystem`` properties
256
+ /// will be equal to the values passed as parameters to this method.
84
257
/// - Parameter id: identity uniquely identifying a, potentially remote, actor in the system
85
258
/// - Parameter system: `system` which should be used to resolve the `identity`, and be associated with the returned actor
86
259
static func resolve( id: ID , using system: ActorSystem ) throws -> Self
@@ -90,10 +263,17 @@ public protocol DistributedActor: AnyActor, Identifiable, Hashable
90
263
91
264
@available ( SwiftStdlib 5 . 7 , * )
92
265
extension DistributedActor {
266
+
267
+ /// A distributed actor's hash and equality is implemented by directly delegating to its ``id``.
268
+ ///
269
+ /// For more details see the "Hashable and Identifiable conformance" section of ``DistributedActor``.
93
270
nonisolated public func hash( into hasher: inout Hasher ) {
94
271
self . id. hash ( into: & hasher)
95
272
}
96
273
274
+ /// A distributed actor's hash and equality is implemented by directly delegating to its ``id``.
275
+ ///
276
+ /// For more details see the "Hashable and Identifiable conformance" section of ``DistributedActor``.
97
277
nonisolated public static func == ( lhs: Self , rhs: Self ) -> Bool {
98
278
lhs. id == rhs. id
99
279
}
@@ -102,12 +282,31 @@ extension DistributedActor {
102
282
// ==== Codable conformance ----------------------------------------------------
103
283
104
284
extension CodingUserInfoKey {
285
+
286
+ /// Key which is required to be set on a `Decoder`'s `userInfo` while attempting
287
+ /// to `init(from:)` a `DistributedActor`. The stored value under this key must
288
+ /// conform to ``DistributedActorSystem``.
289
+ ///
290
+ /// Forgetting to set this key will result in that initializer throwing, because
291
+ /// an actor system is required in order to call ``DistributedActor/resolve(id:using:)`` using it.
105
292
@available ( SwiftStdlib 5 . 7 , * )
106
293
public static let actorSystemKey = CodingUserInfoKey ( rawValue: " $distributed_actor_system " ) !
107
294
}
108
295
109
296
@available ( SwiftStdlib 5 . 7 , * )
110
297
extension DistributedActor /*: implicitly Decodable */ where Self. ID: Decodable {
298
+
299
+ /// Initializes an instance of this distributed actor by decoding its ``id``,
300
+ /// and passing it to the ``DistributedActorSystem`` obtained from `decoder.userInfo[actorSystemKey]`.
301
+ ///
302
+ /// ## Requires: The decoder must have the ``CodingUserInfoKey/actorSystemKey`` set to
303
+ /// the ``ActorSystem`` that this actor expects, as it will be used to call ``DistributedActor/resolve(id:using:)``
304
+ /// on, in order to obtain the instance this initializer should return.
305
+ ///
306
+ /// - Parameter decoder: used to decode the ``ID`` of this distributed actor.
307
+ /// - Throws: If the actor system value in `decoder.userInfo` is missing or mis-typed;
308
+ /// the `ID` fails to decode from the passed `decoder`;
309
+ // or if the ``DistributedActor/resolve(id:using:)`` method invoked by this initializer throws.
111
310
nonisolated public init ( from decoder: Decoder ) throws {
112
311
guard let system = decoder. userInfo [ . actorSystemKey] as? ActorSystem else {
113
312
throw DistributedActorCodingError ( message:
@@ -122,6 +321,8 @@ extension DistributedActor /*: implicitly Decodable */ where Self.ID: Decodable
122
321
123
322
@available ( SwiftStdlib 5 . 7 , * )
124
323
extension DistributedActor /*: implicitly Encodable */ where Self. ID: Encodable {
324
+
325
+ /// Encodes the `actor.id` as a single value into the passed `encoder`.
125
326
nonisolated public func encode( to encoder: Encoder ) throws {
126
327
var container = encoder. singleValueContainer ( )
127
328
try container. encode ( self . id)
@@ -157,9 +358,17 @@ extension DistributedActor {
157
358
158
359
// ==== isRemote / isLocal -----------------------------------------------------
159
360
361
+ /// Verifies if the passed ``DistributedActor`` conforming type is a remote reference.
362
+ /// Passing a type not conforming to ``DistributedActor`` may result in undefined behavior.
363
+ ///
364
+ /// Official API to perform this task is `whenLocal`.
160
365
@_silgen_name ( " swift_distributed_actor_is_remote " )
161
366
public func __isRemoteActor( _ actor : AnyObject ) -> Bool
162
367
368
+ /// Verifies if the passed ``DistributedActor`` conforming type is a local reference.
369
+ /// Passing a type not conforming to ``DistributedActor`` may result in undefined behavior.
370
+ ///
371
+ /// Official API to perform this task is `whenLocal`.
163
372
public func __isLocalActor( _ actor : AnyObject ) -> Bool {
164
373
return !__isRemoteActor( actor )
165
374
}
0 commit comments