@@ -83,49 +83,60 @@ internal final class CheckedContinuationCanary: @unchecked Sendable {
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 . 1 , * )
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 value: __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 error: __owned E) {
170
181
if let c: UnsafeContinuation < T , E > = canary. takeContinuation ( ) {
@@ -189,10 +200,10 @@ extension CheckedContinuation {
189
200
///
190
201
/// A continuation must be resumed exactly once. If the continuation has
191
202
/// already been resumed through this object, then the attempt to resume
192
- /// the continuation again will trap.
203
+ /// the continuation will trap.
193
204
///
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
196
207
/// able to reschedule it.
197
208
@_alwaysEmitIntoClient
198
209
public func resume< Er: Error > ( with result: Result < T , Er > ) where E == Error {
@@ -213,10 +224,10 @@ extension CheckedContinuation {
213
224
///
214
225
/// A continuation must be resumed exactly once. If the continuation has
215
226
/// already been resumed through this object, then the attempt to resume
216
- /// the continuation again will trap.
227
+ /// the continuation will trap.
217
228
///
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
220
231
/// able to reschedule it.
221
232
@_alwaysEmitIntoClient
222
233
public func resume( with result: Result < T , E > ) {
@@ -233,17 +244,26 @@ extension CheckedContinuation {
233
244
///
234
245
/// A continuation must be resumed exactly once. If the continuation has
235
246
/// already been resumed through this object, then the attempt to resume
236
- /// the continuation again will trap.
247
+ /// the continuation will trap.
237
248
///
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
240
251
/// able to reschedule it.
241
252
@_alwaysEmitIntoClient
242
253
public func resume( ) where T == Void {
243
254
self . resume ( returning: ( ) )
244
255
}
245
256
}
246
257
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.
247
267
@available ( SwiftStdlib 5 . 1 , * )
248
268
public func withCheckedContinuation< T> (
249
269
function: String = #function,
@@ -254,6 +274,18 @@ public func withCheckedContinuation<T>(
254
274
}
255
275
}
256
276
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.
257
289
@available ( SwiftStdlib 5 . 1 , * )
258
290
public func withCheckedThrowingContinuation< T> (
259
291
function: String = #function,
0 commit comments