From b87dfde1525a11cb250bd7e519ca8588cefc3be9 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 12 Feb 2024 15:26:24 +0100 Subject: [PATCH 1/9] - rename `JSProxyContext.NativeTID` to `JSNativeTID` - pass TID to InstallMainSynchronizationContext and GCHandle back - don't use synchronous resolve for Tasks pre-resolved on another thread - fix CTS leak in WaitAssertsOnJSInteropThreads - rename mono_wasm_invoke_method_bound to mono_wasm_invoke_method - drop mono_wasm_invoke_method_raw in favor of mono_wasm_invoke_method - load segmentation-rules and symbols into worker - normalized functions in managed-exports.ts - renamed runtimeHelpers.proxyGCHandle - handle exit_code === undefined - handle document undefined in appendElementOnExit (worker) - made loaderHelpers.invariantMode local variable - improved thread dump for detached threads and UI thread - mono_wasm_init_threads asynchronously and earlier and moved the code - moved more logic into start_runtime() - --- .../JavaScript/CancelablePromise.cs | 2 +- .../JavaScript/Interop/JavaScriptExports.cs | 8 +- .../JavaScript/JSFunctionBinding.cs | 8 +- .../JavaScript/JSProxyContext.cs | 9 +- .../JavaScript/JSSynchronizationContext.cs | 2 +- .../InteropServices/JavaScript/JSWebWorker.cs | 1 + .../Marshaling/JSMarshalerArgument.Task.cs | 19 +- .../JavaScript/WebWorkerTest.cs | 2 +- .../tests/debugger-test/debugger-main.js | 8 +- src/mono/browser/runtime/cwraps.ts | 8 +- src/mono/browser/runtime/dotnet.d.ts | 20 +- src/mono/browser/runtime/driver.c | 25 +- src/mono/browser/runtime/gc-handles.ts | 5 +- src/mono/browser/runtime/globals.ts | 1 + src/mono/browser/runtime/invoke-cs.ts | 16 +- src/mono/browser/runtime/invoke-js.ts | 2 +- src/mono/browser/runtime/lazyLoading.ts | 5 +- src/mono/browser/runtime/loader/assets.ts | 11 + src/mono/browser/runtime/loader/exit.ts | 12 +- src/mono/browser/runtime/loader/globals.ts | 1 - src/mono/browser/runtime/loader/icu.ts | 8 +- src/mono/browser/runtime/loader/run.ts | 10 + src/mono/browser/runtime/managed-exports.ts | 357 ++++++++++-------- src/mono/browser/runtime/marshal-to-cs.ts | 7 +- src/mono/browser/runtime/marshal-to-js.ts | 5 +- src/mono/browser/runtime/marshal.ts | 7 +- .../browser/runtime/pthreads/browser/index.ts | 19 +- .../browser/runtime/pthreads/shared/index.ts | 14 +- .../browser/runtime/pthreads/worker/index.ts | 4 + src/mono/browser/runtime/run.ts | 3 +- .../browser/runtime/satelliteAssemblies.ts | 5 +- src/mono/browser/runtime/startup.ts | 125 +++--- src/mono/browser/runtime/types/index.ts | 18 +- src/mono/browser/runtime/types/internal.ts | 34 +- 34 files changed, 411 insertions(+), 370 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CancelablePromise.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CancelablePromise.cs index f96e0f4f0cf0a2..fc6fe9ce0c6115 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CancelablePromise.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CancelablePromise.cs @@ -45,7 +45,7 @@ public static void CancelPromise(Task promise) // FIXME: race condition // we know that holder.GCHandle is still valid because we hold the ProxyContext lock // but the message may arrive to the target thread after it was resolved, making GCHandle invalid - Interop.Runtime.CancelPromisePost(holder.ProxyContext.NativeTID, holder.GCHandle); + Interop.Runtime.CancelPromisePost(holder.ProxyContext.JSNativeTID, holder.GCHandle); } } #endif diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs index 74a67b477648c9..ab0a6642b1820a 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs @@ -296,11 +296,11 @@ public static void GetManagedStackTrace(JSMarshalerArgument* arguments_buffer) // this is here temporarily, until JSWebWorker becomes public API [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicMethods, "System.Runtime.InteropServices.JavaScript.JSWebWorker", "System.Runtime.InteropServices.JavaScript")] - // the marshaled signature is: - // void InstallMainSynchronizationContext() - public static void InstallMainSynchronizationContext() + public static void InstallMainSynchronizationContext(nint* nativeTIDAndProxyContextGCHandlePtr) { - JSSynchronizationContext.InstallWebWorkerInterop(true, CancellationToken.None); + var jsSynchronizationContext = JSSynchronizationContext.InstallWebWorkerInterop(true, CancellationToken.None); + jsSynchronizationContext.ProxyContext.JSNativeTID = *nativeTIDAndProxyContextGCHandlePtr; + *nativeTIDAndProxyContextGCHandlePtr = jsSynchronizationContext.ProxyContext.ContextHandle; } #endif diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs index 5761fe0010bb00..acbd4a92ef9042 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSFunctionBinding.cs @@ -253,7 +253,7 @@ internal static unsafe void DispatchJSFunctionSync(JSObject jsFunction, Span true; #else public nint ContextHandle; - public nint NativeTID; + public nint JSNativeTID; // target thread where JavaScript is running public int ManagedTID; public bool IsMainThread; public JSSynchronizationContext SynchronizationContext; @@ -53,7 +56,7 @@ public static IntPtr GetNativeThreadId() public JSProxyContext(bool isMainThread, JSSynchronizationContext synchronizationContext) { SynchronizationContext = synchronizationContext; - NativeTID = GetNativeThreadId(); + JSNativeTID = GetNativeThreadId(); ManagedTID = Environment.CurrentManagedThreadId; IsMainThread = isMainThread; ContextHandle = (nint)GCHandle.Alloc(this, GCHandleType.Normal); @@ -467,7 +470,7 @@ public static void ReleaseCSOwnedObject(JSObject jso, bool skipJS) // this is async message, we need to call this as the last thing // the same jsHandle would not be re-used until JS side considers it free - Interop.Runtime.ReleaseCSOwnedObjectPost(ctx.NativeTID, jsHandle); + Interop.Runtime.ReleaseCSOwnedObjectPost(ctx.JSNativeTID, jsHandle); } #else Interop.Runtime.ReleaseCSOwnedObject(jsHandle); diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs index a1e6b6c93b093d..3953633580811f 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSSynchronizationContext.cs @@ -179,7 +179,7 @@ private unsafe void ScheduleJSPump() { // While we COULD pump here, we don't want to. We want the pump to happen on the next event loop turn. // Otherwise we could get a chain where a pump generates a new work item and that makes us pump again, forever. - TargetThreadScheduleBackgroundJob(ProxyContext.NativeTID, (void*)(delegate* unmanaged[Cdecl])&BackgroundJobHandler); + TargetThreadScheduleBackgroundJob(ProxyContext.JSNativeTID, (delegate* unmanaged[Cdecl])&BackgroundJobHandler); } public override void Post(SendOrPostCallback d, object? state) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSWebWorker.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSWebWorker.cs index 5721c67d5ea241..928339b8062061 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSWebWorker.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSWebWorker.cs @@ -66,6 +66,7 @@ public JSWebWorkerInstance(Func> body, CancellationToken cancellationTok // TODO TaskCreationOptions.HideScheduler ? _taskCompletionSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); _thread = new Thread(ThreadMain); + _thread.Name = "JSWebWorker"; _resultTask = null; _cancellationToken = cancellationToken; _cancellationRegistration = null; diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs index 4d8b10cfd452a9..cf58e5e10e0e64 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs @@ -228,13 +228,19 @@ static void MarshalResult(ref JSMarshalerArgument arg, object? taskResult) public void ToJS(Task? value) { Task? task = value; + var ctx = ToJSContext; + var isCurrentThread = ctx.IsCurrentThread(); if (task == null) { + if (!isCurrentThread) + { + Environment.FailFast("NULL not supported in MT"); + } slot.Type = MarshalerType.None; return; } - if (task.IsCompleted) + if (isCurrentThread && task.IsCompleted) { if (task.Exception != null) { @@ -252,8 +258,6 @@ public void ToJS(Task? value) } } - var ctx = ToJSContext; - if (slot.Type != MarshalerType.TaskPreCreated) { // this path should only happen when the Task is passed as argument of JSImport @@ -298,14 +302,20 @@ static void Complete(Task task, object? th) public void ToJS(Task? value, ArgumentToJSCallback marshaler) { Task? task = value; + var ctx = ToJSContext; + var isCurrentThread = ctx.IsCurrentThread(); if (task == null) { + if (!isCurrentThread) + { + Environment.FailFast("NULL not supported in MT"); + } slot.Type = MarshalerType.None; return; } - if (task.IsCompleted) + if (isCurrentThread && task.IsCompleted) { if (task.Exception != null) { @@ -325,7 +335,6 @@ public void ToJS(Task? value, ArgumentToJSCallback marshaler) } } - var ctx = ToJSContext; if (slot.Type != MarshalerType.TaskPreCreated) { // this path should only happen when the Task is passed as argument of JSImport diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTest.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTest.cs index c88d32277da8bb..514741a6b8753b 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTest.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/tests/System.Runtime.InteropServices.JavaScript.UnitTests/System/Runtime/InteropServices/JavaScript/WebWorkerTest.cs @@ -439,7 +439,7 @@ await executor.Execute(async () => [Theory, MemberData(nameof(GetTargetThreadsAndBlockingCalls))] public async Task WaitAssertsOnJSInteropThreads(Executor executor, NamedCall method) { - var cts = CreateTestCaseTimeoutSource(); + using var cts = CreateTestCaseTimeoutSource(); await executor.Execute(Task () => { Exception? exception = null; diff --git a/src/mono/browser/debugger/tests/debugger-test/debugger-main.js b/src/mono/browser/debugger/tests/debugger-test/debugger-main.js index 6849e490de36aa..58688dfc8a470e 100644 --- a/src/mono/browser/debugger/tests/debugger-test/debugger-main.js +++ b/src/mono/browser/debugger/tests/debugger-test/debugger-main.js @@ -36,18 +36,18 @@ try { } } - // this is fake implementation of legacy `bind_static_method` which uses `mono_wasm_invoke_method_raw` + // this is fake implementation of legacy `bind_static_method` which uses `mono_wasm_invoke_method` // We have unit tests that stop on unhandled managed exceptions. - // as opposed to [JSExport], the `mono_wasm_invoke_method_raw` doesn't handle managed exceptions. + // as opposed to [JSExport], the `mono_wasm_invoke_method` doesn't handle managed exceptions. // Same way as old `bind_static_method` didn't App.bind_static_method_native = (method_name) => { try { const monoMethodPtr = App.exports.DebuggerTests.BindStaticMethod.GetMonoMethodPtr(method_name); // this is only implemented for void methods with no arguments - const invoker = runtime.Module.cwrap("mono_wasm_invoke_method_raw", "number", ["number", "number"]); + const invoker = runtime.Module.cwrap("mono_wasm_invoke_method", "number", ["number", "number", "number"]); return function () { try { - return invoker(monoMethodPtr); + return invoker(monoMethodPtr, 0, 0); } catch (err) { console.error(err); diff --git a/src/mono/browser/runtime/cwraps.ts b/src/mono/browser/runtime/cwraps.ts index 29d384f65795b0..63ac26751dbd45 100644 --- a/src/mono/browser/runtime/cwraps.ts +++ b/src/mono/browser/runtime/cwraps.ts @@ -6,7 +6,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import type { MonoAssembly, MonoClass, MonoMethod, MonoObject, - MonoType, MonoObjectRef, MonoStringRef, JSMarshalerArguments + MonoType, MonoObjectRef, MonoStringRef } from "./types/internal"; import type { VoidPtr, CharPtrPtr, Int32Ptr, CharPtr, ManagedPointer } from "./types/emscripten"; import { Module, runtimeHelpers } from "./globals"; @@ -64,8 +64,7 @@ const fn_signatures: SigLine[] = [ [() => !runtimeHelpers.emscriptenBuildOptions.enableBrowserProfiler, "mono_wasm_profiler_init_aot", "void", ["string"]], [true, "mono_wasm_profiler_init_browser", "void", ["number"]], [false, "mono_wasm_exec_regression", "number", ["number", "string"]], - [false, "mono_wasm_invoke_method_bound", "number", ["number", "number", "number"]], - [false, "mono_wasm_invoke_method_raw", "number", ["number", "number"]], + [false, "mono_wasm_invoke_method", "number", ["number", "number", "number"]], [true, "mono_wasm_write_managed_pointer_unsafe", "void", ["number", "number"]], [true, "mono_wasm_copy_managed_pointer", "void", ["number", "number"]], [true, "mono_wasm_i52_to_f64", "number", ["number", "number"]], @@ -182,8 +181,7 @@ export interface t_Cwraps { mono_wasm_getenv(name: string): CharPtr; mono_wasm_set_main_args(argc: number, argv: VoidPtr): void; mono_wasm_exec_regression(verbose_level: number, image: string): number; - mono_wasm_invoke_method_bound(method: MonoMethod, args: JSMarshalerArguments, fail: MonoStringRef): number; - mono_wasm_invoke_method_raw(method: MonoMethod, fail: MonoStringRef): number; + mono_wasm_invoke_method(method: MonoMethod, args: VoidPtr, fail: MonoStringRef): number; mono_wasm_write_managed_pointer_unsafe(destination: VoidPtr | MonoObjectRef, pointer: ManagedPointer): void; mono_wasm_copy_managed_pointer(destination: VoidPtr | MonoObjectRef, source: VoidPtr | MonoObjectRef): void; mono_wasm_i52_to_f64(source: VoidPtr, error: Int32Ptr): number; diff --git a/src/mono/browser/runtime/dotnet.d.ts b/src/mono/browser/runtime/dotnet.d.ts index 23f55cacc455ba..e60265f04a2813 100644 --- a/src/mono/browser/runtime/dotnet.d.ts +++ b/src/mono/browser/runtime/dotnet.d.ts @@ -352,7 +352,15 @@ type SingleAssetBehaviors = /** * Typically blazor.boot.json */ - | "manifest"; + | "manifest" +/** + * The debugging symbols + */ + | "symbols" +/** + * Load segmentation rules file for Hybrid Globalization. + */ + | "segmentation-rules"; type AssetBehaviors = SingleAssetBehaviors | /** * Load asset as a managed resource assembly. @@ -381,15 +389,7 @@ type AssetBehaviors = SingleAssetBehaviors | /** * The javascript module that came from nuget package . */ - | "js-module-library-initializer" -/** - * The javascript module for threads. - */ - | "symbols" -/** - * Load segmentation rules file for Hybrid Globalization. - */ - | "segmentation-rules"; + | "js-module-library-initializer"; declare const enum GlobalizationMode { /** * Load sharded ICU data. diff --git a/src/mono/browser/runtime/driver.c b/src/mono/browser/runtime/driver.c index a7bd6f5966e0b0..5de7d985cb22f7 100644 --- a/src/mono/browser/runtime/driver.c +++ b/src/mono/browser/runtime/driver.c @@ -228,7 +228,7 @@ mono_wasm_load_runtime (const char *unused, int debug_level) } EMSCRIPTEN_KEEPALIVE int -mono_wasm_invoke_method_bound (MonoMethod *method, void* args /*JSMarshalerArguments*/, MonoString **out_exc) +mono_wasm_invoke_method (MonoMethod *method, void* args, MonoString **out_exc) { PVOLATILE(MonoObject) temp_exc = NULL; @@ -236,7 +236,7 @@ mono_wasm_invoke_method_bound (MonoMethod *method, void* args /*JSMarshalerArgum int is_err = 0; MONO_ENTER_GC_UNSAFE; - mono_runtime_invoke (method, NULL, invoke_args, (MonoObject **)&temp_exc); + mono_runtime_invoke (method, NULL, args ? invoke_args : NULL, (MonoObject **)&temp_exc); // this failure is unlikely because it would be runtime error, not application exception. // the application exception is passed inside JSMarshalerArguments `args` @@ -251,27 +251,6 @@ mono_wasm_invoke_method_bound (MonoMethod *method, void* args /*JSMarshalerArgum return is_err; } -EMSCRIPTEN_KEEPALIVE int -mono_wasm_invoke_method_raw (MonoMethod *method, MonoString **out_exc) -{ - PVOLATILE(MonoObject) temp_exc = NULL; - - int is_err = 0; - - MONO_ENTER_GC_UNSAFE; - mono_runtime_invoke (method, NULL, NULL, (MonoObject **)&temp_exc); - - if (temp_exc && out_exc) { - PVOLATILE(MonoObject) exc2 = NULL; - store_volatile((MonoObject**)out_exc, (MonoObject*)mono_object_to_string ((MonoObject*)temp_exc, (MonoObject **)&exc2)); - if (exc2) - store_volatile((MonoObject**)out_exc, (MonoObject*)mono_string_new (root_domain, "Exception Double Fault")); - is_err = 1; - } - MONO_EXIT_GC_UNSAFE; - return is_err; -} - EMSCRIPTEN_KEEPALIVE MonoMethod* mono_wasm_assembly_get_entry_point (MonoAssembly *assembly, int auto_insert_breakpoint) { diff --git a/src/mono/browser/runtime/gc-handles.ts b/src/mono/browser/runtime/gc-handles.ts index c192891e514236..5fe3a27a50fe1a 100644 --- a/src/mono/browser/runtime/gc-handles.ts +++ b/src/mono/browser/runtime/gc-handles.ts @@ -4,13 +4,14 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import BuildConfiguration from "consts:configuration"; -import { loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; +import { loaderHelpers, mono_assert } from "./globals"; import { assert_js_interop, js_import_wrapper_by_fn_handle } from "./invoke-js"; import { mono_log_info, mono_log_warn } from "./logging"; import { bound_cs_function_symbol, imported_js_function_symbol, proxy_debug_symbol } from "./marshal"; import { GCHandle, GCHandleNull, JSHandle, WeakRefInternal } from "./types/internal"; import { _use_weak_ref, create_weak_ref } from "./weak-ref"; import { exportsByAssembly } from "./invoke-cs"; +import { release_js_owned_object_by_gc_handle } from "./managed-exports"; const _use_finalization_registry = typeof globalThis.FinalizationRegistry === "function"; let _js_owned_object_registry: FinalizationRegistry; @@ -152,7 +153,7 @@ export function teardown_managed_proxy(owner: any, gc_handle: GCHandle, skipMana } if (gc_handle !== GCHandleNull && _js_owned_object_table.delete(gc_handle) && !skipManaged) { if (loaderHelpers.is_runtime_running()) { - runtimeHelpers.javaScriptExports.release_js_owned_object_by_gc_handle(gc_handle); + release_js_owned_object_by_gc_handle(gc_handle); } } if (is_gcv_handle(gc_handle)) { diff --git a/src/mono/browser/runtime/globals.ts b/src/mono/browser/runtime/globals.ts index be2ae8f86bee95..f619d22bb07a68 100644 --- a/src/mono/browser/runtime/globals.ts +++ b/src/mono/browser/runtime/globals.ts @@ -64,6 +64,7 @@ export function setRuntimeGlobals(globalObjects: GlobalObjects) { afterPreInit: createPromiseController(), afterPreRun: createPromiseController(), beforeOnRuntimeInitialized: createPromiseController(), + afterMonoStarted: createPromiseController(), afterOnRuntimeInitialized: createPromiseController(), afterPostRun: createPromiseController(), mono_wasm_exit: () => { diff --git a/src/mono/browser/runtime/invoke-cs.ts b/src/mono/browser/runtime/invoke-cs.ts index fd0af4ca25dc42..6c7c653b449f68 100644 --- a/src/mono/browser/runtime/invoke-cs.ts +++ b/src/mono/browser/runtime/invoke-cs.ts @@ -16,7 +16,7 @@ import { monoStringToString } from "./strings"; import { MonoObjectRef, MonoStringRef, MonoString, MonoObject, MonoMethod, JSMarshalerArguments, JSFunctionSignature, BoundMarshalerToCs, BoundMarshalerToJs, VoidPtrNull, MonoObjectRefNull, MonoObjectNull, MarshalerType, MonoAssembly } from "./types/internal"; import { Int32Ptr } from "./types/emscripten"; import cwraps from "./cwraps"; -import { assert_c_interop, assert_js_interop, wrap_error_root, wrap_no_error_root } from "./invoke-js"; +import { assert_js_interop, wrap_error_root, wrap_no_error_root } from "./invoke-js"; import { startMeasure, MeasuredBlock, endMeasure } from "./profiler"; import { mono_log_debug } from "./logging"; @@ -353,7 +353,7 @@ export function invoke_method_and_handle_exception(method: MonoMethod, args: JSM const fail_root = mono_wasm_new_root(); try { set_args_context(args); - const fail = cwraps.mono_wasm_invoke_method_bound(method, args, fail_root.address); + const fail = cwraps.mono_wasm_invoke_method(method, args as any, fail_root.address); if (fail) runtimeHelpers.nativeAbort("ERR24: Unexpected error: " + monoStringToString(fail_root)); if (is_args_exception(args)) { const exc = get_arg(args, 0); @@ -365,18 +365,6 @@ export function invoke_method_and_handle_exception(method: MonoMethod, args: JSM } } -export function invoke_method_raw(method: MonoMethod): void { - assert_c_interop(); - const fail_root = mono_wasm_new_root(); - try { - const fail = cwraps.mono_wasm_invoke_method_raw(method, fail_root.address); - if (fail) runtimeHelpers.nativeAbort("ERR24: Unexpected error: " + monoStringToString(fail_root)); - } - finally { - fail_root.release(); - } -} - export const exportsByAssembly: Map = new Map(); function _walk_exports_to_set_function(assembly: string, namespace: string, classname: string, methodname: string, signature_hash: number, fn: Function): void { const parts = `${namespace}.${classname}`.replace(/\//g, ".").split("."); diff --git a/src/mono/browser/runtime/invoke-js.ts b/src/mono/browser/runtime/invoke-js.ts index 79f676ee08b437..ccd7e294a3f883 100644 --- a/src/mono/browser/runtime/invoke-js.ts +++ b/src/mono/browser/runtime/invoke-js.ts @@ -462,7 +462,7 @@ export function wrap_no_error_root(is_exception: Int32Ptr | null, result?: WasmR export function assert_js_interop(): void { loaderHelpers.assert_runtime_running(); if (WasmEnableThreads) { - mono_assert(runtimeHelpers.mono_wasm_bindings_is_ready && runtimeHelpers.proxy_context_gc_handle, "Please use dedicated worker for working with JavaScript interop. See https://github.com/dotnet/runtime/blob/main/src/mono/wasm/threads.md#JS-interop-on-dedicated-threads"); + mono_assert(runtimeHelpers.mono_wasm_bindings_is_ready && runtimeHelpers.proxyGCHandle, "Please use dedicated worker for working with JavaScript interop. See https://github.com/dotnet/runtime/blob/main/src/mono/wasm/threads.md#JS-interop-on-dedicated-threads"); } else { mono_assert(runtimeHelpers.mono_wasm_bindings_is_ready, "The runtime must be initialized."); } diff --git a/src/mono/browser/runtime/lazyLoading.ts b/src/mono/browser/runtime/lazyLoading.ts index 55bcfd67101e8c..c95cdc6309e438 100644 --- a/src/mono/browser/runtime/lazyLoading.ts +++ b/src/mono/browser/runtime/lazyLoading.ts @@ -1,7 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import { loaderHelpers, runtimeHelpers } from "./globals"; +import { loaderHelpers } from "./globals"; +import { load_lazy_assembly } from "./managed-exports"; import { AssetEntry } from "./types"; export async function loadLazyAssembly(assemblyNameToLoad: string): Promise { @@ -51,7 +52,7 @@ export async function loadLazyAssembly(assemblyNameToLoad: string): Promise = { mono_wasm_bindings_is_ready: false, - javaScriptExports: {} as any, config: globalObjects.module.config, diagnosticTracing: false, nativeAbort: (reason: any) => { throw reason; }, diff --git a/src/mono/browser/runtime/loader/icu.ts b/src/mono/browser/runtime/loader/icu.ts index 46a75726702e93..2efc00f398eda5 100644 --- a/src/mono/browser/runtime/loader/icu.ts +++ b/src/mono/browser/runtime/loader/icu.ts @@ -7,14 +7,14 @@ import { mono_log_info, mono_log_debug } from "./logging"; export function init_globalization() { loaderHelpers.preferredIcuAsset = getIcuResourceName(loaderHelpers.config); - loaderHelpers.invariantMode = loaderHelpers.config.globalizationMode == GlobalizationMode.Invariant; + let invariantMode = loaderHelpers.config.globalizationMode == GlobalizationMode.Invariant; - if (!loaderHelpers.invariantMode) { + if (!invariantMode) { if (loaderHelpers.preferredIcuAsset) { mono_log_debug("ICU data archive(s) available, disabling invariant mode"); } else if (loaderHelpers.config.globalizationMode !== GlobalizationMode.Custom && loaderHelpers.config.globalizationMode !== GlobalizationMode.All && loaderHelpers.config.globalizationMode !== GlobalizationMode.Sharded) { mono_log_debug("ICU data archive(s) not available, using invariant globalization mode"); - loaderHelpers.invariantMode = true; + invariantMode = true; loaderHelpers.preferredIcuAsset = null; } else { const msg = "invariant globalization mode is inactive and no ICU data archives are available"; @@ -29,7 +29,7 @@ export function init_globalization() { if (env_variables[hybridEnv] === undefined && loaderHelpers.config.globalizationMode === GlobalizationMode.Hybrid) { env_variables[hybridEnv] = "1"; } - else if (env_variables[invariantEnv] === undefined && loaderHelpers.invariantMode) { + else if (env_variables[invariantEnv] === undefined && invariantMode) { env_variables[invariantEnv] = "1"; } if (env_variables["TZ"] === undefined) { diff --git a/src/mono/browser/runtime/loader/run.ts b/src/mono/browser/runtime/loader/run.ts index 9a19aa6c5217fb..1f85b4e2412002 100644 --- a/src/mono/browser/runtime/loader/run.ts +++ b/src/mono/browser/runtime/loader/run.ts @@ -513,6 +513,16 @@ async function createEmscriptenWorker(): Promise { prepareAssetsWorker(); + setTimeout(async () => { + try { + // load subset which is on JS heap rather than in WASM linear memory + await mono_download_assets(); + } + catch (err) { + mono_exit(1, err); + } + }, 0); + const promises = importModules(); const es6Modules = await Promise.all(promises); await initializeModules(es6Modules as any); diff --git a/src/mono/browser/runtime/managed-exports.ts b/src/mono/browser/runtime/managed-exports.ts index 9ff79b65c210e4..e39c45ae9eaccf 100644 --- a/src/mono/browser/runtime/managed-exports.ts +++ b/src/mono/browser/runtime/managed-exports.ts @@ -3,14 +3,19 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; -import { GCHandle, MarshalerToCs, MarshalerToJs, MarshalerType, MonoMethod } from "./types/internal"; +import { GCHandle, GCHandleNull, MarshalerToCs, MarshalerToJs, MarshalerType, MonoMethod } from "./types/internal"; import cwraps from "./cwraps"; import { runtimeHelpers, Module, loaderHelpers, mono_assert } from "./globals"; import { alloc_stack_frame, get_arg, set_arg_type, set_gc_handle } from "./marshal"; -import { invoke_method_and_handle_exception, invoke_method_raw } from "./invoke-cs"; +import { invoke_method_and_handle_exception } from "./invoke-cs"; import { marshal_array_to_cs, marshal_array_to_cs_impl, marshal_exception_to_cs, marshal_intptr_to_cs } from "./marshal-to-cs"; import { marshal_int32_to_js, end_marshal_task_to_js, marshal_string_to_js, begin_marshal_task_to_js } from "./marshal-to-js"; import { do_not_force_dispose } from "./gc-handles"; +import { assert_c_interop } from "./invoke-js"; +import { getI32, setI32 } from "./memory"; +import { mono_wasm_main_thread_ptr } from "./pthreads/shared"; + +const managedExports: ManagedExports = {} as any; export function init_managed_exports(): void { const exports_fqn_asm = "System.Runtime.InteropServices.JavaScript"; @@ -24,183 +29,215 @@ export function init_managed_exports(): void { if (!runtimeHelpers.runtime_interop_exports_class) throw "Can't find " + runtimeHelpers.runtime_interop_namespace + "." + runtimeHelpers.runtime_interop_exports_classname + " class"; - const install_main_synchronization_context = WasmEnableThreads ? get_method("InstallMainSynchronizationContext") : undefined; - mono_assert(!WasmEnableThreads || install_main_synchronization_context, "Can't find InstallMainSynchronizationContext method"); - const call_entry_point = get_method("CallEntrypoint"); - mono_assert(call_entry_point, "Can't find CallEntrypoint method"); - const release_js_owned_object_by_gc_handle_method = get_method("ReleaseJSOwnedObjectByGCHandle"); - mono_assert(release_js_owned_object_by_gc_handle_method, "Can't find ReleaseJSOwnedObjectByGCHandle method"); - const complete_task_method = get_method("CompleteTask"); - mono_assert(complete_task_method, "Can't find CompleteTask method"); - const call_delegate_method = get_method("CallDelegate"); - mono_assert(call_delegate_method, "Can't find CallDelegate method"); - const get_managed_stack_trace_method = get_method("GetManagedStackTrace"); - mono_assert(get_managed_stack_trace_method, "Can't find GetManagedStackTrace method"); - const load_satellite_assembly_method = get_method("LoadSatelliteAssembly"); - mono_assert(load_satellite_assembly_method, "Can't find LoadSatelliteAssembly method"); - const load_lazy_assembly_method = get_method("LoadLazyAssembly"); - mono_assert(load_lazy_assembly_method, "Can't find LoadLazyAssembly method"); - - runtimeHelpers.javaScriptExports.call_entry_point = async (entry_point: MonoMethod, program_args?: string[]): Promise => { - loaderHelpers.assert_runtime_running(); - const sp = Module.stackSave(); - try { - Module.runtimeKeepalivePush(); - const args = alloc_stack_frame(4); - const res = get_arg(args, 1); - const arg1 = get_arg(args, 2); - const arg2 = get_arg(args, 3); - marshal_intptr_to_cs(arg1, entry_point); - if (program_args && program_args.length == 0) { - program_args = undefined; - } - marshal_array_to_cs_impl(arg2, program_args, MarshalerType.String); + managedExports.InstallMainSynchronizationContext = WasmEnableThreads ? get_method("InstallMainSynchronizationContext") : undefined; + managedExports.CallEntrypoint = get_method("CallEntrypoint"); + managedExports.ReleaseJSOwnedObjectByGCHandle = get_method("ReleaseJSOwnedObjectByGCHandle"); + managedExports.CompleteTask = get_method("CompleteTask"); + managedExports.CallDelegate = get_method("CallDelegate"); + managedExports.GetManagedStackTrace = get_method("GetManagedStackTrace"); + managedExports.LoadSatelliteAssembly = get_method("LoadSatelliteAssembly"); + managedExports.LoadLazyAssembly = get_method("LoadLazyAssembly"); +} - // because this is async, we could pre-allocate the promise - let promise = begin_marshal_task_to_js(res, MarshalerType.TaskPreCreated, marshal_int32_to_js); +// the marshaled signature is: Task? CallEntrypoint(MonoMethod* entrypointPtr, string[] args) +export async function call_entry_point(entry_point: MonoMethod, program_args?: string[]): Promise { + loaderHelpers.assert_runtime_running(); + const sp = Module.stackSave(); + try { + Module.runtimeKeepalivePush(); + const args = alloc_stack_frame(4); + const res = get_arg(args, 1); + const arg1 = get_arg(args, 2); + const arg2 = get_arg(args, 3); + marshal_intptr_to_cs(arg1, entry_point); + if (program_args && program_args.length == 0) { + program_args = undefined; + } + marshal_array_to_cs_impl(arg2, program_args, MarshalerType.String); - // NOTE: at the moment this is synchronous call on the same thread and therefore we could marshal (null) result synchronously - invoke_method_and_handle_exception(call_entry_point, args); + // because this is async, we could pre-allocate the promise + let promise = begin_marshal_task_to_js(res, MarshalerType.TaskPreCreated, marshal_int32_to_js); - // in case the C# side returned synchronously - promise = end_marshal_task_to_js(args, marshal_int32_to_js, promise); + // NOTE: at the moment this is synchronous call on the same thread and therefore we could marshal (null) result synchronously + invoke_method_and_handle_exception(managedExports.CallEntrypoint, args); - if (promise === null || promise === undefined) { - promise = Promise.resolve(0); - } - (promise as any)[do_not_force_dispose] = true; // prevent disposing the task in forceDisposeProxies() - return await promise; - } finally { - Module.runtimeKeepalivePop();// after await promise ! - Module.stackRestore(sp); + // in case the C# side returned synchronously + promise = end_marshal_task_to_js(args, marshal_int32_to_js, promise); + + if (promise === null || promise === undefined) { + promise = Promise.resolve(0); } - }; - runtimeHelpers.javaScriptExports.load_satellite_assembly = (dll: Uint8Array): void => { - const sp = Module.stackSave(); - try { - const args = alloc_stack_frame(3); - const arg1 = get_arg(args, 2); - set_arg_type(arg1, MarshalerType.Array); - marshal_array_to_cs(arg1, dll, MarshalerType.Byte); - invoke_method_and_handle_exception(load_satellite_assembly_method, args); - } finally { - Module.stackRestore(sp); + (promise as any)[do_not_force_dispose] = true; // prevent disposing the task in forceDisposeProxies() + return await promise; + } finally { + Module.runtimeKeepalivePop();// after await promise ! + Module.stackRestore(sp); + } +} + +// the marshaled signature is: void LoadSatelliteAssembly(byte[] dll) +export function load_satellite_assembly(dll: Uint8Array): void { + const sp = Module.stackSave(); + try { + const args = alloc_stack_frame(3); + const arg1 = get_arg(args, 2); + set_arg_type(arg1, MarshalerType.Array); + marshal_array_to_cs(arg1, dll, MarshalerType.Byte); + invoke_method_and_handle_exception(managedExports.LoadSatelliteAssembly, args); + } finally { + Module.stackRestore(sp); + } +} + +// the marshaled signature is: void LoadLazyAssembly(byte[] dll, byte[] pdb) +export function load_lazy_assembly(dll: Uint8Array, pdb: Uint8Array | null): void { + const sp = Module.stackSave(); + try { + const args = alloc_stack_frame(4); + const arg1 = get_arg(args, 2); + const arg2 = get_arg(args, 3); + set_arg_type(arg1, MarshalerType.Array); + set_arg_type(arg2, MarshalerType.Array); + marshal_array_to_cs(arg1, dll, MarshalerType.Byte); + marshal_array_to_cs(arg2, pdb, MarshalerType.Byte); + invoke_method_and_handle_exception(managedExports.LoadLazyAssembly, args); + } finally { + Module.stackRestore(sp); + } +} + +// the marshaled signature is: void ReleaseJSOwnedObjectByGCHandle(GCHandle gcHandle) +export function release_js_owned_object_by_gc_handle(gc_handle: GCHandle) { + mono_assert(gc_handle, "Must be valid gc_handle"); + loaderHelpers.assert_runtime_running(); + const sp = Module.stackSave(); + try { + const args = alloc_stack_frame(3); + const arg1 = get_arg(args, 2); + set_arg_type(arg1, MarshalerType.Object); + set_gc_handle(arg1, gc_handle); + invoke_method_and_handle_exception(managedExports.ReleaseJSOwnedObjectByGCHandle, args); + } finally { + Module.stackRestore(sp); + } +} + +// the marshaled signature is: void CompleteTask(GCHandle holder, Exception? exceptionResult, T? result) +export function complete_task(holder_gc_handle: GCHandle, isCanceling: boolean, error?: any, data?: any, res_converter?: MarshalerToCs) { + loaderHelpers.assert_runtime_running(); + const sp = Module.stackSave(); + try { + const args = alloc_stack_frame(5); + const res = get_arg(args, 1); + const arg1 = get_arg(args, 2); + set_arg_type(arg1, MarshalerType.Object); + set_gc_handle(arg1, holder_gc_handle); + const arg2 = get_arg(args, 3); + if (error) { + marshal_exception_to_cs(arg2, error); + if (isCanceling) { + set_arg_type(res, MarshalerType.Discard); + } + } else { + set_arg_type(arg2, MarshalerType.None); + const arg3 = get_arg(args, 4); + mono_assert(res_converter, "res_converter missing"); + res_converter(arg3, data); } - }; - runtimeHelpers.javaScriptExports.load_lazy_assembly = (dll: Uint8Array, pdb: Uint8Array | null): void => { - const sp = Module.stackSave(); - try { - const args = alloc_stack_frame(4); - const arg1 = get_arg(args, 2); + invoke_method_and_handle_exception(managedExports.CompleteTask, args); + } finally { + Module.stackRestore(sp); + } +} + +// the marshaled signature is: TRes? CallDelegate(GCHandle callback, T1? arg1, T2? arg2, T3? arg3) +export function call_delegate(callback_gc_handle: GCHandle, arg1_js: any, arg2_js: any, arg3_js: any, res_converter?: MarshalerToJs, arg1_converter?: MarshalerToCs, arg2_converter?: MarshalerToCs, arg3_converter?: MarshalerToCs) { + loaderHelpers.assert_runtime_running(); + const sp = Module.stackSave(); + try { + const args = alloc_stack_frame(6); + + const arg1 = get_arg(args, 2); + set_arg_type(arg1, MarshalerType.Object); + set_gc_handle(arg1, callback_gc_handle); + // payload arg numbers are shifted by one, the real first is a gc handle of the callback + + if (arg1_converter) { const arg2 = get_arg(args, 3); - set_arg_type(arg1, MarshalerType.Array); - set_arg_type(arg2, MarshalerType.Array); - marshal_array_to_cs(arg1, dll, MarshalerType.Byte); - marshal_array_to_cs(arg2, pdb, MarshalerType.Byte); - invoke_method_and_handle_exception(load_lazy_assembly_method, args); - } finally { - Module.stackRestore(sp); + arg1_converter(arg2, arg1_js); } - }; - runtimeHelpers.javaScriptExports.release_js_owned_object_by_gc_handle = (gc_handle: GCHandle) => { - mono_assert(gc_handle, "Must be valid gc_handle"); - loaderHelpers.assert_runtime_running(); - const sp = Module.stackSave(); - try { - const args = alloc_stack_frame(3); - const arg1 = get_arg(args, 2); - set_arg_type(arg1, MarshalerType.Object); - set_gc_handle(arg1, gc_handle); - invoke_method_and_handle_exception(release_js_owned_object_by_gc_handle_method, args); - } finally { - Module.stackRestore(sp); + if (arg2_converter) { + const arg3 = get_arg(args, 4); + arg2_converter(arg3, arg2_js); } - }; - runtimeHelpers.javaScriptExports.complete_task = (holder_gc_handle: GCHandle, isCanceling: boolean, error?: any, data?: any, res_converter?: MarshalerToCs) => { - loaderHelpers.assert_runtime_running(); - const sp = Module.stackSave(); - try { - const args = alloc_stack_frame(5); - const res = get_arg(args, 1); - const arg1 = get_arg(args, 2); - set_arg_type(arg1, MarshalerType.Object); - set_gc_handle(arg1, holder_gc_handle); - const arg2 = get_arg(args, 3); - if (error) { - marshal_exception_to_cs(arg2, error); - if (isCanceling) { - set_arg_type(res, MarshalerType.Discard); - } - } else { - set_arg_type(arg2, MarshalerType.None); - const arg3 = get_arg(args, 4); - mono_assert(res_converter, "res_converter missing"); - res_converter(arg3, data); - } - invoke_method_and_handle_exception(complete_task_method, args); - } finally { - Module.stackRestore(sp); + if (arg3_converter) { + const arg4 = get_arg(args, 5); + arg3_converter(arg4, arg3_js); } - }; - runtimeHelpers.javaScriptExports.call_delegate = (callback_gc_handle: GCHandle, arg1_js: any, arg2_js: any, arg3_js: any, res_converter?: MarshalerToJs, arg1_converter?: MarshalerToCs, arg2_converter?: MarshalerToCs, arg3_converter?: MarshalerToCs) => { - loaderHelpers.assert_runtime_running(); - const sp = Module.stackSave(); - try { - const args = alloc_stack_frame(6); - - const arg1 = get_arg(args, 2); - set_arg_type(arg1, MarshalerType.Object); - set_gc_handle(arg1, callback_gc_handle); - // payload arg numbers are shifted by one, the real first is a gc handle of the callback - - if (arg1_converter) { - const arg2 = get_arg(args, 3); - arg1_converter(arg2, arg1_js); - } - if (arg2_converter) { - const arg3 = get_arg(args, 4); - arg2_converter(arg3, arg2_js); - } - if (arg3_converter) { - const arg4 = get_arg(args, 5); - arg3_converter(arg4, arg3_js); - } - invoke_method_and_handle_exception(call_delegate_method, args); + invoke_method_and_handle_exception(managedExports.CallDelegate, args); - if (res_converter) { - const res = get_arg(args, 1); - return res_converter(res); - } - } finally { - Module.stackRestore(sp); - } - }; - runtimeHelpers.javaScriptExports.get_managed_stack_trace = (exception_gc_handle: GCHandle) => { - loaderHelpers.assert_runtime_running(); - const sp = Module.stackSave(); - try { - const args = alloc_stack_frame(3); - - const arg1 = get_arg(args, 2); - set_arg_type(arg1, MarshalerType.Exception); - set_gc_handle(arg1, exception_gc_handle); - - invoke_method_and_handle_exception(get_managed_stack_trace_method, args); + if (res_converter) { const res = get_arg(args, 1); - return marshal_string_to_js(res); - } finally { - Module.stackRestore(sp); + return res_converter(res); } - }; - if (WasmEnableThreads && install_main_synchronization_context) { - runtimeHelpers.javaScriptExports.install_main_synchronization_context = () => invoke_method_raw(install_main_synchronization_context); + } finally { + Module.stackRestore(sp); + } +} + +// the marshaled signature is: string GetManagedStackTrace(GCHandle exception) +export function get_managed_stack_trace(exception_gc_handle: GCHandle) { + loaderHelpers.assert_runtime_running(); + const sp = Module.stackSave(); + try { + const args = alloc_stack_frame(3); + + const arg1 = get_arg(args, 2); + set_arg_type(arg1, MarshalerType.Exception); + set_gc_handle(arg1, exception_gc_handle); + + invoke_method_and_handle_exception(managedExports.GetManagedStackTrace, args); + const res = get_arg(args, 1); + return marshal_string_to_js(res); + } finally { + Module.stackRestore(sp); } } -export function get_method(method_name: string): MonoMethod { +// InstallMainSynchronizationContext(nint* nativeTIDAndProxyContextGCHandlePtr) +export function install_main_synchronization_context(): GCHandle { + if (!WasmEnableThreads) return GCHandleNull; + assert_c_interop(); + + const sp = Module.stackSave(); + try { + // tid in, gc_handle out + const args = Module.stackAlloc(4); + setI32(args, mono_wasm_main_thread_ptr()); + const fail = cwraps.mono_wasm_invoke_method(managedExports.InstallMainSynchronizationContext!, args, 0 as any); + if (fail) runtimeHelpers.nativeAbort("ERR24: Unexpected error"); + return getI32(args) as any; + } finally { + Module.stackRestore(sp); + } +} + +function get_method(method_name: string): MonoMethod { const res = cwraps.mono_wasm_assembly_find_method(runtimeHelpers.runtime_interop_exports_class, method_name, -1); if (!res) throw "Can't find method " + runtimeHelpers.runtime_interop_namespace + "." + runtimeHelpers.runtime_interop_exports_classname + "." + method_name; return res; } + +type ManagedExports = { + InstallMainSynchronizationContext: MonoMethod | undefined, + entry_point: MonoMethod, + CallEntrypoint: MonoMethod, + ReleaseJSOwnedObjectByGCHandle: MonoMethod, + CompleteTask: MonoMethod, + CallDelegate: MonoMethod, + GetManagedStackTrace: MonoMethod, + LoadSatelliteAssembly: MonoMethod, + LoadLazyAssembly: MonoMethod, +} diff --git a/src/mono/browser/runtime/marshal-to-cs.ts b/src/mono/browser/runtime/marshal-to-cs.ts index e1baec112ab388..63657a122195e7 100644 --- a/src/mono/browser/runtime/marshal-to-cs.ts +++ b/src/mono/browser/runtime/marshal-to-cs.ts @@ -8,7 +8,7 @@ import WasmEnableJsInteropByValue from "consts:wasmEnableJsInteropByValue"; import { isThenable } from "./cancelable-promise"; import cwraps from "./cwraps"; import { alloc_gcv_handle, assert_not_disposed, cs_owned_js_handle_symbol, js_owned_gc_handle_symbol, mono_wasm_get_js_handle, setup_managed_proxy, teardown_managed_proxy } from "./gc-handles"; -import { Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; +import { Module, loaderHelpers, mono_assert } from "./globals"; import { ManagedError, set_gc_handle, set_js_handle, set_arg_type, set_arg_i32, set_arg_f64, set_arg_i52, set_arg_f32, set_arg_i16, set_arg_u8, set_arg_b8, set_arg_date, @@ -24,6 +24,7 @@ import { JSMarshalerArgument, JSMarshalerArguments, JSMarshalerType, MarshalerTo import { TypedArray } from "./types/emscripten"; import { addUnsettledPromise, settleUnsettledPromise } from "./pthreads/shared/eventloop"; import { mono_log_debug } from "./logging"; +import { complete_task } from "./managed-exports"; export const jsinteropDoc = "For more information see https://aka.ms/dotnet-wasm-jsinterop"; @@ -358,7 +359,7 @@ function _marshal_task_to_cs(arg: JSMarshalerArgument, value: Promise, _?: teardown_managed_proxy(holder, gc_handle, /*skipManaged: */ true); // order of operations with teardown_managed_proxy matters // so that managed user code running in the continuation could allocate the same GCHandle number and the local registry would be already ok with that - runtimeHelpers.javaScriptExports.complete_task(gc_handle, false, null, data, res_converter || _marshal_cs_object_to_cs); + complete_task(gc_handle, false, null, data, res_converter || _marshal_cs_object_to_cs); } catch (ex) { try { @@ -385,7 +386,7 @@ function _marshal_task_to_cs(arg: JSMarshalerArgument, value: Promise, _?: // we can unregister the GC handle just on JS side teardown_managed_proxy(holder, gc_handle, /*skipManaged: */ true); // order of operations with teardown_managed_proxy matters - runtimeHelpers.javaScriptExports.complete_task(gc_handle, holder.isCanceled, reason, null, undefined); + complete_task(gc_handle, holder.isCanceled, reason, null, undefined); } catch (ex) { try { diff --git a/src/mono/browser/runtime/marshal-to-js.ts b/src/mono/browser/runtime/marshal-to-js.ts index b1eca560532e9f..e40c83899bc07f 100644 --- a/src/mono/browser/runtime/marshal-to-js.ts +++ b/src/mono/browser/runtime/marshal-to-js.ts @@ -7,7 +7,7 @@ import WasmEnableJsInteropByValue from "consts:wasmEnableJsInteropByValue"; import cwraps from "./cwraps"; import { _lookup_js_owned_object, mono_wasm_get_js_handle, mono_wasm_get_jsobj_from_js_handle, mono_wasm_release_cs_owned_object, register_with_jsv_handle, setup_managed_proxy, teardown_managed_proxy } from "./gc-handles"; -import { Module, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; +import { Module, loaderHelpers, mono_assert } from "./globals"; import { ManagedObject, ManagedError, get_arg_gc_handle, get_arg_js_handle, get_arg_type, get_arg_i32, get_arg_f64, get_arg_i52, get_arg_i16, get_arg_u8, get_arg_f32, @@ -21,6 +21,7 @@ import { GCHandleNull, JSMarshalerArgument, JSMarshalerArguments, JSMarshalerTyp import { TypedArray } from "./types/emscripten"; import { get_marshaler_to_cs_by_type, jsinteropDoc, marshal_exception_to_cs } from "./marshal-to-cs"; import { localHeapViewF64, localHeapViewI32, localHeapViewU8 } from "./memory"; +import { call_delegate } from "./managed-exports"; export function initialize_marshalers_to_js(): void { if (cs_to_js_marshalers.size == 0) { @@ -199,7 +200,7 @@ function _marshal_delegate_to_js(arg: JSMarshalerArgument, _?: MarshalerType, re result = (arg1_js: any, arg2_js: any, arg3_js: any): any => { mono_assert(!WasmEnableThreads || !result.isDisposed, "Delegate is disposed and should not be invoked anymore."); // arg numbers are shifted by one, the real first is a gc handle of the callback - return runtimeHelpers.javaScriptExports.call_delegate(gc_handle, arg1_js, arg2_js, arg3_js, res_converter, arg1_converter, arg2_converter, arg3_converter); + return call_delegate(gc_handle, arg1_js, arg2_js, arg3_js, res_converter, arg1_converter, arg2_converter, arg3_converter); }; result.dispose = () => { if (!result.isDisposed) { diff --git a/src/mono/browser/runtime/marshal.ts b/src/mono/browser/runtime/marshal.ts index 7af38977bff198..8aca7501b8653a 100644 --- a/src/mono/browser/runtime/marshal.ts +++ b/src/mono/browser/runtime/marshal.ts @@ -10,6 +10,7 @@ import { mono_wasm_new_external_root } from "./roots"; import { GCHandle, JSHandle, MonoObject, MonoString, GCHandleNull, JSMarshalerArguments, JSFunctionSignature, JSMarshalerType, JSMarshalerArgument, MarshalerToJs, MarshalerToCs, WasmRoot, MarshalerType } from "./types/internal"; import { TypedArray, VoidPtr } from "./types/emscripten"; import { utf16ToString } from "./strings"; +import { get_managed_stack_trace } from "./managed-exports"; export const cs_to_js_marshalers = new Map(); export const js_to_cs_marshalers = new Map(); @@ -265,7 +266,7 @@ export function get_arg_js_handle(arg: JSMarshalerArgument): JSHandle { export function set_arg_proxy_context(arg: JSMarshalerArgument): void { if (!WasmEnableThreads) return; mono_assert(arg, "Null arg"); - setI32(arg + 16, runtimeHelpers.proxy_context_gc_handle); + setI32(arg + 16, runtimeHelpers.proxyGCHandle); } export function set_js_handle(arg: JSMarshalerArgument, jsHandle: JSHandle): void { @@ -353,10 +354,10 @@ export class ManagedError extends Error implements IDisposable { this.managed_stack = "... omitted managed stack trace.\n" + this.getSuperStack(); return this.managed_stack; } - if (!WasmEnableThreads || runtimeHelpers.proxy_context_gc_handle) { + if (!WasmEnableThreads || runtimeHelpers.proxyGCHandle) { const gc_handle = (this)[js_owned_gc_handle_symbol]; if (gc_handle !== GCHandleNull) { - const managed_stack = runtimeHelpers.javaScriptExports.get_managed_stack_trace(gc_handle); + const managed_stack = get_managed_stack_trace(gc_handle); if (managed_stack) { this.managed_stack = managed_stack + "\n" + this.getSuperStack(); return this.managed_stack; diff --git a/src/mono/browser/runtime/pthreads/browser/index.ts b/src/mono/browser/runtime/pthreads/browser/index.ts index e1ea4961a84634..1d43935e01458c 100644 --- a/src/mono/browser/runtime/pthreads/browser/index.ts +++ b/src/mono/browser/runtime/pthreads/browser/index.ts @@ -4,12 +4,13 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import { MonoWorkerToMainMessage, PThreadInfo, pthreadPtr } from "../shared/types"; -import { MonoThreadMessage } from "../shared"; +import { MonoThreadMessage, mono_wasm_pthread_ptr, update_thread_info } from "../shared"; import { PThreadWorker, allocateUnusedWorker, getRunningWorkers, getUnusedWorkerPool, getWorker, loadWasmModuleToWorker } from "../shared/emscripten-internals"; import { createPromiseController, mono_assert, runtimeHelpers } from "../../globals"; import { MainToWorkerMessageType, PromiseAndController, PromiseController, WorkerToMainMessageType, monoMessageSymbol } from "../../types/internal"; import { mono_log_info } from "../../logging"; import { monoThreadInfo } from "../worker"; +import { mono_wasm_init_diagnostics } from "../../diagnostics"; const threadPromises: Map[]> = new Map(); @@ -81,6 +82,7 @@ function monoWorkerMessageHandler(worker: PThreadWorker, ev: MessageEvent): let thread: Thread; pthreadId = message.info?.pthreadId ?? 0; + worker.info = Object.assign(worker.info, message.info, {}); switch (message.monoCmd) { case WorkerToMainMessageType.preload: // this one shot port from setupPreloadChannelToMainThread @@ -98,13 +100,13 @@ function monoWorkerMessageHandler(worker: PThreadWorker, ev: MessageEvent): worker.thread = thread; worker.info.isRunning = true; resolveThreadPromises(pthreadId, thread); - // fall through + break; case WorkerToMainMessageType.monoRegistered: case WorkerToMainMessageType.monoAttached: case WorkerToMainMessageType.enabledInterop: case WorkerToMainMessageType.monoUnRegistered: case WorkerToMainMessageType.updateInfo: - worker.info = Object.assign(worker.info!, message.info, {}); + // just worker.info updates above break; default: throw new Error(`Unhandled message from worker: ${message.monoCmd}`); @@ -138,6 +140,17 @@ export function thread_available(): Promise { return pendingWorkerLoad.promise; } +export async function mono_wasm_init_threads() { + if (!WasmEnableThreads) return; + monoThreadInfo.pthreadId = mono_wasm_pthread_ptr(); + monoThreadInfo.threadName = "UI Thread"; + monoThreadInfo.isUI = true; + monoThreadInfo.isRunning = true; + update_thread_info(); + await instantiateWasmPThreadWorkerPool(); + await mono_wasm_init_diagnostics(); +} + /// We call on the main thread this during startup to pre-allocate a pool of pthread workers. /// At this point asset resolution needs to be working (ie we loaded MonoConfig). /// This is used instead of the Emscripten PThread.initMainThread because we call it later. diff --git a/src/mono/browser/runtime/pthreads/shared/index.ts b/src/mono/browser/runtime/pthreads/shared/index.ts index d91efc5775173b..0ddaaec365f7ef 100644 --- a/src/mono/browser/runtime/pthreads/shared/index.ts +++ b/src/mono/browser/runtime/pthreads/shared/index.ts @@ -33,8 +33,8 @@ export function isMonoThreadMessage(x: unknown): x is MonoThreadMessage { export function mono_wasm_install_js_worker_interop(context_gc_handle: GCHandle): void { if (!WasmEnableThreads) return; bindings_init(); - if (!runtimeHelpers.proxy_context_gc_handle) { - runtimeHelpers.proxy_context_gc_handle = context_gc_handle; + if (!runtimeHelpers.proxyGCHandle) { + runtimeHelpers.proxyGCHandle = context_gc_handle; mono_log_debug("Installed JSSynchronizationContext"); } Module.runtimeKeepalivePush(); @@ -51,20 +51,20 @@ export function mono_wasm_install_js_worker_interop(context_gc_handle: GCHandle) export function mono_wasm_uninstall_js_worker_interop(): void { if (!WasmEnableThreads) return; mono_assert(runtimeHelpers.mono_wasm_bindings_is_ready, "JS interop is not installed on this worker."); - mono_assert(runtimeHelpers.proxy_context_gc_handle, "JSSynchronizationContext is not installed on this worker."); + mono_assert(runtimeHelpers.proxyGCHandle, "JSSynchronizationContext is not installed on this worker."); forceDisposeProxies(true, runtimeHelpers.diagnosticTracing); Module.runtimeKeepalivePop(); - runtimeHelpers.proxy_context_gc_handle = GCHandleNull; + runtimeHelpers.proxyGCHandle = GCHandleNull; runtimeHelpers.mono_wasm_bindings_is_ready = false; update_thread_info(); } // this is just for Debug build of the runtime, making it easier to debug worker threads export function update_thread_info(): void { - const threadType = monoThreadInfo.isUI ? "main" - : !monoThreadInfo.isAttached ? "emsc" + const threadType = !monoThreadInfo.isRegistered ? "emsc" + : monoThreadInfo.isUI ? "-UI-" : monoThreadInfo.isTimer ? "timr" : monoThreadInfo.isLongRunning ? "long" : monoThreadInfo.isThreadPoolGate ? "gate" @@ -73,7 +73,7 @@ export function update_thread_info(): void { : monoThreadInfo.isExternalEventLoop ? "jsww" : monoThreadInfo.isBackground ? "back" : "norm"; - monoThreadInfo.threadPrefix = `0x${monoThreadInfo.pthreadId.toString(16).padStart(8, "0")}-${threadType}`; + monoThreadInfo.threadPrefix = `${monoThreadInfo.isRegistered ? "0x" : "--"}${monoThreadInfo.pthreadId.toString(16).padStart(8, "0")}-${threadType}`; loaderHelpers.set_thread_prefix(monoThreadInfo.threadPrefix!); if (!loaderHelpers.config.forwardConsoleLogsToWS) { diff --git a/src/mono/browser/runtime/pthreads/worker/index.ts b/src/mono/browser/runtime/pthreads/worker/index.ts index 0ea6e82de3d980..b79d4a67555abe 100644 --- a/src/mono/browser/runtime/pthreads/worker/index.ts +++ b/src/mono/browser/runtime/pthreads/worker/index.ts @@ -135,6 +135,8 @@ export function mono_wasm_pthread_on_pthread_registered(pthread_id: number): voi if (!WasmEnableThreads) return; try { mono_assert(monoThreadInfo !== null && monoThreadInfo.pthreadId == pthread_id, "expected monoThreadInfo to be set already when registering"); + monoThreadInfo.isRegistered = true; + update_thread_info(); postMessageToMain({ monoCmd: WorkerToMainMessageType.monoRegistered, info: monoThreadInfo, @@ -198,6 +200,8 @@ export function mono_wasm_pthread_on_pthread_unregistered(pthread_id: number): v mono_assert(pthread_id === monoThreadInfo.pthreadId, "expected pthread_id to match when un-registering"); postRunWorker(); monoThreadInfo.isAttached = false; + monoThreadInfo.isRegistered = false; + monoThreadInfo.threadName = "unregistered:(" + monoThreadInfo.threadName + ")"; update_thread_info(); postMessageToMain({ monoCmd: WorkerToMainMessageType.monoUnRegistered, diff --git a/src/mono/browser/runtime/run.ts b/src/mono/browser/runtime/run.ts index 0b8ab30725e25e..932d851973b752 100644 --- a/src/mono/browser/runtime/run.ts +++ b/src/mono/browser/runtime/run.ts @@ -11,6 +11,7 @@ import { mono_log_info } from "./logging"; import { assert_js_interop } from "./invoke-js"; import { assembly_load } from "./invoke-cs"; import { cancelThreads } from "./pthreads/browser"; +import { call_entry_point } from "./managed-exports"; /** * Possible signatures are described here https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/program-structure/main-command-line @@ -63,7 +64,7 @@ export async function mono_run_main(main_assembly_name?: string, args?: string[] } const method = find_entry_point(main_assembly_name); - const res = await runtimeHelpers.javaScriptExports.call_entry_point(method, args); + const res = await call_entry_point(method, args); // one more timer loop before we return, so that any remaining queued calls could run await new Promise(resolve => globalThis.setTimeout(resolve, 0)); diff --git a/src/mono/browser/runtime/satelliteAssemblies.ts b/src/mono/browser/runtime/satelliteAssemblies.ts index 100af0696aae4d..4dcc17407b1e4e 100644 --- a/src/mono/browser/runtime/satelliteAssemblies.ts +++ b/src/mono/browser/runtime/satelliteAssemblies.ts @@ -1,7 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -import { loaderHelpers, runtimeHelpers } from "./globals"; +import { loaderHelpers } from "./globals"; +import { load_satellite_assembly } from "./managed-exports"; import { AssetEntry } from "./types"; export async function loadSatelliteAssemblies(culturesToLoad: string[]): Promise { @@ -30,6 +31,6 @@ export async function loadSatelliteAssemblies(culturesToLoad: string[]): Promise .reduce((previous, next) => previous.concat(next), new Array>()) .map(async bytesPromise => { const bytes = await bytesPromise; - runtimeHelpers.javaScriptExports.load_satellite_assembly(new Uint8Array(bytes)); + load_satellite_assembly(new Uint8Array(bytes)); })); } \ No newline at end of file diff --git a/src/mono/browser/runtime/startup.ts b/src/mono/browser/runtime/startup.ts index 24ad5786e6afbd..07923cc2dd8170 100644 --- a/src/mono/browser/runtime/startup.ts +++ b/src/mono/browser/runtime/startup.ts @@ -13,20 +13,19 @@ import { initialize_marshalers_to_cs } from "./marshal-to-cs"; import { initialize_marshalers_to_js } from "./marshal-to-js"; import { init_polyfills_async } from "./polyfills"; import { strings_init, utf8ToString } from "./strings"; -import { init_managed_exports } from "./managed-exports"; +import { init_managed_exports, install_main_synchronization_context } from "./managed-exports"; import { cwraps_internal } from "./exports-internal"; import { CharPtr, InstantiateWasmCallBack, InstantiateWasmSuccessCallback } from "./types/emscripten"; import { wait_for_all_assets } from "./assets"; -import { mono_wasm_init_diagnostics } from "./diagnostics"; import { replace_linker_placeholders } from "./exports-binding"; import { endMeasure, MeasuredBlock, startMeasure } from "./profiler"; import { interp_pgo_load_data, interp_pgo_save_data } from "./interp-pgo"; import { mono_log_debug, mono_log_error, mono_log_warn } from "./logging"; // threads -import { preAllocatePThreadWorkerPool, instantiateWasmPThreadWorkerPool } from "./pthreads/browser"; +import { preAllocatePThreadWorkerPool, mono_wasm_init_threads } from "./pthreads/browser"; import { currentWorkerThreadEvents, dotnetPthreadCreated, initWorkerThreadEvents, monoThreadInfo } from "./pthreads/worker"; -import { mono_wasm_main_thread_ptr, mono_wasm_pthread_ptr } from "./pthreads/shared"; +import { mono_wasm_pthread_ptr, update_thread_info } from "./pthreads/shared"; import { jiterpreter_allocate_tables } from "./jiterpreter-support"; import { localHeapViewU8 } from "./memory"; import { assertNoProxies } from "./gc-handles"; @@ -240,22 +239,12 @@ async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) { // signal this stage, this will allow pending assets to allocate memory runtimeHelpers.beforeOnRuntimeInitialized.promise_control.resolve(); - await wait_for_all_assets(); - + let threadsReady: Promise | undefined; if (WasmEnableThreads) { - await mono_wasm_init_threads(); + threadsReady = mono_wasm_init_threads(); } - // load runtime and apply environment settings (if necessary) - await start_runtime(); - - if (runtimeHelpers.config.interpreterPgo) { - await interp_pgo_load_data(); - } - - if (!ENVIRONMENT_IS_WORKER) { - Module.runtimeKeepalivePush(); - } + await wait_for_all_assets(); if (runtimeHelpers.config.virtualWorkingDirectory) { const FS = Module.FS; @@ -268,20 +257,33 @@ async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) { FS.chdir(cwd); } - bindings_init(); - jiterpreter_allocate_tables(); + if (WasmEnableThreads && threadsReady) { + await threadsReady; + } + + if (runtimeHelpers.config.interpreterPgo) { + await interp_pgo_load_data(); + } + + if (runtimeHelpers.config.interpreterPgo) + setTimeout(maybeSaveInterpPgoTable, (runtimeHelpers.config.interpreterPgoSaveDelay || 15) * 1000); + + + Module.runtimeKeepalivePush(); + + // load runtime and apply environment settings (if necessary) + await start_runtime(); + + if (!ENVIRONMENT_IS_WORKER) { + Module.runtimeKeepalivePush(); + } if (ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER) { Module.runtimeKeepalivePush(); } - runtimeHelpers.runtimeReady = true; runtimeList.registerRuntime(exportedRuntimeAPI); - if (WasmEnableThreads) { - runtimeHelpers.javaScriptExports.install_main_synchronization_context(); - } - if (!runtimeHelpers.mono_wasm_runtime_is_ready) mono_wasm_runtime_ready(); if (loaderHelpers.config.debugLevel !== 0 && loaderHelpers.config.cacheBootResources) { @@ -304,6 +306,7 @@ async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) { await mono_wasm_after_user_runtime_initialized(); endMeasure(mark, MeasuredBlock.onRuntimeInitialized); } catch (err) { + Module.runtimeKeepalivePop(); mono_log_error("onRuntimeInitializedAsync() failed", err); loaderHelpers.mono_exit(1, err); throw err; @@ -344,7 +347,7 @@ export function postRunWorker() { if (!WasmEnableThreads) return; const mark = startMeasure(); try { - if (runtimeHelpers.proxy_context_gc_handle) { + if (runtimeHelpers.proxyGCHandle) { const pthread_ptr = mono_wasm_pthread_ptr(); mono_log_warn(`JSSynchronizationContext is still installed on worker 0x${pthread_ptr.toString(16)}.`); } else { @@ -362,19 +365,6 @@ export function postRunWorker() { } } -async function mono_wasm_init_threads() { - if (!WasmEnableThreads) return; - - const threadPrefix = `0x${mono_wasm_main_thread_ptr().toString(16)}-main`; - monoThreadInfo.threadPrefix = threadPrefix; - monoThreadInfo.threadName = "UI Thread"; - monoThreadInfo.isUI = true; - monoThreadInfo.isAttached = true; - loaderHelpers.set_thread_prefix(threadPrefix); - await instantiateWasmPThreadWorkerPool(); - await mono_wasm_init_diagnostics(); -} - function mono_wasm_pre_init_essential(isWorker: boolean): void { if (!isWorker) Module.addRunDependency("mono_wasm_pre_init_essential"); @@ -416,8 +406,6 @@ async function mono_wasm_pre_init_essential_async(): Promise { async function mono_wasm_after_user_runtime_initialized(): Promise { mono_log_debug("mono_wasm_after_user_runtime_initialized"); try { - mono_log_debug("Initializing mono runtime"); - if (Module.onDotnetReady) { try { await Module.onDotnetReady(); @@ -496,31 +484,50 @@ async function ensureUsedWasmFeatures() { } } -async function start_runtime() { - const mark = startMeasure(); +export function start_runtime() { + try { + const mark = startMeasure(); + mono_log_debug("Initializing mono runtime"); + for (const k in runtimeHelpers.config.environmentVariables) { + const v = runtimeHelpers.config.environmentVariables![k]; + if (typeof (v) === "string") + mono_wasm_setenv(k, v); + else + throw new Error(`Expected environment variable '${k}' to be a string but it was ${typeof v}: '${v}'`); + } + if (runtimeHelpers.config.runtimeOptions) + mono_wasm_set_runtime_options(runtimeHelpers.config.runtimeOptions); - for (const k in runtimeHelpers.config.environmentVariables) { - const v = runtimeHelpers.config.environmentVariables![k]; - if (typeof (v) === "string") - mono_wasm_setenv(k, v); - else - throw new Error(`Expected environment variable '${k}' to be a string but it was ${typeof v}: '${v}'`); - } - if (runtimeHelpers.config.runtimeOptions) - mono_wasm_set_runtime_options(runtimeHelpers.config.runtimeOptions); + if (runtimeHelpers.config.aotProfilerOptions) + mono_wasm_init_aot_profiler(runtimeHelpers.config.aotProfilerOptions); + + if (runtimeHelpers.config.browserProfilerOptions) + mono_wasm_init_browser_profiler(runtimeHelpers.config.browserProfilerOptions); + + mono_wasm_load_runtime("unused", runtimeHelpers.config.debugLevel); - if (runtimeHelpers.config.aotProfilerOptions) - mono_wasm_init_aot_profiler(runtimeHelpers.config.aotProfilerOptions); + jiterpreter_allocate_tables(); + + bindings_init(); - if (runtimeHelpers.config.browserProfilerOptions) - mono_wasm_init_browser_profiler(runtimeHelpers.config.browserProfilerOptions); + runtimeHelpers.runtimeReady = true; - mono_wasm_load_runtime("unused", runtimeHelpers.config.debugLevel); + if (WasmEnableThreads) { + monoThreadInfo.isAttached = true; + monoThreadInfo.isRegistered = true; + update_thread_info(); + runtimeHelpers.proxyGCHandle = install_main_synchronization_context(); + } - if (runtimeHelpers.config.interpreterPgo) - setTimeout(maybeSaveInterpPgoTable, (runtimeHelpers.config.interpreterPgoSaveDelay || 15) * 1000); + // get GCHandle of the ctx + runtimeHelpers.afterMonoStarted.promise_control.resolve(runtimeHelpers.proxyGCHandle); - endMeasure(mark, MeasuredBlock.startRuntime); + endMeasure(mark, MeasuredBlock.startRuntime); + } catch (err) { + mono_log_error("start_runtime() failed", err); + loaderHelpers.mono_exit(1, err); + throw err; + } } async function maybeSaveInterpPgoTable() { diff --git a/src/mono/browser/runtime/types/index.ts b/src/mono/browser/runtime/types/index.ts index 0e382399d92d1e..e0f76ea3a546e8 100644 --- a/src/mono/browser/runtime/types/index.ts +++ b/src/mono/browser/runtime/types/index.ts @@ -316,7 +316,15 @@ export type SingleAssetBehaviors = /** * Typically blazor.boot.json */ - | "manifest"; + | "manifest" + /** + * The debugging symbols + */ + | "symbols" + /** + * Load segmentation rules file for Hybrid Globalization. + */ + | "segmentation-rules"; export type AssetBehaviors = SingleAssetBehaviors | /** @@ -347,14 +355,6 @@ export type AssetBehaviors = SingleAssetBehaviors | * The javascript module that came from nuget package . */ | "js-module-library-initializer" - /** - * The javascript module for threads. - */ - | "symbols" - /** - * Load segmentation rules file for Hybrid Globalization. - */ - | "segmentation-rules" export const enum GlobalizationMode { /** diff --git a/src/mono/browser/runtime/types/internal.ts b/src/mono/browser/runtime/types/internal.ts index 9b5af8831e43cf..44d4310b7da80a 100644 --- a/src/mono/browser/runtime/types/internal.ts +++ b/src/mono/browser/runtime/types/internal.ts @@ -127,7 +127,6 @@ export type LoaderHelpers = { scriptUrl: string modulesUniqueQuery?: string preferredIcuAsset?: string | null, - invariantMode: boolean, actual_downloaded_assets_count: number, actual_instantiated_assets_count: number, @@ -194,13 +193,12 @@ export type RuntimeHelpers = { quit: Function, nativeExit: (code: number) => void, nativeAbort: (reason: any) => void, - javaScriptExports: JavaScriptExports, subtle: SubtleCrypto | null, updateMemoryViews: () => void getMemory(): WebAssembly.Memory, getWasmIndirectFunctionTable(): WebAssembly.Table, runtimeReady: boolean, - proxy_context_gc_handle: GCHandle, + proxyGCHandle: GCHandle | undefined, cspPolicy: boolean, allAssetsInMemory: PromiseAndController, @@ -210,6 +208,7 @@ export type RuntimeHelpers = { afterPreInit: PromiseAndController, afterPreRun: PromiseAndController, beforeOnRuntimeInitialized: PromiseAndController, + afterMonoStarted: PromiseAndController, afterOnRuntimeInitialized: PromiseAndController, afterPostRun: PromiseAndController, @@ -300,35 +299,6 @@ export function notThenable(x: T | PromiseLike): x is T { /// Primarily intended for debugging purposes. export type EventPipeSessionID = bigint; -// in all the exported internals methods, we use the same data structures for stack frame as normal full blow interop -// see src\libraries\System.Runtime.InteropServices.JavaScript\src\System\Runtime\InteropServices\JavaScript\Interop\JavaScriptExports.cs -export interface JavaScriptExports { - // the marshaled signature is: void ReleaseJSOwnedObjectByGCHandle(GCHandle gcHandle) - release_js_owned_object_by_gc_handle(gc_handle: GCHandle): void; - - // the marshaled signature is: void CompleteTask(GCHandle holder, Exception? exceptionResult, T? result) - complete_task(holder_gc_handle: GCHandle, isCanceling: boolean, error?: any, data?: any, res_converter?: MarshalerToCs): void; - - // the marshaled signature is: TRes? CallDelegate(GCHandle callback, T1? arg1, T2? arg2, T3? arg3) - call_delegate(callback_gc_handle: GCHandle, arg1_js: any, arg2_js: any, arg3_js: any, - res_converter?: MarshalerToJs, arg1_converter?: MarshalerToCs, arg2_converter?: MarshalerToCs, arg3_converter?: MarshalerToCs): any; - - // the marshaled signature is: Task? CallEntrypoint(MonoMethod* entrypointPtr, string[] args) - call_entry_point(entry_point: MonoMethod, args?: string[]): Promise; - - // the marshaled signature is: void InstallMainSynchronizationContext() - install_main_synchronization_context(): void; - - // the marshaled signature is: string GetManagedStackTrace(GCHandle exception) - get_managed_stack_trace(exception_gc_handle: GCHandle): string | null - - // the marshaled signature is: void LoadSatelliteAssembly(byte[] dll) - load_satellite_assembly(dll: Uint8Array): void; - - // the marshaled signature is: void LoadLazyAssembly(byte[] dll, byte[] pdb) - load_lazy_assembly(dll: Uint8Array, pdb: Uint8Array | null): void; -} - export type MarshalerToJs = (arg: JSMarshalerArgument, element_type?: MarshalerType, res_converter?: MarshalerToJs, arg1_converter?: MarshalerToCs, arg2_converter?: MarshalerToCs, arg3_converter?: MarshalerToCs) => any; export type MarshalerToCs = (arg: JSMarshalerArgument, value: any, element_type?: MarshalerType, res_converter?: MarshalerToCs, arg1_converter?: MarshalerToJs, arg2_converter?: MarshalerToJs, arg3_converter?: MarshalerToJs) => void; export type BoundMarshalerToJs = (args: JSMarshalerArguments) => any; From 2f48c848eea4ec83fd1d01e418796225900e549c Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 12 Feb 2024 15:58:29 +0100 Subject: [PATCH 2/9] fix --- .../System/Runtime/InteropServices/JavaScript/JSProxyContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs index ee3418092eae7c..8ca8fd85cbb8f1 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs @@ -30,7 +30,7 @@ private JSProxyContext() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool IsCurrentThread() => true; + public static bool IsCurrentThread() => true; #else public nint ContextHandle; public nint JSNativeTID; // target thread where JavaScript is running From 68615a3f61e4ae9a1a301145987aaf54ebf93eff Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 12 Feb 2024 16:22:01 +0100 Subject: [PATCH 3/9] fix --- .../Runtime/InteropServices/JavaScript/JSProxyContext.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs index 8ca8fd85cbb8f1..c33ee713acddba 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSProxyContext.cs @@ -30,7 +30,9 @@ private JSProxyContext() } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsCurrentThread() => true; +#pragma warning disable CA1822 // Mark members as static + public bool IsCurrentThread() => true; +#pragma warning restore CA1822 // Mark members as static #else public nint ContextHandle; public nint JSNativeTID; // target thread where JavaScript is running From e6feaa467b1df9074b4b61c9e785da7ef6e3a681 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 12 Feb 2024 19:29:29 +0100 Subject: [PATCH 4/9] more --- src/mono/browser/runtime/globals.ts | 15 ++++++++------- src/mono/browser/runtime/invoke-cs.ts | 16 ++++++++-------- src/mono/browser/runtime/managed-exports.ts | 16 ++++++++-------- src/mono/browser/runtime/startup.ts | 10 +++++----- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/mono/browser/runtime/globals.ts b/src/mono/browser/runtime/globals.ts index f619d22bb07a68..169673d7bf9b25 100644 --- a/src/mono/browser/runtime/globals.ts +++ b/src/mono/browser/runtime/globals.ts @@ -9,7 +9,7 @@ import gitHash from "consts:gitHash"; import { RuntimeAPI } from "./types/index"; -import type { GlobalObjects, EmscriptenInternals, RuntimeHelpers, LoaderHelpers, DotnetModuleInternal, PromiseAndController, EmscriptenBuildOptions } from "./types/internal"; +import type { GlobalObjects, EmscriptenInternals, RuntimeHelpers, LoaderHelpers, DotnetModuleInternal, PromiseAndController, EmscriptenBuildOptions, GCHandle } from "./types/internal"; import { mono_log_error } from "./logging"; // these are our public API (except internal) @@ -55,7 +55,7 @@ export function setRuntimeGlobals(globalObjects: GlobalObjects) { loaderHelpers = globalObjects.loaderHelpers; exportedRuntimeAPI = globalObjects.api; - Object.assign(runtimeHelpers, { + const rh: Partial = { gitHash, allAssetsInMemory: createPromiseController(), dotnetReady: createPromiseController(), @@ -64,16 +64,17 @@ export function setRuntimeGlobals(globalObjects: GlobalObjects) { afterPreInit: createPromiseController(), afterPreRun: createPromiseController(), beforeOnRuntimeInitialized: createPromiseController(), - afterMonoStarted: createPromiseController(), + afterMonoStarted: createPromiseController(), afterOnRuntimeInitialized: createPromiseController(), afterPostRun: createPromiseController(), - mono_wasm_exit: () => { + nativeExit: () => { throw new Error("Mono shutdown"); }, - abort: (reason: any) => { + nativeAbort: (reason: any) => { throw reason; - } - }); + }, + }; + Object.assign(runtimeHelpers, rh); Object.assign(globalObjects.module.config!, {}) as any; Object.assign(globalObjects.api, { diff --git a/src/mono/browser/runtime/invoke-cs.ts b/src/mono/browser/runtime/invoke-cs.ts index 6c7c653b449f68..fffb0c01bc2b96 100644 --- a/src/mono/browser/runtime/invoke-cs.ts +++ b/src/mono/browser/runtime/invoke-cs.ts @@ -140,7 +140,7 @@ function bind_fn_0V(closure: BindingClosure) { try { const args = alloc_stack_frame(2); // call C# side - invoke_method_and_handle_exception(method, args); + invoke_sync_method(method, args); } finally { Module.stackRestore(sp); endMeasure(mark, MeasuredBlock.callCsFunction, fqn); @@ -163,7 +163,7 @@ function bind_fn_1V(closure: BindingClosure) { marshaler1(args, arg1); // call C# side - invoke_method_and_handle_exception(method, args); + invoke_sync_method(method, args); } finally { Module.stackRestore(sp); endMeasure(mark, MeasuredBlock.callCsFunction, fqn); @@ -187,7 +187,7 @@ function bind_fn_1R(closure: BindingClosure) { marshaler1(args, arg1); // call C# side - invoke_method_and_handle_exception(method, args); + invoke_sync_method(method, args); const js_result = res_converter(args); return js_result; @@ -217,7 +217,7 @@ function bind_fn_1RA(closure: BindingClosure) { let promise = res_converter(args); // call C# side - invoke_method_and_handle_exception(method, args); + invoke_sync_method(method, args); // in case the C# side returned synchronously promise = end_marshal_task_to_js(args, undefined, promise); @@ -248,7 +248,7 @@ function bind_fn_2R(closure: BindingClosure) { marshaler2(args, arg2); // call C# side - invoke_method_and_handle_exception(method, args); + invoke_sync_method(method, args); const js_result = res_converter(args); return js_result; @@ -280,7 +280,7 @@ function bind_fn_2RA(closure: BindingClosure) { let promise = res_converter(args); // call C# side - invoke_method_and_handle_exception(method, args); + invoke_sync_method(method, args); // in case the C# side returned synchronously promise = end_marshal_task_to_js(args, undefined, promise); @@ -322,7 +322,7 @@ function bind_fn(closure: BindingClosure) { } // call C# side - invoke_method_and_handle_exception(method, args); + invoke_sync_method(method, args); if (is_async) { // in case the C# side returned synchronously js_result = end_marshal_task_to_js(args, undefined, js_result); @@ -348,7 +348,7 @@ type BindingClosure = { isDisposed: boolean, } -export function invoke_method_and_handle_exception(method: MonoMethod, args: JSMarshalerArguments): void { +export function invoke_sync_method(method: MonoMethod, args: JSMarshalerArguments): void { assert_js_interop(); const fail_root = mono_wasm_new_root(); try { diff --git a/src/mono/browser/runtime/managed-exports.ts b/src/mono/browser/runtime/managed-exports.ts index e39c45ae9eaccf..418ccab257a6dd 100644 --- a/src/mono/browser/runtime/managed-exports.ts +++ b/src/mono/browser/runtime/managed-exports.ts @@ -7,7 +7,7 @@ import { GCHandle, GCHandleNull, MarshalerToCs, MarshalerToJs, MarshalerType, Mo import cwraps from "./cwraps"; import { runtimeHelpers, Module, loaderHelpers, mono_assert } from "./globals"; import { alloc_stack_frame, get_arg, set_arg_type, set_gc_handle } from "./marshal"; -import { invoke_method_and_handle_exception } from "./invoke-cs"; +import { invoke_sync_method } from "./invoke-cs"; import { marshal_array_to_cs, marshal_array_to_cs_impl, marshal_exception_to_cs, marshal_intptr_to_cs } from "./marshal-to-cs"; import { marshal_int32_to_js, end_marshal_task_to_js, marshal_string_to_js, begin_marshal_task_to_js } from "./marshal-to-js"; import { do_not_force_dispose } from "./gc-handles"; @@ -59,7 +59,7 @@ export async function call_entry_point(entry_point: MonoMethod, program_args?: s let promise = begin_marshal_task_to_js(res, MarshalerType.TaskPreCreated, marshal_int32_to_js); // NOTE: at the moment this is synchronous call on the same thread and therefore we could marshal (null) result synchronously - invoke_method_and_handle_exception(managedExports.CallEntrypoint, args); + invoke_sync_method(managedExports.CallEntrypoint, args); // in case the C# side returned synchronously promise = end_marshal_task_to_js(args, marshal_int32_to_js, promise); @@ -83,7 +83,7 @@ export function load_satellite_assembly(dll: Uint8Array): void { const arg1 = get_arg(args, 2); set_arg_type(arg1, MarshalerType.Array); marshal_array_to_cs(arg1, dll, MarshalerType.Byte); - invoke_method_and_handle_exception(managedExports.LoadSatelliteAssembly, args); + invoke_sync_method(managedExports.LoadSatelliteAssembly, args); } finally { Module.stackRestore(sp); } @@ -100,7 +100,7 @@ export function load_lazy_assembly(dll: Uint8Array, pdb: Uint8Array | null): voi set_arg_type(arg2, MarshalerType.Array); marshal_array_to_cs(arg1, dll, MarshalerType.Byte); marshal_array_to_cs(arg2, pdb, MarshalerType.Byte); - invoke_method_and_handle_exception(managedExports.LoadLazyAssembly, args); + invoke_sync_method(managedExports.LoadLazyAssembly, args); } finally { Module.stackRestore(sp); } @@ -116,7 +116,7 @@ export function release_js_owned_object_by_gc_handle(gc_handle: GCHandle) { const arg1 = get_arg(args, 2); set_arg_type(arg1, MarshalerType.Object); set_gc_handle(arg1, gc_handle); - invoke_method_and_handle_exception(managedExports.ReleaseJSOwnedObjectByGCHandle, args); + invoke_sync_method(managedExports.ReleaseJSOwnedObjectByGCHandle, args); } finally { Module.stackRestore(sp); } @@ -144,7 +144,7 @@ export function complete_task(holder_gc_handle: GCHandle, isCanceling: boolean, mono_assert(res_converter, "res_converter missing"); res_converter(arg3, data); } - invoke_method_and_handle_exception(managedExports.CompleteTask, args); + invoke_sync_method(managedExports.CompleteTask, args); } finally { Module.stackRestore(sp); } @@ -175,7 +175,7 @@ export function call_delegate(callback_gc_handle: GCHandle, arg1_js: any, arg2_j arg3_converter(arg4, arg3_js); } - invoke_method_and_handle_exception(managedExports.CallDelegate, args); + invoke_sync_method(managedExports.CallDelegate, args); if (res_converter) { const res = get_arg(args, 1); @@ -197,7 +197,7 @@ export function get_managed_stack_trace(exception_gc_handle: GCHandle) { set_arg_type(arg1, MarshalerType.Exception); set_gc_handle(arg1, exception_gc_handle); - invoke_method_and_handle_exception(managedExports.GetManagedStackTrace, args); + invoke_sync_method(managedExports.GetManagedStackTrace, args); const res = get_arg(args, 1); return marshal_string_to_js(res); } finally { diff --git a/src/mono/browser/runtime/startup.ts b/src/mono/browser/runtime/startup.ts index 07923cc2dd8170..704fd894955c88 100644 --- a/src/mono/browser/runtime/startup.ts +++ b/src/mono/browser/runtime/startup.ts @@ -261,10 +261,6 @@ async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) { await threadsReady; } - if (runtimeHelpers.config.interpreterPgo) { - await interp_pgo_load_data(); - } - if (runtimeHelpers.config.interpreterPgo) setTimeout(maybeSaveInterpPgoTable, (runtimeHelpers.config.interpreterPgoSaveDelay || 15) * 1000); @@ -272,7 +268,11 @@ async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) { Module.runtimeKeepalivePush(); // load runtime and apply environment settings (if necessary) - await start_runtime(); + start_runtime(); + + if (runtimeHelpers.config.interpreterPgo) { + await interp_pgo_load_data(); + } if (!ENVIRONMENT_IS_WORKER) { Module.runtimeKeepalivePush(); From f1ef16d7b25a1df605ce447d424434d3cbd048ff Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Mon, 12 Feb 2024 19:33:06 +0100 Subject: [PATCH 5/9] more --- src/mono/browser/runtime/globals.ts | 8 ++------ src/mono/browser/runtime/loader/globals.ts | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/mono/browser/runtime/globals.ts b/src/mono/browser/runtime/globals.ts index 169673d7bf9b25..258fe1756a7d19 100644 --- a/src/mono/browser/runtime/globals.ts +++ b/src/mono/browser/runtime/globals.ts @@ -67,12 +67,8 @@ export function setRuntimeGlobals(globalObjects: GlobalObjects) { afterMonoStarted: createPromiseController(), afterOnRuntimeInitialized: createPromiseController(), afterPostRun: createPromiseController(), - nativeExit: () => { - throw new Error("Mono shutdown"); - }, - nativeAbort: (reason: any) => { - throw reason; - }, + nativeAbort: (reason: any) => { throw reason || new Error("abort"); }, + nativeExit: (code: number) => { throw new Error("exit:" + code); } }; Object.assign(runtimeHelpers, rh); diff --git a/src/mono/browser/runtime/loader/globals.ts b/src/mono/browser/runtime/loader/globals.ts index 6aabc58b51be2c..8dd151c3fbfdd2 100644 --- a/src/mono/browser/runtime/loader/globals.ts +++ b/src/mono/browser/runtime/loader/globals.ts @@ -76,7 +76,7 @@ export function setLoaderGlobals( mono_wasm_bindings_is_ready: false, config: globalObjects.module.config, diagnosticTracing: false, - nativeAbort: (reason: any) => { throw reason; }, + nativeAbort: (reason: any) => { throw reason || new Error("abort"); }, nativeExit: (code: number) => { throw new Error("exit:" + code); } }; const lh: Partial = { From 38fa42961a0c54a568e85c12d64bb1f414870a15 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Tue, 13 Feb 2024 08:01:45 +0100 Subject: [PATCH 6/9] Update src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Marek FiĊĦera --- .../JavaScript/Marshaling/JSMarshalerArgument.Task.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs index cf58e5e10e0e64..eee6b5f3037cf9 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs @@ -235,7 +235,7 @@ public void ToJS(Task? value) { if (!isCurrentThread) { - Environment.FailFast("NULL not supported in MT"); + Environment.FailFast("Marshalling null task to JS is not supported in MT"); } slot.Type = MarshalerType.None; return; From 9de21ce1bb7a37b5dfd8b2c0268f98c734e51653 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Tue, 13 Feb 2024 08:15:55 +0100 Subject: [PATCH 7/9] feedback --- .../JavaScript/Interop/JavaScriptExports.cs | 21 +++++++++--- src/mono/browser/runtime/cwraps.ts | 4 +-- src/mono/browser/runtime/invoke-cs.ts | 2 +- src/mono/browser/runtime/managed-exports.ts | 19 +++++++---- src/mono/browser/runtime/startup.ts | 32 +++++++++---------- 5 files changed, 49 insertions(+), 29 deletions(-) diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs index ab0a6642b1820a..f4a6efb3c333f3 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs @@ -296,11 +296,24 @@ public static void GetManagedStackTrace(JSMarshalerArgument* arguments_buffer) // this is here temporarily, until JSWebWorker becomes public API [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicMethods, "System.Runtime.InteropServices.JavaScript.JSWebWorker", "System.Runtime.InteropServices.JavaScript")] - public static void InstallMainSynchronizationContext(nint* nativeTIDAndProxyContextGCHandlePtr) + // the marshaled signature is: + // void InstallMainSynchronizationContext(nint jsNativeTID, out GCHandle contextHandle) + public static void InstallMainSynchronizationContext(JSMarshalerArgument* arguments_buffer) { - var jsSynchronizationContext = JSSynchronizationContext.InstallWebWorkerInterop(true, CancellationToken.None); - jsSynchronizationContext.ProxyContext.JSNativeTID = *nativeTIDAndProxyContextGCHandlePtr; - *nativeTIDAndProxyContextGCHandlePtr = jsSynchronizationContext.ProxyContext.ContextHandle; + ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame() + ref JSMarshalerArgument arg_1 = ref arguments_buffer[2];// initialized and set by caller + ref JSMarshalerArgument arg_2 = ref arguments_buffer[3];// initialized and set by caller + + try + { + var jsSynchronizationContext = JSSynchronizationContext.InstallWebWorkerInterop(true, CancellationToken.None); + jsSynchronizationContext.ProxyContext.JSNativeTID = arg_1.slot.IntPtrValue; + arg_2.slot.GCHandle = jsSynchronizationContext.ProxyContext.ContextHandle; + } + catch (Exception ex) + { + arg_exc.ToJS(ex); + } } #endif diff --git a/src/mono/browser/runtime/cwraps.ts b/src/mono/browser/runtime/cwraps.ts index 996837d37e5c97..7c38a34ee4e852 100644 --- a/src/mono/browser/runtime/cwraps.ts +++ b/src/mono/browser/runtime/cwraps.ts @@ -6,7 +6,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import type { MonoAssembly, MonoClass, MonoMethod, MonoObject, - MonoType, MonoObjectRef, MonoStringRef + MonoType, MonoObjectRef, MonoStringRef, JSMarshalerArguments } from "./types/internal"; import type { VoidPtr, CharPtrPtr, Int32Ptr, CharPtr, ManagedPointer } from "./types/emscripten"; import { Module, runtimeHelpers } from "./globals"; @@ -181,7 +181,7 @@ export interface t_Cwraps { mono_wasm_getenv(name: string): CharPtr; mono_wasm_set_main_args(argc: number, argv: VoidPtr): void; mono_wasm_exec_regression(verbose_level: number, image: string): number; - mono_wasm_invoke_method(method: MonoMethod, args: VoidPtr, fail: MonoStringRef): number; + mono_wasm_invoke_method(method: MonoMethod, args: JSMarshalerArguments, fail: MonoStringRef): number; mono_wasm_write_managed_pointer_unsafe(destination: VoidPtr | MonoObjectRef, pointer: ManagedPointer): void; mono_wasm_copy_managed_pointer(destination: VoidPtr | MonoObjectRef, source: VoidPtr | MonoObjectRef): void; mono_wasm_i52_to_f64(source: VoidPtr, error: Int32Ptr): number; diff --git a/src/mono/browser/runtime/invoke-cs.ts b/src/mono/browser/runtime/invoke-cs.ts index fffb0c01bc2b96..d45063ab30bbb6 100644 --- a/src/mono/browser/runtime/invoke-cs.ts +++ b/src/mono/browser/runtime/invoke-cs.ts @@ -353,7 +353,7 @@ export function invoke_sync_method(method: MonoMethod, args: JSMarshalerArgument const fail_root = mono_wasm_new_root(); try { set_args_context(args); - const fail = cwraps.mono_wasm_invoke_method(method, args as any, fail_root.address); + const fail = cwraps.mono_wasm_invoke_method(method, args, fail_root.address); if (fail) runtimeHelpers.nativeAbort("ERR24: Unexpected error: " + monoStringToString(fail_root)); if (is_args_exception(args)) { const exc = get_arg(args, 0); diff --git a/src/mono/browser/runtime/managed-exports.ts b/src/mono/browser/runtime/managed-exports.ts index 418ccab257a6dd..f662ee71852361 100644 --- a/src/mono/browser/runtime/managed-exports.ts +++ b/src/mono/browser/runtime/managed-exports.ts @@ -6,13 +6,13 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import { GCHandle, GCHandleNull, MarshalerToCs, MarshalerToJs, MarshalerType, MonoMethod } from "./types/internal"; import cwraps from "./cwraps"; import { runtimeHelpers, Module, loaderHelpers, mono_assert } from "./globals"; -import { alloc_stack_frame, get_arg, set_arg_type, set_gc_handle } from "./marshal"; +import { alloc_stack_frame, get_arg, get_arg_gc_handle, is_args_exception, set_arg_intptr, set_arg_type, set_gc_handle } from "./marshal"; import { invoke_sync_method } from "./invoke-cs"; import { marshal_array_to_cs, marshal_array_to_cs_impl, marshal_exception_to_cs, marshal_intptr_to_cs } from "./marshal-to-cs"; -import { marshal_int32_to_js, end_marshal_task_to_js, marshal_string_to_js, begin_marshal_task_to_js } from "./marshal-to-js"; +import { marshal_int32_to_js, end_marshal_task_to_js, marshal_string_to_js, begin_marshal_task_to_js, marshal_exception_to_js } from "./marshal-to-js"; import { do_not_force_dispose } from "./gc-handles"; import { assert_c_interop } from "./invoke-js"; -import { getI32, setI32 } from "./memory"; +import { getI32 } from "./memory"; import { mono_wasm_main_thread_ptr } from "./pthreads/shared"; const managedExports: ManagedExports = {} as any; @@ -205,7 +205,7 @@ export function get_managed_stack_trace(exception_gc_handle: GCHandle) { } } -// InstallMainSynchronizationContext(nint* nativeTIDAndProxyContextGCHandlePtr) +// void InstallMainSynchronizationContext(nint jsNativeTID, out GCHandle contextHandle) export function install_main_synchronization_context(): GCHandle { if (!WasmEnableThreads) return GCHandleNull; assert_c_interop(); @@ -213,10 +213,17 @@ export function install_main_synchronization_context(): GCHandle { const sp = Module.stackSave(); try { // tid in, gc_handle out - const args = Module.stackAlloc(4); - setI32(args, mono_wasm_main_thread_ptr()); + const args = alloc_stack_frame(4); + const arg1 = get_arg(args, 2); + const arg2 = get_arg(args, 3); + set_arg_intptr(arg1, mono_wasm_main_thread_ptr() as any); const fail = cwraps.mono_wasm_invoke_method(managedExports.InstallMainSynchronizationContext!, args, 0 as any); if (fail) runtimeHelpers.nativeAbort("ERR24: Unexpected error"); + get_arg_gc_handle(arg2); + if (is_args_exception(args)) { + const exc = get_arg(args, 0); + throw marshal_exception_to_js(exc); + } return getI32(args) as any; } finally { Module.stackRestore(sp); diff --git a/src/mono/browser/runtime/startup.ts b/src/mono/browser/runtime/startup.ts index 8e786e55fa1715..cb2fb41d711365 100644 --- a/src/mono/browser/runtime/startup.ts +++ b/src/mono/browser/runtime/startup.ts @@ -486,25 +486,25 @@ async function ensureUsedWasmFeatures() { export function start_runtime() { try { - const mark = startMeasure(); + const mark = startMeasure(); mono_log_debug("Initializing mono runtime"); - for (const k in runtimeHelpers.config.environmentVariables) { - const v = runtimeHelpers.config.environmentVariables![k]; - if (typeof (v) === "string") - mono_wasm_setenv(k, v); - else - throw new Error(`Expected environment variable '${k}' to be a string but it was ${typeof v}: '${v}'`); - } - if (runtimeHelpers.config.runtimeOptions) - mono_wasm_set_runtime_options(runtimeHelpers.config.runtimeOptions); + for (const k in runtimeHelpers.config.environmentVariables) { + const v = runtimeHelpers.config.environmentVariables![k]; + if (typeof (v) === "string") + mono_wasm_setenv(k, v); + else + throw new Error(`Expected environment variable '${k}' to be a string but it was ${typeof v}: '${v}'`); + } + if (runtimeHelpers.config.runtimeOptions) + mono_wasm_set_runtime_options(runtimeHelpers.config.runtimeOptions); - if (runtimeHelpers.config.aotProfilerOptions) - mono_wasm_init_aot_profiler(runtimeHelpers.config.aotProfilerOptions); + if (runtimeHelpers.config.aotProfilerOptions) + mono_wasm_init_aot_profiler(runtimeHelpers.config.aotProfilerOptions); - if (runtimeHelpers.config.browserProfilerOptions) - mono_wasm_init_browser_profiler(runtimeHelpers.config.browserProfilerOptions); + if (runtimeHelpers.config.browserProfilerOptions) + mono_wasm_init_browser_profiler(runtimeHelpers.config.browserProfilerOptions); - mono_wasm_load_runtime(); + mono_wasm_load_runtime(); jiterpreter_allocate_tables(); @@ -522,7 +522,7 @@ export function start_runtime() { // get GCHandle of the ctx runtimeHelpers.afterMonoStarted.promise_control.resolve(runtimeHelpers.proxyGCHandle); - endMeasure(mark, MeasuredBlock.startRuntime); + endMeasure(mark, MeasuredBlock.startRuntime); } catch (err) { mono_log_error("start_runtime() failed", err); loaderHelpers.mono_exit(1, err); From 940b4e56a379a8653e09df3ed3c17f904ce1352f Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Tue, 13 Feb 2024 13:10:49 +0100 Subject: [PATCH 8/9] fix --- src/mono/browser/runtime/globals.ts | 2 +- src/mono/browser/runtime/managed-exports.ts | 11 ++++++----- src/mono/browser/runtime/types/internal.ts | 1 + 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/mono/browser/runtime/globals.ts b/src/mono/browser/runtime/globals.ts index 258fe1756a7d19..15ea06b82c6d07 100644 --- a/src/mono/browser/runtime/globals.ts +++ b/src/mono/browser/runtime/globals.ts @@ -68,7 +68,7 @@ export function setRuntimeGlobals(globalObjects: GlobalObjects) { afterOnRuntimeInitialized: createPromiseController(), afterPostRun: createPromiseController(), nativeAbort: (reason: any) => { throw reason || new Error("abort"); }, - nativeExit: (code: number) => { throw new Error("exit:" + code); } + nativeExit: (code: number) => { throw new Error("exit:" + code); }, }; Object.assign(runtimeHelpers, rh); diff --git a/src/mono/browser/runtime/managed-exports.ts b/src/mono/browser/runtime/managed-exports.ts index f662ee71852361..4a95d0ca362a55 100644 --- a/src/mono/browser/runtime/managed-exports.ts +++ b/src/mono/browser/runtime/managed-exports.ts @@ -6,14 +6,14 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import { GCHandle, GCHandleNull, MarshalerToCs, MarshalerToJs, MarshalerType, MonoMethod } from "./types/internal"; import cwraps from "./cwraps"; import { runtimeHelpers, Module, loaderHelpers, mono_assert } from "./globals"; -import { alloc_stack_frame, get_arg, get_arg_gc_handle, is_args_exception, set_arg_intptr, set_arg_type, set_gc_handle } from "./marshal"; +import { JavaScriptMarshalerArgSize, alloc_stack_frame, get_arg, get_arg_gc_handle, is_args_exception, set_arg_intptr, set_arg_type, set_gc_handle } from "./marshal"; import { invoke_sync_method } from "./invoke-cs"; import { marshal_array_to_cs, marshal_array_to_cs_impl, marshal_exception_to_cs, marshal_intptr_to_cs } from "./marshal-to-cs"; import { marshal_int32_to_js, end_marshal_task_to_js, marshal_string_to_js, begin_marshal_task_to_js, marshal_exception_to_js } from "./marshal-to-js"; import { do_not_force_dispose } from "./gc-handles"; import { assert_c_interop } from "./invoke-js"; -import { getI32 } from "./memory"; import { mono_wasm_main_thread_ptr } from "./pthreads/shared"; +import { _zero_region } from "./memory"; const managedExports: ManagedExports = {} as any; @@ -213,18 +213,19 @@ export function install_main_synchronization_context(): GCHandle { const sp = Module.stackSave(); try { // tid in, gc_handle out - const args = alloc_stack_frame(4); + const bytes = JavaScriptMarshalerArgSize * 4; + const args = Module.stackAlloc(bytes) as any; + _zero_region(args, bytes); const arg1 = get_arg(args, 2); const arg2 = get_arg(args, 3); set_arg_intptr(arg1, mono_wasm_main_thread_ptr() as any); const fail = cwraps.mono_wasm_invoke_method(managedExports.InstallMainSynchronizationContext!, args, 0 as any); if (fail) runtimeHelpers.nativeAbort("ERR24: Unexpected error"); - get_arg_gc_handle(arg2); if (is_args_exception(args)) { const exc = get_arg(args, 0); throw marshal_exception_to_js(exc); } - return getI32(args) as any; + return get_arg_gc_handle(arg2) as any; } finally { Module.stackRestore(sp); } diff --git a/src/mono/browser/runtime/types/internal.ts b/src/mono/browser/runtime/types/internal.ts index e68e9edf6c8182..adcdf194a2f02b 100644 --- a/src/mono/browser/runtime/types/internal.ts +++ b/src/mono/browser/runtime/types/internal.ts @@ -56,6 +56,7 @@ export const MonoStringRefNull: MonoStringRef = 0; export const JSHandleDisposed: JSHandle = -1; export const JSHandleNull: JSHandle = 0; export const GCHandleNull: GCHandle = 0; +export const GCHandleInvalid: GCHandle = -1; export const VoidPtrNull: VoidPtr = 0; export const CharPtrNull: CharPtr = 0; export const NativePointerNull: NativePointer = 0; From bfb7258e30e4794225ec25934da0f20f6930e660 Mon Sep 17 00:00:00 2001 From: pavelsavara Date: Tue, 13 Feb 2024 13:21:38 +0100 Subject: [PATCH 9/9] PThreadPtr type consistency --- .../runtime/diagnostics/browser/controller.ts | 5 +++-- .../browser/runtime/pthreads/browser/index.ts | 16 ++++++++-------- .../pthreads/shared/emscripten-internals.ts | 12 ++++++------ .../pthreads/shared/emscripten-replacements.ts | 5 +++-- .../browser/runtime/pthreads/shared/index.ts | 14 ++++++++------ .../browser/runtime/pthreads/shared/types.ts | 7 +++++-- .../browser/runtime/pthreads/worker/index.ts | 10 +++++----- src/mono/browser/runtime/startup.ts | 5 ++--- 8 files changed, 40 insertions(+), 34 deletions(-) diff --git a/src/mono/browser/runtime/diagnostics/browser/controller.ts b/src/mono/browser/runtime/diagnostics/browser/controller.ts index 8cc60f7742c4cf..7bcdf62b868505 100644 --- a/src/mono/browser/runtime/diagnostics/browser/controller.ts +++ b/src/mono/browser/runtime/diagnostics/browser/controller.ts @@ -10,6 +10,7 @@ import { withStackAlloc, getI32 } from "../../memory"; import { Thread, waitForThread } from "../../pthreads/browser"; import { isDiagnosticMessage, makeDiagnosticServerControlCommand } from "../shared/controller-commands"; import monoDiagnosticsMock from "consts:monoDiagnosticsMock"; +import { PThreadPtr } from "../../pthreads/shared/types"; /// An object that can be used to control the diagnostic server. export interface ServerController { @@ -57,10 +58,10 @@ export async function startDiagnosticServer(websocket_url: string): Promise { + const result: PThreadPtr | undefined = withStackAlloc(sizeOfPthreadT, (pthreadIdPtr) => { if (!cwraps.mono_wasm_diagnostic_server_create_thread(websocket_url, pthreadIdPtr)) return undefined; - const pthreadId = getI32(pthreadIdPtr); + const pthreadId = getI32(pthreadIdPtr) as any as PThreadPtr; return pthreadId; }); if (result === undefined) { diff --git a/src/mono/browser/runtime/pthreads/browser/index.ts b/src/mono/browser/runtime/pthreads/browser/index.ts index 1d43935e01458c..84c58b694d13a1 100644 --- a/src/mono/browser/runtime/pthreads/browser/index.ts +++ b/src/mono/browser/runtime/pthreads/browser/index.ts @@ -3,7 +3,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; -import { MonoWorkerToMainMessage, PThreadInfo, pthreadPtr } from "../shared/types"; +import { MonoWorkerToMainMessage, PThreadInfo, PThreadPtr, PThreadPtrNull } from "../shared/types"; import { MonoThreadMessage, mono_wasm_pthread_ptr, update_thread_info } from "../shared"; import { PThreadWorker, allocateUnusedWorker, getRunningWorkers, getUnusedWorkerPool, getWorker, loadWasmModuleToWorker } from "../shared/emscripten-internals"; import { createPromiseController, mono_assert, runtimeHelpers } from "../../globals"; @@ -12,23 +12,23 @@ import { mono_log_info } from "../../logging"; import { monoThreadInfo } from "../worker"; import { mono_wasm_init_diagnostics } from "../../diagnostics"; -const threadPromises: Map[]> = new Map(); +const threadPromises: Map[]> = new Map(); export interface Thread { - readonly pthreadPtr: pthreadPtr; + readonly pthreadPtr: PThreadPtr; readonly port: MessagePort; postMessageToWorker(message: T): void; } class ThreadImpl implements Thread { - constructor(readonly pthreadPtr: pthreadPtr, readonly worker: Worker, readonly port: MessagePort) { } + constructor(readonly pthreadPtr: PThreadPtr, readonly worker: Worker, readonly port: MessagePort) { } postMessageToWorker(message: T): void { this.port.postMessage(message); } } /// wait until the thread with the given id has set up a message port to the runtime -export function waitForThread(pthreadPtr: pthreadPtr): Promise { +export function waitForThread(pthreadPtr: PThreadPtr): Promise { if (!WasmEnableThreads) return null as any; const worker = getWorker(pthreadPtr); if (worker?.thread) { @@ -44,7 +44,7 @@ export function waitForThread(pthreadPtr: pthreadPtr): Promise { return promiseAndController.promise; } -export function resolveThreadPromises(pthreadPtr: pthreadPtr, thread?: Thread): void { +export function resolveThreadPromises(pthreadPtr: PThreadPtr, thread?: Thread): void { if (!WasmEnableThreads) return; const arr = threadPromises.get(pthreadPtr); if (arr !== undefined) { @@ -62,13 +62,13 @@ export function resolveThreadPromises(pthreadPtr: pthreadPtr, thread?: Thread): // handler that runs in the main thread when a message is received from a pthread worker function monoWorkerMessageHandler(worker: PThreadWorker, ev: MessageEvent): void { if (!WasmEnableThreads) return; - let pthreadId: pthreadPtr; + let pthreadId: PThreadPtr; // this is emscripten message if (ev.data.cmd === "killThread") { pthreadId = ev.data["thread"]; mono_assert(pthreadId == worker.info.pthreadId, "expected pthreadId to match"); worker.info.isRunning = false; - worker.info.pthreadId = 0; + worker.info.pthreadId = PThreadPtrNull; return; } diff --git a/src/mono/browser/runtime/pthreads/shared/emscripten-internals.ts b/src/mono/browser/runtime/pthreads/shared/emscripten-internals.ts index 5516f1a0f813db..0529744570efda 100644 --- a/src/mono/browser/runtime/pthreads/shared/emscripten-internals.ts +++ b/src/mono/browser/runtime/pthreads/shared/emscripten-internals.ts @@ -3,10 +3,10 @@ import { Module } from "../../globals"; import { Thread } from "../browser"; -import { PThreadInfo, pthreadPtr } from "./types"; +import { PThreadInfo, PThreadPtr } from "./types"; /** @module emscripten-internals accessors to the functions in the emscripten PThreads library, including - * the low-level representations of {@linkcode pthreadPtr} thread info structs, etc. + * the low-level representations of {@linkcode PThreadPtr} thread info structs, etc. * Additionally, note that some of these functions are replaced by {@linkcode file://./emscripten-replacements.ts}. * These have a hard dependency on the version of Emscripten that we are using and may need to be kept in sync with * {@linkcode file://./../../../emsdk/upstream/emscripten/src/library_pthread.js} @@ -27,7 +27,7 @@ export interface PThreadLibrary { /// N.B. emscripten deletes the `pthread` property from the worker when it is not actively running a pthread export interface PThreadWorker extends Worker { - pthread_ptr: pthreadPtr; + pthread_ptr: PThreadPtr; loaded: boolean; // this info is updated via async messages from the worker, it could be stale info: PThreadInfo; @@ -35,12 +35,12 @@ export interface PThreadWorker extends Worker { } interface PThreadInfoMap { - [key: pthreadPtr]: PThreadWorker; + [key: number]: PThreadWorker; } -export function getWorker(pthreadPtr: pthreadPtr): PThreadWorker | undefined { - return getModulePThread().pthreads[pthreadPtr]; +export function getWorker(pthreadPtr: PThreadPtr): PThreadWorker | undefined { + return getModulePThread().pthreads[pthreadPtr as any]; } export function allocateUnusedWorker(): void { diff --git a/src/mono/browser/runtime/pthreads/shared/emscripten-replacements.ts b/src/mono/browser/runtime/pthreads/shared/emscripten-replacements.ts index f0bbf80d6636b3..2f004c49369a62 100644 --- a/src/mono/browser/runtime/pthreads/shared/emscripten-replacements.ts +++ b/src/mono/browser/runtime/pthreads/shared/emscripten-replacements.ts @@ -9,6 +9,7 @@ import { mono_wasm_pthread_on_pthread_created } from "../worker"; import { PThreadLibrary, PThreadWorker, getModulePThread, getUnusedWorkerPool } from "./emscripten-internals"; import { loaderHelpers, mono_assert } from "../../globals"; import { mono_log_warn } from "../../logging"; +import { PThreadPtrNull } from "./types"; /** @module emscripten-replacements Replacements for individual functions in the emscripten PThreads library. * These have a hard dependency on the version of Emscripten that we are using and may need to be kept in sync with @@ -46,7 +47,7 @@ export function replaceEmscriptenPThreadLibrary(modulePThread: PThreadLibrary): // we can't reuse the worker, because user code could leave the worker JS globals in a dirty state worker.info.isRunning = false; resolveThreadPromises(worker.pthread_ptr, undefined); - worker.info.pthreadId = 0; + worker.info.pthreadId = PThreadPtrNull; if (worker.thread?.port) { worker.thread.port.close(); } @@ -116,7 +117,7 @@ function allocateUnusedWorker(): PThreadWorker { getUnusedWorkerPool().push(worker); worker.loaded = false; worker.info = { - pthreadId: 0, + pthreadId: PThreadPtrNull, reuseCount: 0, updateCount: 0, threadPrefix: " - ", diff --git a/src/mono/browser/runtime/pthreads/shared/index.ts b/src/mono/browser/runtime/pthreads/shared/index.ts index 0ddaaec365f7ef..2e2b6a1dc71602 100644 --- a/src/mono/browser/runtime/pthreads/shared/index.ts +++ b/src/mono/browser/runtime/pthreads/shared/index.ts @@ -9,7 +9,7 @@ import { mono_log_debug, set_thread_prefix } from "../../logging"; import { bindings_init } from "../../startup"; import { forceDisposeProxies } from "../../gc-handles"; import { GCHandle, GCHandleNull, WorkerToMainMessageType, monoMessageSymbol } from "../../types/internal"; -import { MonoWorkerToMainMessage } from "./types"; +import { MonoWorkerToMainMessage, PThreadPtr, PThreadPtrNull } from "./types"; import { monoThreadInfo } from "../worker"; /// Messages sent on the dedicated mono channel between a pthread and the browser thread @@ -73,7 +73,9 @@ export function update_thread_info(): void { : monoThreadInfo.isExternalEventLoop ? "jsww" : monoThreadInfo.isBackground ? "back" : "norm"; - monoThreadInfo.threadPrefix = `${monoThreadInfo.isRegistered ? "0x" : "--"}${monoThreadInfo.pthreadId.toString(16).padStart(8, "0")}-${threadType}`; + const hexPtr = (monoThreadInfo.pthreadId as any).toString(16).padStart(8, "0"); + const hexPrefix = monoThreadInfo.isRegistered ? "0x" : "--"; + monoThreadInfo.threadPrefix = `${hexPrefix}${hexPtr}-${threadType}`; loaderHelpers.set_thread_prefix(monoThreadInfo.threadPrefix!); if (!loaderHelpers.config.forwardConsoleLogsToWS) { @@ -92,13 +94,13 @@ export function update_thread_info(): void { } } -export function mono_wasm_pthread_ptr(): number { - if (!WasmEnableThreads) return 0; +export function mono_wasm_pthread_ptr(): PThreadPtr { + if (!WasmEnableThreads) return PThreadPtrNull; return (Module)["_pthread_self"](); } -export function mono_wasm_main_thread_ptr(): number { - if (!WasmEnableThreads) return 0; +export function mono_wasm_main_thread_ptr(): PThreadPtr { + if (!WasmEnableThreads) return PThreadPtrNull; return (Module)["_emscripten_main_runtime_thread_id"](); } diff --git a/src/mono/browser/runtime/pthreads/shared/types.ts b/src/mono/browser/runtime/pthreads/shared/types.ts index 11716137a87e57..1ea8e0c65e5796 100644 --- a/src/mono/browser/runtime/pthreads/shared/types.ts +++ b/src/mono/browser/runtime/pthreads/shared/types.ts @@ -4,10 +4,13 @@ import type { WorkerToMainMessageType } from "../../types/internal"; /// pthread_t in C -export type pthreadPtr = number; +export type PThreadPtr = { + __brand: "PThreadPtr" +} +export const PThreadPtrNull: PThreadPtr = 0; export interface PThreadInfo { - pthreadId: pthreadPtr; + pthreadId: PThreadPtr; reuseCount: number, updateCount: number, diff --git a/src/mono/browser/runtime/pthreads/worker/index.ts b/src/mono/browser/runtime/pthreads/worker/index.ts index b79d4a67555abe..568bf510082928 100644 --- a/src/mono/browser/runtime/pthreads/worker/index.ts +++ b/src/mono/browser/runtime/pthreads/worker/index.ts @@ -7,7 +7,7 @@ import WasmEnableThreads from "consts:wasmEnableThreads"; import { ENVIRONMENT_IS_PTHREAD, loaderHelpers, mono_assert } from "../../globals"; import { mono_wasm_pthread_ptr, postMessageToMain, update_thread_info } from "../shared"; -import { PThreadInfo } from "../shared/types"; +import { PThreadInfo, PThreadPtr, PThreadPtrNull } from "../shared/types"; import { WorkerToMainMessageType, is_nullish } from "../../types/internal"; import { MonoThreadMessage } from "../shared"; import { @@ -58,7 +58,7 @@ class WorkerSelf implements PThreadSelf { // in the worker, so this becomes non-null very early. export let pthread_self: PThreadSelf = null as any as PThreadSelf; export const monoThreadInfo: PThreadInfo = { - pthreadId: 0, + pthreadId: PThreadPtrNull, reuseCount: 0, updateCount: 0, threadPrefix: " - ", @@ -131,7 +131,7 @@ export function mono_wasm_pthread_on_pthread_created(): void { } /// Called in the worker thread (not main thread) from mono when a pthread becomes registered to the mono runtime. -export function mono_wasm_pthread_on_pthread_registered(pthread_id: number): void { +export function mono_wasm_pthread_on_pthread_registered(pthread_id: PThreadPtr): void { if (!WasmEnableThreads) return; try { mono_assert(monoThreadInfo !== null && monoThreadInfo.pthreadId == pthread_id, "expected monoThreadInfo to be set already when registering"); @@ -151,7 +151,7 @@ export function mono_wasm_pthread_on_pthread_registered(pthread_id: number): voi } /// Called in the worker thread (not main thread) from mono when a pthread becomes attached to the mono runtime. -export function mono_wasm_pthread_on_pthread_attached(pthread_id: number, thread_name: CharPtr, background_thread: number, threadpool_thread: number, external_eventloop: number, debugger_thread: number): void { +export function mono_wasm_pthread_on_pthread_attached(pthread_id: PThreadPtr, thread_name: CharPtr, background_thread: number, threadpool_thread: number, external_eventloop: number, debugger_thread: number): void { if (!WasmEnableThreads) return; try { mono_assert(monoThreadInfo !== null && monoThreadInfo.pthreadId == pthread_id, "expected monoThreadInfo to be set already when attaching"); @@ -194,7 +194,7 @@ export function mono_wasm_pthread_set_name(name: CharPtr): void { } /// Called in the worker thread (not main thread) from mono when a pthread becomes detached from the mono runtime. -export function mono_wasm_pthread_on_pthread_unregistered(pthread_id: number): void { +export function mono_wasm_pthread_on_pthread_unregistered(pthread_id: PThreadPtr): void { if (!WasmEnableThreads) return; try { mono_assert(pthread_id === monoThreadInfo.pthreadId, "expected pthread_id to match when un-registering"); diff --git a/src/mono/browser/runtime/startup.ts b/src/mono/browser/runtime/startup.ts index cb2fb41d711365..e8416dc30a4b4a 100644 --- a/src/mono/browser/runtime/startup.ts +++ b/src/mono/browser/runtime/startup.ts @@ -25,7 +25,7 @@ import { mono_log_debug, mono_log_error, mono_log_warn } from "./logging"; // threads import { preAllocatePThreadWorkerPool, mono_wasm_init_threads } from "./pthreads/browser"; import { currentWorkerThreadEvents, dotnetPthreadCreated, initWorkerThreadEvents, monoThreadInfo } from "./pthreads/worker"; -import { mono_wasm_pthread_ptr, update_thread_info } from "./pthreads/shared"; +import { update_thread_info } from "./pthreads/shared"; import { jiterpreter_allocate_tables } from "./jiterpreter-support"; import { localHeapViewU8 } from "./memory"; import { assertNoProxies } from "./gc-handles"; @@ -348,8 +348,7 @@ export function postRunWorker() { const mark = startMeasure(); try { if (runtimeHelpers.proxyGCHandle) { - const pthread_ptr = mono_wasm_pthread_ptr(); - mono_log_warn(`JSSynchronizationContext is still installed on worker 0x${pthread_ptr.toString(16)}.`); + mono_log_warn("JSSynchronizationContext is still installed on worker."); } else { assertNoProxies(); }