Skip to content

Commit a2a5c64

Browse files
committed
Change task's base and escalated priority propagation rules for
detached, unstructured and structured concurrency child tasks Radar-Id: rdar://problem/86100376
1 parent 9df1c9a commit a2a5c64

File tree

3 files changed

+100
-30
lines changed

3 files changed

+100
-30
lines changed

include/swift/ABI/MetadataValues.h

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2014,12 +2014,12 @@ enum class JobKind : size_t {
20142014
enum class JobPriority : size_t {
20152015
// This is modelled off of Dispatch.QoS, and the values are directly
20162016
// stolen from there.
2017-
UserInteractive = 0x21,
2018-
UserInitiated = 0x19,
2019-
Default = 0x15,
2020-
Utility = 0x11,
2021-
Background = 0x09,
2022-
Unspecified = 0x00,
2017+
UserInteractive = 0x21, /* UI */
2018+
UserInitiated = 0x19, /* IN */
2019+
Default = 0x15, /* DEF */
2020+
Utility = 0x11, /* UT */
2021+
Background = 0x09, /* BG */
2022+
Unspecified = 0x00, /* UN */
20232023
};
20242024

20252025
/// A tri-valued comparator which orders higher priorities first.
@@ -2028,6 +2028,11 @@ inline int descendingPriorityOrder(JobPriority lhs,
20282028
return (lhs == rhs ? 0 : lhs > rhs ? -1 : 1);
20292029
}
20302030

2031+
inline JobPriority withUserInteractivePriorityDowngrade(JobPriority priority) {
2032+
return (priority == JobPriority::UserInteractive) ? JobPriority::UserInitiated
2033+
: priority;
2034+
}
2035+
20312036
/// Flags for task creation.
20322037
class TaskCreateFlags : public FlagSet<size_t> {
20332038
public:

stdlib/public/Concurrency/Task.cpp

Lines changed: 87 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,18 @@ JobPriority swift::swift_task_basePriority(AsyncTask *task)
484484
return pri;
485485
}
486486

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+
487499
/// Implementation of task creation.
488500
SWIFT_CC(swift)
489501
static AsyncTaskAndContext swift_task_create_commonImpl(
@@ -492,10 +504,11 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
492504
const Metadata *futureResultType,
493505
TaskContinuationFunction *function, void *closureContext,
494506
size_t initialContextSize) {
507+
495508
TaskCreateFlags taskCreateFlags(rawTaskCreateFlags);
509+
JobFlags jobFlags(JobKind::Task, JobPriority::Unspecified);
496510

497511
// Propagate task-creation flags to job flags as appropriate.
498-
JobFlags jobFlags(JobKind::Task, taskCreateFlags.getCreationPriority());
499512
jobFlags.task_setIsChildTask(taskCreateFlags.isChildTask());
500513
if (futureResultType) {
501514
jobFlags.task_setIsFuture(true);
@@ -548,34 +561,85 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
548561
}
549562

550563
AsyncTask *parent = nullptr;
564+
AsyncTask *currentTask = swift_task_getCurrent();
551565
if (jobFlags.task_isChildTask()) {
552-
parent = swift_task_getCurrent();
566+
parent = currentTask;
553567
assert(parent != nullptr && "creating a child task with no active task");
554568
}
555569

556-
// Inherit the priority of the currently-executing task if unspecified and
557-
// we want to inherit.
558-
if (jobFlags.getPriority() == JobPriority::Unspecified &&
559-
(jobFlags.task_isChildTask() || taskCreateFlags.inheritContext())) {
560-
AsyncTask *currentTask = parent;
561-
if (!currentTask)
562-
currentTask = swift_task_getCurrent();
563-
564-
if (currentTask)
565-
jobFlags.setPriority(currentTask->getPriority());
566-
else if (taskCreateFlags.inheritContext())
567-
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.
568631
}
569632

570-
// Adjust user-interactive priorities down to user-initiated.
571-
if (jobFlags.getPriority() == JobPriority::UserInteractive)
572-
jobFlags.setPriority(JobPriority::UserInitiated);
633+
if (isUnspecified(basePriority)) {
634+
basePriority = JobPriority::Default;
635+
}
573636

574-
// If there is still no job priority, use the default priority.
575-
if (jobFlags.getPriority() == JobPriority::Unspecified)
576-
jobFlags.setPriority(JobPriority::Default);
637+
SWIFT_TASK_DEBUG_LOG("Task's base priority = %d", basePriority);
577638

578-
JobPriority basePriority = jobFlags.getPriority();
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);
579643

580644
// Figure out the size of the header.
581645
size_t headerSize = sizeof(AsyncTask);
@@ -669,7 +733,7 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
669733
// Initialize the task so that resuming it will run the given
670734
// function on the initial context.
671735
AsyncTask *task = nullptr;
672-
bool captureCurrentVoucher = taskCreateFlags.copyTaskLocals() || jobFlags.task_isChildTask();
736+
bool captureCurrentVoucher = !taskIsDetached(taskCreateFlags, jobFlags);
673737
if (asyncLet) {
674738
// Initialize the refcount bits to "immortal", so that
675739
// ARC operations don't have any effect on the task.
@@ -710,7 +774,7 @@ static AsyncTaskAndContext swift_task_create_commonImpl(
710774
futureAsyncContextPrefix->indirectResult = futureFragment->getStoragePtr();
711775
}
712776

713-
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);
714778

715779
// Initialize the task-local allocator.
716780
initialContext->ResumeParent = reinterpret_cast<TaskContinuationFunction *>(

stdlib/public/Concurrency/TaskStatus.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,8 @@ void swift::updateNewChildWithParentAndGroupState(AsyncTask *child,
411411
// Propagate max priority of parent to child task's active status and the Job
412412
// header
413413
JobPriority pri = parentStatus.getStoredPriority();
414-
newChildTaskStatus = newChildTaskStatus.withNewPriority(pri);
414+
newChildTaskStatus =
415+
newChildTaskStatus.withNewPriority(withUserInteractivePriorityDowngrade(pri));
415416
child->Flags.setPriority(pri);
416417

417418
child->_private().Status.store(newChildTaskStatus, std::memory_order_relaxed);

0 commit comments

Comments
 (0)