Skip to content

Commit e98fc3e

Browse files
authored
Merge pull request #36572 from mikeash/async-task-dispatch-integration2
[Concurrency] Make Job objects work as Dispatch objects.
2 parents e4f4b89 + 0989524 commit e98fc3e

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
@@ -1227,6 +1227,9 @@ namespace SpecialPointerAuthDiscriminators {
12271227

12281228
/// Swift async context parameter stored in the extended frame info.
12291229
const uint16_t SwiftAsyncContextExtendedFrameEntry = 0xc31a; // = 49946
1230+
1231+
/// Dispatch integration.
1232+
const uint16_t DispatchInvokeFunction = 0xf493; // = 62611
12301233
}
12311234

12321235
/// 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
@@ -55,10 +55,15 @@
5555

5656
#include "../CompatibilityOverride/CompatibilityOverride.h"
5757
#include "swift/Runtime/Concurrency.h"
58+
#include "swift/Runtime/EnvironmentVariables.h"
5859
#include "TaskPrivate.h"
5960

6061
#include <dispatch/dispatch.h>
6162

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

6469
SWIFT_CC(swift)
@@ -180,18 +185,78 @@ void swift::donateThreadToGlobalExecutorUntil(bool (*condition)(void *),
180185

181186
#else
182187

188+
// Ensure that Job's layout is compatible with what Dispatch expects.
189+
// Note: MinimalDispatchObjectHeader just has the fields we care about, it is
190+
// not complete and should not be used for anything other than these asserts.
191+
struct MinimalDispatchObjectHeader {
192+
const void *VTable;
193+
void *Opaque;
194+
void *Linkage;
195+
};
196+
static_assert(
197+
offsetof(Job, metadata) == offsetof(MinimalDispatchObjectHeader, VTable),
198+
"Job Metadata field must match location of Dispatch VTable field.");
199+
static_assert(offsetof(Job, SchedulerPrivate[Job::DispatchLinkageIndex]) ==
200+
offsetof(MinimalDispatchObjectHeader, Linkage),
201+
"Dispatch Linkage field must match Job "
202+
"SchedulerPrivate[DispatchLinkageIndex].");
203+
183204
/// The function passed to dispatch_async_f to execute a job.
184205
static void __swift_run_job(void *_job) {
185206
Job *job = (Job*) _job;
186-
swift_job_run(job, ExecutorRef::generic());
207+
auto metadata =
208+
reinterpret_cast<const DispatchClassMetadata *>(job->metadata);
209+
metadata->VTableInvoke(job, nullptr, 0);
187210
}
188211

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

197262
static constexpr size_t globalQueueCacheCount =
@@ -259,14 +324,12 @@ static void swift_task_enqueueGlobalImpl(Job *job) {
259324
// the priorities of work added to this queue using Dispatch's public
260325
// API, but as discussed above, that is less important than avoiding
261326
// performance problems.
262-
dispatch_function_t dispatchFunction = &__swift_run_job;
263-
void *dispatchContext = job;
264-
265327
JobPriority priority = job->getPriority();
266328

267329
auto queue = getGlobalQueue(priority);
268330

269-
dispatch_async_f(queue, dispatchContext, dispatchFunction);
331+
dispatchEnqueue(queue, job, (dispatch_qos_class_t)priority,
332+
DISPATCH_QUEUE_GLOBAL_EXECUTOR);
270333
#endif
271334
}
272335

@@ -293,6 +356,9 @@ static void swift_task_enqueueGlobalWithDelayImpl(unsigned long long delay,
293356

294357
auto queue = getGlobalQueue(priority);
295358

359+
job->SchedulerPrivate[Job::DispatchQueueIndex] =
360+
DISPATCH_QUEUE_GLOBAL_EXECUTOR;
361+
296362
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, delay);
297363
dispatch_after_f(when, queue, dispatchContext, dispatchFunction);
298364
#endif
@@ -317,13 +383,13 @@ static void swift_task_enqueueMainExecutorImpl(Job *job) {
317383
insertIntoJobQueue(job);
318384
#else
319385

320-
dispatch_function_t dispatchFunction = &__swift_run_job_main_executor;
321-
void *dispatchContext = job;
386+
JobPriority priority = job->getPriority();
322387

323388
// This is an inline function that compiles down to a pointer to a global.
324389
auto mainQueue = dispatch_get_main_queue();
325390

326-
dispatch_async_f(mainQueue, dispatchContext, dispatchFunction);
391+
dispatchEnqueue(mainQueue, job, (dispatch_qos_class_t)priority,
392+
DISPATCH_QUEUE_MAIN_EXECUTOR);
327393

328394
#endif
329395
}

stdlib/public/Concurrency/Task.cpp

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

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

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

@@ -211,7 +228,8 @@ static FullMetadata<DispatchClassMetadata> taskHeapMetadata = {
211228
},
212229
{
213230
MetadataKind::Task,
214-
dummyVTableFunction
231+
dispatchSwiftObjectType,
232+
jobInvoke
215233
}
216234
};
217235

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)