@@ -36,14 +36,7 @@ internal final class CheckedContinuationCanary {
3636 return functionPtr. assumingMemoryBound ( to: String . self)
3737 }
3838
39- internal static func create< T> ( continuation: UnsafeContinuation < T > ,
40- function: String ) -> Self {
41- return _create (
42- continuation: unsafeBitCast ( continuation, to: UnsafeRawPointer . self) ,
43- function: function)
44- }
45-
46- internal static func create< T> ( continuation: UnsafeThrowingContinuation < T > ,
39+ internal static func create< T, E> ( continuation: UnsafeContinuation < T , E > ,
4740 function: String ) -> Self {
4841 return _create (
4942 continuation: unsafeBitCast ( continuation, to: UnsafeRawPointer . self) ,
@@ -56,23 +49,14 @@ internal final class CheckedContinuationCanary {
5649
5750 // Take the continuation away from the container, or return nil if it's
5851 // already been taken.
59- private func _takeContinuation ( ) -> UnsafeRawPointer ? {
52+ internal func takeContinuation < T , E > ( ) -> UnsafeContinuation < T , E > ? {
6053 // Atomically exchange the current continuation value with a null pointer.
6154 let rawContinuationPtr = unsafeBitCast ( _continuationPtr,
6255 to: Builtin . RawPointer. self)
6356 let rawOld = Builtin . atomicrmw_xchg_seqcst_Word ( rawContinuationPtr,
6457 0 . _builtinWordValue)
6558
66- return unsafeBitCast ( rawOld, to: UnsafeRawPointer ? . self)
67- }
68-
69- internal func takeContinuation< T> ( ) -> UnsafeContinuation < T > ? {
70- return unsafeBitCast ( _takeContinuation ( ) ,
71- to: UnsafeContinuation< T>? . self )
72- }
73- internal func takeThrowingContinuation< T> ( ) -> UnsafeThrowingContinuation < T > ? {
74- return unsafeBitCast ( _takeContinuation ( ) ,
75- to: UnsafeThrowingContinuation< T>? . self )
59+ return unsafeBitCast ( rawOld, to: UnsafeContinuation< T, E>? . self )
7660 }
7761
7862 deinit {
@@ -97,27 +81,30 @@ internal final class CheckedContinuationCanary {
9781/// is abandoned without resuming the task, then the task will be stuck in
9882/// the suspended state forever, and conversely, if the same continuation is
9983/// resumed multiple times, it will put the task in an undefined state.
84+ ///
10085/// `UnsafeContinuation` avoids enforcing these invariants at runtime because
10186/// it aims to be a low-overhead mechanism for interfacing Swift tasks with
10287/// event loops, delegate methods, callbacks, and other non-`async` scheduling
10388/// mechanisms. However, during development, being able to verify that the
10489/// invariants are being upheld in testing is important.
10590///
10691/// `CheckedContinuation` is designed to be a drop-in API replacement for
107- /// `UnsafeContinuation` that can be used for testing purposes, at the cost
108- /// of an extra allocation and indirection for the wrapper object.
109- /// Changing a call of `withUnsafeContinuation` into a call of
110- /// `withCheckedContinuation` should be enough to obtain the extra checking
111- /// without further source modification in most circumstances.
112- public struct CheckedContinuation < T> {
113- let canary : CheckedContinuationCanary
92+ /// `UnsafeContinuation` that can be used for testing purposes, at the cost of
93+ /// an extra allocation and indirection for the wrapper object. Changing a call
94+ /// of `withUnsafeContinuation` or `withUnsafeThrowingContinuation` into a call
95+ /// of `withCheckedContinuation` or `withCheckedThrowingContinuation` should be
96+ /// enough to obtain the extra checking without further source modification in
97+ /// most circumstances.
98+ public struct CheckedContinuation < T, E: Error > {
99+ private let canary : CheckedContinuationCanary
114100
115101 /// Initialize a `CheckedContinuation` wrapper around an
116102 /// `UnsafeContinuation`.
117103 ///
118- /// In most cases, you should use `withCheckedContinuation` instead.
119- /// You only need to initialize your own `CheckedContinuation<T>` if you
120- /// already have an `UnsafeContinuation` you want to impose checking on.
104+ /// In most cases, you should use `withCheckedContinuation` or
105+ /// `withCheckedThrowingContinuation` instead. You only need to initialize
106+ /// your own `CheckedContinuation<T, E>` if you already have an
107+ /// `UnsafeContinuation` you want to impose checking on.
121108 ///
122109 /// - Parameters:
123110 /// - continuation: a fresh `UnsafeContinuation` that has not yet
@@ -126,7 +113,7 @@ public struct CheckedContinuation<T> {
126113 /// - function: a string identifying the declaration that is the notional
127114 /// source for the continuation, used to identify the continuation in
128115 /// runtime diagnostics related to misuse of this continuation.
129- public init ( continuation: UnsafeContinuation < T > , function: String = #function) {
116+ public init ( continuation: UnsafeContinuation < T , E > , function: String = #function) {
130117 canary = CheckedContinuationCanary . create (
131118 continuation: continuation,
132119 function: function)
@@ -135,6 +122,8 @@ public struct CheckedContinuation<T> {
135122 /// Resume the task awaiting the continuation by having it return normally
136123 /// from its suspension point.
137124 ///
125+ /// - Parameter value: The value to return from the continuation.
126+ ///
138127 /// A continuation must be resumed exactly once. If the continuation has
139128 /// already been resumed through this object, then the attempt to resume
140129 /// the continuation again will trap.
@@ -143,107 +132,41 @@ public struct CheckedContinuation<T> {
143132 /// the caller. The task will continue executing when its executor is
144133 /// able to reschedule it.
145134 public func resume( returning x: __owned T) {
146- if let c: UnsafeContinuation < T > = canary. takeContinuation ( ) {
135+ if let c: UnsafeContinuation < T , E > = canary. takeContinuation ( ) {
147136 c. resume ( returning: x)
148137 } else {
149138 fatalError ( " SWIFT TASK CONTINUATION MISUSE: \( canary. function) tried to resume its continuation more than once, returning \( x) ! \n " )
150139 }
151140 }
152- }
153-
154- extension CheckedContinuation where T == Void {
155- /// Resume the task awaiting the continuation by having it return normally
156- /// from its suspension point.
157- ///
158- /// A continuation must be resumed exactly once. If the continuation has
159- /// already been resumed through this object, then the attempt to resume
160- /// the continuation again will trap.
161- ///
162- /// After `resume` enqueues the task, control is immediately returned to
163- /// the caller. The task will continue executing when its executor is
164- /// able to reschedule it.
165- @inlinable
166- public func resume( ) {
167- self . resume ( returning: ( ) )
168- }
169- }
170-
171- public func withCheckedContinuation< T> (
172- function: String = #function,
173- _ body: ( CheckedContinuation < T > ) -> Void
174- ) async -> T {
175- return await withUnsafeContinuation {
176- body ( CheckedContinuation ( continuation: $0, function: function) )
177- }
178- }
179-
180- /// A wrapper class for `UnsafeThrowingContinuation` that logs misuses of the
181- /// continuation, logging a message if the continuation is resumed
182- /// multiple times, or if an object is destroyed without its continuation
183- /// ever being resumed.
184- ///
185- /// Raw `UnsafeThrowingContinuation`, like other unsafe constructs, requires the
186- /// user to apply it correctly in order to maintain invariants. The key
187- /// invariant is that the continuation must be resumed exactly once,
188- /// and bad things happen if this invariant is not upheld--if a continuation
189- /// is abandoned without resuming the task, then the task will be stuck in
190- /// the suspended state forever, and conversely, if the same continuation is
191- /// resumed multiple times, it will put the task in an undefined state.
192- /// `UnsafeThrowingContinuation` avoids enforcing these invariants at runtime because
193- /// it aims to be a low-overhead mechanism for interfacing Swift tasks with
194- /// event loops, delegate methods, callbacks, and other non-`async` scheduling
195- /// mechanisms. However, during development, being able to verify that the
196- /// invariants are being upheld in testing is important.
197- ///
198- /// `CheckedThrowingContinuation` is designed to be a drop-in API replacement for
199- /// `UnsafeThrowingContinuation` that can be used for testing purposes, at the cost
200- /// of an extra allocation and indirection for the wrapper object.
201- /// Changing a call of `withUnsafeThrowingContinuation` into a call of
202- /// `withCheckedThrowingContinuation` should be enough to obtain the extra checking
203- /// without further source modification in most circumstances.
204- public struct CheckedThrowingContinuation < T> {
205- let canary : CheckedContinuationCanary
206141
207- /// Initialize a `CheckedThrowingContinuation` wrapper around an
208- /// `UnsafeThrowingContinuation`.
209- ///
210- /// In most cases, you should use `withCheckedThrowingContinuation` instead.
211- /// You only need to initialize your own `CheckedThrowingContinuation<T>` if you
212- /// already have an `UnsafeThrowingContinuation` you want to impose checking on.
213- ///
214- /// - Parameters:
215- /// - continuation: a fresh `UnsafeThrowingContinuation` that has not yet
216- /// been resumed. The `UnsafeThrowingContinuation` must not be used outside of
217- /// this object once it's been given to the new object.
218- /// - function: a string identifying the declaration that is the notional
219- /// source for the continuation, used to identify the continuation in
220- /// runtime diagnostics related to misuse of this continuation.
221- public init ( continuation: UnsafeThrowingContinuation < T > , function: String = #function) {
222- canary = CheckedContinuationCanary . create (
223- continuation: continuation,
224- function: function)
225- }
226-
227- /// Resume the task awaiting the continuation by having it return normally
142+ /// Resume the task awaiting the continuation by having it throw an error
228143 /// from its suspension point.
229144 ///
145+ /// - Parameter error: The error to throw from the continuation.
146+ ///
230147 /// A continuation must be resumed exactly once. If the continuation has
231148 /// already been resumed through this object, then the attempt to resume
232149 /// the continuation again will trap.
233150 ///
234151 /// After `resume` enqueues the task, control is immediately returned to
235152 /// the caller. The task will continue executing when its executor is
236153 /// able to reschedule it.
237- public func resume( returning x: __owned T ) {
238- if let c: UnsafeThrowingContinuation < T > = canary. takeThrowingContinuation ( ) {
239- c. resume ( returning : x)
154+ public func resume( throwing x: __owned E ) {
155+ if let c: UnsafeContinuation < T , E > = canary. takeContinuation ( ) {
156+ c. resume ( throwing : x)
240157 } else {
241- fatalError ( " SWIFT TASK CONTINUATION MISUSE: \( canary. function) tried to resume its continuation more than once, returning \( x) ! \n " )
158+ fatalError ( " SWIFT TASK CONTINUATION MISUSE: \( canary. function) tried to resume its continuation more than once, throwing \( x) ! \n " )
242159 }
243160 }
244-
245- /// Resume the task awaiting the continuation by having it throw an error
246- /// from its suspension point.
161+ }
162+
163+ extension CheckedContinuation {
164+ /// Resume the task awaiting the continuation by having it either
165+ /// return normally or throw an error based on the state of the given
166+ /// `Result` value.
167+ ///
168+ /// - Parameter result: A value to either return or throw from the
169+ /// continuation.
247170 ///
248171 /// A continuation must be resumed exactly once. If the continuation has
249172 /// already been resumed through this object, then the attempt to resume
@@ -252,36 +175,40 @@ public struct CheckedThrowingContinuation<T> {
252175 /// After `resume` enqueues the task, control is immediately returned to
253176 /// the caller. The task will continue executing when its executor is
254177 /// able to reschedule it.
255- public func resume( throwing x: __owned Error) {
256- if let c: UnsafeThrowingContinuation < T > = canary. takeThrowingContinuation ( ) {
257- c. resume ( throwing: x)
258- } else {
259- fatalError ( " SWIFT TASK CONTINUATION MISUSE: \( canary. function) tried to resume its continuation more than once, throwing \( x) ! \n " )
178+ @_alwaysEmitIntoClient
179+ public func resume< Er: Error > ( with result: Result < T , Er > ) where E == Error {
180+ switch result {
181+ case . success( let val) :
182+ self . resume ( returning: val)
183+ case . failure( let err) :
184+ self . resume ( throwing: err)
260185 }
261186 }
262187
263188 /// Resume the task awaiting the continuation by having it either
264189 /// return normally or throw an error based on the state of the given
265190 /// `Result` value.
266191 ///
192+ /// - Parameter result: A value to either return or throw from the
193+ /// continuation.
194+ ///
267195 /// A continuation must be resumed exactly once. If the continuation has
268196 /// already been resumed through this object, then the attempt to resume
269197 /// the continuation again will trap.
270198 ///
271199 /// After `resume` enqueues the task, control is immediately returned to
272200 /// the caller. The task will continue executing when its executor is
273201 /// able to reschedule it.
274- public func resume< E: Error > ( with x: __owned Result< T , E > ) {
275- switch x {
276- case . success( let s) :
277- return resume ( returning: s)
278- case . failure( let e) :
279- return resume ( throwing: e)
202+ @_alwaysEmitIntoClient
203+ public func resume( with result: Result < T , E > ) {
204+ switch result {
205+ case . success( let val) :
206+ self . resume ( returning: val)
207+ case . failure( let err) :
208+ self . resume ( throwing: err)
280209 }
281210 }
282- }
283211
284- extension CheckedThrowingContinuation where T == Void {
285212 /// Resume the task awaiting the continuation by having it return normally
286213 /// from its suspension point.
287214 ///
@@ -292,18 +219,27 @@ extension CheckedThrowingContinuation where T == Void {
292219 /// After `resume` enqueues the task, control is immediately returned to
293220 /// the caller. The task will continue executing when its executor is
294221 /// able to reschedule it.
295- @inlinable
296- public func resume( ) {
222+ @_alwaysEmitIntoClient
223+ public func resume( ) where T == Void {
297224 self . resume ( returning: ( ) )
298225 }
299226}
300227
228+ public func withCheckedContinuation< T> (
229+ function: String = #function,
230+ _ body: ( CheckedContinuation < T , Never > ) -> Void
231+ ) async -> T {
232+ return await withUnsafeContinuation {
233+ body ( CheckedContinuation ( continuation: $0, function: function) )
234+ }
235+ }
236+
301237public func withCheckedThrowingContinuation< T> (
302238 function: String = #function,
303- _ body: ( CheckedThrowingContinuation < T > ) -> Void
239+ _ body: ( CheckedContinuation < T , Error > ) -> Void
304240) async throws -> T {
305241 return try await withUnsafeThrowingContinuation {
306- body ( CheckedThrowingContinuation ( continuation: $0, function: function) )
242+ body ( CheckedContinuation ( continuation: $0, function: function) )
307243 }
308244}
309245
0 commit comments