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+
6166using namespace swift ;
6267
6368SWIFT_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.
176197static 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
189254static 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
0 commit comments