Skip to content

Commit c35df54

Browse files
committed
Use &_dispatch_main_q as the identity of the main actor.
I added Builtin.buildMainActorExecutor before, but because I never implemented it correctly in IRGen, it's not okay to use it on old versions, so I had to introduce a new feature only for it. The shim dispatch queue class in the Concurrency runtime is rather awful, but I couldn't think of a reasonable alternative without just entirely hard-coding the witness table in the runtime. It's not ABI, at least.
1 parent 8b03396 commit c35df54

File tree

14 files changed

+129
-31
lines changed

14 files changed

+129
-31
lines changed

include/swift/ABI/Executor.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,15 @@ class ExecutorRef {
8282
return ExecutorRef(actor, 0);
8383
}
8484

85+
/// Given a pointer to a serial executor and its SerialExecutor
86+
/// conformance, return an executor reference for it.
87+
static ExecutorRef forOrdinary(HeapObject *identity,
88+
const SerialExecutorWitnessTable *witnessTable) {
89+
assert(identity);
90+
assert(witnessTable);
91+
return ExecutorRef(identity, reinterpret_cast<uintptr_t>(witnessTable));
92+
}
93+
8594
HeapObject *getIdentity() const {
8695
return Identity;
8796
}
@@ -112,6 +121,9 @@ class ExecutorRef {
112121
return Identity != newExecutor.Identity;
113122
}
114123

124+
/// Is this executor the main executor?
125+
bool isMainExecutor() const;
126+
115127
bool operator==(ExecutorRef other) const {
116128
return Identity == other.Identity;
117129
}

include/swift/Basic/Compiler.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,29 @@
137137
#define SWIFT_POINTER_IS_4_BYTES 1
138138
#endif
139139

140+
// Produce a string literal for the raw argument tokens.
141+
#define SWIFT_STRINGIZE_RAW(TOK) #TOK
142+
143+
// Produce a string literal for the macro-expanded argument tokens.
144+
#define SWIFT_STRINGIZE_EXPANDED(TOK) SWIFT_STRINGIZE_RAW(TOK)
145+
146+
#if defined(__USER_LABEL_PREFIX__)
147+
#define SWIFT_SYMBOL_PREFIX_STRING \
148+
SWIFT_STRINGIZE_EXPANDED(__USER_LABEL_PREFIX__)
149+
#else
150+
// Clang and GCC always define __USER_LABEL_PREFIX__, so this should
151+
// only come up with MSVC, and Windows doesn't use a prefix.
152+
#define SWIFT_SYMBOL_PREFIX_STRING ""
153+
#endif
154+
155+
// An attribute to override the symbol name of a declaration.
156+
// This does not compensate for platform symbol prefixes; for that,
157+
// use SWIFT_ASM_LABEL_WITH_PREFIX.
158+
//
159+
// This only actually works on Clang or GCC; MSVC does not provide
160+
// an attribute to change the asm label.
161+
#define SWIFT_ASM_LABEL_RAW(STRING) __asm__(STRING)
162+
#define SWIFT_ASM_LABEL_WITH_PREFIX(STRING) \
163+
SWIFT_ASM_LABEL_RAW(SWIFT_SYMBOL_PREFIX_STRING STRING)
164+
140165
#endif // SWIFT_BASIC_COMPILER_H

include/swift/Basic/Features.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,6 @@ LANGUAGE_FEATURE(BuiltinTaskGroup, 0, "TaskGroup builtins", true)
5151
LANGUAGE_FEATURE(InheritActorContext, 0, "@_inheritActorContext attribute", true)
5252
LANGUAGE_FEATURE(ImplicitSelfCapture, 0, "@_implicitSelfCapture attribute", true)
5353
LANGUAGE_FEATURE(BuiltinBuildExecutor, 0, "Executor-building builtins", true)
54+
LANGUAGE_FEATURE(BuiltinBuildMainExecutor, 0, "MainActor executor building builtin", true)
5455

5556
#undef LANGUAGE_FEATURE

include/swift/Runtime/Concurrency.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,10 @@ void swift_task_enqueueGlobalWithDelay(unsigned long long delay, Job *job);
550550
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
551551
void swift_task_enqueueMainExecutor(Job *job);
552552

553+
/// Enqueue the given job on the main executor.
554+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
555+
void swift_task_enqueueOnDispatchQueue(Job *job, HeapObject *queue);
556+
553557
/// A hook to take over global enqueuing.
554558
typedef SWIFT_CC(swift) void (*swift_task_enqueueGlobal_original)(Job *job);
555559
SWIFT_EXPORT_FROM(swift_Concurrency)
@@ -655,6 +659,10 @@ AsyncTask *swift_task_getCurrent(void);
655659
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
656660
ExecutorRef swift_task_getCurrentExecutor(void);
657661

662+
/// Return the main-actor executor reference.
663+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
664+
ExecutorRef swift_task_getMainExecutor(void);
665+
658666
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
659667
bool swift_task_isCurrentExecutor(ExecutorRef executor);
660668

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1666,6 +1666,14 @@ FUNCTION(TaskGetCurrentExecutor,
16661666
ARGS(),
16671667
ATTRS(NoUnwind, ArgMemOnly))
16681668

1669+
// ExecutorRef swift_task_getMainExecutor();
1670+
FUNCTION(TaskGetMainExecutor,
1671+
swift_task_getMainExecutor, SwiftCC,
1672+
ConcurrencyAvailability,
1673+
RETURNS(SwiftExecutorTy),
1674+
ARGS(),
1675+
ATTRS(NoUnwind, ArgMemOnly))
1676+
16691677
// void swift_defaultActor_initialize(DefaultActor *actor);
16701678
FUNCTION(DefaultActorInitialize,
16711679
swift_defaultActor_initialize, SwiftCC,

lib/AST/ASTPrinter.cpp

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

2777+
static bool usesFeatureBuiltinBuildMainExecutor(Decl *decl) {
2778+
return false;
2779+
}
2780+
27772781
static bool usesFeatureBuiltinContinuation(Decl *decl) {
27782782
return false;
27792783
}

lib/IRGen/GenConcurrency.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,12 @@ const LoadableTypeInfo &TypeConverter::getExecutorTypeInfo() {
127127

128128
void irgen::emitBuildMainActorExecutorRef(IRGenFunction &IGF,
129129
Explosion &out) {
130-
// FIXME
130+
auto call = IGF.Builder.CreateCall(IGF.IGM.getTaskGetMainExecutorFn(),
131+
{});
132+
call->setDoesNotThrow();
133+
call->setCallingConv(IGF.IGM.SwiftCC);
134+
135+
IGF.emitAllExtractValues(call, IGF.IGM.SwiftExecutorTy, out);
131136
}
132137

133138
void irgen::emitBuildDefaultActorExecutorRef(IRGenFunction &IGF,

stdlib/public/Concurrency/Actor.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,7 @@ static bool swift_task_isCurrentExecutorImpl(ExecutorRef executor) {
311311
return currentTracking->getActiveExecutor() == executor;
312312
}
313313

314-
return executor == _swift_task_getMainExecutor()
315-
&& isExecutingOnMainThread();
314+
return executor.isMainExecutor() && isExecutingOnMainThread();
316315
}
317316

318317
/// Logging level for unexpected executors:
@@ -355,7 +354,7 @@ void swift::swift_task_reportUnexpectedExecutor(
355354

356355
const char *functionIsolation;
357356
const char *whereExpected;
358-
if (executor == _swift_task_getMainExecutor()) {
357+
if (executor.isMainExecutor()) {
359358
functionIsolation = "@MainActor function";
360359
whereExpected = "the main thread";
361360
} else {

stdlib/public/Concurrency/Actor.swift

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,3 @@ public func _defaultActorDestroy(_ actor: AnyObject)
5151
@_silgen_name("swift_task_enqueueMainExecutor")
5252
@usableFromInline
5353
internal func _enqueueOnMain(_ job: UnownedJob)
54-
55-
// Used by the concurrency runtime
56-
@available(SwiftStdlib 5.5, *)
57-
extension SerialExecutor {
58-
@_silgen_name("_swift_task_getMainExecutor")
59-
internal func _getMainExecutor() -> UnownedSerialExecutor {
60-
return MainActor.shared.unownedExecutor
61-
}
62-
}

stdlib/public/Concurrency/Executor.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,27 @@ func _checkExpectedExecutor(_filenameStart: Builtin.RawPointer,
9292
_reportUnexpectedExecutor(
9393
_filenameStart, _filenameLength, _filenameIsASCII, _line, _executor)
9494
}
95+
96+
@available(SwiftStdlib 5.5, *)
97+
@_silgen_name("swift_task_enqueueOnDispatchQueue")
98+
internal func _enqueueOnDispatchQueue(_ job: UnownedJob, queue: AnyObject)
99+
100+
/// Used by the runtime solely for the witness table it produces.
101+
/// FIXME: figure out some way to achieve that which doesn't generate
102+
/// all the other metadata
103+
///
104+
/// Expected to work for any primitive dispatch queue; note that this
105+
/// means a dispatch_queue_t, which is not the same as DispatchQueue
106+
/// on platforms where that is an instance of a wrapper class.
107+
@available(SwiftStdlib 5.5, *)
108+
internal class DispatchQueueShim: UnsafeSendable, SerialExecutor {
109+
@inlinable
110+
func enqueue(_ job: UnownedJob) {
111+
_enqueueOnDispatchQueue(job, queue: self)
112+
}
113+
114+
@inlinable
115+
func asUnownedSerialExecutor() -> UnownedSerialExecutor {
116+
return UnownedSerialExecutor(ordinary: self)
117+
}
118+
}

stdlib/public/Concurrency/GlobalExecutor.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,8 +389,7 @@ static void swift_task_enqueueMainExecutorImpl(Job *job) {
389389
// This is an inline function that compiles down to a pointer to a global.
390390
auto mainQueue = dispatch_get_main_queue();
391391

392-
dispatchEnqueue(mainQueue, job, (dispatch_qos_class_t)priority,
393-
DISPATCH_QUEUE_MAIN_EXECUTOR);
392+
dispatchEnqueue(mainQueue, job, (dispatch_qos_class_t)priority, mainQueue);
394393

395394
#endif
396395
}
@@ -403,5 +402,22 @@ void swift::swift_task_enqueueMainExecutor(Job *job) {
403402
swift_task_enqueueMainExecutorImpl(job);
404403
}
405404

405+
void swift::swift_task_enqueueOnDispatchQueue(Job *job,
406+
HeapObject *_queue) {
407+
JobPriority priority = job->getPriority();
408+
auto queue = reinterpret_cast<dispatch_queue_t>(_queue);
409+
dispatchEnqueue(queue, job, (dispatch_qos_class_t)priority, queue);
410+
}
411+
412+
ExecutorRef swift::swift_task_getMainExecutor() {
413+
return ExecutorRef::forOrdinary(
414+
reinterpret_cast<HeapObject*>(&_dispatch_main_q),
415+
_swift_task_getDispatchQueueSerialExecutorWitnessTable());
416+
}
417+
418+
bool ExecutorRef::isMainExecutor() const {
419+
return Identity == reinterpret_cast<HeapObject*>(&_dispatch_main_q);
420+
}
421+
406422
#define OVERRIDE_GLOBAL_EXECUTOR COMPATIBILITY_OVERRIDE
407423
#include COMPATIBILITY_OVERRIDE_INCLUDE_PATH

stdlib/public/Concurrency/MainActor.swift

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,25 @@ import Swift
1515
/// A singleton actor whose executor is equivalent to the main
1616
/// dispatch queue.
1717
@available(SwiftStdlib 5.5, *)
18-
@globalActor public final actor MainActor: SerialExecutor, GlobalActor {
18+
@globalActor public final actor MainActor: GlobalActor {
1919
public static let shared = MainActor()
2020

2121
@inlinable
2222
public nonisolated var unownedExecutor: UnownedSerialExecutor {
23-
return asUnownedSerialExecutor()
23+
#if compiler(>=5.5) && $BuiltinBuildMainExecutor
24+
return UnownedSerialExecutor(Builtin.buildMainActorExecutorRef())
25+
#else
26+
fatalError("Swift compiler is incompatible with this SDK version")
27+
#endif
2428
}
2529

2630
@inlinable
27-
public nonisolated func asUnownedSerialExecutor() -> UnownedSerialExecutor {
28-
return UnownedSerialExecutor(ordinary: self)
31+
public static var sharedUnownedExecutor: UnownedSerialExecutor {
32+
#if compiler(>=5.5) && $BuiltinBuildMainExecutor
33+
return UnownedSerialExecutor(Builtin.buildMainActorExecutorRef())
34+
#else
35+
fatalError("Swift compiler is incompatible with this SDK version")
36+
#endif
2937
}
3038

3139
@inlinable

stdlib/public/Concurrency/Task.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,10 +233,9 @@ static ExecutorRef executorForEnqueuedJob(Job *job) {
233233
void *jobQueue = job->SchedulerPrivate[Job::DispatchQueueIndex];
234234
if (jobQueue == DISPATCH_QUEUE_GLOBAL_EXECUTOR)
235235
return ExecutorRef::generic();
236-
else if (jobQueue == DISPATCH_QUEUE_MAIN_EXECUTOR)
237-
return _swift_task_getMainExecutor();
238236
else
239-
swift_unreachable("jobQueue was not a known value.");
237+
return ExecutorRef::forOrdinary(reinterpret_cast<HeapObject*>(jobQueue),
238+
_swift_task_getDispatchQueueSerialExecutorWitnessTable());
240239
}
241240

242241
static void jobInvoke(void *obj, void *unused, uint32_t flags) {

stdlib/public/Concurrency/TaskPrivate.h

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,13 @@ void _swift_tsan_release(void *addr);
7777
/// Special values used with DispatchQueueIndex to indicate the global and main
7878
/// executors.
7979
#define DISPATCH_QUEUE_GLOBAL_EXECUTOR (void *)1
80-
#define DISPATCH_QUEUE_MAIN_EXECUTOR (void *)2
81-
82-
#pragma clang diagnostic push
83-
#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
84-
// FIXME: remove this and switch to a representation that uses
85-
// _dispatch_main_q somehow
86-
extern "C" SWIFT_CC(swift)
87-
ExecutorRef _swift_task_getMainExecutor();
88-
#pragma clang diagnostic pop
80+
81+
inline SerialExecutorWitnessTable *
82+
_swift_task_getDispatchQueueSerialExecutorWitnessTable() {
83+
extern SerialExecutorWitnessTable wtable
84+
SWIFT_ASM_LABEL_WITH_PREFIX("$ss17DispatchQueueShimCScfsWP");
85+
return &wtable;
86+
}
8987

9088
// ==== ------------------------------------------------------------------------
9189

0 commit comments

Comments
 (0)