Skip to content

Commit fd07214

Browse files
authored
Fix UnobservedTaskException from SemaphoreSlim.WaitAsync (#60890)
* Fix UnobservedTaskException from SemaphoreSlim.WaitAsync If a SemaphoreSlim.WaitAsync times out, it correctly returns false, but it also results in TaskScheduler.UnobservedTaskException being raised unexpectedly, due to internal use of a faulted task whose exception isn't observed. This fixes that by marking any such exceptions as having been observed. * Fix wasm build
1 parent 39a4a70 commit fd07214

File tree

3 files changed

+32
-1
lines changed

3 files changed

+32
-1
lines changed

src/libraries/System.Private.CoreLib/src/System/Threading/SemaphoreSlim.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,7 @@ private async Task<bool> WaitUntilCountOrTimeoutAsync(TaskNode asyncWaiter, int
745745
public ConfiguredNoThrowAwaiter(Task<T> task) => _task = task;
746746
public ConfiguredNoThrowAwaiter<T> GetAwaiter() => this;
747747
public bool IsCompleted => _task.IsCompleted;
748-
public void GetResult() { }
748+
public void GetResult() => _task.MarkExceptionsAsHandled();
749749
public void UnsafeOnCompleted(Action continuation) => _task.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted(continuation);
750750
public void OnCompleted(Action continuation) => _task.ConfigureAwait(false).GetAwaiter().OnCompleted(continuation);
751751
}

src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1863,6 +1863,12 @@ internal List<ExceptionDispatchInfo> GetExceptionDispatchInfos()
18631863
return Volatile.Read(ref m_contingentProperties)?.m_exceptionsHolder?.GetCancellationExceptionDispatchInfo(); // may be null
18641864
}
18651865

1866+
/// <summary>Marks any exceptions stored in the Task as having been handled.</summary>
1867+
internal void MarkExceptionsAsHandled()
1868+
{
1869+
Volatile.Read(ref m_contingentProperties)?.m_exceptionsHolder?.MarkAsHandled(calledFromFinalizer: false);
1870+
}
1871+
18661872
/// <summary>
18671873
/// Throws an aggregate exception if the task contains exceptions.
18681874
/// </summary>

src/libraries/System.Threading/tests/SemaphoreSlimTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics;
5+
using System.Runtime.CompilerServices;
56
using System.Threading.Tasks;
7+
using Microsoft.DotNet.RemoteExecutor;
68
using Xunit;
79

810
namespace System.Threading.Tests
@@ -615,5 +617,28 @@ public static void TestConcurrentWaitAndWaitAsync(int syncWaiters, int asyncWait
615617
semaphore.Release(totalWaiters / 2);
616618
Task.WaitAll(tasks);
617619
}
620+
621+
[ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
622+
public void WaitAsync_Timeout_NoUnhandledException()
623+
{
624+
RemoteExecutor.Invoke(async () =>
625+
{
626+
Exception error = null;
627+
TaskScheduler.UnobservedTaskException += (s, e) => Volatile.Write(ref error, e.Exception);
628+
629+
var sem = new SemaphoreSlim(0);
630+
for (int i = 0; i < 2; ++i)
631+
{
632+
await sem.WaitAsync(1);
633+
GC.Collect();
634+
GC.WaitForPendingFinalizers();
635+
}
636+
637+
if (Volatile.Read(ref error) is Exception e)
638+
{
639+
throw e;
640+
}
641+
}).Dispose();
642+
}
618643
}
619644
}

0 commit comments

Comments
 (0)