Skip to content

Commit f0f83ec

Browse files
committed
Add better example, thanks to @dnadoba
1 parent 2cb8e23 commit f0f83ec

File tree

1 file changed

+49
-7
lines changed

1 file changed

+49
-7
lines changed

Diff for: proposals/NNNN-task-priority-escalation-apis.md

+49-7
Original file line numberDiff line numberDiff line change
@@ -69,19 +69,58 @@ Another example of libraries which may want to reach for manual task priority es
6969
In order to address the above use-cases, we propose to add a pair of APIs: to react to priority escalation happening within a block of code, and an API to _cause_ a priority escalation without resorting to trickery using creating new tasks whose only purpose is to escalate the priority of some other task:
7070

7171
```swift
72-
let m: Mutex<Task<Void, Never>?>
72+
enum State {
73+
case initialized
74+
case task(Task<Void, Never>)
75+
case priority(TaskPriority)
76+
}
77+
let m: Mutex<State> = .init(.initialized)
7378

7479
await withTaskPriorityEscalationHandler {
75-
await withCheckedContinuation { cc in
76-
let t = Task { cc.resume() }
77-
m.withLock { $0 = t }
78-
} onPriorityEscalated: { newPriority in
79-
let t = m.withLock { $0 }
80-
Task.escalatePriority(t, to: newPriority)
80+
await withCheckedContinuation { cc in
81+
let task = Task { cc.resume() }
82+
83+
let newPriority: TaskPriority? = state.withLock { state -> TaskPriority? in
84+
defer { state = .task(task) }
85+
switch state {
86+
case .initialized:
87+
return nil
88+
case .task:
89+
preconditionFailure("unreachable")
90+
case .priority(let priority):
91+
return priority
92+
}
93+
}
94+
// priority was escalated just before we have store the task in the mutex
95+
if let newPriority {
96+
Task.escalatePriority(task, to: newPriority)
97+
}
98+
} onPriorityEscalated: { newPriority in
99+
state.withLock { state in
100+
switch state {
101+
case .initialized, .priority:
102+
// priority was escalated just before managed to store the task in the mutex
103+
state = .priority(newPriority)
104+
case .task(let task):
105+
Task.escalatePriority(task, to: newPriority)
106+
}
107+
}
81108
}
82109
}
83110
```
84111

112+
The above snippet handles edge various ordering situations, including the task escalation happening after
113+
the time the handler is registered but _before_ we managed to create and store the task.
114+
115+
If priority escalation happened before the handler was installed, the `Task.currentPriority` will
116+
also naturally have the escalated value while the operation executes, meaning that the `Task` created
117+
during the `operation` would naturally _start at_ the appropriate escalated priority, because it inherits the
118+
outer task's priority by querying Task.currentPriority.
119+
120+
In general, task escalation remains a slightly racy affair, we could always observe an escalation "too late" for it to matter,
121+
and have any meaningful effect on the work's execution, however this API and associated patterns handle most situations which
122+
we care about in practice.
123+
85124
## Detailed design
86125

87126
We propose the addition of a task priority escalation handler, similar to task cancellation handlers already present in the concurrency library:
@@ -195,3 +234,6 @@ While at first this looks promising, we did not really remove much of the comple
195234

196235
Overall, this seems like a tightly knit API that changes current idioms of `with...Handler ` without really saving us from the inherent complexity of these handlers being invoked concurrently, and limiting the usefulness of those handlers to just "around a continuation" which may not always be the case.
197236

237+
### Acknowledgements
238+
239+
We'd like to thank John McCall, David Nadoba for their input on the APIs during early reviews.

0 commit comments

Comments
 (0)