Files
swift-mirror/stdlib/public/Concurrency/PartialAsyncTask.swift
Alastair Houghton 28f96e64ab [Concurrency][Stdlib] Add SwiftStdlibCurrentOS availability, use it.
If you use SwiftStdlibCurrentOS availability, you will be able to
use new types and functions from within the implementation. This
works by, when appropriate, building with the CurrentOS availability
set to the current deployment target.

rdar://150944675
2025-05-12 12:07:24 +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")
}