Skip to content

Commit 2c3a9cc

Browse files
authored
🍒[5.9][Concurrency] SerialExecutor.isSameExclusiveExecutionContext (#64665)
1 parent 2078cea commit 2c3a9cc

27 files changed

+472
-15
lines changed

include/swift/ABI/Executor.h

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,26 @@ class ExecutorRef {
5959

6060
// We future-proof the ABI here by masking the low bits off the
6161
// implementation pointer before using it as a witness table.
62+
//
63+
// We have 3 bits for future use remaining here.
6264
enum: uintptr_t {
6365
WitnessTableMask = ~uintptr_t(alignof(void*) - 1)
6466
};
6567

68+
/// The kind is stored in the free bits in the `Implementation` witness table reference.
69+
enum class ExecutorKind : uintptr_t {
70+
/// Ordinary executor.
71+
///
72+
/// Note that the "Generic" executor is also implicitly "Ordinary".
73+
/// To check if an executor is Generic, explicitly check this by calling `isGeneric`.
74+
Ordinary = 0b00,
75+
/// Executor that may need to participate in complex "same context" checks,
76+
/// by invoking `isSameExclusiveExecutionContext` when comparing execution contexts.
77+
ComplexEquality = 0b01,
78+
};
79+
80+
static_assert(static_cast<uintptr_t>(ExecutorKind::Ordinary) == 0);
81+
6682
constexpr ExecutorRef(HeapObject *identity, uintptr_t implementation)
6783
: Identity(identity), Implementation(implementation) {}
6884

@@ -89,7 +105,18 @@ class ExecutorRef {
89105
const SerialExecutorWitnessTable *witnessTable) {
90106
assert(identity);
91107
assert(witnessTable);
92-
return ExecutorRef(identity, reinterpret_cast<uintptr_t>(witnessTable));
108+
auto wtable = reinterpret_cast<uintptr_t>(witnessTable) |
109+
static_cast<uintptr_t>(ExecutorKind::Ordinary);
110+
return ExecutorRef(identity, wtable);
111+
}
112+
113+
static ExecutorRef forComplexEquality(HeapObject *identity,
114+
const SerialExecutorWitnessTable *witnessTable) {
115+
assert(identity);
116+
assert(witnessTable);
117+
auto wtable = reinterpret_cast<uintptr_t>(witnessTable) |
118+
static_cast<uintptr_t>(ExecutorKind::ComplexEquality);
119+
return ExecutorRef(identity, wtable);
93120
}
94121

95122
HeapObject *getIdentity() const {
@@ -101,6 +128,23 @@ class ExecutorRef {
101128
return Identity == 0;
102129
}
103130

131+
ExecutorKind getExecutorKind() const {
132+
return static_cast<ExecutorKind>(Implementation & ~WitnessTableMask);
133+
}
134+
135+
/// Is this an ordinary executor reference?
136+
/// These executor references are the default kind, and have no special treatment elsewhere in the system.
137+
bool isOrdinary() const {
138+
return getExecutorKind() == ExecutorKind::Ordinary;
139+
}
140+
141+
/// Is this an `complex-equality` executor reference?
142+
/// These executor references should implement `isSameExclusiveExecutionContext` which will be invoked
143+
/// when two executors are compared for being the same exclusive execution context.
144+
bool isComplexEquality() const {
145+
return getExecutorKind() == ExecutorKind::ComplexEquality;
146+
}
147+
104148
/// Is this a default-actor executor reference?
105149
bool isDefaultActor() const {
106150
return !isGeneric() && Implementation == 0;
@@ -126,7 +170,9 @@ class ExecutorRef {
126170
bool isMainExecutor() const;
127171

128172
/// Get the raw value of the Implementation field, for tracing.
129-
uintptr_t getRawImplementation() { return Implementation; }
173+
uintptr_t getRawImplementation() const {
174+
return Implementation & WitnessTableMask;
175+
}
130176

131177
bool operator==(ExecutorRef other) const {
132178
return Identity == other.Identity;

include/swift/AST/Builtins.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,11 @@ BUILTIN_MISC_OPERATION_WITH_SILGEN(AutoDiffAllocateSubcontext, "autoDiffAllocate
999999
BUILTIN_MISC_OPERATION_WITH_SILGEN(BuildOrdinarySerialExecutorRef,
10001000
"buildOrdinarySerialExecutorRef", "n", Special)
10011001

1002+
/// Build a Builtin.Executor value from an "complex equality" serial executor
1003+
/// reference.
1004+
BUILTIN_MISC_OPERATION_WITH_SILGEN(BuildComplexEqualitySerialExecutorRef,
1005+
"buildComplexEqualitySerialExecutorRef", "n", Special)
1006+
10021007
/// Build a Builtin.Executor value from a default actor reference.
10031008
BUILTIN_MISC_OPERATION_WITH_SILGEN(BuildDefaultActorExecutorRef,
10041009
"buildDefaultActorExecutorRef", "n", Special)

include/swift/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ LANGUAGE_FEATURE(BuiltinTaskGroupWithArgument, 0, "TaskGroup builtins", true)
7878
LANGUAGE_FEATURE(InheritActorContext, 0, "@_inheritActorContext attribute", true)
7979
LANGUAGE_FEATURE(ImplicitSelfCapture, 0, "@_implicitSelfCapture attribute", true)
8080
LANGUAGE_FEATURE(BuiltinBuildExecutor, 0, "Executor-building builtins", true)
81+
LANGUAGE_FEATURE(BuiltinBuildComplexEqualityExecutor, 0, "Executor-building for 'complexEquality executor' builtins", true)
8182
LANGUAGE_FEATURE(BuiltinBuildMainExecutor, 0, "MainActor executor building builtin", true)
8283
LANGUAGE_FEATURE(BuiltinCreateAsyncTaskInGroup, 0, "MainActor executor building builtin", true)
8384
LANGUAGE_FEATURE(BuiltinCopy, 0, "Builtin.copy()", true)

include/swift/Runtime/Concurrency.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,9 @@ bool swift_task_isOnExecutor(
715715
const Metadata *selfType,
716716
const SerialExecutorWitnessTable *wtable);
717717

718+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
719+
bool swift_executor_isComplexEquality(ExecutorRef ref);
720+
718721
/// Return the 64bit TaskID (if the job is an AsyncTask),
719722
/// or the 32bits of the job Id otherwise.
720723
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)

include/swift/Runtime/Metadata.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,20 @@ const RelativeWitnessTable *swift_getAssociatedConformanceWitnessRelative(
423423
const ProtocolRequirement *reqBase,
424424
const ProtocolRequirement *assocConformance);
425425

426+
/// Compare two witness tables, which may involving checking the
427+
/// contents of their conformance descriptors.
428+
///
429+
/// Runtime availability: Swift 5.4
430+
///
431+
/// \param lhs The first protocol witness table to compare.
432+
/// \param rhs The second protocol witness table to compare.
433+
///
434+
/// \returns true if both witness tables describe the same conformance, false otherwise.
435+
SWIFT_RUNTIME_EXPORT
436+
SWIFT_CC(swift)
437+
bool swift_compareWitnessTables(const WitnessTable *lhs,
438+
const WitnessTable *rhs);
439+
426440
/// Determine whether two protocol conformance descriptors describe the same
427441
/// conformance of a type to a protocol.
428442
///

lib/AST/ASTPrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2979,6 +2979,10 @@ static bool usesFeatureBuiltinBuildExecutor(Decl *decl) {
29792979
return false;
29802980
}
29812981

2982+
static bool usesFeatureBuiltinBuildComplexEqualityExecutor(Decl *decl) {
2983+
return false;
2984+
}
2985+
29822986
static bool usesFeatureBuiltinBuildMainExecutor(Decl *decl) {
29832987
return false;
29842988
}

lib/AST/Builtins.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1596,6 +1596,15 @@ static ValueDecl *getBuildOrdinarySerialExecutorRef(ASTContext &ctx,
15961596
_executor);
15971597
}
15981598

1599+
static ValueDecl *getBuildComplexEqualitySerialExecutorRef(ASTContext &ctx,
1600+
Identifier id) {
1601+
return getBuiltinFunction(ctx, id, _thin,
1602+
_generics(_unrestricted,
1603+
_conformsTo(_typeparam(0), _serialExecutor)),
1604+
_parameters(_typeparam(0)),
1605+
_executor);
1606+
}
1607+
15991608
static ValueDecl *getAutoDiffCreateLinearMapContext(ASTContext &ctx,
16001609
Identifier id) {
16011610
return getBuiltinFunction(
@@ -2887,6 +2896,8 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
28872896

28882897
case BuiltinValueKind::BuildOrdinarySerialExecutorRef:
28892898
return getBuildOrdinarySerialExecutorRef(Context, Id);
2899+
case BuiltinValueKind::BuildComplexEqualitySerialExecutorRef:
2900+
return getBuildComplexEqualitySerialExecutorRef(Context, Id);
28902901

28912902
case BuiltinValueKind::PoundAssert:
28922903
return getPoundAssert(Context, Id);

lib/IRGen/GenBuiltin.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,13 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin,
408408
emitBuildOrdinarySerialExecutorRef(IGF, actor, type, conf, out);
409409
return;
410410
}
411+
if (Builtin.ID == BuiltinValueKind::BuildComplexEqualitySerialExecutorRef) {
412+
auto actor = args.claimNext();
413+
auto type = substitutions.getReplacementTypes()[0]->getCanonicalType();
414+
auto conf = substitutions.getConformances()[0];
415+
emitBuildComplexEqualitySerialExecutorRef(IGF, actor, type, conf, out);
416+
return;
417+
}
411418

412419
if (Builtin.ID == BuiltinValueKind::InitializeDistributedRemoteActor) {
413420
auto actorMetatype = args.claimNext();

lib/IRGen/GenConcurrency.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ void irgen::emitBuildDefaultActorExecutorRef(IRGenFunction &IGF,
159159
void irgen::emitBuildOrdinarySerialExecutorRef(IRGenFunction &IGF,
160160
llvm::Value *executor,
161161
CanType executorType,
162-
ProtocolConformanceRef executorConf,
162+
ProtocolConformanceRef executorConf,
163163
Explosion &out) {
164164
// The implementation word of an "ordinary" serial executor is
165165
// just the witness table pointer with no flags set.
@@ -173,6 +173,31 @@ void irgen::emitBuildOrdinarySerialExecutorRef(IRGenFunction &IGF,
173173
out.add(impl);
174174
}
175175

176+
void irgen::emitBuildComplexEqualitySerialExecutorRef(IRGenFunction &IGF,
177+
llvm::Value *executor,
178+
CanType executorType,
179+
ProtocolConformanceRef executorConf,
180+
Explosion &out) {
181+
llvm::Value *identity =
182+
IGF.Builder.CreatePtrToInt(executor, IGF.IGM.ExecutorFirstTy);
183+
184+
// The implementation word of an "complex equality" serial executor is
185+
// the witness table pointer with the ExecutorKind::ComplexEquality flag set.
186+
llvm::Value *impl =
187+
emitWitnessTableRef(IGF, executorType, executorConf);
188+
impl = IGF.Builder.CreatePtrToInt(impl, IGF.IGM.ExecutorSecondTy);
189+
190+
// NOTE: Refer to ExecutorRef::ExecutorKind for the flag values.
191+
llvm::IntegerType *IntPtrTy = IGF.IGM.IntPtrTy;
192+
auto complexEqualityExecutorKindFlag =
193+
llvm::Constant::getIntegerValue(IntPtrTy, APInt(IntPtrTy->getBitWidth(),
194+
0b01));
195+
impl = IGF.Builder.CreateOr(impl, complexEqualityExecutorKindFlag);
196+
197+
out.add(identity);
198+
out.add(impl);
199+
}
200+
176201
void irgen::emitGetCurrentExecutor(IRGenFunction &IGF, Explosion &out) {
177202
auto *call = IGF.Builder.CreateCall(
178203
IGF.IGM.getTaskGetCurrentExecutorFunctionPointer(), {});

lib/IRGen/GenConcurrency.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ void emitBuildOrdinarySerialExecutorRef(IRGenFunction &IGF,
5454
ProtocolConformanceRef executorConformance,
5555
Explosion &out);
5656

57+
/// Emit the buildComplexEqualitySerialExecutorRef builtin.
58+
void emitBuildComplexEqualitySerialExecutorRef(IRGenFunction &IGF,
59+
llvm::Value *executor,
60+
CanType executorType,
61+
ProtocolConformanceRef executorConformance,
62+
Explosion &out);
63+
5764
/// Emit the getCurrentExecutor builtin.
5865
void emitGetCurrentExecutor(IRGenFunction &IGF, Explosion &out);
5966

lib/SIL/IR/OperandOwnership.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,6 +941,7 @@ BUILTIN_OPERAND_OWNERSHIP(PointerEscape, AutoDiffProjectTopLevelSubcontext)
941941
BUILTIN_OPERAND_OWNERSHIP(ForwardingConsume, ConvertTaskToJob)
942942

943943
BUILTIN_OPERAND_OWNERSHIP(BitwiseEscape, BuildOrdinarySerialExecutorRef)
944+
BUILTIN_OPERAND_OWNERSHIP(BitwiseEscape, BuildComplexEqualitySerialExecutorRef)
944945
BUILTIN_OPERAND_OWNERSHIP(BitwiseEscape, BuildDefaultActorExecutorRef)
945946
BUILTIN_OPERAND_OWNERSHIP(BitwiseEscape, BuildMainActorExecutorRef)
946947

lib/SIL/IR/ValueOwnership.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@ CONSTANT_OWNERSHIP_BUILTIN(None, ResumeNonThrowingContinuationReturning)
567567
CONSTANT_OWNERSHIP_BUILTIN(None, ResumeThrowingContinuationReturning)
568568
CONSTANT_OWNERSHIP_BUILTIN(None, ResumeThrowingContinuationThrowing)
569569
CONSTANT_OWNERSHIP_BUILTIN(None, BuildOrdinarySerialExecutorRef)
570+
CONSTANT_OWNERSHIP_BUILTIN(None, BuildComplexEqualitySerialExecutorRef)
570571
CONSTANT_OWNERSHIP_BUILTIN(None, BuildDefaultActorExecutorRef)
571572
CONSTANT_OWNERSHIP_BUILTIN(None, BuildMainActorExecutorRef)
572573
CONSTANT_OWNERSHIP_BUILTIN(None, StartAsyncLet)

lib/SIL/Utils/InstructionUtils.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -904,6 +904,7 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType)
904904
case BuiltinValueKind::Swift3ImplicitObjCEntrypoint:
905905
return RuntimeEffect::ObjectiveC | RuntimeEffect::Allocating;
906906
case BuiltinValueKind::BuildOrdinarySerialExecutorRef:
907+
case BuiltinValueKind::BuildComplexEqualitySerialExecutorRef:
907908
case BuiltinValueKind::BuildDefaultActorExecutorRef:
908909
case BuiltinValueKind::BuildMainActorExecutorRef:
909910
case BuiltinValueKind::StartAsyncLet:

lib/SIL/Verifier/SILVerifier.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2052,6 +2052,7 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
20522052
}
20532053

20542054
if (builtinKind == BuiltinValueKind::BuildOrdinarySerialExecutorRef ||
2055+
builtinKind == BuiltinValueKind::BuildComplexEqualitySerialExecutorRef ||
20552056
builtinKind == BuiltinValueKind::BuildDefaultActorExecutorRef) {
20562057
require(arguments.size() == 1,
20572058
"builtin expects one argument");

lib/SILGen/SILGenBuiltin.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1770,6 +1770,12 @@ static ManagedValue emitBuiltinBuildOrdinarySerialExecutorRef(
17701770
return emitBuildExecutorRef(SGF, loc, subs, args,
17711771
BuiltinValueKind::BuildOrdinarySerialExecutorRef);
17721772
}
1773+
static ManagedValue emitBuiltinBuildComplexEqualitySerialExecutorRef(
1774+
SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs,
1775+
ArrayRef<ManagedValue> args, SGFContext C) {
1776+
return emitBuildExecutorRef(SGF, loc, subs, args,
1777+
BuiltinValueKind::BuildComplexEqualitySerialExecutorRef);
1778+
}
17731779
static ManagedValue emitBuiltinBuildDefaultActorExecutorRef(
17741780
SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs,
17751781
ArrayRef<ManagedValue> args, SGFContext C) {

lib/SILOptimizer/Transforms/AccessEnforcementReleaseSinking.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ static bool isBarrier(SILInstruction *inst) {
192192
case BuiltinValueKind::InitializeDistributedRemoteActor:
193193
case BuiltinValueKind::InitializeNonDefaultDistributedActor:
194194
case BuiltinValueKind::BuildOrdinarySerialExecutorRef:
195+
case BuiltinValueKind::BuildComplexEqualitySerialExecutorRef:
195196
case BuiltinValueKind::BuildDefaultActorExecutorRef:
196197
case BuiltinValueKind::BuildMainActorExecutorRef:
197198
case BuiltinValueKind::ResumeNonThrowingContinuationReturning:

lib/Serialization/ModuleFormat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5858
/// describe what change you made. The content of this comment isn't important;
5959
/// it just ensures a conflict if two people change the module format.
6060
/// Don't worry about adhering to the 80-column limit for this line.
61-
const uint16_t SWIFTMODULE_VERSION_MINOR = 755; // InternalOrBelow dependency
61+
const uint16_t SWIFTMODULE_VERSION_MINOR = 756; // build complexEquality executor builtin
6262

6363
/// A standard hash seed used for all string hashes in a serialized module.
6464
///

stdlib/public/Concurrency/Actor.cpp

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -295,14 +295,49 @@ JobPriority swift::swift_task_getCurrentThreadPriority() {
295295
#endif
296296
}
297297

298+
// Implemented in Swift to avoid some annoying hard-coding about
299+
// SerialExecutor's protocol witness table. We could inline this
300+
// with effort, though.
301+
extern "C" SWIFT_CC(swift)
302+
bool _task_serialExecutor_isSameExclusiveExecutionContext(
303+
HeapObject *currentExecutor, HeapObject *executor,
304+
const Metadata *selfType,
305+
const SerialExecutorWitnessTable *wtable);
306+
298307
SWIFT_CC(swift)
299308
static bool swift_task_isCurrentExecutorImpl(ExecutorRef executor) {
300-
if (auto currentTracking = ExecutorTrackingInfo::current()) {
301-
return currentTracking->getActiveExecutor() == executor;
309+
auto current = ExecutorTrackingInfo::current();
310+
311+
if (!current) {
312+
// TODO(ktoso): checking the "is main thread" is not correct, main executor can be not main thread, relates to rdar://106188692
313+
return executor.isMainExecutor() && isExecutingOnMainThread();
314+
}
315+
316+
auto currentExecutor = current->getActiveExecutor();
317+
if (currentExecutor == executor) {
318+
return true;
302319
}
303320

304-
// TODO(ktoso): checking the "is main thread" is not correct, main executor can be not main thread, relates to rdar://106188692
305-
return executor.isMainExecutor() && isExecutingOnMainThread();
321+
if (executor.isComplexEquality()) {
322+
if (!swift_compareWitnessTables(
323+
reinterpret_cast<const WitnessTable*>(currentExecutor.getSerialExecutorWitnessTable()),
324+
reinterpret_cast<const WitnessTable*>(executor.getSerialExecutorWitnessTable()))) {
325+
// different witness table, we cannot invoke complex equality call
326+
return false;
327+
}
328+
// Avoid passing nulls to Swift for the isSame check:
329+
if (!currentExecutor.getIdentity() || !executor.getIdentity()) {
330+
return false;
331+
}
332+
333+
return _task_serialExecutor_isSameExclusiveExecutionContext(
334+
currentExecutor.getIdentity(),
335+
executor.getIdentity(),
336+
swift_getObjectType(currentExecutor.getIdentity()),
337+
executor.getSerialExecutorWitnessTable());
338+
}
339+
340+
return false;
306341
}
307342

308343
/// Logging level for unexpected executors:

0 commit comments

Comments
 (0)