Skip to content

Commit cc2863c

Browse files
authored
Merge pull request #36878 from rjmccall/custom-executors
Introduce basic support for custom executors
2 parents 25c366d + 565198e commit cc2863c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1072
-303
lines changed

include/swift/ABI/Executor.h

Lines changed: 38 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -27,25 +27,42 @@ class AsyncContext;
2727
class AsyncTask;
2828
class DefaultActor;
2929
class Job;
30-
31-
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
32-
SWIFT_EXPORT_FROM(swift_Concurrency)
33-
Metadata* MainActorMetadata;
30+
class SerialExecutorWitnessTable;
3431

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

60+
// We future-proof the ABI here by masking the low bits off the
61+
// implementation pointer before using it as a witness table.
62+
enum: uintptr_t {
63+
WitnessTableMask = ~uintptr_t(alignof(void*) - 1)
64+
};
65+
4966
constexpr ExecutorRef(HeapObject *identity, uintptr_t implementation)
5067
: Identity(identity), Implementation(implementation) {}
5168

@@ -58,23 +75,11 @@ class ExecutorRef {
5875
return ExecutorRef(nullptr, 0);
5976
}
6077

61-
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
62-
/// NOTE: I didn't go with Executor::forMainActor(DefaultActor*) because
63-
/// __swift_run_job_main_executor can't take more than one argument.
64-
static ExecutorRef mainExecutor() {
65-
auto identity = getMainActorIdentity();
66-
return ExecutorRef(identity, 0);
67-
}
68-
static HeapObject *getMainActorIdentity() {
69-
return reinterpret_cast<HeapObject*>(
70-
ExecutorRefFlags::MainActorIdentity);
71-
}
72-
7378
/// Given a pointer to a default actor, return an executor reference
7479
/// for it.
7580
static ExecutorRef forDefaultActor(DefaultActor *actor) {
7681
assert(actor);
77-
return ExecutorRef(actor, unsigned(ExecutorRefFlags::DefaultActor));
82+
return ExecutorRef(actor, 0);
7883
}
7984

8085
HeapObject *getIdentity() const {
@@ -86,37 +91,29 @@ class ExecutorRef {
8691
return Identity == 0;
8792
}
8893

89-
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
90-
bool isMainExecutor() const {
91-
if (Identity == getMainActorIdentity())
92-
return true;
93-
94-
if (Identity == nullptr || MainActorMetadata == nullptr)
95-
return false;
96-
97-
Metadata const* metadata = swift_getObjectType(Identity);
98-
return metadata == MainActorMetadata;
99-
}
100-
10194
/// Is this a default-actor executor reference?
10295
bool isDefaultActor() const {
103-
return Implementation & unsigned(ExecutorRefFlags::DefaultActor);
96+
return !isGeneric() && Implementation == 0;
10497
}
10598
DefaultActor *getDefaultActor() const {
10699
assert(isDefaultActor());
107100
return reinterpret_cast<DefaultActor*>(Identity);
108101
}
109102

103+
const SerialExecutorWitnessTable *getSerialExecutorWitnessTable() const {
104+
assert(!isGeneric() && !isDefaultActor());
105+
auto table = Implementation & WitnessTableMask;
106+
return reinterpret_cast<const SerialExecutorWitnessTable*>(table);
107+
}
108+
110109
/// Do we have to do any work to start running as the requested
111110
/// executor?
112111
bool mustSwitchToRun(ExecutorRef newExecutor) const {
113112
return Identity != newExecutor.Identity;
114113
}
115114

116115
bool operator==(ExecutorRef other) const {
117-
return Identity == other.Identity
118-
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
119-
|| (isMainExecutor() && other.isMainExecutor());
116+
return Identity == other.Identity;
120117
}
121118
bool operator!=(ExecutorRef other) const {
122119
return !(*this == other);

include/swift/ABI/MetadataValues.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2199,20 +2199,6 @@ enum class ContinuationStatus : size_t {
21992199
Resumed = 2
22002200
};
22012201

2202-
/// Flags describing the executor implementation that are stored
2203-
/// in the ExecutorRef.
2204-
enum class ExecutorRefFlags : size_t {
2205-
// The number of bits available here is very limited because it's
2206-
// potentially just the alignment bits of a protocol witness table
2207-
// pointer
2208-
2209-
/// The executor is a default actor.
2210-
DefaultActor = 0x1,
2211-
2212-
/// TODO: remove this
2213-
MainActorIdentity = 0x2,
2214-
};
2215-
22162202
} // end namespace swift
22172203

22182204
#endif // SWIFT_ABI_METADATAVALUES_H

include/swift/AST/ASTSynthesis.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ enum SingletonTypeSynthesizer {
4949
_rawUnsafeContinuation,
5050
_void,
5151
_word,
52+
_serialExecutor,
5253
};
5354
inline Type synthesizeType(SynthesisContext &SC,
5455
SingletonTypeSynthesizer kind) {
@@ -65,6 +66,9 @@ inline Type synthesizeType(SynthesisContext &SC,
6566
case _void: return SC.Context.TheEmptyTupleType;
6667
case _word: return BuiltinIntegerType::get(BuiltinIntegerWidth::pointer(),
6768
SC.Context);
69+
case _serialExecutor:
70+
return SC.Context.getProtocol(KnownProtocolKind::SerialExecutor)
71+
->getDeclaredInterfaceType();
6872
}
6973
}
7074

include/swift/AST/Attr.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,8 @@ class AvailableAttr : public DeclAttribute {
743743
llvm::VersionTuple Obsoleted
744744
= llvm::VersionTuple());
745745

746+
AvailableAttr *clone(ASTContext &C, bool implicit) const;
747+
746748
static bool classof(const DeclAttribute *DA) {
747749
return DA->getKind() == DAK_Available;
748750
}

include/swift/AST/Builtins.def

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -736,10 +736,6 @@ BUILTIN_MISC_OPERATION(ResumeThrowingContinuationReturning,
736736
BUILTIN_MISC_OPERATION(ResumeThrowingContinuationThrowing,
737737
"resumeThrowingContinuationThrowing", "", Special)
738738

739-
/// Build a Builtin.Executor value from the given serial executor reference.
740-
BUILTIN_MISC_OPERATION(BuildSerialExecutorRef,
741-
"buildSerialExecutorRef", "", Special)
742-
743739
/// Create a task group.
744740
BUILTIN_MISC_OPERATION(CreateTaskGroup,
745741
"createTaskGroup", "", Special)
@@ -761,11 +757,11 @@ BUILTIN_MISC_OPERATION(DestroyTaskGroup,
761757
BUILTIN_MISC_OPERATION(Id, Name, Attrs, Overload)
762758
#endif
763759

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

771767
// getCurrentAsyncTask: () -> Builtin.NativeObject
@@ -835,6 +831,19 @@ BUILTIN_MISC_OPERATION_WITH_SILGEN(AutoDiffProjectTopLevelSubcontext, "autoDiffP
835831
// autoDiffAllocateSubcontext: (Builtin.NativeObject, Builtin.Word) -> Builtin.RawPointer
836832
BUILTIN_MISC_OPERATION_WITH_SILGEN(AutoDiffAllocateSubcontext, "autoDiffAllocateSubcontext", "", Special)
837833

834+
/// Build a Builtin.Executor value from an "ordinary" serial executor
835+
/// reference.
836+
BUILTIN_MISC_OPERATION_WITH_SILGEN(BuildOrdinarySerialExecutorRef,
837+
"buildOrdinarySerialExecutorRef", "n", Special)
838+
839+
/// Build a Builtin.Executor value from a default actor reference.
840+
BUILTIN_MISC_OPERATION_WITH_SILGEN(BuildDefaultActorExecutorRef,
841+
"buildDefaultActorExecutorRef", "n", Special)
842+
843+
/// Build a Builtin.Executor value for the main actor.
844+
BUILTIN_MISC_OPERATION_WITH_SILGEN(BuildMainActorExecutorRef,
845+
"buildMainActorExecutorRef", "n", Special)
846+
838847
#undef BUILTIN_MISC_OPERATION_WITH_SILGEN
839848

840849
#undef BUILTIN_MISC_OPERATION

include/swift/AST/Decl.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3778,9 +3778,8 @@ class ClassDecl final : public NominalTypeDecl {
37783778
/// Get the closest-to-root superclass that's an actor class.
37793779
const ClassDecl *getRootActorClass() const;
37803780

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

37853784
/// Is this the NSObject class type?
37863785
bool isNSObject() const;
@@ -4006,6 +4005,7 @@ enum class KnownDerivableProtocolKind : uint8_t {
40064005
Decodable,
40074006
AdditiveArithmetic,
40084007
Differentiable,
4008+
Actor,
40094009
};
40104010

40114011
/// ProtocolDecl - A declaration of a protocol, for example:

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4357,6 +4357,13 @@ ERROR(async_objc_dynamic_self,none,
43574357
ERROR(actor_inheritance,none,
43584358
"actor types do not support inheritance", ())
43594359

4360+
ERROR(unowned_executor_outside_actor,none,
4361+
"'unownedExecutor' can only be implemented within the main "
4362+
"definition of an actor", ())
4363+
ERROR(override_implicit_unowned_executor,none,
4364+
"cannot override an actor's 'unownedExecutor' property that wasn't "
4365+
"explicitly defined", ())
4366+
43604367
ERROR(actor_isolated_non_self_reference,none,
43614368
"actor-isolated %0 %1 can only be %select{referenced|mutated|used 'inout'}3 "
43624369
"%select{from inside the actor|on 'self'}2",

include/swift/AST/KnownIdentifiers.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ IDENTIFIER(className)
218218

219219
IDENTIFIER(_defaultActorInitialize)
220220
IDENTIFIER(_defaultActorDestroy)
221+
IDENTIFIER(unownedExecutor)
221222

222223
IDENTIFIER_(ErrorType)
223224
IDENTIFIER(Code)

include/swift/AST/KnownProtocols.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ PROTOCOL(CaseIterable)
7272
PROTOCOL(SIMDScalar)
7373
PROTOCOL(BinaryInteger)
7474
PROTOCOL(RangeReplaceableCollection)
75+
PROTOCOL(SerialExecutor)
7576

7677
PROTOCOL_(BridgedNSError)
7778
PROTOCOL_(BridgedStoredNSError)

include/swift/AST/KnownSDKTypes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ KNOWN_SDK_TYPE_DECL(ObjectiveC, ObjCBool, StructDecl, 0)
3939
// standardized
4040
KNOWN_SDK_TYPE_DECL(Concurrency, UnsafeContinuation, NominalTypeDecl, 2)
4141
KNOWN_SDK_TYPE_DECL(Concurrency, MainActor, NominalTypeDecl, 0)
42+
KNOWN_SDK_TYPE_DECL(Concurrency, UnownedSerialExecutor, NominalTypeDecl, 0)
4243

4344
KNOWN_SDK_TYPE_DECL(Concurrency, TaskLocal, ClassDecl, 1)
4445

include/swift/Basic/Features.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,10 @@ LANGUAGE_FEATURE(RethrowsProtocol, 0, "@rethrows protocol", true)
4343
LANGUAGE_FEATURE(GlobalActors, 0, "Global actors", langOpts.EnableExperimentalConcurrency)
4444
LANGUAGE_FEATURE(BuiltinJob, 0, "Builtin.Job type", true)
4545
LANGUAGE_FEATURE(Sendable, 0, "Sendable and @Sendable", true)
46-
LANGUAGE_FEATURE(BuiltinExecutor, 0, "Executor builtins", true)
46+
LANGUAGE_FEATURE(BuiltinExecutor, 0, "Builtin.Executor type", true)
4747
LANGUAGE_FEATURE(BuiltinContinuation, 0, "Continuation builtins", true)
4848
LANGUAGE_FEATURE(BuiltinTaskGroup, 0, "TaskGroup builtins", true)
4949
LANGUAGE_FEATURE(InheritActorContext, 0, "@_inheritActorContext attribute", true)
50+
LANGUAGE_FEATURE(BuiltinBuildExecutor, 0, "Executor-building builtins", true)
5051

5152
#undef LANGUAGE_FEATURE

include/swift/Runtime/Concurrency.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -547,14 +547,10 @@ void swift_task_enqueueGlobal(Job *job);
547547
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
548548
void swift_task_enqueueGlobalWithDelay(unsigned long long delay, Job *job);
549549

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

554-
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
555-
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
556-
void swift_MainActor_register(HeapObject *actor);
557-
558554
/// A hook to take over global enqueuing.
559555
typedef SWIFT_CC(swift) void (*swift_task_enqueueGlobal_original)(Job *job);
560556
SWIFT_EXPORT_FROM(swift_Concurrency)

include/swift/Strings.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ constexpr static const StringLiteral CXX_TEMPLATE_INST_PREFIX =
4747
constexpr static const StringLiteral SEMANTICS_PROGRAMTERMINATION_POINT =
4848
"programtermination_point";
4949

50+
constexpr static const StringLiteral SEMANTICS_DEFAULT_ACTOR =
51+
"defaultActor";
52+
5053
constexpr static const StringLiteral DEFAULT_ACTOR_STORAGE_FIELD_NAME =
5154
"$defaultActor";
5255

lib/AST/ASTContext.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,6 +1002,7 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
10021002
case KnownProtocolKind::Actor:
10031003
case KnownProtocolKind::AsyncSequence:
10041004
case KnownProtocolKind::AsyncIteratorProtocol:
1005+
case KnownProtocolKind::SerialExecutor:
10051006
M = getLoadedModule(Id_Concurrency);
10061007
break;
10071008
default:

0 commit comments

Comments
 (0)