dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.14k stars 4.71k forks source link

.NET Standard 2.1 vs .NET 6 Threading in Blazor WASM #61381

Closed AlmightyLks closed 2 years ago

AlmightyLks commented 2 years ago

Describe the bug

For whatever reason, threading within Blazor WASM seemed to have "worked" in .NET Standard 2.1, but within .NET 6 it's (understandably) disallowed via a PlatformNotSupportedException? I do not have a clear explanation for this, which is why I ask for further help onto why this is. I have been trying to use the roslyn compiler with .NET 6, but it kept failing on me as I noticed someone else has got it to work with .NET Standard 2.1.

To Reproduce

I have created a very simple example of the exact same code for .NET Standard 2.1 vs .NET 6 Blazor WASM here: https://github.com/AlmightyLks/BlazorWasm You may fiddle with it yourself, as I appreciate any help for this.

As to how to simply reproduce:

Exceptions (if any)

System.PlatformNotSupportedException: Cannot wait on monitors on this runtime. at 
System.Threading.Monitor.ObjWait(Int32 millisecondsTimeout, Object obj) at System.Threading.Monitor.Wait(Object obj, Int32 millisecondsTimeout) at
 System.Threading.ManualResetEventSlim.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at
 System.Threading.Tasks.Task.SpinThenBlockingWait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at
System.Threading.Tasks.Task.InternalWaitCore(Int32 millisecondsTimeout, CancellationToken cancellationToken) at 
System.Threading.Tasks.Task.InternalWait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at 
Microsoft.CodeAnalysis.CSharp.ClsComplianceChecker.WaitForWorkers() at 
Microsoft.CodeAnalysis.CSharp.ClsComplianceChecker.CheckCompliance(CSharpCompilation compilation, BindingDiagnosticBag diagnostics, CancellationToken cancellationToken, SyntaxTree filterTree, Nullable`1 filterSpanWithinTree) at
 Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetClsComplianceDiagnostics(SyntaxTree syntaxTree, Nullable`1 filterSpanWithinTree, CancellationToken cancellationToken) at
 Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetSourceDeclarationDiagnostics(SyntaxTree syntaxTree, Nullable`1 filterSpanWithinTree, Func`4 locationFilterOpt, CancellationToken cancellationToken) at
 Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetDiagnosticsWithoutFiltering(CompilationStage stage, Boolean includeEarlierStages, BindingDiagnosticBag builder, CancellationToken cancellationToken) at
 Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetDiagnostics(CompilationStage stage, Boolean includeEarlierStages, DiagnosticBag diagnostics, CancellationToken cancellationToken) at Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetDiagnostics(CompilationStage stage, Boolean includeEarlierStages, CancellationToken cancellationToken) at
Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetDiagnostics(CancellationToken cancellationToken) at
 BlazorNet6.Compiler.TryLoadSource(String source, Assembly& assembly) in C:\Users\Wholesome\source\repos\BlazorWasm\BlazorNet6\Compiler.cs:line 58 at 
BlazorNet6.Pages.Index.ButtonClick(MouseEventArgs args) in C:\Users\Wholesome\source\repos\BlazorWasm\BlazorNet6\Pages\Index.razor:line 36

Further technical details

"Include the output of dotnet --info"


.NET SDK (reflecting any global.json):
Version:   6.0.100
Commit:    9e8b04bbff

Runtime Environment: OS Name: Windows OS Version: 10.0.19042 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\6.0.100\

Host (useful for support): Version: 6.0.0 Commit: 4822e3c3aa

.NET SDKs installed: 5.0.402 [C:\Program Files\dotnet\sdk] 6.0.100-preview.7.21379.14 [C:\Program Files\dotnet\sdk] 6.0.100 [C:\Program Files\dotnet\sdk]

.NET runtimes installed: Microsoft.AspNetCore.All 2.1.28 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.App 2.1.28 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.0-preview.7.21378.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 2.1.28 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.20 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.0-preview.7.21377.19 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 3.1.20 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.0-preview.7.21378.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]



Please let me know if you need any more information.
dotnet-issue-labeler[bot] commented 2 years ago

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

pranavkm commented 2 years ago

FYI @lewing

ghost commented 2 years ago

Tagging subscribers to 'arch-wasm': @lewing See info in area-owners.md if you want to be subscribed.

Issue Details
### Describe the bug For whatever reason, threading within Blazor WASM seemed to have "worked" in .NET Standard 2.1, but within .NET 6 it's (understandably) disallowed via a PlatformNotSupportedException? I do not have a clear explanation for this, which is why I ask for further help onto why this is. I have been trying to use the roslyn compiler with .NET 6, but it kept failing on me as I noticed someone else has got it to work with .NET Standard 2.1. ### To Reproduce I have created a very simple example of the exact same code for .NET Standard 2.1 vs .NET 6 Blazor WASM here: https://github.com/AlmightyLks/BlazorWasm You may fiddle with it yourself, as I appreciate any help for this. As to how to simply reproduce: - Run both projects - Hit the "Run" button for both of them - Look at the result ### Exceptions (if any) ``` System.PlatformNotSupportedException: Cannot wait on monitors on this runtime. at System.Threading.Monitor.ObjWait(Int32 millisecondsTimeout, Object obj) at System.Threading.Monitor.Wait(Object obj, Int32 millisecondsTimeout) at System.Threading.ManualResetEventSlim.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task.SpinThenBlockingWait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task.InternalWaitCore(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task.InternalWait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at Microsoft.CodeAnalysis.CSharp.ClsComplianceChecker.WaitForWorkers() at Microsoft.CodeAnalysis.CSharp.ClsComplianceChecker.CheckCompliance(CSharpCompilation compilation, BindingDiagnosticBag diagnostics, CancellationToken cancellationToken, SyntaxTree filterTree, Nullable`1 filterSpanWithinTree) at Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetClsComplianceDiagnostics(SyntaxTree syntaxTree, Nullable`1 filterSpanWithinTree, CancellationToken cancellationToken) at Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetSourceDeclarationDiagnostics(SyntaxTree syntaxTree, Nullable`1 filterSpanWithinTree, Func`4 locationFilterOpt, CancellationToken cancellationToken) at Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetDiagnosticsWithoutFiltering(CompilationStage stage, Boolean includeEarlierStages, BindingDiagnosticBag builder, CancellationToken cancellationToken) at Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetDiagnostics(CompilationStage stage, Boolean includeEarlierStages, DiagnosticBag diagnostics, CancellationToken cancellationToken) at Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetDiagnostics(CompilationStage stage, Boolean includeEarlierStages, CancellationToken cancellationToken) at Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetDiagnostics(CancellationToken cancellationToken) at BlazorNet6.Compiler.TryLoadSource(String source, Assembly& assembly) in C:\Users\Wholesome\source\repos\BlazorWasm\BlazorNet6\Compiler.cs:line 58 at BlazorNet6.Pages.Index.ButtonClick(MouseEventArgs args) in C:\Users\Wholesome\source\repos\BlazorWasm\BlazorNet6\Pages\Index.razor:line 36 ``` ### Further technical details - Blazor WASM .NET Standard 2.1 - Blazor WASM .NET 6 - Visual Studio 2022 Version 17.0.0 > "Include the output of `dotnet --info`" ``` .NET SDK (reflecting any global.json): Version: 6.0.100 Commit: 9e8b04bbff Runtime Environment: OS Name: Windows OS Version: 10.0.19042 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\6.0.100\ Host (useful for support): Version: 6.0.0 Commit: 4822e3c3aa .NET SDKs installed: 5.0.402 [C:\Program Files\dotnet\sdk] 6.0.100-preview.7.21379.14 [C:\Program Files\dotnet\sdk] 6.0.100 [C:\Program Files\dotnet\sdk] .NET runtimes installed: Microsoft.AspNetCore.All 2.1.28 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.App 2.1.28 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 5.0.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.0-preview.7.21378.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 6.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 2.1.28 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.30 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.20 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 5.0.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.0-preview.7.21377.19 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 6.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 3.1.20 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 5.0.11 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.0-preview.7.21378.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 6.0.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] ``` Please let me know if you need any more information.
Author: AlmightyLks
Assignees: -
Labels: `arch-wasm`, `untriaged`
Milestone: -
marek-safar commented 2 years ago

Duplicate of #61308

AlmightyLks commented 2 years ago

@marek-safar Mind me asking how this explains why it works with .net standard 2.1 and not with .net 6? I may bring this question over to the other issue if you will

marek-safar commented 2 years ago

Microsoft.CodeAnalysis.CSharp has different builds for NS2.0 and NETCORE3.1. It's quite possible that the implementation for NS2.0 is less optimized or different in another way but the general problem is the same. Most threading APIs are PNSE for browsers right now.

AlmightyLks commented 2 years ago

It may have different builds for .net core 3.1 & .net standard 2.0 https://github.com/dotnet/roslyn/blob/main/src/Compilers/CSharp/Portable/Microsoft.CodeAnalysis.CSharp.csproj#L8

However, all that WaitForWorkers() does is calling .GetAwaiter().GetResult() https://github.com/dotnet/roslyn/blob/315c2e149ba7889b0937d872274c33fcbfe9af5f/src/Compilers/CSharp/Portable/Compiler/ClsComplianceChecker.cs#L166-L178

I can't seem to find a target framework-dependent implementation on this within the roslyn compiler library, and this leads back to the standard library

Correct me if I am wrong here

scalablecory commented 2 years ago

@marek-safar it seems like @AlmightyLks is still looking for more information but a bot accidentally auto-closed this. Reopening!

scalablecory commented 2 years ago

Judging by this exception, this happens because there's a Task.Wait() that blocks inside of Microsoft.CodeAnalysis.CSharp.ClsComplianceChecker.CheckCompliance.

iirc WASM is single-threaded, so a Task.Wait() that blocks would be a deadlock, right? If so, I think Blazor/WASM is working fine here, and you may need to call an async version of that method and/or file a bug in dotnet/roslyn.

MerlinVR commented 2 years ago

If anyone runs into this particular issue, a quick workaround I found for the current Task.Wait() limitation while using Roslyn which doesn't involve changing the target framework to standard 2.1 is to set concurrentBuild: false on the CSharpCompilationOptions when creating the compilation.

marek-safar commented 2 years ago

@AlmightyLks I didn't look that closely to find out what are the differences but it's possible that netstandard version has a different code path or configuration elsewhere.

lewing commented 2 years ago

Given that the current wasm runtime is single threaded, In the move to .NET6 we decided it was better to Fail all .Wait() calls than to have some fail seemingly randomly at runtime. Because of that change this is expected. I hope the workaround is sufficient. Closing