Skip to content

Commit edc81e8

Browse files
authored
Merge pull request #78625 from ktoso/wip-taskpriority-escalate-api
[Concurrency] Task priority escalation APIs
2 parents 63b2f58 + 26c521d commit edc81e8

File tree

11 files changed

+443
-5
lines changed

11 files changed

+443
-5
lines changed

include/swift/ABI/MetadataValues.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1678,7 +1678,7 @@ namespace SpecialPointerAuthDiscriminators {
16781678
const uint16_t AsyncContextResume = 0xd707; // = 55047
16791679
const uint16_t AsyncContextYield = 0xe207; // = 57863
16801680
const uint16_t CancellationNotificationFunction = 0x1933; // = 6451
1681-
const uint16_t EscalationNotificationFunction = 0x5be4; // = 23524
1681+
const uint16_t EscalationNotificationFunction = 0xf59d; // = 62877
16821682
const uint16_t AsyncThinNullaryFunction = 0x0f08; // = 3848
16831683
const uint16_t AsyncFutureFunction = 0x720f; // = 29199
16841684

include/swift/ABI/TaskStatus.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,9 @@ class CancellationNotificationStatusRecord : public TaskStatusRecord {
242242
: TaskStatusRecord(TaskStatusRecordKind::CancellationNotification),
243243
Function(fn), Argument(arg) {}
244244

245-
void run() { Function(Argument); }
245+
void run() {
246+
Function(Argument);
247+
}
246248

247249
static bool classof(const TaskStatusRecord *record) {
248250
return record->getKind() == TaskStatusRecordKind::CancellationNotification;
@@ -259,7 +261,7 @@ class CancellationNotificationStatusRecord : public TaskStatusRecord {
259261
/// subsequently used.
260262
class EscalationNotificationStatusRecord : public TaskStatusRecord {
261263
public:
262-
using FunctionType = void(void *, JobPriority);
264+
using FunctionType = SWIFT_CC(swift) void(JobPriority, SWIFT_CONTEXT void *);
263265

264266
private:
265267
FunctionType *__ptrauth_swift_escalation_notification_function Function;
@@ -268,9 +270,12 @@ class EscalationNotificationStatusRecord : public TaskStatusRecord {
268270
public:
269271
EscalationNotificationStatusRecord(FunctionType *fn, void *arg)
270272
: TaskStatusRecord(TaskStatusRecordKind::EscalationNotification),
271-
Function(fn), Argument(arg) {}
273+
Function(fn), Argument(arg) {
274+
}
272275

273-
void run(JobPriority newPriority) { Function(Argument, newPriority); }
276+
void run(JobPriority newPriority) {
277+
Function(newPriority, Argument);
278+
}
274279

275280
static bool classof(const TaskStatusRecord *record) {
276281
return record->getKind() == TaskStatusRecordKind::EscalationNotification;

include/swift/Runtime/Concurrency.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,18 @@ SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
609609
void swift_task_removeCancellationHandler(
610610
CancellationNotificationStatusRecord *record);
611611

612+
/// Create and add an priority escalation record to the task.
613+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
614+
EscalationNotificationStatusRecord*
615+
swift_task_addPriorityEscalationHandler(
616+
EscalationNotificationStatusRecord::FunctionType handler,
617+
void *handlerContext);
618+
619+
/// Remove the passed priority escalation record from the task.
620+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
621+
void swift_task_removePriorityEscalationHandler(
622+
EscalationNotificationStatusRecord *record);
623+
612624
/// Create a NullaryContinuationJob from a continuation.
613625
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
614626
NullaryContinuationJob*

stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,17 @@ OVERRIDE_TASK(task_removeCancellationHandler, void,
206206
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::,
207207
(CancellationNotificationStatusRecord *record), (record))
208208

209+
OVERRIDE_TASK(task_addPriorityEscalationHandler,
210+
EscalationNotificationStatusRecord *,
211+
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::,
212+
(EscalationNotificationStatusRecord::FunctionType handler,
213+
void *context),
214+
(handler, context))
215+
216+
OVERRIDE_TASK(task_removePriorityEscalationHandler, void,
217+
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::,
218+
(EscalationNotificationStatusRecord *record), (record))
219+
209220
OVERRIDE_TASK(task_createNullaryContinuationJob, NullaryContinuationJob *,
210221
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::,
211222
(size_t priority,

stdlib/public/Concurrency/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ set(SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES
140140
PartialAsyncTask.swift
141141
SourceCompatibilityShims.swift
142142
Task.swift
143+
Task+PriorityEscalation.swift
143144
Task+TaskExecutor.swift
144145
TaskCancellation.swift
145146
TaskGroup.swift
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2020 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import Swift
14+
@_implementationOnly import SwiftConcurrencyInternalShims
15+
16+
// ==== Task Priority Escalation -----------------------------------------------
17+
18+
extension Task {
19+
/// Escalate the task `priority` of the passed in task to the `newPriority`.
20+
///
21+
/// - Warning: This API should rarely be used, and instead you can rely on
22+
/// structured concurrency and implicit priority escalation which happens
23+
/// when a higher priority task awaits on a result of a lower priority task.
24+
///
25+
/// I.e. using `await` on the target task usually is the correct way to
26+
/// escalate the target task to the current priority of the calling task,
27+
/// especially because in such setup if the waiting task gets escalated,
28+
/// the waited on task would be escalated automatically as well.
29+
///
30+
/// The concurrency runtime is free to interpret and handle escalation
31+
/// depending on platform characteristics.
32+
///
33+
/// Priority escalation is propagated to child tasks of the waited-on task,
34+
/// and will trigger any priority escalation handlers, if any were registered.
35+
///
36+
/// Escalation can only *increase* the priority of a task, and
37+
/// de-escalating priority is not supported.
38+
///
39+
/// This method can be called from any task or thread.
40+
///
41+
/// - Parameters:
42+
/// - task: the task which to escalate the priority of
43+
/// - newPriority: the new priority the task should continue executing on
44+
@available(SwiftStdlib 6.2, *)
45+
public static func escalatePriority(_ task: Task, to newPriority: TaskPriority) {
46+
_taskEscalate(task._task, newPriority: newPriority.rawValue)
47+
}
48+
}
49+
50+
extension UnsafeCurrentTask {
51+
/// Escalate the task `priority` of the passed in task to the `newPriority`.
52+
///
53+
/// - Warning: This API should rarely be used, and instead you can rely on
54+
/// structured concurrency and implicit priority escalation which happens
55+
/// when a higher priority task awaits on a result of a lower priority task.
56+
///
57+
/// I.e. using `await` on the target task usually is the correct way to
58+
/// escalate the target task to the current priority of the calling task,
59+
/// especially because in such setup if the waiting task gets escalated,
60+
/// the waited on task would be escalated automatically as well.
61+
///
62+
/// The concurrency runtime is free to interpret and handle escalation
63+
/// depending on platform characteristics.
64+
///
65+
/// Priority escalation is propagated to child tasks of the waited-on task,
66+
/// and will trigger any priority escalation handlers, if any were registered.
67+
///
68+
/// Escalation can only *increase* the priority of a task, and
69+
/// de-escalating priority is not supported.
70+
///
71+
/// This method can be called from any task or thread.
72+
///
73+
/// - Parameters:
74+
/// - task: the task which to escalate the priority of
75+
/// - newPriority: the new priority the task should continue executing on
76+
@available(SwiftStdlib 6.2, *)
77+
public static func escalatePriority(_ task: UnsafeCurrentTask, to newPriority: TaskPriority) {
78+
_taskEscalate(task._task, newPriority: newPriority.rawValue)
79+
}
80+
}
81+
82+
// ==== Task Priority Escalation Handlers --------------------------------------
83+
84+
/// Runs the passed `operation` while registering a task priority escalation handler.
85+
/// The handler will be triggered concurrently to the current task if the current
86+
/// is subject to priority escalation.
87+
///
88+
/// The handler may perform additional actions upon priority escalation,
89+
/// but cannot influence how the escalation influences the task, i.e. the task's
90+
/// priority will be escalated regardless of actions performed in the handler.
91+
///
92+
/// The handler will only trigger if a priority escalation occurs while the
93+
/// operation is in progress.
94+
///
95+
/// If multiple task escalation handlers are nester they will all be triggered.
96+
///
97+
/// Task escalation propagates through structured concurrency child-tasks.
98+
///
99+
/// - Parameters:
100+
/// - operation: the operation during which to listen for priority escalation
101+
/// - handler: handler to invoke, concurrently to `operation`,
102+
/// when priority escalation happens
103+
/// - Returns: the value returned by `operation`
104+
/// - Throws: when the `operation` throws an error
105+
@available(SwiftStdlib 6.2, *)
106+
public func withTaskPriorityEscalationHandler<T, E>(
107+
operation: () async throws(E) -> T,
108+
onPriorityEscalated handler: @Sendable (TaskPriority) -> Void,
109+
isolation: isolated (any Actor)? = #isolation
110+
) async throws(E) -> T {
111+
// NOTE: We have to create the closure beforehand as otherwise it seems
112+
// the task-local allocator may be used and we end up violating stack-discipline
113+
// when releasing the handler closure vs. the record.
114+
let handler0: (UInt8) -> Void = {
115+
handler(TaskPriority(rawValue: $0))
116+
}
117+
let record = _taskAddPriorityEscalationHandler(handler: handler0)
118+
defer { _taskRemovePriorityEscalationHandler(record: record) }
119+
120+
return try await operation()
121+
}

stdlib/public/Concurrency/Task.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1766,6 +1766,31 @@ static void swift_task_removeCancellationHandlerImpl(
17661766
swift_task_dealloc(record);
17671767
}
17681768

1769+
SWIFT_CC(swift)
1770+
static EscalationNotificationStatusRecord*
1771+
swift_task_addPriorityEscalationHandlerImpl(
1772+
EscalationNotificationStatusRecord::FunctionType handler,
1773+
void *context) {
1774+
void *allocation =
1775+
swift_task_alloc(sizeof(EscalationNotificationStatusRecord));
1776+
auto unsigned_handler = swift_auth_code(handler, 62877);
1777+
auto *record = ::new (allocation)
1778+
EscalationNotificationStatusRecord(handler, context);
1779+
1780+
addStatusRecordToSelf(record, [&](ActiveTaskStatus oldStatus, ActiveTaskStatus& newStatus) {
1781+
return true;
1782+
});
1783+
1784+
return record;
1785+
}
1786+
1787+
SWIFT_CC(swift)
1788+
static void swift_task_removePriorityEscalationHandlerImpl(
1789+
EscalationNotificationStatusRecord *record) {
1790+
removeStatusRecordFromSelf(record);
1791+
swift_task_dealloc(record);
1792+
}
1793+
17691794
SWIFT_CC(swift)
17701795
static NullaryContinuationJob*
17711796
swift_task_createNullaryContinuationJobImpl(

stdlib/public/Concurrency/Task.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1071,6 +1071,20 @@ func _enqueueJobGlobalWithDeadline(_ seconds: Int64, _ nanoseconds: Int64,
10711071
_ toleranceSec: Int64, _ toleranceNSec: Int64,
10721072
_ clock: Int32, _ task: Builtin.Job)
10731073

1074+
@usableFromInline
1075+
@available(SwiftStdlib 9999, *)
1076+
@_silgen_name("swift_task_addPriorityEscalationHandler")
1077+
func _taskAddPriorityEscalationHandler(
1078+
handler: (UInt8) -> Void
1079+
) -> UnsafeRawPointer /*EscalationNotificationStatusRecord*/
1080+
1081+
@usableFromInline
1082+
@available(SwiftStdlib 9999, *)
1083+
@_silgen_name("swift_task_removePriorityEscalationHandler")
1084+
func _taskRemovePriorityEscalationHandler(
1085+
record: UnsafeRawPointer /*EscalationNotificationStatusRecord*/
1086+
)
1087+
10741088
@available(SwiftStdlib 5.1, *)
10751089
@usableFromInline
10761090
@_silgen_name("swift_task_asyncMainDrainQueue")
@@ -1152,6 +1166,11 @@ func _taskIsCancelled(_ task: Builtin.NativeObject) -> Bool
11521166
@_silgen_name("swift_task_currentPriority")
11531167
internal func _taskCurrentPriority(_ task: Builtin.NativeObject) -> UInt8
11541168

1169+
@available(SwiftStdlib 6.2, *)
1170+
@_silgen_name("swift_task_escalate")
1171+
internal func _taskEscalate(_ task: Builtin.NativeObject, newPriority: UInt8)
1172+
1173+
@available(SwiftStdlib 5.1, *)
11551174
@_silgen_name("swift_task_basePriority")
11561175
internal func _taskBasePriority(_ task: Builtin.NativeObject) -> UInt8
11571176

0 commit comments

Comments
 (0)