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+
6267using namespace swift ;
6368
6469SWIFT_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.
184205static 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
197262static 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}
0 commit comments