@@ -17,29 +17,185 @@ import _Concurrency
1717
1818/// Common protocol to which all distributed actors conform implicitly.
1919///
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.
2322///
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`.
2624///
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
29176/// 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
42194/// actor system for the decoding initializer when decoding a distributed actor.
195+ ///
196+ /// - SeeAlso: ``DistributedActorSystem``
197+ /// - SeeAlso: ``Actor``
198+ /// - SeeAlso: ``AnyActor``
43199@available ( SwiftStdlib 5 . 7 , * )
44200public protocol DistributedActor : AnyActor , Identifiable , Hashable
45201 where ID == ActorSystem . ActorID ,
@@ -58,17 +214,32 @@ public protocol DistributedActor: AnyActor, Identifiable, Hashable
58214 /// to return the same exact resolved actor instance, however all the references would
59215 /// represent logically references to the same distributed actor, e.g. on a different node.
60216 ///
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.
63226 nonisolated override var id : ID { get }
64227
65- /// The `ActorSystem ` that is managing this distributed actor.
228+ /// The ``DistributedActorSystem` ` that is managing this distributed actor.
66229 ///
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) .
69232 ///
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.
72243 nonisolated var actorSystem : ActorSystem { get }
73244
74245 /// Resolves the passed in `id` against the `system`, returning
@@ -81,6 +252,8 @@ public protocol DistributedActor: AnyActor, Identifiable, Hashable
81252 /// the system, allowing it to take over the remote messaging with the
82253 /// remote actor instance.
83254 ///
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.
84257 /// - Parameter id: identity uniquely identifying a, potentially remote, actor in the system
85258 /// - Parameter system: `system` which should be used to resolve the `identity`, and be associated with the returned actor
86259 static func resolve( id: ID , using system: ActorSystem ) throws -> Self
@@ -90,10 +263,17 @@ public protocol DistributedActor: AnyActor, Identifiable, Hashable
90263
91264@available ( SwiftStdlib 5 . 7 , * )
92265extension 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``.
93270 nonisolated public func hash( into hasher: inout Hasher ) {
94271 self . id. hash ( into: & hasher)
95272 }
96273
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``.
97277 nonisolated public static func == ( lhs: Self , rhs: Self ) -> Bool {
98278 lhs. id == rhs. id
99279 }
@@ -102,12 +282,31 @@ extension DistributedActor {
102282// ==== Codable conformance ----------------------------------------------------
103283
104284extension 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.
105292 @available ( SwiftStdlib 5 . 7 , * )
106293 public static let actorSystemKey = CodingUserInfoKey ( rawValue: " $distributed_actor_system " ) !
107294}
108295
109296@available ( SwiftStdlib 5 . 7 , * )
110297extension 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.
111310 nonisolated public init ( from decoder: Decoder ) throws {
112311 guard let system = decoder. userInfo [ . actorSystemKey] as? ActorSystem else {
113312 throw DistributedActorCodingError ( message:
@@ -122,6 +321,8 @@ extension DistributedActor /*: implicitly Decodable */ where Self.ID: Decodable
122321
123322@available ( SwiftStdlib 5 . 7 , * )
124323extension DistributedActor /*: implicitly Encodable */ where Self. ID: Encodable {
324+
325+ /// Encodes the `actor.id` as a single value into the passed `encoder`.
125326 nonisolated public func encode( to encoder: Encoder ) throws {
126327 var container = encoder. singleValueContainer ( )
127328 try container. encode ( self . id)
@@ -157,9 +358,17 @@ extension DistributedActor {
157358
158359// ==== isRemote / isLocal -----------------------------------------------------
159360
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`.
160365@_silgen_name ( " swift_distributed_actor_is_remote " )
161366public func __isRemoteActor( _ actor : AnyObject ) -> Bool
162367
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`.
163372public func __isLocalActor( _ actor : AnyObject ) -> Bool {
164373 return !__isRemoteActor( actor )
165374}
0 commit comments