mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
The `_SwiftConcurrencyShims` module was imported `@_implementationOnly` which was causing warnings to be emitted during the stdlib build. The module currently serves no purpose; the only declaration it contains is a defunct `_SwiftContext` struct which is not referenced by anything. The module needs to continue to exist for source compatibility, though, since it is part of the toolchain and imported publicly from other modules.
774 lines
28 KiB
Swift
774 lines
28 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2020 - 2021 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
|
|
|
|
// TODO(swift): rename the file to ExecutorJob.swift eventually, we don't use PartialTask terminology anymore
|
|
|
|
@available(SwiftStdlib 5.1, *)
|
|
@_silgen_name("swift_job_run")
|
|
@usableFromInline
|
|
internal func _swiftJobRun(_ job: UnownedJob,
|
|
_ executor: UnownedSerialExecutor) -> ()
|
|
|
|
@_unavailableInEmbedded
|
|
@available(SwiftStdlib 6.0, *)
|
|
@_silgen_name("swift_job_run_on_task_executor")
|
|
@usableFromInline
|
|
internal func _swiftJobRunOnTaskExecutor(_ job: UnownedJob,
|
|
_ executor: UnownedTaskExecutor) -> ()
|
|
|
|
@_unavailableInEmbedded
|
|
@available(SwiftStdlib 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(SwiftStdlib 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(SwiftStdlib 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(SwiftStdlib 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(SwiftStdlib 5.9, *)
|
|
public var priority: JobPriority {
|
|
let raw = _swift_concurrency_jobPriority(self)
|
|
return JobPriority(rawValue: raw)
|
|
}
|
|
|
|
@available(SwiftStdlib 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) {
|
|
_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) {
|
|
_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(SwiftStdlib 6.0, *)
|
|
@_alwaysEmitIntoClient
|
|
@inlinable
|
|
public func runSynchronously(on executor: UnownedTaskExecutor) {
|
|
_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(SwiftStdlib 6.0, *)
|
|
@_alwaysEmitIntoClient
|
|
@inlinable
|
|
public func runSynchronously(isolatedTo serialExecutor: UnownedSerialExecutor,
|
|
taskExecutor: UnownedTaskExecutor) {
|
|
_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(SwiftStdlib 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 = _swift_concurrency_jobPriority(UnownedJob(context: self.context))
|
|
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) {
|
|
_swiftJobRun(UnownedJob(self), executor)
|
|
}
|
|
}
|
|
|
|
/// A unit of schedulable work.
|
|
///
|
|
/// Unless you're implementing a scheduler,
|
|
/// you don't generally interact with jobs directly.
|
|
@available(SwiftStdlib 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 = _swift_concurrency_jobPriority(UnownedJob(context: self.context))
|
|
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 "ExecutorJob(id: \(id))"
|
|
} else {
|
|
return "ExecutorJob(id: nil)"
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(SwiftStdlib 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) {
|
|
_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(SwiftStdlib 6.0, *)
|
|
@_alwaysEmitIntoClient
|
|
@inlinable
|
|
__consuming public func runSynchronously(on executor: UnownedTaskExecutor) {
|
|
_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(SwiftStdlib 6.0, *)
|
|
@_alwaysEmitIntoClient
|
|
@inlinable
|
|
__consuming public func runSynchronously(isolatedTo serialExecutor: UnownedSerialExecutor,
|
|
taskExecutor: UnownedTaskExecutor) {
|
|
_swiftJobRunOnTaskExecutor(UnownedJob(self), serialExecutor, taskExecutor)
|
|
}
|
|
}
|
|
#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(SwiftStdlib 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(SwiftStdlib 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(SwiftStdlib 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) {
|
|
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
|
|
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
|
|
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
|
|
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):
|
|
self.resume(returning: val)
|
|
case .failure(let err):
|
|
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):
|
|
self.resume(returning: val)
|
|
case .failure(let err):
|
|
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 {
|
|
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
|
|
) {
|
|
continuation.resume(returning: value)
|
|
}
|
|
|
|
@available(SwiftStdlib 5.1, *)
|
|
@_alwaysEmitIntoClient
|
|
internal func _resumeUnsafeThrowingContinuation<T>(
|
|
_ continuation: UnsafeContinuation<T, Error>,
|
|
_ value: sending T
|
|
) {
|
|
continuation.resume(returning: value)
|
|
}
|
|
|
|
@available(SwiftStdlib 5.1, *)
|
|
@_alwaysEmitIntoClient
|
|
internal func _resumeUnsafeThrowingContinuationWithError<T>(
|
|
_ continuation: UnsafeContinuation<T, Error>,
|
|
_ error: consuming Error
|
|
) {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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")
|
|
}
|
|
|
|
// ==== -----------------------------------------------------------------------
|
|
// MARK: Runtime functions
|
|
|
|
@available(SwiftStdlib 5.9, *)
|
|
@_silgen_name("swift_concurrency_jobPriority")
|
|
internal func _swift_concurrency_jobPriority(_ job: UnownedJob) -> UInt8
|