Skip to content

Commit 0989524

Browse files
committed
[Concurrency] Make Job objects work as Dispatch objects.
Fill out the metadata for Job to have a Dispatch-compatible vtable. When available, use the dispatch_enqueue_onto_queue_4Swift to enqueue Jobs directly onto queues. Otherwise, keep using dispatch_async_f as we have been. rdar://75227953
1 parent 8eaa034 commit 0989524

File tree

10 files changed

+141
-26
lines changed

10 files changed

+141
-26
lines changed

include/swift/ABI/Metadata.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,18 +1338,20 @@ using ClassMetadata = TargetClassMetadata<InProcess>;
13381338
/// dispatch expects to see, with padding to place them at the expected offsets.
13391339
template <typename Runtime>
13401340
struct TargetDispatchClassMetadata : public TargetHeapMetadata<Runtime> {
1341-
using DummyVTableCall = void (*)(void);
1341+
using InvokeCall = void (*)(void *, void *, uint32_t);
13421342

1343-
TargetDispatchClassMetadata(MetadataKind Kind,
1344-
DummyVTableCall DummyVTableEntry)
1345-
: TargetHeapMetadata<Runtime>(Kind), DummyVTableEntry(DummyVTableEntry) {}
1343+
TargetDispatchClassMetadata(MetadataKind Kind, unsigned long VTableType,
1344+
InvokeCall Invoke)
1345+
: TargetHeapMetadata<Runtime>(Kind), VTableType(VTableType),
1346+
VTableInvoke(Invoke) {}
13461347

13471348
TargetPointer<Runtime, void> Opaque;
13481349
#if SWIFT_OBJC_INTEROP
13491350
TargetPointer<Runtime, void> OpaqueObjC[3];
13501351
#endif
13511352

1352-
TargetSignedPointer<Runtime, DummyVTableCall> DummyVTableEntry;
1353+
unsigned long VTableType;
1354+
TargetSignedPointer<Runtime, InvokeCall __ptrauth_swift_dispatch_invoke_function> VTableInvoke;
13531355
};
13541356
using DispatchClassMetadata = TargetDispatchClassMetadata<InProcess>;
13551357

include/swift/ABI/MetadataValues.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1212,6 +1212,9 @@ namespace SpecialPointerAuthDiscriminators {
12121212

12131213
/// Swift async context parameter stored in the extended frame info.
12141214
const uint16_t SwiftAsyncContextExtendedFrameEntry = 0xc31a; // = 49946
1215+
1216+
/// Dispatch integration.
1217+
const uint16_t DispatchInvokeFunction = 0xf493; // = 62611
12151218
}
12161219

12171220
/// The number of arguments that will be passed directly to a generic

include/swift/ABI/Task.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,20 @@ extern FullMetadata<DispatchClassMetadata> jobHeapMetadata;
4040

4141
/// A schedulable job.
4242
class alignas(2 * alignof(void*)) Job : public HeapObject {
43-
protected:
43+
public:
4444
// Indices into SchedulerPrivate, for use by the runtime.
4545
enum {
4646
/// The next waiting task link, an AsyncTask that is waiting on a future.
4747
NextWaitingTaskIndex = 0,
48+
49+
/// An opaque field used by Dispatch when enqueueing Jobs directly.
50+
DispatchLinkageIndex = 0,
51+
52+
/// The dispatch queue being used when enqueueing a Job directly with
53+
/// Dispatch.
54+
DispatchQueueIndex = 1,
4855
};
4956

50-
public:
5157
// Reserved for the use of the scheduler.
5258
void *SchedulerPrivate[2];
5359

include/swift/Runtime/Config.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,9 @@ extern uintptr_t __COMPATIBILITY_LIBRARIES_CANNOT_CHECK_THE_IS_SWIFT_BIT_DIRECTL
247247
#define __ptrauth_swift_escalation_notification_function \
248248
__ptrauth(ptrauth_key_function_pointer, 1, \
249249
SpecialPointerAuthDiscriminators::EscalationNotificationFunction)
250+
#define __ptrauth_swift_dispatch_invoke_function \
251+
__ptrauth(ptrauth_key_process_independent_code, 1, \
252+
SpecialPointerAuthDiscriminators::DispatchInvokeFunction)
250253
#define swift_ptrauth_sign_opaque_read_resume_function(__fn, __buffer) \
251254
ptrauth_auth_and_resign(__fn, ptrauth_key_function_pointer, 0, \
252255
ptrauth_key_process_independent_code, \
@@ -272,6 +275,7 @@ extern uintptr_t __COMPATIBILITY_LIBRARIES_CANNOT_CHECK_THE_IS_SWIFT_BIT_DIRECTL
272275
#define __ptrauth_swift_async_context_yield
273276
#define __ptrauth_swift_cancellation_notification_function
274277
#define __ptrauth_swift_escalation_notification_function
278+
#define __ptrauth_swift_dispatch_invoke_function
275279
#define __ptrauth_swift_runtime_function_entry
276280
#define __ptrauth_swift_runtime_function_entry_with_key(__key)
277281
#define __ptrauth_swift_runtime_function_entry_strip(__fn) (__fn)

include/swift/Runtime/EnvironmentVariables.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ extern OnceToken_t initializeToken;
3636
}
3737
#include "../../../stdlib/public/runtime/EnvironmentVariables.def"
3838

39+
// Wrapper around SWIFT_ENABLE_ASYNC_JOB_DISPATCH_INTEGRATION that the
40+
// Concurrency library can call.
41+
SWIFT_RUNTIME_STDLIB_SPI bool concurrencyEnableJobDispatchIntegration();
42+
3943
} // end namespace environment
4044
} // end namespace runtime
4145
} // end namespace Swift

stdlib/public/Concurrency/GlobalExecutor.cpp

Lines changed: 80 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,15 @@
5454
///===----------------------------------------------------------------------===///
5555

5656
#include "swift/Runtime/Concurrency.h"
57+
#include "swift/Runtime/EnvironmentVariables.h"
5758
#include "TaskPrivate.h"
5859

5960
#include <dispatch/dispatch.h>
6061

62+
#if !defined(_WIN32)
63+
#include <dlfcn.h>
64+
#endif
65+
6166
using namespace swift;
6267

6368
SWIFT_CC(swift)
@@ -172,18 +177,78 @@ void swift::donateThreadToGlobalExecutorUntil(bool (*condition)(void *),
172177

173178
#else
174179

180+
// Ensure that Job's layout is compatible with what Dispatch expects.
181+
// Note: MinimalDispatchObjectHeader just has the fields we care about, it is
182+
// not complete and should not be used for anything other than these asserts.
183+
struct MinimalDispatchObjectHeader {
184+
const void *VTable;
185+
void *Opaque;
186+
void *Linkage;
187+
};
188+
static_assert(
189+
offsetof(Job, metadata) == offsetof(MinimalDispatchObjectHeader, VTable),
190+
"Job Metadata field must match location of Dispatch VTable field.");
191+
static_assert(offsetof(Job, SchedulerPrivate[Job::DispatchLinkageIndex]) ==
192+
offsetof(MinimalDispatchObjectHeader, Linkage),
193+
"Dispatch Linkage field must match Job "
194+
"SchedulerPrivate[DispatchLinkageIndex].");
195+
175196
/// The function passed to dispatch_async_f to execute a job.
176197
static void __swift_run_job(void *_job) {
177198
Job *job = (Job*) _job;
178-
swift_job_run(job, ExecutorRef::generic());
199+
auto metadata =
200+
reinterpret_cast<const DispatchClassMetadata *>(job->metadata);
201+
metadata->VTableInvoke(job, nullptr, 0);
179202
}
180203

181-
/// A specialized version of __swift_run_job to execute the job on the main
182-
/// executor.
183-
/// FIXME: only exists for the quick-and-dirty MainActor implementation.
184-
static void __swift_run_job_main_executor(void *_job) {
185-
Job *job = (Job*) _job;
186-
swift_job_run(job, ExecutorRef::mainExecutor());
204+
/// The type of a function pointer for enqueueing a Job object onto a dispatch
205+
/// queue.
206+
typedef void (*dispatchEnqueueFuncType)(dispatch_queue_t queue, void *obj,
207+
dispatch_qos_class_t qos);
208+
209+
/// Initialize dispatchEnqueueFunc and then call through to the proper
210+
/// implementation.
211+
static void initializeDispatchEnqueueFunc(dispatch_queue_t queue, void *obj,
212+
dispatch_qos_class_t qos);
213+
214+
/// A function pointer to the function used to enqueue a Job onto a dispatch
215+
/// queue. Initially set to initializeDispatchEnqueueFunc, so that the first
216+
/// call will initialize it. initializeDispatchEnqueueFunc sets it to point
217+
/// either to dispatch_async_swift_job when it's available, otherwise to
218+
/// dispatchEnqueueDispatchAsync.
219+
static std::atomic<dispatchEnqueueFuncType> dispatchEnqueueFunc{
220+
initializeDispatchEnqueueFunc};
221+
222+
/// A small adapter that dispatches a Job onto a queue using dispatch_async_f.
223+
static void dispatchEnqueueDispatchAsync(dispatch_queue_t queue, void *obj,
224+
dispatch_qos_class_t qos) {
225+
dispatch_async_f(queue, obj, __swift_run_job);
226+
}
227+
228+
static void initializeDispatchEnqueueFunc(dispatch_queue_t queue, void *obj,
229+
dispatch_qos_class_t qos) {
230+
dispatchEnqueueFuncType func = nullptr;
231+
232+
// Always fall back to plain dispatch_async_f on Windows for now.
233+
#if !defined(_WIN32)
234+
if (runtime::environment::concurrencyEnableJobDispatchIntegration())
235+
func = reinterpret_cast<dispatchEnqueueFuncType>(
236+
dlsym(RTLD_NEXT, "dispatch_async_swift_job"));
237+
#endif
238+
239+
if (!func)
240+
func = dispatchEnqueueDispatchAsync;
241+
242+
dispatchEnqueueFunc.store(func, std::memory_order_relaxed);
243+
244+
func(queue, obj, qos);
245+
}
246+
247+
/// Enqueue a Job onto a dispatch queue using dispatchEnqueueFunc.
248+
static void dispatchEnqueue(dispatch_queue_t queue, Job *job,
249+
dispatch_qos_class_t qos, void *executorQueue) {
250+
job->SchedulerPrivate[Job::DispatchQueueIndex] = executorQueue;
251+
dispatchEnqueueFunc.load(std::memory_order_relaxed)(queue, job, qos);
187252
}
188253

189254
static constexpr size_t globalQueueCacheCount =
@@ -254,14 +319,12 @@ void swift::swift_task_enqueueGlobal(Job *job) {
254319
// the priorities of work added to this queue using Dispatch's public
255320
// API, but as discussed above, that is less important than avoiding
256321
// performance problems.
257-
dispatch_function_t dispatchFunction = &__swift_run_job;
258-
void *dispatchContext = job;
259-
260322
JobPriority priority = job->getPriority();
261323

262324
auto queue = getGlobalQueue(priority);
263325

264-
dispatch_async_f(queue, dispatchContext, dispatchFunction);
326+
dispatchEnqueue(queue, job, (dispatch_qos_class_t)priority,
327+
DISPATCH_QUEUE_GLOBAL_EXECUTOR);
265328
#endif
266329
}
267330

@@ -283,6 +346,9 @@ void swift::swift_task_enqueueGlobalWithDelay(unsigned long long delay, Job *job
283346

284347
auto queue = getGlobalQueue(priority);
285348

349+
job->SchedulerPrivate[Job::DispatchQueueIndex] =
350+
DISPATCH_QUEUE_GLOBAL_EXECUTOR;
351+
286352
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, delay);
287353
dispatch_after_f(when, queue, dispatchContext, dispatchFunction);
288354
#endif
@@ -298,13 +364,13 @@ void swift::swift_task_enqueueMainExecutor(Job *job) {
298364
insertIntoJobQueue(job);
299365
#else
300366

301-
dispatch_function_t dispatchFunction = &__swift_run_job_main_executor;
302-
void *dispatchContext = job;
367+
JobPriority priority = job->getPriority();
303368

304369
// This is an inline function that compiles down to a pointer to a global.
305370
auto mainQueue = dispatch_get_main_queue();
306371

307-
dispatch_async_f(mainQueue, dispatchContext, dispatchFunction);
372+
dispatchEnqueue(mainQueue, job, (dispatch_qos_class_t)priority,
373+
DISPATCH_QUEUE_MAIN_EXECUTOR);
308374

309375
#endif
310376

stdlib/public/Concurrency/Task.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ void AsyncTask::completeFuture(AsyncContext *context) {
101101
reinterpret_cast<char *>(context) - sizeof(FutureAsyncContextPrefix));
102102
bool hadErrorResult = false;
103103
auto errorObject = asyncContextPrefix->errorResult;
104-
printf("asyncTask::completeFuture errorObject: %p\n", errorObject);
104+
// printf("asyncTask::completeFuture errorObject: %p\n", errorObject);
105105
fragment->getError() = errorObject;
106106
if (errorObject) {
107107
hadErrorResult = true;
@@ -179,10 +179,26 @@ static void destroyTask(SWIFT_CONTEXT HeapObject *obj) {
179179
free(task);
180180
}
181181

182-
static void dummyVTableFunction(void) {
183-
abort();
182+
static ExecutorRef executorForEnqueuedJob(Job *job) {
183+
void *jobQueue = job->SchedulerPrivate[Job::DispatchQueueIndex];
184+
if (jobQueue == DISPATCH_QUEUE_GLOBAL_EXECUTOR)
185+
return ExecutorRef::generic();
186+
else if (jobQueue == DISPATCH_QUEUE_MAIN_EXECUTOR)
187+
return ExecutorRef::mainExecutor();
188+
else
189+
swift_unreachable("jobQueue was not a known value.");
184190
}
185191

192+
static void jobInvoke(void *obj, void *unused, uint32_t flags) {
193+
(void)unused;
194+
Job *job = reinterpret_cast<Job *>(obj);
195+
196+
swift_job_run(job, executorForEnqueuedJob(job));
197+
}
198+
199+
// Magic constant to identify Swift Job vtables to Dispatch.
200+
static const unsigned long dispatchSwiftObjectType = 1;
201+
186202
FullMetadata<DispatchClassMetadata> swift::jobHeapMetadata = {
187203
{
188204
{
@@ -194,7 +210,8 @@ FullMetadata<DispatchClassMetadata> swift::jobHeapMetadata = {
194210
},
195211
{
196212
MetadataKind::Job,
197-
dummyVTableFunction
213+
dispatchSwiftObjectType,
214+
jobInvoke
198215
}
199216
};
200217

@@ -210,7 +227,8 @@ static FullMetadata<DispatchClassMetadata> taskHeapMetadata = {
210227
},
211228
{
212229
MetadataKind::Task,
213-
dummyVTableFunction
230+
dispatchSwiftObjectType,
231+
jobInvoke
214232
}
215233
};
216234

stdlib/public/Concurrency/TaskPrivate.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ void donateThreadToGlobalExecutorUntil(bool (*condition)(void*),
7171
void _swift_tsan_acquire(void *addr);
7272
void _swift_tsan_release(void *addr);
7373

74+
/// Special values used with DispatchQueueIndex to indicate the global and main
75+
/// executors.
76+
#define DISPATCH_QUEUE_GLOBAL_EXECUTOR (void *)1
77+
#define DISPATCH_QUEUE_MAIN_EXECUTOR (void *)2
78+
7479
// ==== ------------------------------------------------------------------------
7580

7681
namespace {

stdlib/public/runtime/EnvironmentVariables.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,3 +194,7 @@ bool swift_COWChecksEnabled() {
194194
return runtime::environment::SWIFT_DEBUG_ENABLE_COW_CHECKS();
195195
}
196196

197+
SWIFT_RUNTIME_STDLIB_SPI bool concurrencyEnableJobDispatchIntegration() {
198+
return runtime::environment::
199+
SWIFT_ENABLE_ASYNC_JOB_DISPATCH_INTEGRATION();
200+
}

stdlib/public/runtime/EnvironmentVariables.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ VARIABLE(SWIFT_DEBUG_ENABLE_MALLOC_SCRIBBLE, bool, false,
4747
VARIABLE(SWIFT_DEBUG_ENABLE_COW_CHECKS, bool, false,
4848
"Enable internal checks for copy-on-write operations.")
4949

50+
VARIABLE(SWIFT_ENABLE_ASYNC_JOB_DISPATCH_INTEGRATION, bool, true,
51+
"Enable use of dispatch_async_swift_job when available.")
52+
5053
#if defined(__APPLE__) && defined(__MACH__)
5154

5255
VARIABLE(SWIFT_DEBUG_VALIDATE_SHARED_CACHE_PROTOCOL_CONFORMANCES, bool, false,

0 commit comments

Comments
 (0)