diff --git a/Ix.NET/Source/Ix.NET.sln b/Ix.NET/Source/Ix.NET.sln
index e95c5ad13c..32435c26dd 100644
--- a/Ix.NET/Source/Ix.NET.sln
+++ b/Ix.NET/Source/Ix.NET.sln
@@ -53,13 +53,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Refs", "Refs", "{A3D72E6E-4
refs\Directory.build.props = refs\Directory.build.props
EndProjectSection
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Interactive.Ref", "refs\System.Interactive.Ref\System.Interactive.Ref.csproj", "{2EC0C302-B029-4DDB-AC91-000BF11006AD}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Interactive", "refs\System.Interactive.Ref\System.Interactive.csproj", "{2EC0C302-B029-4DDB-AC91-000BF11006AD}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Interactive.Providers.Ref", "refs\System.Interactive.Providers.Ref\System.Interactive.Providers.Ref.csproj", "{5DF341BE-B369-4250-AFD4-604DE8C95E45}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Interactive.Providers", "refs\System.Interactive.Providers.Ref\System.Interactive.Providers.csproj", "{5DF341BE-B369-4250-AFD4-604DE8C95E45}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmarks.System.Interactive", "Benchmarks.System.Interactive\Benchmarks.System.Interactive.csproj", "{3285529A-8227-4D40-B524-1A1F919F0E7B}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Linq.Async.Ref", "refs\System.Linq.Async.Ref\System.Linq.Async.Ref.csproj", "{1754B36C-D0DB-4E5D-8C30-1F116046DC0F}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Linq.Async", "refs\System.Linq.Async.Ref\System.Linq.Async.csproj", "{1754B36C-D0DB-4E5D-8C30-1F116046DC0F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Linq.Async.SourceGenerator", "System.Linq.Async.SourceGenerator\System.Linq.Async.SourceGenerator.csproj", "{5C26D649-5ED4-49EE-AFBD-8FA8F12C4AE4}"
EndProject
diff --git a/Ix.NET/Source/System.Interactive.Providers/System.Interactive.Providers.csproj b/Ix.NET/Source/System.Interactive.Providers/System.Interactive.Providers.csproj
index 93b9840fdc..9f17be765d 100644
--- a/Ix.NET/Source/System.Interactive.Providers/System.Interactive.Providers.csproj
+++ b/Ix.NET/Source/System.Interactive.Providers/System.Interactive.Providers.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/Ix.NET/Source/System.Interactive/System.Interactive.csproj b/Ix.NET/Source/System.Interactive/System.Interactive.csproj
index 8badecf58b..44dbc3967a 100644
--- a/Ix.NET/Source/System.Interactive/System.Interactive.csproj
+++ b/Ix.NET/Source/System.Interactive/System.Interactive.csproj
@@ -10,7 +10,7 @@
-
+
diff --git a/Ix.NET/Source/System.Linq.Async.Tests/System/Linq/Operators/Union.cs b/Ix.NET/Source/System.Linq.Async.Tests/System/Linq/Operators/Union.cs
index 8f84ef9729..3f4c0a9f73 100644
--- a/Ix.NET/Source/System.Linq.Async.Tests/System/Linq/Operators/Union.cs
+++ b/Ix.NET/Source/System.Linq.Async.Tests/System/Linq/Operators/Union.cs
@@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading;
using System.Threading.Tasks;
using Xunit;
@@ -162,6 +163,128 @@ public async Task Union_ToList()
Assert.Equal(new[] { 1, 2, 3, 4, 5 }, (await res.ToListAsync()).OrderBy(x => x));
}
+
+ [Fact]
+ public async Task Union_DisposesNotEmpty()
+ {
+ var e1 = new DisposalDetectingEnumerable(10, 2);
+ var e2 = new DisposalDetectingEnumerable(20, 2);
+ var res = e1.Union(e2).OrderBy(x => x);
+
+ var e = res.GetAsyncEnumerator();
+ await HasNextAsync(e, 10);
+ await HasNextAsync(e, 11);
+ await HasNextAsync(e, 20);
+ await HasNextAsync(e, 21);
+ await NoNextAsync(e);
+
+ Assert.Single(e1.Enumerators);
+ Assert.Single(e2.Enumerators);
+ Assert.Equal([true, true], [e1.Enumerators[0].Disposed, e2.Enumerators[0].Disposed]);
+ }
+
+ [Fact]
+ public async Task Union_DisposesFirstEmpty()
+ {
+ var e1 = new DisposalDetectingEnumerable(0, 0);
+ var e2 = new DisposalDetectingEnumerable(1, 1);
+ var res = e1.Union(e2);
+
+ var e = res.GetAsyncEnumerator();
+ await HasNextAsync(e, 1);
+ await NoNextAsync(e);
+
+ Assert.Single(e1.Enumerators);
+ Assert.Single(e2.Enumerators);
+ Assert.Equal([true, true], [e1.Enumerators[0].Disposed, e2.Enumerators[0].Disposed]);
+ }
+
+ [Fact]
+ public async Task Union_DisposesSecondOfTwoEmpty()
+ {
+ var e1 = new DisposalDetectingEnumerable(1, 1);
+ var e2 = new DisposalDetectingEnumerable(0, 0);
+ var res = e1.Union(e2);
+
+ var e = res.GetAsyncEnumerator();
+ await HasNextAsync(e, 1);
+ await NoNextAsync(e);
+
+ Assert.Single(e1.Enumerators);
+ Assert.Single(e2.Enumerators);
+ Assert.Equal([true, true], [e1.Enumerators[0].Disposed, e2.Enumerators[0].Disposed]);
+ }
+
+ [Fact]
+ public async Task Union_DisposesSecondOfThreeEmpty()
+ {
+ var e1 = new DisposalDetectingEnumerable(10, 1);
+ var e2 = new DisposalDetectingEnumerable(0, 0);
+ var e3 = new DisposalDetectingEnumerable(30, 1);
+ var res = e1.Union(e2).Union(e3);
+
+ var e = res.GetAsyncEnumerator();
+ await HasNextAsync(e, 10);
+ await HasNextAsync(e, 30);
+ await NoNextAsync(e);
+
+ Assert.Single(e1.Enumerators);
+ Assert.Single(e2.Enumerators);
+ Assert.Single(e3.Enumerators);
+ Assert.Equal([true, true, true], [e1.Enumerators[0].Disposed, e2.Enumerators[0].Disposed, e3.Enumerators[0].Disposed]);
+ }
+
+ [Fact]
+ public async Task Union_DisposesThirdOfThreeEmpty()
+ {
+ var e1 = new DisposalDetectingEnumerable(10, 1);
+ var e2 = new DisposalDetectingEnumerable(20, 1);
+ var e3 = new DisposalDetectingEnumerable(0, 0);
+ var res = e1.Union(e2).Union(e3);
+
+ var e = res.GetAsyncEnumerator();
+ await HasNextAsync(e, 10);
+ await HasNextAsync(e, 20);
+ await NoNextAsync(e);
+
+ Assert.Single(e1.Enumerators);
+ Assert.Single(e2.Enumerators);
+ Assert.Single(e3.Enumerators);
+ Assert.Equal([true, true, true], [e1.Enumerators[0].Disposed, e2.Enumerators[0].Disposed, e3.Enumerators[0].Disposed]);
+ }
+
+ [Fact]
+ public async Task Union_DisposesAllOfTwoEmpty()
+ {
+ var e1 = new DisposalDetectingEnumerable(0, 0);
+ var e2 = new DisposalDetectingEnumerable(0, 0);
+ var res = e1.Union(e2);
+
+ var e = res.GetAsyncEnumerator();
+ await NoNextAsync(e);
+
+ Assert.Single(e1.Enumerators);
+ Assert.Single(e2.Enumerators);
+ Assert.Equal([true, true], [e1.Enumerators[0].Disposed, e2.Enumerators[0].Disposed]);
+ }
+
+ [Fact]
+ public async Task Union_DisposesAllOfThreeEmpty()
+ {
+ var e1 = new DisposalDetectingEnumerable(0, 0);
+ var e2 = new DisposalDetectingEnumerable(0, 0);
+ var e3 = new DisposalDetectingEnumerable(0, 0);
+ var res = e1.Union(e2).Union(e3);
+
+ var e = res.GetAsyncEnumerator();
+ await NoNextAsync(e);
+
+ Assert.Single(e1.Enumerators);
+ Assert.Single(e2.Enumerators);
+ Assert.Single(e3.Enumerators);
+ Assert.Equal([true, true, true], [e1.Enumerators[0].Disposed, e2.Enumerators[0].Disposed, e3.Enumerators[0].Disposed]);
+ }
+
private sealed class Eq : IEqualityComparer
{
public bool Equals(int x, int y)
@@ -174,5 +297,61 @@ public int GetHashCode(int obj)
return EqualityComparer.Default.GetHashCode(Math.Abs(obj));
}
}
+
+ private class DisposalDetectingEnumerable : IAsyncEnumerable
+ {
+ private readonly int _start;
+ private readonly int _count;
+
+ public DisposalDetectingEnumerable(int start, int count)
+ {
+ _start = start;
+ _count = count;
+ }
+
+ public List Enumerators { get; } = new List();
+
+ public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default)
+ {
+ Enumerator r = new(_start, _count);
+ Enumerators.Add(r);
+ return r;
+ }
+
+ public class Enumerator : IAsyncEnumerator
+ {
+ private readonly int _max;
+
+ public Enumerator(int start, int count)
+ {
+ Current = start - 1;
+ _max = start + count;
+ }
+
+ public int Current { get; private set; }
+
+ public bool Disposed { get; private set; }
+
+ public void Dispose()
+ {
+ Disposed = true;
+ }
+
+ public ValueTask DisposeAsync()
+ {
+ Disposed = true;
+ return new ValueTask();
+ }
+ public ValueTask MoveNextAsync()
+ {
+ if (++Current < _max)
+ {
+ return new ValueTask(true);
+ }
+
+ return new ValueTask(false);
+ }
+ }
+ }
}
}
diff --git a/Ix.NET/Source/System.Linq.Async/System.Linq.Async.csproj b/Ix.NET/Source/System.Linq.Async/System.Linq.Async.csproj
index 5c86d96bff..d09589ba79 100644
--- a/Ix.NET/Source/System.Linq.Async/System.Linq.Async.csproj
+++ b/Ix.NET/Source/System.Linq.Async/System.Linq.Async.csproj
@@ -27,7 +27,7 @@
-
+
diff --git a/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Union.cs b/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Union.cs
index 126acb895c..c3d26418cc 100644
--- a/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Union.cs
+++ b/Ix.NET/Source/System.Linq.Async/System/Linq/Operators/Union.cs
@@ -127,10 +127,10 @@ protected sealed override async ValueTask MoveNextCore()
++_index;
var enumerator = enumerable.GetAsyncEnumerator(_cancellationToken);
+ await SetEnumeratorAsync(enumerator).ConfigureAwait(false);
if (await enumerator.MoveNextAsync().ConfigureAwait(false))
{
- await SetEnumeratorAsync(enumerator).ConfigureAwait(false);
StoreFirst();
_state = AsyncIteratorState.Iterating;
diff --git a/Ix.NET/Source/refs/System.Interactive.Providers.Ref/System.Interactive.Providers.Ref.csproj b/Ix.NET/Source/refs/System.Interactive.Providers.Ref/System.Interactive.Providers.csproj
similarity index 88%
rename from Ix.NET/Source/refs/System.Interactive.Providers.Ref/System.Interactive.Providers.Ref.csproj
rename to Ix.NET/Source/refs/System.Interactive.Providers.Ref/System.Interactive.Providers.csproj
index 716b0a8e36..6958af7df7 100644
--- a/Ix.NET/Source/refs/System.Interactive.Providers.Ref/System.Interactive.Providers.Ref.csproj
+++ b/Ix.NET/Source/refs/System.Interactive.Providers.Ref/System.Interactive.Providers.csproj
@@ -5,11 +5,10 @@
Interactive Extensions - Providers Library
net4.8;netstandard2.1;net6.0
Ix;Interactive;Extensions;Enumerable
- System.Interactive.Providers
-
+
diff --git a/Ix.NET/Source/refs/System.Interactive.Ref/System.Interactive.Ref.csproj b/Ix.NET/Source/refs/System.Interactive.Ref/System.Interactive.csproj
similarity index 91%
rename from Ix.NET/Source/refs/System.Interactive.Ref/System.Interactive.Ref.csproj
rename to Ix.NET/Source/refs/System.Interactive.Ref/System.Interactive.csproj
index 0f8f24d31d..598c60f3b7 100644
--- a/Ix.NET/Source/refs/System.Interactive.Ref/System.Interactive.Ref.csproj
+++ b/Ix.NET/Source/refs/System.Interactive.Ref/System.Interactive.csproj
@@ -3,7 +3,6 @@
Interactive Extensions Main Library used to express queries over enumerable sequences.
Interactive Extensions - Main Library
- System.Interactive
Microsoft
net4.8;netstandard2.1;net6.0
Ix;Interactive;Extensions;Enumerable
diff --git a/Ix.NET/Source/refs/System.Linq.Async.Ref/System.Linq.Async.Ref.csproj b/Ix.NET/Source/refs/System.Linq.Async.Ref/System.Linq.Async.csproj
similarity index 100%
rename from Ix.NET/Source/refs/System.Linq.Async.Ref/System.Linq.Async.Ref.csproj
rename to Ix.NET/Source/refs/System.Linq.Async.Ref/System.Linq.Async.csproj
diff --git a/azure-pipelines.ix.yml b/azure-pipelines.ix.yml
index 758943f21e..5aacc1a459 100644
--- a/azure-pipelines.ix.yml
+++ b/azure-pipelines.ix.yml
@@ -32,9 +32,15 @@ stages:
vmImage: ubuntu-latest
steps:
- task: UseDotNet@2
- displayName: Use .NET Core 6.x SDK
+ displayName: Use .NET Core 8.x SDK
inputs:
- version: 6.x
+ version: 8.x
+
+ - task: UseDotNet@2
+ displayName: .NET 6.0 runtime
+ inputs:
+ version: '6.x'
+ packageType: runtime
- task: UseDotNet@2
displayName: .NET Core 3.1 runtime