Skip to content

Commit 0125fe0

Browse files
committed
Fix Create livelock
1 parent b6cf679 commit 0125fe0

File tree

3 files changed

+38
-26
lines changed

3 files changed

+38
-26
lines changed

async-enumerable-dotnet-benchmark/Program.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,19 @@ class Program
2121
// ReSharper disable once ArrangeTypeMemberModifiers
2222
static void Main(string[] args)
2323
{
24-
for (var i = 0; i < 100000; i++)
24+
for (var j = 0; j < 100000; j++)
2525
{
26-
if (i % 10 == 0)
26+
if (j % 10 == 0)
2727
{
28-
Console.WriteLine(i);
28+
Console.WriteLine(j);
2929
}
30-
var list = AsyncEnumerable.Range(1, 100_000)
31-
.SwitchMap(v => AsyncEnumerable.Range(v, 2))
30+
var list = AsyncEnumerable.Create<int>(async e =>
31+
{
32+
for (var i = 0; i < 10 && !e.DisposeAsyncRequested; i++)
33+
{
34+
await e.Next(i);
35+
}
36+
})
3237
.Last()
3338
.GetAsyncEnumerator();
3439

@@ -39,7 +44,7 @@ static void Main(string[] args)
3944
Console.WriteLine("Empty?");
4045
}
4146

42-
if (list.Current != 100_001)
47+
if (list.Current != 9)
4348
{
4449
Console.WriteLine(list.Current);
4550
Console.ReadLine();

async-enumerable-dotnet-test/CreateTest.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public async void Empty()
2222
}
2323

2424
[Fact]
25-
public async void Range()
25+
public async ValueTask Range()
2626
{
2727
var result = AsyncEnumerable.Create<int>(async e =>
2828
{
@@ -35,5 +35,13 @@ public async void Range()
3535
await result.AssertResult(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
3636
}
3737

38+
[Fact]
39+
public async void Range_Loop()
40+
{
41+
for (int j = 0; j < 1000; j++)
42+
{
43+
await Range();
44+
}
45+
}
3846
}
3947
}

async-enumerable-dotnet/impl/CreateEmitter.cs

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ private sealed class CreateEmitterEnumerator : IAsyncEnumerator<T>, IAsyncEmitte
2929

3030
private volatile bool _disposeRequested;
3131

32-
private volatile bool _taskComplete;
33-
3432
public bool DisposeAsyncRequested => _disposeRequested;
3533

34+
private bool _hasValue;
35+
3636
public T Current { get; private set; }
3737

3838
private TaskCompletionSource<bool> _valueReady;
@@ -42,9 +42,19 @@ private sealed class CreateEmitterEnumerator : IAsyncEnumerator<T>, IAsyncEmitte
4242
internal void SetTask(Task task)
4343
{
4444
_task = task;
45-
task.ContinueWith(t =>
45+
task.ContinueWith(async t =>
4646
{
47-
_taskComplete = true;
47+
if (_disposeRequested)
48+
{
49+
return;
50+
}
51+
await ResumeHelper.Await(ref _consumed);
52+
ResumeHelper.Clear(ref _consumed);
53+
if (_disposeRequested)
54+
{
55+
return;
56+
}
57+
4858
ResumeHelper.Resume(ref _valueReady);
4959
});
5060
}
@@ -58,28 +68,16 @@ public ValueTask DisposeAsync()
5868

5969
public async ValueTask<bool> MoveNextAsync()
6070
{
61-
if (_taskComplete)
62-
{
63-
if (_task.IsFaulted)
64-
{
65-
throw _task.Exception;
66-
}
67-
return false;
68-
}
6971
ResumeHelper.Resume(ref _consumed);
7072

7173
await ResumeHelper.Await(ref _valueReady);
7274
ResumeHelper.Clear(ref _valueReady);
73-
74-
if (!_taskComplete)
75+
if (_hasValue)
7576
{
77+
_hasValue = false;
7678
return true;
7779
}
78-
79-
if (_task.IsFaulted)
80-
{
81-
throw _task.Exception;
82-
}
80+
Current = default;
8381
return false;
8482
}
8583

@@ -97,6 +95,7 @@ public async ValueTask Next(T value)
9795
}
9896

9997
Current = value;
98+
_hasValue = true;
10099

101100
ResumeHelper.Resume(ref _valueReady);
102101
}

0 commit comments

Comments
 (0)