[Distributed] move _missingDistributedActorTransport to _Distributed

This commit is contained in:
Konrad `ktoso` Malawski
2021-06-08 14:44:03 +09:00
parent 20382d5b89
commit bb9f2e4eb8
10 changed files with 109 additions and 349 deletions

View File

@@ -88,7 +88,4 @@ FUNC_DECL(Swap, "swap")
FUNC_DECL(UnimplementedInitializer, "_unimplementedInitializer")
FUNC_DECL(Undefined, "_undefined")
// TODO: move to _Distributed
FUNC_DECL(MissingDistributedActorTransport, "_missingDistributedActorTransport")
#undef FUNC_DECL

View File

@@ -19,7 +19,7 @@
# define KNOWN_SDK_FUNC_DECL(Module, Name, Id)
#endif
//KNOWN_SDK_FUNC_DECL(Distributed, MissingDistributedActorTransport, "_missingDistributedActorTransport")
KNOWN_SDK_FUNC_DECL(Distributed, MissingDistributedActorTransport, "_missingDistributedActorTransport")
KNOWN_SDK_FUNC_DECL(Distributed, IsRemoteDistributedActor, "__isRemoteActor")
#undef KNOWN_SDK_FUNC_DECL

View File

@@ -561,9 +561,11 @@ static void addImplicitDistributedActorStoredProperties(ClassDecl *decl) {
///
/// Create a stub body that emits a fatal error message.
static std::pair<BraceStmt *, bool>
synthesizeRemoteFuncStubBody(AbstractFunctionDecl *func, void *) {
synthesizeRemoteFuncStubBody(AbstractFunctionDecl *func, void *context) {
auto distributedFunc = static_cast<AbstractFunctionDecl *>(context);
auto classDecl = func->getDeclContext()->getSelfClassDecl();
auto &ctx = func->getASTContext();
auto &SM = ctx.SourceMgr;
auto *staticStringDecl = ctx.getStaticStringDecl();
auto staticStringType = staticStringDecl->getDeclaredInterfaceType();
@@ -574,8 +576,9 @@ synthesizeRemoteFuncStubBody(AbstractFunctionDecl *func, void *) {
auto uintInit = ctx.getIntBuiltinInitDecl(uintDecl);
auto missingTransportDecl = ctx.getMissingDistributedActorTransport();
assert(missingTransportDecl);
// Create a call to Swift._missingDistributedActorTransport // TODO: move to `Distributed` module
// Create a call to _Distributed._missingDistributedActorTransport
auto loc = func->getLoc();
Expr *ref = new (ctx) DeclRefExpr(missingTransportDecl,
DeclNameLoc(loc), /*Implicit=*/true);
@@ -594,23 +597,34 @@ synthesizeRemoteFuncStubBody(AbstractFunctionDecl *func, void *) {
assert(isa<ConstructorDecl>(className->getBuiltinInitializer().getDecl()));
className->setType(staticStringType);
auto *funcName = new (ctx) MagicIdentifierLiteralExpr(
MagicIdentifierLiteralExpr::Function, loc, /*Implicit=*/true);
auto *funcName = new (ctx) StringLiteralExpr(
ctx.AllocateCopy(func->getName().getBaseName().getIdentifier().str()), loc,
/*Implicit=*/true);
funcName->setType(staticStringType);
funcName->setBuiltinInitializer(staticStringInit);
auto *file = new (ctx) MagicIdentifierLiteralExpr(
MagicIdentifierLiteralExpr::FileID, loc, /*Implicit=*/true);
// Note: Sadly we cannot just rely on #function, #file, #line for the location
// (MagicIdentifierLiteralExpr), of the call because the call is made from a thunk.
// That thunk does not carry those info today although it could.
//
// Instead, we offer the location where the distributed func was declared.
auto fileString = SM.getDisplayNameForLoc(distributedFunc->getStartLoc());
auto *file = new (ctx) StringLiteralExpr(fileString, loc, /*Implicit=*/true);
file->setType(staticStringType);
file->setBuiltinInitializer(staticStringInit);
auto *line = new (ctx) MagicIdentifierLiteralExpr(
MagicIdentifierLiteralExpr::Line, loc, /*Implicit=*/true);
auto startLineAndCol = SM.getPresumedLineAndColumnForLoc(distributedFunc->getStartLoc());
// auto *line = new (ctx) MagicIdentifierLiteralExpr(
// MagicIdentifierLiteralExpr::Line, loc, /*Implicit=*/true);
// auto *line = new (ctx) IntegerLiteralExpr(startLineAndCol.first, loc,
// /*implicit*/ true);
auto *line = IntegerLiteralExpr::createFromUnsigned(ctx, startLineAndCol.first);
line->setType(uintType);
line->setBuiltinInitializer(uintInit);
auto *column = new (ctx) MagicIdentifierLiteralExpr(
MagicIdentifierLiteralExpr::Column, loc, /*Implicit=*/true);
// auto *column = new (ctx) MagicIdentifierLiteralExpr(
// MagicIdentifierLiteralExpr::Column, loc, /*Implicit=*/true);
auto *column = IntegerLiteralExpr::createFromUnsigned(ctx, startLineAndCol.second);
column->setType(uintType);
column->setBuiltinInitializer(uintInit);
@@ -624,9 +638,6 @@ synthesizeRemoteFuncStubBody(AbstractFunctionDecl *func, void *) {
// stmts.push_back(new (ctx) ReturnStmt(SourceLoc(), /*Result=*/nullptr)); // FIXME: this causes 'different types for return type: String vs. ()'
auto body = BraceStmt::create(ctx, SourceLoc(), stmts, SourceLoc(),
/*implicit=*/true);
return { body, /*isTypeChecked=*/true };
}
@@ -680,7 +691,7 @@ static void addImplicitRemoteActorFunction(ClassDecl *decl, FuncDecl *func) {
remoteFuncDecl->setUserAccessible(false);
remoteFuncDecl->setSynthesized();
remoteFuncDecl->setBodySynthesizer(&synthesizeRemoteFuncStubBody);
remoteFuncDecl->setBodySynthesizer(&synthesizeRemoteFuncStubBody, func);
// same access control as the original function is fine
remoteFuncDecl->copyFormalAccessFrom(func, /*sourceIsParentContext=*/false);

View File

@@ -11,260 +11,23 @@
//===----------------------------------------------------------------------===//
import Swift
import _Concurrency
/// Common protocol to which all distributed actors conform implicitly.
///
/// It is not possible to conform to this protocol manually explicitly.
/// Only a 'distributed actor' declaration or protocol with 'DistributedActor'
/// requirement may conform to this protocol.
///
/// The 'DistributedActor' protocol provides the core functionality of any
/// distributed actor, which involves transforming actor
/// which involves enqueuing new partial tasks to be executed at some
/// point.
@available(SwiftStdlib 5.5, *)
public protocol DistributedActor: Actor, Codable {
/// Creates new (local) distributed actor instance, bound to the passed transport.
///
/// Upon initialization, the `actorAddress` field is populated by the transport,
/// with an address assigned to this actor.
///
/// - Parameter transport: the transport this distributed actor instance will
/// associated with.
init(transport: ActorTransport)
/// Resolves the passed in `address` against the `transport`,
/// returning either a local or remote actor reference.
///
/// The transport will be asked to `resolve` the address and return either
/// a local instance or determine that a proxy instance should be created
/// for this address. A proxy actor will forward all invocations through
/// the transport, allowing it to take over the remote messaging with the
/// remote actor instance.
///
/// - Parameter address: the address to resolve, and produce an instance or proxy for.
/// - Parameter transport: transport which should be used to resolve the `address`.
init(resolve address: ActorAddress, using transport: ActorTransport) throws
/// The `ActorTransport` associated with this actor.
/// It is immutable and equal to the transport passed in the local/resolve
/// initializer.
///
/// Conformance to this requirement is synthesized automatically for any
/// `distributed actor` declaration.
nonisolated var actorTransport: ActorTransport { get }
/// Logical address which this distributed actor represents.
///
/// An address is always uniquely pointing at a specific actor instance.
///
/// Conformance to this requirement is synthesized automatically for any
/// `distributed actor` declaration.
nonisolated var actorAddress: ActorAddress { get }
/// Report a call to a _remote function on a distributed (remote) actor,
/// that was not dynamically replaced by some specific ActorTransport library.
@_transparent
public func _missingDistributedActorTransport(
className: StaticString, functionName: StaticString,
file: StaticString, line: UInt, column: UInt
) -> Never {
// This function is marked @_transparent so that it is inlined into the caller
// (the remote function stub), and, depending on the build configuration,
// redundant parameter values (#file etc.) are eliminated, and don't leak
// information about the user's source.
fatalError(
"""
Invoked remote placeholder function '\(functionName)' on remote \
distributed actor of type '\(className)'. Configure an appropriate \
'ActorTransport' for this actor to resolve this error (e.g. by depending \
on some specific transport library).
""", file: file, line: line)
}
// ==== Codable conformance ----------------------------------------------------
extension CodingUserInfoKey {
@available(SwiftStdlib 5.5, *)
static let actorTransportKey = CodingUserInfoKey(rawValue: "$dist_act_trans")!
}
@available(SwiftStdlib 5.5, *)
extension DistributedActor {
nonisolated public init(from decoder: Decoder) throws {
// guard let transport = decoder.userInfo[.actorTransportKey] as? ActorTransport else {
// throw DistributedActorCodingError(message:
// "ActorTransport not available under the decoder.userInfo")
// }
//
// var container = try decoder.singleValueContainer()
// let address = try container.decode(ActorAddress.self)
// self = try Self(resolve: address, using: transport) // FIXME: This is going to be solved by the init() work!!!!
fatalError("\(#function) is not implemented yet for distributed actors'")
}
nonisolated public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(self.actorAddress)
}
}
/******************************************************************************/
/***************************** Actor Transport ********************************/
/******************************************************************************/
@available(SwiftStdlib 5.5, *)
public protocol ActorTransport: Sendable {
/// Resolve a local or remote actor address to a real actor instance, or throw if unable to.
/// The returned value is either a local actor or proxy to a remote actor.
func resolve<Act>(address: ActorAddress, as actorType: Act.Type)
throws -> ActorResolved<Act> where Act: DistributedActor
/// Create an `ActorAddress` 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
/// lifetime.
///
/// The address MUST uniquely identify the actor, and allow resolving it.
/// E.g. if an actor is created under address `addr1` then immediately invoking
/// `transport.resolve(address: addr1, as: Greeter.self)` MUST return a reference
/// to the same actor.
func assignAddress<Act>(
_ actorType: Act.Type
) -> ActorAddress
where Act: DistributedActor
func actorReady<Act>(
_ actor: Act
) where Act: DistributedActor
/// Called during actor deinit/destroy.
func resignAddress(
_ address: ActorAddress
)
}
@available(SwiftStdlib 5.5, *)
public enum ActorResolved<Act: DistributedActor> {
case resolved(Act)
case makeProxy
}
/******************************************************************************/
/***************************** Actor Address **********************************/
/******************************************************************************/
/// Uniquely identifies a distributed actor, and enables sending messages even to remote actors.
///
/// ## Identity
/// The address is the source of truth with regards to referring to a _specific_ actor in the system.
/// This is in contrast to an `ActorPath` which can be thought of as paths in a filesystem, however without any uniqueness
/// or identity guarantees about the files those paths point to.
///
/// ## Lifecycle
/// Note, that an ActorAddress is a pure value, and as such does not "participate" in an actors lifecycle;
/// Thus, it may represent an address of an actor that has already terminated, so attempts to locate (resolve)
/// an `ActorRef` for this address may result with a reference to dead letters (meaning, that the actor this address
/// had pointed to does not exist, and most likely is dead / terminated).
///
/// ## Serialization
///
/// An address can be serialized using `Codable` or other serialization mechanisms.
/// When shared over the network or with other processes it must include the origin's
/// system address (e.g. the network address of the host, or process identifier).
///
/// When using `Codable` serialization this is done automatically, by looking up
/// the address of the `ActorTransport` the actor is associated with if was a local
/// instance, or simply carrying the full address if it already was a remote reference.
///
/// ## Format
/// The address consists of the following parts:
///
/// ```
/// | node | path | incarnation |
/// ( protocol | name? | host | port ) ( [segments] name )? ( uint32 )
/// ```
///
/// For example: `sact://human-readable-name@127.0.0.1:7337/user/wallet/id-121242`.
/// Note that the `ActorIncarnation` is not printed by default in the String representation of a path, yet may be inspected on demand.
@available(SwiftStdlib 5.5, *)
public struct ActorAddress: Codable, Sendable, Equatable, Hashable {
/// Uniquely specifies the actor transport and the protocol used by it.
///
/// E.g. "xpc", "specific-clustering-protocol" etc.
public var `protocol`: String
public var host: String?
public var port: Int?
public var nodeID: UInt64?
public var path: String?
/// Unique Identifier of this actor.
public var uid: UInt64 // TODO: should we remove this
// FIXME: remove this or implement for real; this is just a hack implementation for now
public init(parse: String) {
self.protocol = "sact"
self.host = "xxx"
self.port = 7337
self.nodeID = 11
self.path = "example"
self.uid = 123123
}
}
// TODO: naive impl, bring in a real one
@available(SwiftStdlib 5.5, *)
extension ActorAddress: CustomStringConvertible {
public var description: String {
var result = `protocol`
result += "://"
if let host = host {
result += host
}
if let port = port {
result += ":\(port)"
}
// TODO: decide if we'd want to print the nodeID too here.
if let path = path {
result += "/\(path)"
}
if uid > 0 {
result += "#\(uid)"
}
return result
}
}
/******************************************************************************/
/******************************** Misc ****************************************/
/******************************************************************************/
/// Error protocol to which errors thrown by any `ActorTransport` should conform.
@available(SwiftStdlib 5.5, *)
public protocol ActorTransportError: Error {}
@available(SwiftStdlib 5.5, *)
public struct DistributedActorCodingError: ActorTransportError {
public let message: String
public init(message: String) {
self.message = message
}
public static func missingTransportUserInfo<Act>(_ actorType: Act.Type) -> Self
where Act: DistributedActor {
.init(message: "Missing ActorTransport userInfo while decoding")
}
}
/******************************************************************************/
/************************* Runtime Functions **********************************/
/******************************************************************************/
// ==== isRemote / isLocal -----------------------------------------------------
@_silgen_name("swift_distributed_actor_is_remote")
func __isRemoteActor(_ actor: AnyObject) -> Bool
func __isLocalActor(_ actor: AnyObject) -> Bool {
return !__isRemoteActor(actor)
}
// ==== Proxy Actor lifecycle --------------------------------------------------
/// Called to initialize the distributed-remote actor 'proxy' instance in an actor.
/// The implementation will call this within the actor's initializer.
@_silgen_name("swift_distributedActor_remote_initialize")
func _distributedActorRemoteInitialize(_ actor: AnyObject)
/// Called to destroy the default actor instance in an actor.
/// The implementation will call this within the actor's deinit.
///
/// This will call `actorTransport.resignAddress(self.actorAddress)`.
@_silgen_name("swift_distributedActor_destroy")
func _distributedActorDestroy(_ actor: AnyObject)

View File

@@ -1,8 +0,0 @@
//
// Created by Konrad Malawski on 2021/06/07.
//
#ifndef SWIFT_ASSERTIONREPORTINGDISTRIBUTED_H
#define SWIFT_ASSERTIONREPORTINGDISTRIBUTED_H
#endif //SWIFT_ASSERTIONREPORTINGDISTRIBUTED_H

View File

@@ -14,6 +14,7 @@ set(swift_distributed_link_libraries
swiftCore)
add_swift_target_library(swift_Distributed ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB
AssertDistributed.swift
DistributedActor.swift
SWIFT_MODULE_DEPENDS_LINUX Glibc

View File

@@ -68,21 +68,6 @@ void _swift_stdlib_reportUnimplementedInitializer(
const unsigned char *initName, int initNameLength,
__swift_uint32_t flags);
/// Report a call to a _remote function on a distributed (remote) actor,
/// that was not dynamically replaced by some specific ActorTransport library.
///
/// <file>: <line>: <column>: Fatal error: remote function 'greet(name:)'
/// invoked on remote distributed actor reference of type 'Greeter'.
/// Configure an appropriate 'ActorTransport' for this actor to resolve
/// this error (e.g. by depending on some specific ActorTransport library)
SWIFT_RUNTIME_STDLIB_API
void _swift_stdlib_reportMissingDistributedActorTransport(
const unsigned char *className, int classNameLength,
const unsigned char *funcName, int funcNameLength,
const unsigned char *file, int fileLength,
__swift_uint32_t line, __swift_uint32_t column,
__swift_uint32_t flags);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@@ -222,36 +222,6 @@ func _unimplementedInitializer(className: StaticString,
Builtin.int_trap()
}
/// Prints a fatal error message when a distributed remote actor function is invoked
/// without a transport having dynamically replaced its _remote_ function implementation.
@_transparent
public // COMPILER_INTRINSIC
func _missingDistributedActorTransport(className: StaticString,
functionName: StaticString = #function,
file: StaticString = #file,
line: UInt = #line,
column: UInt = #column
) -> Never {
// This function is marked @_transparent so that it is inlined into the caller
// (the initializer stub), and, depending on the build configuration,
// redundant parameter values (#file etc.) are eliminated, and don't leak
// information about the user's source.
className.withUTF8Buffer { className in
functionName.withUTF8Buffer { functionName in
file.withUTF8Buffer { file in
_swift_stdlib_reportMissingDistributedActorTransport(
className.baseAddress!, CInt(className.count),
functionName.baseAddress!, CInt(functionName.count),
file.baseAddress!, CInt(file.count),
UInt32(line), UInt32(column),
/*flags:*/ 0)
}
}
}
Builtin.int_trap()
}
public // COMPILER_INTRINSIC
func _undefined<T>(
_ message: @autoclosure () -> String = String(),

View File

@@ -117,25 +117,3 @@ void _swift_stdlib_reportUnimplementedInitializer(
free(log);
}
void _swift_stdlib_reportMissingDistributedActorTransport(
const unsigned char *className, int classNameLength,
const unsigned char *funcName, int funcNameLength,
const unsigned char *file, int fileLength,
uint32_t line, uint32_t column,
uint32_t flags
) {
char *log;
swift_asprintf(
&log,
"%.*s:%" PRIu32 ": Fatal error: remote function '%.*s' invoked on remote "
"distributed actor reference of type '%.*s'. Configure an appropriate "
"'ActorTransport' for this actor to resolve this error (e.g. by depending "
"on some specific ActorTransport library)\n",
fileLength, file,
line,
funcNameLength, funcName,
classNameLength, className);
swift_reportError(flags, log);
free(log);
}

View File

@@ -0,0 +1,63 @@
// RUN: %target-fail-simple-swift(-Xfrontend -enable-experimental-distributed -parse-as-library %import-libdispatch) 2>&1 | %FileCheck %s
//
// // TODO: could not figure out how to use 'not --crash' it never is used with target-run-simple-swift
// This test is intended to *crash*, so we're using target-fail-simple-swift
// which expects the exit code of the program to be non-zero;
// We then check stderr for the expected error message using filecheck as usual.
// REQUIRES: executable_test
// REQUIRES: concurrency
// REQUIRES: distributed
import _Distributed
@available(SwiftStdlib 5.5, *)
distributed actor SomeSpecificDistributedActor {
distributed func hello() async throws -> String {
"local impl"
}
}
// ==== Fake Transport ---------------------------------------------------------
@available(SwiftStdlib 5.5, *)
struct FakeTransport: ActorTransport {
func resolve<Act>(address: ActorAddress, as actorType: Act.Type)
throws -> ActorResolved<Act> where Act: DistributedActor {
return .makeProxy
}
func assignAddress<Act>(
_ actorType: Act.Type
) -> ActorAddress where Act : DistributedActor {
ActorAddress(parse: "")
}
public func actorReady<Act>(
_ actor: Act
) where Act: DistributedActor {}
public func resignAddress(
_ address: ActorAddress
) {}
}
// ==== Execute ----------------------------------------------------------------
@available(SwiftStdlib 5.5, *)
func test_remote() async {
let address = ActorAddress(parse: "")
let transport = FakeTransport()
let remote = try! SomeSpecificDistributedActor(resolve: address, using: transport)
_ = try! await remote.hello() // let it crash!
// CHECK: SOURCE_DIR/test/Distributed/Runtime/distributed_no_transport_boom.swift:16: Fatal error: Invoked remote placeholder function '_remote_hello' on remote distributed actor of type 'main.SomeSpecificDistributedActor'. Configure an appropriate 'ActorTransport' for this actor to resolve this error (e.g. by depending on some specific transport library).
}
@available(SwiftStdlib 5.5, *)
@main struct Main {
static func main() async {
await test_remote()
}
}