mirror of https://github.com/Cysharp/UniTask
Fix race condition (todo: too wide lock range?)
parent
b195df9773
commit
6e99accf99
|
@ -13,33 +13,24 @@ namespace NetCoreTests.Linq
|
|||
[Fact]
|
||||
public async Task TwoSource()
|
||||
{
|
||||
var semaphore = new SemaphoreSlim(1, 1);
|
||||
|
||||
var a = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||
{
|
||||
await UniTask.SwitchToThreadPool();
|
||||
|
||||
await semaphore.WaitAsync();
|
||||
await writer.YieldAsync("A1");
|
||||
semaphore.Release();
|
||||
|
||||
await semaphore.WaitAsync();
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(20));
|
||||
await writer.YieldAsync("A2");
|
||||
semaphore.Release();
|
||||
});
|
||||
|
||||
var b = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||
{
|
||||
await UniTask.SwitchToThreadPool();
|
||||
|
||||
await semaphore.WaitAsync();
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(10));
|
||||
await writer.YieldAsync("B1");
|
||||
await writer.YieldAsync("B2");
|
||||
semaphore.Release();
|
||||
|
||||
await semaphore.WaitAsync();
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(30));
|
||||
await writer.YieldAsync("B3");
|
||||
semaphore.Release();
|
||||
});
|
||||
|
||||
var result = await a.Merge(b).ToArrayAsync();
|
||||
|
@ -49,33 +40,27 @@ namespace NetCoreTests.Linq
|
|||
[Fact]
|
||||
public async Task ThreeSource()
|
||||
{
|
||||
var semaphore = new SemaphoreSlim(0, 1);
|
||||
|
||||
var a = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||
{
|
||||
await UniTask.SwitchToThreadPool();
|
||||
|
||||
await semaphore.WaitAsync();
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(10));
|
||||
await writer.YieldAsync("A1");
|
||||
semaphore.Release();
|
||||
|
||||
await semaphore.WaitAsync();
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(30));
|
||||
await writer.YieldAsync("A2");
|
||||
semaphore.Release();
|
||||
});
|
||||
|
||||
var b = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||
{
|
||||
await UniTask.SwitchToThreadPool();
|
||||
|
||||
await semaphore.WaitAsync();
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(20));
|
||||
await writer.YieldAsync("B1");
|
||||
await writer.YieldAsync("B2");
|
||||
semaphore.Release();
|
||||
|
||||
await semaphore.WaitAsync();
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(40));
|
||||
await writer.YieldAsync("B3");
|
||||
semaphore.Release();
|
||||
});
|
||||
|
||||
var c = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||
|
@ -83,7 +68,6 @@ namespace NetCoreTests.Linq
|
|||
await UniTask.SwitchToThreadPool();
|
||||
|
||||
await writer.YieldAsync("C1");
|
||||
semaphore.Release();
|
||||
});
|
||||
|
||||
var result = await a.Merge(b, c).ToArrayAsync();
|
||||
|
|
|
@ -89,18 +89,21 @@ namespace Cysharp.Threading.Tasks.Linq
|
|||
cancellationToken.ThrowIfCancellationRequested();
|
||||
completionSource.Reset();
|
||||
|
||||
if (TryDequeue(out var queuedValue, out var queuedException))
|
||||
lock (states)
|
||||
{
|
||||
if (queuedException != null)
|
||||
if (TryDequeue(out var queuedValue, out var queuedException))
|
||||
{
|
||||
completionSource.TrySetException(queuedException);
|
||||
if (queuedException != null)
|
||||
{
|
||||
completionSource.TrySetException(queuedException);
|
||||
}
|
||||
else
|
||||
{
|
||||
Current = queuedValue;
|
||||
completionSource.TrySetResult(!IsCompletedAll());
|
||||
}
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
Current = queuedValue;
|
||||
completionSource.TrySetResult(!IsCompletedAll());
|
||||
}
|
||||
return new UniTask<bool>(this, completionSource.Version);
|
||||
}
|
||||
|
||||
for (var i = 0; i < length; i++)
|
||||
|
@ -159,7 +162,8 @@ namespace Cysharp.Threading.Tasks.Linq
|
|||
{
|
||||
if (!completionSource.TrySetException(ex))
|
||||
{
|
||||
lock (resultQueue)
|
||||
//
|
||||
lock (states)
|
||||
{
|
||||
resultQueue.Enqueue((default, ex));
|
||||
}
|
||||
|
@ -167,27 +171,27 @@ namespace Cysharp.Threading.Tasks.Linq
|
|||
return;
|
||||
}
|
||||
|
||||
var completed = IsCompletedAll();
|
||||
if (hasNext || completed)
|
||||
var completedAll = IsCompletedAll();
|
||||
if (hasNext || completedAll)
|
||||
{
|
||||
if (completionSource.GetStatus(completionSource.Version).IsCompleted())
|
||||
lock (states)
|
||||
{
|
||||
lock (resultQueue)
|
||||
if (completionSource.GetStatus(completionSource.Version).IsCompleted())
|
||||
{
|
||||
resultQueue.Enqueue((enumerators[index].Current, null));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Current = enumerators[index].Current;
|
||||
completionSource.TrySetResult(!completed);
|
||||
else
|
||||
{
|
||||
Current = enumerators[index].Current;
|
||||
completionSource.TrySetResult(!completedAll);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TryDequeue(out T value, out Exception ex)
|
||||
{
|
||||
lock (resultQueue)
|
||||
lock (states)
|
||||
{
|
||||
if (resultQueue.Count > 0)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue