Fix double monitor release in synchronized runtime-async methods - #133866
Conversation
|
Azure Pipelines: 16 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
Tagging subscribers to this area: @akoeplinger, @matouskozak, @simonrozsival |
Co-authored-by: jakobbotsch <7887810+jakobbotsch@users.noreply.github.com>
|
Azure Pipelines: Successfully started running 5 pipeline(s). 11 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
🔵 Needs a closer look
The JIT monitor-release change affects exception handling and lock ownership, warranting final human review.
Pull request overview
Fixes double monitor release in synchronized runtime-async methods and adds regression coverage.
Changes:
- Clears
lvaMonAcquiredafter monitor release. - Tests
TaskandValueTaskexception identity and lock ownership.
File summaries
| File | Description |
|---|---|
src/tests/async/regression/synchronized-async-version.cs |
Adds synchronized async exception and ownership tests. |
src/coreclr/jit/importer.cpp |
Prevents duplicate monitor release during fault handling. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Lite
|
cc @dotnet/jit-contrib PTAL @EgorBo I will backport this. To my surprise we actually have synchronized task-returning methods in the BCL that may throw (due to cancellation): runtime/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs Lines 1088 to 1098 in f9b470a So this problem is reproducible with: using StreamWriter sw = new StreamWriter(new MemoryStream());
var sb = new StringBuilder();
await TextWriter.Synchronized(sw).WriteLineAsync(sb, new CancellationToken(canceled: true));in .NET 11 with runtime async enabled. |
|
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
|
/ba-g Failure is the one fixed by #133775 |
|
/backport to release/11.0 |
|
Started backporting to |
…nc methods (#133935) Backport of #133866 to release/11.0 /cc @jakobbotsch @Copilot ## Customer Impact - [ ] Customer reported - [X] Found internally Awaiting a synchronized task-returning method that throws from a runtime async method results in double releasing the synchronized lock, throwing the wrong exception to the consumer. For example, in .NET 11, the following code throws `System.Threading.SynchronizationLockException` instead of `System.Threading.Tasks.TaskCanceledException` if compiled with runtime async: ```csharp using StreamWriter sw = new StreamWriter(new MemoryStream()); var sb = new StringBuilder(); await TextWriter.Synchronized(sw).WriteLineAsync(sb, new CancellationToken(canceled: true)); ``` ## Regression - [X] Yes - [ ] No Introduced by async version work in #129901 ## Testing Unit test added. ## Risk Low. Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: jakobbotsch <7887810+jakobbotsch@users.noreply.github.com>
…dotnet#133866) Awaiting a faulted awaitable returned by a synchronized method can release the monitor twice, replacing the original exception or releasing a caller-owned recursive acquisition. - **JIT:** Clear `lvaMonAcquired` immediately after the importer-generated monitor exit, preventing a subsequent await exception from triggering another release. The released-state value is `0`; `1` means acquired. - **Regression coverage:** Extend the existing `Task` and `ValueTask` tests to verify exception identity and monitor ownership, with and without a caller-held lock. Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: jakobbotsch <7887810+jakobbotsch@users.noreply.github.com>
Awaiting a faulted awaitable returned by a synchronized method can release the monitor twice, replacing the original exception or releasing a caller-owned recursive acquisition.
lvaMonAcquiredimmediately after the importer-generated monitor exit, preventing a subsequent await exception from triggering another release. The released-state value is0;1means acquired.TaskandValueTasktests to verify exception identity and monitor ownership, with and without a caller-held lock.