@@ -187,6 +187,29 @@ FutureFragment::Status AsyncTask::waitFuture(AsyncTask *waitingTask,
187
187
escalatedPriority = waitingStatus.getStoredPriority ();
188
188
}
189
189
190
+ #if SWIFT_CONCURRENCY_TASK_TO_THREAD_MODEL
191
+ // In the task to thread model, we will execute the task that we are waiting
192
+ // on, on the current thread itself. As a result, do not bother adding the
193
+ // waitingTask to any thread queue. Instead, we will clear the old task, run
194
+ // the new one and then reattempt to continue running the old task
195
+
196
+ auto oldTask = _swift_task_clearCurrent ();
197
+ assert (oldTask == waitingTask);
198
+
199
+ SWIFT_TASK_DEBUG_LOG (" [RunInline] Switching away from running %p to now running %p" , oldTask, this );
200
+ // Run the new task on the same thread now - this should run the new task to
201
+ // completion. All swift tasks in task-to-thread model run on generic
202
+ // executor
203
+ swift_job_run (this , ExecutorRef::generic ());
204
+
205
+ SWIFT_TASK_DEBUG_LOG (" [RunInline] Switching back from running %p to now running %p" , this , oldTask);
206
+
207
+ // We now are back in the context of the waiting task and need to reevaluate
208
+ // our state
209
+ _swift_task_setCurrent (oldTask);
210
+ queueHead = fragment->waitQueue .load (std::memory_order_acquire);
211
+ continue ;
212
+ #else
190
213
// Put the waiting task at the beginning of the wait queue.
191
214
waitingTask->getNextWaitingTask () = queueHead.getTask ();
192
215
auto newQueueHead = WaitQueueItem::get (Status::Executing, waitingTask);
@@ -198,6 +221,7 @@ FutureFragment::Status AsyncTask::waitFuture(AsyncTask *waitingTask,
198
221
_swift_task_clearCurrent ();
199
222
return FutureFragment::Status::Executing;
200
223
}
224
+ #endif /* SWIFT_CONCURRENCY_TASK_TO_THREAD_MODEL */
201
225
}
202
226
}
203
227
@@ -255,8 +279,13 @@ void AsyncTask::completeFuture(AsyncContext *context) {
255
279
// Schedule every waiting task on the executor.
256
280
auto waitingTask = queueHead.getTask ();
257
281
258
- if (!waitingTask)
282
+ if (!waitingTask) {
259
283
SWIFT_TASK_DEBUG_LOG (" task %p had no waiting tasks" , this );
284
+ } else {
285
+ #if SWIFT_CONCURRENCY_TASK_TO_THREAD_MODEL
286
+ assert (false && " Task should have no waiters in task-to-thread model" );
287
+ #endif
288
+ }
260
289
261
290
while (waitingTask) {
262
291
// Find the next waiting task before we invalidate it by resuming
@@ -574,12 +603,16 @@ static inline bool isUnspecified(JobPriority priority) {
574
603
return priority == JobPriority::Unspecified;
575
604
}
576
605
577
- static inline bool taskIsUnstructured (JobFlags jobFlags) {
578
- return !jobFlags.task_isAsyncLetTask () && !jobFlags.task_isGroupChildTask ();
606
+ static inline bool taskIsStructured (JobFlags jobFlags) {
607
+ return jobFlags.task_isAsyncLetTask () || jobFlags.task_isGroupChildTask ();
608
+ }
609
+
610
+ static inline bool taskIsUnstructured (TaskCreateFlags createFlags, JobFlags jobFlags) {
611
+ return !taskIsStructured (jobFlags) && !createFlags.isInlineTask ();
579
612
}
580
613
581
614
static inline bool taskIsDetached (TaskCreateFlags createFlags, JobFlags jobFlags) {
582
- return taskIsUnstructured (jobFlags) && !createFlags.copyTaskLocals ();
615
+ return taskIsUnstructured (createFlags, jobFlags) && !createFlags.copyTaskLocals ();
583
616
}
584
617
585
618
static std::pair<size_t , size_t > amountToAllocateForHeaderAndTask (
@@ -671,6 +704,9 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
671
704
}
672
705
case TaskOptionRecordKind::RunInline: {
673
706
runInlineOption = cast<RunInlineTaskOptionRecord>(option);
707
+ // TODO (rokhinip): We seem to be creating runInline tasks like detached
708
+ // tasks but they need to maintain the voucher and priority of calling
709
+ // thread and therefore need to behave a bit more like SC child tasks.
674
710
break ;
675
711
}
676
712
}
@@ -692,20 +728,27 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
692
728
// Start with user specified priority at creation time (if any)
693
729
JobPriority basePriority = (taskCreateFlags.getRequestedPriority ());
694
730
695
- if (taskIsDetached (taskCreateFlags, jobFlags)) {
696
- SWIFT_TASK_DEBUG_LOG (" Creating a detached task from %p" , currentTask);
697
- // Case 1: No priority specified
698
- // Base priority = UN
699
- // Escalated priority = UN
700
- // Case 2: Priority specified
701
- // Base priority = user specified priority
702
- // Escalated priority = UN
703
- //
704
- // Task will be created with max priority = max(base priority, UN) = base
705
- // priority. We shouldn't need to do any additional manipulations here since
706
- // basePriority should already be the right value
731
+ if (taskCreateFlags.isInlineTask ()) {
732
+ SWIFT_TASK_DEBUG_LOG (" Creating an inline task from %p" , currentTask);
733
+
734
+ // We'll take the current priority and set it as base and escalated
735
+ // priority of the task. No UI->IN downgrade needed.
736
+ basePriority = swift_task_getCurrentThreadPriority ();
707
737
708
- } else if (taskIsUnstructured (jobFlags)) {
738
+ } else if (taskIsDetached (taskCreateFlags, jobFlags)) {
739
+ SWIFT_TASK_DEBUG_LOG (" Creating a detached task from %p" , currentTask);
740
+ // Case 1: No priority specified
741
+ // Base priority = UN
742
+ // Escalated priority = UN
743
+ // Case 2: Priority specified
744
+ // Base priority = user specified priority
745
+ // Escalated priority = UN
746
+ //
747
+ // Task will be created with max priority = max(base priority, UN) = base
748
+ // priority. We shouldn't need to do any additional manipulations here since
749
+ // basePriority should already be the right value
750
+
751
+ } else if (taskIsUnstructured (taskCreateFlags, jobFlags)) {
709
752
SWIFT_TASK_DEBUG_LOG (" Creating an unstructured task from %p" , currentTask);
710
753
711
754
if (isUnspecified (basePriority)) {
@@ -934,6 +977,15 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
934
977
// Attach to the group, if needed.
935
978
if (group) {
936
979
swift_taskGroup_attachChild (group, task);
980
+ #if SWIFT_CONCURRENCY_TASK_TO_THREAD_MODEL
981
+ // We need to take a retain here to keep the child task for the task group
982
+ // alive. In the non-task-to-thread model, we'd always take this retain
983
+ // below since we'd enqueue the child task. But since we're not going to be
984
+ // enqueueing the child task in this model, we need to take this +1 to
985
+ // balance out the release that exists after the task group child task
986
+ // creation
987
+ swift_retain (task);
988
+ #endif
937
989
}
938
990
939
991
// If we're supposed to copy task locals, do so now.
@@ -948,6 +1000,9 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
948
1000
949
1001
// If we're supposed to enqueue the task, do so now.
950
1002
if (taskCreateFlags.enqueueJob ()) {
1003
+ #if SWIFT_CONCURRENCY_TASK_TO_THREAD_MODEL
1004
+ assert (false && " Should not be enqueuing tasks in task-to-thread model" );
1005
+ #endif
951
1006
swift_retain (task);
952
1007
task->flagAsAndEnqueueOnExecutor (executor);
953
1008
}
@@ -1009,8 +1064,10 @@ void swift::swift_task_run_inline(OpaqueValue *result, void *closureAFP,
1009
1064
// containing a pointer to the allocation enabling us to provide our stack
1010
1065
// allocation rather than swift_task_create_common having to malloc it.
1011
1066
RunInlineTaskOptionRecord option (allocation, allocationBytes);
1067
+ size_t taskCreateFlags = 1 << TaskCreateFlags::Task_IsInlineTask;
1068
+
1012
1069
auto taskAndContext = swift_task_create_common (
1013
- /* rawTaskCreateFlags= */ 0 , &option, futureResultType,
1070
+ taskCreateFlags , &option, futureResultType,
1014
1071
reinterpret_cast <TaskContinuationFunction *>(closure), closureContext,
1015
1072
/* initialContextSize=*/ closureContextSize);
1016
1073
0 commit comments