Files
swift-mirror/stdlib/public/Concurrency/PartialAsyncTask.swift
Alastair Houghton 0813e95569 Merge pull request #81440 from al45tair/currentos-availability
[Concurrency][Stdlib] Add SwiftStdlibCurrentOS availability, use it.
2025-07-07 12:04:00 +01:00

912 lines
34 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import Swift
// N.B. It would be nice to rename this file to ExecutorJob.swift, but when
// trying that we hit an error from the API digester claiming that
//
// +Var UnownedJob.context has mangled name changing from
// 'Swift.UnownedJob.(context in #UNSTABLE ID#) : Builtin.Job' to
// 'Swift.UnownedJob.(context in #UNSTABLE ID#) : Builtin.Job'
//
// This is odd because `context` is private, so it isn't really part of
// the module API.
// TODO: It would be better if some of the functions here could be inlined into
// the Swift code. In particular, some of the ones that deal with data held on
// ExecutorJob.
@available(SwiftStdlib 5.1, *)
@_silgen_name("swift_job_run")
@usableFromInline
internal func _swiftJobRun(_ job: UnownedJob,
_ executor: UnownedSerialExecutor) -> ()
@_unavailableInEmbedded
@available(SwiftStdlibCurrentOS 6.0, *)
@_silgen_name("swift_job_run_on_task_executor")
@usableFromInline
internal func _swiftJobRunOnTaskExecutor(_ job: UnownedJob,
_ executor: UnownedTaskExecutor) -> ()
@_unavailableInEmbedded
@available(SwiftStdlibCurrentOS 6.0, *)
@_silgen_name("swift_job_run_on_serial_and_task_executor")
@usableFromInline
internal func _swiftJobRunOnTaskExecutor(_ job: UnownedJob,
_ serialExecutor: UnownedSerialExecutor,
_ taskExecutor: UnownedTaskExecutor) -> ()
// ==== -----------------------------------------------------------------------
// MARK: UnownedJob
/// A unit of schedulable work.
///
/// Unless you're implementing a scheduler,
/// you don't generally interact with jobs directly.
///
/// An `UnownedJob` must be eventually run *exactly once* using ``runSynchronously(on:)``.
/// Not doing so is effectively going to leak and "hang" the work that the job represents (e.g. a ``Task``).
@available(SwiftStdlib 5.1, *)
@frozen
public struct UnownedJob: Sendable {
private var context: Builtin.Job
@usableFromInline
@available(SwiftStdlibCurrentOS 5.9, *)
internal init(context: Builtin.Job) {
self.context = context
}
#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
/// Create an `UnownedJob` whose lifetime must be managed carefully until it is run exactly once.
@available(SwiftStdlibCurrentOS 5.9, *)
public init(_ job: __owned Job) { // must remain '__owned' in order to not break ABI
self.context = job.context
}
#endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
/// Create an `UnownedJob` whose lifetime must be managed carefully until it is run exactly once.
@available(SwiftStdlibCurrentOS 5.9, *)
public init(_ job: __owned ExecutorJob) { // must remain '__owned' in order to not break ABI
self.context = job.context
}
#endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
/// The priority of this job.
@available(SwiftStdlibCurrentOS 5.9, *)
public var priority: JobPriority {
let raw: UInt8
if #available(SwiftStdlibCurrentOS 6.2, *) {
raw = _jobGetPriority(context)
} else {
fatalError("we shouldn't get here; if we have, availability is broken")
}
return JobPriority(rawValue: raw)
}
@available(SwiftStdlibCurrentOS 5.9, *)
internal var _context: Builtin.Job {
context
}
/// Deprecated API to run a job on a specific executor.
@_alwaysEmitIntoClient
@inlinable
@available(*, deprecated, renamed: "ExecutorJob.runSynchronously(on:)")
public func _runSynchronously(on executor: UnownedSerialExecutor) {
unsafe _swiftJobRun(self, executor)
}
/// Run this job on the passed in executor.
///
/// This operation runs the job on the calling thread and *blocks* until the job completes.
/// The intended use of this method is for an executor to determine when and where it
/// wants to run the job and then call this method on it.
///
/// The passed in executor reference is used to establish the executor context for the job,
/// and should be the same executor as the one semantically calling the `runSynchronously` method.
///
/// - Parameter executor: the executor this job will be semantically running on.
@_alwaysEmitIntoClient
@inlinable
public func runSynchronously(on executor: UnownedSerialExecutor) {
unsafe _swiftJobRun(self, executor)
}
/// Run this job isolated to the passed task executor.
///
/// This operation runs the job on the calling thread and *blocks* until the job completes.
/// The intended use of this method is for an executor to determine when and where it
/// wants to run the job and then call this method on it.
///
/// The passed in executor reference is used to establish the executor context for the job,
/// and should be the same executor as the one semantically calling the `runSynchronously` method.
///
/// This operation consumes the job, preventing it accidental use after it has been run.
///
/// Converting a `ExecutorJob` to an ``UnownedJob`` and invoking ``UnownedJob/runSynchronously(_:)` on it multiple times is undefined behavior,
/// as a job can only ever be run once, and must not be accessed after it has been run.
///
/// - Parameter executor: the task executor this job will be run on.
///
/// - SeeAlso: ``runSynchronously(isolatedTo:taskExecutor:)``
@_unavailableInEmbedded
@available(SwiftStdlibCurrentOS 6.0, *)
@_alwaysEmitIntoClient
@inlinable
public func runSynchronously(on executor: UnownedTaskExecutor) {
unsafe _swiftJobRunOnTaskExecutor(self, executor)
}
/// Run this job isolated to the passed in serial executor, while executing it on the specified task executor.
///
/// This operation runs the job on the calling thread and *blocks* until the job completes.
/// The intended use of this method is for an executor to determine when and where it
/// wants to run the job and then call this method on it.
///
/// The passed in executor reference is used to establish the executor context for the job,
/// and should be the same executor as the one semantically calling the `runSynchronously` method.
///
/// This operation consumes the job, preventing it accidental use after it has been run.
///
/// Converting a `ExecutorJob` to an ``UnownedJob`` and invoking
/// ``UnownedJob/runSynchronously(isolatedTo:taskExecutor:)` on it multiple times
/// is undefined behavior, as a job can only ever be run once, and must not be
/// accessed after it has been run.
///
/// - Parameter serialExecutor: the executor this job will be semantically running on.
/// - Parameter taskExecutor: the task executor this job will be run on.
///
/// - SeeAlso: ``runSynchronously(on:)``
@_unavailableInEmbedded
@available(SwiftStdlibCurrentOS 6.0, *)
@_alwaysEmitIntoClient
@inlinable
public func runSynchronously(isolatedTo serialExecutor: UnownedSerialExecutor,
taskExecutor: UnownedTaskExecutor) {
unsafe _swiftJobRunOnTaskExecutor(self, serialExecutor, taskExecutor)
}
}
@_unavailableInEmbedded
@available(SwiftStdlib 5.9, *)
extension UnownedJob: CustomStringConvertible {
@available(SwiftStdlib 5.9, *)
public var description: String {
let id = _getJobTaskId(self)
/// Tasks are always assigned a unique ID, however some jobs may not have it set,
/// and it appearing as 0 for _different_ jobs may lead to misunderstanding it as
/// being "the same 0 id job", we specifically print 0 (id not set) as nil.
if (id > 0) {
return "\(Self.self)(id: \(id))"
} else {
return "\(Self.self)(id: nil)"
}
}
}
#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
/// Deprecated equivalent of ``ExecutorJob``.
///
/// A unit of schedulable work.
///
/// Unless you're implementing a scheduler,
/// you don't generally interact with jobs directly.
@available(SwiftStdlibCurrentOS 5.9, *)
@available(*, deprecated, renamed: "ExecutorJob")
@frozen
public struct Job: Sendable, ~Copyable {
internal var context: Builtin.Job
@usableFromInline
internal init(context: __owned Builtin.Job) {
self.context = context
}
public init(_ job: UnownedJob) {
self.context = job._context
}
public init(_ job: __owned ExecutorJob) {
self.context = job.context
}
public var priority: JobPriority {
let raw: UInt8
if #available(SwiftStdlibCurrentOS 6.2, *) {
raw = _jobGetPriority(self.context)
} else {
fatalError("we shouldn't get here; if we have, availability is broken")
}
return JobPriority(rawValue: raw)
}
// TODO: move only types cannot conform to protocols, so we can't conform to CustomStringConvertible;
// we can still offer a description to be called explicitly though.
@_unavailableInEmbedded
public var description: String {
let id = _getJobTaskId(UnownedJob(context: self.context))
/// Tasks are always assigned a unique ID, however some jobs may not have it set,
/// and it appearing as 0 for _different_ jobs may lead to misunderstanding it as
/// being "the same 0 id job", we specifically print 0 (id not set) as nil.
if (id > 0) {
return "Job(id: \(id))"
} else {
return "Job(id: nil)"
}
}
}
@available(SwiftStdlib 5.9, *)
extension Job {
/// Run this job on the passed in executor.
///
/// This operation runs the job on the calling thread and *blocks* until the job completes.
/// The intended use of this method is for an executor to determine when and where it
/// wants to run the job and then call this method on it.
///
/// The passed in executor reference is used to establish the executor context for the job,
/// and should be the same executor as the one semantically calling the `runSynchronously` method.
///
/// This operation consumes the job, preventing it accidental use after it has been run.
///
/// Converting a `ExecutorJob` to an ``UnownedJob`` and invoking ``UnownedJob/runSynchronously(_:)` on it multiple times is undefined behavior,
/// as a job can only ever be run once, and must not be accessed after it has been run.
///
/// - Parameter executor: the executor this job will be semantically running on.
@_alwaysEmitIntoClient
@inlinable
__consuming public func runSynchronously(on executor: UnownedSerialExecutor) {
unsafe _swiftJobRun(UnownedJob(self), executor)
}
}
/// A unit of schedulable work.
///
/// Unless you're implementing a scheduler,
/// you don't generally interact with jobs directly.
@available(SwiftStdlibCurrentOS 5.9, *)
@frozen
public struct ExecutorJob: Sendable, ~Copyable {
internal var context: Builtin.Job
@usableFromInline
internal init(context: __owned Builtin.Job) {
self.context = context
}
public init(_ job: UnownedJob) {
self.context = job._context
}
public init(_ job: __owned Job) {
self.context = job.context
}
public var priority: JobPriority {
let raw: UInt8
if #available(SwiftStdlibCurrentOS 6.2, *) {
raw = _jobGetPriority(self.context)
} else {
fatalError("we shouldn't get here; if we have, availability is broken")
}
return JobPriority(rawValue: raw)
}
/// Execute a closure, passing it the bounds of the executor private data
/// for the job.
///
/// Parameters:
///
/// - body: The closure to execute.
///
/// Returns the result of executing the closure.
@available(SwiftStdlibCurrentOS 6.2, *)
public func withUnsafeExecutorPrivateData<R, E>(body: (UnsafeMutableRawBufferPointer) throws(E) -> R) throws(E) -> R {
let base = _jobGetExecutorPrivateData(self.context)
let size = unsafe 2 * MemoryLayout<OpaquePointer>.stride
return unsafe try body(UnsafeMutableRawBufferPointer(start: base,
count: size))
}
/// Kinds of schedulable jobs
@available(SwiftStdlibCurrentOS 6.2, *)
@frozen
public struct Kind: Sendable, RawRepresentable {
public typealias RawValue = UInt8
/// The raw job kind value.
public var rawValue: RawValue
/// Creates a new instance with the specified raw value.
public init?(rawValue: Self.RawValue) {
self.rawValue = rawValue
}
/// A task.
public static let task = Kind(rawValue: RawValue(0))!
// Job kinds >= 192 are private to the implementation.
public static let firstReserved = Kind(rawValue: RawValue(192))!
}
/// What kind of job this is.
@available(SwiftStdlibCurrentOS 6.2, *)
public var kind: Kind {
return Kind(rawValue: _jobGetKind(self.context))!
}
// TODO: move only types cannot conform to protocols, so we can't conform to CustomStringConvertible;
// we can still offer a description to be called explicitly though.
@_unavailableInEmbedded
public var description: String {
let id = _getJobTaskId(UnownedJob(context: self.context))
/// Tasks are always assigned a unique ID, however some jobs may not have it set,
/// and it appearing as 0 for _different_ jobs may lead to misunderstanding it as
/// being "the same 0 id job", we specifically print 0 (id not set) as nil.
if (id > 0) {
return "ExecutorJob(id: \(id))"
} else {
return "ExecutorJob(id: nil)"
}
}
}
@available(SwiftStdlibCurrentOS 5.9, *)
extension ExecutorJob {
/// Run this job on the passed in executor.
///
/// This operation runs the job on the calling thread and *blocks* until the job completes.
/// The intended use of this method is for an executor to determine when and where it
/// wants to run the job and then call this method on it.
///
/// The passed in executor reference is used to establish the executor context for the job,
/// and should be the same executor as the one semantically calling the `runSynchronously` method.
///
/// This operation consumes the job, preventing it accidental use after it has been run.
///
/// Converting a `ExecutorJob` to an ``UnownedJob`` and invoking ``UnownedJob/runSynchronously(_:)` on it multiple times is undefined behavior,
/// as a job can only ever be run once, and must not be accessed after it has been run.
///
/// - Parameter executor: the executor this job will be semantically running on.
@_alwaysEmitIntoClient
@inlinable
__consuming public func runSynchronously(on executor: UnownedSerialExecutor) {
unsafe _swiftJobRun(UnownedJob(self), executor)
}
/// Run this job on the passed in task executor.
///
/// This operation runs the job on the calling thread and *blocks* until the job completes.
/// The intended use of this method is for an executor to determine when and where it
/// wants to run the job and then call this method on it.
///
/// The passed in executor reference is used to establish the executor context for the job,
/// and should be the same executor as the one semantically calling the `runSynchronously` method.
///
/// This operation consumes the job, preventing it accidental use after it has been run.
///
/// Converting a `ExecutorJob` to an ``UnownedJob`` and invoking ``UnownedJob/runSynchronously(_:)` on it multiple times is undefined behavior,
/// as a job can only ever be run once, and must not be accessed after it has been run.
///
/// - Parameter executor: the executor this job will be run on.
///
/// - SeeAlso: ``runSynchronously(isolatedTo:taskExecutor:)``
@_unavailableInEmbedded
@available(SwiftStdlibCurrentOS 6.0, *)
@_alwaysEmitIntoClient
@inlinable
__consuming public func runSynchronously(on executor: UnownedTaskExecutor) {
unsafe _swiftJobRunOnTaskExecutor(UnownedJob(self), executor)
}
/// Run this job isolated to the passed in serial executor, while executing it on the specified task executor.
///
/// This operation runs the job on the calling thread and *blocks* until the job completes.
/// The intended use of this method is for an executor to determine when and where it
/// wants to run the job and then call this method on it.
///
/// The passed in executor reference is used to establish the executor context for the job,
/// and should be the same executor as the one semantically calling the `runSynchronously` method.
///
/// This operation consumes the job, preventing it accidental use after it has been run.
///
/// - Parameter serialExecutor: the executor this job will be semantically running on.
/// - Parameter taskExecutor: the task executor this job will be run on.
///
/// - SeeAlso: ``runSynchronously(on:)``
@_unavailableInEmbedded
@available(SwiftStdlibCurrentOS 6.0, *)
@_alwaysEmitIntoClient
@inlinable
__consuming public func runSynchronously(isolatedTo serialExecutor: UnownedSerialExecutor,
taskExecutor: UnownedTaskExecutor) {
unsafe _swiftJobRunOnTaskExecutor(UnownedJob(self), serialExecutor, taskExecutor)
}
}
// Stack-disciplined job-local allocator support
@available(SwiftStdlibCurrentOS 6.2, *)
extension ExecutorJob {
/// Obtain a stack-disciplined job-local allocator.
///
/// If the job does not support allocation, this property will be `nil`.
public var allocator: LocalAllocator? {
guard self.kind == .task else {
return nil
}
return LocalAllocator(context: self.context)
}
/// A job-local stack-disciplined allocator.
///
/// This can be used to allocate additional data required by an
/// executor implementation; memory allocated in this manner will
/// be released automatically when the job is disposed of by the
/// runtime.
///
/// N.B. Because this allocator is stack disciplined, explicitly
/// deallocating memory out-of-order will cause your program to abort.
public struct LocalAllocator {
internal var context: Builtin.Job
/// Allocate a specified number of bytes of uninitialized memory.
public func allocate(capacity: Int) -> UnsafeMutableRawBufferPointer {
let base = _jobAllocate(context, capacity)
return unsafe UnsafeMutableRawBufferPointer(start: base, count: capacity)
}
/// Allocate uninitialized memory for a single instance of type `T`.
public func allocate<T>(as type: T.Type) -> UnsafeMutablePointer<T> {
let base = _jobAllocate(context, MemoryLayout<T>.size)
return unsafe base.bindMemory(to: type, capacity: 1)
}
/// Allocate uninitialized memory for the specified number of
/// instances of type `T`.
public func allocate<T>(capacity: Int, as type: T.Type)
-> UnsafeMutableBufferPointer<T> {
let base = _jobAllocate(context, MemoryLayout<T>.stride * capacity)
let typedBase = unsafe base.bindMemory(to: T.self, capacity: capacity)
return unsafe UnsafeMutableBufferPointer<T>(start: typedBase, count: capacity)
}
/// Deallocate previously allocated memory. Note that the task
/// allocator is stack disciplined, so if you deallocate a block of
/// memory, all memory allocated after that block is also deallocated.
public func deallocate(_ buffer: UnsafeMutableRawBufferPointer) {
unsafe _jobDeallocate(context, buffer.baseAddress!)
}
/// Deallocate previously allocated memory. Note that the task
/// allocator is stack disciplined, so if you deallocate a block of
/// memory, all memory allocated after that block is also deallocated.
public func deallocate<T>(_ pointer: UnsafeMutablePointer<T>) {
unsafe _jobDeallocate(context, UnsafeMutableRawPointer(pointer))
}
/// Deallocate previously allocated memory. Note that the task
/// allocator is stack disciplined, so if you deallocate a block of
/// memory, all memory allocated after that block is also deallocated.
public func deallocate<T>(_ buffer: UnsafeMutableBufferPointer<T>) {
unsafe _jobDeallocate(context, UnsafeMutableRawPointer(buffer.baseAddress!))
}
}
}
#endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
// ==== -----------------------------------------------------------------------
// MARK: JobPriority
/// The priority of this job.
///
/// The executor determines how priority information affects the way tasks are scheduled.
/// The behavior varies depending on the executor currently being used.
/// Typically, executors attempt to run tasks with a higher priority
/// before tasks with a lower priority.
/// However, the semantics of how priority is treated are left up to each
/// platform and `Executor` implementation.
///
/// A ExecutorJob's priority is roughly equivalent to a `TaskPriority`,
/// however, since not all jobs are tasks, represented as separate type.
///
/// Conversions between the two priorities are available as initializers on the respective types.
@available(SwiftStdlibCurrentOS 5.9, *)
@frozen
public struct JobPriority: Sendable {
public typealias RawValue = UInt8
/// The raw priority value.
public var rawValue: RawValue
}
@available(SwiftStdlib 5.9, *)
extension TaskPriority {
/// Convert this ``UnownedJob/Priority`` to a ``TaskPriority``.
///
/// Most values are directly interchangeable, but this initializer reserves the right to fail for certain values.
@available(SwiftStdlib 5.9, *)
public init?(_ p: JobPriority) {
guard p.rawValue != 0 else {
// 0 is "undefined"
return nil
}
self = TaskPriority(rawValue: p.rawValue)
}
}
@available(SwiftStdlibCurrentOS 5.9, *)
extension JobPriority: Equatable {
public static func == (lhs: JobPriority, rhs: JobPriority) -> Bool {
lhs.rawValue == rhs.rawValue
}
public static func != (lhs: JobPriority, rhs: JobPriority) -> Bool {
lhs.rawValue != rhs.rawValue
}
}
@available(SwiftStdlibCurrentOS 5.9, *)
extension JobPriority: Comparable {
public static func < (lhs: JobPriority, rhs: JobPriority) -> Bool {
lhs.rawValue < rhs.rawValue
}
public static func <= (lhs: JobPriority, rhs: JobPriority) -> Bool {
lhs.rawValue <= rhs.rawValue
}
public static func > (lhs: JobPriority, rhs: JobPriority) -> Bool {
lhs.rawValue > rhs.rawValue
}
public static func >= (lhs: JobPriority, rhs: JobPriority) -> Bool {
lhs.rawValue >= rhs.rawValue
}
}
// ==== -----------------------------------------------------------------------
// MARK: UnsafeContinuation
/// A mechanism to interface
/// between synchronous and asynchronous code,
/// without correctness checking.
///
/// A *continuation* is an opaque representation of program state.
/// To create a continuation in asynchronous code,
/// call the `withUnsafeContinuation(_:)` or
/// `withUnsafeThrowingContinuation(_:)` function.
/// To resume the asynchronous task,
/// call the `resume(returning:)`,
/// `resume(throwing:)`,
/// `resume(with:)`,
/// or `resume()` method.
///
/// - Important: You must call a resume method exactly once
/// on every execution path throughout the program.
/// Resuming from a continuation more than once is undefined behavior.
/// Never resuming leaves the task in a suspended state indefinitely,
/// and leaks any associated resources.
///
/// `CheckedContinuation` performs runtime checks
/// for missing or multiple resume operations.
/// `UnsafeContinuation` avoids enforcing these invariants at runtime
/// because it aims to be a low-overhead mechanism
/// for interfacing Swift tasks with
/// event loops, delegate methods, callbacks,
/// and other non-`async` scheduling mechanisms.
/// However, during development, the ability to verify that the
/// invariants are being upheld in testing is important.
/// Because both types have the same interface,
/// you can replace one with the other in most circumstances,
/// without making other changes.
@available(SwiftStdlib 5.1, *)
@frozen
@unsafe
public struct UnsafeContinuation<T, E: Error>: Sendable {
@usableFromInline internal var context: Builtin.RawUnsafeContinuation
@_alwaysEmitIntoClient
internal init(_ context: Builtin.RawUnsafeContinuation) {
unsafe self.context = context
}
/// Resume the task that's awaiting the continuation
/// by returning the given value.
///
/// - Parameter value: The value to return from the continuation.
///
/// A continuation must be resumed exactly once.
/// If the continuation has already resumed,
/// then calling this method results in undefined behavior.
///
/// After calling this method,
/// control immediately returns to the caller.
/// The task continues executing
/// when its executor schedules it.
@_alwaysEmitIntoClient
public func resume(returning value: sending T) where E == Never {
#if compiler(>=5.5) && $BuiltinContinuation
unsafe Builtin.resumeNonThrowingContinuationReturning(context, value)
#else
fatalError("Swift compiler is incompatible with this SDK version")
#endif
}
/// Resume the task that's awaiting the continuation
/// by returning the given value.
///
/// - Parameter value: The value to return from the continuation.
///
/// A continuation must be resumed exactly once.
/// If the continuation has already resumed,
/// then calling this method results in undefined behavior.
///
/// After calling this method,
/// control immediately returns to the caller.
/// The task continues executing
/// when its executor schedules it.
@_alwaysEmitIntoClient
public func resume(returning value: sending T) {
#if compiler(>=5.5) && $BuiltinContinuation
unsafe Builtin.resumeThrowingContinuationReturning(context, value)
#else
fatalError("Swift compiler is incompatible with this SDK version")
#endif
}
/// Resume the task that's awaiting the continuation
/// by throwing the given error.
///
/// - Parameter error: The error to throw from the continuation.
///
/// A continuation must be resumed exactly once.
/// If the continuation has already resumed,
/// then calling this method results in undefined behavior.
///
/// After calling this method,
/// control immediately returns to the caller.
/// The task continues executing
/// when its executor schedules it.
@_alwaysEmitIntoClient
public func resume(throwing error: consuming E) {
#if compiler(>=5.5) && $BuiltinContinuation
unsafe Builtin.resumeThrowingContinuationThrowing(context, error)
#else
fatalError("Swift compiler is incompatible with this SDK version")
#endif
}
}
@available(SwiftStdlib 5.1, *)
extension UnsafeContinuation {
/// Resume the task that's awaiting the continuation
/// by returning or throwing the given result value.
///
/// - Parameter result: The result.
/// If it contains a `.success` value,
/// the continuation returns that value;
/// otherwise, it throws the `.error` value.
///
/// A continuation must be resumed exactly once.
/// If the continuation has already resumed,
/// then calling this method results in undefined behavior.
///
/// After calling this method,
/// control immediately returns to the caller.
/// The task continues executing
/// when its executor schedules it.
@_alwaysEmitIntoClient
public func resume<Er: Error>(with result: __shared sending Result<T, Er>) where E == Error {
switch result {
case .success(let val):
unsafe self.resume(returning: val)
case .failure(let err):
unsafe self.resume(throwing: err)
}
}
/// Resume the task that's awaiting the continuation
/// by returning or throwing the given result value.
///
/// - Parameter result: The result.
/// If it contains a `.success` value,
/// the continuation returns that value;
/// otherwise, it throws the `.error` value.
///
/// A continuation must be resumed exactly once.
/// If the continuation has already resumed,
/// then calling this method results in undefined behavior.
///
/// After calling this method,
/// control immediately returns to the caller.
/// The task continues executing
/// when its executor schedules it.
@_alwaysEmitIntoClient
public func resume(with result: __shared sending Result<T, E>) {
switch result {
case .success(let val):
unsafe self.resume(returning: val)
case .failure(let err):
unsafe self.resume(throwing: err)
}
}
/// Resume the task that's awaiting the continuation by returning.
///
/// A continuation must be resumed exactly once.
/// If the continuation has already resumed,
/// then calling this method results in undefined behavior.
///
/// After calling this method,
/// control immediately returns to the caller.
/// The task continues executing
/// when its executor schedules it.
@_alwaysEmitIntoClient
public func resume() where T == Void {
unsafe self.resume(returning: ())
}
}
#if _runtime(_ObjC)
// Intrinsics used by SILGen to resume or fail continuations.
@available(SwiftStdlib 5.1, *)
@_alwaysEmitIntoClient
internal func _resumeUnsafeContinuation<T>(
_ continuation: UnsafeContinuation<T, Never>,
_ value: sending T
) {
unsafe continuation.resume(returning: value)
}
@available(SwiftStdlib 5.1, *)
@_alwaysEmitIntoClient
internal func _resumeUnsafeThrowingContinuation<T>(
_ continuation: UnsafeContinuation<T, Error>,
_ value: sending T
) {
unsafe continuation.resume(returning: value)
}
@available(SwiftStdlib 5.1, *)
@_alwaysEmitIntoClient
internal func _resumeUnsafeThrowingContinuationWithError<T>(
_ continuation: UnsafeContinuation<T, Error>,
_ error: consuming Error
) {
unsafe continuation.resume(throwing: error)
}
#endif
/// Invokes the passed in closure with a unsafe continuation for the current task.
///
/// The body of the closure executes synchronously on the calling task, and once it returns
/// the calling task is suspended. It is possible to immediately resume the task, or escape the
/// continuation in order to complete it afterwards, which will then resume the suspended task.
///
/// You must invoke the continuation's `resume` method exactly once.
///
/// Missing to invoke it (eventually) will cause the calling task to remain suspended
/// indefinitely which will result in the task "hanging" as well as being leaked with
/// no possibility to destroy it.
///
/// Unlike the "checked" continuation variant, the `UnsafeContinuation` does not
/// detect or diagnose any kind of misuse, so you need to be extra careful to avoid
/// calling `resume` twice or forgetting to call resume before letting go of the
/// continuation object.
///
/// - Parameter fn: A closure that takes an `UnsafeContinuation` parameter.
/// - Returns: The value continuation is resumed with.
///
/// - SeeAlso: `withUnsafeThrowingContinuation(function:_:)`
/// - SeeAlso: `withCheckedContinuation(function:_:)`
/// - SeeAlso: `withCheckedThrowingContinuation(function:_:)`
@available(SwiftStdlib 5.1, *)
@_alwaysEmitIntoClient
@unsafe
public func withUnsafeContinuation<T>(
isolation: isolated (any Actor)? = #isolation,
_ fn: (UnsafeContinuation<T, Never>) -> Void
) async -> sending T {
return await Builtin.withUnsafeContinuation {
unsafe fn(UnsafeContinuation<T, Never>($0))
}
}
/// Invokes the passed in closure with a unsafe continuation for the current task.
///
/// The body of the closure executes synchronously on the calling task, and once it returns
/// the calling task is suspended. It is possible to immediately resume the task, or escape the
/// continuation in order to complete it afterwards, which will then resume the suspended task.
///
/// If `resume(throwing:)` is called on the continuation, this function throws that error.
///
/// You must invoke the continuation's `resume` method exactly once.
///
/// Missing to invoke it (eventually) will cause the calling task to remain suspended
/// indefinitely which will result in the task "hanging" as well as being leaked with
/// no possibility to destroy it.
///
/// Unlike the "checked" continuation variant, the `UnsafeContinuation` does not
/// detect or diagnose any kind of misuse, so you need to be extra careful to avoid
/// calling `resume` twice or forgetting to call resume before letting go of the
/// continuation object.
///
/// - Parameter fn: A closure that takes an `UnsafeContinuation` parameter.
/// - Returns: The value continuation is resumed with.
///
/// - SeeAlso: `withUnsafeContinuation(function:_:)`
/// - SeeAlso: `withCheckedContinuation(function:_:)`
/// - SeeAlso: `withCheckedThrowingContinuation(function:_:)`
@available(SwiftStdlib 5.1, *)
@_alwaysEmitIntoClient
@unsafe
public func withUnsafeThrowingContinuation<T>(
isolation: isolated (any Actor)? = #isolation,
_ fn: (UnsafeContinuation<T, Error>) -> Void
) async throws -> sending T {
return try await Builtin.withUnsafeThrowingContinuation {
unsafe fn(UnsafeContinuation<T, Error>($0))
}
}
// Note: hack to stage out @_unsafeInheritExecutor forms of various functions
// in favor of #isolation. The _unsafeInheritExecutor_ prefix is meaningful
// to the type checker.
@available(SwiftStdlib 5.1, *)
@_alwaysEmitIntoClient
@_unsafeInheritExecutor
public func _unsafeInheritExecutor_withUnsafeContinuation<T>(
_ fn: (UnsafeContinuation<T, Never>) -> Void
) async -> sending T {
return await Builtin.withUnsafeContinuation {
unsafe fn(UnsafeContinuation<T, Never>($0))
}
}
// Note: hack to stage out @_unsafeInheritExecutor forms of various functions
// in favor of #isolation. The _unsafeInheritExecutor_ prefix is meaningful
// to the type checker.
@available(SwiftStdlib 5.1, *)
@_alwaysEmitIntoClient
@_unsafeInheritExecutor
public func _unsafeInheritExecutor_withUnsafeThrowingContinuation<T>(
_ fn: (UnsafeContinuation<T, Error>) -> Void
) async throws -> sending T {
return try await Builtin.withUnsafeThrowingContinuation {
unsafe fn(UnsafeContinuation<T, Error>($0))
}
}
/// A hack to mark an SDK that supports swift_continuation_await.
@available(SwiftStdlib 5.1, *)
@_alwaysEmitIntoClient
public func _abiEnableAwaitContinuation() {
fatalError("never use this function")
}