@@ -66,112 +66,50 @@ public struct AsyncMergeSequence<UpstreamAsyncSequence: AsyncSequence>: AsyncSeq
6666 return Iterator ( upstreamIterators: self . upstreamAsyncSequences. map { $0. makeAsyncIterator ( ) } )
6767 }
6868
69- actor UpstreamAsyncIteratorState {
70- var busy = false
71- var finished = false
72-
73- func setBusy( _ value: Bool ) {
74- self . busy = value
75- }
76-
77- func setFinished( ) {
78- self . finished = true
79- self . busy = false
80- }
81-
82- func isAvailable( ) -> Bool {
83- !self . busy && !self . finished
84- }
69+ enum UpstreamElement {
70+ case element( Element )
71+ case finished
8572 }
8673
87- final class UpstreamAsyncIterator < BaseAsyncIterator: AsyncIteratorProtocol > : AsyncIteratorProtocol {
88- public typealias Element = BaseAsyncIterator . Element
89- var iterator : BaseAsyncIterator ?
90- let state = UpstreamAsyncIteratorState ( )
74+ actor ElementCounter {
75+ var counter = 0
9176
92- init ( iterator : BaseAsyncIterator ? ) {
93- self . iterator = iterator
77+ func increaseCounter ( ) {
78+ self . counter += 1
9479 }
9580
96- public func next( ) async throws -> BaseAsyncIterator . Element ? {
97- guard !Task. isCancelled else { return nil }
98-
99- await self . state. setBusy ( true )
100- let next = try await self . iterator? . next ( )
101- if next == nil {
102- await self . state. setFinished ( )
103- }
104- await self . state. setBusy ( false )
105- return next
81+ func decreaseCounter( ) {
82+ guard self . counter > 0 else { return }
83+ self . counter -= 1
10684 }
10785
108- public func isAvailable ( ) async -> Bool {
109- await self . state . isAvailable ( )
86+ func hasElement ( ) -> Bool {
87+ self . counter > 0
11088 }
11189 }
11290
113- enum UpstreamElement {
114- case element( Element )
115- case finished
116- }
117-
11891 public struct Iterator : AsyncIteratorProtocol {
119- let passthrough = AsyncStreams . Passthrough < UpstreamElement > ( )
120- var passthroughIterator : AsyncStreams . Passthrough < UpstreamElement > . AsyncIterator
121- let upstreamIterators : [ UpstreamAsyncIterator < UpstreamAsyncSequence . AsyncIterator > ]
92+ let sink = AsyncStreams . Passthrough < UpstreamElement > ( )
93+ var sinkIterator : AsyncStreams . Passthrough < UpstreamElement > . AsyncIterator
94+ let upstreamIterators : [ SharedAsyncIterator < UpstreamAsyncSequence . AsyncIterator > ]
95+ let elementCounter = ElementCounter ( )
12296 var numberOfFinished = 0
12397
12498 public init ( upstreamIterators: [ UpstreamAsyncSequence . AsyncIterator ] ) {
125- self . upstreamIterators = upstreamIterators. map { UpstreamAsyncIterator ( iterator: $0) }
126- self . passthroughIterator = self . passthrough . makeAsyncIterator ( )
99+ self . upstreamIterators = upstreamIterators. map { SharedAsyncIterator ( iterator: $0) }
100+ self . sinkIterator = self . sink . makeAsyncIterator ( )
127101 }
128102
129- // swiftlint:disable:next cyclomatic_complexity
130- public mutating func next( ) async throws -> Element ? {
131- guard !Task. isCancelled else { return nil }
132-
133- let localPassthrough = self . passthrough
134-
135- // iterating over the upstream iterators to ask for their next element. Only
136- // the available iterators are requested (not already being computing the next
137- // element from the previous iteration)
138- for upstreamIterator in self . upstreamIterators {
139- guard !Task. isCancelled else { break }
140-
141- let localUpstreamIterator = upstreamIterator
142-
143- // isAvailable() means is not busy and not finished
144- if await localUpstreamIterator. isAvailable ( ) {
145- Task {
146- do {
147- let nextElement = try await localUpstreamIterator. next ( )
148-
149- // if the next element is nil, it means one if the upstream iterator
150- // is finished ... its does not mean the zipped async sequence is finished
151- guard let nonNilNextElement = nextElement else {
152- localPassthrough. send ( . finished)
153- return
154- }
155-
156- localPassthrough. send ( . element( nonNilNextElement) )
157- } catch is CancellationError {
158- localPassthrough. send ( . finished)
159- } catch {
160- localPassthrough. send ( termination: . failure( error) )
161- }
162- }
163- }
164- }
165-
103+ mutating func nextElementFromSink( ) async throws -> Element ? {
166104 var noValue = true
167105 var value : Element ?
168106
169107 // we now have to eliminate the intermediate ".finished" values until the next
170108 // true value is found.
171109 // if every upstream iterator has finished, then the zipped async sequence is also finished
172110 while noValue {
173- guard let nextChildElement = try await self . passthroughIterator . next ( ) else {
174- // the passthrough is finished, so is the zipped async sequence
111+ guard let nextChildElement = try await self . sinkIterator . next ( ) else {
112+ // the sink stream is finished, so is the zipped async sequence
175113 noValue = false
176114 value = nil
177115 break
@@ -187,13 +125,70 @@ public struct AsyncMergeSequence<UpstreamAsyncSequence: AsyncSequence>: AsyncSeq
187125 break
188126 }
189127 case let . element( element) :
190- // nominal case: a net element is available
128+ // nominal case: a next element is available
191129 noValue = false
192130 value = element
131+ await self . elementCounter. decreaseCounter ( )
193132 }
194133 }
195134
196135 return value
197136 }
137+
138+ public mutating func next( ) async throws -> Element ? {
139+ guard !Task. isCancelled else { return nil }
140+
141+ // before requesting elements from the upstream iterators, we should reauest the next element from the sink iterator
142+ // if it has some stacked values
143+
144+ // for now we leave it commented as I'm not sure it is not counterproductive.
145+ // This "early" drain might prevent from requesting the next available upstream iterators as soon as possible
146+ // since the sink iterator might deliver a value and the next will return right away
147+
148+ // guard await !self.elementCounter.hasElement() else {
149+ // return try await self.nextElementFromSink()
150+ // }
151+
152+ let localSink = self . sink
153+ let localElementCounter = self . elementCounter
154+
155+ // iterating over the upstream iterators to ask for their next element. Only
156+ // the available iterators are requested (not already being computing the next
157+ // element from the previous iteration and not already finished)
158+ for upstreamIterator in self . upstreamIterators {
159+ guard !Task. isCancelled else { break }
160+
161+ let localUpstreamIterator = upstreamIterator
162+ guard await !localUpstreamIterator. isFinished ( ) else { continue }
163+ Task {
164+ do {
165+ let nextSharedElement = try await localUpstreamIterator. next ( )
166+
167+ // if the next element is nil, it means one of the upstream iterator
168+ // is finished ... its does not mean the zipped async sequence is finished (all upstream iterators have to be finished)
169+ guard let nextNonNilSharedElement = nextSharedElement else {
170+ await localSink. send ( . finished)
171+ return
172+ }
173+
174+ guard case let . value( nextElement) = nextNonNilSharedElement else {
175+ // the upstream iterator was not available ... see you at the next iteration
176+ return
177+ }
178+
179+ // we have a next element from an upstream iterator, pushing it in the sink stream
180+ await localSink. send ( . element( nextElement) )
181+ await localElementCounter. increaseCounter ( )
182+ } catch is CancellationError {
183+ await localSink. send ( . finished)
184+ } catch {
185+ await localSink. send ( termination: . failure( error) )
186+ }
187+ }
188+ }
189+
190+ // we wait for the sink iterator to deliver the next element
191+ return try await self . nextElementFromSink ( )
192+ }
198193 }
199194}
0 commit comments