Skip to content

Cherry-pick concurrency docs #40115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Dec 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion stdlib/public/Concurrency/AsyncIteratorProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import Swift

/// A type that that asychronously supplies the values of a sequence one at a
/// A type that asynchronously supplies the values of a sequence one at a
/// time.
///
/// The `AsyncIteratorProtocol` defines the type returned by the
Expand Down
2 changes: 2 additions & 0 deletions stdlib/public/Concurrency/AsyncSequence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ extension AsyncSequence {
/// The predicate executes each time the asynchronous sequence produces an
/// element, until either the predicate returns `false` or the sequence ends.
///
/// If the asynchronous sequence is empty, this method returns `true`.
///
/// - Parameter predicate: A closure that takes an element of the asynchronous
/// sequence as its argument and returns a Boolean value that indicates
/// whether the passed element satisfies a condition.
Expand Down
347 changes: 253 additions & 94 deletions stdlib/public/Concurrency/AsyncStream.swift

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ extension AsyncSequence {
/// }
/// // Prints: 1 2 3 4 Error: MyError()
///
/// - Parameter isIncluded: A error-throwing closure that takes an element of
/// - Parameter predicate: A error-throwing closure that takes an element of
/// the asynchronous sequence as its argument and returns a Boolean value
/// that indicates whether to include the element in the modified sequence.
/// - Returns: An asynchronous sequence that contains, in order, the elements
Expand Down
374 changes: 309 additions & 65 deletions stdlib/public/Concurrency/AsyncThrowingStream.swift

Large diffs are not rendered by default.

124 changes: 78 additions & 46 deletions stdlib/public/Concurrency/CheckedContinuation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,49 +83,60 @@ internal final class CheckedContinuationCanary {
}
}

/// A wrapper class for `UnsafeContinuation` that logs misuses of the
/// continuation, logging a message if the continuation is resumed
/// multiple times, or if an object is destroyed without its continuation
/// ever being resumed.
/// A mechanism to interface
/// between synchronous and asynchronous code,
/// logging correctness violations.
///
/// Raw `UnsafeContinuation`, like other unsafe constructs, requires the
/// user to apply it correctly in order to maintain invariants. The key
/// invariant is that the continuation must be resumed exactly once,
/// and bad things happen if this invariant is not upheld--if a continuation
/// is abandoned without resuming the task, then the task will be stuck in
/// the suspended state forever, and conversely, if the same continuation is
/// resumed multiple times, it will put the task in an undefined state.
/// A *continuation* is an opaque representation of program state.
/// To create a continuation in asynchronous code,
/// call the `withUnsafeContinuation(function:_:)` or
/// `withUnsafeThrowingContinuation(function:_:)` function.
/// To resume the asynchronous task,
/// call the `resume(returning:)`,
/// `resume(throwing:)`,
/// `resume(with:)`,
/// or `resume()` method.
///
/// `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, being able to verify that the
/// invariants are being upheld in testing is important.
/// - 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` logs a message
/// if either of these invariants is violated.
///
/// `CheckedContinuation` is designed to be a drop-in API replacement for
/// `UnsafeContinuation` that can be used for testing purposes, at the cost of
/// an extra allocation and indirection for the wrapper object. Changing a call
/// of `withUnsafeContinuation` or `withUnsafeThrowingContinuation` into a call
/// of `withCheckedContinuation` or `withCheckedThrowingContinuation` should be
/// enough to obtain the extra checking without further source modification in
/// most circumstances.
/// `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, *)
public struct CheckedContinuation<T, E: Error>: UnsafeSendable {
private let canary: CheckedContinuationCanary

/// Initialize a `CheckedContinuation` wrapper around an
/// `UnsafeContinuation`.
/// Creates a checked continuation from an unsafe continuation.
///
/// In most cases, you should use `withCheckedContinuation` or
/// `withCheckedThrowingContinuation` instead. You only need to initialize
/// Instead of calling this initializer,
/// most code calls the `withCheckedContinuation(function:_:)` or
/// `withCheckedThrowingContinuation(function:_:)` function instead.
/// You only need to initialize
/// your own `CheckedContinuation<T, E>` if you already have an
/// `UnsafeContinuation` you want to impose checking on.
///
/// - Parameters:
/// - continuation: a fresh `UnsafeContinuation` that has not yet
/// been resumed. The `UnsafeContinuation` must not be used outside of
/// this object once it's been given to the new object.
/// - function: a string identifying the declaration that is the notional
/// - continuation: An instance of `UnsafeContinuation`
/// that hasn't yet been resumed.
/// After passing the unsafe continuation to this initializer,
/// don't use it outside of this object.
/// - function: A string identifying the declaration that is the notional
/// source for the continuation, used to identify the continuation in
/// runtime diagnostics related to misuse of this continuation.
public init(continuation: UnsafeContinuation<T, E>, function: String = #function) {
Expand All @@ -141,10 +152,10 @@ public struct CheckedContinuation<T, E: Error>: UnsafeSendable {
///
/// A continuation must be resumed exactly once. If the continuation has
/// already been resumed through this object, then the attempt to resume
/// the continuation again will trap.
/// the continuation will trap.
///
/// After `resume` enqueues the task, control is immediately returned to
/// the caller. The task will continue executing when its executor is
/// After `resume` enqueues the task, control immediately returns to
/// the caller. The task continues executing when its executor is
/// able to reschedule it.
public func resume(returning value: __owned T) {
if let c: UnsafeContinuation<T, E> = canary.takeContinuation() {
Expand All @@ -161,10 +172,10 @@ public struct CheckedContinuation<T, E: Error>: UnsafeSendable {
///
/// A continuation must be resumed exactly once. If the continuation has
/// already been resumed through this object, then the attempt to resume
/// the continuation again will trap.
/// the continuation will trap.
///
/// After `resume` enqueues the task, control is immediately returned to
/// the caller. The task will continue executing when its executor is
/// After `resume` enqueues the task, control immediately returns to
/// the caller. The task continues executing when its executor is
/// able to reschedule it.
public func resume(throwing error: __owned E) {
if let c: UnsafeContinuation<T, E> = canary.takeContinuation() {
Expand All @@ -186,10 +197,10 @@ extension CheckedContinuation {
///
/// A continuation must be resumed exactly once. If the continuation has
/// already been resumed through this object, then the attempt to resume
/// the continuation again will trap.
/// the continuation will trap.
///
/// After `resume` enqueues the task, control is immediately returned to
/// the caller. The task will continue executing when its executor is
/// After `resume` enqueues the task, control immediately returns to
/// the caller. The task continues executing when its executor is
/// able to reschedule it.
@_alwaysEmitIntoClient
public func resume<Er: Error>(with result: Result<T, Er>) where E == Error {
Expand All @@ -210,10 +221,10 @@ extension CheckedContinuation {
///
/// A continuation must be resumed exactly once. If the continuation has
/// already been resumed through this object, then the attempt to resume
/// the continuation again will trap.
/// the continuation will trap.
///
/// After `resume` enqueues the task, control is immediately returned to
/// the caller. The task will continue executing when its executor is
/// After `resume` enqueues the task, control immediately returns to
/// the caller. The task continues executing when its executor is
/// able to reschedule it.
@_alwaysEmitIntoClient
public func resume(with result: Result<T, E>) {
Expand All @@ -230,17 +241,26 @@ extension CheckedContinuation {
///
/// A continuation must be resumed exactly once. If the continuation has
/// already been resumed through this object, then the attempt to resume
/// the continuation again will trap.
/// the continuation will trap.
///
/// After `resume` enqueues the task, control is immediately returned to
/// the caller. The task will continue executing when its executor is
/// After `resume` enqueues the task, control immediately returns to
/// the caller. The task continues executing when its executor is
/// able to reschedule it.
@_alwaysEmitIntoClient
public func resume() where T == Void {
self.resume(returning: ())
}
}

/// Suspends the current task,
/// then calls the given closure with a checked continuation for the current task.
///
/// - Parameters:
/// - function: A string identifying the declaration that is the notional
/// source for the continuation, used to identify the continuation in
/// runtime diagnostics related to misuse of this continuation.
/// - body: A closure that takes a `CheckedContinuation` parameter.
/// You must resume the continuation exactly once.
@available(SwiftStdlib 5.1, *)
public func withCheckedContinuation<T>(
function: String = #function,
Expand All @@ -251,6 +271,18 @@ public func withCheckedContinuation<T>(
}
}

/// Suspends the current task,
/// then calls the given closure with a checked throwing continuation for the current task.
///
/// - Parameters:
/// - function: A string identifying the declaration that is the notional
/// source for the continuation, used to identify the continuation in
/// runtime diagnostics related to misuse of this continuation.
/// - body: A closure that takes an `UnsafeContinuation` parameter.
/// You must resume the continuation exactly once.
///
/// If `resume(throwing:)` is called on the continuation,
/// this function throws that error.
@available(SwiftStdlib 5.1, *)
public func withCheckedThrowingContinuation<T>(
function: String = #function,
Expand Down
4 changes: 2 additions & 2 deletions stdlib/public/Concurrency/Executor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@

import Swift

/// A service which can execute jobs.
/// A service that can execute jobs.
@available(SwiftStdlib 5.1, *)
public protocol Executor: AnyObject, Sendable {
func enqueue(_ job: UnownedJob)
}

/// A service which can execute jobs.
/// A service that executes jobs.
@available(SwiftStdlib 5.1, *)
public protocol SerialExecutor: Executor {
// This requirement is repeated here as a non-override so that we
Expand Down
4 changes: 2 additions & 2 deletions stdlib/public/Concurrency/GlobalActor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import Swift
/// A type that represents a globally-unique actor that can be used to isolate
/// various declarations anywhere in the program.
///
/// A type that conforms to the `GlobalActor` protocol and is marked with the
/// A type that conforms to the `GlobalActor` protocol and is marked with
/// the `@globalActor` attribute can be used as a custom attribute. Such types
/// are called global actor types, and can be applied to any declaration to
/// specify that such types are isolated to that global actor type. When using
/// such a declaration from another actor (or from nonisolated code),
/// synchronization is performed through the \c shared actor instance to ensure
/// synchronization is performed through the shared actor instance to ensure
/// mutually-exclusive access to the declaration.
@available(SwiftStdlib 5.1, *)
public protocol GlobalActor {
Expand Down
Loading