@@ -413,7 +413,7 @@ static void completeTaskWithClosure(SWIFT_ASYNC_CONTEXT AsyncContext *context,
413413 reinterpret_cast <char *>(context) - sizeof (AsyncContextPrefix));
414414
415415 swift_release ((HeapObject *)asyncContextPrefix->closureContext );
416-
416+
417417 // Clean up the rest of the task.
418418 return completeTaskAndRelease (context, error);
419419}
@@ -470,6 +470,32 @@ const void *AsyncTask::getResumeFunctionForLogging() {
470470 return reinterpret_cast <const void *>(ResumeTask);
471471}
472472
473+ JobPriority swift::swift_task_currentPriority (AsyncTask *task)
474+ {
475+ // This is racey but this is to be used in an API is inherently racey anyways.
476+ auto oldStatus = task->_private ().Status .load (std::memory_order_relaxed);
477+ return oldStatus.getStoredPriority ();
478+ }
479+
480+ JobPriority swift::swift_task_basePriority (AsyncTask *task)
481+ {
482+ JobPriority pri = task->_private ().BasePriority ;
483+ SWIFT_TASK_DEBUG_LOG (" Task %p has base priority = %zu" , task, pri);
484+ return pri;
485+ }
486+
487+ static inline bool isUnspecified (JobPriority priority) {
488+ return priority == JobPriority::Unspecified;
489+ }
490+
491+ static inline bool taskIsUnstructured (JobFlags jobFlags) {
492+ return !jobFlags.task_isAsyncLetTask () && !jobFlags.task_isGroupChildTask ();
493+ }
494+
495+ static inline bool taskIsDetached (TaskCreateFlags createFlags, JobFlags jobFlags) {
496+ return taskIsUnstructured (jobFlags) && !createFlags.copyTaskLocals ();
497+ }
498+
473499// / Implementation of task creation.
474500SWIFT_CC (swift)
475501static AsyncTaskAndContext swift_task_create_commonImpl(
@@ -478,10 +504,11 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
478504 const Metadata *futureResultType,
479505 TaskContinuationFunction *function, void *closureContext,
480506 size_t initialContextSize) {
507+
481508 TaskCreateFlags taskCreateFlags (rawTaskCreateFlags);
509+ JobFlags jobFlags (JobKind::Task, JobPriority::Unspecified);
482510
483511 // Propagate task-creation flags to job flags as appropriate.
484- JobFlags jobFlags (JobKind::Task, taskCreateFlags.getPriority ());
485512 jobFlags.task_setIsChildTask (taskCreateFlags.isChildTask ());
486513 if (futureResultType) {
487514 jobFlags.task_setIsFuture (true );
@@ -520,7 +547,7 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
520547 // of in a FutureFragment.
521548 hasAsyncLetResultBuffer = true ;
522549 assert (asyncLet && " Missing async let storage" );
523-
550+
524551 jobFlags.task_setIsAsyncLetTask (true );
525552 jobFlags.task_setIsChildTask (true );
526553 break ;
@@ -534,32 +561,85 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
534561 }
535562
536563 AsyncTask *parent = nullptr ;
564+ AsyncTask *currentTask = swift_task_getCurrent ();
537565 if (jobFlags.task_isChildTask ()) {
538- parent = swift_task_getCurrent () ;
566+ parent = currentTask ;
539567 assert (parent != nullptr && " creating a child task with no active task" );
540568 }
541569
542- // Inherit the priority of the currently-executing task if unspecified and
543- // we want to inherit.
544- if (jobFlags.getPriority () == JobPriority::Unspecified &&
545- (jobFlags.task_isChildTask () || taskCreateFlags.inheritContext ())) {
546- AsyncTask *currentTask = parent;
547- if (!currentTask)
548- currentTask = swift_task_getCurrent ();
549-
550- if (currentTask)
551- jobFlags.setPriority (currentTask->getPriority ());
552- else if (taskCreateFlags.inheritContext ())
553- jobFlags.setPriority (swift_task_getCurrentThreadPriority ());
570+ // Start with user specified priority at creation time (if any)
571+ JobPriority basePriority = (taskCreateFlags.getRequestedPriority ());
572+
573+ if (taskIsDetached (taskCreateFlags, jobFlags)) {
574+ SWIFT_TASK_DEBUG_LOG (" Creating a detached task from %p" , currentTask);
575+ // Case 1: No priority specified
576+ // Base priority = UN
577+ // Escalated priority = UN
578+ // Case 2: Priority specified
579+ // Base priority = user specified priority
580+ // Escalated priority = UN
581+ //
582+ // Task will be created with max priority = max(base priority, UN) = base
583+ // priority. We shouldn't need to do any additional manipulations here since
584+ // basePriority should already be the right value
585+
586+ } else if (taskIsUnstructured (jobFlags)) {
587+ SWIFT_TASK_DEBUG_LOG (" Creating an unstructured task from %p" , currentTask);
588+
589+ if (isUnspecified (basePriority)) {
590+ // Case 1: No priority specified
591+ // Base priority = Base priority of parent with a UI -> IN downgrade
592+ // Escalated priority = UN
593+ if (currentTask) {
594+ basePriority = currentTask->_private ().BasePriority ;
595+ } else {
596+ basePriority = swift_task_getCurrentThreadPriority ();
597+ }
598+ basePriority = withUserInteractivePriorityDowngrade (basePriority);
599+ } else {
600+ // Case 2: User specified a priority
601+ // Base priority = user specified priority
602+ // Escalated priority = UN
603+ }
604+
605+ // Task will be created with max priority = max(base priority, UN) = base
606+ // priority
607+ } else {
608+ // Is a structured concurrency child task. Must have a parent.
609+ assert ((asyncLet || group) && parent);
610+ SWIFT_TASK_DEBUG_LOG (" Creating an structured concurrency task from %p" , currentTask);
611+
612+ if (isUnspecified (basePriority)) {
613+ // Case 1: No priority specified
614+ // Base priority = Base priority of parent with a UI -> IN downgrade
615+ // Escalated priority = Escalated priority of parent with a UI -> IN
616+ // downgrade
617+ JobPriority parentBasePri = parent->_private ().BasePriority ;
618+ basePriority = withUserInteractivePriorityDowngrade (parentBasePri);
619+ } else {
620+ // Case 2: User priority specified
621+ // Base priority = User specified priority
622+ // Escalated priority = Escalated priority of parent with a UI -> IN
623+ // downgrade
624+ }
625+
626+ // Task will be created with escalated priority = base priority. We will
627+ // update the escalated priority with the right rules in
628+ // updateNewChildWithParentAndGroupState when we link the child into
629+ // the parent task/task group since we'll have the right
630+ // synchronization then.
631+ }
632+
633+ if (isUnspecified (basePriority)) {
634+ basePriority = JobPriority::Default;
554635 }
555636
556- // Adjust user-interactive priorities down to user-initiated.
557- if (jobFlags.getPriority () == JobPriority::UserInteractive)
558- jobFlags.setPriority (JobPriority::UserInitiated);
637+ SWIFT_TASK_DEBUG_LOG (" Task's base priority = %d" , basePriority);
559638
560- // If there is still no job priority, use the default priority.
561- if (jobFlags.getPriority () == JobPriority::Unspecified)
562- jobFlags.setPriority (JobPriority::Default);
639+ // TODO (rokhinip): Figure out the semantics of the job priority and where
640+ // it ought to be set conclusively - seems like it ought to be at enqueue
641+ // time. For now, maintain current semantics of setting jobPriority as well.
642+ jobFlags.setPriority (basePriority);
563643
564644 // Figure out the size of the header.
565645 size_t headerSize = sizeof (AsyncTask);
@@ -591,14 +671,14 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
591671 void *allocation = nullptr ;
592672 if (asyncLet) {
593673 assert (parent);
594-
674+
595675 // If there isn't enough room in the fixed async let allocation to
596676 // set up the initial context, then we'll have to allocate more space
597677 // from the parent.
598678 if (asyncLet->getSizeOfPreallocatedSpace () < amountToAllocate) {
599679 hasAsyncLetResultBuffer = false ;
600680 }
601-
681+
602682 // DEPRECATED. This is separated from the above condition because we
603683 // also have to handle an older async let ABI that did not provide
604684 // space for the initial slab in the compiler-generated preallocation.
@@ -653,7 +733,7 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
653733 // Initialize the task so that resuming it will run the given
654734 // function on the initial context.
655735 AsyncTask *task = nullptr ;
656- bool captureCurrentVoucher = taskCreateFlags. copyTaskLocals () || jobFlags. task_isChildTask ( );
736+ bool captureCurrentVoucher = ! taskIsDetached (taskCreateFlags, jobFlags);
657737 if (asyncLet) {
658738 // Initialize the refcount bits to "immortal", so that
659739 // ARC operations don't have any effect on the task.
@@ -678,7 +758,7 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
678758 auto groupChildFragment = task->groupChildFragment ();
679759 new (groupChildFragment) AsyncTask::GroupChildFragment (group);
680760 }
681-
761+
682762 // Initialize the future fragment if applicable.
683763 if (futureResultType) {
684764 assert (task->isFuture ());
@@ -694,7 +774,7 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
694774 futureAsyncContextPrefix->indirectResult = futureFragment->getStoragePtr ();
695775 }
696776
697- SWIFT_TASK_DEBUG_LOG (" creating task %p with parent %p" , task, parent);
777+ SWIFT_TASK_DEBUG_LOG (" creating task %p with parent %p at base pri %zu " , task, parent, basePriority );
698778
699779 // Initialize the task-local allocator.
700780 initialContext->ResumeParent = reinterpret_cast <TaskContinuationFunction *>(
@@ -704,9 +784,10 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
704784 if (asyncLet && initialSlabSize > 0 ) {
705785 assert (parent);
706786 void *initialSlab = (char *)allocation + amountToAllocate;
707- task->Private .initializeWithSlab (task, initialSlab, initialSlabSize);
787+ task->Private .initializeWithSlab (basePriority, initialSlab,
788+ initialSlabSize);
708789 } else {
709- task->Private .initialize (task );
790+ task->Private .initialize (basePriority );
710791 }
711792
712793 // Perform additional linking between parent and child task.
@@ -940,7 +1021,7 @@ static AsyncTask *swift_continuation_initImpl(ContinuationAsyncContext *context,
9401021 // must happen-after this call.
9411022 context->AwaitSynchronization .store (flags.isPreawaited ()
9421023 ? ContinuationStatus::Awaited
943- : ContinuationStatus::Pending,
1024+ : ContinuationStatus::Pending,
9441025 std::memory_order_relaxed);
9451026
9461027 AsyncTask *task;
0 commit comments