Skip to content

Commit

Permalink
Fix Union Dispose bug #2112 (#2131)
Browse files Browse the repository at this point in the history
* Fix Union Dispose bug #2112
* Update Ix build to use .NET 8.0 SDK
* Align ref project names with assembly names (This seems to have become necessary in .NET SDK 8.0.)
  • Loading branch information
idg10 authored Jun 19, 2024
1 parent 2edf31a commit 4f341fc
Show file tree
Hide file tree
Showing 10 changed files with 195 additions and 12 deletions.
6 changes: 3 additions & 3 deletions Ix.NET/Source/Ix.NET.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

<ItemGroup>
<ProjectReference Include="..\System.Interactive\System.Interactive.csproj" />
<ReferenceAssemblyProjectReference Include="..\refs\System.Interactive.Providers.Ref\System.Interactive.Providers.Ref.csproj" ReferenceOutputAssembly="false" />
<ReferenceAssemblyProjectReference Include="..\refs\System.Interactive.Providers.Ref\System.Interactive.Providers.csproj" ReferenceOutputAssembly="false" />
</ItemGroup>

</Project>
2 changes: 1 addition & 1 deletion Ix.NET/Source/System.Interactive/System.Interactive.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<ItemGroup>
<EmbeddedResource Include="Properties\System.Interactive.rd.xml" />
<ReferenceAssemblyProjectReference Include="..\refs\System.Interactive.Ref\System.Interactive.Ref.csproj" ReferenceOutputAssembly="false" />
<ReferenceAssemblyProjectReference Include="..\refs\System.Interactive.Ref\System.Interactive.csproj" ReferenceOutputAssembly="false" />
</ItemGroup>

</Project>
179 changes: 179 additions & 0 deletions Ix.NET/Source/System.Linq.Async.Tests/System/Linq/Operators/Union.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

Expand Down Expand Up @@ -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<int>
{
public bool Equals(int x, int y)
Expand All @@ -174,5 +297,61 @@ public int GetHashCode(int obj)
return EqualityComparer<int>.Default.GetHashCode(Math.Abs(obj));
}
}

private class DisposalDetectingEnumerable : IAsyncEnumerable<int>
{
private readonly int _start;
private readonly int _count;

public DisposalDetectingEnumerable(int start, int count)
{
_start = start;
_count = count;
}

public List<Enumerator> Enumerators { get; } = new List<Enumerator>();

public IAsyncEnumerator<int> GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
Enumerator r = new(_start, _count);
Enumerators.Add(r);
return r;
}

public class Enumerator : IAsyncEnumerator<int>
{
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<bool> MoveNextAsync()
{
if (++Current < _max)
{
return new ValueTask<bool>(true);
}

return new ValueTask<bool>(false);
}
}
}
}
}
2 changes: 1 addition & 1 deletion Ix.NET/Source/System.Linq.Async/System.Linq.Async.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

<ItemGroup>
<PackageReference Condition="'$(TargetFramework)' == 'net48' or '$(TargetFramework)' == 'netstandard2.0' " Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
<ReferenceAssemblyProjectReference Include="..\refs\System.Linq.Async.Ref\System.Linq.Async.Ref.csproj" />
<ReferenceAssemblyProjectReference Include="..\refs\System.Linq.Async.Ref\System.Linq.Async.csproj" />
<ProjectReference Include="..\System.Linq.Async.SourceGenerator\System.Linq.Async.SourceGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" Private="false" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,10 @@ protected sealed override async ValueTask<bool> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@
<AssemblyTitle>Interactive Extensions - Providers Library</AssemblyTitle>
<TargetFrameworks>net4.8;netstandard2.1;net6.0</TargetFrameworks>
<PackageTags>Ix;Interactive;Extensions;Enumerable</PackageTags>
<AssemblyName>System.Interactive.Providers</AssemblyName>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\System.Interactive.Ref\System.Interactive.Ref.csproj" />
<ProjectReference Include="..\System.Interactive.Ref\System.Interactive.csproj" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
<PropertyGroup>
<Description>Interactive Extensions Main Library used to express queries over enumerable sequences.</Description>
<AssemblyTitle>Interactive Extensions - Main Library</AssemblyTitle>
<AssemblyName>System.Interactive</AssemblyName>
<Authors>Microsoft</Authors>
<TargetFrameworks>net4.8;netstandard2.1;net6.0</TargetFrameworks>
<PackageTags>Ix;Interactive;Extensions;Enumerable</PackageTags>
Expand Down
10 changes: 8 additions & 2 deletions azure-pipelines.ix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 4f341fc

Please sign in to comment.