From aeaf7010af302201219dc75d2c77263d0ff10aa4 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Fri, 2 Sep 2022 07:00:56 +0900 Subject: [PATCH 1/3] [Distributed] More docs for ad-hoc requirements --- .../Distributed/DistributedActorSystem.swift | 94 ++++++++++++++++++- 1 file changed, 92 insertions(+), 2 deletions(-) diff --git a/stdlib/public/Distributed/DistributedActorSystem.swift b/stdlib/public/Distributed/DistributedActorSystem.swift index f634d3b827720..02d5304f31270 100644 --- a/stdlib/public/Distributed/DistributedActorSystem.swift +++ b/stdlib/public/Distributed/DistributedActorSystem.swift @@ -173,7 +173,7 @@ import _Concurrency /// /// Implementing the remote calls correctly and efficiently is the important task for a distributed actor system library. /// Since those methods are not currently expressible as protocol requirements due to advanced use of generics -/// combined with type aliases, they will not appear in the protocol's documentation as explicit requirements. +/// combined with associated types, they will not appear in the protocol's documentation as explicit requirements. /// Instead, we present their signatures that a conforming type has to implement here: /// /// > Note: Although the `remoteCall` methods are not expressed as protocol requirements in source, @@ -688,6 +688,32 @@ func _executeDistributedTarget( /// Once encoded, the system should use some underlying transport mechanism to send the /// bytes serialized by the invocation to the remote peer. /// +/// ### Protocol requirements +/// Similar to the ``DistributedActorSystem`` and its `remoteCall` and `remoteCallVoid` protocol requirements, +/// the `DistributedTargetInvocationEncoder` contains a few methods which are not possible to express in source due to +/// advanced use generics combined with associated types. Specifically, the `recordArgument` and `recordReturnType` +/// methods are not expressed in source as protocol requirements, but will be treated by the compiler as-if they were. +/// +/// > Note: Although the `recordArgument` method is not expressed as protocol requirement in source, +/// > the compiler will provide the same errors as-if they were declared explicitly in this protocol. +/// +/// In addition to the compiler offering compile errors if those witnesses are missing in an adopting type, +/// we present their signatures here for reference: +/// +/// ```swift +/// /// Record an argument of `Argument` type. +/// /// This will be invoked for every argument of the target, in declaration order. +/// mutating func recordArgument( +/// _ argument: DistributedTargetArgument +/// ) throws +/// +/// /// Ad-hoc requirement +/// /// +/// /// Record the return type of the distributed method. +/// /// This method will not be invoked if the target is returning `Void`. +/// mutating func recordReturnType(_ type: R.Type) throws +/// ``` +/// /// ## Decoding an invocation /// Since every actor system is going to deal with a concrete invocation type, they may /// implement decoding them whichever way is most optimal for the given system. @@ -771,6 +797,35 @@ public struct RemoteCallArgument { /// Decoder that must be provided to `executeDistributedTarget` and is used /// by the Swift runtime to decode arguments of the invocation. +/// +/// ### Protocol requirements +/// Similar to the ``DistributedTargetInvocationEncoder`` and its `recordArgument` and `recordReturnType` protocol requirements, +/// the `DistributedTargetInvocationDecoder` contains a method which is not possible to express in source due to +/// advanced use generics combined with associated types. Specifically, the `decodeNextArgument` +/// method is not expressed in source as protocol requirement, but will be treated by the compiler as-if it was. +/// +/// > Note: Although the `decodeNextArgument` method is not expressed as protocol requirement in source, +/// > the compiler will provide the same errors as-if they were declared explicitly in this protocol. +/// +/// In addition to the compiler offering compile errors if this witness is missing in an adopting type, +/// we present its signature here for reference: +/// +/// ```swift +/// /// Ad-hoc protocol requirement +/// /// +/// /// Attempt to decode the next argument from the underlying buffers into pre-allocated storage +/// /// pointed at by 'pointer'. +/// /// +/// /// This method should throw if it has no more arguments available, if decoding the argument failed, +/// /// or, optionally, if the argument type we're trying to decode does not match the stored type. +/// /// +/// /// The result of the decoding operation must be stored into the provided 'pointer' rather than +/// /// returning a value. This pattern allows the runtime to use a heavily optimized, pre-allocated +/// /// buffer for all the arguments and their expected types. The 'pointer' passed here is a pointer +/// /// to a "slot" in that pre-allocated buffer. That buffer will then be passed to a thunk that +/// /// performs the actual distributed (local) instance method invocation. +/// mutating func decodeNextArgument() throws -> Argument +/// ``` @available(SwiftStdlib 5.7, *) public protocol DistributedTargetInvocationDecoder { associatedtype SerializationRequirement @@ -801,15 +856,50 @@ public protocol DistributedTargetInvocationDecoder { mutating func decodeReturnType() throws -> Any.Type? } +/// Protocol a distributed invocation execution's result handler. +/// +/// An instance conforming to this type must be passed when invoking +/// ``executeDistributedTarget(on:target:invocationDecoder:handler:)`` while handling an incoming distributed call. +/// +/// The handler will then be invoked with the return value (or error) that the invoked target returned (or threw). +/// +/// ### Protocol requirements +/// Similar to the ``DistributedActorSystem`` and its `remoteCall` and `remoteCallVoid` protocol requirements, +/// the `DistributedTargetInvocationResultHandler` contains a method which is not possible to express in source due to +/// advanced use generics combined with associated types. Specifically, the `onReturn` method is not expressed in +/// source as protocol requirement, but will be treated by the compiler as-if they were. +/// +/// > Note: Although the `onReturn` method is not expressed as protocol requirement in source, +/// > the compiler will provide the same errors as-if they were declared explicitly in this protocol. +/// +/// In addition to the compiler offering compile errors if this witnesses is missing in an adopting type, +/// we present its signature here for reference: +/// +/// ```swift +/// /// Ad-hoc protocol requirement +/// /// +/// /// Invoked when the distributed target execution returned successfully. +/// /// The `value` is the return value of the executed distributed invocation target. +/// func onReturn(value: Success) async throws +/// ``` @available(SwiftStdlib 5.7, *) public protocol DistributedTargetInvocationResultHandler { associatedtype SerializationRequirement + +// /// Ad-hoc protocol requirement +// /// +// /// Invoked when the distributed target execution returned successfully. +// /// The `value` is the return value of the executed distributed invocation target. // func onReturn(value: Success) async throws - /// Invoked when the distributed target invocation of a `Void` returning + /// Invoked when the distributed target execution of a `Void` returning /// function has completed successfully. func onReturnVoid() async throws + /// Invoked when the distributed target execution of a target has thrown an error. + /// + /// It is not guaranteed that the error conform to the ``SerializationRequirement``; + /// This guarantee is only given to return values (and offered by `onReturn`). func onThrow(error: Err) async throws } From 236e55b71ea848551f19f4e49c6579cc2046be69 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 14 Sep 2022 10:00:46 +0900 Subject: [PATCH 2/3] [Distributed] rewrite docs referencing old wording of some APIs --- .../Distributed/DistributedActorSystem.swift | 59 +++++++++++++++---- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/stdlib/public/Distributed/DistributedActorSystem.swift b/stdlib/public/Distributed/DistributedActorSystem.swift index 02d5304f31270..38b2589a1ceb5 100644 --- a/stdlib/public/Distributed/DistributedActorSystem.swift +++ b/stdlib/public/Distributed/DistributedActorSystem.swift @@ -280,17 +280,24 @@ public protocol DistributedActorSystem: Sendable { // ==== --------------------------------------------------------------------- // - MARK: Resolving actors by identity - /// Resolve a local or remote actor address to a real actor instance, or throw if unable to. + /// Resolves a local or remote ``ActorID`` to a reference to given actor, or throws if unable to. + /// /// The returned value is either a local actor or proxy to a remote actor. /// - /// Resolving an actor is called when a specific distributed actors `init(from:)` - /// decoding initializer is invoked. Once the actor's identity is deserialized - /// using the `decodeIdentity(from:)` call, it is fed into this function, which - /// is responsible for resolving the identity to a remote or local actor reference. + /// This function is not intended to be used directly, but instead is called by the Swift runtime + /// whenever ``DistributedActor/resolve(id:using:)` or a concrete distributed actor's `init(from:)` is invoked. + /// + /// This function should either return an existing actor reference, or `nil` to signal that a remote distributed actor + /// "proxy" should be created for this ``ActorID``. If the resolve fails, meaning that it can neither locate a local + /// actor managed by this actor system, nor identify that the identity is located on some remote actor system, then + /// this function should throw. + /// + /// ```swift + /// distributed actor Worker { /* ... */ } /// - /// If the resolve fails, meaning that it cannot locate a local actor managed for - /// this identity, managed by this transport, nor can a remote actor reference - /// be created for this identity on this transport, then this function must throw. + /// // the following internally calls actorSystem.resolve(id: id, as: Worker.self) + /// let worker: Worker = try Worker.resolve(id: id, using: actorSystem) + /// ``` /// /// If this function returns correctly, the returned actor reference is immediately /// usable. It may not necessarily imply the strict *existence* of a remote actor @@ -300,13 +307,20 @@ public protocol DistributedActorSystem: Sendable { /// /// Detecting liveness of such remote actors shall be offered / by transport libraries /// by other means, such as "watching an actor for termination" or similar. + /// + /// - Parameter id: The `ActorID` to resolve an actor reference for + /// - Parameter actorType: The type of distributed actor the ID is expected to point at. + /// + /// - Throws: When unable to confirm if the `id` is correct, the resolved actor does not match the expected `actorType`, + /// or any other internal validation error within the actor system's resolve process occurs. func resolve(id: ActorID, as actorType: Act.Type) throws -> Act? where Act: DistributedActor, Act.ID == ActorID // ==== --------------------------------------------------------------------- // - MARK: Actor Lifecycle - /// Create an `ActorID` for the passed actor type. + + /// Assign an ``ActorID`` for the passed actor type. /// /// This function is invoked by an distributed actor during its initialization, /// and the returned address value is stored along with it for the time of its @@ -326,10 +340,10 @@ public protocol DistributedActorSystem: Sendable { /// mapping for the purpose of implementing the `resolve(id:as:)` method. /// /// The system usually should NOT retain the passed reference, and it will be informed via - /// `resignID(_:)` when the actor has been deallocated so it can remove the stale reference from its + /// ``resignID(_:)`` when the actor has been deallocated so it can remove the stale reference from its /// internal `ActorID: DistributedActor` mapping. /// - /// The `actor.id` of the passed actor must be an `ActorID` that this system previously has assigned. + /// The ``DistributedActor/id`` of the passed actor must be an ``ActorID`` that this system previously has assigned. /// /// If `actorReady` gets called with some unknown ID, it should crash immediately as it signifies some /// very unexpected use of the system. @@ -726,10 +740,13 @@ func _executeDistributedTarget( /// entry points on the provided types. @available(SwiftStdlib 5.7, *) public protocol DistributedTargetInvocationEncoder { + /// The serialization requirement that the types passed to `recordArgument` and `recordReturnType` are required to conform to. associatedtype SerializationRequirement /// The arguments must be encoded order-preserving, and once `decodeGenericSubstitutions` /// is called, the substitutions must be returned in the same order in which they were recorded. + /// + /// - Parameter type: a generic substitution type to be recorded for this invocation. mutating func recordGenericSubstitution(_ type: T.Type) throws // /// Ad-hoc requirement @@ -742,6 +759,8 @@ public protocol DistributedTargetInvocationEncoder { /// Record the error type of the distributed method. /// This method will not be invoked if the target is not throwing. + /// + /// - Parameter type: the type of error that was declared to be thrown by the invocation target. Currently this can only ever be `Error.self`. mutating func recordErrorType(_ type: E.Type) throws // /// Ad-hoc requirement @@ -750,6 +769,10 @@ public protocol DistributedTargetInvocationEncoder { // /// This method will not be invoked if the target is returning `Void`. // mutating func recordReturnType(_ type: R.Type) throws + /// Invoked to signal to the encoder that no further `record...` calls will be made on it. + /// + /// Useful if the encoder needs to perform some "final" task before the underlying message is considered complete, + /// e.g. computing a checksum, or some additional message signing or finalization step. mutating func doneRecording() throws } @@ -828,8 +851,17 @@ public struct RemoteCallArgument { /// ``` @available(SwiftStdlib 5.7, *) public protocol DistributedTargetInvocationDecoder { + /// The serialization requirement that the types passed to `decodeNextArgument` are required to conform to. + /// The type returned by `decodeReturnType` is also expected to conform to this associated type requirement. associatedtype SerializationRequirement + /// Decode all generic substitutions that were recorded for this invocation. + /// + /// The values retrieved from here must be in the same order as they were recorded by + /// ``DistributedTargetInvocationEncoder/recordGenericSubstitution(_:)``. + /// + /// - Returns: array of all generic substitutions necessary to execute this invocation target. + /// - Throws: if decoding substitutions fails. mutating func decodeGenericSubstitutions() throws -> [Any.Type] // /// Ad-hoc protocol requirement @@ -847,6 +879,10 @@ public protocol DistributedTargetInvocationDecoder { // /// performs the actual distributed (local) instance method invocation. // mutating func decodeNextArgument() throws -> Argument + /// Decode the specific error type that the distributed invocation target has recorded. + /// Currently this effectively can only ever be `Error.self`. + /// + /// If the target known to not be throwing, or no error type was recorded, the method should return `nil`. mutating func decodeErrorType() throws -> Any.Type? /// Attempt to decode the known return type of the distributed invocation. @@ -884,6 +920,7 @@ public protocol DistributedTargetInvocationDecoder { /// ``` @available(SwiftStdlib 5.7, *) public protocol DistributedTargetInvocationResultHandler { + /// The serialization requirement that the value passed to `onReturn` is required to conform to. associatedtype SerializationRequirement // /// Ad-hoc protocol requirement From dc5fe945128921674bbdce2118bf4feff6acd5fa Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Thu, 15 Sep 2022 19:27:27 +0900 Subject: [PATCH 3/3] fix typos / review feedback --- .../Distributed/DistributedActorSystem.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/stdlib/public/Distributed/DistributedActorSystem.swift b/stdlib/public/Distributed/DistributedActorSystem.swift index 38b2589a1ceb5..5f55b5cd4111b 100644 --- a/stdlib/public/Distributed/DistributedActorSystem.swift +++ b/stdlib/public/Distributed/DistributedActorSystem.swift @@ -177,7 +177,7 @@ import _Concurrency /// Instead, we present their signatures that a conforming type has to implement here: /// /// > Note: Although the `remoteCall` methods are not expressed as protocol requirements in source, -/// > the compiler will provide the same errors as-if they were declared explicitly in this protocol. +/// > the compiler will provide the same errors as-if it was declared explicitly in this protocol. /// /// ```swift /// /// Invoked by the Swift runtime when making a remote call. @@ -705,11 +705,11 @@ func _executeDistributedTarget( /// ### Protocol requirements /// Similar to the ``DistributedActorSystem`` and its `remoteCall` and `remoteCallVoid` protocol requirements, /// the `DistributedTargetInvocationEncoder` contains a few methods which are not possible to express in source due to -/// advanced use generics combined with associated types. Specifically, the `recordArgument` and `recordReturnType` +/// advanced use of generics combined with associated types. Specifically, the `recordArgument` and `recordReturnType` /// methods are not expressed in source as protocol requirements, but will be treated by the compiler as-if they were. /// /// > Note: Although the `recordArgument` method is not expressed as protocol requirement in source, -/// > the compiler will provide the same errors as-if they were declared explicitly in this protocol. +/// > the compiler will provide the same errors as-if it was declared explicitly in this protocol. /// /// In addition to the compiler offering compile errors if those witnesses are missing in an adopting type, /// we present their signatures here for reference: @@ -824,11 +824,11 @@ public struct RemoteCallArgument { /// ### Protocol requirements /// Similar to the ``DistributedTargetInvocationEncoder`` and its `recordArgument` and `recordReturnType` protocol requirements, /// the `DistributedTargetInvocationDecoder` contains a method which is not possible to express in source due to -/// advanced use generics combined with associated types. Specifically, the `decodeNextArgument` +/// advanced use of generics combined with associated types. Specifically, the `decodeNextArgument` /// method is not expressed in source as protocol requirement, but will be treated by the compiler as-if it was. /// /// > Note: Although the `decodeNextArgument` method is not expressed as protocol requirement in source, -/// > the compiler will provide the same errors as-if they were declared explicitly in this protocol. +/// > the compiler will provide the same errors as-if it was declared explicitly in this protocol. /// /// In addition to the compiler offering compile errors if this witness is missing in an adopting type, /// we present its signature here for reference: @@ -902,11 +902,11 @@ public protocol DistributedTargetInvocationDecoder { /// ### Protocol requirements /// Similar to the ``DistributedActorSystem`` and its `remoteCall` and `remoteCallVoid` protocol requirements, /// the `DistributedTargetInvocationResultHandler` contains a method which is not possible to express in source due to -/// advanced use generics combined with associated types. Specifically, the `onReturn` method is not expressed in +/// advanced use of generics combined with associated types. Specifically, the `onReturn` method is not expressed in /// source as protocol requirement, but will be treated by the compiler as-if they were. /// /// > Note: Although the `onReturn` method is not expressed as protocol requirement in source, -/// > the compiler will provide the same errors as-if they were declared explicitly in this protocol. +/// > the compiler will provide the same errors as-if it was declared explicitly in this protocol. /// /// In addition to the compiler offering compile errors if this witnesses is missing in an adopting type, /// we present its signature here for reference: @@ -914,7 +914,7 @@ public protocol DistributedTargetInvocationDecoder { /// ```swift /// /// Ad-hoc protocol requirement /// /// -/// /// Invoked when the distributed target execution returned successfully. +/// /// Invoked when the distributed target execution returns successfully. /// /// The `value` is the return value of the executed distributed invocation target. /// func onReturn(value: Success) async throws /// ``` @@ -925,7 +925,7 @@ public protocol DistributedTargetInvocationResultHandler { // /// Ad-hoc protocol requirement // /// -// /// Invoked when the distributed target execution returned successfully. +// /// Invoked when the distributed target execution returns successfully. // /// The `value` is the return value of the executed distributed invocation target. // func onReturn(value: Success) async throws