Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
212 changes: 211 additions & 1 deletion stdlib/public/Concurrency/Task.swift
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,22 @@ extension Task {
/// have that error propagated here upon cancellation.
///
/// - Returns: The task's result.
@_alwaysEmitIntoClient
public var value: Success {
@_silgen_name("$sScT7valueTTxvg") // "_t" suffix for the typed throws version
get async throws(Failure) {
do {
return try await _taskFutureGetThrowing(_task)
} catch {
throw error as! Failure
}
}
}

// Legacy non-typed throws computed property
@usableFromInline
internal var __abi_value: Success {
@_silgen_name("$sScT5valuexvg")
get async throws {
return try await _taskFutureGetThrowing(_task)
}
Expand All @@ -189,7 +204,7 @@ extension Task {
do {
return .success(try await value)
} catch {
return .failure(error as! Failure) // as!-safe, guaranteed to be Failure
return .failure(error)
}
}
}
Expand Down Expand Up @@ -790,6 +805,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 +1093,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
}
5 changes: 4 additions & 1 deletion test/abi/macOS/arm64/concurrency.swift
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,10 @@ Added: _swift_task_getPreferredTaskExecutor
Added: _swift_task_popTaskExecutorPreference
Added: _swift_task_pushTaskExecutorPreference

// Typed throws Task<Success, Failure>
// property descriptor for Swift.Task.__abi_value : A
Added: _$sScT11__abi_valuexvpMV

// Adopt #isolation in with...Continuation APIs
// Swift.withCheckedThrowingContinuation<A>(isolation: isolated Swift.Actor?, function: Swift.String, _: (Swift.CheckedContinuation<A, Swift.Error>) -> ()) async throws -> A
Added: _$ss31withCheckedThrowingContinuation9isolation8function_xScA_pSgYi_SSyScCyxs5Error_pGXEtYaKlF
Expand Down Expand Up @@ -322,4 +326,3 @@ Added: _$ss9TaskLocalC13withValueImpl_9operation9isolation4file4lineqd__xn_qd__y
// Swift.TaskLocal.withValue<A>(_: A, operation: () async throws -> A1, isolation: isolated Swift.Actor?, file: Swift.String, line: Swift.UInt) async throws -> A1
Added: _$ss9TaskLocalC9withValue_9operation9isolation4file4lineqd__x_qd__yYaKXEScA_pSgYiSSSutYaKlF
Added: _$ss9TaskLocalC9withValue_9operation9isolation4file4lineqd__x_qd__yYaKXEScA_pSgYiSSSutYaKlFTu

4 changes: 4 additions & 0 deletions test/abi/macOS/x86_64/concurrency.swift
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,10 @@ Added: _swift_task_getPreferredTaskExecutor
Added: _swift_task_popTaskExecutorPreference
Added: _swift_task_pushTaskExecutorPreference

// Typed throws Task<Success, Failure>
// property descriptor for Swift.Task.__abi_value : A
Added: _$sScT11__abi_valuexvpMV

// Adopt #isolation in with...Continuation APIs
// Swift.withCheckedThrowingContinuation<A>(isolation: isolated Swift.Actor?, function: Swift.String, _: (Swift.CheckedContinuation<A, Swift.Error>) -> ()) async throws -> A
Added: _$ss31withCheckedThrowingContinuation9isolation8function_xScA_pSgYi_SSyScCyxs5Error_pGXEtYaKlF
Expand Down
6 changes: 6 additions & 0 deletions test/api-digester/stability-concurrency-abi.test
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ Func TaskLocal.withValue(_:operation:file:line:) has parameter 1 type change fro
Func TaskLocal.withValue(_:operation:file:line:) has parameter 2 type change from Swift.String to (any _Concurrency.Actor)?
Func TaskLocal.withValue(_:operation:file:line:) has parameter 3 type change from Swift.UInt to Swift.String

// Adopt typed throws in Task<> and Task::value
// (abi compat was handled but this test does not understand the silgen_name trickery)
Accessor Task.value.Get() has mangled name changing from 'Swift.Task.value.getter : A' to 'Swift.Task.__abi_value.getter : A'
Var Task.value has been renamed to Var __abi_value
Var Task.value has mangled name changing from 'Swift.Task.value : A' to 'Swift.Task.__abi_value : A'

// *** DO NOT DISABLE OR XFAIL THIS TEST. *** (See comment above.)


Expand Down