@@ -63,9 +63,27 @@ using namespace swift;
6363SWIFT_CC (swift)
6464void (*swift::swift_task_enqueueGlobal_hook)(Job *job) = nullptr;
6565
66+ SWIFT_CC (swift)
67+ void (*swift::swift_task_enqueueGlobalWithDelay_hook)(unsigned long long delay, Job *job) = nullptr;
68+
6669#if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR
70+
71+ #include < chrono>
72+ #include < thread>
73+
6774static Job *JobQueue = nullptr ;
6875
76+ class DelayedJob {
77+ public:
78+ Job *job;
79+ unsigned long long when;
80+ DelayedJob *next;
81+
82+ DelayedJob (Job *job, unsigned long long when) : job(job), when(when), next(nullptr ) {}
83+ };
84+
85+ static DelayedJob *DelayedJobQueue = nullptr ;
86+
6987// / Get the next-in-queue storage slot.
7088static Job *&nextInQueue (Job *cur) {
7189 return reinterpret_cast <Job*&>(cur->SchedulerPrivate );
@@ -89,13 +107,58 @@ static void insertIntoJobQueue(Job *newJob) {
89107 *position = newJob;
90108}
91109
110+ static unsigned long long currentNanos () {
111+ auto now = std::chrono::steady_clock::now ();
112+ auto nowNanos = std::chrono::time_point_cast<std::chrono::nanoseconds>(now);
113+ auto value = std::chrono::duration_cast<std::chrono::nanoseconds>(nowNanos.time_since_epoch ());
114+ return value.count ();
115+ }
116+
117+ // / Insert a job into the cooperative global queue.
118+ static void insertIntoDelayedJobQueue (unsigned long long delay, Job *job) {
119+ DelayedJob **position = &DelayedJobQueue;
120+ DelayedJob *newJob = new DelayedJob (job, currentNanos () + delay);
121+
122+ while (auto cur = *position) {
123+ // If we find a job with lower priority, insert here.
124+ if (cur->when > newJob->when ) {
125+ newJob->next = cur;
126+ *position = newJob;
127+ return ;
128+ }
129+
130+ // Otherwise, keep advancing through the queue.
131+ position = &cur->next ;
132+ }
133+ *position = newJob;
134+ }
135+
92136// / Claim the next job from the cooperative global queue.
93137static Job *claimNextFromJobQueue () {
94- if (auto job = JobQueue) {
95- JobQueue = nextInQueue (job);
96- return job;
138+ // Check delayed jobs first
139+ while (true ) {
140+ if (auto delayedJob = DelayedJobQueue) {
141+ if (delayedJob->when < currentNanos ()) {
142+ DelayedJobQueue = delayedJob->next ;
143+ auto job = delayedJob->job ;
144+
145+ delete delayedJob;
146+
147+ return job;
148+ }
149+ }
150+ if (auto job = JobQueue) {
151+ JobQueue = nextInQueue (job);
152+ return job;
153+ }
154+ // there are only delayed jobs left, but they are not ready,
155+ // so we sleep until the first one is
156+ if (auto delayedJob = DelayedJobQueue) {
157+ std::this_thread::sleep_for (std::chrono::nanoseconds (delayedJob->when - currentNanos ()));
158+ continue ;
159+ }
160+ return nullptr ;
97161 }
98- return nullptr ;
99162}
100163
101164void swift::donateThreadToGlobalExecutorUntil (bool (*condition)(void *),
@@ -177,6 +240,30 @@ void swift::swift_task_enqueueGlobal(Job *job) {
177240#endif
178241}
179242
243+ void swift::swift_task_enqueueGlobalWithDelay (unsigned long long delay, Job *job) {
244+ assert (job && " no job provided" );
245+
246+ // If the hook is defined, use it.
247+ if (swift_task_enqueueGlobalWithDelay_hook)
248+ return swift_task_enqueueGlobalWithDelay_hook (delay, job);
249+
250+ #if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR
251+ insertIntoDelayedJobQueue (delay, job);
252+ #else
253+
254+ dispatch_function_t dispatchFunction = &__swift_run_job;
255+ void *dispatchContext = job;
256+
257+ JobPriority priority = job->getPriority ();
258+
259+ // TODO: cache this to avoid the extra call
260+ auto queue = dispatch_get_global_queue ((dispatch_qos_class_t ) priority,
261+ /* flags*/ 0 );
262+ dispatch_time_t when = dispatch_time (DISPATCH_TIME_NOW, delay);
263+ dispatch_after_f (when, queue, dispatchContext, dispatchFunction);
264+ #endif
265+ }
266+
180267
181268// / Enqueues a task on the main executor.
182269// / FIXME: only exists for the quick-and-dirty MainActor implementation.
0 commit comments