@@ -83,49 +83,60 @@ internal final class CheckedContinuationCanary {
83
83
}
84
84
}
85
85
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.
90
89
///
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.
98
99
///
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.
104
108
///
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.
112
121
@available ( SwiftStdlib 5 . 5 , * )
113
122
public struct CheckedContinuation < T, E: Error > {
114
123
private let canary : CheckedContinuationCanary
115
124
116
- /// Initialize a `CheckedContinuation` wrapper around an
117
- /// `UnsafeContinuation`.
125
+ /// Creates a checked continuation from an unsafe continuation.
118
126
///
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
121
131
/// your own `CheckedContinuation<T, E>` if you already have an
122
132
/// `UnsafeContinuation` you want to impose checking on.
123
133
///
124
134
/// - 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
129
140
/// source for the continuation, used to identify the continuation in
130
141
/// runtime diagnostics related to misuse of this continuation.
131
142
public init ( continuation: UnsafeContinuation < T , E > , function: String = #function) {
@@ -141,10 +152,10 @@ public struct CheckedContinuation<T, E: Error> {
141
152
///
142
153
/// A continuation must be resumed exactly once. If the continuation has
143
154
/// already been resumed through this object, then the attempt to resume
144
- /// the continuation again will trap.
155
+ /// the continuation will trap.
145
156
///
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
148
159
/// able to reschedule it.
149
160
public func resume( returning x: __owned T) {
150
161
if let c: UnsafeContinuation < T , E > = canary. takeContinuation ( ) {
@@ -161,10 +172,10 @@ public struct CheckedContinuation<T, E: Error> {
161
172
///
162
173
/// A continuation must be resumed exactly once. If the continuation has
163
174
/// already been resumed through this object, then the attempt to resume
164
- /// the continuation again will trap.
175
+ /// the continuation will trap.
165
176
///
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
168
179
/// able to reschedule it.
169
180
public func resume( throwing x: __owned E) {
170
181
if let c: UnsafeContinuation < T , E > = canary. takeContinuation ( ) {
@@ -186,10 +197,10 @@ extension CheckedContinuation {
186
197
///
187
198
/// A continuation must be resumed exactly once. If the continuation has
188
199
/// already been resumed through this object, then the attempt to resume
189
- /// the continuation again will trap.
200
+ /// the continuation will trap.
190
201
///
191
- /// After `resume` enqueues the task, control is immediately returned to
192
- /// the caller. The task will continue executing when its executor is
202
+ /// After `resume` enqueues the task, control immediately returns to
203
+ /// the caller. The task continues executing when its executor is
193
204
/// able to reschedule it.
194
205
@_alwaysEmitIntoClient
195
206
public func resume< Er: Error > ( with result: Result < T , Er > ) where E == Error {
@@ -210,10 +221,10 @@ extension CheckedContinuation {
210
221
///
211
222
/// A continuation must be resumed exactly once. If the continuation has
212
223
/// already been resumed through this object, then the attempt to resume
213
- /// the continuation again will trap.
224
+ /// the continuation will trap.
214
225
///
215
- /// After `resume` enqueues the task, control is immediately returned to
216
- /// the caller. The task will continue executing when its executor is
226
+ /// After `resume` enqueues the task, control immediately returns to
227
+ /// the caller. The task continues executing when its executor is
217
228
/// able to reschedule it.
218
229
@_alwaysEmitIntoClient
219
230
public func resume( with result: Result < T , E > ) {
@@ -230,17 +241,26 @@ extension CheckedContinuation {
230
241
///
231
242
/// A continuation must be resumed exactly once. If the continuation has
232
243
/// already been resumed through this object, then the attempt to resume
233
- /// the continuation again will trap.
244
+ /// the continuation will trap.
234
245
///
235
- /// After `resume` enqueues the task, control is immediately returned to
236
- /// the caller. The task will continue executing when its executor is
246
+ /// After `resume` enqueues the task, control immediately returns to
247
+ /// the caller. The task continues executing when its executor is
237
248
/// able to reschedule it.
238
249
@_alwaysEmitIntoClient
239
250
public func resume( ) where T == Void {
240
251
self . resume ( returning: ( ) )
241
252
}
242
253
}
243
254
255
+ /// Suspends the current task,
256
+ /// then calls the given closure with a checked continuation for the current task.
257
+ ///
258
+ /// - Parameters:
259
+ /// - function: A string identifying the declaration that is the notional
260
+ /// source for the continuation, used to identify the continuation in
261
+ /// runtime diagnostics related to misuse of this continuation.
262
+ /// - body: A closure that takes an `UnsafeContinuation` parameter.
263
+ /// You must resume the continuation exactly once.
244
264
@available ( SwiftStdlib 5 . 5 , * )
245
265
public func withCheckedContinuation< T> (
246
266
function: String = #function,
@@ -251,6 +271,18 @@ public func withCheckedContinuation<T>(
251
271
}
252
272
}
253
273
274
+ /// Suspends the current task,
275
+ /// then calls the given closure with a checked throwing continuation for the current task.
276
+ ///
277
+ /// - Parameters:
278
+ /// - function: A string identifying the declaration that is the notional
279
+ /// source for the continuation, used to identify the continuation in
280
+ /// runtime diagnostics related to misuse of this continuation.
281
+ /// - body: A closure that takes an `UnsafeContinuation` parameter.
282
+ /// You must resume the continuation exactly once.
283
+ ///
284
+ /// If `resume(throwing:)` is called on the continuation,
285
+ /// this function throws that error.
254
286
@available ( SwiftStdlib 5 . 5 , * )
255
287
public func withCheckedThrowingContinuation< T> (
256
288
function: String = #function,
0 commit comments