diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 4106ed3ece2ac0..feb456ffbdb241 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -11927,6 +11927,9 @@ bool Compiler::impWrapTopOfStackInAwait() info.compIsStatic ? fgGetCritSectOfStaticMethod() : gtNewLclvNode(info.compThisArg, TYP_REF); GenTree* exitMon = gtNewHelperCallNode(CORINFO_HELP_MON_EXIT, TYP_VOID, lockObject, varAddrNode); impAppendTree(exitMon, CHECK_SPILL_ALL, impCurStmtDI); + + // The fault handler must not release the monitor again if the await throws. + impStoreToTemp(lvaMonAcquired, gtNewZeroConNode(TYP_I_IMPL), CHECK_SPILL_ALL); } if (impFoldAwaitedTopOfStack()) diff --git a/src/tests/async/regression/synchronized-async-version.cs b/src/tests/async/regression/synchronized-async-version.cs index b032dd17eaef89..a7f239c994c741 100644 --- a/src/tests/async/regression/synchronized-async-version.cs +++ b/src/tests/async/regression/synchronized-async-version.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -8,6 +9,48 @@ public class Async2Synchronized { + [Theory] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public static void FaultedAwaitable(bool useValueTask, bool callerHoldsLock) + { + Async2Synchronized p = new(); + InvalidOperationException expected = new("boom"); + Task task = Task.FromException(expected); + + if (callerHoldsLock) + { + Monitor.Enter(p); + } + + try + { + InvalidOperationException actual = Assert.Throws(() => + { + if (useValueTask) + { + p.FooValueTask(new ValueTask(task)).GetAwaiter().GetResult(); + } + else + { + p.Foo(task).GetAwaiter().GetResult(); + } + }); + + Assert.Same(expected, actual); + Assert.Equal(callerHoldsLock, Monitor.IsEntered(p)); + } + finally + { + if (Monitor.IsEntered(p)) + { + Monitor.Exit(p); + } + } + } + [Fact] public static void TestEntryPoint() {