Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 203 additions & 2 deletions stdlib/public/Concurrency/Task.swift
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,14 @@ extension Task {
///
/// - Returns: The task's result.
public var value: Success {
get async throws {
return try await _taskFutureGetThrowing(_task)
// FIXME: This seems wrong that abi test is not freaking out here?
// At the same time, adding a silgen_name compat accessor with old signature results in duplicate definitions -- is mangling of typed throws not done properly on return position?
get async throws(Failure) {
do {
return try await _taskFutureGetThrowing(_task)
} catch {
throw error as! Failure
}
}
}

Expand Down Expand Up @@ -790,6 +796,104 @@ extension Task where Failure == Error {
#endif
}

// ==== Typed throws Task.init overloads ---------------------------------------

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we decide we want to do this, I'd see if i can DRY this up in any way.

@available(SwiftStdlib 6.0, *)
extension Task {
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
@discardableResult
@_alwaysEmitIntoClient
@_allowFeatureSuppression(IsolatedAny)
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model")
public init(
priority: TaskPriority? = nil,
@_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping @isolated(any) () async throws(Failure) -> Success
) {
fatalError("Unavailable in task-to-thread concurrency model")
}
#elseif $Embedded
@discardableResult
@_alwaysEmitIntoClient
public init(
priority: TaskPriority? = nil,
@_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping @isolated(any) () async throws(Failure) -> Success
) {
#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup
// Set up the task flags for a new task.
let flags = taskCreateFlags(
priority: priority, isChildTask: false, copyTaskLocals: true,
inheritContext: true, enqueueJob: true,
addPendingGroupTaskUnconditionally: false,
isDiscardingTask: false)

// Create the asynchronous task future.
let (task, _) = Builtin.createAsyncTask(flags, operation)

self._task = task
#else
fatalError("Unsupported Swift compiler")
#endif
}
#else // if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
/// Runs the given operation asynchronously
/// as part of a new top-level task on behalf of the current actor.
///
/// Use this function when creating asynchronous work
/// that operates on behalf of the synchronous function that calls it.
/// Like `Task.detached(priority:operation:)`,
/// this function creates a separate, top-level task.
/// Unlike `detach(priority:operation:)`,
/// the task created by `Task.init(priority:operation:)`
/// inherits the priority and actor context of the caller,
/// so the operation is treated more like an asynchronous extension
/// to the synchronous operation.
///
/// You need to keep a reference to the task
/// if you want to cancel it by calling the `Task.cancel()` method.
/// Discarding your reference to a detached task
/// doesn't implicitly cancel that task,
/// it only makes it impossible for you to explicitly cancel the task.
///
/// - Parameters:
/// - priority: The priority of the task.
/// Pass `nil` to use the priority from `Task.currentPriority`.
/// - operation: The operation to perform.
@discardableResult
@_alwaysEmitIntoClient
@_allowFeatureSuppression(IsolatedAny)
public init(
priority: TaskPriority? = nil,
@_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping @isolated(any) () async throws(Failure) -> Success
) {
#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup
// Set up the task flags for a new task.
let flags = taskCreateFlags(
priority: priority, isChildTask: false, copyTaskLocals: true,
inheritContext: true, enqueueJob: true,
addPendingGroupTaskUnconditionally: false,
isDiscardingTask: false)

// Create the asynchronous task future.
#if $BuiltinCreateTask
let builtinSerialExecutor =
Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor

let (task, _) = Builtin.createTask(flags: flags,
initialSerialExecutor:
builtinSerialExecutor,
operation: operation)
#else
let (task, _) = Builtin.createAsyncTask(flags, operation)
#endif

self._task = task
#else
fatalError("Unsupported Swift compiler")
#endif
}
#endif
}

// ==== Detached Tasks ---------------------------------------------------------

@available(SwiftStdlib 5.1, *)
Expand Down Expand Up @@ -980,6 +1084,103 @@ extension Task where Failure == Error {
#endif
}

// ==== Typed throws Task.detached overloads -----------------------------------

@available(SwiftStdlib 6.0, *)
extension Task {
#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
@discardableResult
@_alwaysEmitIntoClient
@_allowFeatureSuppression(IsolatedAny)
@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model")
public static func detached(
priority: TaskPriority? = nil,
operation: __owned @Sendable @escaping @isolated(any) () async throws(Failure) -> Success
) -> Task<Success, Failure> {
fatalError("Unavailable in task-to-thread concurrency model")
}
#elseif $Embedded
@discardableResult
@_alwaysEmitIntoClient
public static func detached(
priority: TaskPriority? = nil,
operation: __owned @Sendable @escaping () async throws(Failure) -> Success
) -> Task<Success, Failure> {
#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup
// Set up the job flags for a new task.
let flags = taskCreateFlags(
priority: priority, isChildTask: false, copyTaskLocals: false,
inheritContext: false, enqueueJob: true,
addPendingGroupTaskUnconditionally: false,
isDiscardingTask: false)

// Create the asynchronous task future.
let (task, _) = Builtin.createAsyncTask(flags, operation)

return Task(task)
#else
fatalError("Unsupported Swift compiler")
#endif
}
#else
/// Runs the given throwing operation asynchronously
/// as part of a new top-level task.
///
/// If the operation throws an error, this method propagates that error.
///
/// Don't use a detached task if it's possible
/// to model the operation using structured concurrency features like child tasks.
/// Child tasks inherit the parent task's priority and task-local storage,
/// and canceling a parent task automatically cancels all of its child tasks.
/// You need to handle these considerations manually with a detached task.
///
/// You need to keep a reference to the detached task
/// if you want to cancel it by calling the `Task.cancel()` method.
/// Discarding your reference to a detached task
/// doesn't implicitly cancel that task,
/// it only makes it impossible for you to explicitly cancel the task.
///
/// - Parameters:
/// - priority: The priority of the task.
/// - operation: The operation to perform.
///
/// - Returns: A reference to the task.
@discardableResult
@_alwaysEmitIntoClient
@_allowFeatureSuppression(IsolatedAny)
public static func detached(
priority: TaskPriority? = nil,
operation: __owned @Sendable @escaping @isolated(any) () async throws(Failure) -> Success
) -> Task<Success, Failure> {
#if compiler(>=5.5) && $BuiltinCreateAsyncTaskInGroup
// Set up the job flags for a new task.
let flags = taskCreateFlags(
priority: priority, isChildTask: false, copyTaskLocals: false,
inheritContext: false, enqueueJob: true,
addPendingGroupTaskUnconditionally: false,
isDiscardingTask: false)

// Create the asynchronous task future.
#if $BuiltinCreateTask
let builtinSerialExecutor =
Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor

let (task, _) = Builtin.createTask(flags: flags,
initialSerialExecutor:
builtinSerialExecutor,
operation: operation)
#else
let (task, _) = Builtin.createAsyncTask(flags, operation)
#endif

return Task(task)
#else
fatalError("Unsupported Swift compiler")
#endif
}
#endif
}

// ==== Voluntary Suspension -----------------------------------------------------

@available(SwiftStdlib 5.1, *)
Expand Down
18 changes: 18 additions & 0 deletions test/Concurrency/typed_throws.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,21 @@ func testAsyncFor<S: AsyncSequence>(seq: S) async throws(MyError)
for try await _ in seq {
}
}

@available(SwiftStdlib 6.0, *)
func testTask() async throws(MyError) {
let t: Task<Int, MyError> = Task { () throws(MyError) -> Int in
throw MyError.failed
}

_ = try await t.value
}

@available(SwiftStdlib 6.0, *)
func testTaskDetached() async throws(MyError) {
let t: Task<Int, MyError> = Task.detached { () throws(MyError) -> Int in
throw MyError.failed
}

_ = try await t.value
}