morelinq / MoreLINQ

Extensions to LINQ to Objects
https://morelinq.github.io/
Apache License 2.0
3.63k stars 409 forks source link

`Merge` masks original exception with an `ArgumentException` about no tasks #1056

Closed atifaziz closed 3 months ago

atifaziz commented 5 months ago

Code to reproduce:

var items =
    Enumerable.Range(1, 3)
              .Select(n => Source(n, n % 2 is 0))
              .Merge();

await foreach (var item in items)
    Console.WriteLine(item);

async IAsyncEnumerable<int> Source(int n, bool fail = false)
{
    await Task.Delay(TimeSpan.FromSeconds(n));
    yield return n;
    if (fail)
        throw new();
}

When run, it will output:

1
2
System.ArgumentException: The tasks argument contains no tasks. (Parameter 'tasks')
   at System.Threading.Tasks.Task.WhenAny[TTask](ReadOnlySpan`1 tasks)
   at System.Threading.Tasks.Task.WhenAny[TTask](IEnumerable`1 tasks)
   at System.Threading.Tasks.Task.WhenAny[TResult](IEnumerable`1 tasks)
   at MoreLinq.Experimental.Async.ExperimentalEnumerable.<>c__DisplayClass1_0`1.<<Merge>g__Async|0>d.MoveNext()
--- End of stack trace from previous location ---
   at MoreLinq.Experimental.Async.ExperimentalEnumerable.<>c__DisplayClass1_0`1.<<Merge>g__Async|0>d.System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult(Int16 token)
   at UserQuery.Main() in C:\Users\johndoe\AppData\Local\Temp\LINQPad8\_hiuxehhq\uljkai\LINQPadQuery:line 7
   at UserQuery.Main() in C:\Users\johndoe\AppData\Local\Temp\LINQPad8\_hiuxehhq\uljkai\LINQPadQuery:line 7

It throws ArgumentException when it should have thrown Exception.

The problem seems to be due to the following clean-up loop, which can call Task.WhenAny with zero tasks:

https://github.com/morelinq/MoreLINQ/blob/0e154ef592f33ce0f6f3d534a9eedee273f0ce72/MoreLinq/Experimental/Async/Merge.cs#L204-L211