Skip to content

Commit e2344fb

Browse files
committed
[Concurrency] Set thread base priority when running escalated Tasks
1 parent 105ac4b commit e2344fb

File tree

7 files changed

+68
-25
lines changed

7 files changed

+68
-25
lines changed

include/swift/ABI/Task.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@
2929
#include "bitset"
3030
#include "queue" // TODO: remove and replace with our own mpsc
3131

32+
// Does the runtime integrate with libdispatch?
33+
#if defined(SWIFT_CONCURRENCY_USES_DISPATCH)
34+
#define SWIFT_CONCURRENCY_ENABLE_DISPATCH SWIFT_CONCURRENCY_USES_DISPATCH
35+
#else
36+
#define SWIFT_CONCURRENCY_ENABLE_DISPATCH 0
37+
#endif
38+
3239
// Does the runtime provide priority escalation support?
3340
#ifndef SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
3441
#if SWIFT_CONCURRENCY_ENABLE_DISPATCH && \
@@ -422,7 +429,11 @@ class AsyncTask : public Job {
422429
///
423430
/// Generally this should be done immediately after updating
424431
/// ActiveTask.
425-
void flagAsRunning();
432+
///
433+
/// If the return value is non-zero, it must be passed to
434+
/// swift_dispatch_thread_reset_override_self. If it is zero,
435+
/// calling that function is optional.
436+
uint32_t flagAsRunning();
426437

427438
/// Flag that this task is now suspended with information about what it is
428439
/// waiting on.

include/swift/Runtime/Concurrency.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,6 @@
3838
#define SWIFT_CONCURRENCY_TASK_TO_THREAD_MODEL 0
3939
#endif
4040

41-
// Does the runtime integrate with libdispatch?
42-
#if defined(SWIFT_CONCURRENCY_USES_DISPATCH)
43-
#define SWIFT_CONCURRENCY_ENABLE_DISPATCH SWIFT_CONCURRENCY_USES_DISPATCH
44-
#else
45-
#define SWIFT_CONCURRENCY_ENABLE_DISPATCH 0
46-
#endif
47-
4841
namespace swift {
4942
class DefaultActor;
5043
class TaskOptionRecord;

include/swift/Runtime/DispatchShims.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,27 @@ swift_dispatch_thread_override_self(qos_class_t override_qos) {
4848
return 0;
4949
}
5050

51+
static inline uint32_t
52+
swift_dispatch_thread_override_self_with_base(qos_class_t override_qos, qos_class_t base_qos) {
53+
54+
if (__builtin_available(macOS 27.0, iOS 27.0, tvOS 27.0, watchOS 27.0, *)) {
55+
return dispatch_thread_override_self_with_base(override_qos, base_qos);
56+
} else if (__builtin_available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)) {
57+
// If we don't have the ability to set our base qos correctly, at least set the override
58+
// We want to return 0 here because we have nothing to reset in this case
59+
(void) dispatch_thread_override_self(override_qos);
60+
}
61+
62+
return 0;
63+
}
64+
65+
static inline void
66+
swift_dispatch_thread_reset_override_self(uint32_t opaque) {
67+
if (__builtin_available(macOS 27.0, iOS 27.0, tvOS 27.0, watchOS 27.0, *)) {
68+
dispatch_thread_reset_override_self(opaque);
69+
}
70+
}
71+
5172
static inline int
5273
swift_dispatch_lock_override_start_with_debounce(dispatch_lock_t *lock_addr,
5374
dispatch_tid_t expected_thread, qos_class_t override_to_apply) {

stdlib/public/Concurrency/Actor.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,12 +237,14 @@ void swift::runJobInEstablishedExecutorContext(Job *job) {
237237
// current thread. If the task suspends somewhere, it should
238238
// update the task status appropriately; we don't need to update
239239
// it afterwards.
240-
task->flagAsRunning();
240+
uint32_t dispatch_opaque_priority = task->flagAsRunning();
241241

242242
auto traceHandle = concurrency::trace::job_run_begin(job);
243243
task->runInFullyEstablishedContext();
244244
concurrency::trace::job_run_end(traceHandle);
245245

246+
swift_dispatch_thread_reset_override_self(dispatch_opaque_priority);
247+
246248
assert(ActiveTask::get() == nullptr &&
247249
"active task wasn't cleared before suspending?");
248250
if (oldTask) ActiveTask::set(oldTask);

stdlib/public/Concurrency/Task.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ FutureFragment::Status AsyncTask::waitFuture(AsyncTask *waitingTask,
132132
SWIFT_TASK_DEBUG_LOG("task %p waiting on task %p, completed immediately",
133133
waitingTask, this);
134134
_swift_tsan_acquire(static_cast<Job *>(this));
135-
if (contextInitialized) waitingTask->flagAsRunning();
135+
// Continuting execution inline so we shouldn't have to reset priority here
136+
if (contextInitialized) assert(waitingTask->flagAsRunning() == 0);
136137
// The task is done; we don't need to wait.
137138
return queueHead.getStatus();
138139

@@ -1666,8 +1667,9 @@ static void swift_continuation_awaitImpl(ContinuationAsyncContext *context) {
16661667
// we try to tail-call.
16671668
} while (false);
16681669
#else
1669-
// Restore the running state of the task and resume it.
1670-
task->flagAsRunning();
1670+
// Restore the running state of the task and resume it. We are on the same
1671+
// thread as before so dispatch shouldn't pass us anything to restore
1672+
assert(task->flagAsRunning() == 0);
16711673
#endif /* SWIFT_CONCURRENCY_TASK_TO_THREAD_MODEL */
16721674

16731675
if (context->isExecutorSwitchForced())

stdlib/public/Concurrency/TaskGroup.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1822,7 +1822,8 @@ reevaluate_if_taskgroup_has_results:;
18221822
// We're going back to running the task, so if we suspended before,
18231823
// we need to flag it as running again.
18241824
if (hasSuspended) {
1825-
waitingTask->flagAsRunning();
1825+
// Continuting execution inline so we shouldn't have to reset priority here
1826+
assert(waitingTask->flagAsRunning() == 0);
18261827
}
18271828

18281829
// Success! We are allowed to poll.

stdlib/public/Concurrency/TaskPrivate.h

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -935,32 +935,40 @@ inline bool AsyncTask::isCancelled() const {
935935
.isCancelled();
936936
}
937937

938-
inline void AsyncTask::flagAsRunning() {
938+
inline uint32_t AsyncTask::flagAsRunning() {
939939

940940
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
941941
dispatch_thread_override_info_s threadOverrideInfo;
942942
threadOverrideInfo = swift_dispatch_thread_get_current_override_qos_floor();
943943
qos_class_t overrideFloor = threadOverrideInfo.override_qos_floor;
944+
qos_class_t basePriorityCeil = overrideFloor;
945+
qos_class_t taskBasePriority = (qos_class_t) _private().BasePriority;
944946
#endif
945947

946948
auto oldStatus = _private()._status().load(std::memory_order_relaxed);
947949
assert(!oldStatus.isRunning());
948950
assert(!oldStatus.isComplete());
949951

952+
uint32_t dispatch_opaque_priority = 0;
950953
if (!oldStatus.hasTaskDependency()) {
951954
SWIFT_TASK_DEBUG_LOG("%p->flagAsRunning() with no task dependency", this);
952955
assert(_private().dependencyRecord == nullptr);
953956

954957
while (true) {
955958
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
956-
// Task's priority is greater than the thread's - do a self escalation
959+
// If the base priority is not equal to the current override floor then
960+
// dispqatch may need to apply the base priority to the thread. If the
961+
// current priority is higher than the override floor, then dispatch may
962+
// need to apply a self-override. In either case, call into dispatch to
963+
// do this.
957964
qos_class_t maxTaskPriority = (qos_class_t) oldStatus.getStoredPriority();
958-
if (threadOverrideInfo.can_override && (maxTaskPriority > overrideFloor)) {
959-
SWIFT_TASK_DEBUG_LOG("[Override] Self-override thread with oq_floor %#x to match task %p's max priority %#x",
960-
overrideFloor, this, maxTaskPriority);
965+
if (threadOverrideInfo.can_override && (taskBasePriority != basePriorityCeil || maxTaskPriority > overrideFloor)) {
966+
SWIFT_TASK_DEBUG_LOG("[Override] Self-override thread with oq_floor %#x to match task %p's max priority %#x and base priority %#x",
967+
overrideFloor, this, maxTaskPriority, taskBasePriority);
961968

962-
(void) swift_dispatch_thread_override_self(maxTaskPriority);
969+
dispatch_opaque_priority = swift_dispatch_thread_override_self_with_base(maxTaskPriority, taskBasePriority);
963970
overrideFloor = maxTaskPriority;
971+
basePriorityCeil = taskBasePriority;
964972
}
965973
#endif
966974
// Set self as executor and remove escalation bit if any - the task's
@@ -989,14 +997,19 @@ inline void AsyncTask::flagAsRunning() {
989997
ActiveTaskStatus& newStatus) {
990998

991999
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
992-
// Task's priority is greater than the thread's - do a self escalation
1000+
// If the base priority is not equal to the current override floor then
1001+
// dispqatch may need to apply the base priority to the thread. If the
1002+
// current priority is higher than the override floor, then dispatch may
1003+
// need to apply a self-override. In either case, call into dispatch to
1004+
// do this.
9931005
qos_class_t maxTaskPriority = (qos_class_t) oldStatus.getStoredPriority();
994-
if (threadOverrideInfo.can_override && (maxTaskPriority > overrideFloor)) {
995-
SWIFT_TASK_DEBUG_LOG("[Override] Self-override thread with oq_floor %#x to match task %p's max priority %#x",
996-
overrideFloor, this, maxTaskPriority);
1006+
if (threadOverrideInfo.can_override && (taskBasePriority != basePriorityCeil || maxTaskPriority > overrideFloor)) {
1007+
SWIFT_TASK_DEBUG_LOG("[Override] Self-override thread with oq_floor %#x to match task %p's max priority %#x and base priority %#x",
1008+
overrideFloor, this, maxTaskPriority, taskBasePriority);
9971009

998-
(void) swift_dispatch_thread_override_self(maxTaskPriority);
1010+
dispatch_opaque_priority = swift_dispatch_thread_override_self_with_base(maxTaskPriority, taskBasePriority);
9991011
overrideFloor = maxTaskPriority;
1012+
basePriorityCeil = taskBasePriority;
10001013
}
10011014
#endif
10021015
// Set self as executor and remove escalation bit if any - the task's
@@ -1012,7 +1025,7 @@ inline void AsyncTask::flagAsRunning() {
10121025
swift_task_enterThreadLocalContext(
10131026
(char *)&_private().ExclusivityAccessSet[0]);
10141027
}
1015-
1028+
return dispatch_opaque_priority;
10161029
}
10171030

10181031
/// TODO (rokhinip): We need the handoff of the thread to the next executor to

0 commit comments

Comments
 (0)