Skip to content

Commit 3a943ba

Browse files
authored
🍒[5.7][Distributed] More docs for ad-hoc requirements (swiftlang#61087)
1 parent b6a0418 commit 3a943ba

File tree

1 file changed

+141
-14
lines changed

1 file changed

+141
-14
lines changed

stdlib/public/Distributed/DistributedActorSystem.swift

Lines changed: 141 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,11 @@ import _Concurrency
173173
///
174174
/// Implementing the remote calls correctly and efficiently is the important task for a distributed actor system library.
175175
/// Since those methods are not currently expressible as protocol requirements due to advanced use of generics
176-
/// combined with type aliases, they will not appear in the protocol's documentation as explicit requirements.
176+
/// combined with associated types, they will not appear in the protocol's documentation as explicit requirements.
177177
/// Instead, we present their signatures that a conforming type has to implement here:
178178
///
179179
/// > Note: Although the `remoteCall` methods are not expressed as protocol requirements in source,
180-
/// > the compiler will provide the same errors as-if they were declared explicitly in this protocol.
180+
/// > the compiler will provide the same errors as-if it was declared explicitly in this protocol.
181181
///
182182
/// ```swift
183183
/// /// Invoked by the Swift runtime when making a remote call.
@@ -280,17 +280,24 @@ public protocol DistributedActorSystem: Sendable {
280280
// ==== ---------------------------------------------------------------------
281281
// - MARK: Resolving actors by identity
282282

283-
/// Resolve a local or remote actor address to a real actor instance, or throw if unable to.
283+
/// Resolves a local or remote ``ActorID`` to a reference to given actor, or throws if unable to.
284+
///
284285
/// The returned value is either a local actor or proxy to a remote actor.
285286
///
286-
/// Resolving an actor is called when a specific distributed actors `init(from:)`
287-
/// decoding initializer is invoked. Once the actor's identity is deserialized
288-
/// using the `decodeIdentity(from:)` call, it is fed into this function, which
289-
/// is responsible for resolving the identity to a remote or local actor reference.
287+
/// This function is not intended to be used directly, but instead is called by the Swift runtime
288+
/// whenever ``DistributedActor/resolve(id:using:)` or a concrete distributed actor's `init(from:)` is invoked.
289+
///
290+
/// This function should either return an existing actor reference, or `nil` to signal that a remote distributed actor
291+
/// "proxy" should be created for this ``ActorID``. If the resolve fails, meaning that it can neither locate a local
292+
/// actor managed by this actor system, nor identify that the identity is located on some remote actor system, then
293+
/// this function should throw.
294+
///
295+
/// ```swift
296+
/// distributed actor Worker { /* ... */ }
290297
///
291-
/// If the resolve fails, meaning that it cannot locate a local actor managed for
292-
/// this identity, managed by this transport, nor can a remote actor reference
293-
/// be created for this identity on this transport, then this function must throw.
298+
/// // the following internally calls actorSystem.resolve(id: id, as: Worker.self)
299+
/// let worker: Worker = try Worker.resolve(id: id, using: actorSystem)
300+
/// ```
294301
///
295302
/// If this function returns correctly, the returned actor reference is immediately
296303
/// usable. It may not necessarily imply the strict *existence* of a remote actor
@@ -300,13 +307,20 @@ public protocol DistributedActorSystem: Sendable {
300307
///
301308
/// Detecting liveness of such remote actors shall be offered / by transport libraries
302309
/// by other means, such as "watching an actor for termination" or similar.
310+
///
311+
/// - Parameter id: The `ActorID` to resolve an actor reference for
312+
/// - Parameter actorType: The type of distributed actor the ID is expected to point at.
313+
///
314+
/// - Throws: When unable to confirm if the `id` is correct, the resolved actor does not match the expected `actorType`,
315+
/// or any other internal validation error within the actor system's resolve process occurs.
303316
func resolve<Act>(id: ActorID, as actorType: Act.Type) throws -> Act?
304317
where Act: DistributedActor,
305318
Act.ID == ActorID
306319

307320
// ==== ---------------------------------------------------------------------
308321
// - MARK: Actor Lifecycle
309-
/// Create an `ActorID` for the passed actor type.
322+
323+
/// Assign an ``ActorID`` for the passed actor type.
310324
///
311325
/// This function is invoked by an distributed actor during its initialization,
312326
/// and the returned address value is stored along with it for the time of its
@@ -326,10 +340,10 @@ public protocol DistributedActorSystem: Sendable {
326340
/// mapping for the purpose of implementing the `resolve(id:as:)` method.
327341
///
328342
/// The system usually should NOT retain the passed reference, and it will be informed via
329-
/// `resignID(_:)` when the actor has been deallocated so it can remove the stale reference from its
343+
/// ``resignID(_:)`` when the actor has been deallocated so it can remove the stale reference from its
330344
/// internal `ActorID: DistributedActor` mapping.
331345
///
332-
/// The `actor.id` of the passed actor must be an `ActorID` that this system previously has assigned.
346+
/// The ``DistributedActor/id`` of the passed actor must be an ``ActorID`` that this system previously has assigned.
333347
///
334348
/// If `actorReady` gets called with some unknown ID, it should crash immediately as it signifies some
335349
/// very unexpected use of the system.
@@ -688,6 +702,32 @@ func _executeDistributedTarget<D: DistributedTargetInvocationDecoder>(
688702
/// Once encoded, the system should use some underlying transport mechanism to send the
689703
/// bytes serialized by the invocation to the remote peer.
690704
///
705+
/// ### Protocol requirements
706+
/// Similar to the ``DistributedActorSystem`` and its `remoteCall` and `remoteCallVoid` protocol requirements,
707+
/// the `DistributedTargetInvocationEncoder` contains a few methods which are not possible to express in source due to
708+
/// advanced use of generics combined with associated types. Specifically, the `recordArgument` and `recordReturnType`
709+
/// methods are not expressed in source as protocol requirements, but will be treated by the compiler as-if they were.
710+
///
711+
/// > Note: Although the `recordArgument` method is not expressed as protocol requirement in source,
712+
/// > the compiler will provide the same errors as-if it was declared explicitly in this protocol.
713+
///
714+
/// In addition to the compiler offering compile errors if those witnesses are missing in an adopting type,
715+
/// we present their signatures here for reference:
716+
///
717+
/// ```swift
718+
/// /// Record an argument of `Argument` type.
719+
/// /// This will be invoked for every argument of the target, in declaration order.
720+
/// mutating func recordArgument<Value: SerializationRequirement>(
721+
/// _ argument: DistributedTargetArgument<Value>
722+
/// ) throws
723+
///
724+
/// /// Ad-hoc requirement
725+
/// ///
726+
/// /// Record the return type of the distributed method.
727+
/// /// This method will not be invoked if the target is returning `Void`.
728+
/// mutating func recordReturnType<R: SerializationRequirement>(_ type: R.Type) throws
729+
/// ```
730+
///
691731
/// ## Decoding an invocation
692732
/// Since every actor system is going to deal with a concrete invocation type, they may
693733
/// implement decoding them whichever way is most optimal for the given system.
@@ -700,10 +740,13 @@ func _executeDistributedTarget<D: DistributedTargetInvocationDecoder>(
700740
/// entry points on the provided types.
701741
@available(SwiftStdlib 5.7, *)
702742
public protocol DistributedTargetInvocationEncoder {
743+
/// The serialization requirement that the types passed to `recordArgument` and `recordReturnType` are required to conform to.
703744
associatedtype SerializationRequirement
704745

705746
/// The arguments must be encoded order-preserving, and once `decodeGenericSubstitutions`
706747
/// is called, the substitutions must be returned in the same order in which they were recorded.
748+
///
749+
/// - Parameter type: a generic substitution type to be recorded for this invocation.
707750
mutating func recordGenericSubstitution<T>(_ type: T.Type) throws
708751

709752
// /// Ad-hoc requirement
@@ -716,6 +759,8 @@ public protocol DistributedTargetInvocationEncoder {
716759

717760
/// Record the error type of the distributed method.
718761
/// This method will not be invoked if the target is not throwing.
762+
///
763+
/// - Parameter type: the type of error that was declared to be thrown by the invocation target. Currently this can only ever be `Error.self`.
719764
mutating func recordErrorType<E: Error>(_ type: E.Type) throws
720765

721766
// /// Ad-hoc requirement
@@ -724,6 +769,10 @@ public protocol DistributedTargetInvocationEncoder {
724769
// /// This method will not be invoked if the target is returning `Void`.
725770
// mutating func recordReturnType<R: SerializationRequirement>(_ type: R.Type) throws
726771

772+
/// Invoked to signal to the encoder that no further `record...` calls will be made on it.
773+
///
774+
/// Useful if the encoder needs to perform some "final" task before the underlying message is considered complete,
775+
/// e.g. computing a checksum, or some additional message signing or finalization step.
727776
mutating func doneRecording() throws
728777
}
729778

@@ -771,10 +820,48 @@ public struct RemoteCallArgument<Value> {
771820

772821
/// Decoder that must be provided to `executeDistributedTarget` and is used
773822
/// by the Swift runtime to decode arguments of the invocation.
823+
///
824+
/// ### Protocol requirements
825+
/// Similar to the ``DistributedTargetInvocationEncoder`` and its `recordArgument` and `recordReturnType` protocol requirements,
826+
/// the `DistributedTargetInvocationDecoder` contains a method which is not possible to express in source due to
827+
/// advanced use of generics combined with associated types. Specifically, the `decodeNextArgument`
828+
/// method is not expressed in source as protocol requirement, but will be treated by the compiler as-if it was.
829+
///
830+
/// > Note: Although the `decodeNextArgument` method is not expressed as protocol requirement in source,
831+
/// > the compiler will provide the same errors as-if it was declared explicitly in this protocol.
832+
///
833+
/// In addition to the compiler offering compile errors if this witness is missing in an adopting type,
834+
/// we present its signature here for reference:
835+
///
836+
/// ```swift
837+
/// /// Ad-hoc protocol requirement
838+
/// ///
839+
/// /// Attempt to decode the next argument from the underlying buffers into pre-allocated storage
840+
/// /// pointed at by 'pointer'.
841+
/// ///
842+
/// /// This method should throw if it has no more arguments available, if decoding the argument failed,
843+
/// /// or, optionally, if the argument type we're trying to decode does not match the stored type.
844+
/// ///
845+
/// /// The result of the decoding operation must be stored into the provided 'pointer' rather than
846+
/// /// returning a value. This pattern allows the runtime to use a heavily optimized, pre-allocated
847+
/// /// buffer for all the arguments and their expected types. The 'pointer' passed here is a pointer
848+
/// /// to a "slot" in that pre-allocated buffer. That buffer will then be passed to a thunk that
849+
/// /// performs the actual distributed (local) instance method invocation.
850+
/// mutating func decodeNextArgument<Argument: SerializationRequirement>() throws -> Argument
851+
/// ```
774852
@available(SwiftStdlib 5.7, *)
775853
public protocol DistributedTargetInvocationDecoder {
854+
/// The serialization requirement that the types passed to `decodeNextArgument` are required to conform to.
855+
/// The type returned by `decodeReturnType` is also expected to conform to this associated type requirement.
776856
associatedtype SerializationRequirement
777857

858+
/// Decode all generic substitutions that were recorded for this invocation.
859+
///
860+
/// The values retrieved from here must be in the same order as they were recorded by
861+
/// ``DistributedTargetInvocationEncoder/recordGenericSubstitution(_:)``.
862+
///
863+
/// - Returns: array of all generic substitutions necessary to execute this invocation target.
864+
/// - Throws: if decoding substitutions fails.
778865
mutating func decodeGenericSubstitutions() throws -> [Any.Type]
779866

780867
// /// Ad-hoc protocol requirement
@@ -792,6 +879,10 @@ public protocol DistributedTargetInvocationDecoder {
792879
// /// performs the actual distributed (local) instance method invocation.
793880
// mutating func decodeNextArgument<Argument: SerializationRequirement>() throws -> Argument
794881

882+
/// Decode the specific error type that the distributed invocation target has recorded.
883+
/// Currently this effectively can only ever be `Error.self`.
884+
///
885+
/// If the target known to not be throwing, or no error type was recorded, the method should return `nil`.
795886
mutating func decodeErrorType() throws -> Any.Type?
796887

797888
/// Attempt to decode the known return type of the distributed invocation.
@@ -801,15 +892,51 @@ public protocol DistributedTargetInvocationDecoder {
801892
mutating func decodeReturnType() throws -> Any.Type?
802893
}
803894

895+
/// Protocol a distributed invocation execution's result handler.
896+
///
897+
/// An instance conforming to this type must be passed when invoking
898+
/// ``executeDistributedTarget(on:target:invocationDecoder:handler:)`` while handling an incoming distributed call.
899+
///
900+
/// The handler will then be invoked with the return value (or error) that the invoked target returned (or threw).
901+
///
902+
/// ### Protocol requirements
903+
/// Similar to the ``DistributedActorSystem`` and its `remoteCall` and `remoteCallVoid` protocol requirements,
904+
/// the `DistributedTargetInvocationResultHandler` contains a method which is not possible to express in source due to
905+
/// advanced use of generics combined with associated types. Specifically, the `onReturn` method is not expressed in
906+
/// source as protocol requirement, but will be treated by the compiler as-if they were.
907+
///
908+
/// > Note: Although the `onReturn` method is not expressed as protocol requirement in source,
909+
/// > the compiler will provide the same errors as-if it was declared explicitly in this protocol.
910+
///
911+
/// In addition to the compiler offering compile errors if this witnesses is missing in an adopting type,
912+
/// we present its signature here for reference:
913+
///
914+
/// ```swift
915+
/// /// Ad-hoc protocol requirement
916+
/// ///
917+
/// /// Invoked when the distributed target execution returns successfully.
918+
/// /// The `value` is the return value of the executed distributed invocation target.
919+
/// func onReturn<Success: SerializationRequirement>(value: Success) async throws
920+
/// ```
804921
@available(SwiftStdlib 5.7, *)
805922
public protocol DistributedTargetInvocationResultHandler {
923+
/// The serialization requirement that the value passed to `onReturn` is required to conform to.
806924
associatedtype SerializationRequirement
925+
926+
// /// Ad-hoc protocol requirement
927+
// ///
928+
// /// Invoked when the distributed target execution returns successfully.
929+
// /// The `value` is the return value of the executed distributed invocation target.
807930
// func onReturn<Success: SerializationRequirement>(value: Success) async throws
808931

809-
/// Invoked when the distributed target invocation of a `Void` returning
932+
/// Invoked when the distributed target execution of a `Void` returning
810933
/// function has completed successfully.
811934
func onReturnVoid() async throws
812935

936+
/// Invoked when the distributed target execution of a target has thrown an error.
937+
///
938+
/// It is not guaranteed that the error conform to the ``SerializationRequirement``;
939+
/// This guarantee is only given to return values (and offered by `onReturn`).
813940
func onThrow<Err: Error>(error: Err) async throws
814941
}
815942

0 commit comments

Comments
 (0)