Skip to content

Introduce basic support for custom executors #36878

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 38 additions & 41 deletions include/swift/ABI/Executor.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,42 @@ class AsyncContext;
class AsyncTask;
class DefaultActor;
class Job;

/// FIXME: only exists for the quick-and-dirty MainActor implementation.
SWIFT_EXPORT_FROM(swift_Concurrency)
Metadata* MainActorMetadata;
class SerialExecutorWitnessTable;

/// An unmanaged reference to an executor.
///
/// The representation is two words: identity and implementation.
/// The identity word is a reference to the executor object; for
/// default actors, this is the actor object. The implementation
/// word describes how the executor works; it carries a witness table
/// as well as a small number of bits indicating various special
/// implementation properties. As an exception to both of these
/// rules, a null identity represents a generic executor and
/// implies a null implementation word.
/// This type corresponds to the type Optional<Builtin.Executor> in
/// Swift. The representation of nil in Optional<Builtin.Executor>
/// aligns with what this type calls the generic executor, so the
/// notional subtype of this type which is never generic corresponds
/// to the type Builtin.Executor.
///
/// An executor reference is divided into two pieces:
///
/// - The identity, which is just a (potentially ObjC) object
/// reference; when this is null, the reference is generic.
/// Equality of executor references is based solely on equality
/// of identity.
///
/// - The implementation, which is an optional reference to a
/// witness table for the SerialExecutor protocol. When this
/// is null, but the identity is non-null, the reference is to
/// a default actor. The low bits of the implementation pointer
/// are reserved for the use of marking interesting properties
/// about the executor's implementation. The runtime masks these
/// bits off before accessing the witness table, so setting them
/// in the future should back-deploy as long as the witness table
/// reference is still present.
class ExecutorRef {
HeapObject *Identity; // Not necessarily Swift reference-countable
uintptr_t Implementation;

// We future-proof the ABI here by masking the low bits off the
// implementation pointer before using it as a witness table.
enum: uintptr_t {
WitnessTableMask = ~uintptr_t(alignof(void*) - 1)
};

constexpr ExecutorRef(HeapObject *identity, uintptr_t implementation)
: Identity(identity), Implementation(implementation) {}

Expand All @@ -58,23 +75,11 @@ class ExecutorRef {
return ExecutorRef(nullptr, 0);
}

/// FIXME: only exists for the quick-and-dirty MainActor implementation.
/// NOTE: I didn't go with Executor::forMainActor(DefaultActor*) because
/// __swift_run_job_main_executor can't take more than one argument.
static ExecutorRef mainExecutor() {
auto identity = getMainActorIdentity();
return ExecutorRef(identity, 0);
}
static HeapObject *getMainActorIdentity() {
return reinterpret_cast<HeapObject*>(
ExecutorRefFlags::MainActorIdentity);
}

/// Given a pointer to a default actor, return an executor reference
/// for it.
static ExecutorRef forDefaultActor(DefaultActor *actor) {
assert(actor);
return ExecutorRef(actor, unsigned(ExecutorRefFlags::DefaultActor));
return ExecutorRef(actor, 0);
}

HeapObject *getIdentity() const {
Expand All @@ -86,37 +91,29 @@ class ExecutorRef {
return Identity == 0;
}

/// FIXME: only exists for the quick-and-dirty MainActor implementation.
bool isMainExecutor() const {
if (Identity == getMainActorIdentity())
return true;

if (Identity == nullptr || MainActorMetadata == nullptr)
return false;

Metadata const* metadata = swift_getObjectType(Identity);
return metadata == MainActorMetadata;
}

/// Is this a default-actor executor reference?
bool isDefaultActor() const {
return Implementation & unsigned(ExecutorRefFlags::DefaultActor);
return !isGeneric() && Implementation == 0;
}
DefaultActor *getDefaultActor() const {
assert(isDefaultActor());
return reinterpret_cast<DefaultActor*>(Identity);
}

const SerialExecutorWitnessTable *getSerialExecutorWitnessTable() const {
assert(!isGeneric() && !isDefaultActor());
auto table = Implementation & WitnessTableMask;
return reinterpret_cast<const SerialExecutorWitnessTable*>(table);
}

/// Do we have to do any work to start running as the requested
/// executor?
bool mustSwitchToRun(ExecutorRef newExecutor) const {
return Identity != newExecutor.Identity;
}

bool operator==(ExecutorRef other) const {
return Identity == other.Identity
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
|| (isMainExecutor() && other.isMainExecutor());
return Identity == other.Identity;
}
bool operator!=(ExecutorRef other) const {
return !(*this == other);
Expand Down
14 changes: 0 additions & 14 deletions include/swift/ABI/MetadataValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -2187,20 +2187,6 @@ enum class ContinuationStatus : size_t {
Resumed = 2
};

/// Flags describing the executor implementation that are stored
/// in the ExecutorRef.
enum class ExecutorRefFlags : size_t {
// The number of bits available here is very limited because it's
// potentially just the alignment bits of a protocol witness table
// pointer

/// The executor is a default actor.
DefaultActor = 0x1,

/// TODO: remove this
MainActorIdentity = 0x2,
};

} // end namespace swift

#endif // SWIFT_ABI_METADATAVALUES_H
4 changes: 4 additions & 0 deletions include/swift/AST/ASTSynthesis.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ enum SingletonTypeSynthesizer {
_rawUnsafeContinuation,
_void,
_word,
_serialExecutor,
};
inline Type synthesizeType(SynthesisContext &SC,
SingletonTypeSynthesizer kind) {
Expand All @@ -65,6 +66,9 @@ inline Type synthesizeType(SynthesisContext &SC,
case _void: return SC.Context.TheEmptyTupleType;
case _word: return BuiltinIntegerType::get(BuiltinIntegerWidth::pointer(),
SC.Context);
case _serialExecutor:
return SC.Context.getProtocol(KnownProtocolKind::SerialExecutor)
->getDeclaredInterfaceType();
}
}

Expand Down
2 changes: 2 additions & 0 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,8 @@ class AvailableAttr : public DeclAttribute {
llvm::VersionTuple Obsoleted
= llvm::VersionTuple());

AvailableAttr *clone(ASTContext &C, bool implicit) const;

static bool classof(const DeclAttribute *DA) {
return DA->getKind() == DAK_Available;
}
Expand Down
23 changes: 16 additions & 7 deletions include/swift/AST/Builtins.def
Original file line number Diff line number Diff line change
Expand Up @@ -736,10 +736,6 @@ BUILTIN_MISC_OPERATION(ResumeThrowingContinuationReturning,
BUILTIN_MISC_OPERATION(ResumeThrowingContinuationThrowing,
"resumeThrowingContinuationThrowing", "", Special)

/// Build a Builtin.Executor value from the given serial executor reference.
BUILTIN_MISC_OPERATION(BuildSerialExecutorRef,
"buildSerialExecutorRef", "", Special)

/// Create a task group.
BUILTIN_MISC_OPERATION(CreateTaskGroup,
"createTaskGroup", "", Special)
Expand All @@ -761,11 +757,11 @@ BUILTIN_MISC_OPERATION(DestroyTaskGroup,
BUILTIN_MISC_OPERATION(Id, Name, Attrs, Overload)
#endif

// getCurrentExecutor: () async -> Builtin.Executor
// getCurrentExecutor: () async -> Builtin.Executor?
//
// Retrieve the ExecutorRef on which the current asynchronous
// function is executing.
// Does not retain an actor executor.
// function is executing, or nil if the function isn't running
// anywhere in particular.
BUILTIN_MISC_OPERATION_WITH_SILGEN(GetCurrentExecutor, "getCurrentExecutor", "", Special)

// getCurrentAsyncTask: () -> Builtin.NativeObject
Expand Down Expand Up @@ -835,6 +831,19 @@ BUILTIN_MISC_OPERATION_WITH_SILGEN(AutoDiffProjectTopLevelSubcontext, "autoDiffP
// autoDiffAllocateSubcontext: (Builtin.NativeObject, Builtin.Word) -> Builtin.RawPointer
BUILTIN_MISC_OPERATION_WITH_SILGEN(AutoDiffAllocateSubcontext, "autoDiffAllocateSubcontext", "", Special)

/// Build a Builtin.Executor value from an "ordinary" serial executor
/// reference.
BUILTIN_MISC_OPERATION_WITH_SILGEN(BuildOrdinarySerialExecutorRef,
"buildOrdinarySerialExecutorRef", "n", Special)

/// Build a Builtin.Executor value from a default actor reference.
BUILTIN_MISC_OPERATION_WITH_SILGEN(BuildDefaultActorExecutorRef,
"buildDefaultActorExecutorRef", "n", Special)

/// Build a Builtin.Executor value for the main actor.
BUILTIN_MISC_OPERATION_WITH_SILGEN(BuildMainActorExecutorRef,
"buildMainActorExecutorRef", "n", Special)

#undef BUILTIN_MISC_OPERATION_WITH_SILGEN

#undef BUILTIN_MISC_OPERATION
Expand Down
6 changes: 3 additions & 3 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3778,9 +3778,8 @@ class ClassDecl final : public NominalTypeDecl {
/// Get the closest-to-root superclass that's an actor class.
const ClassDecl *getRootActorClass() const;

/// Does this class explicitly declare any of the methods that
/// would prevent it from being a default actor?
bool hasExplicitCustomActorMethods() const;
/// Fetch this class's unownedExecutor property, if it has one.
const VarDecl *getUnownedExecutorProperty() const;

/// Is this the NSObject class type?
bool isNSObject() const;
Expand Down Expand Up @@ -4006,6 +4005,7 @@ enum class KnownDerivableProtocolKind : uint8_t {
Decodable,
AdditiveArithmetic,
Differentiable,
Actor,
};

/// ProtocolDecl - A declaration of a protocol, for example:
Expand Down
7 changes: 7 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -4354,6 +4354,13 @@ ERROR(async_objc_dynamic_self,none,
ERROR(actor_inheritance,none,
"actor types do not support inheritance", ())

ERROR(unowned_executor_outside_actor,none,
"'unownedExecutor' can only be implemented within the main "
"definition of an actor", ())
ERROR(override_implicit_unowned_executor,none,
"cannot override an actor's 'unownedExecutor' property that wasn't "
"explicitly defined", ())

ERROR(actor_isolated_non_self_reference,none,
"actor-isolated %0 %1 can only be %select{referenced|mutated|used 'inout'}3 "
"%select{from inside the actor|on 'self'}2",
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/KnownIdentifiers.def
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ IDENTIFIER(className)

IDENTIFIER(_defaultActorInitialize)
IDENTIFIER(_defaultActorDestroy)
IDENTIFIER(unownedExecutor)

IDENTIFIER_(ErrorType)
IDENTIFIER(Code)
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/KnownProtocols.def
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ PROTOCOL(CaseIterable)
PROTOCOL(SIMDScalar)
PROTOCOL(BinaryInteger)
PROTOCOL(RangeReplaceableCollection)
PROTOCOL(SerialExecutor)

PROTOCOL_(BridgedNSError)
PROTOCOL_(BridgedStoredNSError)
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/KnownSDKTypes.def
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ KNOWN_SDK_TYPE_DECL(ObjectiveC, ObjCBool, StructDecl, 0)
// standardized
KNOWN_SDK_TYPE_DECL(Concurrency, UnsafeContinuation, NominalTypeDecl, 2)
KNOWN_SDK_TYPE_DECL(Concurrency, MainActor, NominalTypeDecl, 0)
KNOWN_SDK_TYPE_DECL(Concurrency, UnownedSerialExecutor, NominalTypeDecl, 0)

KNOWN_SDK_TYPE_DECL(Concurrency, TaskLocal, ClassDecl, 1)

Expand Down
3 changes: 2 additions & 1 deletion include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ LANGUAGE_FEATURE(RethrowsProtocol, 0, "@rethrows protocol", true)
LANGUAGE_FEATURE(GlobalActors, 0, "Global actors", langOpts.EnableExperimentalConcurrency)
LANGUAGE_FEATURE(BuiltinJob, 0, "Builtin.Job type", true)
LANGUAGE_FEATURE(Sendable, 0, "Sendable and @Sendable", true)
LANGUAGE_FEATURE(BuiltinExecutor, 0, "Executor builtins", true)
LANGUAGE_FEATURE(BuiltinExecutor, 0, "Builtin.Executor type", true)
LANGUAGE_FEATURE(BuiltinContinuation, 0, "Continuation builtins", true)
LANGUAGE_FEATURE(BuiltinTaskGroup, 0, "TaskGroup builtins", true)
LANGUAGE_FEATURE(InheritActorContext, 0, "@_inheritActorContext attribute", true)
LANGUAGE_FEATURE(BuiltinBuildExecutor, 0, "Executor-building builtins", true)

#undef LANGUAGE_FEATURE
6 changes: 1 addition & 5 deletions include/swift/Runtime/Concurrency.h
Original file line number Diff line number Diff line change
Expand Up @@ -547,14 +547,10 @@ void swift_task_enqueueGlobal(Job *job);
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_task_enqueueGlobalWithDelay(unsigned long long delay, Job *job);

/// FIXME: only exists for the quick-and-dirty MainActor implementation.
/// Enqueue the given job on the main executor.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_task_enqueueMainExecutor(Job *job);

/// FIXME: only exists for the quick-and-dirty MainActor implementation.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_MainActor_register(HeapObject *actor);

/// A hook to take over global enqueuing.
typedef SWIFT_CC(swift) void (*swift_task_enqueueGlobal_original)(Job *job);
SWIFT_EXPORT_FROM(swift_Concurrency)
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ constexpr static const StringLiteral CXX_TEMPLATE_INST_PREFIX =
constexpr static const StringLiteral SEMANTICS_PROGRAMTERMINATION_POINT =
"programtermination_point";

constexpr static const StringLiteral SEMANTICS_DEFAULT_ACTOR =
"defaultActor";

constexpr static const StringLiteral DEFAULT_ACTOR_STORAGE_FIELD_NAME =
"$defaultActor";

Expand Down
1 change: 1 addition & 0 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1002,6 +1002,7 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
case KnownProtocolKind::Actor:
case KnownProtocolKind::AsyncSequence:
case KnownProtocolKind::AsyncIteratorProtocol:
case KnownProtocolKind::SerialExecutor:
M = getLoadedModule(Id_Concurrency);
break;
default:
Expand Down
Loading