@@ -49,10 +49,50 @@ import Swift
49
49
@available ( SwiftStdlib 5 . 5 , * )
50
50
public struct AsyncStream < Element> {
51
51
public struct Continuation : Sendable {
52
+ /// Indication of the type of termination informed to
53
+ /// `onTermination`.
52
54
public enum Termination {
55
+
56
+ /// The stream was finished via the `finish` method
53
57
case finished
58
+
59
+ /// The stream was cancelled
54
60
case cancelled
55
61
}
62
+
63
+ /// A result of yielding values.
64
+ public enum YieldResult {
65
+
66
+ /// When a value is successfully enqueued, either buffered
67
+ /// or immediately consumed to resume a pending call to next
68
+ /// and a count of remaining slots available in the buffer at
69
+ /// the point in time of yielding. Note: transacting upon the
70
+ /// remaining count is only valid when then calls to yield are
71
+ /// mutually exclusive.
72
+ case enqueued( remaining: Int )
73
+
74
+ /// Yielding resulted in not buffering an element because the
75
+ /// buffer was full. The element is the dropped value.
76
+ case dropped( Element )
77
+
78
+ /// Indication that the continuation was yielded when the
79
+ /// stream was already in a terminal state: either by cancel or
80
+ /// by finishing.
81
+ case terminated
82
+ }
83
+
84
+ /// A strategy that handles exhaustion of a buffer’s capacity.
85
+ public enum BufferingPolicy {
86
+ case unbounded
87
+
88
+ /// When the buffer is full, discard the newly received element.
89
+ /// This enforces keeping the specified amount of oldest values.
90
+ case bufferingOldest( Int )
91
+
92
+ /// When the buffer is full, discard the oldest element in the buffer.
93
+ /// This enforces keeping the specified amount of newest values.
94
+ case bufferingNewest( Int )
95
+ }
56
96
57
97
let storage : _Storage
58
98
@@ -64,7 +104,8 @@ public struct AsyncStream<Element> {
64
104
///
65
105
/// This can be called more than once and returns to the caller immediately
66
106
/// without blocking for any awaiting consumption from the iteration.
67
- public func yield( _ value: __owned Element) {
107
+ @discardableResult
108
+ public func yield( _ value: __owned Element) -> YieldResult {
68
109
storage. yield ( value)
69
110
}
70
111
@@ -105,7 +146,8 @@ public struct AsyncStream<Element> {
105
146
/// - Parameter elementType: The type the AsyncStream will produce.
106
147
/// - Parameter maxBufferedElements: The maximum number of elements to
107
148
/// hold in the buffer past any checks for continuations being resumed.
108
- /// - Parameter build: The work associated with yielding values to the AsyncStream.
149
+ /// - Parameter build: The work associated with yielding values to the
150
+ /// AsyncStream.
109
151
///
110
152
/// The maximum number of pending elements limited by dropping the oldest
111
153
/// value when a new value comes in if the buffer would exceed the limit
@@ -117,13 +159,34 @@ public struct AsyncStream<Element> {
117
159
/// concurrent contexts could result in out of order delivery.
118
160
public init (
119
161
_ elementType: Element . Type = Element . self,
120
- maxBufferedElements limit: Int = . max ,
162
+ bufferingPolicy limit: Continuation . BufferingPolicy = . unbounded ,
121
163
_ build: ( Continuation ) -> Void
122
164
) {
123
165
let storage : _Storage = . create( limit: limit)
124
- produce = storage. next
166
+ self . init ( unfolding : storage. next)
125
167
build ( Continuation ( storage: storage) )
126
168
}
169
+
170
+
171
+ public init (
172
+ unfolding produce: @escaping ( ) async -> Element ? ,
173
+ onCancel: ( @Sendable ( ) -> Void ) ? = nil
174
+ ) {
175
+ let storage : _AsyncStreamCriticalStorage < Optional < ( ) async -> Element ? > >
176
+ = . create( produce)
177
+ self . produce = {
178
+ return await Task . withCancellationHandler {
179
+ storage. value = nil
180
+ onCancel ? ( )
181
+ } operation: {
182
+ guard let result = await storage. value ? ( ) else {
183
+ storage. value = nil
184
+ return nil
185
+ }
186
+ return result
187
+ }
188
+ }
189
+ }
127
190
}
128
191
129
192
@available ( SwiftStdlib 5 . 5 , * )
@@ -139,9 +202,9 @@ extension AsyncStream: AsyncSequence {
139
202
140
203
/// The next value from the AsyncStream.
141
204
///
142
- /// When next returns nil this signifies the end of the AsyncStream. Any such
143
- /// case that next is invoked concurrently and contends with another call to
144
- /// next is a programmer error and will fatalError.
205
+ /// When next returns nil this signifies the end of the AsyncStream. Any
206
+ /// such case that next is invoked concurrently and contends with another
207
+ /// call to next is a programmer error and will fatalError.
145
208
///
146
209
/// If the task this iterator is running in is canceled while next is
147
210
/// awaiting a value, this will terminate the AsyncStream and next may return nil
@@ -167,12 +230,13 @@ extension AsyncStream.Continuation {
167
230
///
168
231
/// This can be called more than once and returns to the caller immediately
169
232
/// without blocking for any awaiting consumption from the iteration.
233
+ @discardableResult
170
234
public func yield(
171
235
with result: Result < Element , Never >
172
- ) {
236
+ ) -> YieldResult {
173
237
switch result {
174
238
case . success( let val) :
175
- storage. yield ( val)
239
+ return storage. yield ( val)
176
240
}
177
241
}
178
242
@@ -182,7 +246,9 @@ extension AsyncStream.Continuation {
182
246
///
183
247
/// This can be called more than once and returns to the caller immediately
184
248
/// without blocking for any awaiting consumption from the iteration.
185
- public func yield( ) where Element == Void {
186
- storage. yield ( ( ) )
249
+ /// without blocking for any awaiting consuption from the iteration.
250
+ @discardableResult
251
+ public func yield( ) -> YieldResult where Element == Void {
252
+ return storage. yield ( ( ) )
187
253
}
188
254
}
0 commit comments