Skip to content

Commit e92a94b

Browse files
committed
Merge users/alexpeck/unobservedtaskexception
2 parents abda7bc + 828ed64 commit e92a94b

File tree

4 files changed

+93
-5
lines changed

4 files changed

+93
-5
lines changed

BitFaster.Caching.UnitTests/Atomic/AsyncAtomicFactoryTests.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,45 @@ await Task.WhenAll(first, second)
156156
}
157157
}
158158

159+
[Fact]
160+
public async Task WhenValueCreateThrowsDoesNotCauseUnobservedTaskException()
161+
{
162+
bool unobservedExceptionThrown = false;
163+
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
164+
165+
try
166+
{
167+
await AsyncAtomicFactoryGetValueAsync();
168+
169+
GC.Collect();
170+
GC.WaitForPendingFinalizers();
171+
}
172+
finally
173+
{
174+
TaskScheduler.UnobservedTaskException -= OnUnobservedTaskException;
175+
}
176+
177+
unobservedExceptionThrown.Should().BeFalse();
178+
179+
void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
180+
{
181+
unobservedExceptionThrown = true;
182+
e.SetObserved();
183+
}
184+
185+
static async Task AsyncAtomicFactoryGetValueAsync()
186+
{
187+
var a = new AsyncAtomicFactory<int, int>();
188+
try
189+
{
190+
_ = await a.GetValueAsync(12, i => throw new ArithmeticException());
191+
}
192+
catch (ArithmeticException)
193+
{
194+
}
195+
}
196+
}
197+
159198
[Fact]
160199
public void WhenValueNotCreatedHashCodeIsZero()
161200
{

BitFaster.Caching.UnitTests/Atomic/ScopedAsyncAtomicFactoryTests.cs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,6 @@ public async Task WhenCallersRunConcurrentlyResultIsFromWinner()
156156
winnerCount.Should().Be(1);
157157
}
158158

159-
160159
[Fact]
161160
public async Task WhenCallersRunConcurrentlyWithFailureSameExceptionIsPropagated()
162161
{
@@ -199,6 +198,48 @@ await Task.WhenAll(first, second)
199198
}
200199
}
201200

201+
[Fact]
202+
public async Task WhenValueCreateThrowsDoesNotCauseUnobservedTaskException()
203+
{
204+
bool unobservedExceptionThrown = false;
205+
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;
206+
207+
try
208+
{
209+
await AsyncAtomicFactoryGetValueAsync();
210+
211+
GC.Collect();
212+
GC.WaitForPendingFinalizers();
213+
}
214+
finally
215+
{
216+
TaskScheduler.UnobservedTaskException -= OnUnobservedTaskException;
217+
}
218+
219+
unobservedExceptionThrown.Should().BeFalse();
220+
221+
void OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
222+
{
223+
unobservedExceptionThrown = true;
224+
e.SetObserved();
225+
}
226+
227+
static async Task AsyncAtomicFactoryGetValueAsync()
228+
{
229+
var a = new ScopedAsyncAtomicFactory<int, IntHolder>();
230+
try
231+
{
232+
_ = await a.TryCreateLifetimeAsync(1, k =>
233+
{
234+
throw new ArithmeticException();
235+
});
236+
}
237+
catch (ArithmeticException)
238+
{
239+
}
240+
}
241+
}
242+
202243
[Fact]
203244
public async Task WhenDisposedWhileInitResultIsDisposed()
204245
{

BitFaster.Caching/Atomic/AsyncAtomicFactory.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,14 @@ public async ValueTask<V> CreateValueAsync<TFactory>(K key, TFactory valueFactor
155155

156156
return value;
157157
}
158-
catch (Exception)
158+
catch (Exception ex)
159159
{
160160
Volatile.Write(ref isInitialized, false);
161-
throw;
161+
tcs.SetException(ex);
162+
163+
// always await the task to avoid unobserved task exceptions - normal case is that no other thread is waiting.
164+
// this will re-throw the exception.
165+
await tcs.Task.ConfigureAwait(false);
162166
}
163167
}
164168

BitFaster.Caching/Atomic/ScopedAsyncAtomicFactory.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,14 @@ public async ValueTask<Scoped<V>> CreateScopeAsync<TFactory>(K key, TFactory val
174174

175175
return scope;
176176
}
177-
catch (Exception)
177+
catch (Exception ex)
178178
{
179179
Volatile.Write(ref isTaskInitialized, false);
180-
throw;
180+
tcs.SetException(ex);
181+
182+
// always await the task to avoid unobserved task exceptions - normal case is that no other thread is waiting.
183+
// this will re-throw the exception.
184+
await tcs.Task.ConfigureAwait(false);
181185
}
182186
}
183187

0 commit comments

Comments
 (0)