Skip to content

Commit 948b637

Browse files
authored
Merge branch 'main' into daily-test-improver/performance-optimization-20250829184016
2 parents 32c7574 + 9c681e1 commit 948b637

1 file changed

Lines changed: 36 additions & 1 deletion

File tree

src/FSharp.Control.AsyncSeq/AsyncSeq.fs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,42 @@ module AsyncSeq =
374374
member x.Dispose() = () } }
375375

376376
let append (inp1: AsyncSeq<'T>) (inp2: AsyncSeq<'T>) : AsyncSeq<'T> =
377-
AsyncGenerator.append inp1 inp2
377+
// Optimized append implementation that doesn't create generator chains
378+
// This fixes the memory leak issue in Issue #35
379+
{ new IAsyncEnumerable<'T> with
380+
member x.GetEnumerator() =
381+
let mutable currentEnumerator : IAsyncEnumerator<'T> option = None
382+
let mutable useSecond = false
383+
{ new IAsyncEnumerator<'T> with
384+
member x.MoveNext() = async {
385+
match currentEnumerator with
386+
| None ->
387+
// Start with the first sequence
388+
let enum1 = inp1.GetEnumerator()
389+
currentEnumerator <- Some enum1
390+
return! x.MoveNext()
391+
| Some enum when not useSecond ->
392+
// Try to get next element from first sequence
393+
let! result = enum.MoveNext()
394+
match result with
395+
| Some v -> return Some v
396+
| None ->
397+
// First sequence is exhausted, switch to second
398+
dispose enum
399+
let enum2 = inp2.GetEnumerator()
400+
currentEnumerator <- Some enum2
401+
useSecond <- true
402+
return! x.MoveNext()
403+
| Some enum ->
404+
// Get elements from second sequence
405+
return! enum.MoveNext()
406+
}
407+
member x.Dispose() =
408+
match currentEnumerator with
409+
| Some enum -> dispose enum
410+
| None -> ()
411+
}
412+
}
378413

379414
let inline delay (f: unit -> AsyncSeq<'T>) : AsyncSeq<'T> =
380415
AsyncGenerator.delay f

0 commit comments

Comments
 (0)