2424
2525#if SWIFT_CONCURRENCY_ENABLE_DISPATCH
2626#include < dispatch/dispatch.h>
27-
2827#if !defined(_WIN32)
2928#include < dlfcn.h>
3029#endif
31-
3230#endif
3331
3432// Ensure that Job's layout is compatible with what Dispatch expects.
@@ -226,6 +224,74 @@ static void swift_task_enqueueGlobalWithDelayImpl(JobDelay delay,
226224}
227225
228226#define DISPATCH_UP_OR_MONOTONIC_TIME_MASK (1ULL << 63 )
227+ #define DISPATCH_WALLTIME_MASK (1ULL << 62 )
228+ #define DISPATCH_TIME_MAX_VALUE (DISPATCH_WALLTIME_MASK - 1 )
229+
230+ struct __swift_job_source {
231+ dispatch_source_t source;
232+ Job *job;
233+ };
234+
235+ static void _swift_run_job_leeway (struct __swift_job_source *jobSource) {
236+ dispatch_source_t source = jobSource->source ;
237+ dispatch_release (source);
238+ Job *job = jobSource->job ;
239+ auto task = dyn_cast<AsyncTask>(job);
240+ assert (task && " provided job must be a task" );
241+ _swift_task_dealloc_specific (task, jobSource);
242+ __swift_run_job (job);
243+ }
244+
245+ #if defined(__i386__) || defined(__x86_64__) || !defined(__APPLE__)
246+ #define TIME_UNIT_USES_NANOSECONDS 1
247+ #else
248+ #define TIME_UNIT_USES_NANOSECONDS 0
249+ #endif
250+
251+ #if TIME_UNIT_USES_NANOSECONDS
252+ // x86 currently implements mach time in nanoseconds
253+ // this is NOT likely to change
254+ static inline uint64_t
255+ platform_time (uint64_t nsec) {
256+ return nsec;
257+ }
258+ #else
259+ #define DISPATCH_USE_HOST_TIME 1
260+ #if defined(__APPLE__)
261+ #if defined(__arm__) || defined(__arm64__)
262+ // Apple arm platforms currently use a fixed mach timebase of 125/3 (24 MHz)
263+ static inline uint64_t
264+ platform_time (uint64_t nsec) {
265+ if (!nsec) {
266+ return nsec;
267+ }
268+ if (nsec >= (uint64_t )INT64_MAX) {
269+ return INT64_MAX;
270+ }
271+ if (nsec >= UINT64_MAX / 3ull ) {
272+ return (nsec / 125ull ) * 3ull ;
273+ } else {
274+ return (nsec * 3ull ) / 125ull ;
275+ }
276+ }
277+ #endif
278+ #endif
279+ #endif
280+
281+ static inline dispatch_time_t
282+ clock_and_value_to_time (int clock, long long deadline) {
283+ uint64_t value = platform_time ((uint64_t )deadline);
284+ if (value >= DISPATCH_TIME_MAX_VALUE) {
285+ return DISPATCH_TIME_FOREVER;
286+ }
287+ switch (clock) {
288+ case swift_clock_id_suspending:
289+ return value;
290+ case swift_clock_id_continuous:
291+ return value | DISPATCH_UP_OR_MONOTONIC_TIME_MASK;
292+ }
293+ __builtin_unreachable ();
294+ }
229295
230296SWIFT_CC (swift)
231297static void swift_task_enqueueGlobalWithDeadlineImpl(long long sec,
@@ -234,9 +300,7 @@ static void swift_task_enqueueGlobalWithDeadlineImpl(long long sec,
234300 long long tnsec,
235301 int clock, Job *job) {
236302 assert (job && " no job provided" );
237-
238- dispatch_function_t dispatchFunction = &__swift_run_job;
239- void *dispatchContext = job;
303+ auto task = cast<AsyncTask>(job);
240304
241305 JobPriority priority = job->getPriority ();
242306
@@ -245,20 +309,33 @@ static void swift_task_enqueueGlobalWithDeadlineImpl(long long sec,
245309 job->SchedulerPrivate [Job::DispatchQueueIndex] =
246310 DISPATCH_QUEUE_GLOBAL_EXECUTOR;
247311
248- long long nowSec;
249- long long nowNsec;
250- swift_get_time (&nowSec, &nowNsec, (swift_clock_id)clock);
312+ uint64_t deadline = sec * NSEC_PER_SEC + nsec;
313+ dispatch_time_t when = clock_and_value_to_time (clock, deadline);
314+
315+ if (tnsec != -1 ) {
316+ uint64_t leeway = tsec * NSEC_PER_SEC + tnsec;
251317
252- uint64_t delta = (sec - nowSec) * NSEC_PER_SEC + nsec - nowNsec;
318+ dispatch_source_t source =
319+ dispatch_source_create (DISPATCH_SOURCE_TYPE_TIMER, 0 , 0 , queue);
320+ dispatch_source_set_timer (source, when, DISPATCH_TIME_FOREVER, leeway);
253321
254- dispatch_time_t when = dispatch_time (DISPATCH_TIME_NOW, delta );
322+ size_t sz = sizeof ( struct __swift_job_source );
255323
256- if (clock == swift_clock_id_continuous) {
257- when |= DISPATCH_UP_OR_MONOTONIC_TIME_MASK;
324+ struct __swift_job_source *jobSource =
325+ (struct __swift_job_source *)_swift_task_alloc_specific (task, sz);
326+
327+ jobSource->job = job;
328+ jobSource->source = source;
329+
330+ dispatch_set_context (source, jobSource);
331+ dispatch_source_set_event_handler_f (source,
332+ (dispatch_function_t )&_swift_run_job_leeway);
333+
334+ dispatch_activate (source);
335+ } else {
336+ dispatch_after_f (when, queue, (void *)job,
337+ (dispatch_function_t )&__swift_run_job);
258338 }
259- // TODO: this should pass the leeway/tolerance along when it is not -1 nanoseconds
260- // either a dispatch_source can be created or a better dispatch_after_f can be made for this
261- dispatch_after_f (when, queue, dispatchContext, dispatchFunction);
262339}
263340
264341SWIFT_CC (swift)
0 commit comments