@@ -83,49 +83,60 @@ internal final class CheckedContinuationCanary: @unchecked Sendable {
8383 }
8484}
8585
86- /// A wrapper class for `UnsafeContinuation` that logs misuses of the
87- /// continuation, logging a message if the continuation is resumed
88- /// multiple times, or if an object is destroyed without its continuation
89- /// ever being resumed.
86+ /// A mechanism to interface
87+ /// between synchronous and asynchronous code,
88+ /// logging correctness violations.
9089///
91- /// Raw `UnsafeContinuation`, like other unsafe constructs, requires the
92- /// user to apply it correctly in order to maintain invariants. The key
93- /// invariant is that the continuation must be resumed exactly once,
94- /// and bad things happen if this invariant is not upheld--if a continuation
95- /// is abandoned without resuming the task, then the task will be stuck in
96- /// the suspended state forever, and conversely, if the same continuation is
97- /// resumed multiple times, it will put the task in an undefined state.
90+ /// A *continuation* is an opaque representation of program state.
91+ /// To create a continuation in asynchronous code,
92+ /// call the `withUnsafeContinuation(function:_:)` or
93+ /// `withUnsafeThrowingContinuation(function:_:)` function.
94+ /// To resume the asynchronous task,
95+ /// call the `resume(returning:)`,
96+ /// `resume(throwing:)`,
97+ /// `resume(with:)`,
98+ /// or `resume()` method.
9899///
99- /// `UnsafeContinuation` avoids enforcing these invariants at runtime because
100- /// it aims to be a low-overhead mechanism for interfacing Swift tasks with
101- /// event loops, delegate methods, callbacks, and other non-`async` scheduling
102- /// mechanisms. However, during development, being able to verify that the
103- /// invariants are being upheld in testing is important.
100+ /// - Important: You must call a resume method exactly once
101+ /// on every execution path throughout the program.
102+ ///
103+ /// Resuming from a continuation more than once is undefined behavior.
104+ /// Never resuming leaves the task in a suspended state indefinitely,
105+ /// and leaks any associated resources.
106+ /// `CheckedContinuation` logs a message
107+ /// if either of these invariants is violated.
104108///
105- /// `CheckedContinuation` is designed to be a drop-in API replacement for
106- /// `UnsafeContinuation` that can be used for testing purposes, at the cost of
107- /// an extra allocation and indirection for the wrapper object. Changing a call
108- /// of `withUnsafeContinuation` or `withUnsafeThrowingContinuation` into a call
109- /// of `withCheckedContinuation` or `withCheckedThrowingContinuation` should be
110- /// enough to obtain the extra checking without further source modification in
111- /// most circumstances.
109+ /// `CheckedContinuation` performs runtime checks
110+ /// for missing or multiple resume operations.
111+ /// `UnsafeContinuation` avoids enforcing these invariants at runtime
112+ /// because it aims to be a low-overhead mechanism
113+ /// for interfacing Swift tasks with
114+ /// event loops, delegate methods, callbacks,
115+ /// and other non-`async` scheduling mechanisms.
116+ /// However, during development, the ability to verify that the
117+ /// invariants are being upheld in testing is important.
118+ /// Because both types have the same interface,
119+ /// you can replace one with the other in most circumstances,
120+ /// without making other changes.
112121@available ( SwiftStdlib 5 . 1 , * )
113122public struct CheckedContinuation < T, E: Error > {
114123 private let canary : CheckedContinuationCanary
115124
116- /// Initialize a `CheckedContinuation` wrapper around an
117- /// `UnsafeContinuation`.
125+ /// Creates a checked continuation from an unsafe continuation.
118126 ///
119- /// In most cases, you should use `withCheckedContinuation` or
120- /// `withCheckedThrowingContinuation` instead. You only need to initialize
127+ /// Instead of calling this initializer,
128+ /// most code calls the `withCheckedContinuation(function:_:)` or
129+ /// `withCheckedThrowingContinuation(function:_:)` function instead.
130+ /// You only need to initialize
121131 /// your own `CheckedContinuation<T, E>` if you already have an
122132 /// `UnsafeContinuation` you want to impose checking on.
123133 ///
124134 /// - Parameters:
125- /// - continuation: a fresh `UnsafeContinuation` that has not yet
126- /// been resumed. The `UnsafeContinuation` must not be used outside of
127- /// this object once it's been given to the new object.
128- /// - function: a string identifying the declaration that is the notional
135+ /// - continuation: An instance of `UnsafeContinuation`
136+ /// that hasn't yet been resumed.
137+ /// After passing the unsafe continuation to this initializer,
138+ /// don't use it outside of this object.
139+ /// - function: A string identifying the declaration that is the notional
129140 /// source for the continuation, used to identify the continuation in
130141 /// runtime diagnostics related to misuse of this continuation.
131142 public init ( continuation: UnsafeContinuation < T , E > , function: String = #function) {
@@ -141,10 +152,10 @@ public struct CheckedContinuation<T, E: Error> {
141152 ///
142153 /// A continuation must be resumed exactly once. If the continuation has
143154 /// already been resumed through this object, then the attempt to resume
144- /// the continuation again will trap.
155+ /// the continuation will trap.
145156 ///
146- /// After `resume` enqueues the task, control is immediately returned to
147- /// the caller. The task will continue executing when its executor is
157+ /// After `resume` enqueues the task, control immediately returns to
158+ /// the caller. The task continues executing when its executor is
148159 /// able to reschedule it.
149160 public func resume( returning value: __owned T) {
150161 if let c: UnsafeContinuation < T , E > = canary. takeContinuation ( ) {
@@ -161,10 +172,10 @@ public struct CheckedContinuation<T, E: Error> {
161172 ///
162173 /// A continuation must be resumed exactly once. If the continuation has
163174 /// already been resumed through this object, then the attempt to resume
164- /// the continuation again will trap.
175+ /// the continuation will trap.
165176 ///
166- /// After `resume` enqueues the task, control is immediately returned to
167- /// the caller. The task will continue executing when its executor is
177+ /// After `resume` enqueues the task, control immediately returns to
178+ /// the caller. The task continues executing when its executor is
168179 /// able to reschedule it.
169180 public func resume( throwing error: __owned E) {
170181 if let c: UnsafeContinuation < T , E > = canary. takeContinuation ( ) {
@@ -189,10 +200,10 @@ extension CheckedContinuation {
189200 ///
190201 /// A continuation must be resumed exactly once. If the continuation has
191202 /// already been resumed through this object, then the attempt to resume
192- /// the continuation again will trap.
203+ /// the continuation will trap.
193204 ///
194- /// After `resume` enqueues the task, control is immediately returned to
195- /// the caller. The task will continue executing when its executor is
205+ /// After `resume` enqueues the task, control immediately returns to
206+ /// the caller. The task continues executing when its executor is
196207 /// able to reschedule it.
197208 @_alwaysEmitIntoClient
198209 public func resume< Er: Error > ( with result: Result < T , Er > ) where E == Error {
@@ -213,10 +224,10 @@ extension CheckedContinuation {
213224 ///
214225 /// A continuation must be resumed exactly once. If the continuation has
215226 /// already been resumed through this object, then the attempt to resume
216- /// the continuation again will trap.
227+ /// the continuation will trap.
217228 ///
218- /// After `resume` enqueues the task, control is immediately returned to
219- /// the caller. The task will continue executing when its executor is
229+ /// After `resume` enqueues the task, control immediately returns to
230+ /// the caller. The task continues executing when its executor is
220231 /// able to reschedule it.
221232 @_alwaysEmitIntoClient
222233 public func resume( with result: Result < T , E > ) {
@@ -233,17 +244,26 @@ extension CheckedContinuation {
233244 ///
234245 /// A continuation must be resumed exactly once. If the continuation has
235246 /// already been resumed through this object, then the attempt to resume
236- /// the continuation again will trap.
247+ /// the continuation will trap.
237248 ///
238- /// After `resume` enqueues the task, control is immediately returned to
239- /// the caller. The task will continue executing when its executor is
249+ /// After `resume` enqueues the task, control immediately returns to
250+ /// the caller. The task continues executing when its executor is
240251 /// able to reschedule it.
241252 @_alwaysEmitIntoClient
242253 public func resume( ) where T == Void {
243254 self . resume ( returning: ( ) )
244255 }
245256}
246257
258+ /// Suspends the current task,
259+ /// then calls the given closure with a checked continuation for the current task.
260+ ///
261+ /// - Parameters:
262+ /// - function: A string identifying the declaration that is the notional
263+ /// source for the continuation, used to identify the continuation in
264+ /// runtime diagnostics related to misuse of this continuation.
265+ /// - body: A closure that takes a `CheckedContinuation` parameter.
266+ /// You must resume the continuation exactly once.
247267@available ( SwiftStdlib 5 . 1 , * )
248268public func withCheckedContinuation< T> (
249269 function: String = #function,
@@ -254,6 +274,18 @@ public func withCheckedContinuation<T>(
254274 }
255275}
256276
277+ /// Suspends the current task,
278+ /// then calls the given closure with a checked throwing continuation for the current task.
279+ ///
280+ /// - Parameters:
281+ /// - function: A string identifying the declaration that is the notional
282+ /// source for the continuation, used to identify the continuation in
283+ /// runtime diagnostics related to misuse of this continuation.
284+ /// - body: A closure that takes an `UnsafeContinuation` parameter.
285+ /// You must resume the continuation exactly once.
286+ ///
287+ /// If `resume(throwing:)` is called on the continuation,
288+ /// this function throws that error.
257289@available ( SwiftStdlib 5 . 1 , * )
258290public func withCheckedThrowingContinuation< T> (
259291 function: String = #function,
0 commit comments