-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Revise doc comments for continuations #37303
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
Changes from all commits
0cd6891
1939d5e
ca6d928
474e5f7
c3cb8be
5395d62
55e8ec1
d101b39
915547b
464c4e0
3aa3ca2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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.5, *) | ||
public struct CheckedContinuation<T, E: Error> { | ||
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) { | ||
|
@@ -141,10 +152,10 @@ public struct CheckedContinuation<T, E: Error> { | |
/// | ||
/// 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 x: __owned T) { | ||
if let c: UnsafeContinuation<T, E> = canary.takeContinuation() { | ||
|
@@ -161,10 +172,10 @@ public struct CheckedContinuation<T, E: Error> { | |
/// | ||
/// 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 x: __owned E) { | ||
if let c: UnsafeContinuation<T, E> = canary.takeContinuation() { | ||
|
@@ -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 { | ||
|
@@ -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>) { | ||
|
@@ -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 an `UnsafeContinuation` parameter. | ||
/// You must resume the continuation exactly once. | ||
@available(SwiftStdlib 5.5, *) | ||
public func withCheckedContinuation<T>( | ||
function: String = #function, | ||
|
@@ -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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh wait, that's not quite right. We don't unconditionally suspend here do we? This is just a potential suspension point and if it is immediately resumed we never suspended, or did I get that wrong @jckarter ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From our discussion, we'll dig into this detail and fix separately if this wording isn't correct. Asking CI to test & merge as-is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okey it seems the wording is correct :) |
||
/// | ||
/// - 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.5, *) | ||
public func withCheckedThrowingContinuation<T>( | ||
function: String = #function, | ||
|
Uh oh!
There was an error while loading. Please reload this page.