Skip to content

Commit ebc3d23

Browse files
committed
[Concurrency] Typed throws in Task.init and .detached
1 parent d536ffa commit ebc3d23

File tree

2 files changed

+227
-1
lines changed

2 files changed

+227
-1
lines changed

stdlib/public/Concurrency/Task.swift

Lines changed: 209 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,19 @@ extension Task {
167167
///
168168
/// - Returns: The task's result.
169169
public var value: Success {
170+
get async throws(Failure) {
171+
do {
172+
return try await __abi_value
173+
} catch {
174+
throw error as! Failure
175+
}
176+
}
177+
}
178+
179+
@available(SwiftStdlib 5.1, *)
180+
@_silgen_name("$sScT5valuexvg")
181+
@usableFromInline
182+
internal var __abi_value: Success {
170183
get async throws {
171184
return try await _taskFutureGetThrowing(_task)
172185
}
@@ -187,7 +200,7 @@ extension Task {
187200
public var result: Result<Success, Failure> {
188201
get async {
189202
do {
190-
return .success(try await value)
203+
return .success(try await __abi_value)
191204
} catch {
192205
return .failure(error as! Failure) // as!-safe, guaranteed to be Failure
193206
}
@@ -790,6 +803,104 @@ extension Task where Failure == Error {
790803
#endif
791804
}
792805

806+
// ==== Typed throws Task.init overloads ---------------------------------------
807+
808+
@available(SwiftStdlib 6.0, *)
809+
extension Task {
810+
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
811+
@discardableResult
812+
@_alwaysEmitIntoClient
813+
@_allowFeatureSuppression(IsolatedAny)
814+
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model")
815+
public init(
816+
priority: TaskPriority? = nil,
817+
@_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping @isolated(any) () async throws(Failure) -> Success
818+
) {
819+
fatalError("Unavailable in task-to-thread concurrency model")
820+
}
821+
#elseif $Embedded
822+
@discardableResult
823+
@_alwaysEmitIntoClient
824+
public init(
825+
priority: TaskPriority? = nil,
826+
@_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping @isolated(any) () async throws(Failure) -> Success
827+
) {
828+
#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup
829+
// Set up the task flags for a new task.
830+
let flags = taskCreateFlags(
831+
priority: priority, isChildTask: false, copyTaskLocals: true,
832+
inheritContext: true, enqueueJob: true,
833+
addPendingGroupTaskUnconditionally: false,
834+
isDiscardingTask: false)
835+
836+
// Create the asynchronous task future.
837+
let (task, _) = Builtin.createAsyncTask(flags, operation)
838+
839+
self._task = task
840+
#else
841+
fatalError("Unsupported Swift compiler")
842+
#endif
843+
}
844+
#else // if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
845+
/// Runs the given operation asynchronously
846+
/// as part of a new top-level task on behalf of the current actor.
847+
///
848+
/// Use this function when creating asynchronous work
849+
/// that operates on behalf of the synchronous function that calls it.
850+
/// Like `Task.detached(priority:operation:)`,
851+
/// this function creates a separate, top-level task.
852+
/// Unlike `detach(priority:operation:)`,
853+
/// the task created by `Task.init(priority:operation:)`
854+
/// inherits the priority and actor context of the caller,
855+
/// so the operation is treated more like an asynchronous extension
856+
/// to the synchronous operation.
857+
///
858+
/// You need to keep a reference to the task
859+
/// if you want to cancel it by calling the `Task.cancel()` method.
860+
/// Discarding your reference to a detached task
861+
/// doesn't implicitly cancel that task,
862+
/// it only makes it impossible for you to explicitly cancel the task.
863+
///
864+
/// - Parameters:
865+
/// - priority: The priority of the task.
866+
/// Pass `nil` to use the priority from `Task.currentPriority`.
867+
/// - operation: The operation to perform.
868+
@discardableResult
869+
@_alwaysEmitIntoClient
870+
@_allowFeatureSuppression(IsolatedAny)
871+
public init(
872+
priority: TaskPriority? = nil,
873+
@_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping @isolated(any) () async throws(Failure) -> Success
874+
) {
875+
#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup
876+
// Set up the task flags for a new task.
877+
let flags = taskCreateFlags(
878+
priority: priority, isChildTask: false, copyTaskLocals: true,
879+
inheritContext: true, enqueueJob: true,
880+
addPendingGroupTaskUnconditionally: false,
881+
isDiscardingTask: false)
882+
883+
// Create the asynchronous task future.
884+
#if $BuiltinCreateTask
885+
let builtinSerialExecutor =
886+
Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor
887+
888+
let (task, _) = Builtin.createTask(flags: flags,
889+
initialSerialExecutor:
890+
builtinSerialExecutor,
891+
operation: operation)
892+
#else
893+
let (task, _) = Builtin.createAsyncTask(flags, operation)
894+
#endif
895+
896+
self._task = task
897+
#else
898+
fatalError("Unsupported Swift compiler")
899+
#endif
900+
}
901+
#endif
902+
}
903+
793904
// ==== Detached Tasks ---------------------------------------------------------
794905

795906
@available(SwiftStdlib 5.1, *)
@@ -980,6 +1091,103 @@ extension Task where Failure == Error {
9801091
#endif
9811092
}
9821093

1094+
// ==== Typed throws Task.detached overloads -----------------------------------
1095+
1096+
@available(SwiftStdlib 6.0, *)
1097+
extension Task {
1098+
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
1099+
@discardableResult
1100+
@_alwaysEmitIntoClient
1101+
@_allowFeatureSuppression(IsolatedAny)
1102+
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model")
1103+
public static func detached(
1104+
priority: TaskPriority? = nil,
1105+
operation: __owned @Sendable @escaping @isolated(any) () async throws(Failure) -> Success
1106+
) -> Task<Success, Failure> {
1107+
fatalError("Unavailable in task-to-thread concurrency model")
1108+
}
1109+
#elseif $Embedded
1110+
@discardableResult
1111+
@_alwaysEmitIntoClient
1112+
public static func detached(
1113+
priority: TaskPriority? = nil,
1114+
operation: __owned @Sendable @escaping () async throws(Failure) -> Success
1115+
) -> Task<Success, Failure> {
1116+
#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup
1117+
// Set up the job flags for a new task.
1118+
let flags = taskCreateFlags(
1119+
priority: priority, isChildTask: false, copyTaskLocals: false,
1120+
inheritContext: false, enqueueJob: true,
1121+
addPendingGroupTaskUnconditionally: false,
1122+
isDiscardingTask: false)
1123+
1124+
// Create the asynchronous task future.
1125+
let (task, _) = Builtin.createAsyncTask(flags, operation)
1126+
1127+
return Task(task)
1128+
#else
1129+
fatalError("Unsupported Swift compiler")
1130+
#endif
1131+
}
1132+
#else
1133+
/// Runs the given throwing operation asynchronously
1134+
/// as part of a new top-level task.
1135+
///
1136+
/// If the operation throws an error, this method propagates that error.
1137+
///
1138+
/// Don't use a detached task if it's possible
1139+
/// to model the operation using structured concurrency features like child tasks.
1140+
/// Child tasks inherit the parent task's priority and task-local storage,
1141+
/// and canceling a parent task automatically cancels all of its child tasks.
1142+
/// You need to handle these considerations manually with a detached task.
1143+
///
1144+
/// You need to keep a reference to the detached task
1145+
/// if you want to cancel it by calling the `Task.cancel()` method.
1146+
/// Discarding your reference to a detached task
1147+
/// doesn't implicitly cancel that task,
1148+
/// it only makes it impossible for you to explicitly cancel the task.
1149+
///
1150+
/// - Parameters:
1151+
/// - priority: The priority of the task.
1152+
/// - operation: The operation to perform.
1153+
///
1154+
/// - Returns: A reference to the task.
1155+
@discardableResult
1156+
@_alwaysEmitIntoClient
1157+
@_allowFeatureSuppression(IsolatedAny)
1158+
public static func detached(
1159+
priority: TaskPriority? = nil,
1160+
operation: __owned @Sendable @escaping @isolated(any) () async throws(Failure) -> Success
1161+
) -> Task<Success, Failure> {
1162+
#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup
1163+
// Set up the job flags for a new task.
1164+
let flags = taskCreateFlags(
1165+
priority: priority, isChildTask: false, copyTaskLocals: false,
1166+
inheritContext: false, enqueueJob: true,
1167+
addPendingGroupTaskUnconditionally: false,
1168+
isDiscardingTask: false)
1169+
1170+
// Create the asynchronous task future.
1171+
#if $BuiltinCreateTask
1172+
let builtinSerialExecutor =
1173+
Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor
1174+
1175+
let (task, _) = Builtin.createTask(flags: flags,
1176+
initialSerialExecutor:
1177+
builtinSerialExecutor,
1178+
operation: operation)
1179+
#else
1180+
let (task, _) = Builtin.createAsyncTask(flags, operation)
1181+
#endif
1182+
1183+
return Task(task)
1184+
#else
1185+
fatalError("Unsupported Swift compiler")
1186+
#endif
1187+
}
1188+
#endif
1189+
}
1190+
9831191
// ==== Voluntary Suspension -----------------------------------------------------
9841192

9851193
@available(SwiftStdlib 5.1, *)

test/Concurrency/typed_throws.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,21 @@ func testAsyncFor<S: AsyncSequence>(seq: S) async throws(MyError)
2121
for try await _ in seq {
2222
}
2323
}
24+
25+
@available(SwiftStdlib 6.0, *)
26+
func testTask() async throws(MyError) {
27+
let t: Task<Int, MyError> = Task { () throws(MyError) -> Int in
28+
throw MyError.failed
29+
}
30+
31+
_ = try await t.value
32+
}
33+
34+
@available(SwiftStdlib 6.0, *)
35+
func testTaskDetached() async throws(MyError) {
36+
let t: Task<Int, MyError> = Task.detached { () throws(MyError) -> Int in
37+
throw MyError.failed
38+
}
39+
40+
_ = try await t.value
41+
}

0 commit comments

Comments
 (0)