Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions BitFaster.Caching.UnitTests/Atomic/AsyncAtomicFactoryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,48 @@ public async Task WhenCallersRunConcurrentlyResultIsFromWinner()
winnerCount.Should().Be(1);
}

[Fact]
public async Task WhenCallersRunConcurrentlyWithFailureSameExceptionIsPropagated()
{
var enter = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
var resume = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);

var atomicFactory = new AsyncAtomicFactory<int, int>();

var first = atomicFactory.GetValueAsync(1, async k =>
{
enter.SetResult(true);
await resume.Task;

throw new ArithmeticException("1");
}).AsTask();

var second = atomicFactory.GetValueAsync(1, async k =>
{
enter.SetResult(true);
await resume.Task;

throw new InvalidOperationException("2");
}).AsTask();

await enter.Task;
resume.SetResult(true);

// Both tasks will throw, but the first one to complete will propagate its exception
// Both exceptions should be the same. If they are not, there will be an aggregate exception.
try
{
await Task.WhenAll(first, second)
.TimeoutAfter(TimeSpan.FromSeconds(5), "Tasks did not complete within the expected time. Exceptions are not propagated between callers correctly.");
}
catch (ArithmeticException)
{
}
catch (InvalidOperationException)
{
}
}

[Fact]
public void WhenValueNotCreatedHashCodeIsZero()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public void WhenRemovedEventHandlerIsRegisteredItIsFired()
}

// backcompat: remove conditional compile
#if NETCOREAPP3_0_OR_GREATER
#if NET
[Fact]
public void WhenUpdatedEventHandlerIsRegisteredItIsFired()
{
Expand Down Expand Up @@ -259,7 +259,7 @@ public async Task WhenFactoryThrowsEmptyKeyIsNotEnumerable()
}

// backcompat: remove conditional compile
#if NETCOREAPP3_0_OR_GREATER
#if NET
[Fact]
public void WhenRemovedValueIsReturned()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,49 @@ public async Task WhenCallersRunConcurrentlyResultIsFromWinner()
winnerCount.Should().Be(1);
}


[Fact]
public async Task WhenCallersRunConcurrentlyWithFailureSameExceptionIsPropagated()
{
var enter = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
var resume = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);

var atomicFactory = new ScopedAsyncAtomicFactory<int, IntHolder>();

var first = atomicFactory.TryCreateLifetimeAsync(1, async k =>
{
enter.SetResult(true);
await resume.Task;

throw new ArithmeticException("1");
}).AsTask(); ;

var second = atomicFactory.TryCreateLifetimeAsync(1, async k =>
{
enter.SetResult(true);
await resume.Task;

throw new InvalidOperationException("2");
}).AsTask();

await enter.Task;
resume.SetResult(true);

// Both tasks will throw, but the first one to complete will propagate its exception
// Both exceptions should be the same. If they are not, there will be an aggregate exception.
try
{
await Task.WhenAll(first, second)
.TimeoutAfter(TimeSpan.FromSeconds(5), "Tasks did not complete within the expected time. Exceptions are not propagated between callers correctly.");
}
catch (ArithmeticException)
{
}
catch (InvalidOperationException)
{
}
}

[Fact]
public async Task WhenDisposedWhileInitResultIsDisposed()
{
Expand Down
Loading