Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[browser][MT] fix nested synchronous JS interop call detection #99296

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ public static int SlowRuntimeTimeoutModifier
public static bool IsWasmThreadingSupported => IsBrowser && IsEnvironmentVariableTrue("IsBrowserThreadingSupported");
public static bool IsNotWasmThreadingSupported => !IsWasmThreadingSupported;
public static bool IsWasmBackgroundExec => IsBrowser && IsEnvironmentVariableTrue("IsWasmBackgroundExec");
public static bool IsThreadingSupportedNotBrowserBackgroundExec => IsWasmThreadingSupported && !IsWasmBackgroundExec;
public static bool IsWasmBackgroundExecOrSingleThread => IsWasmBackgroundExec || IsNotWasmThreadingSupported;
public static bool IsThreadingSupportedOrBrowserBackgroundExec => IsWasmBackgroundExec || !IsBrowser;
public static bool IsBinaryFormatterSupported => IsNotMobile && !IsNativeAot;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ internal static unsafe void InvokeJSFunction(JSObject jsFunction, Span<JSMarshal

#if FEATURE_WASM_MANAGED_THREADS
ref JSMarshalerArgument exc = ref arguments[0];
exc.slot.CallerNativeTID = jsFunction.ProxyContext.NativeTID;
exc.slot.CallerNativeTID = JSProxyContext.GetNativeThreadId();

// if we are on correct thread already, just call it
if (jsFunction.ProxyContext.IsCurrentThread())
Expand Down Expand Up @@ -297,7 +297,7 @@ internal static unsafe void InvokeJSImportImpl(JSFunctionBinding signature, Span
ref JSMarshalerArgument res = ref arguments[1];
#if FEATURE_WASM_MANAGED_THREADS
var targetContext = JSProxyContext.SealJSImportCapturing();
exc.slot.CallerNativeTID = targetContext.NativeTID;
exc.slot.CallerNativeTID = JSProxyContext.GetNativeThreadId();
exc.slot.ContextHandle = targetContext.ContextHandle;
res.slot.ContextHandle = targetContext.ContextHandle;
#else
Expand Down Expand Up @@ -464,7 +464,7 @@ internal static unsafe void ResolveOrRejectPromise(JSProxyContext targetContext,
{
ref JSMarshalerArgument exc = ref arguments[0];
#if FEATURE_WASM_MANAGED_THREADS
exc.slot.CallerNativeTID = targetContext.NativeTID;
exc.slot.CallerNativeTID = JSProxyContext.GetNativeThreadId();

if (targetContext.IsCurrentThread())
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ namespace System.Runtime.InteropServices.JavaScript.Tests
{
public class JSExportAsyncTest : JSInteropTestBase, IAsyncLifetime
{
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupportedNotBrowserBackgroundExec))]
public void SyncJsImportJsExportThrows()
{
var ex = Assert.Throws<JSException>(()=>JavaScriptTestHelper.invoke1_Boolean(true, nameof(JavaScriptTestHelper.EchoBoolean)));
Assert.Contains("Cannot call synchronous C# method", ex.Message);
}

[Theory]
[MemberData(nameof(MarshalBooleanCases))]
public async Task JsExportBooleanAsync(bool value)
Expand Down Expand Up @@ -150,6 +157,7 @@ public void JsExportIntPtr(IntPtr value)
[MemberData(nameof(MarshalIntPtrCases))]
public unsafe void JsExportVoidPtr(IntPtr xvalue)
{
JavaScriptTestHelper.AssertWasmBackgroundExec();
void* value = (void*)xvalue;
void* res = JavaScriptTestHelper.invoke1_VoidPtr(value, nameof(JavaScriptTestHelper.EchoVoidPtr));
Assert.True(value == res);
Expand Down Expand Up @@ -180,6 +188,7 @@ public void JsExportDateTimeOffset(DateTimeOffset value)
[MemberData(nameof(MarshalNullableBooleanCases))]
public void JsExportNullableBoolean(bool? value)
{
JavaScriptTestHelper.AssertWasmBackgroundExec();
JsExportTest(value,
JavaScriptTestHelper.invoke1_NullableBoolean,
nameof(JavaScriptTestHelper.EchoNullableBoolean),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ public unsafe void OutOfRange()
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsWasmBackgroundExecOrSingleThread))]
public unsafe void OptimizedPaths()
{
JavaScriptTestHelper.AssertWasmBackgroundExec();
JavaScriptTestHelper.optimizedReached = 0;
JavaScriptTestHelper.invoke0V();
Assert.Equal(1, JavaScriptTestHelper.optimizedReached);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1020,14 +1020,24 @@ public static JSObject EchoIJSObject([JSMarshalAs<JSType.Object>] JSObject arg1)
[JSImport("INTERNAL.forceDisposeProxies")]
internal static partial void ForceDisposeProxies(bool disposeMethods, bool verbose);

public static void AssertWasmBackgroundExec()
{
if (PlatformDetection.IsWasmBackgroundExec && Environment.CurrentManagedThreadId == 1)
{
throw new Exception("With WasmBackgroundExec we are expecting to run tests on the thread pool");
}
}

static JSObject _module;
public static async Task InitializeAsync()
{
AssertWasmBackgroundExec();
if (_module == null)
{
_module = await JSHost.ImportAsync("JavaScriptTestHelper", "../JavaScriptTestHelper.mjs"); ;
await Setup();
}
AssertWasmBackgroundExec();

#if FEATURE_WASM_MANAGED_THREADS
// are we in the UI thread ?
Expand All @@ -1037,6 +1047,7 @@ public static async Task InitializeAsync()
// this gives browser chance to serve UI thread event loop before every test
await Task.Yield();
}
AssertWasmBackgroundExec();
}

public static Task DisposeAsync()
Expand Down
2 changes: 1 addition & 1 deletion src/mono/browser/runtime/invoke-js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ function bind_js_import(signature: JSFunctionSignature): Function {
try {
forceThreadMemoryViewRefresh();
const caller_tid = get_caller_native_tid(args);
runtimeHelpers.isPendingSynchronousCall = runtimeHelpers.currentThreadTID === caller_tid;
runtimeHelpers.isPendingSynchronousCall = runtimeHelpers.managedThreadTID === caller_tid;
bound_fn(args);
}
finally {
Expand Down
Loading