Skip to content

Commit 9b7aa3d

Browse files
authored
[wasm][mt] throw from blocking wait on JS interop threads (#97052)
* [wasm][mt] throw from blocking wait on JS interop threads Also add test * Fix typo * Add JSProxyContextBase conditionally * Fix non-mt browser build * Fix tests build * Do not add JS interop project reference To not mix intree references and source project * Add new flag to Monitor This replaces the previous context base and makes it possible to disable throw for blocking calls in JS interop * Remove old file * Changes from unsaved file * Wrap another blocking wait in JS interop * Do not reference src/System.Runtime.InteropServices.JavaScript.csproj * Disable failing test with blocking Wait * Update the test condition * Add missing line after conflict resolution * Fix build * Update the active issue and don't use define
1 parent e5cebac commit 9b7aa3d

File tree

15 files changed

+106
-32
lines changed

15 files changed

+106
-32
lines changed

src/libraries/System.Net.Http/src/System.Net.Http.csproj

+15-7
Original file line numberDiff line numberDiff line change
@@ -446,37 +446,45 @@
446446

447447
<ItemGroup>
448448
<Reference Include="Microsoft.Win32.Primitives" />
449-
<Reference Include="System.Collections" />
450-
<Reference Include="System.Collections.Concurrent" />
451449
<Reference Include="System.Collections.NonGeneric" />
452450
<Reference Include="System.Console" Condition="'$(Configuration)' == 'Debug'" />
453451
<Reference Include="System.Diagnostics.DiagnosticSource" />
454-
<Reference Include="System.Diagnostics.Tracing" />
455452
<Reference Include="System.IO.Compression" />
456-
<Reference Include="System.Memory" />
457453
<Reference Include="System.Net.NameResolution" />
458454
<Reference Include="System.Net.NetworkInformation" />
459455
<Reference Include="System.Net.Primitives" />
460456
<Reference Include="System.Net.Quic" />
461457
<Reference Include="System.Net.Security" />
462458
<Reference Include="System.Net.Sockets" />
463-
<Reference Include="System.Runtime" />
464-
<Reference Include="System.Runtime.InteropServices" />
465459
<Reference Include="System.Security.Claims" Condition="'$(TargetPlatformIdentifier)' == 'windows'" />
466460
<Reference Include="System.Security.Cryptography" />
467461
<Reference Include="System.Security.Principal.Windows" />
468-
<Reference Include="System.Threading" />
469462
<Reference Include="System.Threading.Channels" />
470463
<Reference Include="System.Threading.ThreadPool" />
471464
<Reference Include="System.IO.Compression.Brotli" />
472465
</ItemGroup>
473466

467+
<ItemGroup Condition="'$(TargetPlatformIdentifier)' != 'browser'">
468+
<Reference Include="System.Collections" />
469+
<Reference Include="System.Collections.Concurrent" />
470+
<Reference Include="System.Diagnostics.Tracing" />
471+
<Reference Include="System.Memory" />
472+
<Reference Include="System.Runtime" />
473+
<Reference Include="System.Runtime.InteropServices" />
474+
<Reference Include="System.Threading" />
475+
</ItemGroup>
476+
474477
<ItemGroup Condition="'$(TargetPlatformIdentifier)' != '' and '$(TargetPlatformIdentifier)' != 'windows' and '$(TargetPlatformIdentifier)' != 'browser'">
475478
<Reference Include="System.Diagnostics.StackTrace" />
476479
<Reference Include="System.Security.Cryptography" />
477480
</ItemGroup>
478481

479482
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'browser'">
483+
<ProjectReference Include="$(CoreLibProject)" />
484+
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime\src\System.Runtime.csproj" />
485+
<ProjectReference Include="$(LibrariesProjectRoot)System.Threading\src\System.Threading.csproj" />
486+
<ProjectReference Include="$(LibrariesProjectRoot)System.Private.Uri\src\System.Private.Uri.csproj" PrivateAssets="all" />
487+
<ProjectReference Include="$(LibrariesProjectRoot)System.Collections.Concurrent\src\System.Collections.Concurrent.csproj" />
480488
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
481489
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\gen\JSImportGenerator\JSImportGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
482490
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\src\System.Runtime.InteropServices.JavaScript.csproj" SkipUseReferenceAssembly="true" />

src/libraries/System.Net.WebSockets.Client/src/System.Net.WebSockets.Client.csproj

+10-4
Original file line numberDiff line numberDiff line change
@@ -38,23 +38,29 @@
3838

3939
<ItemGroup>
4040
<Reference Include="Microsoft.Win32.Primitives" />
41-
<Reference Include="System.Collections" />
4241
<Reference Include="System.Collections.NonGeneric" />
4342
<Reference Include="System.Collections.Specialized" />
4443
<Reference Include="System.Diagnostics.Tracing" />
4544
<Reference Include="System.Net.Primitives" />
4645
<Reference Include="System.Net.Security" />
4746
<Reference Include="System.Net.WebHeaderCollection" />
4847
<Reference Include="System.Net.WebSockets" />
49-
<Reference Include="System.Runtime" />
50-
<Reference Include="System.Threading" />
5148
<Reference Include="System.Net.Http" />
5249
<Reference Include="System.Security.Cryptography" />
53-
<Reference Include="System.Threading.Channels" Condition="'$(TargetPlatformIdentifier)' == 'browser'" />
50+
</ItemGroup>
51+
52+
<ItemGroup Condition="'$(TargetPlatformIdentifier)' != 'browser'">
53+
<Reference Include="System.Collections" />
54+
<Reference Include="System.Runtime" />
55+
<Reference Include="System.Threading" />
5456
<Reference Include="System.Memory" />
5557
</ItemGroup>
5658

5759
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'browser'">
60+
<Reference Include="System.Threading.Channels" />
61+
<ProjectReference Include="$(CoreLibProject)" />
62+
<ProjectReference Include="$(LibrariesProjectRoot)System.Private.Uri\src\System.Private.Uri.csproj" PrivateAssets="all" />
63+
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime\src\System.Runtime.csproj" />
5864
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
5965
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\gen\JSImportGenerator\JSImportGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
6066
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\src\System.Runtime.InteropServices.JavaScript.csproj" SkipUseReferenceAssembly="true" />

src/libraries/System.Private.CoreLib/ref/System.Private.CoreLib.ExtraApis.cs

+11
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,14 @@ public static partial class Debug
3737
public static System.Diagnostics.DebugProvider SetProvider(System.Diagnostics.DebugProvider provider) { throw null; }
3838
}
3939
}
40+
41+
#if FEATURE_WASM_THREADS
42+
namespace System.Threading
43+
{
44+
public partial class Monitor
45+
{
46+
[ThreadStatic]
47+
public static bool ThrowOnBlockingWaitOnJSInteropThread;
48+
}
49+
}
50+
#endif

src/libraries/System.Private.CoreLib/ref/System.Private.CoreLib.ExtraApis.txt

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ T:System.Runtime.Serialization.DeserializationToken
55
M:System.Runtime.Serialization.SerializationInfo.StartDeserialization
66
T:System.Diagnostics.DebugProvider
77
M:System.Diagnostics.Debug.SetProvider(System.Diagnostics.DebugProvider)
8+
F:System.Threading.Monitor.ThrowOnBlockingWaitOnJSInteropThread

src/libraries/System.Runtime.InteropServices.JavaScript/src/System.Runtime.InteropServices.JavaScript.csproj

+4-12
Original file line numberDiff line numberDiff line change
@@ -74,18 +74,10 @@
7474
</ItemGroup>
7575

7676
<ItemGroup>
77-
<Reference Include="System.Collections" />
78-
<Reference Include="System.Memory" />
79-
<Reference Include="System.Net.Primitives" />
80-
<Reference Include="System.Runtime" />
81-
<Reference Include="System.Runtime.InteropServices" />
82-
<Reference Include="System.Runtime.Loader" />
83-
<Reference Include="System.Threading" />
84-
<Reference Include="System.Threading.Thread" />
85-
<Reference Include="System.Threading.Channels" />
86-
</ItemGroup>
87-
88-
<ItemGroup>
77+
<ProjectReference Include="$(CoreLibProject)" />
78+
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime\src\System.Runtime.csproj" />
79+
<ProjectReference Include="$(LibrariesProjectRoot)System.Threading\src\System.Threading.csproj" />
80+
<ProjectReference Include="$(LibrariesProjectRoot)System.Threading.Channels\src\System.Threading.Channels.csproj" />
8981
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices\gen\Microsoft.Interop.SourceGeneration\Microsoft.Interop.SourceGeneration.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
9082
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\gen\JSImportGenerator\JSImportGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
9183
</ItemGroup>

src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs

+3
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,12 @@ public static void CompleteTask(JSMarshalerArgument* arguments_buffer)
225225
}
226226
if (holder.CallbackReady != null)
227227
{
228+
var threadFlag = Monitor.ThrowOnBlockingWaitOnJSInteropThread;
229+
Monitor.ThrowOnBlockingWaitOnJSInteropThread = false;
228230
#pragma warning disable CA1416 // Validate platform compatibility
229231
holder.CallbackReady?.Wait();
230232
#pragma warning restore CA1416 // Validate platform compatibility
233+
Monitor.ThrowOnBlockingWaitOnJSInteropThread = threadFlag;
231234
}
232235
#endif
233236
var callback = holder.Callback!;

src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs

+4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public static JSSynchronizationContext InstallWebWorkerInterop(bool isMainThread
4949
var ctx = new JSSynchronizationContext(isMainThread, cancellationToken);
5050
ctx.previousSynchronizationContext = SynchronizationContext.Current;
5151
SynchronizationContext.SetSynchronizationContext(ctx);
52+
Monitor.ThrowOnBlockingWaitOnJSInteropThread = true;
5253

5354
var proxyContext = ctx.ProxyContext;
5455
JSProxyContext.CurrentThreadContext = proxyContext;
@@ -215,7 +216,10 @@ public override void Send(SendOrPostCallback d, object? state)
215216
Environment.FailFast($"JSSynchronizationContext.Send failed, ManagedThreadId: {Environment.CurrentManagedThreadId}. {Environment.NewLine} {Environment.StackTrace}");
216217
}
217218

219+
var threadFlag = Monitor.ThrowOnBlockingWaitOnJSInteropThread;
220+
Monitor.ThrowOnBlockingWaitOnJSInteropThread = false;
218221
signal.Wait();
222+
Monitor.ThrowOnBlockingWaitOnJSInteropThread = threadFlag;
219223

220224
if (_isCancellationRequested)
221225
{

src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System.Runtime.InteropServices.JavaScript.Tests.csproj

+7-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@
2929
<WasmExtraFilesToDeploy Include="System\Runtime\InteropServices\JavaScript\JavaScriptTestHelper.mjs" />
3030
<WasmExtraFilesToDeploy Include="System\Runtime\InteropServices\JavaScript\SecondRuntimeTest.js" />
3131
<WasmExtraFilesToDeploy Include="System\Runtime\InteropServices\JavaScript\timers.mjs" />
32+
<ProjectReference Include="$(CoreLibProject)" />
33+
<ProjectReference Include="$(LibrariesProjectRoot)System.Private.Uri\src\System.Private.Uri.csproj" PrivateAssets="all" />
34+
<ProjectReference Include="$(LibrariesProjectRoot)System.Collections\src\System.Collections.csproj" />
35+
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime\src\System.Runtime.csproj" />
36+
<ProjectReference Include="$(LibrariesProjectRoot)System.Memory\src\System.Memory.csproj" />
37+
<ProjectReference Include="$(LibrariesProjectRoot)System.Threading\src\System.Threading.csproj" />
38+
<ProjectReference Include="$(LibrariesProjectRoot)System.Threading.Thread\src\System.Threading.Thread.csproj" />
3239
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\src\System.Runtime.InteropServices.JavaScript.csproj" SkipUseReferenceAssembly="true" />
3340
</ItemGroup>
3441
<ItemGroup Condition="'$(FeatureWasmThreads)' != 'true'">
@@ -46,6 +53,5 @@
4653
<None Include="System\Runtime\InteropServices\JavaScript\test.json" />
4754
<WasmExtraFilesToDeploy Include="System\Runtime\InteropServices\JavaScript\WebWorkerTestHelper.mjs" />
4855
<WasmExtraFilesToDeploy Include="System\Runtime\InteropServices\JavaScript\test.json" />
49-
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\src\System.Runtime.InteropServices.JavaScript.csproj" SkipUseReferenceAssembly="true" />
5056
</ItemGroup>
5157
</Project>

src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTest.cs

+19
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,25 @@ await executor.Execute(async () =>
418418
}, cts.Token);
419419
}
420420

421+
[Theory, MemberData(nameof(GetTargetThreads))]
422+
public async Task WaitAssertsOnJSInteropThreads(Executor executor)
423+
{
424+
var cts = CreateTestCaseTimeoutSource();
425+
await executor.Execute(Task () =>
426+
{
427+
Exception? exception = null;
428+
try {
429+
Task.Delay(10, cts.Token).Wait();
430+
} catch (Exception ex) {
431+
exception = ex;
432+
}
433+
434+
executor.AssertBlockingWait(exception);
435+
436+
return Task.CompletedTask;
437+
}, cts.Token);
438+
}
439+
421440
[Theory, MemberData(nameof(GetTargetThreads))]
422441
public async Task ManagedYield(Executor executor)
423442
{

src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTestHelper.cs

+16
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,22 @@ public void AssertAwaitCapturedContext()
230230
}
231231
}
232232

233+
public void AssertBlockingWait(Exception? exception)
234+
{
235+
switch (Type)
236+
{
237+
case ExecutorType.Main:
238+
case ExecutorType.JSWebWorker:
239+
Assert.NotNull(exception);
240+
Assert.IsType<PlatformNotSupportedException>(exception);
241+
break;
242+
case ExecutorType.NewThread:
243+
case ExecutorType.ThreadPool:
244+
Assert.Null(exception);
245+
break;
246+
}
247+
}
248+
233249
public void AssertInteropThread()
234250
{
235251
switch (Type)

src/libraries/System.Threading/src/CompatibilitySuppressions.Threading.xml

+4
Original file line numberDiff line numberDiff line change
@@ -100,4 +100,8 @@
100100
<DiagnosticId>CP0014</DiagnosticId>
101101
<Target>M:System.Threading.Monitor.Wait(System.Object):[T:System.Runtime.Versioning.UnsupportedOSPlatformAttribute]</Target>
102102
</Suppression>
103+
<Suppression>
104+
<DiagnosticId>CP0002</DiagnosticId>
105+
<Target>F:System.Threading.Monitor.ThrowOnBlockingWaitOnJSInteropThread</Target>
106+
</Suppression>
103107
</Suppressions>

src/mono/System.Private.CoreLib/src/System/Threading/Monitor.Mono.cs

+11
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ namespace System.Threading
99
{
1010
public static partial class Monitor
1111
{
12+
#if FEATURE_WASM_THREADS
13+
[ThreadStatic]
14+
public static bool ThrowOnBlockingWaitOnJSInteropThread;
15+
#endif
16+
1217
[Intrinsic]
1318
[MethodImplAttribute(MethodImplOptions.InternalCall)] // Interpreter is missing this intrinsic
1419
public static void Enter(object obj) => Enter(obj);
@@ -77,6 +82,12 @@ public static bool IsEntered(object obj)
7782
public static bool Wait(object obj, int millisecondsTimeout)
7883
{
7984
ArgumentNullException.ThrowIfNull(obj);
85+
#if FEATURE_WASM_THREADS
86+
if (ThrowOnBlockingWaitOnJSInteropThread)
87+
{
88+
throw new PlatformNotSupportedException("blocking Wait is not supported on the JS interop threads.");
89+
}
90+
#endif
8091
return ObjWait(millisecondsTimeout, obj);
8192
}
8293

src/mono/browser/debugger/DebuggerTestSuite/SteppingTests.cs

+1
Original file line numberDiff line numberDiff line change
@@ -957,6 +957,7 @@ await EvaluateAndCheck(
957957
}
958958

959959
[Fact]
960+
[ActiveIssue("https://github.com/dotnet/runtime/issues/97652", typeof(DebuggerTests), nameof(DebuggerTests.WasmMultiThreaded))]
960961
public async Task StepOverWithMoreThanOneCommandInSameLineAsync()
961962
{
962963
await SetBreakpoint("dotnet://debugger-test.dll/debugger-test.cs", 710, 0);

src/mono/browser/debugger/tests/debugger-test/debugger-test.csproj

-4
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,5 @@
119119
</ItemGroup>
120120
</Target>
121121

122-
<ItemGroup>
123-
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\src\System.Runtime.InteropServices.JavaScript.csproj" SkipUseReferenceAssembly="true"/>
124-
</ItemGroup>
125-
126122
<Import Project="$(BrowserProjectRoot)\build\WasmApp.InTree.targets" />
127123
</Project>

src/tests/FunctionalTests/WebAssembly/Directory.Build.props

-4
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@
1212
<ExpectedExitCode>42</ExpectedExitCode>
1313
</PropertyGroup>
1414

15-
<ItemGroup>
16-
<ProjectReference Include="$(LibrariesProjectRoot)System.Runtime.InteropServices.JavaScript\src\System.Runtime.InteropServices.JavaScript.csproj" SkipUseReferenceAssembly="true"/>
17-
</ItemGroup>
18-
1915
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props, '$(MSBuildThisFileDirectory)..'))" />
2016

2117
<ItemGroup Condition="'$(IsWasmTestProject)' != 'false'">

0 commit comments

Comments
 (0)