Skip to content

Commit 07f80be

Browse files
committed
[Concurrency] API stubs: Task.Handle, priority and runDetached
1 parent 9023e56 commit 07f80be

File tree

2 files changed

+161
-0
lines changed

2 files changed

+161
-0
lines changed

stdlib/public/Concurrency/Task.swift

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,142 @@ import Swift
2828
public struct Task {
2929
}
3030

31+
// ==== Task Priority ----------------------------------------------------------
32+
33+
extension Task {
34+
public struct Priority: Comparable {
35+
public static let `default`: Task.Priority = .init() // TODO: replace with actual values
36+
37+
// TODO: specifics of implementation are not decided yet
38+
let __value: Int = 0
39+
40+
public static func < (lhs: Self, rhs: Self) -> Bool {
41+
lhs.__value < rhs.__value
42+
}
43+
}
44+
}
45+
46+
// ==== Task Handle ------------------------------------------------------------
47+
48+
extension Task {
49+
50+
/// A task handle refers to an in-flight `Task`, allowing for (potentially)
51+
/// awaiting for its result or potentially canceling it.
52+
public final class Handle<Success, Failure: Error> {
53+
54+
/// Wait for the task to complete, returning (or throwing) its result.
55+
///
56+
/// ### Priority
57+
/// If the task has not completed yet, its priority will be elevated to the
58+
/// priority of the current task. Note that this may not be as effective as
59+
/// creating the task with the "right" priority to in the first place.
60+
public func get() async throws -> Success {
61+
fatalError("\(#function) not implemented yet.")
62+
}
63+
64+
/// Attempt to cancel the task.
65+
///
66+
/// Whether this function has any effect is task-dependent.
67+
///
68+
/// For a task to respect cancellation it must cooperatively check for it
69+
/// while running. Many tasks will check for cancellation before beginning
70+
/// their "actual work", however this is not a requirement nor is it guaranteed
71+
/// how and when tasks check for cancellation in general.
72+
public func cancel() {
73+
fatalError("\(#function) not implemented yet.")
74+
}
75+
}
76+
}
77+
78+
extension Task.Handle where Failure == Never {
79+
/// Wait for the task to complete, returning its result.
80+
///
81+
/// ### Priority
82+
/// If the task has not completed yet, its priority will be elevated to the
83+
/// priority of the current task. Note that this may not be as effective as
84+
/// creating the task with the "right" priority to in the first place.
85+
public func get() async -> Success {
86+
fatalError("\(#function) not implemented yet.")
87+
}
88+
}
89+
90+
// ==== Detached Tasks ---------------------------------------------------------
91+
92+
extension Task {
93+
/// Run given `operation` as part of a new top-level task.
94+
///
95+
/// Creating detached tasks should, generally, be avoided in favor of using
96+
/// `async` functions, `async let` declarations and `await` expressions - as
97+
/// those benefit from structured, bounded concurrency which is easier to reason
98+
/// about, as well as automatically inheriting the parent tasks priority,
99+
/// task-local storage, deadlines, as well as being cancelled automatically
100+
/// when their parent task is cancelled. Detached tasks do not get any of those
101+
/// benefits, and thus should only be used when an operation is impossible to
102+
/// be modelled with child tasks.
103+
///
104+
/// ### Cancellation
105+
/// A detached task always runs to completion unless it is explicitly cancelled.
106+
/// Specifically, dropping a detached tasks `Task.Handle` does _not_ automatically
107+
/// cancel given task.
108+
///
109+
/// Canceling a task must be performed explicitly via `handle.cancel()`.
110+
///
111+
/// - Parameters:
112+
/// - priority: priority of the task TODO: reword and define more explicitly once we have priorities well-defined
113+
/// - operation:
114+
/// - Returns: handle to the task, allowing to `await handle.get()` on the
115+
/// tasks result or `cancel` it.
116+
///
117+
/// - Note: it is generally preferable to use child tasks rather than detached
118+
/// tasks. Child tasks automatically carry priorities, task-local state,
119+
/// deadlines and have other benefits resulting from the structured
120+
/// concurrency concepts that they model. Consider using detached tasks only
121+
/// when strictly necessary and impossible to model operations otherwise.
122+
public static func runDetached<T>(
123+
priority: Priority = .default,
124+
operation: () async -> T
125+
) -> Handle<T, Never> {
126+
fatalError("\(#function) not implemented yet.")
127+
}
128+
129+
/// Run given throwing `operation` as part of a new top-level task.
130+
///
131+
/// Creating detached tasks should, generally, be avoided in favor of using
132+
/// `async` functions, `async let` declarations and `await` expressions - as
133+
/// those benefit from structured, bounded concurrency which is easier to reason
134+
/// about, as well as automatically inheriting the parent tasks priority,
135+
/// task-local storage, deadlines, as well as being cancelled automatically
136+
/// when their parent task is cancelled. Detached tasks do not get any of those
137+
/// benefits, and thus should only be used when an operation is impossible to
138+
/// be modelled with child tasks.
139+
///
140+
/// ### Cancellation
141+
/// A detached task always runs to completion unless it is explicitly cancelled.
142+
/// Specifically, dropping a detached tasks `Task.Handle` does _not_ automatically
143+
/// cancel given task.
144+
///
145+
/// Canceling a task must be performed explicitly via `handle.cancel()`.
146+
///
147+
/// - Parameters:
148+
/// - priority: priority of the task TODO: reword and define more explicitly once we have priorities well-defined
149+
/// - operation:
150+
/// - Returns: handle to the task, allowing to `await handle.get()` on the
151+
/// tasks result or `cancel` it. If the operation fails the handle will
152+
/// throw the error the operation has thrown when awaited on.
153+
///
154+
/// - Note: it is generally preferable to use child tasks rather than detached
155+
/// tasks. Child tasks automatically carry priorities, task-local state,
156+
/// deadlines and have other benefits resulting from the structured
157+
/// concurrency concepts that they model. Consider using detached tasks only
158+
/// when strictly necessary and impossible to model operations otherwise.
159+
public static func runDetached<T>(
160+
priority: Priority = .default,
161+
operation: () async throws -> T
162+
) -> Handle<T, Error> {
163+
fatalError("\(#function) not implemented yet.")
164+
}
165+
}
166+
31167
// ==== UnsafeContinuation -----------------------------------------------------
32168

33169
extension Task {

test/Concurrency/async_tasks.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
// REQUIRES: concurrency
33

44
func someAsyncFunc() async -> String { "" }
5+
56
struct MyError: Error {}
7+
func someThrowingAsyncFunc() async throws -> String { throw MyError() }
68

79
// non-async function with "weird shape" representing some API that has not adopted
810
// swift concurrency yet, but we want to call it from an async function and make it
@@ -37,3 +39,26 @@ func test_unsafeThrowingContinuations() async {
3739

3840
// TODO: Potentially could offer some warnings if we know that a continuation was resumed or escaped at all in a closure?
3941
}
42+
43+
// ==== Detached Tasks ---------------------------------------------------------
44+
45+
func test_detached() async {
46+
let handle = Task.runDetached() {
47+
await someAsyncFunc() // able to call async functions
48+
}
49+
50+
let result: String = await handle.get()
51+
_ = result
52+
}
53+
54+
func test_detached_throwing() async -> String {
55+
let handle: Task.Handle<String, Error> = Task.runDetached() {
56+
await try someThrowingAsyncFunc() // able to call async functions
57+
}
58+
59+
do {
60+
return await try handle.get()
61+
} catch {
62+
print("caught: \(error)")
63+
}
64+
}

0 commit comments

Comments
 (0)